Kannel: Open Source WAP and SMS gateway  $Revision: 5037 $
fdset.c File Reference
#include "gw-config.h"
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include "gwlib/gwlib.h"

Go to the source code of this file.

Data Structures

struct  FDSet
 
struct  action
 

Functions

static struct actionaction_create (int type)
 
static void action_destroy (struct action *action)
 
static void action_destroy_item (void *action)
 
static void submit_action (FDSet *set, struct action *action)
 
static void submit_action_nosync (FDSet *set, struct action *action)
 
static int handle_action (FDSet *set, struct action *action)
 
static int find_entry (FDSet *set, int fd)
 
static void remove_entry (FDSet *set, int entry)
 
static void remove_deleted_entries (FDSet *set)
 
static void poller (void *arg)
 
FDSetfdset_create_real (long timeout)
 
void fdset_destroy (FDSet *set)
 
void fdset_register (FDSet *set, int fd, int events, fdset_callback_t callback, void *data)
 
void fdset_listen (FDSet *set, int fd, int mask, int events)
 
void fdset_unregister (FDSet *set, int fd)
 
void fdset_set_timeout (FDSet *set, long timeout)
 

Function Documentation

static struct action* action_create ( int  type)
static

Definition at line 155 of file fdset.c.

References type.

Referenced by fdset_destroy(), fdset_listen(), fdset_register(), fdset_set_timeout(), and fdset_unregister().

156 {
157  struct action *new;
158 
159  new = gw_malloc(sizeof(*new));
160  new->type = type;
161  new->fd = -1;
162  new->mask = 0;
163  new->events = 0;
164  new->callback = NULL;
165  new->data = NULL;
166  new->done = NULL;
167 
168  return new;
169 }
Definition: fdset.c:140
int type
Definition: smsc_cimd2.c:215
static void action_destroy ( struct action action)
static

Definition at line 171 of file fdset.c.

References action::done, and gwlist_destroy().

Referenced by action_destroy_item(), handle_action(), and submit_action().

172 {
173  if (action == NULL)
174  return;
175 
176  gwlist_destroy(action->done, NULL);
177  gw_free(action);
178 }
List * done
Definition: fdset.c:151
void gwlist_destroy(List *list, gwlist_item_destructor_t *destructor)
Definition: list.c:145
static void action_destroy_item ( void *  action)
static

Definition at line 181 of file fdset.c.

References action_destroy().

Referenced by fdset_destroy().

182 {
184 }
Definition: fdset.c:140
static void action_destroy(struct action *action)
Definition: fdset.c:171
FDSet* fdset_create_real ( long  timeout)

Definition at line 368 of file fdset.c.

References error(), fdset_destroy(), gwlist_create, gwthread_create, and poller().

Referenced by port_add(), and start_client_threads().

369 {
370  FDSet *new;
371 
372  new = gw_malloc(sizeof(*new));
373 
374  /* Start off with space for one element because we can't malloc 0 bytes
375  * and we don't want to worry about these pointers being NULL. */
376  new->size = 1;
377  new->entries = 0;
378  new->pollinfo = gw_malloc(sizeof(new->pollinfo[0]) * new->size);
379  new->callbacks = gw_malloc(sizeof(new->callbacks[0]) * new->size);
380  new->datafields = gw_malloc(sizeof(new->datafields[0]) * new->size);
381  new->times = gw_malloc(sizeof(new->times[0]) * new->size);
382  new->timeout = timeout > 0 ? timeout : -1;
383  new->scanning = 0;
384  new->deleted_entries = 0;
385 
386  new->actions = gwlist_create();
387 
388  new->poll_thread = gwthread_create(poller, new);
389  if (new->poll_thread < 0) {
390  error(0, "Could not start internal thread for fdset.");
391  fdset_destroy(new);
392  return NULL;
393  }
394 
395  return new;
396 }
void error(int err, const char *fmt,...)
Definition: log.c:612
long timeout
Definition: fdset.c:148
#define gwthread_create(func, arg)
Definition: gwthread.h:90
void fdset_destroy(FDSet *set)
Definition: fdset.c:398
#define gwlist_create()
Definition: list.h:136
Definition: fdset.c:70
static void poller(void *arg)
Definition: fdset.c:318
void fdset_destroy ( FDSet set)

Definition at line 398 of file fdset.c.

