mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-06 15:17:01 +02:00
BUG/MINOR: signals/poller: ensure wakeup from signals
Add self-wake in signal_handler() to fix a race condition with a signal
coming in between checking signal_queue_len and entering polling sleep.
The changes in commit 43c891dda
("BUG/MINOR: signals/poller: set the
poller timeout to 0 when there are signals") were insufficient.
Move the signal_queue_len check from the poll implementations to
run_poll_loop() to keep that logic in one place.
The poll loops are terminated either by the parameter wake being set or
wake up due to a write to their poller_wr_pipe by wake_thread() in
signal_handler().
This fixes issue #1841.
Must be backported in every stable version.
This commit is contained in:
parent
ef2d2340e6
commit
eea152ee68
@ -222,11 +222,10 @@ static void _do_poll(struct poller *p, int exp, int wake)
|
|||||||
thread_idle_now();
|
thread_idle_now();
|
||||||
thread_harmless_now();
|
thread_harmless_now();
|
||||||
|
|
||||||
/* Now let's wait for polled events.
|
/* Now let's wait for polled events. */
|
||||||
* Check if the signal queue is not empty in case we received a signal
|
wait_time = wake ? 0 : compute_poll_timeout(exp);
|
||||||
* before entering the loop, so we don't wait MAX_DELAY_MS for nothing */
|
|
||||||
wait_time = (wake | signal_queue_len) ? 0 : compute_poll_timeout(exp);
|
|
||||||
clock_entering_poll();
|
clock_entering_poll();
|
||||||
|
|
||||||
do {
|
do {
|
||||||
int timeout = (global.tune.options & GTUNE_BUSY_POLLING) ? 0 : wait_time;
|
int timeout = (global.tune.options & GTUNE_BUSY_POLLING) ? 0 : wait_time;
|
||||||
|
|
||||||
@ -239,7 +238,7 @@ static void _do_poll(struct poller *p, int exp, int wake)
|
|||||||
}
|
}
|
||||||
if (timeout || !wait_time)
|
if (timeout || !wait_time)
|
||||||
break;
|
break;
|
||||||
if (signal_queue_len || wake)
|
if (wake)
|
||||||
break;
|
break;
|
||||||
if (tick_isset(exp) && tick_is_expired(exp, now_ms))
|
if (tick_isset(exp) && tick_is_expired(exp, now_ms))
|
||||||
break;
|
break;
|
||||||
|
@ -178,10 +178,8 @@ static void _do_poll(struct poller *p, int exp, int wake)
|
|||||||
thread_idle_now();
|
thread_idle_now();
|
||||||
thread_harmless_now();
|
thread_harmless_now();
|
||||||
|
|
||||||
/* Now let's wait for polled events.
|
/* Now let's wait for polled events. */
|
||||||
* Check if the signal queue is not empty in case we received a signal
|
wait_time = wake ? 0 : compute_poll_timeout(exp);
|
||||||
* before entering the loop, so we don't wait MAX_DELAY_MS for nothing */
|
|
||||||
wait_time = (wake | signal_queue_len) ? 0 : compute_poll_timeout(exp);
|
|
||||||
clock_entering_poll();
|
clock_entering_poll();
|
||||||
|
|
||||||
do {
|
do {
|
||||||
@ -219,7 +217,7 @@ static void _do_poll(struct poller *p, int exp, int wake)
|
|||||||
break;
|
break;
|
||||||
if (timeout || !wait_time)
|
if (timeout || !wait_time)
|
||||||
break;
|
break;
|
||||||
if (signal_queue_len || wake)
|
if (wake)
|
||||||
break;
|
break;
|
||||||
if (tick_isset(exp) && tick_is_expired(exp, now_ms))
|
if (tick_isset(exp) && tick_is_expired(exp, now_ms))
|
||||||
break;
|
break;
|
||||||
|
@ -166,10 +166,8 @@ static void _do_poll(struct poller *p, int exp, int wake)
|
|||||||
}
|
}
|
||||||
fd_nbupdt = 0;
|
fd_nbupdt = 0;
|
||||||
|
|
||||||
/* Now let's wait for polled events.
|
/* Now let's wait for polled events. */
|
||||||
* Check if the signal queue is not empty in case we received a signal
|
wait_time = wake ? 0 : compute_poll_timeout(exp);
|
||||||
* before entering the loop, so we don't wait MAX_DELAY_MS for nothing */
|
|
||||||
wait_time = (wake | signal_queue_len) ? 0 : compute_poll_timeout(exp);
|
|
||||||
fd = global.tune.maxpollevents;
|
fd = global.tune.maxpollevents;
|
||||||
clock_entering_poll();
|
clock_entering_poll();
|
||||||
|
|
||||||
@ -193,7 +191,7 @@ static void _do_poll(struct poller *p, int exp, int wake)
|
|||||||
}
|
}
|
||||||
if (timeout || !wait_time)
|
if (timeout || !wait_time)
|
||||||
break;
|
break;
|
||||||
if (signal_queue_len || wake)
|
if (wake)
|
||||||
break;
|
break;
|
||||||
if (tick_isset(exp) && tick_is_expired(exp, now_ms))
|
if (tick_isset(exp) && tick_is_expired(exp, now_ms))
|
||||||
break;
|
break;
|
||||||
|
@ -204,10 +204,8 @@ static void _do_poll(struct poller *p, int exp, int wake)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now let's wait for polled events.
|
/* Now let's wait for polled events. */
|
||||||
* Check if the signal queue is not empty in case we received a signal
|
wait_time = wake ? 0 : compute_poll_timeout(exp);
|
||||||
* before entering the loop, so we don't wait MAX_DELAY_MS for nothing */
|
|
||||||
wait_time = (wake | signal_queue_len) ? 0 : compute_poll_timeout(exp);
|
|
||||||
clock_entering_poll();
|
clock_entering_poll();
|
||||||
status = poll(poll_events, nbfd, wait_time);
|
status = poll(poll_events, nbfd, wait_time);
|
||||||
clock_update_date(wait_time, status);
|
clock_update_date(wait_time, status);
|
||||||
|
@ -2810,7 +2810,7 @@ void run_poll_loop()
|
|||||||
if (killed > 1)
|
if (killed > 1)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* expire immediately if events are pending */
|
/* expire immediately if events or signals are pending */
|
||||||
wake = 1;
|
wake = 1;
|
||||||
if (thread_has_tasks())
|
if (thread_has_tasks())
|
||||||
activity[tid].wake_tasks++;
|
activity[tid].wake_tasks++;
|
||||||
@ -2821,6 +2821,10 @@ void run_poll_loop()
|
|||||||
if (thread_has_tasks()) {
|
if (thread_has_tasks()) {
|
||||||
activity[tid].wake_tasks++;
|
activity[tid].wake_tasks++;
|
||||||
_HA_ATOMIC_AND(&th_ctx->flags, ~TH_FL_SLEEPING);
|
_HA_ATOMIC_AND(&th_ctx->flags, ~TH_FL_SLEEPING);
|
||||||
|
} else if (signal_queue_len) {
|
||||||
|
/* this check is required after setting TH_FL_SLEEPING to avoid
|
||||||
|
* a race with wakeup on signals using wake_threads() */
|
||||||
|
_HA_ATOMIC_AND(&th_ctx->flags, ~TH_FL_SLEEPING);
|
||||||
} else
|
} else
|
||||||
wake = 0;
|
wake = 0;
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,9 @@ void signal_handler(int sig)
|
|||||||
signal_state[sig].count++;
|
signal_state[sig].count++;
|
||||||
if (sig)
|
if (sig)
|
||||||
signal(sig, signal_handler); /* re-arm signal */
|
signal(sig, signal_handler); /* re-arm signal */
|
||||||
|
|
||||||
|
/* If the thread is TH_FL_SLEEPING we need to wake it */
|
||||||
|
wake_thread(tid);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Call handlers of all pending signals and clear counts and queue length. The
|
/* Call handlers of all pending signals and clear counts and queue length. The
|
||||||
|
Loading…
Reference in New Issue
Block a user