From: Ray Strode Date: Tue, 22 May 2007 04:48:00 +0000 (-0400) Subject: first cut at an event loop X-Git-Tag: 0.1.0~276 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=eb7a035201ac88ca93c7a73400ecc81713fad7cb;p=thirdparty%2Fplymouth.git first cut at an event loop --- diff --git a/src/ply-event-loop.c b/src/ply-event-loop.c new file mode 100644 index 00000000..b22a4a0f --- /dev/null +++ b/src/ply-event-loop.c @@ -0,0 +1,657 @@ +#include "config.h" +#include "ply-event-loop.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ply-logger.h" +#include "ply-list.h" +#include "ply-utils.h" + +#ifndef PLY_EVENT_LOOP_NUM_EVENT_HANDLERS +#define PLY_EVENT_LOOP_NUM_EVENT_HANDLERS 64 +#endif + +typedef void (* ply_event_loop_free_handler_t) (void *); + +typedef struct +{ + int fd; + ply_event_handler_t new_data_handler; + ply_event_handler_t disconnected_handler; + void *user_data; + ply_event_loop_free_handler_t free_function; +} ply_event_source_t; + +typedef struct +{ + int signal_number; + ply_event_handler_t handler; + void *user_data; + + sighandler_t old_posix_signal_handler; +} ply_signal_source_t; + +static int ply_signal_dispatcher_sender_fd = -1, + ply_signal_dispatcher_receiver_fd = -1; + +typedef struct +{ + ply_list_t *sources; +} ply_signal_dispatcher_t; + +struct _ply_event_loop +{ + int epoll_fd; + int exit_code; + + ply_list_t *sources; + ply_signal_dispatcher_t *signal_dispatcher; + + uint32_t should_exit : 1; +}; + +static void ply_event_loop_process_pending_events (ply_event_loop_t *loop); +static bool ply_event_loop_remove_source (ply_event_loop_t *loop, + ply_event_source_t *source); +ply_list_node_t *ply_event_loop_find_source_node (ply_event_loop_t *loop, + int fd); + +ply_list_node_t * +ply_signal_dispatcher_find_source_node (ply_signal_dispatcher_t *dispatcher, + int signal_number); + + +static ply_signal_source_t * +ply_signal_source_new (int signal_number, + ply_event_handler_t signal_handler, + void *user_data) +{ + ply_signal_source_t *source; + + source = calloc (1, sizeof (ply_signal_source_t)); + source->signal_number = signal_number; + source->handler = signal_handler; + source->user_data = user_data; + source->old_posix_signal_handler = NULL; + + return source; +} + +static void +ply_signal_source_free (ply_signal_source_t *handler) +{ + if (handler == NULL) + return; + + free (handler); +} + +static ply_signal_dispatcher_t * +ply_signal_dispatcher_new (void) +{ + ply_signal_dispatcher_t *dispatcher; + + if (!ply_open_unidirectional_pipe (&ply_signal_dispatcher_sender_fd, + &ply_signal_dispatcher_receiver_fd)) + return NULL; + + dispatcher = calloc (1, sizeof (ply_signal_dispatcher_t)); + + dispatcher->sources = ply_list_new (); + + return dispatcher; +} + +static void +ply_signal_dispatcher_free (ply_signal_dispatcher_t *dispatcher) +{ + ply_list_node_t *node; + + if (dispatcher == NULL) + return; + + close (ply_signal_dispatcher_receiver_fd); + ply_signal_dispatcher_receiver_fd = -1; + close (ply_signal_dispatcher_sender_fd); + ply_signal_dispatcher_sender_fd = -1; + + node = ply_list_get_first_node (dispatcher->sources); + while (node != NULL) + { + ply_list_node_t *next_node; + ply_signal_source_t *source; + + source = (ply_signal_source_t *) ply_list_node_get_data (node); + + next_node = ply_list_get_next_node (dispatcher->sources, node); + + ply_signal_source_free (source); + + node = next_node; + } + + ply_list_free (dispatcher->sources); + + free (dispatcher); +} + +static void +ply_signal_dispatcher_posix_signal_handler (int signal_number) +{ + if (ply_signal_dispatcher_sender_fd < 0) + return; + + ply_write (ply_signal_dispatcher_sender_fd, &signal_number, + sizeof (signal_number)); +} + +static int +ply_signal_dispatcher_get_next_signal_from_pipe (ply_signal_dispatcher_t *dispatcher) +{ + int signal_number; + + if (!ply_read (ply_signal_dispatcher_receiver_fd, &signal_number, + sizeof (signal_number))) + signal_number = 0; + + return signal_number; +} + +static void +ply_signal_dispatcher_dispatch_signal (ply_signal_dispatcher_t *dispatcher, + int fd) +{ + ply_list_node_t *node; + int signal_number; + + assert (fd == ply_signal_dispatcher_receiver_fd); + + signal_number = ply_signal_dispatcher_get_next_signal_from_pipe (dispatcher); + + node = ply_list_get_first_node (dispatcher->sources); + while (node != NULL) + { + ply_signal_source_t *source; + + source = (ply_signal_source_t *) ply_list_node_get_data (node); + + if (source->signal_number == signal_number) + { + if (source->handler != NULL) + source->handler (source->user_data, signal_number); + } + + node = ply_list_get_next_node (dispatcher->sources, node); + } +} + +static void +ply_signal_dispatcher_reset_signal_sources (ply_signal_dispatcher_t *dispatcher, + int fd) +{ + ply_list_node_t *node; + + node = ply_list_get_first_node (dispatcher->sources); + while (node != NULL) + { + ply_signal_source_t *handler; + + handler = (ply_signal_source_t *) ply_list_node_get_data (node); + + signal (handler->signal_number, + handler->old_posix_signal_handler != NULL? + handler->old_posix_signal_handler : SIG_DFL); + + node = ply_list_get_next_node (dispatcher->sources, node); + } +} + +static ply_event_source_t * +ply_event_source_new (int fd, + ply_event_handler_t new_data_handler, + ply_event_handler_t disconnected_handler, + void *user_data, + ply_event_loop_free_handler_t free_function) +{ + ply_event_source_t *source; + + source = calloc (1, sizeof (ply_event_source_t)); + + source->fd = fd; + source->new_data_handler = new_data_handler; + source->disconnected_handler = disconnected_handler; + source->user_data = user_data; + source->free_function = free_function; + + return source; +} + +static void +ply_event_source_free (ply_event_source_t *source) +{ + if (source == NULL) + return; + + if (source->free_function != NULL) + source->free_function ((void *) source->user_data); + + free (source); +} + +ply_event_loop_t * +ply_event_loop_new (void) +{ + ply_event_loop_t *loop; + + loop = calloc (1, sizeof (ply_event_loop_t)); + + loop->epoll_fd = epoll_create (PLY_EVENT_LOOP_NUM_EVENT_HANDLERS); + + assert (loop->epoll_fd >= 0); + + loop->should_exit = false; + loop->exit_code = 0; + + loop->sources = ply_list_new (); + + loop->signal_dispatcher = ply_signal_dispatcher_new (); + + if (loop->signal_dispatcher == NULL) + return NULL; + + ply_event_loop_watch_fd (loop, + ply_signal_dispatcher_receiver_fd, + (ply_event_handler_t) + ply_signal_dispatcher_dispatch_signal, + (ply_event_handler_t) + ply_signal_dispatcher_reset_signal_sources, + loop->signal_dispatcher); + + return loop; +} + +void +ply_event_loop_free (ply_event_loop_t *loop) +{ + ply_list_node_t *node; + + if (loop == NULL) + return; + + ply_signal_dispatcher_free (loop->signal_dispatcher); + + node = ply_list_get_first_node (loop->sources); + while (node != NULL) + { + ply_list_node_t *next_node; + ply_event_source_t *source; + + source = (ply_event_source_t *) ply_list_node_get_data (node); + + next_node = ply_list_get_next_node (loop->sources, node); + + ply_event_loop_remove_source (loop, source); + + ply_event_source_free (source); + + node = next_node; + } + ply_list_free (loop->sources); + + close (loop->epoll_fd); + free (loop); +} + +static bool +ply_event_loop_add_source (ply_event_loop_t *loop, + ply_event_source_t *source) +{ + struct epoll_event event = { 0 }; + + event.events = EPOLLIN | EPOLLPRI | EPOLLERR | EPOLLHUP; + event.data.ptr = source; + + if (epoll_ctl (loop->epoll_fd, EPOLL_CTL_ADD, source->fd, &event) < 0) + return false; + + ply_list_append_data (loop->sources, source); + + return true; +} + +static bool +ply_event_loop_remove_source_node (ply_event_loop_t *loop, + ply_list_node_t *source_node) +{ + ply_event_source_t *source; + struct epoll_event event = { 0 }; + + source = (ply_event_source_t *) ply_list_node_get_data (source_node); + + event.events = EPOLLIN | EPOLLPRI | EPOLLERR | EPOLLHUP; + event.data.ptr = source; + + if (epoll_ctl (loop->epoll_fd, EPOLL_CTL_DEL, source->fd, &event) < 0) + return false; + + ply_list_remove_node (loop->sources, source_node); + + return true; +} + +static bool +ply_event_loop_remove_source (ply_event_loop_t *loop, + ply_event_source_t *source) +{ + ply_list_node_t *source_node; + + source_node = ply_list_find_node (loop->sources, source); + + assert (source_node != NULL); + + return ply_event_loop_remove_source_node (loop, source_node); +} + +ply_list_node_t * +ply_event_loop_find_source_node (ply_event_loop_t *loop, + int fd) +{ + ply_list_node_t *node; + + node = ply_list_get_first_node (loop->sources); + while (node != NULL) + { + ply_event_source_t *source; + + source = (ply_event_source_t *) ply_list_node_get_data (node); + + if (source->fd == fd) + break; + + node = ply_list_get_next_node (loop->sources, node); + } + + return node; +} + +bool +ply_event_loop_watch_fd (ply_event_loop_t *loop, + int fd, + ply_event_handler_t new_data_handler, + ply_event_handler_t disconnected_handler, + void *user_data) +{ + ply_list_node_t *node; + ply_event_source_t *source; + + node = ply_event_loop_find_source_node (loop, fd); + + assert (node == NULL); + + source = ply_event_source_new (fd, + new_data_handler, + disconnected_handler, + user_data, NULL); + + if (!ply_event_loop_add_source (loop, source)) + return false; + + return true; +} + +bool +ply_event_loop_stop_watching_fd (ply_event_loop_t *loop, + int fd) +{ + ply_list_node_t *node; + ply_event_source_t *source; + + node = ply_event_loop_find_source_node (loop, fd); + + assert (node != NULL); + + source = (ply_event_source_t *) ply_list_node_get_data (node); + + if (!ply_event_loop_remove_source_node (loop, node)) + return false; + + ply_event_source_free (source); + + return true; +} + +ply_list_node_t * +ply_signal_dispatcher_find_source_node (ply_signal_dispatcher_t *dispatcher, + int signal_number) +{ + ply_list_node_t *node; + + node = ply_list_get_first_node (dispatcher->sources); + while (node != NULL) + { + ply_signal_source_t *handler; + + handler = (ply_signal_source_t *) ply_list_node_get_data (node); + + if (handler->signal_number == signal_number) + break; + + node = ply_list_get_next_node (dispatcher->sources, node); + } + + return node; +} + +bool +ply_event_loop_watch_signal (ply_event_loop_t *loop, + int signal_number, + ply_event_handler_t signal_handler, + void *user_data) +{ + ply_signal_source_t *source; + + source = ply_signal_source_new (signal_number, + signal_handler, + user_data); + + source->old_posix_signal_handler = + signal (signal_number, ply_signal_dispatcher_posix_signal_handler); + ply_list_append_data (loop->signal_dispatcher->sources, source); + + return true; +} + +static void +ply_signal_dispatcher_remove_source_node (ply_signal_dispatcher_t *dispatcher, + ply_list_node_t *node) +{ + ply_signal_source_t *source; + + source = (ply_signal_source_t *) ply_list_node_get_data (node); + + signal (source->signal_number, + source->old_posix_signal_handler != NULL? + source->old_posix_signal_handler : SIG_DFL); + + ply_list_remove_node (dispatcher->sources, node); +} + +bool +ply_event_loop_stop_watching_signal (ply_event_loop_t *loop, + int signal_number) +{ + ply_list_node_t *node; + + node = ply_signal_dispatcher_find_source_node (loop->signal_dispatcher, + signal_number); + + if (node == NULL) + return false; + + ply_signal_dispatcher_remove_source_node (loop->signal_dispatcher, node); + + return true; +} + +static void +ply_event_loop_process_pending_events (ply_event_loop_t *loop) +{ + int number_of_received_events, i; + static struct epoll_event *events = NULL; + int saved_errno; + + if (events == NULL) + events = + malloc (PLY_EVENT_LOOP_NUM_EVENT_HANDLERS * sizeof (struct epoll_event)); + + memset (events, -1, + PLY_EVENT_LOOP_NUM_EVENT_HANDLERS * sizeof (struct epoll_event)); + + do + { + number_of_received_events = epoll_wait (loop->epoll_fd, events, + sizeof (events), -1); + + if (number_of_received_events < 0) + { + saved_errno = errno; + + if (saved_errno != EINTR) + { + ply_event_loop_exit (loop, 255); + return; + } + } + } + while ((number_of_received_events < 0) && (saved_errno == EINTR)); + + for (i = 0; i < number_of_received_events; i++) + { + ply_event_source_t *source; + + source = (ply_event_source_t *) (events[i].data.ptr); + + if ((events[i].events & EPOLLIN) || (events[i].events & EPOLLPRI)) + { + if (source->new_data_handler != NULL) + source->new_data_handler (source->user_data, source->fd); + } + else if ((events[i].events & EPOLLHUP) || (events[i].events & EPOLLERR)) + { + if (source->disconnected_handler != NULL) + source->disconnected_handler (source->user_data, source->fd); + + if (!ply_event_loop_remove_source (loop, source)) + { + /* do nothing + */ + } + + ply_event_source_free (source); + source = NULL; + } + + if (loop->should_exit) + break; + } +} + +void +ply_event_loop_exit (ply_event_loop_t *loop, int exit_code) +{ + loop->should_exit = true; + loop->exit_code = exit_code; +} + +int +ply_event_loop_run (ply_event_loop_t *loop) +{ + if (loop->should_exit) + return loop->exit_code; + + do + { + ply_event_loop_process_pending_events (loop); + } + while (!loop->should_exit); + + loop->should_exit = false; + + return loop->exit_code; +} + +#ifdef PLY_EVENT_LOOP_ENABLE_TEST + +static ply_event_loop_t *loop; + +static void +usr1_signal_handler (void) +{ + write (1, "got sigusr1\n", sizeof ("got sigusr1\n") - 1); +} + +static void +hangup_signal_handler (void) +{ + write (1, "got hangup\n", sizeof ("got hangup\n") - 1); +} + +static void +terminate_signal_handler (void) +{ + write (1, "got terminate\n", sizeof ("got terminate\n") - 1); + ply_event_loop_exit (loop, 0); +} + +static void +line_received_handler (void) +{ + char line[512] = { 0 }; + printf ("Received line: "); + fflush (stdout); + + fgets (line, sizeof (line), stdin); + printf ("%s", line); +} + +int +main (int argc, + char **argv) +{ + int exit_code; + + loop = ply_event_loop_new (); + + ply_event_loop_watch_signal (loop, SIGHUP, + (ply_event_handler_t) hangup_signal_handler, + NULL); + ply_event_loop_watch_signal (loop, SIGTERM, + (ply_event_handler_t) + terminate_signal_handler, NULL); + ply_event_loop_watch_signal (loop, SIGUSR1, + (ply_event_handler_t) + usr1_signal_handler, NULL); + + ply_event_loop_watch_fd (loop, 0, + (ply_event_handler_t) line_received_handler, + (ply_event_handler_t) line_received_handler, + NULL); + + exit_code = ply_event_loop_run (loop); + + ply_event_loop_free (loop); + + return exit_code; +} +#endif /* PLY_EVENT_LOOP_ENABLE_TEST */ +/* vim: set ts=4 sw=4 expandtab autoindent cindent cino={.5s,(0: */ diff --git a/src/ply-event-loop.h b/src/ply-event-loop.h new file mode 100644 index 00000000..0b4b9d41 --- /dev/null +++ b/src/ply-event-loop.h @@ -0,0 +1,33 @@ +#ifndef PLY_EVENT_LOOP_H +#define PLY_EVENT_LOOP_H + +#include + +typedef struct _ply_event_loop ply_event_loop_t; + +typedef void (* ply_event_handler_t) (void *user_data, + int source); + +#ifndef PLY_HIDE_FUNCTION_DECLARATIONS +ply_event_loop_t *ply_event_loop_new (void); +void ply_event_loop_free (ply_event_loop_t *loop); +bool ply_event_loop_watch_fd (ply_event_loop_t *loop, + int fd, + ply_event_handler_t new_data_handler, + ply_event_handler_t disconnected_handler, + void *user_data); +bool ply_event_loop_stop_watching_fd (ply_event_loop_t *loop, + int fd); +bool ply_event_loop_watch_signal (ply_event_loop_t *loop, + int signal_number, + ply_event_handler_t signal_handler, + void *user_data); +bool ply_event_loop_stop_watching_signal (ply_event_loop_t *loop, + int signal_number); + +int ply_event_loop_run (ply_event_loop_t *loop); +void ply_event_loop_exit (ply_event_loop_t *loop, + int exit_code); +#endif + +#endif diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am index 85f176d8..4472ef39 100644 --- a/src/tests/Makefile.am +++ b/src/tests/Makefile.am @@ -11,5 +11,6 @@ include $(srcdir)/ply-terminal-session-test.am include $(srcdir)/ply-init-control-test.am include $(srcdir)/ply-logger-test.am include $(srcdir)/ply-list-test.am +include $(srcdir)/ply-event-loop-test.am noinst_PROGRAMS = $(TESTS) diff --git a/src/tests/ply-event-loop-test.am b/src/tests/ply-event-loop-test.am new file mode 100644 index 00000000..e58bd5e8 --- /dev/null +++ b/src/tests/ply-event-loop-test.am @@ -0,0 +1,12 @@ +TESTS += ply-event-loop-test + +ply_event_loop_test_CFLAGS = $(PLYMOUTH_CFLAGS) -DPLY_EVENT_LOOP_ENABLE_TEST +ply_event_loop_test_LDADD = $(PLYMOUTH_LIBS) + +ply_event_loop_test_SOURCES = \ + $(srcdir)/../ply-utils.h \ + $(srcdir)/../ply-utils.c \ + $(srcdir)/../ply-list.h \ + $(srcdir)/../ply-list.c \ + $(srcdir)/../ply-event-loop.h \ + $(srcdir)/../ply-event-loop.c