loop.c File Reference

a simple object-oriented event loop More...

#include <stdlib.h>
#include <stddef.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/epoll.h>
#include <signal.h>
#include <string.h>
#include <time.h>
#include "debug.h"
#include "process.h"
#include "socket.h"
#include "loop.h"
#include "list.h"

Functions

int co_loop_create (void)
 sets up the event loop
 
int co_loop_destroy (void)
 closes the event loop
 
void co_loop_start (void)
 starts the event loop, listening for messages on sockets via _co_loop_poll_sockets
 
void co_loop_stop (void)
 stops the event loop
 
int co_loop_add_process (co_obj_t *proc)
 adds a process to the event loop (for it to listen for) More...
 
int co_loop_remove_process (pid_t pid)
 removes a process from the event loop More...
 
int co_loop_add_socket (co_obj_t *new_sock, co_obj_t *context)
 adds a new socket to the event loop (for it to listen on) More...
 
int co_loop_remove_socket (co_obj_t *old_sock, co_obj_t *context)
 removes a socket from the event loop More...
 
co_obj_tco_loop_get_socket (char *uri, co_obj_t *context)
 gets a socket that is registered with the event loop More...
 
int co_loop_add_timer (co_obj_t *new_timer, co_obj_t *context)
 schedules a new timer with the event loop More...
 
int co_loop_remove_timer (co_obj_t *old_timer, co_obj_t *context)
 removes a timer from the event loop More...
 
co_obj_tco_loop_get_timer (void *ptr, co_obj_t *context)
 gets a socket that is registered with the event loop More...
 
int co_loop_set_timer (co_obj_t *old_timer, long msecs, co_obj_t *context)
 sets timer to expire in msecs from now More...
 
co_obj_tco_timer_create (struct timeval deadline, co_cb_t timer_cb, void *ptr)
 malloc and initialize a timer More...
 

Detailed Description

a simple object-oriented event loop

Author
Josh King (jheretic), jking.nosp@m.@cha.nosp@m.mbana.nosp@m..net

Function Documentation

int co_loop_add_process ( co_obj_t proc)

adds a process to the event loop (for it to listen for)

Parameters
procthe process to be added

References co_list_append().

310  {
311  CHECK(IS_PROCESS(proc),"Not a process.");
312  CHECK(co_list_append(processes,proc),"Failed to add process %d",((co_process_t*)proc)->pid);
313  ((co_process_t*)proc)->registered = true;
314  return 1;
315 error:
316  return 0;
317 }
int co_list_append(co_obj_t *list, co_obj_t *new_obj)
insert new item at end of list
Definition: list.c:425
Definition: process.h:58
int co_loop_add_socket ( co_obj_t new_sock,
co_obj_t context 
)

adds a new socket to the event loop (for it to listen on)

Parameters
new_sockthe new socket to be added
contexta co_obj_t context pointer (currently unused)

References co_list_append(), co_list_contains(), co_list_parse(), and co_loop_remove_socket().

Referenced by _watch(), and main().

330  {
331  DEBUG("Adding socket to event loop.");
332  CHECK(IS_SOCK(new_sock),"Not a socket.");
333  co_socket_t *sock = (co_socket_t*)new_sock;
334  co_fd_t *fd = (co_fd_t*)context;
335  CHECK(fd->socket == sock,"FD does not match socket");
336  co_obj_t *node = NULL;
337  struct epoll_event event;
338 
339  memset(&event, 0, sizeof(struct epoll_event));
340  event.events = EPOLLIN;
341 
342  if((node = co_list_parse(sockets, _co_loop_match_socket_i, sock->uri))) {
343  CHECK((node == (co_obj_t*)sock), "Different socket with URI %s already registered.", sock->uri);
344  if ((sock->listen) && (sock->fd->fd > 0) && sock->fd_registered) {
345  CHECK(co_list_contains(sock->rfd_lst,(co_obj_t*)fd),"Socket does not contain FD");
346  DEBUG("Adding RFD %d to epoll.", fd->fd);
347  event.data.ptr = (co_obj_t*)fd;
348  CHECK((epoll_ctl(poll_fd, EPOLL_CTL_ADD, fd->fd, &event)) != -1, "Failed to add receive FD epoll event.");
349  } else {
350  SENTINEL("Socket %s already registered.", sock->uri);
351  }
352  } else if((sock->listen) && (sock->fd->fd > 0) && !sock->fd_registered) {
353  CHECK(fd = sock->fd,"Invalid listening socket");
354  DEBUG("Adding FD %d to epoll.", sock->fd->fd);
355  event.data.ptr = (co_obj_t*)fd;
356  CHECK((epoll_ctl(poll_fd, EPOLL_CTL_ADD, sock->fd->fd, &event)) != -1, "Failed to add listen FD epoll event.");
357  sock->fd_registered = true;
358  co_list_append(sockets, (co_obj_t*)sock);
359  return 1;
360  } else {
361  co_loop_remove_socket((co_obj_t*)sock, NULL);
362  SENTINEL("Unknown error registering socket %s.", sock->uri);
363  }
364 
365 error:
366  return 0;
367 }
int co_list_append(co_obj_t *list, co_obj_t *new_obj)
insert new item at end of list
Definition: list.c:425
Definition: obj.h:131
Definition: socket.h:65
int co_list_contains(co_obj_t *list, co_obj_t *item)
determine if list contains specified item
Definition: list.c:258
Definition: socket.h:48
int co_loop_remove_socket(co_obj_t *old_sock, co_obj_t *context)
removes a socket from the event loop
Definition: loop.c:375
co_obj_t * co_list_parse(co_obj_t *list, co_iter_t iter, void *context)
process list with given iterator function
Definition: list.c:233
int co_loop_add_timer ( co_obj_t new_timer,
co_obj_t context 
)

