int sel_select(selector_t *sel, sel_send_sig_cb send_sig, long thread_id, void *cb_data, struct timeval *timeout) { int err; struct timeval loc_timeout; sel_wait_list_t wait_entry; if (sel->have_timer_lock) sel->os_hnd->lock(sel->os_hnd, sel->timer_lock); process_timers(sel, (struct timeval *)(&loc_timeout)); if (timeout) { if (cmp_timeval((struct timeval *)(&loc_timeout), timeout) >= 0) memcpy(&loc_timeout, timeout, sizeof(loc_timeout)); } add_sel_wait_list(sel, &wait_entry, send_sig, cb_data, thread_id, &loc_timeout); if (sel->have_timer_lock) sel->os_hnd->unlock(sel->os_hnd, sel->timer_lock); err = process_fds(sel, send_sig, thread_id, cb_data, &loc_timeout); if (sel->have_timer_lock) sel->os_hnd->lock(sel->os_hnd, sel->timer_lock); remove_sel_wait_list(sel, &wait_entry); if (sel->have_timer_lock) sel->os_hnd->unlock(sel->os_hnd, sel->timer_lock); return err; }
static int timeval_to_relative_ms(struct timeval * tv) { struct timeval now; struct timeval dt; long long t; int r; gettimeofday(&now, NULL); r = cmp_timeval(tv, &now); if (r >= 0) { timersub(tv, &now, &dt); } else { timersub(&now, tv, &dt); } t = dt.tv_sec; t *= 1000; t += (dt.tv_usec + 500) / 1000; if (t > INT_MAX) { t = INT_MAX; } else if (t < INT_MIN) { t = INT_MIN; } return r >= 0 ? t : -t; }
static void send_up(sel_timer_t *elem, sel_timer_t **top, sel_timer_t **last) { sel_timer_t *tmp1, *tmp2, *parent; parent = elem->up; while (parent && (cmp_timeval(&elem->timeout, &parent->timeout) < 0)) { tmp1 = elem->left; tmp2 = elem->right; if (parent->left == elem) { elem->left = parent; elem->right = parent->right; if (elem->right) elem->right->up = elem; } else { elem->right = parent; elem->left = parent->left; if (elem->left) elem->left->up = elem; } elem->up = parent->up; if (parent->up) { if (parent->up->left == parent) { parent->up->left = elem; } else { parent->up->right = elem; } } else { *top = elem; } parent->up = elem; parent->left = tmp1; if (parent->left) parent->left->up = parent; parent->right = tmp2; if (parent->right) parent->right->up = parent; if (*last == elem) *last = parent; parent = elem->up; } }
/* * Process timers on selector. The timeout is always set, to a very * long value if no timers are waiting. Note that this *must* be * called with sel->timer_lock held. Note that if this processes * any timers, the timeout will be set to { 0,0 }. */ static void process_timers(selector_t *sel, volatile struct timeval *timeout) { struct timeval now; sel_timer_t *timer; int called = 0; timer = theap_get_top(&sel->timer_heap); gettimeofday(&now, NULL); while (timer && cmp_timeval(&now, &timer->val.timeout) >= 0) { called = 1; theap_remove(&(sel->timer_heap), timer); timer->val.in_heap = 0; if (sel->have_timer_lock) sel->os_hnd->unlock(sel->os_hnd, sel->timer_lock); timer->val.handler(sel, timer, timer->val.user_data); if (sel->have_timer_lock) sel->os_hnd->lock(sel->os_hnd, sel->timer_lock); timer = theap_get_top(&sel->timer_heap); } if (called) { /* If called, set the timeout to zero. */ timeout->tv_sec = 0; timeout->tv_usec = 0; } else if (timer) { gettimeofday(&now, NULL); diff_timeval((struct timeval *) timeout, (struct timeval *) &timer->val.timeout, &now); } else { /* No timers, just set a long time. */ timeout->tv_sec = 100000; timeout->tv_usec = 0; } }
static void add_to_heap(sel_timer_t **top, sel_timer_t **last, sel_timer_t *elem) { sel_timer_t **next; sel_timer_t *parent; #ifdef MASSIVE_DEBUG fprintf(*debug_out, "add_to_heap entry\n"); print_tree(*top, *last); check_tree(*top, *last); #endif elem->left = NULL; elem->right = NULL; elem->up = NULL; if (*top == NULL) { *top = elem; *last = elem; goto out; } find_next_pos(*last, &next, &parent); *next = elem; elem->up = parent; *last = elem; if (cmp_timeval(&elem->timeout, &parent->timeout) < 0) { send_up(elem, top, last); } out: #ifdef MASSIVE_DEBUG fprintf(*debug_out, "add_to_heap exit\n"); print_tree(*top, *last); check_tree(*top, *last); #endif return; }
void sel_select_once(selector_t *sel) { fd_set tmp_read_set; fd_set tmp_write_set; fd_set tmp_except_set; int i; int err; sel_timer_t *timer; struct timeval timeout, *to_time; if (sel->timer_top) { struct timeval now; /* Check for timers to time out. */ gettimeofday(&now, NULL); timer = sel->timer_top; while (cmp_timeval(&now, &timer->timeout) >= 0) { remove_from_heap(&(sel->timer_top), &(sel->timer_last), timer); timer->in_heap = 0; timer->handler(sel, timer, timer->user_data); timer = sel->timer_top; gettimeofday(&now, NULL); if (!timer) goto no_timers; } /* Calculate how long to wait now. */ diff_timeval(&timeout, &sel->timer_top->timeout, &now); to_time = &timeout; } else { no_timers: to_time = NULL; } memcpy(&tmp_read_set, &sel->read_set, sizeof(tmp_read_set)); memcpy(&tmp_write_set, &sel->write_set, sizeof(tmp_write_set)); memcpy(&tmp_except_set, &sel->except_set, sizeof(tmp_except_set)); err = select(sel->maxfd+1, &tmp_read_set, &tmp_write_set, &tmp_except_set, to_time); if (err == 0) { /* A timeout occurred. */ } else if (err < 0) { /* An error occurred. */ if (errno == EINTR) { /* EINTR is ok, just restart the operation. */ timeout.tv_sec = 1; timeout.tv_usec = 0; } else { /* An error is bad, we need to abort. */ syslog(LOG_ERR, "select_loop() - select: %m"); exit(1); } } else { /* We got some I/O. */ for (i=0; i<=sel->maxfd; i++) { if (FD_ISSET(i, &tmp_read_set)) { if (sel->fds[i].handle_read == NULL) { /* Somehow we don't have a handler for this. Just shut it down. */ sel_set_fd_read_handler(sel, i, SEL_FD_HANDLER_DISABLED); } else { sel->fds[i].handle_read(i, sel->fds[i].data); } } if (FD_ISSET(i, &tmp_write_set)) { if (sel->fds[i].handle_write == NULL) { /* Somehow we don't have a handler for this. Just shut it down. */ sel_set_fd_write_handler(sel, i, SEL_FD_HANDLER_DISABLED); } else { sel->fds[i].handle_write(i, sel->fds[i].data); } } if (FD_ISSET(i, &tmp_except_set)) { if (sel->fds[i].handle_except == NULL) { /* Somehow we don't have a handler for this. Just shut it down. */ sel_set_fd_except_handler(sel, i, SEL_FD_HANDLER_DISABLED); } else { sel->fds[i].handle_except(i, sel->fds[i].data); } } } } if (got_sighup) { got_sighup = 0; if (user_sighup_handler != NULL) { user_sighup_handler(); } } if (got_sigint) { got_sigint = 0; if (user_sigint_handler != NULL) { user_sigint_handler(); } } }
static void remove_from_heap(sel_timer_t **top, sel_timer_t **last, sel_timer_t *elem) { sel_timer_t *to_insert; #ifdef MASSIVE_DEBUG fprintf(*debug_out, "remove_from_head entry\n"); print_tree(*top, *last); check_tree(*top, *last); #endif /* First remove the last element from the tree, if it's not what's being removed, we will use it for insertion into the removal place. */ to_insert = *last; if (! to_insert->up) { /* This is the only element in the heap. */ *top = NULL; *last = NULL; goto out; } else { /* Set the new last position, and remove the item we will insert. */ find_prev_elem(to_insert, last); if (to_insert->up->left == to_insert) { to_insert->up->left = NULL; } else { to_insert->up->right = NULL; } } if (elem == to_insert) { /* We got lucky and removed the last element. We are done. */ goto out; } /* Now stick the formerly last element into the removed element's position. */ if (elem->up) { if (elem->up->left == elem) { elem->up->left = to_insert; } else { elem->up->right = to_insert; } } else { /* The head of the tree is being replaced. */ *top = to_insert; } to_insert->up = elem->up; if (elem->left) elem->left->up = to_insert; if (elem->right) elem->right->up = to_insert; to_insert->left = elem->left; to_insert->right = elem->right; if (*last == elem) *last = to_insert; elem = to_insert; /* Now propigate it to the right place in the tree. */ if (elem->up && cmp_timeval(&elem->timeout, &elem->up->timeout) < 0) { send_up(elem, top, last); } else { send_down(elem, top, last); } out: #ifdef MASSIVE_DEBUG fprintf(*debug_out, "remove_from_head exit\n"); print_tree(*top, *last); check_tree(*top, *last); #endif return; }
static void send_down(sel_timer_t *elem, sel_timer_t **top, sel_timer_t **last) { sel_timer_t *tmp1, *tmp2, *left, *right; left = elem->left; while (left) { right = elem->right; /* Choose the smaller of the two below me to swap with. */ if ((right) && (cmp_timeval(&left->timeout, &right->timeout) > 0)) { if (cmp_timeval(&elem->timeout, &right->timeout) > 0) { /* Swap with the right element. */ tmp1 = right->left; tmp2 = right->right; if (elem->up) { if (elem->up->left == elem) { elem->up->left = right; } else { elem->up->right = right; } } else { *top = right; } right->up = elem->up; elem->up = right; right->left = elem->left; right->right = elem; elem->left = tmp1; elem->right = tmp2; if (right->left) right->left->up = right; if (elem->left) elem->left->up = elem; if (elem->right) elem->right->up = elem; if (*last == right) *last = elem; } else goto done; } else { /* The left element is smaller, or the right doesn't exist. */ if (cmp_timeval(&elem->timeout, &left->timeout) > 0) { /* Swap with the left element. */ tmp1 = left->left; tmp2 = left->right; if (elem->up) { if (elem->up->left == elem) { elem->up->left = left; } else { elem->up->right = left; } } else { *top = left; } left->up = elem->up; elem->up = left; left->left = elem; left->right = elem->right; elem->left = tmp1; elem->right = tmp2; if (left->right) left->right->up = left; if (elem->left) elem->left->up = elem; if (elem->right) elem->right->up = elem; if (*last == left) *last = elem; } else goto done; } left = elem->left; } done: return; }
static void check_tree_item(sel_timer_t *curr, int *depth, int max_depth, sel_timer_t **real_last, int *found_last) { if (! curr->left) { if (curr->right) { fprintf(*debug_out, "Tree corrupt B\n"); *((int *) NULL) = 0; } else if (*depth > max_depth) { fprintf(*debug_out, "Tree corrupt C\n"); *((int *) NULL) = 0; } else if (*depth < (max_depth - 1)) { fprintf(*debug_out, "Tree corrupt D\n"); *((int *) NULL) = 0; } else if ((*found_last) && (*depth == max_depth)) { fprintf(*debug_out, "Tree corrupt E\n"); *((int *) NULL) = 0; } else if (*depth == max_depth) { *real_last = curr; } else { *found_last = 1; } } else { if (curr->left->up != curr) { fprintf(*debug_out, "Tree corrupt I\n"); *((int *) NULL) = 0; } if (cmp_timeval(&(curr->left->timeout), &(curr->timeout)) < 0) { fprintf(*debug_out, "Tree corrupt K\n"); *((int *) NULL) = 0; } (*depth)++; check_tree_item(curr->left, depth, max_depth, real_last, found_last); (*depth)--; if (! curr->right) { if (*depth != (max_depth - 1)) { fprintf(*debug_out, "Tree corrupt F\n"); *((int *) NULL) = 0; } if (*found_last) { fprintf(*debug_out, "Tree corrupt G\n"); *((int *) NULL) = 0; } *found_last = 1; } else { if (curr->right->up != curr) { fprintf(*debug_out, "Tree corrupt H\n"); *((int *) NULL) = 0; } if (cmp_timeval(&(curr->right->timeout), &(curr->timeout)) < 0) { fprintf(*debug_out, "Tree corrupt L\n"); *((int *) NULL) = 0; } (*depth)++; check_tree_item(curr->right, depth, max_depth, real_last, found_last); (*depth)--; } } }
/* * return == 0 when timeout * > 0 when successful * < 0 when error */ static int process_fds(selector_t *sel, sel_send_sig_cb send_sig, long thread_id, void *cb_data, volatile struct timeval *timeout) { fd_set tmp_read_set; fd_set tmp_write_set; fd_set tmp_except_set; int i; int err; int num_fds; if (sel->have_fd_lock) sel->os_hnd->lock(sel->os_hnd, sel->fd_lock); memcpy(&tmp_read_set, (void *) &sel->read_set, sizeof(tmp_read_set)); memcpy(&tmp_write_set, (void *) &sel->write_set, sizeof(tmp_write_set)); memcpy(&tmp_except_set, (void *) &sel->except_set, sizeof(tmp_except_set)); num_fds = sel->maxfd+1; if (sel->add_read) { int timeout_invalid; struct timeval ttimeout; timeout_invalid = 1; sel->add_read(sel, &num_fds, &tmp_read_set, &ttimeout, &timeout_invalid, sel->read_cb_data); if (!timeout_invalid && (cmp_timeval(&ttimeout, (struct timeval *)timeout) <= 0)) { *timeout= ttimeout; } } if (sel->have_fd_lock) sel->os_hnd->unlock(sel->os_hnd, sel->fd_lock); err = select(num_fds, &tmp_read_set, &tmp_write_set, &tmp_except_set, (struct timeval *) timeout); if (err <= 0) { if ((err == 0) && (sel->check_timeout)) sel->check_timeout(sel, sel->read_cb_data); goto out; } if (sel->check_read) sel->check_read(sel, &tmp_read_set, sel->read_cb_data); /* We got some I/O. */ for (i=0; i<=sel->maxfd; i++) { if (FD_ISSET(i, &tmp_read_set)) { sel_fd_handler_t handle_read; void *data; fd_state_t *state; if (sel->have_fd_lock) sel->os_hnd->lock(sel->os_hnd, sel->fd_lock); if (sel->fds[i].handle_read == NULL) { /* Somehow we don't have a handler for this. Just shut it down. */ sel_set_fd_read_handler(sel, i, SEL_FD_HANDLER_DISABLED); } else { handle_read = sel->fds[i].handle_read; data = sel->fds[i].data; state = sel->fds[i].state; state->use_count++; if (sel->have_fd_lock) sel->os_hnd->unlock(sel->os_hnd, sel->fd_lock); handle_read(i, data); if (sel->have_fd_lock) sel->os_hnd->lock(sel->os_hnd, sel->fd_lock); state->use_count--; if (state->deleted && state->use_count == 0) { if (state->done) state->done(i, data); free(state); } } if (sel->have_fd_lock) sel->os_hnd->unlock(sel->os_hnd, sel->fd_lock); } if (FD_ISSET(i, &tmp_write_set)) { sel_fd_handler_t handle_write; void *data; fd_state_t *state; if (sel->have_fd_lock) sel->os_hnd->lock(sel->os_hnd, sel->fd_lock); if (sel->fds[i].handle_write == NULL) { /* Somehow we don't have a handler for this. Just shut it down. */ sel_set_fd_write_handler(sel, i, SEL_FD_HANDLER_DISABLED); } else { handle_write = sel->fds[i].handle_write; data = sel->fds[i].data; state = sel->fds[i].state; state->use_count++; if (sel->have_fd_lock) sel->os_hnd->unlock(sel->os_hnd, sel->fd_lock); handle_write(i, data); if (sel->have_fd_lock) sel->os_hnd->lock(sel->os_hnd, sel->fd_lock); state->use_count--; if (state->deleted && state->use_count == 0) { if (state->done) state->done(i, data); free(state); } } if (sel->have_fd_lock) sel->os_hnd->unlock(sel->os_hnd, sel->fd_lock); } if (FD_ISSET(i, &tmp_except_set)) { sel_fd_handler_t handle_except; void *data; fd_state_t *state; if (sel->have_fd_lock) sel->os_hnd->lock(sel->os_hnd, sel->fd_lock); if (sel->fds[i].handle_except == NULL) { /* Somehow we don't have a handler for this. Just shut it down. */ sel_set_fd_except_handler(sel, i, SEL_FD_HANDLER_DISABLED); } else { handle_except = sel->fds[i].handle_except; data = sel->fds[i].data; state = sel->fds[i].state; state->use_count++; if (sel->have_fd_lock) sel->os_hnd->unlock(sel->os_hnd, sel->fd_lock); handle_except(i, data); if (sel->have_fd_lock) sel->os_hnd->lock(sel->os_hnd, sel->fd_lock); state->use_count--; if (state->deleted && state->use_count == 0) { if (state->done) state->done(i, data); free(state); } } if (sel->have_fd_lock) sel->os_hnd->unlock(sel->os_hnd, sel->fd_lock); } } out: return err; }
static int heap_cmp_key(heap_val_t *v1, heap_val_t *v2) { return cmp_timeval(&v1->timeout, &v2->timeout); }