BUG/MEDIUM: debug/thread: make the debug handler not wait for !rdv_requests
authorWilly Tarreau <w@1wt.eu>
Thu, 19 Jan 2023 17:52:31 +0000 (18:52 +0100)
committerWilly Tarreau <w@1wt.eu>
Thu, 19 Jan 2023 18:22:17 +0000 (19:22 +0100)
commit9debe0fb2754c0c29203d50069389d2c55ca3fae
tree32353bc22f9940a2c00e4c2acde5200c802ce79d
parent7e70bfc8cbd7abd888b317fa7ef5661ca044a820
BUG/MEDIUM: debug/thread: make the debug handler not wait for !rdv_requests

The debug handler may deadlock with some threads waiting for isolation.
This may happend during a "show threads" command or even during a panic.
The reason is the call to thread_harmless_end() which waits for rdv_requests
to turn to zero before releasing its position in thread_dump_state,
while that one may not progress if another thread was interrupted in
thread_isolate() and is waiting for that thread to drop thread_dump_state.

In order to address this, we now use thread_harmless_end_sig() introduced
by previous commit:

   MINOR: threads: add a thread_harmless_end() version that doesn't wait

However there's a catch: since commit f7afdd910 ("MINOR: debug: mark
oneself harmless while waiting for threads to finish"), there's a second
pair of thread_harmless_now()/thread_harmless_end() that surround the
loop around thread_dump_state. Marking a thread harmless before this
loop and dropping that without checking rdv_requests there could break
the harmless promise made to the other thread if it returns first and
proceeds with its isolated work. Hence we just drop this pair which was
only preventive for other signal handlers, while as indicated in that
patch's commit message, other signals are handled asynchronously and do
not require that extra protection.

This fix must be backported to 2.7.

The problem can be seen by running "show threads" in fast loops (100/s)
while reloading haproxy very quickly (10/s) and sending lots of traffic
to it (100krps, 15 Gbps). In this case the soft stop calls pool_gc()
which isolates a lot and manages to race with the dumps after a few
tens of seconds, leaving the process with all threads at 100%.
src/debug.c