References action_create(), action_destroy_item(), FDSet::actions, FDSet::callbacks, FDSet::datafields, action::DESTROY, FDSet::entries, error(), gwlist_destroy(), gwlist_len(), gwthread_join(), gwthread_self(), FDSet::poll_thread, FDSet::pollinfo, submit_action(), FDSet::times, and warning().

Referenced by client_shutdown(), fdset_create_real(), handle_action(), port_remove(), and start_client_threads().

399 {
400  if (set == NULL)
401  return;
402 
403  if (set->poll_thread < 0 || gwthread_self() == set->poll_thread) {
404  if (set->entries > 0) {
405  warning(0, "Destroying fdset with %d active entries.",
406  set->entries);
407  }
408  gw_free(set->pollinfo);
409  gw_free(set->callbacks);
410  gw_free(set->datafields);
411  gw_free(set->times);
412  if (gwlist_len(set->actions) > 0) {
413  error(0, "Destroying fdset with %ld pending actions.",
414  gwlist_len(set->actions));
415  }
417  gw_free(set);
418  } else {
419  long thread = set->poll_thread;
421  gwthread_join(thread);
422  }
423 }
void error(int err, const char *fmt,...)
Definition: log.c:612
static struct action * action_create(int type)
Definition: fdset.c:155
long gwthread_self(void)
void gwthread_join(long thread)
long gwlist_len(List *list)
Definition: list.c:166
time_t * times
Definition: fdset.c:88
fdset_callback_t ** callbacks
Definition: fdset.c:96
List * actions
Definition: fdset.c:126
struct pollfd * pollinfo
Definition: fdset.c:83
void ** datafields
Definition: fdset.c:97
static void submit_action(FDSet *set, struct action *action)
Definition: fdset.c:191
void warning(int err, const char *fmt,...)
Definition: log.c:624
static void action_destroy_item(void *action)
Definition: fdset.c:181
int entries
Definition: fdset.c:85
long poll_thread
Definition: fdset.c:76
void gwlist_destroy(List *list, gwlist_item_destructor_t *destructor)
Definition: list.c:145
void fdset_listen ( FDSet set,
int  fd,
int  mask,
int  events 
)

Definition at line 470 of file fdset.c.

References action(), action_create(), pollfd::events, action::events, action::fd, find_entry(), gw_assert(), gwthread_self(), action::LISTEN, action::mask, FDSet::poll_thread, FDSet::pollinfo, pollfd::revents, FDSet::scanning, submit_action(), FDSet::times, and warning().

Referenced by handle_action(), unlocked_register_pollin(), and unlocked_register_pollout().

471 {
472  int entry;
473 
474  gw_assert(set != NULL);
475 
476  if (gwthread_self() != set->poll_thread) {
477  struct action *action;
478 
479  action = action_create(LISTEN);
480  action->fd = fd;
481  action->mask = mask;
482  action->events = events;
483  submit_action(set, action);
484  return;
485  }
486 
487  entry = find_entry(set, fd);
488  if (entry < 0) {
489  warning(0, "fdset_listen called on unregistered fd %d.", fd);
490  return;
491  }
492 
493  /* Copy the bits from events specified by the mask, and preserve the
494  * bits not specified by the mask. */
495  set->pollinfo[entry].events =
496  (set->pollinfo[entry].events & ~mask) | (events & mask);
497 
498  /* If poller is currently scanning the array, then change the
499  * revents field so that the callback function will not be called
500  * for events we should no longer listen for. The idea is the
501  * same as for the events field, except that we only turn bits off. */
502  if (set->scanning) {
503  set->pollinfo[entry].revents =
504  set->pollinfo[entry].revents & (events | ~mask);
505  }
506 
507  time(&set->times[entry]);
508 }
static struct action * action_create(int type)
Definition: fdset.c:155
long gwthread_self(void)
int events
Definition: fdset.c:145
static int find_entry(FDSet *set, int fd)
Definition: fdset.c:269
Definition: fdset.c:140
int scanning
Definition: fdset.c:112
time_t * times
Definition: fdset.c:88
int fd
Definition: fdset.c:143
short events
Definition: gwpoll.h:86
struct pollfd * pollinfo
Definition: fdset.c:83
static void submit_action(FDSet *set, struct action *action)
Definition: fdset.c:191
static int action(int hex)
void warning(int err, const char *fmt,...)
Definition: log.c:624
gw_assert(wtls_machine->packet_to_send!=NULL)
short revents
Definition: gwpoll.h:87
int mask
Definition: fdset.c:144
long poll_thread
Definition: fdset.c:76
void fdset_register ( FDSet set,
int  fd,
int  events,
fdset_callback_t  callback,
void *  data 
)

