static void io_loop_handle_timeouts_real(struct ioloop *ioloop) { struct priorityq_item *item; struct timeval tv, tv_call; unsigned int t_id; if (gettimeofday(&ioloop_timeval, NULL) < 0) i_fatal("gettimeofday(): %m"); /* Don't bother comparing usecs. */ if (unlikely(ioloop_time > ioloop_timeval.tv_sec)) { /* time moved backwards */ io_loops_timeouts_update(-(long)(ioloop_time - ioloop_timeval.tv_sec)); ioloop->time_moved_callback(ioloop_time, ioloop_timeval.tv_sec); /* the callback may have slept, so check the time again. */ if (gettimeofday(&ioloop_timeval, NULL) < 0) i_fatal("gettimeofday(): %m"); } else if (unlikely(ioloop_timeval.tv_sec > ioloop->next_max_time)) { io_loops_timeouts_update(ioloop_timeval.tv_sec - ioloop->next_max_time); /* time moved forwards */ ioloop->time_moved_callback(ioloop->next_max_time, ioloop_timeval.tv_sec); } ioloop_time = ioloop_timeval.tv_sec; tv_call = ioloop_timeval; while ((item = priorityq_peek(ioloop->timeouts)) != NULL) { struct timeout *timeout = (struct timeout *)item; /* use tv_call to make sure we don't get to infinite loop in case callbacks update ioloop_timeval. */ if (timeout_get_wait_time(timeout, &tv, &tv_call) > 0) break; /* update timeout's next_run and reposition it in the queue */ timeout_reset_timeval(timeout, &tv_call); if (timeout->log != NULL) { ioloop->cur_log = timeout->log; io_loop_log_ref(ioloop->cur_log); i_set_failure_prefix(timeout->log->prefix); } t_id = t_push(); timeout->callback(timeout->context); if (t_pop() != t_id) { i_panic("Leaked a t_pop() call in timeout handler %p", (void *)timeout->callback); } if (ioloop->cur_log != NULL) { io_loop_log_unref(&ioloop->cur_log); i_set_failure_prefix(ioloop->default_log_prefix); } } }
struct priorityq_item *priorityq_pop(struct priorityq *pq) { struct priorityq_item *item; item = priorityq_peek(pq); if (item != NULL) { priorityq_remove_idx(pq, 0); item->idx = UINT_MAX; } return item; }
int io_loop_get_wait_time(struct ioloop *ioloop, struct timeval *tv_r) { struct timeval tv_now; struct priorityq_item *item; struct timeout *timeout; int msecs; item = priorityq_peek(ioloop->timeouts); timeout = (struct timeout *)item; if (timeout == NULL) { /* no timeouts. use INT_MAX msecs for timeval and return -1 for poll/epoll infinity. */ tv_r->tv_sec = INT_MAX / 1000; tv_r->tv_usec = 0; /* APPLE */ ioloop->next_max_time = (1ULL << (time_t_max_bits()-1)) - 1; return -1; } tv_now.tv_sec = 0; msecs = timeout_get_wait_time(timeout, tv_r, &tv_now); ioloop->next_max_time = (tv_now.tv_sec + msecs/1000) + 1; return msecs; }
void test_priorityq(void) { #define PQ_MAX_ITEMS 100 static const int input[] = { 1, 2, 3, 4, 5, 6, 7, 8, -1, 8, 7, 6, 5, 4, 3, 2, 1, -1, 8, 7, 5, 6, 1, 3, 4, 2, -1, -1 }; static const int output[] = { 1, 2, 3, 4, 5, 6, 7, 8 }; struct pq_test_item *item, items[PQ_MAX_ITEMS]; struct priorityq_item *const *all_items; unsigned int i, j; struct priorityq *pq; pool_t pool; int prev; pool = pool_alloconly_create("priorityq items", 1024); /* simple tests with popping only */ test_begin("priorityq"); for (i = 0; input[i] != -1; i++) { p_clear(pool); pq = priorityq_init(cmp_int, 1); for (j = 0; input[i] != -1; i++, j++) { test_assert(priorityq_count(pq) == j); item = p_new(pool, struct pq_test_item, 1); item->num = input[i]; priorityq_add(pq, &item->item); } all_items = priorityq_items(pq); test_assert(priorityq_count(pq) == N_ELEMENTS(output)); item = (struct pq_test_item *)all_items[0]; test_assert(item->num == output[0]); for (j = 1; j < N_ELEMENTS(output); j++) { item = (struct pq_test_item *)all_items[j]; test_assert(item->num > output[0]); test_assert(item->num <= output[N_ELEMENTS(output)-1]); } for (j = 0; j < N_ELEMENTS(output); j++) { test_assert(priorityq_count(pq) == N_ELEMENTS(output) - j); item = (struct pq_test_item *)priorityq_peek(pq); test_assert(output[j] == item->num); item = (struct pq_test_item *)priorityq_pop(pq); test_assert(output[j] == item->num); } test_assert(priorityq_count(pq) == 0); test_assert(priorityq_peek(pq) == NULL); test_assert(priorityq_pop(pq) == NULL); priorityq_deinit(&pq); } test_end(); /* randomized tests, remove elements */ test_begin("priorityq randomized"); for (i = 0; i < 100; i++) { pq = priorityq_init(cmp_int, 1); for (j = 0; j < PQ_MAX_ITEMS; j++) { items[j].num = rand(); priorityq_add(pq, &items[j].item); } for (j = 0; j < PQ_MAX_ITEMS; j++) { if (rand() % 3 == 0) { priorityq_remove(pq, &items[j].item); items[j].num = -1; } } prev = 0; while (priorityq_count(pq) > 0) { item = (struct pq_test_item *)priorityq_pop(pq); test_assert(item->num >= 0 && prev <= item->num); prev = item->num; item->num = -1; } for (j = 0; j < PQ_MAX_ITEMS; j++) { test_assert(items[j].num == -1); } priorityq_deinit(&pq); } test_end(); pool_unref(&pool); }