schedules a new timer with the event loop

Parameters
timerthe timer to schedule
contexta co_obj_t context pointer (currently unused)

References co_list_append(), co_list_insert_before(), and co_list_parse().

Referenced by _schedule(), and co_loop_set_timer().

397  {
398  co_timer_t *timer = (co_timer_t*)new_timer;
399  co_obj_t *node = NULL;
400  struct timeval now;
401 
402  if (timer->pending)
403  return 0;
404 
405  _co_loop_gettime(&now);
406  CHECK(timer->timer_cb,"No callback function associated with timer");
407  CHECK(_co_loop_tv_diff(&timer->deadline,&now) > -1000,"Invalid timer deadline: %ld.%06ld %ld.%06ld",
408  timer->deadline.tv_sec,timer->deadline.tv_usec,
409  now.tv_sec,now.tv_usec);
410 
411  CHECK(co_list_parse(timers,_co_loop_match_timer_i,timer->ptr) == NULL,"Timer already scheduled");
412 
413  // insert into list in chronological order
414  if((node = co_list_parse(timers, _co_loop_compare_timer_i, &timer->deadline))) {
415  CHECK(co_list_insert_before(timers,(co_obj_t*)timer,node),"Failed to insert timer.");
416  } else {
417  CHECK(co_list_append(timers,(co_obj_t*)timer),"Failed to insert timer.");
418  }
419 
420 // DEBUG("Successfully added timer %ld.%06ld %p",timer->deadline.tv_sec,timer->deadline.tv_usec,timer->ptr);
421 
422  timer->pending = true;
423  return 1;
424 
425 error:
426  return 0;
427 }
int co_list_append(co_obj_t *list, co_obj_t *new_obj)
insert new item at end of list
Definition: list.c:425
int co_list_insert_before(co_obj_t *list, co_obj_t *new_obj, co_obj_t *this_obj)
insert new item in list before specified item
Definition: list.c:273
Definition: obj.h:131
Definition: loop.h:48
co_obj_t * co_list_parse(co_obj_t *list, co_iter_t iter, void *context)
process list with given iterator function
Definition: list.c:233
co_obj_t* co_loop_get_socket ( char *  uri,
co_obj_t context 
)

gets a socket that is registered with the event loop

Parameters
uria URI to match against the available sockets
contexta co_obj_t context pointer (currently unused)

References co_list_parse().

389  {
390  co_obj_t *sock = NULL;
391  CHECK((sock = co_list_parse(sockets, _co_loop_match_socket_i, uri)), "Failed to find socket %s", uri);
392  return sock;
393 error:
394  return NULL;
395 }
Definition: obj.h:131
co_obj_t * co_list_parse(co_obj_t *list, co_iter_t iter, void *context)
process list with given iterator function
Definition: list.c:233
co_obj_t* co_loop_get_timer ( void *  ptr,
co_obj_t context 
)

gets a socket that is registered with the event loop

Parameters
ptrvoid pointer to match against the available timers
contexta co_obj_t context pointer (currently unused)

References co_list_parse().

Referenced by _unschedule().

448  {
449  co_obj_t *timer = NULL;
450  CHECK((timer = co_list_parse(timers, _co_loop_match_timer_i, ptr)), "Failed to find timer: %p",ptr);
451  return timer;
452 error:
453  return NULL;
454 }
Definition: obj.h:131
co_obj_t * co_list_parse(co_obj_t *list, co_iter_t iter, void *context)
process list with given iterator function
Definition: list.c:233
int co_loop_remove_process ( pid_t  pid)

removes a process from the event loop

Parameters
pidthe id of the process to be removed

References co_list_delete(), and co_list_parse().

319  {
320  co_obj_t *proc = NULL;
321  CHECK((proc = co_list_parse(processes, _co_loop_match_process_i, &pid)), "Failed to delete process %d!", pid);
322  proc = co_list_delete(processes, proc);
323  ((co_process_t*)proc)->registered = false;
324  return 1;
325 
326 error:
327  return 0;
328 }
co_obj_t * co_list_delete(co_obj_t *list, co_obj_t *item)
delete specified item from list
Definition: list.c:457
Definition: obj.h:131
Definition: process.h:58
co_obj_t * co_list_parse(co_obj_t *list, co_iter_t iter, void *context)
process list with given iterator function
Definition: list.c:233
int co_loop_remove_socket ( co_obj_t old_sock,
co_obj_t context 
)