Definition at line 425 of file fdset.c.

References action(), action_create(), action::callback, FDSet::callbacks, action::data, FDSet::datafields, FDSet::entries, pollfd::events, action::events, pollfd::fd, action::fd, gw_assert(), gwthread_self(), FDSet::poll_thread, FDSet::pollinfo, action::REGISTER, pollfd::revents, FDSet::size, submit_action_nosync(), and FDSet::times.

Referenced by conn_register_real(), and handle_action().

427 {
428  int new;
429 
430  gw_assert(set != NULL);
431 
432  if (gwthread_self() != set->poll_thread) {
433  struct action *action;
434 
435  action = action_create(REGISTER);
436  action->fd = fd;
437  action->events = events;
438  action->callback = callback;
439  action->data = data;
440  submit_action_nosync(set, action);
441  return;
442  }
443 
444  gw_assert(set->entries <= set->size);
445 
446  if (set->entries >= set->size) {
447  int newsize = set->entries + 1;
448  set->pollinfo = gw_realloc(set->pollinfo,
449  sizeof(set->pollinfo[0]) * newsize);
450  set->callbacks = gw_realloc(set->callbacks,
451  sizeof(set->callbacks[0]) * newsize);
452  set->datafields = gw_realloc(set->datafields,
453  sizeof(set->datafields[0]) * newsize);
454  set->times = gw_realloc(set->times, sizeof(set->times[0]) * newsize);
455  set->size = newsize;
456  }
457 
458  /* We don't check set->scanning. Adding new entries is not harmful
459  * because their revents fields are 0. */
460 
461  new = set->entries++;
462  set->pollinfo[new].fd = fd;
463  set->pollinfo[new].events = events;
464  set->pollinfo[new].revents = 0;
465  set->callbacks[new] = callback;
466  set->datafields[new] = data;
467  time(&set->times[new]);
468 }
static struct action * action_create(int type)
Definition: fdset.c:155
long gwthread_self(void)
int events
Definition: fdset.c:145
Definition: fdset.c:140
int size
Definition: fdset.c:84
time_t * times
Definition: fdset.c:88
int fd
Definition: fdset.c:143
short events
Definition: gwpoll.h:86
fdset_callback_t ** callbacks
Definition: fdset.c:96
struct pollfd * pollinfo
Definition: fdset.c:83
void ** datafields
Definition: fdset.c:97
static int action(int hex)
gw_assert(wtls_machine->packet_to_send!=NULL)
fdset_callback_t * callback
Definition: fdset.c:146
int fd
Definition: gwpoll.h:85
int entries
Definition: fdset.c:85
short revents
Definition: gwpoll.h:87
static void submit_action_nosync(FDSet *set, struct action *action)
Definition: fdset.c:216
void * data
Definition: fdset.c:147
long poll_thread
Definition: fdset.c:76
void fdset_set_timeout ( FDSet set,
long  timeout 
)

Set timeout in seconds for this FDSet.

Definition at line 547 of file fdset.c.

References action(), action_create(), gw_assert(), gwthread_self(), FDSet::poll_thread, action::SET_TIMEOUT, submit_action(), FDSet::timeout, and action::timeout.

Referenced by http_set_client_timeout(), and port_set_timeout().

548 {
549  gw_assert(set != NULL);
550 
551  if (gwthread_self() != set->poll_thread) {
552  struct action *action;
553 
554  action = action_create(SET_TIMEOUT);
555  action->timeout = timeout;
556  submit_action(set, action);
557  return;
558  }
559  set->timeout = timeout;
560 }
static struct action * action_create(int type)
Definition: fdset.c:155
long gwthread_self(void)
Definition: fdset.c:140
long timeout
Definition: fdset.c:148
static void submit_action(FDSet *set, struct action *action)
Definition: fdset.c:191
static int action(int hex)
long timeout
Definition: fdset.c:91
gw_assert(wtls_machine->packet_to_send!=NULL)
long poll_thread
Definition: fdset.c:76
void fdset_unregister ( FDSet set,
int  fd 
)

