You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1333 lines
35 KiB
1333 lines
35 KiB
/* ply-event-loop.c - small epoll based event loop
|
|
*
|
|
* Copyright (C) 2007 Red Hat, Inc.
|
|
*
|
|
* This file is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published
|
|
* by the Free Software Foundation; either version 2 of the License,
|
|
* or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
* 02111-1307, USA.
|
|
*
|
|
* Written by: Ray Strode <rstrode@redhat.com>
|
|
*/
|
|
#include "config.h"
|
|
#include "ply-event-loop.h"
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <math.h>
|
|
#include <signal.h>
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <sys/epoll.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/termios.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
|
|
|
|
#ifndef PLY_EVENT_LOOP_NO_TIMED_WAKEUP
|
|
#define PLY_EVENT_LOOP_NO_TIMED_WAKEUP 0.0
|
|
#endif
|
|
|
|
typedef struct
|
|
{
|
|
int fd;
|
|
ply_list_t *destinations;
|
|
ply_list_t *fd_watches;
|
|
uint32_t is_getting_polled : 1;
|
|
uint32_t is_disconnected : 1;
|
|
} ply_event_source_t;
|
|
|
|
typedef struct
|
|
{
|
|
ply_event_source_t *source;
|
|
|
|
ply_event_loop_fd_status_t status;
|
|
ply_event_handler_t status_met_handler;
|
|
ply_event_handler_t disconnected_handler;
|
|
void *user_data;
|
|
} ply_event_destination_t;
|
|
|
|
struct _ply_fd_watch
|
|
{
|
|
ply_event_destination_t *destination;
|
|
};
|
|
|
|
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;
|
|
|
|
typedef struct
|
|
{
|
|
ply_event_loop_exit_handler_t handler;
|
|
void *user_data;
|
|
} ply_event_loop_exit_closure_t;
|
|
|
|
typedef struct
|
|
{
|
|
double timeout;
|
|
ply_event_loop_timeout_handler_t handler;
|
|
void *user_data;
|
|
} ply_event_loop_timeout_watch_t;
|
|
|
|
struct _ply_event_loop
|
|
{
|
|
int epoll_fd;
|
|
int exit_code;
|
|
double wakeup_time;
|
|
|
|
ply_list_t *sources;
|
|
ply_list_t *exit_closures;
|
|
ply_list_t *timeout_watches;
|
|
|
|
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 void ply_event_loop_remove_source (ply_event_loop_t *loop,
|
|
ply_event_source_t *source);
|
|
static ply_list_node_t *ply_event_loop_find_source_node (ply_event_loop_t *loop,
|
|
int fd);
|
|
|
|
static 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_destination_t *
|
|
ply_event_destination_new (ply_event_loop_fd_status_t status,
|
|
ply_event_handler_t status_met_handler,
|
|
ply_event_handler_t disconnected_handler,
|
|
void *user_data)
|
|
{
|
|
ply_event_destination_t *destination;
|
|
|
|
destination = calloc (1, sizeof (ply_event_destination_t));
|
|
|
|
destination->source = NULL;
|
|
destination->status = status;
|
|
destination->status_met_handler = status_met_handler;
|
|
destination->disconnected_handler = disconnected_handler;
|
|
destination->user_data = user_data;
|
|
|
|
return destination;
|
|
}
|
|
|
|
static void
|
|
ply_event_destination_free (ply_event_destination_t *destination)
|
|
{
|
|
if (destination == NULL)
|
|
return;
|
|
|
|
free (destination);
|
|
}
|
|
|
|
static ply_fd_watch_t *
|
|
ply_fd_watch_new (ply_event_destination_t *destination)
|
|
{
|
|
ply_fd_watch_t *watch;
|
|
|
|
watch = calloc (1, sizeof (ply_fd_watch_t));
|
|
watch->destination = destination;
|
|
|
|
return watch;
|
|
}
|
|
|
|
static void
|
|
ply_fd_watch_free (ply_fd_watch_t *watch)
|
|
{
|
|
watch->destination = NULL;
|
|
free (watch);
|
|
}
|
|
|
|
static ply_event_source_t *
|
|
ply_event_source_new (int fd)
|
|
{
|
|
ply_event_source_t *source;
|
|
|
|
source = calloc (1, sizeof (ply_event_source_t));
|
|
|
|
source->fd = fd;
|
|
source->destinations = ply_list_new ();
|
|
source->fd_watches = ply_list_new ();
|
|
source->is_getting_polled = false;
|
|
source->is_disconnected = false;
|
|
|
|
return source;
|
|
}
|
|
|
|
static void
|
|
ply_event_source_free (ply_event_source_t *source)
|
|
{
|
|
if (source == NULL)
|
|
return;
|
|
|
|
assert (ply_list_get_length (source->destinations) == 0);
|
|
|
|
ply_list_free (source->destinations);
|
|
ply_list_free (source->fd_watches);
|
|
free (source);
|
|
}
|
|
|
|
static void
|
|
ply_event_loop_update_source_event_mask (ply_event_loop_t *loop,
|
|
ply_event_source_t *source)
|
|
{
|
|
ply_list_node_t *node;
|
|
struct epoll_event event = { 0 };
|
|
int status;
|
|
|
|
assert (loop != NULL);
|
|
assert (source != NULL);
|
|
assert (source->destinations != NULL);
|
|
|
|
event.events = EPOLLERR | EPOLLHUP;
|
|
|
|
node = ply_list_get_first_node (source->destinations);
|
|
while (node != NULL)
|
|
{
|
|
ply_list_node_t *next_node;
|
|
ply_event_destination_t *destination;
|
|
|
|
destination = (ply_event_destination_t *) ply_list_node_get_data (node);
|
|
next_node = ply_list_get_next_node (source->destinations, node);
|
|
|
|
if (destination->status & PLY_EVENT_LOOP_FD_STATUS_HAS_DATA)
|
|
event.events |= EPOLLIN;
|
|
|
|
if (destination->status & PLY_EVENT_LOOP_FD_STATUS_HAS_CONTROL_DATA)
|
|
event.events |= EPOLLPRI;
|
|
|
|
if (destination->status & PLY_EVENT_LOOP_FD_STATUS_CAN_TAKE_DATA)
|
|
event.events |= EPOLLOUT;
|
|
|
|
node = next_node;
|
|
}
|
|
event.data.ptr = source;
|
|
|
|
if (source->is_getting_polled)
|
|
{
|
|
status = epoll_ctl (loop->epoll_fd, EPOLL_CTL_MOD, source->fd, &event);
|
|
}
|
|
}
|
|
|
|
static ply_fd_watch_t *
|
|
ply_event_loop_add_destination_for_source (ply_event_loop_t *loop,
|
|
ply_event_destination_t *destination,
|
|
ply_event_source_t *source)
|
|
{
|
|
ply_list_node_t *destination_node;
|
|
ply_fd_watch_t *watch;
|
|
|
|
assert (loop != NULL);
|
|
assert (destination != NULL);
|
|
assert (destination->source == NULL);
|
|
assert (source != NULL);
|
|
|
|
destination->source = source;
|
|
destination_node = ply_list_append_data (source->destinations, destination);
|
|
assert (destination_node != NULL);
|
|
assert (destination->source == source);
|
|
|
|
ply_event_loop_update_source_event_mask (loop, source);
|
|
|
|
watch = ply_fd_watch_new (destination);
|
|
|
|
ply_list_append_data (source->fd_watches, watch);
|
|
|
|
return watch;
|
|
}
|
|
|
|
static ply_event_destination_t *
|
|
ply_event_loop_get_destination_from_fd_watch (ply_event_loop_t *loop,
|
|
ply_fd_watch_t *watch)
|
|
{
|
|
ply_event_destination_t *destination;
|
|
|
|
assert (loop != NULL);
|
|
assert (watch != NULL);
|
|
assert (watch->destination != NULL);
|
|
|
|
destination = watch->destination;
|
|
|
|
return destination;
|
|
}
|
|
|
|
static void
|
|
ply_event_loop_remove_destination_by_fd_watch (ply_event_loop_t *loop,
|
|
ply_fd_watch_t *watch)
|
|
{
|
|
ply_event_destination_t *destination;
|
|
ply_event_source_t *source;
|
|
|
|
assert (loop != NULL);
|
|
assert (watch != NULL);
|
|
|
|
destination = ply_event_loop_get_destination_from_fd_watch (loop, watch);
|
|
assert (destination != NULL);
|
|
|
|
source = destination->source;
|
|
assert (source != NULL);
|
|
|
|
ply_list_remove_data (source->destinations, destination);
|
|
assert (ply_list_find_node (source->destinations, destination) == NULL);
|
|
ply_event_loop_update_source_event_mask (loop, 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);
|
|
loop->wakeup_time = PLY_EVENT_LOOP_NO_TIMED_WAKEUP;
|
|
|
|
assert (loop->epoll_fd >= 0);
|
|
|
|
loop->should_exit = false;
|
|
loop->exit_code = 0;
|
|
|
|
loop->sources = ply_list_new ();
|
|
loop->exit_closures = ply_list_new ();
|
|
loop->timeout_watches = 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_LOOP_FD_STATUS_HAS_DATA,
|
|
(ply_event_handler_t)
|
|
ply_signal_dispatcher_dispatch_signal,
|
|
(ply_event_handler_t)
|
|
ply_signal_dispatcher_reset_signal_sources,
|
|
loop->signal_dispatcher);
|
|
|
|
return loop;
|
|
}
|
|
|
|
static void
|
|
ply_event_loop_free_exit_closures (ply_event_loop_t *loop)
|
|
{
|
|
ply_list_node_t *node;
|
|
|
|
node = ply_list_get_first_node (loop->exit_closures);
|
|
while (node != NULL)
|
|
{
|
|
ply_list_node_t *next_node;
|
|
ply_event_loop_exit_closure_t *closure;
|
|
|
|
closure = (ply_event_loop_exit_closure_t *) ply_list_node_get_data (node);
|
|
next_node = ply_list_get_next_node (loop->exit_closures, node);
|
|
free (closure);
|
|
|
|
node = next_node;
|
|
}
|
|
ply_list_free (loop->exit_closures);
|
|
}
|
|
|
|
static void
|
|
ply_event_loop_run_exit_closures (ply_event_loop_t *loop)
|
|
{
|
|
ply_list_node_t *node;
|
|
|
|
node = ply_list_get_first_node (loop->exit_closures);
|
|
while (node != NULL)
|
|
{
|
|
ply_list_node_t *next_node;
|
|
ply_event_loop_exit_closure_t *closure;
|
|
|
|
closure = (ply_event_loop_exit_closure_t *) ply_list_node_get_data (node);
|
|
|
|
assert (closure->handler != NULL);
|
|
next_node = ply_list_get_next_node (loop->exit_closures, node);
|
|
|
|
closure->handler (closure->user_data, loop->exit_code, loop);
|
|
|
|
node = next_node;
|
|
}
|
|
}
|
|
|
|
void
|
|
ply_event_loop_free (ply_event_loop_t *loop)
|
|
{
|
|
if (loop == NULL)
|
|
return;
|
|
|
|
assert (ply_list_get_length (loop->sources) == 0);
|
|
assert (ply_list_get_length (loop->timeout_watches) == 0);
|
|
|
|
ply_signal_dispatcher_free (loop->signal_dispatcher);
|
|
ply_event_loop_free_exit_closures (loop);
|
|
|
|
ply_list_free (loop->sources);
|
|
ply_list_free (loop->timeout_watches);
|
|
|
|
close (loop->epoll_fd);
|
|
free (loop);
|
|
}
|
|
|
|
static 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;
|
|
}
|
|
|
|
static void
|
|
ply_event_loop_add_source (ply_event_loop_t *loop,
|
|
ply_event_source_t *source)
|
|
{
|
|
struct epoll_event event = { 0 };
|
|
int status;
|
|
|
|
assert (ply_event_loop_find_source_node (loop, source->fd) == NULL);
|
|
assert (source->is_getting_polled == false);
|
|
|
|
event.events = EPOLLERR | EPOLLHUP;
|
|
event.data.ptr = source;
|
|
|
|
status = epoll_ctl (loop->epoll_fd, EPOLL_CTL_ADD, source->fd, &event);
|
|
assert (status == 0);
|
|
|
|
source->is_getting_polled = true;
|
|
|
|
ply_list_append_data (loop->sources, source);
|
|
}
|
|
|
|
static void
|
|
ply_event_loop_remove_source_node (ply_event_loop_t *loop,
|
|
ply_list_node_t *source_node)
|
|
{
|
|
ply_event_source_t *source;
|
|
int status;
|
|
|
|
source = (ply_event_source_t *) ply_list_node_get_data (source_node);
|
|
|
|
assert (source != NULL);
|
|
|
|
if (source->is_getting_polled)
|
|
{
|
|
status = epoll_ctl (loop->epoll_fd, EPOLL_CTL_DEL, source->fd, NULL);
|
|
source->is_getting_polled = false;
|
|
}
|
|
|
|
ply_list_remove_node (loop->sources, source_node);
|
|
}
|
|
|
|
static void
|
|
ply_event_loop_remove_source (ply_event_loop_t *loop,
|
|
ply_event_source_t *source)
|
|
{
|
|
ply_list_node_t *source_node;
|
|
assert (ply_list_get_length (source->destinations) == 0);
|
|
|
|
source_node = ply_list_find_node (loop->sources, source);
|
|
|
|
assert (source_node != NULL);
|
|
|
|
ply_event_loop_remove_source_node (loop, source_node);
|
|
}
|
|
|
|
void
|
|
ply_event_loop_free_sources (ply_event_loop_t *loop)
|
|
{
|
|
ply_list_node_t *node;
|
|
|
|
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_node (loop, node);
|
|
node = next_node;
|
|
}
|
|
}
|
|
|
|
static bool
|
|
ply_event_loop_fd_status_is_valid (ply_event_loop_fd_status_t status)
|
|
{
|
|
return (status & ~(PLY_EVENT_LOOP_FD_STATUS_NONE
|
|
| PLY_EVENT_LOOP_FD_STATUS_HAS_DATA
|
|
| PLY_EVENT_LOOP_FD_STATUS_HAS_CONTROL_DATA
|
|
| PLY_EVENT_LOOP_FD_STATUS_CAN_TAKE_DATA)) == 0;
|
|
|
|
}
|
|
|
|
static ply_event_source_t *
|
|
ply_event_loop_get_source_from_fd (ply_event_loop_t *loop,
|
|
int fd)
|
|
{
|
|
ply_list_node_t *source_node;
|
|
ply_event_source_t *source;
|
|
|
|
source_node = ply_event_loop_find_source_node (loop, fd);
|
|
|
|
if (source_node == NULL)
|
|
{
|
|
source = ply_event_source_new (fd);
|
|
ply_event_loop_add_source (loop, source);
|
|
|
|
source_node = ply_list_get_last_node (loop->sources);
|
|
assert (source_node != NULL);
|
|
}
|
|
|
|
source = (ply_event_source_t *) ply_list_node_get_data (source_node);
|
|
assert (source->fd == fd);
|
|
|
|
return source;
|
|
}
|
|
|
|
ply_fd_watch_t *
|
|
ply_event_loop_watch_fd (ply_event_loop_t *loop,
|
|
int fd,
|
|
ply_event_loop_fd_status_t status,
|
|
ply_event_handler_t status_met_handler,
|
|
ply_event_handler_t disconnected_handler,
|
|
void *user_data)
|
|
{
|
|
|
|
ply_event_source_t *source;
|
|
ply_event_destination_t *destination;
|
|
ply_fd_watch_t *watch;
|
|
|
|
assert (loop != NULL);
|
|
assert (fd >= 0);
|
|
assert (ply_event_loop_fd_status_is_valid (status));
|
|
assert (status != PLY_EVENT_LOOP_FD_STATUS_NONE || status_met_handler == NULL);
|
|
|
|
source = ply_event_loop_get_source_from_fd (loop, fd);
|
|
assert (source != NULL);
|
|
|
|
destination = ply_event_destination_new (status, status_met_handler,
|
|
disconnected_handler, user_data);
|
|
watch = ply_event_loop_add_destination_for_source (loop, destination, source);
|
|
|
|
return watch;
|
|
}
|
|
|
|
void
|
|
ply_event_loop_stop_watching_fd (ply_event_loop_t *loop,
|
|
ply_fd_watch_t *watch)
|
|
{
|
|
ply_event_destination_t *destination;
|
|
ply_event_source_t *source;
|
|
|
|
destination = ply_event_loop_get_destination_from_fd_watch (loop, watch);
|
|
assert (destination != NULL);
|
|
|
|
source = destination->source;
|
|
assert (source != NULL);
|
|
assert (source->fd >= 0);
|
|
|
|
/* if we're already disconnected then the watch is already scheduled
|
|
* to be removed by ply_event_loop_disconnect_source
|
|
*/
|
|
if (source->is_disconnected)
|
|
{
|
|
ply_list_remove_data (source->fd_watches, watch);
|
|
ply_fd_watch_free (watch);
|
|
return;
|
|
}
|
|
|
|
ply_event_loop_remove_destination_by_fd_watch (loop, watch);
|
|
|
|
ply_list_remove_data (source->fd_watches, watch);
|
|
ply_fd_watch_free (watch);
|
|
ply_event_destination_free (destination);
|
|
|
|
if (ply_list_get_length (source->destinations) == 0)
|
|
{
|
|
ply_event_loop_remove_source (loop, source);
|
|
ply_event_source_free (source);
|
|
}
|
|
}
|
|
|
|
static 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);
|
|
|
|
assert (handler != NULL);
|
|
|
|
if (handler->signal_number == signal_number)
|
|
break;
|
|
|
|
node = ply_list_get_next_node (dispatcher->sources, node);
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
void
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
void
|
|
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;
|
|
|
|
ply_signal_dispatcher_remove_source_node (loop->signal_dispatcher, node);
|
|
}
|
|
|
|
void
|
|
ply_event_loop_watch_for_exit (ply_event_loop_t *loop,
|
|
ply_event_loop_exit_handler_t exit_handler,
|
|
void *user_data)
|
|
{
|
|
ply_event_loop_exit_closure_t *closure;
|
|
|
|
assert (loop != NULL);
|
|
assert (exit_handler != NULL);
|
|
|
|
closure = calloc (1, sizeof (ply_event_loop_exit_closure_t));
|
|
closure->handler = exit_handler;
|
|
closure->user_data = user_data;
|
|
|
|
ply_list_append_data (loop->exit_closures, closure);
|
|
}
|
|
|
|
void
|
|
ply_event_loop_stop_watching_for_exit (ply_event_loop_t *loop,
|
|
ply_event_loop_exit_handler_t exit_handler,
|
|
void *user_data)
|
|
{
|
|
ply_list_node_t *node;
|
|
|
|
node = ply_list_get_first_node (loop->exit_closures);
|
|
while (node != NULL)
|
|
{
|
|
ply_list_node_t *next_node;
|
|
ply_event_loop_exit_closure_t *closure;
|
|
|
|
closure = (ply_event_loop_exit_closure_t *) ply_list_node_get_data (node);
|
|
next_node = ply_list_get_next_node (loop->exit_closures, node);
|
|
|
|
if (closure->handler == exit_handler &&
|
|
closure->user_data == user_data) {
|
|
ply_list_remove_node (loop->exit_closures, node);
|
|
free (closure);
|
|
}
|
|
|
|
node = next_node;
|
|
}
|
|
}
|
|
|
|
void
|
|
ply_event_loop_watch_for_timeout (ply_event_loop_t *loop,
|
|
double seconds,
|
|
ply_event_loop_timeout_handler_t timeout_handler,
|
|
void *user_data)
|
|
{
|
|
ply_event_loop_timeout_watch_t *timeout_watch;
|
|
|
|
assert (loop != NULL);
|
|
assert (timeout_handler != NULL);
|
|
assert (seconds > 0.0);
|
|
|
|
timeout_watch = calloc (1, sizeof (ply_event_loop_timeout_watch_t));
|
|
timeout_watch->timeout = ply_get_timestamp () + seconds;
|
|
timeout_watch->handler = timeout_handler;
|
|
timeout_watch->user_data = user_data;
|
|
|
|
if (fabs (loop->wakeup_time - PLY_EVENT_LOOP_NO_TIMED_WAKEUP) <= 0)
|
|
loop->wakeup_time = timeout_watch->timeout;
|
|
else
|
|
loop->wakeup_time = MIN (loop->wakeup_time, timeout_watch->timeout);
|
|
|
|
ply_list_append_data (loop->timeout_watches, timeout_watch);
|
|
}
|
|
|
|
void
|
|
ply_event_loop_stop_watching_for_timeout (ply_event_loop_t *loop,
|
|
ply_event_loop_timeout_handler_t timeout_handler,
|
|
void *user_data)
|
|
{
|
|
ply_list_node_t *node;
|
|
|
|
node = ply_list_get_first_node (loop->timeout_watches);
|
|
while (node != NULL)
|
|
{
|
|
ply_list_node_t *next_node;
|
|
ply_event_loop_timeout_watch_t *timeout_watch;
|
|
|
|
timeout_watch = (ply_event_loop_timeout_watch_t *) ply_list_node_get_data (node);
|
|
next_node = ply_list_get_next_node (loop->timeout_watches, node);
|
|
|
|
if (timeout_watch->handler == timeout_handler &&
|
|
timeout_watch->user_data == user_data) {
|
|
ply_list_remove_node (loop->timeout_watches, node);
|
|
free (timeout_watch);
|
|
}
|
|
|
|
node = next_node;
|
|
}
|
|
}
|
|
|
|
static ply_event_loop_fd_status_t
|
|
ply_event_loop_get_fd_status_from_poll_mask (uint32_t mask)
|
|
{
|
|
ply_event_loop_fd_status_t status;
|
|
|
|
status = PLY_EVENT_LOOP_FD_STATUS_NONE;
|
|
|
|
if (mask & EPOLLIN)
|
|
status |= PLY_EVENT_LOOP_FD_STATUS_HAS_DATA;
|
|
|
|
if (mask & EPOLLPRI)
|
|
status |= PLY_EVENT_LOOP_FD_STATUS_HAS_CONTROL_DATA;
|
|
|
|
if (mask & EPOLLOUT)
|
|
status |= PLY_EVENT_LOOP_FD_STATUS_CAN_TAKE_DATA;
|
|
|
|
return status;
|
|
}
|
|
|
|
static bool
|
|
ply_event_loop_source_has_met_status (ply_event_source_t *source,
|
|
ply_event_loop_fd_status_t status)
|
|
{
|
|
ply_list_node_t *node;
|
|
|
|
assert (source != NULL);
|
|
assert (ply_event_loop_fd_status_is_valid (status));
|
|
|
|
node = ply_list_get_first_node (source->destinations);
|
|
while (node != NULL)
|
|
{
|
|
ply_list_node_t *next_node;
|
|
ply_event_destination_t *destination;
|
|
|
|
destination = (ply_event_destination_t *) ply_list_node_get_data (node);
|
|
next_node = ply_list_get_next_node (source->destinations, node);
|
|
|
|
if (((destination->status & status) != 0)
|
|
&& (destination->status_met_handler != NULL))
|
|
return true;
|
|
|
|
node = next_node;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void
|
|
ply_event_loop_handle_met_status_for_source (ply_event_loop_t *loop,
|
|
ply_event_source_t *source,
|
|
ply_event_loop_fd_status_t status)
|
|
{
|
|
ply_list_node_t *node;
|
|
|
|
assert (loop != NULL);
|
|
assert (source != NULL);
|
|
assert (ply_event_loop_fd_status_is_valid (status));
|
|
|
|
node = ply_list_get_first_node (source->destinations);
|
|
while (node != NULL)
|
|
{
|
|
ply_list_node_t *next_node;
|
|
ply_event_destination_t *destination;
|
|
|
|
destination = (ply_event_destination_t *) ply_list_node_get_data (node);
|
|
next_node = ply_list_get_next_node (source->destinations, node);
|
|
|
|
if (((destination->status & status) != 0)
|
|
&& (destination->status_met_handler != NULL))
|
|
destination->status_met_handler (destination->user_data, source->fd);
|
|
|
|
node = next_node;
|
|
}
|
|
}
|
|
|
|
static void
|
|
ply_event_loop_handle_disconnect_for_source (ply_event_loop_t *loop,
|
|
ply_event_source_t *source)
|
|
{
|
|
ply_list_node_t *node;
|
|
|
|
assert (loop != NULL);
|
|
assert (source != NULL);
|
|
|
|
source->is_disconnected = true;
|
|
node = ply_list_get_first_node (source->destinations);
|
|
while (node != NULL)
|
|
{
|
|
ply_list_node_t *next_node;
|
|
ply_event_destination_t *destination;
|
|
|
|
destination = (ply_event_destination_t *) ply_list_node_get_data (node);
|
|
next_node = ply_list_get_next_node (source->destinations, node);
|
|
|
|
if (destination->disconnected_handler != NULL)
|
|
destination->disconnected_handler (destination->user_data, source->fd);
|
|
|
|
node = next_node;
|
|
}
|
|
}
|
|
|
|
static void
|
|
ply_event_loop_free_watches_for_source (ply_event_loop_t *loop,
|
|
ply_event_source_t *source)
|
|
{
|
|
ply_list_node_t *node;
|
|
|
|
assert (loop != NULL);
|
|
assert (source != NULL);
|
|
|
|
node = ply_list_get_first_node (source->fd_watches);
|
|
while (node != NULL)
|
|
{
|
|
ply_list_node_t *next_node;
|
|
ply_fd_watch_t *watch;
|
|
|
|
next_node = ply_list_get_next_node (source->fd_watches, node);
|
|
|
|
watch = (ply_fd_watch_t *) ply_list_node_get_data (node);
|
|
|
|
assert (watch != NULL);
|
|
ply_fd_watch_free (watch);
|
|
ply_list_remove_node (source->fd_watches, node);
|
|
node = next_node;
|
|
}
|
|
}
|
|
|
|
static void
|
|
ply_event_loop_free_timeout_watches (ply_event_loop_t *loop)
|
|
{
|
|
ply_list_node_t *node;
|
|
double now;
|
|
|
|
assert (loop != NULL);
|
|
|
|
now = ply_get_timestamp ();
|
|
node = ply_list_get_first_node (loop->timeout_watches);
|
|
while (node != NULL)
|
|
{
|
|
ply_list_node_t *next_node;
|
|
ply_event_loop_timeout_watch_t *watch;
|
|
|
|
watch = (ply_event_loop_timeout_watch_t *) ply_list_node_get_data (node);
|
|
next_node = ply_list_get_next_node (loop->timeout_watches, node);
|
|
|
|
free (watch);
|
|
ply_list_remove_node (loop->timeout_watches, node);
|
|
|
|
node = next_node;
|
|
}
|
|
|
|
assert (ply_list_get_length (loop->timeout_watches) == 0);
|
|
loop->wakeup_time = PLY_EVENT_LOOP_NO_TIMED_WAKEUP;
|
|
}
|
|
|
|
static void
|
|
ply_event_loop_free_destinations_for_source (ply_event_loop_t *loop,
|
|
ply_event_source_t *source)
|
|
{
|
|
ply_list_node_t *node;
|
|
|
|
assert (loop != NULL);
|
|
assert (source != NULL);
|
|
|
|
node = ply_list_get_first_node (source->destinations);
|
|
while (node != NULL)
|
|
{
|
|
ply_list_node_t *next_node;
|
|
ply_event_destination_t *destination;
|
|
|
|
next_node = ply_list_get_next_node (source->destinations, node);
|
|
|
|
destination =
|
|
(ply_event_destination_t *) ply_list_node_get_data (node);
|
|
|
|
assert (destination != NULL);
|
|
ply_event_destination_free (destination);
|
|
ply_list_remove_node (source->destinations, node);
|
|
node = next_node;
|
|
}
|
|
}
|
|
|
|
static void
|
|
ply_event_loop_disconnect_source (ply_event_loop_t *loop,
|
|
ply_event_source_t *source)
|
|
{
|
|
ply_event_loop_handle_disconnect_for_source (loop, source);
|
|
|
|
/* at this point, we've told the event loop users about the
|
|
* fd disconnection, so we can invalidate any outstanding
|
|
* watches and free the destinations.
|
|
*/
|
|
ply_event_loop_free_watches_for_source (loop, source);
|
|
ply_event_loop_free_destinations_for_source (loop, source);
|
|
assert (ply_list_get_length (source->destinations) == 0);
|
|
|
|
ply_event_loop_remove_source (loop, source);
|
|
ply_event_source_free (source);
|
|
}
|
|
|
|
static void
|
|
ply_event_loop_handle_timeouts (ply_event_loop_t *loop)
|
|
{
|
|
ply_list_node_t *node;
|
|
double now;
|
|
|
|
assert (loop != NULL);
|
|
|
|
now = ply_get_timestamp ();
|
|
node = ply_list_get_first_node (loop->timeout_watches);
|
|
while (node != NULL)
|
|
{
|
|
ply_list_node_t *next_node;
|
|
ply_event_loop_timeout_watch_t *watch;
|
|
|
|
watch = (ply_event_loop_timeout_watch_t *) ply_list_node_get_data (node);
|
|
next_node = ply_list_get_next_node (loop->timeout_watches, node);
|
|
|
|
if (watch->timeout <= now)
|
|
{
|
|
assert (watch->handler != NULL);
|
|
watch->handler (watch->user_data, loop);
|
|
free (watch);
|
|
ply_list_remove_node (loop->timeout_watches, node);
|
|
}
|
|
|
|
node = next_node;
|
|
}
|
|
|
|
if (ply_list_get_length (loop->timeout_watches) == 0)
|
|
loop->wakeup_time = PLY_EVENT_LOOP_NO_TIMED_WAKEUP;
|
|
}
|
|
|
|
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;
|
|
|
|
assert (loop != NULL);
|
|
|
|
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
|
|
{
|
|
int timeout;
|
|
|
|
if (fabs (loop->wakeup_time - PLY_EVENT_LOOP_NO_TIMED_WAKEUP) <= 0)
|
|
timeout = -1;
|
|
else
|
|
{
|
|
timeout = (int) ((loop->wakeup_time - ply_get_timestamp ()) * 1000);
|
|
timeout = MAX (timeout, 0);
|
|
}
|
|
|
|
number_of_received_events = epoll_wait (loop->epoll_fd, events,
|
|
sizeof (events), timeout);
|
|
|
|
ply_event_loop_handle_timeouts (loop);
|
|
|
|
if (number_of_received_events < 0)
|
|
{
|
|
if (errno != EINTR)
|
|
{
|
|
ply_event_loop_exit (loop, 255);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
while ((number_of_received_events < 0) && (errno == EINTR));
|
|
|
|
for (i = 0; i < number_of_received_events; i++)
|
|
{
|
|
ply_event_source_t *source;
|
|
ply_event_loop_fd_status_t status;
|
|
bool is_disconnected;
|
|
|
|
source = (ply_event_source_t *) (events[i].data.ptr);
|
|
status = ply_event_loop_get_fd_status_from_poll_mask (events[i].events);
|
|
|
|
is_disconnected = false;
|
|
if ((events[i].events & EPOLLHUP) || (events[i].events & EPOLLERR))
|
|
{
|
|
int bytes_ready;
|
|
|
|
bytes_ready = 0;
|
|
if (ioctl (source->fd, FIONREAD, &bytes_ready) < 0)
|
|
bytes_ready = 0;
|
|
|
|
if (bytes_ready <= 0)
|
|
is_disconnected = true;
|
|
}
|
|
|
|
if (is_disconnected)
|
|
{
|
|
source->is_getting_polled = false;
|
|
ply_event_loop_disconnect_source (loop, source);
|
|
}
|
|
else if (ply_event_loop_source_has_met_status (source, status))
|
|
ply_event_loop_handle_met_status_for_source (loop, source, status);
|
|
|
|
if (loop->should_exit)
|
|
break;
|
|
}
|
|
}
|
|
|
|
void
|
|
ply_event_loop_exit (ply_event_loop_t *loop,
|
|
int exit_code)
|
|
{
|
|
assert (loop != NULL);
|
|
|
|
loop->should_exit = true;
|
|
loop->exit_code = exit_code;
|
|
}
|
|
|
|
int
|
|
ply_event_loop_run (ply_event_loop_t *loop)
|
|
{
|
|
while (!loop->should_exit)
|
|
ply_event_loop_process_pending_events (loop);
|
|
|
|
ply_event_loop_run_exit_closures (loop);
|
|
ply_event_loop_free_sources (loop);
|
|
ply_event_loop_free_timeout_watches (loop);
|
|
|
|
loop->should_exit = false;
|
|
|
|
return loop->exit_code;
|
|
}
|
|
|
|
#ifdef PLY_EVENT_LOOP_ENABLE_TEST
|
|
|
|
static ply_event_loop_t *loop;
|
|
|
|
static void
|
|
alrm_signal_handler (void)
|
|
{
|
|
write (1, "times up!\n", sizeof ("times up!\n") - 1);
|
|
ply_event_loop_exit (loop, 0);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
static void
|
|
on_timeout (ply_event_loop_t *loop)
|
|
{
|
|
printf ("timeout elapsed\n");
|
|
}
|
|
|
|
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_signal (loop, SIGALRM,
|
|
(ply_event_handler_t)
|
|
alrm_signal_handler, NULL);
|
|
|
|
ply_event_loop_watch_for_timeout (loop, 2.0,
|
|
(ply_event_loop_timeout_handler_t)
|
|
on_timeout, loop);
|
|
ply_event_loop_watch_fd (loop, 0, PLY_EVENT_LOOP_FD_STATUS_HAS_DATA,
|
|
(ply_event_handler_t) line_received_handler,
|
|
(ply_event_handler_t) line_received_handler,
|
|
NULL);
|
|
|
|
alarm (5);
|
|
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: */
|