removes a socket from the event loop

Parameters
old_sockthe socket to be removed
contexta co_obj_t context pointer (currently unused)

References co_list_delete(), and co_list_parse().

Referenced by co_loop_add_socket().

375  {
376  co_socket_t *sock = (co_socket_t*)old_sock;
377  co_obj_t *node = NULL;
378  CHECK((node = co_list_parse(sockets, _co_loop_match_socket_i, sock->uri)), "Failed to delete socket %s!", sock->uri);
379  co_list_delete(sockets, node);
380  sock->fd_registered = false;
381  epoll_ctl(poll_fd, EPOLL_CTL_DEL, sock->fd->fd, NULL);
382  CHECK(co_list_parse(sock->rfd_lst,_co_loop_remove_fd_i,NULL) == NULL,"Failed to delete rfd_lst");
383  return 1;
384 
385 error:
386  return 0;
387 }
co_obj_t * co_list_delete(co_obj_t *list, co_obj_t *item)
delete specified item from list
Definition: list.c:457
Definition: obj.h:131
Definition: socket.h:65
co_obj_t * co_list_parse(co_obj_t *list, co_iter_t iter, void *context)
process list with given iterator function
Definition: list.c:233
int co_loop_remove_timer ( co_obj_t old_timer,
co_obj_t context 
)

removes a timer from the event loop

Parameters
old_timerthe timer to remove from list
contexta co_obj_t context pointer (currently unused)

References co_list_delete(), and co_list_parse().

Referenced by _unschedule(), and co_loop_set_timer().

429  {
430  co_timer_t *timer = (co_timer_t*)old_timer;
431  co_obj_t *node = NULL;
432 
433  if (!timer->pending)
434  return 0;
435 
436  // remove from list
437  CHECK((node = co_list_parse(timers, _co_loop_match_timer_i, timer->ptr)),
438  "Failed to delete timer %ld.%06ld %p",timer->deadline.tv_sec,timer->deadline.tv_usec,timer->ptr);
439  co_list_delete(timers,node);
440 
441  timer->pending = false;
442  return 1;
443 
444 error:
445  return 0;
446 }
co_obj_t * co_list_delete(co_obj_t *list, co_obj_t *item)
delete specified item from list
Definition: list.c:457
Definition: obj.h:131
Definition: loop.h:48
co_obj_t * co_list_parse(co_obj_t *list, co_iter_t iter, void *context)
process list with given iterator function
Definition: list.c:233
int co_loop_set_timer ( co_obj_t timer,
long  msecs,
co_obj_t context 
)

sets timer to expire in msecs from now

Parameters
timerthe timer to set
msecsnumber of milliseconds
contexta co_obj_t context pointer (currently unused)

References co_loop_add_timer(), and co_loop_remove_timer().

456  {
457  co_timer_t *timer = (co_timer_t*)old_timer;
458  struct timeval *deadline = &timer->deadline;
459 
460  if (timer->pending)
461  co_loop_remove_timer((co_obj_t*)timer,context);
462 
463  _co_loop_gettime(&timer->deadline);
464 
465  deadline->tv_sec += msecs / 1000;
466  deadline->tv_usec += (msecs % 1000) * 1000;
467 
468  if (deadline->tv_usec > 1000000) {
469  deadline->tv_sec++;
470  deadline->tv_usec %= 1000000;
471  }
472 
473  return co_loop_add_timer((co_obj_t*)timer,context);
474 }
int co_loop_remove_timer(co_obj_t *old_timer, co_obj_t *context)
removes a timer from the event loop
Definition: loop.c:429
Definition: obj.h:131
int co_loop_add_timer(co_obj_t *new_timer, co_obj_t *context)
schedules a new timer with the event loop
Definition: loop.c:397
Definition: loop.h:48
co_obj_t* co_timer_create ( struct timeval  deadline,
co_cb_t  timer_cb,
void *  ptr 
)

malloc and initialize a timer

Parameters
sizetimer struct size
prototimer protocol

Referenced by _schedule().

476  {
477  co_timer_t *new_timer = h_calloc(1,sizeof(co_timer_t));
478 
479  new_timer->_header._type = _ext8;
480  new_timer->_header._ref = 0;
481  new_timer->_header._flags = 0;
482  new_timer->_exttype = _co_timer;
483  new_timer->_len = sizeof(co_timer_t);
484  new_timer->pending = false;
485  if (deadline.tv_sec > 0 || deadline.tv_usec > 0) {
486  new_timer->deadline.tv_sec = deadline.tv_sec;
487  new_timer->deadline.tv_usec = deadline.tv_usec;
488  } else
489  new_timer->deadline = (struct timeval){0};
490  new_timer->timer_cb = timer_cb ? timer_cb : NULL;
491  new_timer->ptr = ptr ? ptr : (void*)new_timer;
492 
493  return (co_obj_t*)new_timer;
494 }
Definition: obj.h:131
Definition: loop.h:48