Definition at line 510 of file fdset.c.

References action(), action_create(), FDSet::deleted_entries, FDSet::entries, pollfd::fd, action::fd, find_entry(), gw_assert(), gwthread_self(), FDSet::poll_thread, FDSet::pollinfo, remove_entry(), FDSet::scanning, submit_action(), action::UNREGISTER, and warning().

Referenced by conn_destroy(), conn_unregister(), and handle_action().

511 {
512  int entry;
513 
514  gw_assert(set != NULL);
515 
516  if (gwthread_self() != set->poll_thread) {
517  struct action *action;
518 
519  action = action_create(UNREGISTER);
520  action->fd = fd;
521  submit_action(set, action);
522  return;
523  }
524 
525  /* Remove the entry from the pollinfo array */
526 
527  entry = find_entry(set, fd);
528  if (entry < 0) {
529  warning(0, "fdset_listen called on unregistered fd %d.", fd);
530  return;
531  }
532 
533  if (entry == set->entries - 1) {
534  /* It's the last entry. We can safely remove it even while
535  * the array is being scanned, because the scan checks set->entries. */
536  set->entries--;
537  } else if (set->scanning) {
538  /* We can't remove entries because the array is being
539  * scanned. Mark it as deleted. */
540  set->pollinfo[entry].fd = -1;
541  set->deleted_entries++;
542  } else {
543  remove_entry(set, entry);
544  }
545 }
static struct action * action_create(int type)
Definition: fdset.c:155
long gwthread_self(void)
static int find_entry(FDSet *set, int fd)
Definition: fdset.c:269
Definition: fdset.c:140
int scanning
Definition: fdset.c:112
int fd
Definition: fdset.c:143
struct pollfd * pollinfo
Definition: fdset.c:83
int deleted_entries
Definition: fdset.c:118
static void submit_action(FDSet *set, struct action *action)
Definition: fdset.c:191
static int action(int hex)
void warning(int err, const char *fmt,...)
Definition: log.c:624
gw_assert(wtls_machine->packet_to_send!=NULL)
static void remove_entry(FDSet *set, int entry)
Definition: fdset.c:284
int fd
Definition: gwpoll.h:85
int entries
Definition: fdset.c:85
long poll_thread
Definition: fdset.c:76
static int find_entry ( FDSet set,
int  fd 
)
static

Definition at line 269 of file fdset.c.

References FDSet::entries, pollfd::fd, gw_assert(), gwthread_self(), FDSet::poll_thread, and FDSet::pollinfo.

Referenced by fdset_listen(), and fdset_unregister().

270 {
271  int i;
272 
273  gw_assert(set != NULL);
275 
276  for (i = 0; i < set->entries; i++) {
277  if (set->pollinfo[i].fd == fd)
278  return i;
279  }
280 
281  return -1;
282 }
long gwthread_self(void)
int fd
Definition: fdset.c:143
struct pollfd * pollinfo
Definition: fdset.c:83
gw_assert(wtls_machine->packet_to_send!=NULL)
int fd
Definition: gwpoll.h:85
int entries
Definition: fdset.c:85
long poll_thread
Definition: fdset.c:76
static int handle_action ( FDSet set,
struct action action 
)
static

Definition at line 226 of file fdset.c.

References action_destroy(), action::callback, action::data, action::DESTROY, action::done, action::events, action::fd, fdset_destroy(), fdset_listen(), fdset_register(), fdset_unregister(), gw_assert(), gwlist_produce(), gwthread_self(), action::LISTEN, action::mask, panic, FDSet::poll_thread, action::REGISTER, action::SET_TIMEOUT, FDSet::timeout, action::timeout, action::type, and action::UNREGISTER.

Referenced by poller().

