diff --git a/src/network/timeout.c b/src/network/timeout.c index f8cdc02..c7ba463 100644 --- a/src/network/timeout.c +++ b/src/network/timeout.c @@ -42,6 +42,7 @@ void timeout_queue_initialize(struct timeout_queue* t, time_t now, size_t max) { t->last = now; t->max = max; + memset(&t->lock, 0, sizeof(t->lock)); t->events = hub_malloc_zero(max * sizeof(struct timeout_evt*)); } @@ -52,12 +53,56 @@ void timeout_queue_shutdown(struct timeout_queue* t) t->max = 0; } +static int timeout_queue_locked(struct timeout_queue* t) +{ + return t->lock.ptr != NULL; +} + +static int timeout_queue_lock(struct timeout_queue* t) +{ + t->lock.ptr = t; +} + +// unlock and flush the locked events to the main timeout queue. +static int timeout_queue_unlock(struct timeout_queue* t) +{ + struct timeout_evt* evt, *tmp, *first; + size_t pos; + t->lock.ptr = NULL; + + evt = t->lock.next; + while (evt) + { + tmp = evt->next; + pos = evt->timestamp % t->max; + first = t->events[pos]; + if (first) + { + first->prev->next = evt; + evt->prev = first->prev; + first->prev = evt; + } + else + { + t->events[pos] = evt; + evt->prev = evt; + } + evt->next = 0; + evt = tmp; + } + + t->lock.next = 0; + t->lock.prev = 0; +} + + size_t timeout_queue_process(struct timeout_queue* t, time_t now) { size_t pos = (size_t) t->last; size_t events = 0; struct timeout_evt* evt = 0; t->last = now; + timeout_queue_lock(t); for (; pos <= now; pos++) { while ((evt = t->events[pos % t->max])) @@ -67,6 +112,7 @@ size_t timeout_queue_process(struct timeout_queue* t, time_t now) events++; } } + timeout_queue_unlock(t); return events; } @@ -82,6 +128,61 @@ size_t timeout_queue_get_next_timeout(struct timeout_queue* t, time_t now) return seconds; } +static void timeout_queue_insert_locked(struct timeout_queue* t, struct timeout_evt* evt) +{ + /* All events point back to the sentinel. + * this means the event is considered schedule (see timeout_evt_is_scheduled), + * and it is easy to tell if the event is in the wait queue or not. + */ + evt->prev = &t->lock; + evt->next = NULL; + + // The sentinel next points to the first event in the locked queue + // The sentinel prev points to the last evetnt in the locked queue. + // NOTE: if prev is != NULL then next also must be != NULL. + if (t->lock.prev) + { + t->lock.prev->next = evt; + t->lock.prev = evt; + } + else + { + t->lock.next = evt; + t->lock.prev = evt; + } + return; +} + +static void timeout_queue_remove_locked(struct timeout_queue* t, struct timeout_evt* evt) +{ + uhub_assert(evt->prev == &t->lock); + if (t->lock.next == evt) + { + t->lock.next = evt->next; + if (t->lock.prev == evt) + t->lock.prev = evt->next; + } + else + { + struct timeout_evt *prev, *it; + prev = 0; + it = t->lock.next; + while (it) + { + prev = it; + it = it->next; + if (it == evt) + { + prev->next = it->next; + if (!prev->next) + t->lock.prev = prev; + } + } + } + timeout_evt_reset(evt); +} + + void timeout_queue_insert(struct timeout_queue* t, struct timeout_evt* evt, size_t seconds) { @@ -90,6 +191,12 @@ void timeout_queue_insert(struct timeout_queue* t, struct timeout_evt* evt, size evt->timestamp = t->last + seconds; evt->next = 0; + if (timeout_queue_locked(t)) + { + timeout_queue_insert_locked(t, evt); + return; + } + first = t->events[pos]; if (first) @@ -112,6 +219,13 @@ void timeout_queue_remove(struct timeout_queue* t, struct timeout_evt* evt) size_t pos = (evt->timestamp % t->max); struct timeout_evt* first = t->events[pos]; + // Removing a locked event + if (evt->prev == &t->lock) + { + timeout_queue_remove_locked(t, evt); + return; + } + if (!first || !evt->prev) return; diff --git a/src/network/timeout.h b/src/network/timeout.h index d661b06..c48efaf 100644 --- a/src/network/timeout.h +++ b/src/network/timeout.h @@ -43,6 +43,7 @@ struct timeout_queue { time_t last; size_t max; + struct timeout_evt lock; struct timeout_evt** events; };