diff --git a/include/proto/task.h b/include/proto/task.h index 714adc969..6d18e38c6 100644 --- a/include/proto/task.h +++ b/include/proto/task.h @@ -599,9 +599,15 @@ void process_runnable_tasks(); /* * Extract all expired timers from the timer queue, and wakes up all - * associated tasks. Returns the date of next event (or eternity). + * associated tasks. */ -int wake_expired_tasks(); +void wake_expired_tasks(); + +/* Checks the next timer for the current thread by looking into its own timer + * list and the global one. It may return TICK_ETERNITY if no timer is present. + * Note that the next timer might very well be slighly in the past. + */ +int next_timer_expiry(); /* * Delete every tasks before running the master polling loop diff --git a/src/haproxy.c b/src/haproxy.c index a628c4043..4e4a7deb7 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -2616,6 +2616,8 @@ static void run_poll_loop() tv_update_date(0,1); while (1) { + wake_expired_tasks(); + /* Process a few tasks */ process_runnable_tasks(); @@ -2624,9 +2626,6 @@ static void run_poll_loop() if (tid == 0) signal_process_queue(); - /* Check if we can expire some tasks */ - next = wake_expired_tasks(); - /* stop when there's nothing left to do */ if ((jobs - unstoppable_jobs) == 0) break; @@ -2651,6 +2650,9 @@ static void run_poll_loop() wake = 0; } + /* If we have to sleep, measure how long */ + next = wake ? TICK_ETERNITY : next_timer_expiry(); + /* The poller will ensure it returns around */ cur_poller.poll(&cur_poller, next, wake); diff --git a/src/task.c b/src/task.c index abf1583b3..304e05694 100644 --- a/src/task.c +++ b/src/task.c @@ -155,14 +155,13 @@ void __task_queue(struct task *task, struct eb_root *wq) /* * Extract all expired timers from the timer queue, and wakes up all - * associated tasks. Returns the date of next event (or eternity). + * associated tasks. */ -int wake_expired_tasks() +void wake_expired_tasks() { struct task_per_thread * const tt = sched; // thread's tasks struct task *task; struct eb32_node *eb; - int ret = TICK_ETERNITY; __decl_hathreads(int key); while (1) { @@ -178,11 +177,8 @@ int wake_expired_tasks() break; } - if (tick_is_lt(now_ms, eb->key)) { - /* timer not expired yet, revisit it later */ - ret = eb->key; + if (tick_is_lt(now_ms, eb->key)) break; - } /* timer looks expired, detach it from the queue */ task = eb32_entry(eb, struct task, wq); @@ -225,11 +221,8 @@ int wake_expired_tasks() key = eb->key; HA_RWLOCK_RDUNLOCK(TASK_WQ_LOCK, &wq_lock); - if (tick_is_lt(now_ms, key)) { - /* timer not expired yet, revisit it later */ - ret = tick_first(ret, key); + if (tick_is_lt(now_ms, key)) goto leave; - } /* There's really something of interest here, let's visit the queue */ @@ -247,11 +240,8 @@ int wake_expired_tasks() break; } - if (tick_is_lt(now_ms, eb->key)) { - /* timer not expired yet, revisit it later */ - ret = tick_first(ret, eb->key); + if (tick_is_lt(now_ms, eb->key)) break; - } /* timer looks expired, detach it from the queue */ task = eb32_entry(eb, struct task, wq); @@ -282,6 +272,46 @@ int wake_expired_tasks() HA_RWLOCK_WRUNLOCK(TASK_WQ_LOCK, &wq_lock); #endif leave: + return; +} + +/* Checks the next timer for the current thread by looking into its own timer + * list and the global one. It may return TICK_ETERNITY if no timer is present. + * Note that the next timer might very well be slighly in the past. + */ +int next_timer_expiry() +{ + struct task_per_thread * const tt = sched; // thread's tasks + struct eb32_node *eb; + int ret = TICK_ETERNITY; + __decl_hathreads(int key); + + /* first check in the thread-local timers */ + eb = eb32_lookup_ge(&tt->timers, now_ms - TIMER_LOOK_BACK); + if (!eb) { + /* we might have reached the end of the tree, typically because + * is in the first half and we're first scanning the last + * half. Let's loop back to the beginning of the tree now. + */ + eb = eb32_first(&tt->timers); + } + + if (eb) + ret = eb->key; + +#ifdef USE_THREAD + if (!eb_is_empty(&timers)) { + HA_RWLOCK_RDLOCK(TASK_WQ_LOCK, &wq_lock); + eb = eb32_lookup_ge(&timers, now_ms - TIMER_LOOK_BACK); + if (!eb) + eb = eb32_first(&timers); + if (eb) + key = eb->key; + HA_RWLOCK_RDUNLOCK(TASK_WQ_LOCK, &wq_lock); + if (eb) + ret = tick_first(ret, key); + } +#endif return ret; }