227 {
228  int result;
229 
230  gw_assert(set != NULL);
232  gw_assert(action != NULL);
233 
234  result = 0;
235 
236  switch (action->type) {
237  case REGISTER:
238  fdset_register(set, action->fd, action->events,
239  action->callback, action->data);
240  break;
241  case LISTEN:
242  fdset_listen(set, action->fd, action->mask, action->events);
243  break;
244  case UNREGISTER:
245  fdset_unregister(set, action->fd);
246  break;
247  case DESTROY:
248  fdset_destroy(set);
249  result = -1;
250  break;
251  case SET_TIMEOUT:
252  set->timeout = action->timeout;
253  break;
254  default:
255  panic(0, "fdset: handle_action got unknown action type %d.",
256  action->type);
257  }
258 
259  if (action->done == NULL)
260  action_destroy(action);
261  else
262  gwlist_produce(action->done, action);
263 
264  return result;
265 }
long gwthread_self(void)
void fdset_listen(FDSet *set, int fd, int mask, int events)
Definition: fdset.c:470
enum action::@57 type
int events
Definition: fdset.c:145
void gwlist_produce(List *list, void *item)
Definition: list.c:411
void fdset_unregister(FDSet *set, int fd)
Definition: fdset.c:510
int fd
Definition: fdset.c:143
void fdset_register(FDSet *set, int fd, int events, fdset_callback_t callback, void *data)
Definition: fdset.c:425
long timeout
Definition: fdset.c:148
long timeout
Definition: fdset.c:91
gw_assert(wtls_machine->packet_to_send!=NULL)
void fdset_destroy(FDSet *set)
Definition: fdset.c:398
fdset_callback_t * callback
Definition: fdset.c:146
#define panic
Definition: log.h:87
List * done
Definition: fdset.c:151
int mask
Definition: fdset.c:144
void * data
Definition: fdset.c:147
long poll_thread
Definition: fdset.c:76
static void action_destroy(struct action *action)
Definition: fdset.c:171
static void poller ( void *  arg)
static

Definition at line 318 of file fdset.c.

References action(), FDSet::actions, FDSet::callbacks, FDSet::datafields, debug(), FDSet::deleted_entries, FDSet::entries, error(), pollfd::fd, gw_assert(), gwlist_extract_first(), gwthread_poll(), gwthread_sleep(), handle_action(), POLLERR, FDSet::pollinfo, remove_deleted_entries(), pollfd::revents, FDSet::scanning, FDSet::timeout, and FDSet::times.

Referenced by fdset_create_real().

319 {
320  FDSet *set = arg;
321  struct action *action;
322  int ret;
323  int i;
324  time_t now;
325 
326  gw_assert(set != NULL);
327 
328  for (;;) {
329  while ((action = gwlist_extract_first(set->actions)) != NULL) {
330  /* handle_action returns -1 if the set was destroyed. */
331  if (handle_action(set, action) < 0)
332  return;
333  }
334 
335  /* Block for defined timeout, waiting for activity */
336  ret = gwthread_poll(set->pollinfo, set->entries, set->timeout);
337 
338  if (ret < 0) {
339  if (errno != EINTR) {
340  error(errno, "Poller: can't handle error; sleeping 1 second.");
341  gwthread_sleep(1.0);
342  }
343  continue;
344  }
345  time(&now);
346  /* Callbacks may modify the table while we scan it, so be careful. */
347  set->scanning = 1;
348  for (i = 0; i < set->entries; i++) {
349  if (set->pollinfo[i].revents != 0) {
350  set->callbacks[i](set->pollinfo[i].fd,
351  set->pollinfo[i].revents,
352  set->datafields[i]);
353  /* update event time */
354  time(&set->times[i]);
355  } else if (set->timeout > 0 && difftime(set->times[i] + set->timeout, now) <= 0) {
356  debug("gwlib.fdset", 0, "Timeout for fd:%d appears.", set->pollinfo[i].fd);
357  set->callbacks[i](set->pollinfo[i].fd, POLLERR, set->datafields[i]);
358  }
359  }
360  set->scanning = 0;
361 
362  if (set->deleted_entries > 0)
364  }
365 }
void error(int err, const char *fmt,...)
Definition: log.c:612
Definition: fdset.c:140
int scanning
Definition: fdset.c:112
time_t * times
Definition: fdset.c:88
static void remove_deleted_entries(FDSet *set)
Definition: fdset.c:297
int gwthread_poll(struct pollfd *fds, long numfds, double timeout)
fdset_callback_t ** callbacks
Definition: fdset.c:96
List * actions
Definition: fdset.c:126
struct pollfd * pollinfo
Definition: fdset.c:83
void * gwlist_extract_first(List *list)
Definition: list.c:305
int deleted_entries
Definition: fdset.c:118
void ** datafields
Definition: fdset.c:97
static int action(int hex)
long timeout
Definition: fdset.c:91
#define POLLERR
Definition: gwpoll.h:96
gw_assert(wtls_machine->packet_to_send!=NULL)
void gwthread_sleep(double seconds)
int fd
Definition: gwpoll.h:85
void debug(const char *place, int err, const char *fmt,...)
Definition: log.c:690
int entries
Definition: fdset.c:85
short revents
Definition: gwpoll.h:87
Definition: fdset.c:70
static int handle_action(FDSet *set, struct action *action)
Definition: fdset.c:226
static void remove_deleted_entries ( FDSet set)
static

