分类:
2008-10-13 16:12:03
//封装的epoll
static GObjectClass *parent_class = NULL;
#if HAVE_EPOLL_SUPPORT
#include
#include
/* epoll_create() etc based main-loop.
Note that we use Level-Triggered behavior,
not edge-triggered, since that is how GskMainLoops are.
*/
#define EPOLL_INITIAL_SIZE 2048
#define MAX_EPOLL_EVENTS 512
static inline const char *
op_to_string (int op)
{
return (op == EPOLL_CTL_DEL) ? "del"
: (op == EPOLL_CTL_MOD) ? "mod"
: (op == EPOLL_CTL_ADD) ? "add"
: "op-unknown";
}
/* --- GskMainLoopPollBase methods --- */
static gboolean
gsk_main_loop_epoll_setup (GskMainLoop *main_loop)
{
GskMainLoopClass *pclass = GSK_MAIN_LOOP_CLASS (parent_class);
int fd;
if (pclass->setup != NULL)
if (!(*pclass->setup) (main_loop))
return FALSE;
fd = epoll_create (EPOLL_INITIAL_SIZE);
if (fd < 0)
return FALSE;
GSK_MAIN_LOOP_EPOLL (main_loop)->fd = fd;
return TRUE;
}
static void
gsk_main_loop_epoll_config_fd (GskMainLoopPollBase *main_loop,
int fd,
GIOCondition old_io_conditions,
GIOCondition io_conditions)
{
GskMainLoopEpoll *epoll = GSK_MAIN_LOOP_EPOLL (main_loop);
int op = (io_conditions == 0) ? EPOLL_CTL_DEL
: (old_io_conditions == 0) ? EPOLL_CTL_ADD
: EPOLL_CTL_MOD;
struct epoll_event event;
if (old_io_conditions == 0 && io_conditions == 0)
return;
event.events = ((io_conditions & G_IO_IN) ? (EPOLLIN) : 0)
| ((io_conditions & G_IO_OUT) ? (EPOLLOUT) : 0)
| ((io_conditions & G_IO_HUP) ? (EPOLLHUP) : 0)
;
event.data.fd = fd;
if (epoll_ctl (epoll->fd, op, fd, &event) < 0)
{
g_warning ("epoll_ctl: op=%s, fd=%d, new_events=%x failed: %s",
op_to_string (op), fd, event.events, g_strerror (errno));
}
}
static gboolean
gsk_main_loop_epoll_do_polling (GskMainLoopPollBase *main_loop,
int max_timeout,
guint max_events,
guint *num_events_out,
GskMainLoopEvent *events)
{
GskMainLoopEpoll *main_loop_epoll = GSK_MAIN_LOOP_EPOLL (main_loop);
struct epoll_event *e_events = main_loop_epoll->epoll_events;
int n_events;
int i;
guint n_out = 0;
errno = EINTR; // HACK: ignore errors which don't set errno !?!
n_events = epoll_wait (main_loop_epoll->fd, e_events,
MIN (max_events, MAX_EPOLL_EVENTS),
max_timeout);
#if 0
g_message ("epoll_wait: max_timeout=%d, max_events=%u, n_events out=%d",
max_timeout, max_events, n_events);
#endif
if (n_events < 0)
{
int e = errno;
*num_events_out = 0;
if (gsk_errno_is_ignorable (e))
return TRUE;
g_warning ("error running epoll_wait: %s", g_strerror (e));
return TRUE;
}
for (i = 0; i < n_events; i++)
{
int fd = e_events[i].data.fd;
unsigned e = e_events[i].events;
GIOCondition condition = 0;
if (e & EPOLLIN)
condition |= G_IO_IN;
if (e & EPOLLHUP)
condition |= G_IO_HUP;
if (e & EPOLLOUT)
condition |= G_IO_OUT;
events[n_out].type = GSK_MAIN_LOOP_EVENT_IO;
events[n_out].data.io.events = condition;
events[n_out].data.io.fd = fd;
n_out++;
}
*num_events_out = n_out;
return TRUE;
}
static void
gsk_main_loop_epoll_finalize (GObject *object)
{
GskMainLoopEpoll *main_loop_epoll = GSK_MAIN_LOOP_EPOLL (object);
g_free (main_loop_epoll->epoll_events);
(*parent_class->finalize) (object);
}
#endif /* HAVE_EPOLL_SUPPORT */
/* --- functions --- */
static void
gsk_main_loop_epoll_init (GskMainLoopEpoll *main_loop_epoll)
{
#if HAVE_EPOLL_SUPPORT
main_loop_epoll->fd = -1;
main_loop_epoll->epoll_events = g_new (struct epoll_event, MAX_EPOLL_EVENTS);
#endif /* HAVE_EPOLL_SUPPORT */
}
static void
gsk_main_loop_epoll_class_init (GskMainLoopEpollClass *class)
{
#if HAVE_EPOLL_SUPPORT
GskMainLoopPollBaseClass *main_loop_poll_base_class = GSK_MAIN_LOOP_POLL_BASE_CLASS (class);
GskMainLoopClass *main_loop_class = GSK_MAIN_LOOP_CLASS (class);
GObjectClass *object_class = G_OBJECT_CLASS (class);
main_loop_class->setup = gsk_main_loop_epoll_setup;
main_loop_poll_base_class->config_fd = gsk_main_loop_epoll_config_fd;
main_loop_poll_base_class->do_polling = gsk_main_loop_epoll_do_polling;
object_class->finalize = gsk_main_loop_epoll_finalize;
#endif /* HAVE_EPOLL_SUPPORT */
parent_class = g_type_class_peek_parent (class);
}
GType gsk_main_loop_epoll_get_type()
{
static GType main_loop_epoll_type = 0;
if (!main_loop_epoll_type)
{
static const GTypeInfo main_loop_epoll_info =
{
sizeof(GskMainLoopEpollClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) gsk_main_loop_epoll_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GskMainLoopEpoll),
0, /* n_preallocs */
(GInstanceInitFunc) gsk_main_loop_epoll_init,
NULL /* value_table */
};
main_loop_epoll_type = g_type_register_static (GSK_TYPE_MAIN_LOOP_POLL_BASE,
"GskMainLoopEpoll",
&main_loop_epoll_info, 0);
}
return main_loop_epoll_type;
}
//封装的kqueue
static GObjectClass *parent_class = NULL;
#if HAVE_KQUEUE
#include
/* Include headers for kqueue(), kevent() and struct kevent. */
#include
#include
#include
#endif
#include
#include
/* --- GskMainLoop methods --- */
static gboolean
gsk_main_loop_kqueue_setup (GskMainLoop *main_loop)
{
GskMainLoopKqueue *main_loop_kqueue = GSK_MAIN_LOOP_KQUEUE (main_loop);
#if HAVE_KQUEUE
main_loop_kqueue->kernel_queue_id = kqueue ();
return (main_loop_kqueue->kernel_queue_id >= 0);
#else
(void) main_loop_kqueue;
return FALSE;
#endif
}
#if HAVE_KQUEUE
static void
kqueue_flush_pending_changes (GskMainLoopKqueue *main_loop_kqueue)
{
if (main_loop_kqueue->num_updates > 0)
{
struct timespec timeout = { 0, 0 };
kevent (main_loop_kqueue->kernel_queue_id,
(const struct kevent *) main_loop_kqueue->kevent_array,
main_loop_kqueue->num_updates,
NULL, 0,
&timeout);
main_loop_kqueue->num_updates = 0;
}
}
static inline void
change_signal (int signo, gboolean ignore)
{
struct sigaction action;
memset (&action, 0, sizeof (action));
action.sa_handler = ignore ? SIG_IGN : SIG_DFL;
sigaction (signo, &action, NULL);
}
static inline struct kevent *
get_next_update ( GskMainLoopKqueue *main_loop_kqueue)
{
if (main_loop_kqueue->num_updates == main_loop_kqueue->max_updates)
{
if (main_loop_kqueue->max_updates == 0)
main_loop_kqueue->max_updates = 128;
else
main_loop_kqueue->max_updates *= 2;
main_loop_kqueue->kevent_array
= g_realloc (main_loop_kqueue->kevent_array,
sizeof (struct kevent) * main_loop_kqueue->max_updates);
}
return ((struct kevent *) (main_loop_kqueue->kevent_array))
+ main_loop_kqueue->num_updates;
}
static void
gsk_main_loop_kqueue_change (GskMainLoop *main_loop,
GskMainLoopChange *change)
{
GskMainLoopKqueue *main_loop_kqueue = GSK_MAIN_LOOP_KQUEUE (main_loop);
if (main_loop_kqueue->num_updates + 1 >= main_loop_kqueue->max_updates)
{
if (main_loop_kqueue->max_updates == 0)
main_loop_kqueue->max_updates = 128;
else
main_loop_kqueue->max_updates *= 2;
main_loop_kqueue->kevent_array
= g_realloc (main_loop_kqueue->kevent_array,
sizeof (struct kevent) * main_loop_kqueue->max_updates);
}
switch (change->type)
{
static gboolean has_ignored_sigchld = FALSE;
struct kevent *event;
case GSK_MAIN_LOOP_EVENT_PROCESS:
if (change->data.process.did_exit)
return;
event = get_next_update (main_loop_kqueue);
event->ident = change->data.process.pid;
event->filter = EVFILT_PROC;
event->flags = change->data.process.add ? (EV_ADD|EV_ONESHOT|EV_CLEAR) : EV_DELETE;
event->fflags = NOTE_EXIT;
event->data = 0;
event->udata = NULL;
main_loop_kqueue->num_updates++;
if (!has_ignored_sigchld)
{
change_signal (SIGCHLD, TRUE);
has_ignored_sigchld = TRUE;
}
break;
case GSK_MAIN_LOOP_EVENT_SIGNAL:
event = get_next_update (main_loop_kqueue);
event->ident = change->data.signal.number;
event->filter = EVFILT_SIGNAL;
event->flags = change->data.signal.add ? EV_ADD : EV_DELETE;
event->fflags = 0;
event->data = 0;
event->udata = NULL;
change_signal (change->data.signal.number, change->data.signal.add);
main_loop_kqueue->num_updates++;
kqueue_flush_pending_changes (main_loop_kqueue);
break;
case GSK_MAIN_LOOP_EVENT_IO:
if ((change->data.io.old_events ^ change->data.io.events) & G_IO_IN)
{
event = get_next_update (main_loop_kqueue);
event->ident = change->data.io.fd;
event->filter = EVFILT_READ;
event->flags = (change->data.io.events & G_IO_IN) ? EV_ADD : EV_DELETE;
event->fflags = 0;
event->data = 0;
event->udata = NULL;
main_loop_kqueue->num_updates++;
}
if ((change->data.io.old_events ^ change->data.io.events) & G_IO_OUT)
{
event = get_next_update (main_loop_kqueue);
event->ident = change->data.io.fd;
event->filter = EVFILT_WRITE;
event->flags = (change->data.io.events & G_IO_OUT) ? EV_ADD : EV_DELETE;
event->fflags = 0;
event->data = 0;
event->udata = NULL;
main_loop_kqueue->num_updates++;
}
kqueue_flush_pending_changes (main_loop_kqueue);
break;
}
}
static guint
gsk_main_loop_kqueue_poll (GskMainLoop *main_loop,
guint max_events_out,
GskMainLoopEvent *events,
gint timeout_milli)
{
struct kevent *out = alloca (sizeof (struct kevent) * max_events_out);
GskMainLoopKqueue *main_loop_kqueue = GSK_MAIN_LOOP_KQUEUE (main_loop);
guint kevent_rv;
guint i;
guint rv = 0;
struct timespec timeout;
struct timespec *p_timeout;
if (timeout_milli < 0)
{
p_timeout = NULL;
}
else
{
timeout.tv_sec = timeout_milli / 1000;
timeout.tv_nsec = timeout_milli % 1000 * 1000 * 1000;
p_timeout = &timeout;
}
kevent_rv = kevent (main_loop_kqueue->kernel_queue_id,
main_loop_kqueue->kevent_array,
main_loop_kqueue->num_updates,
out,
max_events_out,
p_timeout);
main_loop_kqueue->num_updates = 0;
for (i = 0; i < kevent_rv; i++)
{
switch (out[i].filter)
{
case EVFILT_READ:
events[rv].type = GSK_MAIN_LOOP_EVENT_IO;
events[rv].data.io.events = G_IO_IN;
events[rv].data.io.fd = out[i].ident;
rv++;
break;
case EVFILT_WRITE:
events[rv].type = GSK_MAIN_LOOP_EVENT_IO;
events[rv].data.io.events = G_IO_OUT;
events[rv].data.io.fd = out[i].ident;
rv++;
break;
case EVFILT_SIGNAL:
events[rv].type = GSK_MAIN_LOOP_EVENT_SIGNAL;
events[rv].data.signal = out[i].ident;
rv++;
break;
case EVFILT_PROC:
if (out[i].fflags == NOTE_EXIT)
{
events[rv].type = GSK_MAIN_LOOP_EVENT_PROCESS;
events[rv].data.process_wait_info.pid = out[i].ident;
if (gsk_main_loop_do_waitpid (out[i].ident,
&events[rv].data.process_wait_info))
rv++;
}
break;
default:
g_warning ("unexpected type of event from kevent (%d)",
out[i].filter);
}
}
return rv;
}
#endif
static void
gsk_main_loop_kqueue_finalize(GObject *object)
{
GskMainLoopKqueue *kqueue = GSK_MAIN_LOOP_KQUEUE (object);
gsk_main_loop_destroy_all_sources (GSK_MAIN_LOOP (object));
if (kqueue->kernel_queue_id >= 0)
close (kqueue->kernel_queue_id);
(*parent_class->finalize) (object);
}
/* --- class methods --- */
static void
gsk_main_loop_kqueue_init (GskMainLoopKqueue *kqueue)
{
kqueue->kernel_queue_id = -1;
}
static void
gsk_main_loop_kqueue_class_init (GskMainLoopClass *main_loop_class)
{
GObjectClass *object_class = G_OBJECT_CLASS (main_loop_class);
parent_class = g_type_class_peek_parent (main_loop_class);
main_loop_class->setup = gsk_main_loop_kqueue_setup;
#if HAVE_KQUEUE
main_loop_class->poll = gsk_main_loop_kqueue_poll;
main_loop_class->change = gsk_main_loop_kqueue_change;
#endif
object_class->finalize = gsk_main_loop_kqueue_finalize;
}
GType
gsk_main_loop_kqueue_get_type()
{
static GType main_loop_kqueue_type = 0;
if (!main_loop_kqueue_type)
{
static const GTypeInfo main_loop_kqueue_info =
{
sizeof(GskMainLoopKqueueClass),
(GBaseInitFunc) NULL,
(GBaseFinalizeFunc) NULL,
(GClassInitFunc) gsk_main_loop_kqueue_class_init,
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (GskMainLoopKqueue),
0, /* n_preallocs */
(GInstanceInitFunc) gsk_main_loop_kqueue_init,
NULL /* value_table */
};
GType parent = GSK_TYPE_MAIN_LOOP;
main_loop_kqueue_type = g_type_register_static (parent,
"GskMainLoopKqueue",
&main_loop_kqueue_info, 0);
}
return main_loop_kqueue_type;
}