/src/google/protobuf-c/protobuf-c-dispatch.c
C | 910 lines | 760 code | 72 blank | 78 comment | 133 complexity | 2b2ea235aefd70d4b07b94a7c44d74f5 MD5 | raw file
Possible License(s): BSD-3-Clause
- /*
- * Copyright (c) 2008-2011, Dave Benson.
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with
- * or without modification, are permitted provided that the
- * following conditions are met:
- *
- * Redistributions of source code must retain the above
- * copyright notice, this list of conditions and the following
- * disclaimer.
- * Redistributions in binary form must reproduce
- * the above copyright notice, this list of conditions and
- * the following disclaimer in the documentation and/or other
- * materials provided with the distribution.
- *
- * Neither the name
- * of "protobuf-c" nor the names of its contributors
- * may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
- * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
- * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
- * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
- * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
- /* NOTE: this may not work very well on windows, where i'm
- not sure that "SOCKETs" are allocated nicely like
- file-descriptors are */
- /* TODO:
- * * epoll() implementation
- * * kqueue() implementation
- * * windows port (yeah, right, volunteers are DEFINITELY needed for this one...)
- */
- #if HAVE_PROTOBUF_C_CONFIG_H
- #include "protobuf-c-config.h"
- #endif
- #include <assert.h>
- #if HAVE_ALLOCA_H
- # include <alloca.h>
- #elif HAVE_MALLOC_H
- # include <malloc.h>
- #endif
- #include <sys/time.h>
- #include <unistd.h>
- #include <string.h>
- #include <stdio.h>
- #if HAVE_SYS_POLL_H
- # include <sys/poll.h>
- # define USE_POLL 1
- #elif HAVE_SYS_SELECT_H
- # include <sys/select.h>
- # define USE_POLL 0
- #endif
- /* windows annoyances: use select, use a full-fledges map for fds */
- #ifdef WIN32
- # include <winsock.h>
- # define USE_POLL 0
- # define HAVE_SMALL_FDS 0
- #endif
- #include <limits.h>
- #include <errno.h>
- #include <signal.h>
- #include "protobuf-c-dispatch.h"
- #include "gskrbtreemacros.h"
- #include "gsklistmacros.h"
- #define DEBUG_DISPATCH_INTERNALS 0
- #define DEBUG_DISPATCH 0
- #ifndef HAVE_SMALL_FDS
- # define HAVE_SMALL_FDS 1
- #endif
- #define protobuf_c_assert(condition) assert(condition)
- #define ALLOC_WITH_ALLOCATOR(allocator, size) ((allocator)->alloc ((allocator)->allocator_data, (size)))
- #define FREE_WITH_ALLOCATOR(allocator, ptr) ((allocator)->free ((allocator)->allocator_data, (ptr)))
- /* macros that assume you have a ProtobufCAllocator* named
- allocator in scope */
- #define ALLOC(size) ALLOC_WITH_ALLOCATOR((allocator), size)
- #define FREE(ptr) FREE_WITH_ALLOCATOR((allocator), ptr)
- typedef struct _Callback Callback;
- struct _Callback
- {
- ProtobufCDispatchCallback func;
- void *data;
- };
- typedef struct _FDMap FDMap;
- struct _FDMap
- {
- int notify_desired_index; /* -1 if not an known fd */
- int change_index; /* -1 if no prior change */
- int closed_since_notify_started;
- };
- #if !HAVE_SMALL_FDS
- typedef struct _FDMapNode FDMapNode;
- struct _FDMapNode
- {
- ProtobufC_FD fd;
- FDMapNode *left, *right, *parent;
- protobuf_c_boolean is_red;
- FDMap map;
- };
- #endif
- typedef struct _RealDispatch RealDispatch;
- struct _RealDispatch
- {
- ProtobufCDispatch base;
- Callback *callbacks; /* parallels notifies_desired */
- size_t notifies_desired_alloced;
- size_t changes_alloced;
- #if HAVE_SMALL_FDS
- FDMap *fd_map; /* map indexed by fd */
- size_t fd_map_size; /* number of elements of fd_map */
- #else
- FDMapNode *fd_map_tree; /* map indexed by fd */
- #endif
- protobuf_c_boolean is_dispatching;
- ProtobufCDispatchTimer *timer_tree;
- ProtobufCAllocator *allocator;
- ProtobufCDispatchTimer *recycled_timeouts;
- ProtobufCDispatchIdle *first_idle, *last_idle;
- ProtobufCDispatchIdle *recycled_idles;
- };
- struct _ProtobufCDispatchTimer
- {
- RealDispatch *dispatch;
- /* the actual timeout time */
- unsigned long timeout_secs;
- unsigned timeout_usecs;
- /* red-black tree stuff */
- ProtobufCDispatchTimer *left, *right, *parent;
- protobuf_c_boolean is_red;
- /* user callback */
- ProtobufCDispatchTimerFunc func;
- void *func_data;
- };
- struct _ProtobufCDispatchIdle
- {
- RealDispatch *dispatch;
- ProtobufCDispatchIdle *prev, *next;
- /* user callback */
- ProtobufCDispatchIdleFunc func;
- void *func_data;
- };
- /* Define the tree of timers, as per gskrbtreemacros.h */
- #define TIMER_GET_IS_RED(n) ((n)->is_red)
- #define TIMER_SET_IS_RED(n,v) ((n)->is_red = (v))
- #define TIMERS_COMPARE(a,b, rv) \
- if (a->timeout_secs < b->timeout_secs) rv = -1; \
- else if (a->timeout_secs > b->timeout_secs) rv = 1; \
- else if (a->timeout_usecs < b->timeout_usecs) rv = -1; \
- else if (a->timeout_usecs > b->timeout_usecs) rv = 1; \
- else if (a < b) rv = -1; \
- else if (a > b) rv = 1; \
- else rv = 0;
- #define GET_TIMER_TREE(d) \
- (d)->timer_tree, ProtobufCDispatchTimer *, \
- TIMER_GET_IS_RED, TIMER_SET_IS_RED, \
- parent, left, right, \
- TIMERS_COMPARE
- #if !HAVE_SMALL_FDS
- #define FD_MAP_NODES_COMPARE(a,b, rv) \
- if (a->fd < b->fd) rv = -1; \
- else if (a->fd > b->fd) rv = 1; \
- else rv = 0;
- #define GET_FD_MAP_TREE(d) \
- (d)->fd_map_tree, FDMapNode *, \
- TIMER_GET_IS_RED, TIMER_SET_IS_RED, \
- parent, left, right, \
- FD_MAP_NODES_COMPARE
- #define COMPARE_FD_TO_FD_MAP_NODE(a,b, rv) \
- if (a < b->fd) rv = -1; \
- else if (a > b->fd) rv = 1; \
- else rv = 0;
- #endif
- /* declare the idle-handler list */
- #define GET_IDLE_LIST(d) \
- ProtobufCDispatchIdle *, d->first_idle, d->last_idle, prev, next
- /* Create or destroy a Dispatch */
- ProtobufCDispatch *protobuf_c_dispatch_new (ProtobufCAllocator *allocator)
- {
- RealDispatch *rv = ALLOC (sizeof (RealDispatch));
- struct timeval tv;
- rv->base.n_changes = 0;
- rv->notifies_desired_alloced = 8;
- rv->base.notifies_desired = ALLOC (sizeof (ProtobufC_FDNotify) * rv->notifies_desired_alloced);
- rv->base.n_notifies_desired = 0;
- rv->callbacks = ALLOC (sizeof (Callback) * rv->notifies_desired_alloced);
- rv->changes_alloced = 8;
- rv->base.changes = ALLOC (sizeof (ProtobufC_FDNotifyChange) * rv->changes_alloced);
- #if HAVE_SMALL_FDS
- rv->fd_map_size = 16;
- rv->fd_map = ALLOC (sizeof (FDMap) * rv->fd_map_size);
- memset (rv->fd_map, 255, sizeof (FDMap) * rv->fd_map_size);
- #else
- rv->fd_map_tree = NULL;
- #endif
- rv->allocator = allocator;
- rv->timer_tree = NULL;
- rv->first_idle = rv->last_idle = NULL;
- rv->base.has_idle = 0;
- rv->recycled_idles = NULL;
- rv->recycled_timeouts = NULL;
- rv->is_dispatching = 0;
- /* need to handle SIGPIPE more gracefully than default */
- signal (SIGPIPE, SIG_IGN);
- gettimeofday (&tv, NULL);
- rv->base.last_dispatch_secs = tv.tv_sec;
- rv->base.last_dispatch_usecs = tv.tv_usec;
- return &rv->base;
- }
- #if !HAVE_SMALL_FDS
- void free_fd_tree_recursive (ProtobufCAllocator *allocator,
- FDMapNode *node)
- {
- if (node)
- {
- free_fd_tree_recursive (allocator, node->left);
- free_fd_tree_recursive (allocator, node->right);
- FREE (node);
- }
- }
- #endif
- /* XXX: leaking timer_tree seemingly? */
- void
- protobuf_c_dispatch_free(ProtobufCDispatch *dispatch)
- {
- RealDispatch *d = (RealDispatch *) dispatch;
- ProtobufCAllocator *allocator = d->allocator;
- while (d->recycled_timeouts != NULL)
- {
- ProtobufCDispatchTimer *t = d->recycled_timeouts;
- d->recycled_timeouts = t->right;
- FREE (t);
- }
- while (d->recycled_idles != NULL)
- {
- ProtobufCDispatchIdle *i = d->recycled_idles;
- d->recycled_idles = i->next;
- FREE (i);
- }
- FREE (d->base.notifies_desired);
- FREE (d->base.changes);
- FREE (d->callbacks);
- #if HAVE_SMALL_FDS
- FREE (d->fd_map);
- #else
- free_fd_tree_recursive (allocator, d->fd_map_tree);
- #endif
- FREE (d);
- }
- ProtobufCAllocator *
- protobuf_c_dispatch_peek_allocator (ProtobufCDispatch *dispatch)
- {
- RealDispatch *d = (RealDispatch *) dispatch;
- return d->allocator;
- }
- /* TODO: perhaps thread-private dispatches make more sense? */
- static ProtobufCDispatch *def = NULL;
- ProtobufCDispatch *protobuf_c_dispatch_default (void)
- {
- if (def == NULL)
- def = protobuf_c_dispatch_new (&protobuf_c_default_allocator);
- return def;
- }
- #if HAVE_SMALL_FDS
- static void
- enlarge_fd_map (RealDispatch *d,
- unsigned fd)
- {
- size_t new_size = d->fd_map_size * 2;
- FDMap *new_map;
- ProtobufCAllocator *allocator = d->allocator;
- while (fd >= new_size)
- new_size *= 2;
- new_map = ALLOC (sizeof (FDMap) * new_size);
- memcpy (new_map, d->fd_map, d->fd_map_size * sizeof (FDMap));
- memset (new_map + d->fd_map_size,
- 255,
- sizeof (FDMap) * (new_size - d->fd_map_size));
- FREE (d->fd_map);
- d->fd_map = new_map;
- d->fd_map_size = new_size;
- }
- static inline void
- ensure_fd_map_big_enough (RealDispatch *d,
- unsigned fd)
- {
- if (fd >= d->fd_map_size)
- enlarge_fd_map (d, fd);
- }
- #endif
- static unsigned
- allocate_notifies_desired_index (RealDispatch *d)
- {
- unsigned rv = d->base.n_notifies_desired++;
- ProtobufCAllocator *allocator = d->allocator;
- if (rv == d->notifies_desired_alloced)
- {
- unsigned new_size = d->notifies_desired_alloced * 2;
- ProtobufC_FDNotify *n = ALLOC (new_size * sizeof (ProtobufC_FDNotify));
- Callback *c = ALLOC (new_size * sizeof (Callback));
- memcpy (n, d->base.notifies_desired, d->notifies_desired_alloced * sizeof (ProtobufC_FDNotify));
- FREE (d->base.notifies_desired);
- memcpy (c, d->callbacks, d->notifies_desired_alloced * sizeof (Callback));
- FREE (d->callbacks);
- d->base.notifies_desired = n;
- d->callbacks = c;
- d->notifies_desired_alloced = new_size;
- }
- #if DEBUG_DISPATCH_INTERNALS
- fprintf (stderr, "allocate_notifies_desired_index: returning %u\n", rv);
- #endif
- return rv;
- }
- static unsigned
- allocate_change_index (RealDispatch *d)
- {
- unsigned rv = d->base.n_changes++;
- if (rv == d->changes_alloced)
- {
- ProtobufCAllocator *allocator = d->allocator;
- unsigned new_size = d->changes_alloced * 2;
- ProtobufC_FDNotifyChange *n = ALLOC (new_size * sizeof (ProtobufC_FDNotifyChange));
- memcpy (n, d->base.changes, d->changes_alloced * sizeof (ProtobufC_FDNotifyChange));
- FREE (d->base.changes);
- d->base.changes = n;
- d->changes_alloced = new_size;
- }
- return rv;
- }
- static inline FDMap *
- get_fd_map (RealDispatch *d, ProtobufC_FD fd)
- {
- #if HAVE_SMALL_FDS
- if ((unsigned)fd >= d->fd_map_size)
- return NULL;
- else
- return d->fd_map + fd;
- #else
- FDMapNode *node;
- GSK_RBTREE_LOOKUP_COMPARATOR (GET_FD_MAP_TREE (d), fd, COMPARE_FD_TO_FD_MAP_NODE, node);
- return node ? &node->map : NULL;
- #endif
- }
- static inline FDMap *
- force_fd_map (RealDispatch *d, ProtobufC_FD fd)
- {
- #if HAVE_SMALL_FDS
- ensure_fd_map_big_enough (d, fd);
- return d->fd_map + fd;
- #else
- {
- FDMap *fm = get_fd_map (d, fd);
- ProtobufCAllocator *allocator = d->allocator;
- if (fm == NULL)
- {
- FDMapNode *node = ALLOC (sizeof (FDMapNode));
- FDMapNode *conflict;
- node->fd = fd;
- memset (&node->map, 255, sizeof (FDMap));
- GSK_RBTREE_INSERT (GET_FD_MAP_TREE (d), node, conflict);
- assert (conflict == NULL);
- fm = &node->map;
- }
- return fm;
- }
- #endif
- }
- static void
- deallocate_change_index (RealDispatch *d,
- FDMap *fm)
- {
- unsigned ch_ind = fm->change_index;
- unsigned from = d->base.n_changes - 1;
- ProtobufC_FD from_fd;
- fm->change_index = -1;
- if (ch_ind == from)
- {
- d->base.n_changes--;
- return;
- }
- from_fd = d->base.changes[ch_ind].fd;
- get_fd_map (d, from_fd)->change_index = ch_ind;
- d->base.changes[ch_ind] = d->base.changes[from];
- d->base.n_changes--;
- }
- static void
- deallocate_notify_desired_index (RealDispatch *d,
- ProtobufC_FD fd,
- FDMap *fm)
- {
- unsigned nd_ind = fm->notify_desired_index;
- unsigned from = d->base.n_notifies_desired - 1;
- ProtobufC_FD from_fd;
- (void) fd;
- #if DEBUG_DISPATCH_INTERNALS
- fprintf (stderr, "deallocate_notify_desired_index: fd=%d, nd_ind=%u\n",fd,nd_ind);
- #endif
- fm->notify_desired_index = -1;
- if (nd_ind == from)
- {
- d->base.n_notifies_desired--;
- return;
- }
- from_fd = d->base.notifies_desired[from].fd;
- get_fd_map (d, from_fd)->notify_desired_index = nd_ind;
- d->base.notifies_desired[nd_ind] = d->base.notifies_desired[from];
- d->callbacks[nd_ind] = d->callbacks[from];
- d->base.n_notifies_desired--;
- }
- /* Registering file-descriptors to watch. */
- void
- protobuf_c_dispatch_watch_fd (ProtobufCDispatch *dispatch,
- ProtobufC_FD fd,
- unsigned events,
- ProtobufCDispatchCallback callback,
- void *callback_data)
- {
- RealDispatch *d = (RealDispatch *) dispatch;
- unsigned f = fd; /* avoid tiring compiler warnings: "comparison of signed versus unsigned" */
- unsigned nd_ind, change_ind;
- unsigned old_events;
- FDMap *fm;
- #if DEBUG_DISPATCH
- fprintf (stderr, "dispatch: watch_fd: %d, %s%s\n",
- fd,
- (events&PROTOBUF_C_EVENT_READABLE)?"r":"",
- (events&PROTOBUF_C_EVENT_WRITABLE)?"w":"");
- #endif
- if (callback == NULL)
- assert (events == 0);
- else
- assert (events != 0);
- fm = force_fd_map (d, f);
- /* XXX: should we set fm->map.closed_since_notify_started=0 ??? */
- if (fm->notify_desired_index == -1)
- {
- if (callback != NULL)
- nd_ind = fm->notify_desired_index = allocate_notifies_desired_index (d);
- old_events = 0;
- }
- else
- {
- old_events = dispatch->notifies_desired[fm->notify_desired_index].events;
- if (callback == NULL)
- deallocate_notify_desired_index (d, fd, fm);
- else
- nd_ind = fm->notify_desired_index;
- }
- if (callback == NULL)
- {
- if (fm->change_index == -1)
- {
- change_ind = fm->change_index = allocate_change_index (d);
- dispatch->changes[change_ind].old_events = old_events;
- }
- else
- change_ind = fm->change_index;
- d->base.changes[change_ind].fd = f;
- d->base.changes[change_ind].events = 0;
- return;
- }
- assert (callback != NULL && events != 0);
- if (fm->change_index == -1)
- {
- change_ind = fm->change_index = allocate_change_index (d);
- dispatch->changes[change_ind].old_events = old_events;
- }
- else
- change_ind = fm->change_index;
- d->base.changes[change_ind].fd = fd;
- d->base.changes[change_ind].events = events;
- d->base.notifies_desired[nd_ind].fd = fd;
- d->base.notifies_desired[nd_ind].events = events;
- d->callbacks[nd_ind].func = callback;
- d->callbacks[nd_ind].data = callback_data;
- }
- void
- protobuf_c_dispatch_close_fd (ProtobufCDispatch *dispatch,
- ProtobufC_FD fd)
- {
- protobuf_c_dispatch_fd_closed (dispatch, fd);
- close (fd);
- }
- void
- protobuf_c_dispatch_fd_closed(ProtobufCDispatch *dispatch,
- ProtobufC_FD fd)
- {
- RealDispatch *d = (RealDispatch *) dispatch;
- FDMap *fm;
- #if DEBUG_DISPATCH
- fprintf (stderr, "dispatch: fd %d closed\n", fd);
- #endif
- fm = force_fd_map (d, fd);
- fm->closed_since_notify_started = 1;
- if (fm->change_index != -1)
- deallocate_change_index (d, fm);
- if (fm->notify_desired_index != -1)
- deallocate_notify_desired_index (d, fd, fm);
- }
- static void
- free_timer (ProtobufCDispatchTimer *timer)
- {
- RealDispatch *d = timer->dispatch;
- timer->right = d->recycled_timeouts;
- d->recycled_timeouts = timer;
- }
- void
- protobuf_c_dispatch_dispatch (ProtobufCDispatch *dispatch,
- size_t n_notifies,
- ProtobufC_FDNotify *notifies)
- {
- RealDispatch *d = (RealDispatch *) dispatch;
- unsigned fd_max;
- unsigned i;
- struct timeval tv;
- /* Re-entrancy guard. If this is triggerred, then
- you are calling protobuf_c_dispatch_dispatch (or _run)
- from a callback function. That's not allowed. */
- protobuf_c_assert (!d->is_dispatching);
- d->is_dispatching = 1;
- gettimeofday (&tv, NULL);
- dispatch->last_dispatch_secs = tv.tv_sec;
- dispatch->last_dispatch_usecs = tv.tv_usec;
- fd_max = 0;
- for (i = 0; i < n_notifies; i++)
- if (fd_max < (unsigned) notifies[i].fd)
- fd_max = notifies[i].fd;
- ensure_fd_map_big_enough (d, fd_max);
- for (i = 0; i < n_notifies; i++)
- d->fd_map[notifies[i].fd].closed_since_notify_started = 0;
- for (i = 0; i < n_notifies; i++)
- {
- unsigned fd = notifies[i].fd;
- if (!d->fd_map[fd].closed_since_notify_started
- && d->fd_map[fd].notify_desired_index != -1)
- {
- unsigned nd_ind = d->fd_map[fd].notify_desired_index;
- unsigned events = d->base.notifies_desired[nd_ind].events & notifies[i].events;
- if (events != 0)
- d->callbacks[nd_ind].func (fd, events, d->callbacks[nd_ind].data);
- }
- }
- /* clear changes */
- for (i = 0; i < dispatch->n_changes; i++)
- d->fd_map[dispatch->changes[i].fd].change_index = -1;
- dispatch->n_changes = 0;
- /* handle idle functions */
- while (d->first_idle != NULL)
- {
- ProtobufCDispatchIdle *idle = d->first_idle;
- ProtobufCDispatchIdleFunc func = idle->func;
- void *data = idle->func_data;
- GSK_LIST_REMOVE_FIRST (GET_IDLE_LIST (d));
- idle->func = NULL; /* set to NULL to render remove_idle a no-op */
- func (dispatch, data);
- idle->next = d->recycled_idles;
- d->recycled_idles = idle;
- }
- dispatch->has_idle = 0;
- /* handle timers */
- while (d->timer_tree != NULL)
- {
- ProtobufCDispatchTimer *min_timer;
- GSK_RBTREE_FIRST (GET_TIMER_TREE (d), min_timer);
- if (min_timer->timeout_secs < (unsigned long) tv.tv_sec
- || (min_timer->timeout_secs == (unsigned long) tv.tv_sec
- && min_timer->timeout_usecs <= (unsigned) tv.tv_usec))
- {
- ProtobufCDispatchTimerFunc func = min_timer->func;
- void *func_data = min_timer->func_data;
- GSK_RBTREE_REMOVE (GET_TIMER_TREE (d), min_timer);
- /* Set to NULL as a way to tell protobuf_c_dispatch_remove_timer()
- that we are in the middle of notifying */
- min_timer->func = NULL;
- min_timer->func_data = NULL;
- func (&d->base, func_data);
- free_timer (min_timer);
- }
- else
- {
- d->base.has_timeout = 1;
- d->base.timeout_secs = min_timer->timeout_secs;
- d->base.timeout_usecs = min_timer->timeout_usecs;
- break;
- }
- }
- if (d->timer_tree == NULL)
- d->base.has_timeout = 0;
- /* Finish reentrance guard. */
- d->is_dispatching = 0;
- }
- void
- protobuf_c_dispatch_clear_changes (ProtobufCDispatch *dispatch)
- {
- RealDispatch *d = (RealDispatch *) dispatch;
- unsigned i;
- for (i = 0; i < dispatch->n_changes; i++)
- {
- FDMap *fm = get_fd_map (d, dispatch->changes[i].fd);
- assert (fm->change_index == (int) i);
- fm->change_index = -1;
- }
- dispatch->n_changes = 0;
- }
- static inline unsigned
- events_to_pollfd_events (unsigned ev)
- {
- return ((ev & PROTOBUF_C_EVENT_READABLE) ? POLLIN : 0)
- | ((ev & PROTOBUF_C_EVENT_WRITABLE) ? POLLOUT : 0)
- ;
- }
- static inline unsigned
- pollfd_events_to_events (unsigned ev)
- {
- return ((ev & (POLLIN|POLLHUP)) ? PROTOBUF_C_EVENT_READABLE : 0)
- | ((ev & POLLOUT) ? PROTOBUF_C_EVENT_WRITABLE : 0)
- ;
- }
- void
- protobuf_c_dispatch_run (ProtobufCDispatch *dispatch)
- {
- struct pollfd *fds;
- void *to_free = NULL, *to_free2 = NULL;
- size_t n_events;
- RealDispatch *d = (RealDispatch *) dispatch;
- ProtobufCAllocator *allocator = d->allocator;
- unsigned i;
- int timeout;
- ProtobufC_FDNotify *events;
- if (dispatch->n_notifies_desired < 128)
- fds = alloca (sizeof (struct pollfd) * dispatch->n_notifies_desired);
- else
- to_free = fds = ALLOC (sizeof (struct pollfd) * dispatch->n_notifies_desired);
- for (i = 0; i < dispatch->n_notifies_desired; i++)
- {
- fds[i].fd = dispatch->notifies_desired[i].fd;
- fds[i].events = events_to_pollfd_events (dispatch->notifies_desired[i].events);
- fds[i].revents = 0;
- }
- /* compute timeout */
- if (dispatch->has_idle)
- timeout = 0;
- else if (!dispatch->has_timeout)
- timeout = -1;
- else
- {
- struct timeval tv;
- gettimeofday (&tv, NULL);
- if (dispatch->timeout_secs < (unsigned long) tv.tv_sec
- || (dispatch->timeout_secs == (unsigned long) tv.tv_sec
- && dispatch->timeout_usecs <= (unsigned) tv.tv_usec))
- timeout = 0;
- else
- {
- int du = dispatch->timeout_usecs - tv.tv_usec;
- int ds = dispatch->timeout_secs - tv.tv_sec;
- if (du < 0)
- {
- du += 1000000;
- ds -= 1;
- }
- if (ds > INT_MAX / 1000)
- timeout = INT_MAX / 1000 * 1000;
- else
- /* Round up, so that we ensure that something can run
- if they just wait the full duration */
- timeout = ds * 1000 + (du + 999) / 1000;
- }
- }
- if (poll (fds, dispatch->n_notifies_desired, timeout) < 0)
- {
- if (errno == EINTR)
- return; /* probably a signal interrupted the poll-- let the user have control */
- /* i don't really know what would plausibly cause this */
- fprintf (stderr, "error polling: %s\n", strerror (errno));
- return;
- }
- n_events = 0;
- for (i = 0; i < dispatch->n_notifies_desired; i++)
- if (fds[i].revents)
- n_events++;
- if (n_events < 128)
- events = alloca (sizeof (ProtobufC_FDNotify) * n_events);
- else
- to_free2 = events = ALLOC (sizeof (ProtobufC_FDNotify) * n_events);
- n_events = 0;
- for (i = 0; i < dispatch->n_notifies_desired; i++)
- if (fds[i].revents)
- {
- events[n_events].fd = fds[i].fd;
- events[n_events].events = pollfd_events_to_events (fds[i].revents);
- /* note that we may actually wind up with fewer events
- now that we actually call pollfd_events_to_events() */
- if (events[n_events].events != 0)
- n_events++;
- }
- protobuf_c_dispatch_dispatch (dispatch, n_events, events);
- if (to_free)
- FREE (to_free);
- if (to_free2)
- FREE (to_free2);
- }
- ProtobufCDispatchTimer *
- protobuf_c_dispatch_add_timer(ProtobufCDispatch *dispatch,
- unsigned timeout_secs,
- unsigned timeout_usecs,
- ProtobufCDispatchTimerFunc func,
- void *func_data)
- {
- RealDispatch *d = (RealDispatch *) dispatch;
- ProtobufCDispatchTimer *rv;
- ProtobufCDispatchTimer *at;
- ProtobufCDispatchTimer *conflict;
- protobuf_c_assert (func != NULL);
- if (d->recycled_timeouts != NULL)
- {
- rv = d->recycled_timeouts;
- d->recycled_timeouts = rv->right;
- }
- else
- {
- rv = d->allocator->alloc (d->allocator, sizeof (ProtobufCDispatchTimer));
- }
- rv->timeout_secs = timeout_secs;
- rv->timeout_usecs = timeout_usecs;
- rv->func = func;
- rv->func_data = func_data;
- rv->dispatch = d;
- GSK_RBTREE_INSERT (GET_TIMER_TREE (d), rv, conflict);
-
- /* is this the first element in the tree */
- for (at = rv; at != NULL; at = at->parent)
- if (at->parent && at->parent->right == at)
- break;
- if (at == NULL) /* yes, so set the public members */
- {
- dispatch->has_timeout = 1;
- dispatch->timeout_secs = rv->timeout_secs;
- dispatch->timeout_usecs = rv->timeout_usecs;
- }
- return rv;
- }
- ProtobufCDispatchTimer *
- protobuf_c_dispatch_add_timer_millis
- (ProtobufCDispatch *dispatch,
- unsigned millis,
- ProtobufCDispatchTimerFunc func,
- void *func_data)
- {
- unsigned tsec = dispatch->last_dispatch_secs;
- unsigned tusec = dispatch->last_dispatch_usecs;
- tusec += 1000 * (millis % 1000);
- tsec += millis / 1000;
- if (tusec >= 1000*1000)
- {
- tusec -= 1000*1000;
- tsec += 1;
- }
- return protobuf_c_dispatch_add_timer (dispatch, tsec, tusec, func, func_data);
- }
- void protobuf_c_dispatch_remove_timer (ProtobufCDispatchTimer *timer)
- {
- protobuf_c_boolean may_be_first;
- RealDispatch *d = timer->dispatch;
- /* ignore mid-notify removal */
- if (timer->func == NULL)
- return;
- may_be_first = d->base.timeout_usecs == timer->timeout_usecs
- && d->base.timeout_secs == timer->timeout_secs;
- GSK_RBTREE_REMOVE (GET_TIMER_TREE (d), timer);
- if (may_be_first)
- {
- if (d->timer_tree == NULL)
- d->base.has_timeout = 0;
- else
- {
- ProtobufCDispatchTimer *min;
- GSK_RBTREE_FIRST (GET_TIMER_TREE (d), min);
- d->base.timeout_secs = min->timeout_secs;
- d->base.timeout_usecs = min->timeout_usecs;
- }
- }
- }
- ProtobufCDispatchIdle *
- protobuf_c_dispatch_add_idle (ProtobufCDispatch *dispatch,
- ProtobufCDispatchIdleFunc func,
- void *func_data)
- {
- RealDispatch *d = (RealDispatch *) dispatch;
- ProtobufCDispatchIdle *rv;
- if (d->recycled_idles != NULL)
- {
- rv = d->recycled_idles;
- d->recycled_idles = rv->next;
- }
- else
- {
- ProtobufCAllocator *allocator = d->allocator;
- rv = ALLOC (sizeof (ProtobufCDispatchIdle));
- }
- GSK_LIST_APPEND (GET_IDLE_LIST (d), rv);
- rv->func = func;
- rv->func_data = func_data;
- rv->dispatch = d;
- dispatch->has_idle = 1;
- return rv;
- }
- void
- protobuf_c_dispatch_remove_idle (ProtobufCDispatchIdle *idle)
- {
- if (idle->func != NULL)
- {
- RealDispatch *d = idle->dispatch;
- GSK_LIST_REMOVE (GET_IDLE_LIST (d), idle);
- idle->next = d->recycled_idles;
- d->recycled_idles = idle;
- }
- }
- void protobuf_c_dispatch_destroy_default (void)
- {
- if (def)
- {
- ProtobufCDispatch *to_kill = def;
- def = NULL;
- protobuf_c_dispatch_free (to_kill);
- }
- }