diff --git a/daemon/timerthread.c b/daemon/timerthread.c index 3e06b847f..62d2aa780 100644 --- a/daemon/timerthread.c +++ b/daemon/timerthread.c @@ -158,7 +158,14 @@ static void __timerthread_queue_free(void *p) { static int ttqe_compare(const void *a, const void *b) { const struct timerthread_queue_entry *t1 = a; const struct timerthread_queue_entry *t2 = b; - return timeval_cmp_ptr(&t1->when, &t2->when); + int ret = timeval_cmp_zero(&t1->when, &t2->when); + if (ret) + return ret; + if (t1->idx < t2->idx) + return -1; + if (t1->idx == t2->idx) + return 0; + return 1; } void *timerthread_queue_new(const char *type, size_t size, @@ -183,6 +190,18 @@ void *timerthread_queue_new(const char *type, size_t size, return ttq; } +int __ttqe_find_last_idx(const void *a, const void *b) { + const struct timerthread_queue_entry *ttqe_a = a; + void **data = (void **) b; + const struct timerthread_queue_entry *ttqe_b = data[0]; + int ret = timeval_cmp(&ttqe_b->when, &ttqe_a->when); + if (ret) + return ret; + // same timestamp. track highest seen idx + if (GPOINTER_TO_UINT(data[1]) < ttqe_a->idx) + data[1] = GUINT_TO_POINTER(ttqe_a->idx); + return 1; // and continue to higher idx +} void timerthread_queue_push(struct timerthread_queue *ttq, struct timerthread_queue_entry *ttqe) { // can we send immediately? if (ttq->run_now_func && timerthread_queue_run_one(ttq, ttqe, ttq->run_now_func) == 0) @@ -203,7 +222,22 @@ void timerthread_queue_push(struct timerthread_queue *ttq, struct timerthread_qu // ntohs(rh->seq_num), // ntohl(rh->timestamp)); + ttqe->idx = 0; + mutex_lock(&ttq->lock); + + // check for most common case: no timestamp collision exists + if (!g_tree_lookup(ttq->entries, ttqe)) + ; + else { + // something else exists with the same timestamp. find the highest idx + void *data[2]; + data[0] = ttqe; + data[1] = 0; + g_tree_search(ttq->entries, __ttqe_find_last_idx, data); + ttqe->idx = GPOINTER_TO_UINT(data[1] + 1); + } + // this hands over ownership of cp, so we must copy the timeval out struct timeval tv_send = ttqe->when; g_tree_insert(ttq->entries, ttqe, ttqe); diff --git a/include/timerthread.h b/include/timerthread.h index ebb72556f..5a2c1505f 100644 --- a/include/timerthread.h +++ b/include/timerthread.h @@ -35,6 +35,7 @@ struct timerthread_queue { struct timerthread_queue_entry { struct timeval when; + unsigned int idx; // for equal timestamps void *source; // opaque char __rest[0]; }; diff --git a/lib/auxlib.c b/lib/auxlib.c index 347be237a..6d026c710 100644 --- a/lib/auxlib.c +++ b/lib/auxlib.c @@ -363,9 +363,8 @@ int uint32_eq(const void *a, const void *b) { return (*A == *B) ? TRUE : FALSE; } -int timeval_cmp_ptr(const void *a, const void *b) { +int timeval_cmp_zero(const void *a, const void *b) { const struct timeval *A = a, *B = b; - int ret; /* zero timevals go last */ if (A->tv_sec == 0 && B->tv_sec != 0) @@ -373,13 +372,18 @@ int timeval_cmp_ptr(const void *a, const void *b) { if (B->tv_sec == 0 && A->tv_sec == 0) return -1; if (A->tv_sec == 0 && B->tv_sec == 0) - goto ptr; + return 0; /* earlier timevals go first */ - ret = timeval_cmp(A, B); + return timeval_cmp(A, B); +} + +int timeval_cmp_ptr(const void *a, const void *b) { + const struct timeval *A = a, *B = b; + int ret; + ret = timeval_cmp_zero(A, B); if (ret) return ret; /* equal timeval, so use pointer as tie breaker */ -ptr: if (A < B) return -1; if (A > B) diff --git a/lib/auxlib.h b/lib/auxlib.h index e2c3d032e..16fd73646 100644 --- a/lib/auxlib.h +++ b/lib/auxlib.h @@ -260,6 +260,7 @@ INLINE int timeval_cmp(const struct timeval *a, const struct timeval *b) { return long_cmp(a->tv_usec, b->tv_usec); } // as a GCompareFunc +int timeval_cmp_zero(const void *a, const void *b); int timeval_cmp_ptr(const void *a, const void *b); INLINE void timeval_lowest(struct timeval *l, const struct timeval *n) {