]> git.ipfire.org Git - thirdparty/plymouth.git/commitdiff
first cut at an event loop
authorRay Strode <rstrode@redhat.com>
Tue, 22 May 2007 04:48:00 +0000 (00:48 -0400)
committerRay Strode <rstrode@redhat.com>
Tue, 22 May 2007 04:48:00 +0000 (00:48 -0400)
src/ply-event-loop.c [new file with mode: 0644]
src/ply-event-loop.h [new file with mode: 0644]
src/tests/Makefile.am
src/tests/ply-event-loop-test.am [new file with mode: 0644]

diff --git a/src/ply-event-loop.c b/src/ply-event-loop.c
new file mode 100644 (file)
index 0000000..b22a4a0
--- /dev/null
@@ -0,0 +1,657 @@
+#include "config.h"
+#include "ply-event-loop.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <unistd.h>
+
+#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 (file)
index 0000000..0b4b9d4
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef PLY_EVENT_LOOP_H
+#define PLY_EVENT_LOOP_H
+
+#include <stdbool.h>
+
+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
index 85f176d8053cb480c43171a663279689c9798fb2..4472ef39f75d8da79d30817c251f9c7c9c99187a 100644 (file)
@@ -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 (file)
index 0000000..e58bd5e
--- /dev/null
@@ -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