/** @internal Main loop. * * The function @c su_port_run() waits for wait objects and the timers * associated with the port object. When any wait object is signaled or * timer is expired, it invokes the callbacks, and returns waiting. * * The function @c su_port_run() runs until @c su_port_break() is called * from a callback. * * @param self pointer to port object * */ void su_base_port_run(su_port_t *self) { su_duration_t tout = 0, tout2 = 0; assert(su_port_own_thread(self)); for (self->sup_running = 1; self->sup_running;) { tout = self->sup_max_defer; if (self->sup_prepoll) self->sup_prepoll(self->sup_pp_magic, self->sup_pp_root); if (self->sup_head) self->sup_vtable->su_port_getmsgs(self); if (self->sup_timers || self->sup_deferrable) { su_time_t now = su_now(); su_timer_expire(&self->sup_timers, &tout, now); su_timer_expire(&self->sup_deferrable, &tout2, now); } if (!self->sup_running) break; if (self->sup_head) /* if there are messages do a quick wait */ tout = 0; self->sup_vtable->su_port_wait_events(self, tout); } }
/** Unregister a su_wait_t object. * * The function su_devpoll_port_unregister() unregisters a su_wait_t object. The * wait object, a callback function and a argument are removed from the * port object. * * @param self - pointer to port object * @param root - pointer to root object * @param wait - pointer to wait object * @param callback - callback function pointer (may be NULL) * @param arg - argument given to callback function when it is invoked * (may be NULL) * * @deprecated Use su_devpoll_port_deregister() instead. * * @return Nonzero index of the wait object, or -1 upon an error. */ int su_devpoll_port_unregister(su_port_t *self, su_root_t *root, su_wait_t *wait, su_wakeup_f callback, /* XXX - ignored */ su_wakeup_arg_t *arg) { int i, I; struct su_devpoll *ser; assert(self); assert(su_port_own_thread(self)); I = self->sup_max_index; for (i = 1; i <= I; i++) { ser = self->sup_indices[i]; if (ser->ser_cb && arg == ser->ser_arg && SU_WAIT_CMP(wait[0], ser->ser_wait[0]) == 0) return su_devpoll_port_deregister0(self, ser->ser_id, 0); } su_seterrno(ENOENT); return -1; }
/** @internal * Unregister all su_wait_t objects. * * The function su_poll_port_unregister_all() unregisters all su_wait_t objects * and destroys all queued timers associated with given root object. * * @param self - pointer to port object * @param root - pointer to root object * * @return Number of wait objects removed. */ int su_poll_port_unregister_all(su_port_t *self, su_root_t *root) { int i, j, index, N; int *indices, *reverses; su_wait_t *waits; su_wakeup_f *wait_cbs; su_wakeup_arg_t**wait_args; su_root_t **wait_roots; assert(su_port_own_thread(self)); N = self->sup_n_waits; indices = self->sup_indices; reverses = self->sup_reverses; waits = self->sup_waits; wait_cbs = self->sup_wait_cbs; wait_args = self->sup_wait_args; wait_roots = self->sup_wait_roots; for (i = j = 0; i < N; i++) { index = reverses[i]; assert(index > 0 && indices[index] == i); if (wait_roots[i] == root) { /* XXX - we should free all resources associated with this, too */ if (i < self->sup_pri_offset) self->sup_pri_offset--; indices[index] = indices[0]; indices[0] = -index; continue; } if (i != j) { indices[index] = j; reverses[j] = reverses[i]; waits[j] = waits[i]; wait_cbs[j] = wait_cbs[i]; wait_args[j] = wait_args[i]; wait_roots[j] = wait_roots[i]; } j++; } for (i = j; i < N; i++) { reverses[i] = -1; wait_cbs[i] = NULL; wait_args[i] = NULL; wait_roots[i] = NULL; } memset(&waits[j], 0, (char *)&waits[N] - (char *)&waits[j]); self->sup_n_waits = j; self->sup_registers++; return N - j; }
/** @internal Block until wait object is signaled or timeout. * * This function waits for wait objects and the timers associated with * the root object. When any wait object is signaled or timer is * expired, it invokes the callbacks. * * This function returns when a callback has been invoked or @c tout * milliseconds is elapsed. * * @param self pointer to port * @param tout timeout in milliseconds * * @return * Milliseconds to the next invocation of timer, or @c SU_WAIT_FOREVER if * there are no active timers. */ su_duration_t su_base_port_step(su_port_t *self, su_duration_t tout) { su_time_t now = su_now(); assert(su_port_own_thread(self)); if (self->sup_prepoll) self->sup_prepoll(self->sup_pp_magic, self->sup_pp_root); if (self->sup_head) self->sup_vtable->su_port_getmsgs(self); if (self->sup_timers) su_timer_expire(&self->sup_timers, &tout, now); /* XXX: why isn't the timeout ignored here? */ if (self->sup_deferrable) su_timer_expire(&self->sup_deferrable, &tout, now); /* if there are messages do a quick wait */ if (self->sup_head) tout = 0; if (self->sup_vtable->su_port_wait_events(self, tout)) tout = 0; else tout = SU_WAIT_FOREVER; if (self->sup_head) { if (self->sup_vtable->su_port_getmsgs(self)) { /* Check for wait events that may have been generated by messages */ if (self->sup_vtable->su_port_wait_events(self, 0)) tout = 0; } } if (self->sup_timers || self->sup_deferrable) { su_duration_t tout2 = SU_WAIT_FOREVER; now = su_now(); su_timer_expire(&self->sup_timers, &tout, now); su_timer_expire(&self->sup_deferrable, &tout2, now); if (tout == SU_WAIT_FOREVER && tout2 != SU_WAIT_FOREVER) { if (tout2 < self->sup_max_defer) tout2 = self->sup_max_defer; tout = tout2; } } if (self->sup_head) tout = 0; return tout; }
/**Set mask for a registered event. @internal * * The function su_poll_port_eventmask() sets the mask describing events * that can signal the registered callback. * * @param port pointer to port object * @param index registration index * @param socket socket * @param events new event mask * * @retval 0 when successful, * @retval -1 upon an error. */ int su_poll_port_eventmask(su_port_t *self, int index, int socket, int events) { int n; assert(self); assert(su_port_own_thread(self)); if (index <= 0 || index > self->sup_size_waits) return su_seterrno(EBADF); n = self->sup_indices[index]; if (n < 0) return su_seterrno(EBADF); return su_wait_mask(&self->sup_waits[n], socket, events); }
/* This version can help tuning... */ void su_base_port_run_tune(su_port_t *self) { int i; int timers = 0, messages = 0, events = 0; su_duration_t tout = 0, tout2 = 0; su_time_t started = su_now(), woken = started, bedtime = woken; assert(su_port_own_thread(self)); for (self->sup_running = 1; self->sup_running;) { tout = self->sup_max_defer; timers = 0, messages = 0; if (self->sup_prepoll) self->sup_prepoll(self->sup_pp_magic, self->sup_pp_root); if (self->sup_head) messages = self->sup_vtable->su_port_getmsgs(self); if (self->sup_timers || self->sup_deferrable) { su_time_t now = su_now(); timers = su_timer_expire(&self->sup_timers, &tout, now) + su_timer_expire(&self->sup_deferrable, &tout2, now); } if (!self->sup_running) break; if (self->sup_head) /* if there are messages do a quick wait */ tout = 0; bedtime = su_now(); events = self->sup_vtable->su_port_wait_events(self, tout); woken = su_now(); if (messages || timers || events) SU_DEBUG_1(("su_port_run(%p): %.6f: %u messages %u timers %u " "events slept %.6f/%.3f\n", self, su_time_diff(woken, started), messages, timers, events, su_time_diff(woken, bedtime), tout * 1e-3)); if (!self->sup_running) break; } }
/** Deregister a su_wait_t object. * * Deregisters a registration by index. The wait object, a callback * function and a argument are removed from the port object. The wait * object is destroyed. * * @param self - pointer to port object * @param i - registration index * * @return Index of the wait object, or -1 upon an error. */ int su_poll_port_deregister(su_port_t *self, int i) { su_wait_t wait[1] = { SU_WAIT_INIT }; int retval; assert(self); assert(su_port_own_thread(self)); if (i <= 0 || i > self->sup_size_waits) return su_seterrno(EBADF); if (self->sup_indices[i] < 0) return su_seterrno(EBADF); retval = su_poll_port_deregister0(self, i, 1); su_wait_destroy(wait); return retval; }
/** @internal * Unregister all su_wait_t objects of given su_root_t instance. * * The function su_devpoll_port_unregister_all() unregisters all su_wait_t * objects associated with given root object. * * @param self - pointer to port object * @param root - pointer to root object * * @return Number of wait objects removed. */ int su_devpoll_port_unregister_all(su_port_t *self, su_root_t *root) { int i, I, n; struct su_devpoll *ser; assert(self); assert(root); assert(su_port_own_thread(self)); I = self->sup_max_index; for (i = 1, n = 0; i <= I; i++) { ser = self->sup_indices[i]; if (ser->ser_root != root) continue; su_devpoll_port_deregister0(self, ser->ser_id, 0); n++; } return n; }
/** Unregister a su_wait_t object. * * The function su_poll_port_unregister() unregisters a su_wait_t object. The * wait object, a callback function and a argument are removed from the * port object. * * @param self - pointer to port object * @param root - pointer to root object * @param wait - pointer to wait object * @param callback - callback function pointer (may be NULL) * @param arg - argument given to callback function when it is invoked * (may be NULL) * * @deprecated Use su_poll_port_deregister() instead. * * @return Nonzero index of the wait object, or -1 upon an error. */ int su_poll_port_unregister(su_port_t *self, su_root_t *root, su_wait_t *wait, su_wakeup_f callback, /* XXX - ignored */ su_wakeup_arg_t *arg) { int n, N; assert(self); assert(su_port_own_thread(self)); N = self->sup_n_waits; for (n = 0; n < N; n++) { if (SU_WAIT_CMP(wait[0], self->sup_waits[n]) == 0) { return su_poll_port_deregister0(self, self->sup_reverses[n], 0); } } su_seterrno(ENOENT); return -1; }
/** Execute the @a function by @a task thread. * * @retval 0 if successful * @retval -1 upon an error */ int su_task_execute(su_task_r const task, int (*function)(void *), void *arg, int *return_value) { int dummy; if (function == NULL) return (errno = EFAULT), -1; if (return_value == NULL) return_value = &dummy; if (!su_port_own_thread(task->sut_port)) { return su_port_execute(task, function, arg, return_value); } else { int value = function(arg); if (return_value) *return_value = value; return 0; } }
/** @internal * * Register a #su_wait_t object. The wait object, a callback function and * an argument pointer is stored in the port object. The callback function * will be called when the wait object is signaled. * * Please note if identical wait objects are inserted, only first one is * ever signalled. * * @param self pointer to port * @param root pointer to root object * @param waits pointer to wait object * @param callback callback function pointer * @param arg argument given to callback function when it is invoked * @param priority relative priority of the wait object * (0 is normal, 1 important, 2 realtime) * * @return * Positive index of the wait object, * or -1 upon an error. */ int su_devpoll_port_register(su_port_t *self, su_root_t *root, su_wait_t *wait, su_wakeup_f callback, su_wakeup_arg_t *arg, int priority) { int i, j, n; struct su_devpoll *ser; struct su_devpoll **indices = self->sup_indices; struct su_devpoll **devpoll_by_socket = self->sup_devpoll_by_socket; su_home_t *h = su_port_home(self); struct pollfd pollfd[1]; assert(su_port_own_thread(self)); if (wait->fd < 0) return su_seterrno(EINVAL); n = self->sup_size_indices; if (n >= SU_WAIT_MAX) return su_seterrno(ENOMEM); ser = indices[0]; if (!ser) { i = self->sup_max_index, j = i == 0 ? 15 : i + 16; if (j >= self->sup_size_indices) { /* Reallocate index table */ n = n < 1024 ? 2 * n : n + 1024; indices = su_realloc(h, indices, n * sizeof(indices[0])); if (!indices) return -1; self->sup_indices = indices; self->sup_size_indices = n; } /* Allocate registrations */ ser = su_zalloc(h, (j - i) * (sizeof *ser)); if (!ser) return -1; indices[0] = ser; for (i++; i <= j; i++) { ser->ser_id = i; ser->ser_next = i < j ? ser + 1 : NULL; indices[i] = ser++; } self->sup_max_index = j; ser = indices[0]; } if ((size_t)wait->fd >= self->sup_n_devpoll_by_socket) { size_t n_devpoll_by_socket = ((size_t)wait->fd + 32) / 32 * 32; devpoll_by_socket = su_realloc(h, devpoll_by_socket, n_devpoll_by_socket * (sizeof devpoll_by_socket[0])); if (devpoll_by_socket == NULL) return -1; memset(&devpoll_by_socket[self->sup_n_devpoll_by_socket], 0, (char *)&devpoll_by_socket[n_devpoll_by_socket] - (char *)&devpoll_by_socket[self->sup_n_devpoll_by_socket]); self->sup_devpoll_by_socket = devpoll_by_socket; self->sup_n_devpoll_by_socket = n_devpoll_by_socket; } if (devpoll_by_socket[wait->fd]) /* XXX - we should lift this limitation with epoll, too */ return errno = EEXIST, -1; i = ser->ser_id; pollfd->fd = wait->fd; pollfd->events = wait->events & ~POLLREMOVE; pollfd->revents = 0; if (write(self->sup_devpoll, pollfd, (sizeof pollfd)) != (sizeof pollfd)) { return errno = EIO, -1; } indices[0] = ser->ser_next; devpoll_by_socket[wait->fd] = ser; ser->ser_next = NULL; *ser->ser_wait = *wait; ser->ser_cb = callback; ser->ser_arg = arg; ser->ser_root = root; self->sup_registers++; self->sup_n_registrations++; return i; /* return index */ }
/** @internal * * Register a @c su_wait_t object. The wait object, a callback function and * an argument pointer is stored in the port object. The callback function * will be called when the wait object is signaled. * * Please note if identical wait objects are inserted, only first one is * ever signalled. * * @param self pointer to port * @param root pointer to root object * @param waits pointer to wait object * @param callback callback function pointer * @param arg argument given to callback function when it is invoked * @param priority relative priority of the wait object * (0 is normal, 1 important, 2 realtime) * * @return * Positive index of the wait object, * or -1 upon an error. */ int su_poll_port_register(su_port_t *self, su_root_t *root, su_wait_t *wait, su_wakeup_f callback, su_wakeup_arg_t *arg, int priority) { int i, j, n; assert(su_port_own_thread(self)); n = self->sup_n_waits; if (n >= SU_WAIT_MAX) return su_seterrno(ENOMEM); if (n >= self->sup_size_waits) { su_home_t *h = self->sup_home; /* Reallocate size arrays */ int size; int *indices; int *reverses; su_wait_t *waits; su_wakeup_f *wait_cbs; su_wakeup_arg_t **wait_args; su_root_t **wait_tasks; if (self->sup_size_waits == 0) size = su_root_size_hint; else size = 2 * self->sup_size_waits; if (size < SU_WAIT_MIN) size = SU_WAIT_MIN; /* Too large */ if (-3 - size > 0) return (errno = ENOMEM), -1; indices = su_realloc(h, self->sup_indices, (size + 1) * sizeof(*indices)); if (indices) { self->sup_indices = indices; if (self->sup_size_waits == 0) indices[0] = -1; for (i = self->sup_size_waits + 1; i <= size; i++) indices[i] = -1 - i; } reverses = su_realloc(h, self->sup_reverses, size * sizeof(*waits)); if (reverses) { for (i = self->sup_size_waits; i < size; i++) reverses[i] = -1; self->sup_reverses = reverses; } waits = su_realloc(h, self->sup_waits, size * sizeof(*waits)); if (waits) self->sup_waits = waits; wait_cbs = su_realloc(h, self->sup_wait_cbs, size * sizeof(*wait_cbs)); if (wait_cbs) self->sup_wait_cbs = wait_cbs; wait_args = su_realloc(h, self->sup_wait_args, size * sizeof(*wait_args)); if (wait_args) self->sup_wait_args = wait_args; /* Add sup_wait_roots array, if needed */ wait_tasks = su_realloc(h, self->sup_wait_roots, size * sizeof(*wait_tasks)); if (wait_tasks) self->sup_wait_roots = wait_tasks; if (!(indices && reverses && waits && wait_cbs && wait_args && wait_tasks)) { return -1; } self->sup_size_waits = size; } i = -self->sup_indices[0]; assert(i <= self->sup_size_waits); if (priority > 0) { /* Insert */ for (n = self->sup_n_waits; n > 0; n--) { j = self->sup_reverses[n-1]; assert(self->sup_indices[j] == n - 1); self->sup_indices[j] = n; self->sup_reverses[n] = j; self->sup_waits[n] = self->sup_waits[n-1]; self->sup_wait_cbs[n] = self->sup_wait_cbs[n-1]; self->sup_wait_args[n] = self->sup_wait_args[n-1]; self->sup_wait_roots[n] = self->sup_wait_roots[n-1]; } self->sup_pri_offset++; } else { /* Append - no need to move anything */ n = self->sup_n_waits; } self->sup_n_waits++; self->sup_indices[0] = self->sup_indices[i]; /* Free index */ self->sup_indices[i] = n; self->sup_reverses[n] = i; self->sup_waits[n] = *wait; self->sup_wait_cbs[n] = callback; self->sup_wait_args[n] = arg; self->sup_wait_roots[n] = root; self->sup_registers++; /* Just like epoll, we return -1 or positive integer */ return i; }
/** @internal Deinit a base implementation of port. */ void su_base_port_deinit(su_port_t *self) { if (su_port_own_thread(self)) su_port_release(self); }