conmon: Add polling_set_t epoll helper

This is a tiny helper that makes using epoll a bit less painful.
This commit is contained in:
Alexander Larsson 2017-06-05 13:13:28 +02:00
parent ae77c61a8a
commit 22b78314e6
2 changed files with 125 additions and 0 deletions

103
conmon/pollset.c Normal file
View file

@ -0,0 +1,103 @@
#define _GNU_SOURCE
#include <glib.h>
#include <sys/epoll.h>
#include <unistd.h>
#include <errno.h>
#include "pollset.h"
#define MAX_EVENTS 10
typedef struct _polling_fd_t polling_fd_t;
struct _polling_fd_t {
int fd;
polling_set_input_cb input_cb;
polling_set_error_cb error_cb;
gpointer user_data;
};
static void polling_fd_free(polling_fd_t *polling_fd)
{
if (polling_fd->fd >= 0)
close(polling_fd->fd);
g_free(polling_fd);
}
int polling_set_init(polling_set_t *set)
{
set->epfd = epoll_create1(EPOLL_CLOEXEC);
if (set->epfd < 0)
return -1;
set->fd_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
NULL, (GDestroyNotify)polling_fd_free);
return 0;
}
void polling_set_destroy(polling_set_t *set)
{
if (set->epfd >= 0)
close(set->epfd);
if (set->fd_hash)
g_hash_table_destroy(set->fd_hash);
}
int polling_set_add_fd(polling_set_t *set, int fd, polling_set_input_cb input_cb, polling_set_error_cb error_cb, gpointer user_data)
{
struct epoll_event ev;
polling_fd_t *polling_fd;
ev.events = EPOLLIN;
ev.data.fd = fd;
if (epoll_ctl(set->epfd, EPOLL_CTL_ADD, ev.data.fd, &ev) < 0)
return -1;
polling_fd = g_new0(polling_fd_t, 1);
polling_fd->fd = fd;
polling_fd->input_cb = input_cb;
polling_fd->error_cb = error_cb;
polling_fd->user_data = user_data;
g_hash_table_insert(set->fd_hash, GINT_TO_POINTER(fd), polling_fd);
return 0;
}
void polling_set_remove_fd(polling_set_t *set, int fd)
{
g_hash_table_remove(set->fd_hash, GINT_TO_POINTER(fd));
}
int polling_set_iterate(polling_set_t *set)
{
struct epoll_event evlist[MAX_EVENTS];
int ready;
do {
ready = epoll_wait(set->epfd, evlist, MAX_EVENTS, -1);
} while (ready == -1 && errno == EINTR);
if (ready < 0)
return -1;
for (int i = 0; i < ready; i++) {
int fd = evlist[i].data.fd;
polling_fd_t *polling_fd = g_hash_table_lookup(set->fd_hash, GINT_TO_POINTER(fd));
if (polling_fd == NULL)
continue;
if (evlist[i].events & EPOLLIN &&
polling_fd->input_cb)
polling_fd->input_cb(set, fd, polling_fd->user_data);
else if (evlist[i].events & (EPOLLHUP | EPOLLERR)) {
bool remove = true;
if (polling_fd->error_cb)
remove = polling_fd->error_cb(set, fd, polling_fd->user_data);
if (remove)
polling_set_remove_fd(set, fd);
}
}
return 0;
}

22
conmon/pollset.h Normal file
View file

@ -0,0 +1,22 @@
#pragma once
#include <stdbool.h>
typedef struct _polling_set_t polling_set_t;
struct _polling_set_t {
int epfd;
GHashTable *fd_hash;
};
#define POLLING_SET_INIT { -1, NULL }
typedef void (*polling_set_input_cb) (polling_set_t *set, int fd, gpointer user_data);
typedef bool (*polling_set_error_cb) (polling_set_t *set, int fd, gpointer user_data);
int polling_set_init(polling_set_t *set);
void polling_set_destroy(polling_set_t *set);
int polling_set_add_fd(polling_set_t *set, int fd, polling_set_input_cb input_cb, polling_set_error_cb error_cb, gpointer user_data);
int polling_set_iterate(polling_set_t *set);
#define _cleanup_pollset_ __attribute__((cleanup(polling_set_destroy)))