You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

71 lines
2.8 KiB

  1. commit da767eaaf6128eccd349a54ec6eac2a68dcacacb
  2. Author: Willy Tarreau <w@1wt.eu>
  3. Date: Wed Jul 31 19:15:45 2019 +0200
  4. BUG/MINOR: debug: fix a small race in the thread dumping code
  5. If a thread dump is requested from a signal handler, it may interrupt
  6. a thread already waiting for a dump to complete, and may see the
  7. threads_to_dump variable go to zero while others are waiting, steal
  8. the lock and prevent other threads from ever completing. This tends
  9. to happen when dumping many threads upon a watchdog timeout, to threads
  10. waiting for their turn.
  11. Instead now we proceed in two steps :
  12. 1) the last dumped thread sets all bits again
  13. 2) all threads only wait for their own bit to appear, then clear it
  14. and quit
  15. This way there's no risk that a bit performs a double flip in the same
  16. loop and threads cannot get stuck here anymore.
  17. This should be backported to 2.0 as it clarifies stack traces.
  18. (cherry picked from commit c07736209db764fb2aef6f18ed3687a504c35771)
  19. Signed-off-by: Willy Tarreau <w@1wt.eu>
  20. diff --git a/src/debug.c b/src/debug.c
  21. index 059bc6b9..07624ca5 100644
  22. --- a/src/debug.c
  23. +++ b/src/debug.c
  24. @@ -440,8 +440,8 @@ void debug_handler(int sig, siginfo_t *si, void *arg)
  25. * 1- wait for our turn, i.e. when all lower bits are gone.
  26. * 2- perform the action if our bit is set
  27. * 3- remove our bit to let the next one go, unless we're
  28. - * the last one and have to put them all but ours
  29. - * 4- wait for zero and clear our bit if it's set
  30. + * the last one and have to put them all as a signal
  31. + * 4- wait out bit to re-appear, then clear it and quit.
  32. */
  33. /* wait for all previous threads to finish first */
  34. @@ -454,7 +454,7 @@ void debug_handler(int sig, siginfo_t *si, void *arg)
  35. ha_thread_dump(thread_dump_buffer, tid, thread_dump_tid);
  36. if ((threads_to_dump & all_threads_mask) == tid_bit) {
  37. /* last one */
  38. - HA_ATOMIC_STORE(&threads_to_dump, all_threads_mask & ~tid_bit);
  39. + HA_ATOMIC_STORE(&threads_to_dump, all_threads_mask);
  40. thread_dump_buffer = NULL;
  41. }
  42. else
  43. @@ -462,14 +462,13 @@ void debug_handler(int sig, siginfo_t *si, void *arg)
  44. }
  45. /* now wait for all others to finish dumping. The last one will set all
  46. - * bits again to broadcast the leaving condition.
  47. + * bits again to broadcast the leaving condition so we'll see ourselves
  48. + * present again. This way the threads_to_dump variable never passes to
  49. + * zero until all visitors have stopped waiting.
  50. */
  51. - while (threads_to_dump & all_threads_mask) {
  52. - if (threads_to_dump & tid_bit)
  53. - HA_ATOMIC_AND(&threads_to_dump, ~tid_bit);
  54. - else
  55. - ha_thread_relax();
  56. - }
  57. + while (!(threads_to_dump & tid_bit))
  58. + ha_thread_relax();
  59. + HA_ATOMIC_AND(&threads_to_dump, ~tid_bit);
  60. /* mark the current thread as stuck to detect it upon next invocation
  61. * if it didn't move.