Definition at line 297 of file fdset.c.

References FDSet::deleted_entries, pollfd::fd, FDSet::pollinfo, and remove_entry().

Referenced by poller().

298 {
299  int i;
300 
301  i = 0;
302  while (i < set->entries && set->deleted_entries > 0) {
303  if (set->pollinfo[i].fd < 0) {
304  remove_entry(set, i);
305  set->deleted_entries--;
306  } else {
307  i++;
308  }
309  }
310 }
struct pollfd * pollinfo
Definition: fdset.c:83
int deleted_entries
Definition: fdset.c:118
static void remove_entry(FDSet *set, int entry)
Definition: fdset.c:284
int fd
Definition: gwpoll.h:85
static void remove_entry ( FDSet set,
int  entry 
)
static

Definition at line 284 of file fdset.c.

References FDSet::callbacks, FDSet::datafields, FDSet::entries, FDSet::pollinfo, and FDSet::times.

Referenced by fdset_unregister(), and remove_deleted_entries().

285 {
286  if (entry != set->entries - 1) {
287  /* We need to keep the array contiguous, so move the last element
288  * to fill in the hole. */
289  set->pollinfo[entry] = set->pollinfo[set->entries - 1];
290  set->callbacks[entry] = set->callbacks[set->entries - 1];
291  set->datafields[entry] = set->datafields[set->entries - 1];
292  set->times[entry] = set->times[set->entries - 1];
293  }
294  set->entries--;
295 }
time_t * times
Definition: fdset.c:88
fdset_callback_t ** callbacks
Definition: fdset.c:96
struct pollfd * pollinfo
Definition: fdset.c:83
void ** datafields
Definition: fdset.c:97
int entries
Definition: fdset.c:85
static void submit_action ( FDSet set,
struct action action 
)
static

Definition at line 191 of file fdset.c.

References action_destroy(), FDSet::actions, action::done, gw_assert(), gwlist_add_producer(), gwlist_append(), gwlist_consume(), gwlist_create, gwthread_wakeup(), and FDSet::poll_thread.

Referenced by fdset_destroy(), fdset_listen(), fdset_set_timeout(), and fdset_unregister().

192 {
193  List *done;
194  void *sync;
195 
196  gw_assert(set != NULL);
197  gw_assert(action != NULL);
198 
199  done = gwlist_create();
200  gwlist_add_producer(done);
201 
202  action->done = done;
203 
204  gwlist_append(set->actions, action);
206 
207  sync = gwlist_consume(done);
208  gw_assert(sync == action);
209 
210  action_destroy(action);
211 }
void gwlist_append(List *list, void *item)
Definition: list.c:179
List * actions
Definition: fdset.c:126
gw_assert(wtls_machine->packet_to_send!=NULL)
void * gwlist_consume(List *list)
Definition: list.c:427
void gwthread_wakeup(long thread)
List * done
Definition: fdset.c:151
#define gwlist_create()
Definition: list.h:136
void gwlist_add_producer(List *list)
Definition: list.c:383
Definition: list.c:102
long poll_thread
Definition: fdset.c:76
static void action_destroy(struct action *action)
Definition: fdset.c:171
static void submit_action_nosync ( FDSet set,
struct action action 
)
static

Definition at line 216 of file fdset.c.

References FDSet::actions, gwlist_append(), gwthread_wakeup(), and FDSet::poll_thread.

Referenced by fdset_register().

217 {
218  gwlist_append(set->actions, action);
220 }
void gwlist_append(List *list, void *item)
Definition: list.c:179
List * actions
Definition: fdset.c:126
void gwthread_wakeup(long thread)
long poll_thread
Definition: fdset.c:76
See file LICENSE for details about the license agreement for using, modifying, copying or deriving work from this software.