PageRenderTime 24ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/src/google/protobuf-c/protobuf-c-dispatch.c

http://protobuf-c.googlecode.com/
C | 910 lines | 760 code | 72 blank | 78 comment | 133 complexity | 2b2ea235aefd70d4b07b94a7c44d74f5 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. /*
  2. * Copyright (c) 2008-2011, Dave Benson.
  3. *
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with
  7. * or without modification, are permitted provided that the
  8. * following conditions are met:
  9. *
  10. * Redistributions of source code must retain the above
  11. * copyright notice, this list of conditions and the following
  12. * disclaimer.
  13. * Redistributions in binary form must reproduce
  14. * the above copyright notice, this list of conditions and
  15. * the following disclaimer in the documentation and/or other
  16. * materials provided with the distribution.
  17. *
  18. * Neither the name
  19. * of "protobuf-c" nor the names of its contributors
  20. * may be used to endorse or promote products derived from
  21. * this software without specific prior written permission.
  22. *
  23. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  24. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  25. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  26. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  27. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
  28. * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  29. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  30. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  31. * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
  32. * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  33. * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  34. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  35. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  36. * POSSIBILITY OF SUCH DAMAGE.
  37. */
  38. /* NOTE: this may not work very well on windows, where i'm
  39. not sure that "SOCKETs" are allocated nicely like
  40. file-descriptors are */
  41. /* TODO:
  42. * * epoll() implementation
  43. * * kqueue() implementation
  44. * * windows port (yeah, right, volunteers are DEFINITELY needed for this one...)
  45. */
  46. #if HAVE_PROTOBUF_C_CONFIG_H
  47. #include "protobuf-c-config.h"
  48. #endif
  49. #include <assert.h>
  50. #if HAVE_ALLOCA_H
  51. # include <alloca.h>
  52. #elif HAVE_MALLOC_H
  53. # include <malloc.h>
  54. #endif
  55. #include <sys/time.h>
  56. #include <unistd.h>
  57. #include <string.h>
  58. #include <stdio.h>
  59. #if HAVE_SYS_POLL_H
  60. # include <sys/poll.h>
  61. # define USE_POLL 1
  62. #elif HAVE_SYS_SELECT_H
  63. # include <sys/select.h>
  64. # define USE_POLL 0
  65. #endif
  66. /* windows annoyances: use select, use a full-fledges map for fds */
  67. #ifdef WIN32
  68. # include <winsock.h>
  69. # define USE_POLL 0
  70. # define HAVE_SMALL_FDS 0
  71. #endif
  72. #include <limits.h>
  73. #include <errno.h>
  74. #include <signal.h>
  75. #include "protobuf-c-dispatch.h"
  76. #include "gskrbtreemacros.h"
  77. #include "gsklistmacros.h"
  78. #define DEBUG_DISPATCH_INTERNALS 0
  79. #define DEBUG_DISPATCH 0
  80. #ifndef HAVE_SMALL_FDS
  81. # define HAVE_SMALL_FDS 1
  82. #endif
  83. #define protobuf_c_assert(condition) assert(condition)
  84. #define ALLOC_WITH_ALLOCATOR(allocator, size) ((allocator)->alloc ((allocator)->allocator_data, (size)))
  85. #define FREE_WITH_ALLOCATOR(allocator, ptr) ((allocator)->free ((allocator)->allocator_data, (ptr)))
  86. /* macros that assume you have a ProtobufCAllocator* named
  87. allocator in scope */
  88. #define ALLOC(size) ALLOC_WITH_ALLOCATOR((allocator), size)
  89. #define FREE(ptr) FREE_WITH_ALLOCATOR((allocator), ptr)
  90. typedef struct _Callback Callback;
  91. struct _Callback
  92. {
  93. ProtobufCDispatchCallback func;
  94. void *data;
  95. };
  96. typedef struct _FDMap FDMap;
  97. struct _FDMap
  98. {
  99. int notify_desired_index; /* -1 if not an known fd */
  100. int change_index; /* -1 if no prior change */
  101. int closed_since_notify_started;
  102. };
  103. #if !HAVE_SMALL_FDS
  104. typedef struct _FDMapNode FDMapNode;
  105. struct _FDMapNode
  106. {
  107. ProtobufC_FD fd;
  108. FDMapNode *left, *right, *parent;
  109. protobuf_c_boolean is_red;
  110. FDMap map;
  111. };
  112. #endif
  113. typedef struct _RealDispatch RealDispatch;
  114. struct _RealDispatch
  115. {
  116. ProtobufCDispatch base;
  117. Callback *callbacks; /* parallels notifies_desired */
  118. size_t notifies_desired_alloced;
  119. size_t changes_alloced;
  120. #if HAVE_SMALL_FDS
  121. FDMap *fd_map; /* map indexed by fd */
  122. size_t fd_map_size; /* number of elements of fd_map */
  123. #else
  124. FDMapNode *fd_map_tree; /* map indexed by fd */
  125. #endif
  126. protobuf_c_boolean is_dispatching;
  127. ProtobufCDispatchTimer *timer_tree;
  128. ProtobufCAllocator *allocator;
  129. ProtobufCDispatchTimer *recycled_timeouts;
  130. ProtobufCDispatchIdle *first_idle, *last_idle;
  131. ProtobufCDispatchIdle *recycled_idles;
  132. };
  133. struct _ProtobufCDispatchTimer
  134. {
  135. RealDispatch *dispatch;
  136. /* the actual timeout time */
  137. unsigned long timeout_secs;
  138. unsigned timeout_usecs;
  139. /* red-black tree stuff */
  140. ProtobufCDispatchTimer *left, *right, *parent;
  141. protobuf_c_boolean is_red;
  142. /* user callback */
  143. ProtobufCDispatchTimerFunc func;
  144. void *func_data;
  145. };
  146. struct _ProtobufCDispatchIdle
  147. {
  148. RealDispatch *dispatch;
  149. ProtobufCDispatchIdle *prev, *next;
  150. /* user callback */
  151. ProtobufCDispatchIdleFunc func;
  152. void *func_data;
  153. };
  154. /* Define the tree of timers, as per gskrbtreemacros.h */
  155. #define TIMER_GET_IS_RED(n) ((n)->is_red)
  156. #define TIMER_SET_IS_RED(n,v) ((n)->is_red = (v))
  157. #define TIMERS_COMPARE(a,b, rv) \
  158. if (a->timeout_secs < b->timeout_secs) rv = -1; \
  159. else if (a->timeout_secs > b->timeout_secs) rv = 1; \
  160. else if (a->timeout_usecs < b->timeout_usecs) rv = -1; \
  161. else if (a->timeout_usecs > b->timeout_usecs) rv = 1; \
  162. else if (a < b) rv = -1; \
  163. else if (a > b) rv = 1; \
  164. else rv = 0;
  165. #define GET_TIMER_TREE(d) \
  166. (d)->timer_tree, ProtobufCDispatchTimer *, \
  167. TIMER_GET_IS_RED, TIMER_SET_IS_RED, \
  168. parent, left, right, \
  169. TIMERS_COMPARE
  170. #if !HAVE_SMALL_FDS
  171. #define FD_MAP_NODES_COMPARE(a,b, rv) \
  172. if (a->fd < b->fd) rv = -1; \
  173. else if (a->fd > b->fd) rv = 1; \
  174. else rv = 0;
  175. #define GET_FD_MAP_TREE(d) \
  176. (d)->fd_map_tree, FDMapNode *, \
  177. TIMER_GET_IS_RED, TIMER_SET_IS_RED, \
  178. parent, left, right, \
  179. FD_MAP_NODES_COMPARE
  180. #define COMPARE_FD_TO_FD_MAP_NODE(a,b, rv) \
  181. if (a < b->fd) rv = -1; \
  182. else if (a > b->fd) rv = 1; \
  183. else rv = 0;
  184. #endif
  185. /* declare the idle-handler list */
  186. #define GET_IDLE_LIST(d) \
  187. ProtobufCDispatchIdle *, d->first_idle, d->last_idle, prev, next
  188. /* Create or destroy a Dispatch */
  189. ProtobufCDispatch *protobuf_c_dispatch_new (ProtobufCAllocator *allocator)
  190. {
  191. RealDispatch *rv = ALLOC (sizeof (RealDispatch));
  192. struct timeval tv;
  193. rv->base.n_changes = 0;
  194. rv->notifies_desired_alloced = 8;
  195. rv->base.notifies_desired = ALLOC (sizeof (ProtobufC_FDNotify) * rv->notifies_desired_alloced);
  196. rv->base.n_notifies_desired = 0;
  197. rv->callbacks = ALLOC (sizeof (Callback) * rv->notifies_desired_alloced);
  198. rv->changes_alloced = 8;
  199. rv->base.changes = ALLOC (sizeof (ProtobufC_FDNotifyChange) * rv->changes_alloced);
  200. #if HAVE_SMALL_FDS
  201. rv->fd_map_size = 16;
  202. rv->fd_map = ALLOC (sizeof (FDMap) * rv->fd_map_size);
  203. memset (rv->fd_map, 255, sizeof (FDMap) * rv->fd_map_size);
  204. #else
  205. rv->fd_map_tree = NULL;
  206. #endif
  207. rv->allocator = allocator;
  208. rv->timer_tree = NULL;
  209. rv->first_idle = rv->last_idle = NULL;
  210. rv->base.has_idle = 0;
  211. rv->recycled_idles = NULL;
  212. rv->recycled_timeouts = NULL;
  213. rv->is_dispatching = 0;
  214. /* need to handle SIGPIPE more gracefully than default */
  215. signal (SIGPIPE, SIG_IGN);
  216. gettimeofday (&tv, NULL);
  217. rv->base.last_dispatch_secs = tv.tv_sec;
  218. rv->base.last_dispatch_usecs = tv.tv_usec;
  219. return &rv->base;
  220. }
  221. #if !HAVE_SMALL_FDS
  222. void free_fd_tree_recursive (ProtobufCAllocator *allocator,
  223. FDMapNode *node)
  224. {
  225. if (node)
  226. {
  227. free_fd_tree_recursive (allocator, node->left);
  228. free_fd_tree_recursive (allocator, node->right);
  229. FREE (node);
  230. }
  231. }
  232. #endif
  233. /* XXX: leaking timer_tree seemingly? */
  234. void
  235. protobuf_c_dispatch_free(ProtobufCDispatch *dispatch)
  236. {
  237. RealDispatch *d = (RealDispatch *) dispatch;
  238. ProtobufCAllocator *allocator = d->allocator;
  239. while (d->recycled_timeouts != NULL)
  240. {
  241. ProtobufCDispatchTimer *t = d->recycled_timeouts;
  242. d->recycled_timeouts = t->right;
  243. FREE (t);
  244. }
  245. while (d->recycled_idles != NULL)
  246. {
  247. ProtobufCDispatchIdle *i = d->recycled_idles;
  248. d->recycled_idles = i->next;
  249. FREE (i);
  250. }
  251. FREE (d->base.notifies_desired);
  252. FREE (d->base.changes);
  253. FREE (d->callbacks);
  254. #if HAVE_SMALL_FDS
  255. FREE (d->fd_map);
  256. #else
  257. free_fd_tree_recursive (allocator, d->fd_map_tree);
  258. #endif
  259. FREE (d);
  260. }
  261. ProtobufCAllocator *
  262. protobuf_c_dispatch_peek_allocator (ProtobufCDispatch *dispatch)
  263. {
  264. RealDispatch *d = (RealDispatch *) dispatch;
  265. return d->allocator;
  266. }
  267. /* TODO: perhaps thread-private dispatches make more sense? */
  268. static ProtobufCDispatch *def = NULL;
  269. ProtobufCDispatch *protobuf_c_dispatch_default (void)
  270. {
  271. if (def == NULL)
  272. def = protobuf_c_dispatch_new (&protobuf_c_default_allocator);
  273. return def;
  274. }
  275. #if HAVE_SMALL_FDS
  276. static void
  277. enlarge_fd_map (RealDispatch *d,
  278. unsigned fd)
  279. {
  280. size_t new_size = d->fd_map_size * 2;
  281. FDMap *new_map;
  282. ProtobufCAllocator *allocator = d->allocator;
  283. while (fd >= new_size)
  284. new_size *= 2;
  285. new_map = ALLOC (sizeof (FDMap) * new_size);
  286. memcpy (new_map, d->fd_map, d->fd_map_size * sizeof (FDMap));
  287. memset (new_map + d->fd_map_size,
  288. 255,
  289. sizeof (FDMap) * (new_size - d->fd_map_size));
  290. FREE (d->fd_map);
  291. d->fd_map = new_map;
  292. d->fd_map_size = new_size;
  293. }
  294. static inline void
  295. ensure_fd_map_big_enough (RealDispatch *d,
  296. unsigned fd)
  297. {
  298. if (fd >= d->fd_map_size)
  299. enlarge_fd_map (d, fd);
  300. }
  301. #endif
  302. static unsigned
  303. allocate_notifies_desired_index (RealDispatch *d)
  304. {
  305. unsigned rv = d->base.n_notifies_desired++;
  306. ProtobufCAllocator *allocator = d->allocator;
  307. if (rv == d->notifies_desired_alloced)
  308. {
  309. unsigned new_size = d->notifies_desired_alloced * 2;
  310. ProtobufC_FDNotify *n = ALLOC (new_size * sizeof (ProtobufC_FDNotify));
  311. Callback *c = ALLOC (new_size * sizeof (Callback));
  312. memcpy (n, d->base.notifies_desired, d->notifies_desired_alloced * sizeof (ProtobufC_FDNotify));
  313. FREE (d->base.notifies_desired);
  314. memcpy (c, d->callbacks, d->notifies_desired_alloced * sizeof (Callback));
  315. FREE (d->callbacks);
  316. d->base.notifies_desired = n;
  317. d->callbacks = c;
  318. d->notifies_desired_alloced = new_size;
  319. }
  320. #if DEBUG_DISPATCH_INTERNALS
  321. fprintf (stderr, "allocate_notifies_desired_index: returning %u\n", rv);
  322. #endif
  323. return rv;
  324. }
  325. static unsigned
  326. allocate_change_index (RealDispatch *d)
  327. {
  328. unsigned rv = d->base.n_changes++;
  329. if (rv == d->changes_alloced)
  330. {
  331. ProtobufCAllocator *allocator = d->allocator;
  332. unsigned new_size = d->changes_alloced * 2;
  333. ProtobufC_FDNotifyChange *n = ALLOC (new_size * sizeof (ProtobufC_FDNotifyChange));
  334. memcpy (n, d->base.changes, d->changes_alloced * sizeof (ProtobufC_FDNotifyChange));
  335. FREE (d->base.changes);
  336. d->base.changes = n;
  337. d->changes_alloced = new_size;
  338. }
  339. return rv;
  340. }
  341. static inline FDMap *
  342. get_fd_map (RealDispatch *d, ProtobufC_FD fd)
  343. {
  344. #if HAVE_SMALL_FDS
  345. if ((unsigned)fd >= d->fd_map_size)
  346. return NULL;
  347. else
  348. return d->fd_map + fd;
  349. #else
  350. FDMapNode *node;
  351. GSK_RBTREE_LOOKUP_COMPARATOR (GET_FD_MAP_TREE (d), fd, COMPARE_FD_TO_FD_MAP_NODE, node);
  352. return node ? &node->map : NULL;
  353. #endif
  354. }
  355. static inline FDMap *
  356. force_fd_map (RealDispatch *d, ProtobufC_FD fd)
  357. {
  358. #if HAVE_SMALL_FDS
  359. ensure_fd_map_big_enough (d, fd);
  360. return d->fd_map + fd;
  361. #else
  362. {
  363. FDMap *fm = get_fd_map (d, fd);
  364. ProtobufCAllocator *allocator = d->allocator;
  365. if (fm == NULL)
  366. {
  367. FDMapNode *node = ALLOC (sizeof (FDMapNode));
  368. FDMapNode *conflict;
  369. node->fd = fd;
  370. memset (&node->map, 255, sizeof (FDMap));
  371. GSK_RBTREE_INSERT (GET_FD_MAP_TREE (d), node, conflict);
  372. assert (conflict == NULL);
  373. fm = &node->map;
  374. }
  375. return fm;
  376. }
  377. #endif
  378. }
  379. static void
  380. deallocate_change_index (RealDispatch *d,
  381. FDMap *fm)
  382. {
  383. unsigned ch_ind = fm->change_index;
  384. unsigned from = d->base.n_changes - 1;
  385. ProtobufC_FD from_fd;
  386. fm->change_index = -1;
  387. if (ch_ind == from)
  388. {
  389. d->base.n_changes--;
  390. return;
  391. }
  392. from_fd = d->base.changes[ch_ind].fd;
  393. get_fd_map (d, from_fd)->change_index = ch_ind;
  394. d->base.changes[ch_ind] = d->base.changes[from];
  395. d->base.n_changes--;
  396. }
  397. static void
  398. deallocate_notify_desired_index (RealDispatch *d,
  399. ProtobufC_FD fd,
  400. FDMap *fm)
  401. {
  402. unsigned nd_ind = fm->notify_desired_index;
  403. unsigned from = d->base.n_notifies_desired - 1;
  404. ProtobufC_FD from_fd;
  405. (void) fd;
  406. #if DEBUG_DISPATCH_INTERNALS
  407. fprintf (stderr, "deallocate_notify_desired_index: fd=%d, nd_ind=%u\n",fd,nd_ind);
  408. #endif
  409. fm->notify_desired_index = -1;
  410. if (nd_ind == from)
  411. {
  412. d->base.n_notifies_desired--;
  413. return;
  414. }
  415. from_fd = d->base.notifies_desired[from].fd;
  416. get_fd_map (d, from_fd)->notify_desired_index = nd_ind;
  417. d->base.notifies_desired[nd_ind] = d->base.notifies_desired[from];
  418. d->callbacks[nd_ind] = d->callbacks[from];
  419. d->base.n_notifies_desired--;
  420. }
  421. /* Registering file-descriptors to watch. */
  422. void
  423. protobuf_c_dispatch_watch_fd (ProtobufCDispatch *dispatch,
  424. ProtobufC_FD fd,
  425. unsigned events,
  426. ProtobufCDispatchCallback callback,
  427. void *callback_data)
  428. {
  429. RealDispatch *d = (RealDispatch *) dispatch;
  430. unsigned f = fd; /* avoid tiring compiler warnings: "comparison of signed versus unsigned" */
  431. unsigned nd_ind, change_ind;
  432. unsigned old_events;
  433. FDMap *fm;
  434. #if DEBUG_DISPATCH
  435. fprintf (stderr, "dispatch: watch_fd: %d, %s%s\n",
  436. fd,
  437. (events&PROTOBUF_C_EVENT_READABLE)?"r":"",
  438. (events&PROTOBUF_C_EVENT_WRITABLE)?"w":"");
  439. #endif
  440. if (callback == NULL)
  441. assert (events == 0);
  442. else
  443. assert (events != 0);
  444. fm = force_fd_map (d, f);
  445. /* XXX: should we set fm->map.closed_since_notify_started=0 ??? */
  446. if (fm->notify_desired_index == -1)
  447. {
  448. if (callback != NULL)
  449. nd_ind = fm->notify_desired_index = allocate_notifies_desired_index (d);
  450. old_events = 0;
  451. }
  452. else
  453. {
  454. old_events = dispatch->notifies_desired[fm->notify_desired_index].events;
  455. if (callback == NULL)
  456. deallocate_notify_desired_index (d, fd, fm);
  457. else
  458. nd_ind = fm->notify_desired_index;
  459. }
  460. if (callback == NULL)
  461. {
  462. if (fm->change_index == -1)
  463. {
  464. change_ind = fm->change_index = allocate_change_index (d);
  465. dispatch->changes[change_ind].old_events = old_events;
  466. }
  467. else
  468. change_ind = fm->change_index;
  469. d->base.changes[change_ind].fd = f;
  470. d->base.changes[change_ind].events = 0;
  471. return;
  472. }
  473. assert (callback != NULL && events != 0);
  474. if (fm->change_index == -1)
  475. {
  476. change_ind = fm->change_index = allocate_change_index (d);
  477. dispatch->changes[change_ind].old_events = old_events;
  478. }
  479. else
  480. change_ind = fm->change_index;
  481. d->base.changes[change_ind].fd = fd;
  482. d->base.changes[change_ind].events = events;
  483. d->base.notifies_desired[nd_ind].fd = fd;
  484. d->base.notifies_desired[nd_ind].events = events;
  485. d->callbacks[nd_ind].func = callback;
  486. d->callbacks[nd_ind].data = callback_data;
  487. }
  488. void
  489. protobuf_c_dispatch_close_fd (ProtobufCDispatch *dispatch,
  490. ProtobufC_FD fd)
  491. {
  492. protobuf_c_dispatch_fd_closed (dispatch, fd);
  493. close (fd);
  494. }
  495. void
  496. protobuf_c_dispatch_fd_closed(ProtobufCDispatch *dispatch,
  497. ProtobufC_FD fd)
  498. {
  499. RealDispatch *d = (RealDispatch *) dispatch;
  500. FDMap *fm;
  501. #if DEBUG_DISPATCH
  502. fprintf (stderr, "dispatch: fd %d closed\n", fd);
  503. #endif
  504. fm = force_fd_map (d, fd);
  505. fm->closed_since_notify_started = 1;
  506. if (fm->change_index != -1)
  507. deallocate_change_index (d, fm);
  508. if (fm->notify_desired_index != -1)
  509. deallocate_notify_desired_index (d, fd, fm);
  510. }
  511. static void
  512. free_timer (ProtobufCDispatchTimer *timer)
  513. {
  514. RealDispatch *d = timer->dispatch;
  515. timer->right = d->recycled_timeouts;
  516. d->recycled_timeouts = timer;
  517. }
  518. void
  519. protobuf_c_dispatch_dispatch (ProtobufCDispatch *dispatch,
  520. size_t n_notifies,
  521. ProtobufC_FDNotify *notifies)
  522. {
  523. RealDispatch *d = (RealDispatch *) dispatch;
  524. unsigned fd_max;
  525. unsigned i;
  526. struct timeval tv;
  527. /* Re-entrancy guard. If this is triggerred, then
  528. you are calling protobuf_c_dispatch_dispatch (or _run)
  529. from a callback function. That's not allowed. */
  530. protobuf_c_assert (!d->is_dispatching);
  531. d->is_dispatching = 1;
  532. gettimeofday (&tv, NULL);
  533. dispatch->last_dispatch_secs = tv.tv_sec;
  534. dispatch->last_dispatch_usecs = tv.tv_usec;
  535. fd_max = 0;
  536. for (i = 0; i < n_notifies; i++)
  537. if (fd_max < (unsigned) notifies[i].fd)
  538. fd_max = notifies[i].fd;
  539. ensure_fd_map_big_enough (d, fd_max);
  540. for (i = 0; i < n_notifies; i++)
  541. d->fd_map[notifies[i].fd].closed_since_notify_started = 0;
  542. for (i = 0; i < n_notifies; i++)
  543. {
  544. unsigned fd = notifies[i].fd;
  545. if (!d->fd_map[fd].closed_since_notify_started
  546. && d->fd_map[fd].notify_desired_index != -1)
  547. {
  548. unsigned nd_ind = d->fd_map[fd].notify_desired_index;
  549. unsigned events = d->base.notifies_desired[nd_ind].events & notifies[i].events;
  550. if (events != 0)
  551. d->callbacks[nd_ind].func (fd, events, d->callbacks[nd_ind].data);
  552. }
  553. }
  554. /* clear changes */
  555. for (i = 0; i < dispatch->n_changes; i++)
  556. d->fd_map[dispatch->changes[i].fd].change_index = -1;
  557. dispatch->n_changes = 0;
  558. /* handle idle functions */
  559. while (d->first_idle != NULL)
  560. {
  561. ProtobufCDispatchIdle *idle = d->first_idle;
  562. ProtobufCDispatchIdleFunc func = idle->func;
  563. void *data = idle->func_data;
  564. GSK_LIST_REMOVE_FIRST (GET_IDLE_LIST (d));
  565. idle->func = NULL; /* set to NULL to render remove_idle a no-op */
  566. func (dispatch, data);
  567. idle->next = d->recycled_idles;
  568. d->recycled_idles = idle;
  569. }
  570. dispatch->has_idle = 0;
  571. /* handle timers */
  572. while (d->timer_tree != NULL)
  573. {
  574. ProtobufCDispatchTimer *min_timer;
  575. GSK_RBTREE_FIRST (GET_TIMER_TREE (d), min_timer);
  576. if (min_timer->timeout_secs < (unsigned long) tv.tv_sec
  577. || (min_timer->timeout_secs == (unsigned long) tv.tv_sec
  578. && min_timer->timeout_usecs <= (unsigned) tv.tv_usec))
  579. {
  580. ProtobufCDispatchTimerFunc func = min_timer->func;
  581. void *func_data = min_timer->func_data;
  582. GSK_RBTREE_REMOVE (GET_TIMER_TREE (d), min_timer);
  583. /* Set to NULL as a way to tell protobuf_c_dispatch_remove_timer()
  584. that we are in the middle of notifying */
  585. min_timer->func = NULL;
  586. min_timer->func_data = NULL;
  587. func (&d->base, func_data);
  588. free_timer (min_timer);
  589. }
  590. else
  591. {
  592. d->base.has_timeout = 1;
  593. d->base.timeout_secs = min_timer->timeout_secs;
  594. d->base.timeout_usecs = min_timer->timeout_usecs;
  595. break;
  596. }
  597. }
  598. if (d->timer_tree == NULL)
  599. d->base.has_timeout = 0;
  600. /* Finish reentrance guard. */
  601. d->is_dispatching = 0;
  602. }
  603. void
  604. protobuf_c_dispatch_clear_changes (ProtobufCDispatch *dispatch)
  605. {
  606. RealDispatch *d = (RealDispatch *) dispatch;
  607. unsigned i;
  608. for (i = 0; i < dispatch->n_changes; i++)
  609. {
  610. FDMap *fm = get_fd_map (d, dispatch->changes[i].fd);
  611. assert (fm->change_index == (int) i);
  612. fm->change_index = -1;
  613. }
  614. dispatch->n_changes = 0;
  615. }
  616. static inline unsigned
  617. events_to_pollfd_events (unsigned ev)
  618. {
  619. return ((ev & PROTOBUF_C_EVENT_READABLE) ? POLLIN : 0)
  620. | ((ev & PROTOBUF_C_EVENT_WRITABLE) ? POLLOUT : 0)
  621. ;
  622. }
  623. static inline unsigned
  624. pollfd_events_to_events (unsigned ev)
  625. {
  626. return ((ev & (POLLIN|POLLHUP)) ? PROTOBUF_C_EVENT_READABLE : 0)
  627. | ((ev & POLLOUT) ? PROTOBUF_C_EVENT_WRITABLE : 0)
  628. ;
  629. }
  630. void
  631. protobuf_c_dispatch_run (ProtobufCDispatch *dispatch)
  632. {
  633. struct pollfd *fds;
  634. void *to_free = NULL, *to_free2 = NULL;
  635. size_t n_events;
  636. RealDispatch *d = (RealDispatch *) dispatch;
  637. ProtobufCAllocator *allocator = d->allocator;
  638. unsigned i;
  639. int timeout;
  640. ProtobufC_FDNotify *events;
  641. if (dispatch->n_notifies_desired < 128)
  642. fds = alloca (sizeof (struct pollfd) * dispatch->n_notifies_desired);
  643. else
  644. to_free = fds = ALLOC (sizeof (struct pollfd) * dispatch->n_notifies_desired);
  645. for (i = 0; i < dispatch->n_notifies_desired; i++)
  646. {
  647. fds[i].fd = dispatch->notifies_desired[i].fd;
  648. fds[i].events = events_to_pollfd_events (dispatch->notifies_desired[i].events);
  649. fds[i].revents = 0;
  650. }
  651. /* compute timeout */
  652. if (dispatch->has_idle)
  653. timeout = 0;
  654. else if (!dispatch->has_timeout)
  655. timeout = -1;
  656. else
  657. {
  658. struct timeval tv;
  659. gettimeofday (&tv, NULL);
  660. if (dispatch->timeout_secs < (unsigned long) tv.tv_sec
  661. || (dispatch->timeout_secs == (unsigned long) tv.tv_sec
  662. && dispatch->timeout_usecs <= (unsigned) tv.tv_usec))
  663. timeout = 0;
  664. else
  665. {
  666. int du = dispatch->timeout_usecs - tv.tv_usec;
  667. int ds = dispatch->timeout_secs - tv.tv_sec;
  668. if (du < 0)
  669. {
  670. du += 1000000;
  671. ds -= 1;
  672. }
  673. if (ds > INT_MAX / 1000)
  674. timeout = INT_MAX / 1000 * 1000;
  675. else
  676. /* Round up, so that we ensure that something can run
  677. if they just wait the full duration */
  678. timeout = ds * 1000 + (du + 999) / 1000;
  679. }
  680. }
  681. if (poll (fds, dispatch->n_notifies_desired, timeout) < 0)
  682. {
  683. if (errno == EINTR)
  684. return; /* probably a signal interrupted the poll-- let the user have control */
  685. /* i don't really know what would plausibly cause this */
  686. fprintf (stderr, "error polling: %s\n", strerror (errno));
  687. return;
  688. }
  689. n_events = 0;
  690. for (i = 0; i < dispatch->n_notifies_desired; i++)
  691. if (fds[i].revents)
  692. n_events++;
  693. if (n_events < 128)
  694. events = alloca (sizeof (ProtobufC_FDNotify) * n_events);
  695. else
  696. to_free2 = events = ALLOC (sizeof (ProtobufC_FDNotify) * n_events);
  697. n_events = 0;
  698. for (i = 0; i < dispatch->n_notifies_desired; i++)
  699. if (fds[i].revents)
  700. {
  701. events[n_events].fd = fds[i].fd;
  702. events[n_events].events = pollfd_events_to_events (fds[i].revents);
  703. /* note that we may actually wind up with fewer events
  704. now that we actually call pollfd_events_to_events() */
  705. if (events[n_events].events != 0)
  706. n_events++;
  707. }
  708. protobuf_c_dispatch_dispatch (dispatch, n_events, events);
  709. if (to_free)
  710. FREE (to_free);
  711. if (to_free2)
  712. FREE (to_free2);
  713. }
  714. ProtobufCDispatchTimer *
  715. protobuf_c_dispatch_add_timer(ProtobufCDispatch *dispatch,
  716. unsigned timeout_secs,
  717. unsigned timeout_usecs,
  718. ProtobufCDispatchTimerFunc func,
  719. void *func_data)
  720. {
  721. RealDispatch *d = (RealDispatch *) dispatch;
  722. ProtobufCDispatchTimer *rv;
  723. ProtobufCDispatchTimer *at;
  724. ProtobufCDispatchTimer *conflict;
  725. protobuf_c_assert (func != NULL);
  726. if (d->recycled_timeouts != NULL)
  727. {
  728. rv = d->recycled_timeouts;
  729. d->recycled_timeouts = rv->right;
  730. }
  731. else
  732. {
  733. rv = d->allocator->alloc (d->allocator, sizeof (ProtobufCDispatchTimer));
  734. }
  735. rv->timeout_secs = timeout_secs;
  736. rv->timeout_usecs = timeout_usecs;
  737. rv->func = func;
  738. rv->func_data = func_data;
  739. rv->dispatch = d;
  740. GSK_RBTREE_INSERT (GET_TIMER_TREE (d), rv, conflict);
  741. /* is this the first element in the tree */
  742. for (at = rv; at != NULL; at = at->parent)
  743. if (at->parent && at->parent->right == at)
  744. break;
  745. if (at == NULL) /* yes, so set the public members */
  746. {
  747. dispatch->has_timeout = 1;
  748. dispatch->timeout_secs = rv->timeout_secs;
  749. dispatch->timeout_usecs = rv->timeout_usecs;
  750. }
  751. return rv;
  752. }
  753. ProtobufCDispatchTimer *
  754. protobuf_c_dispatch_add_timer_millis
  755. (ProtobufCDispatch *dispatch,
  756. unsigned millis,
  757. ProtobufCDispatchTimerFunc func,
  758. void *func_data)
  759. {
  760. unsigned tsec = dispatch->last_dispatch_secs;
  761. unsigned tusec = dispatch->last_dispatch_usecs;
  762. tusec += 1000 * (millis % 1000);
  763. tsec += millis / 1000;
  764. if (tusec >= 1000*1000)
  765. {
  766. tusec -= 1000*1000;
  767. tsec += 1;
  768. }
  769. return protobuf_c_dispatch_add_timer (dispatch, tsec, tusec, func, func_data);
  770. }
  771. void protobuf_c_dispatch_remove_timer (ProtobufCDispatchTimer *timer)
  772. {
  773. protobuf_c_boolean may_be_first;
  774. RealDispatch *d = timer->dispatch;
  775. /* ignore mid-notify removal */
  776. if (timer->func == NULL)
  777. return;
  778. may_be_first = d->base.timeout_usecs == timer->timeout_usecs
  779. && d->base.timeout_secs == timer->timeout_secs;
  780. GSK_RBTREE_REMOVE (GET_TIMER_TREE (d), timer);
  781. if (may_be_first)
  782. {
  783. if (d->timer_tree == NULL)
  784. d->base.has_timeout = 0;
  785. else
  786. {
  787. ProtobufCDispatchTimer *min;
  788. GSK_RBTREE_FIRST (GET_TIMER_TREE (d), min);
  789. d->base.timeout_secs = min->timeout_secs;
  790. d->base.timeout_usecs = min->timeout_usecs;
  791. }
  792. }
  793. }
  794. ProtobufCDispatchIdle *
  795. protobuf_c_dispatch_add_idle (ProtobufCDispatch *dispatch,
  796. ProtobufCDispatchIdleFunc func,
  797. void *func_data)
  798. {
  799. RealDispatch *d = (RealDispatch *) dispatch;
  800. ProtobufCDispatchIdle *rv;
  801. if (d->recycled_idles != NULL)
  802. {
  803. rv = d->recycled_idles;
  804. d->recycled_idles = rv->next;
  805. }
  806. else
  807. {
  808. ProtobufCAllocator *allocator = d->allocator;
  809. rv = ALLOC (sizeof (ProtobufCDispatchIdle));
  810. }
  811. GSK_LIST_APPEND (GET_IDLE_LIST (d), rv);
  812. rv->func = func;
  813. rv->func_data = func_data;
  814. rv->dispatch = d;
  815. dispatch->has_idle = 1;
  816. return rv;
  817. }
  818. void
  819. protobuf_c_dispatch_remove_idle (ProtobufCDispatchIdle *idle)
  820. {
  821. if (idle->func != NULL)
  822. {
  823. RealDispatch *d = idle->dispatch;
  824. GSK_LIST_REMOVE (GET_IDLE_LIST (d), idle);
  825. idle->next = d->recycled_idles;
  826. d->recycled_idles = idle;
  827. }
  828. }
  829. void protobuf_c_dispatch_destroy_default (void)
  830. {
  831. if (def)
  832. {
  833. ProtobufCDispatch *to_kill = def;
  834. def = NULL;
  835. protobuf_c_dispatch_free (to_kill);
  836. }
  837. }