[Lldb-commits] [lldb] [lldb] Fix TestThreadExit.py flakiness (PR #190976)

via lldb-commits lldb-commits at lists.llvm.org
Wed Apr 8 06:53:51 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-lldb

Author: Jonas Devlieghere (JDevlieghere)

<details>
<summary>Changes</summary>

When `pthread_join` returns, the target thread signals its internal semaphore, but the underlying Mach thread hasn't been removed from the task yet with `thread_terminate`.

The flakiness is the result of the debugger stopping the process halts the dying thread mid-termination. There is no Mach API to distinguish a dying thread from a live one: it appears as `TH_STATE_STOPPED`, like any other suspended thread.

This PR adds a helper (`wait_for_thread_cleanup`) that polls `task_threads` to ensure the Mach thread is actually gone before the breakpoint. Since there might be other tests that are affected by this race, I put it in a common location so it can be reused.

---
Full diff: https://github.com/llvm/llvm-project/pull/190976.diff


2 Files Affected:

- (added) lldb/packages/Python/lldbsuite/test/make/mach_thread.h (+26) 
- (modified) lldb/test/API/functionalities/thread/thread_exit/main.cpp (+37-39) 


``````````diff
diff --git a/lldb/packages/Python/lldbsuite/test/make/mach_thread.h b/lldb/packages/Python/lldbsuite/test/make/mach_thread.h
new file mode 100644
index 0000000000000..3c00811a6e79c
--- /dev/null
+++ b/lldb/packages/Python/lldbsuite/test/make/mach_thread.h
@@ -0,0 +1,26 @@
+#ifdef __APPLE__
+#include <mach/mach.h>
+#include <thread>
+#endif
+
+// After join returns, the underlying Mach thread may not have been terminated
+// yet (pthread_join uses a semaphore that is signaled before the thread calls
+// thread_terminate). If the debugger stops the process in this window it will
+// freeze the dying thread, making it appear still alive in task_threads(). Poll
+// until the kernel-level thread count matches.
+void wait_for_thread_cleanup(unsigned int expected) {
+#ifdef __APPLE__
+  while (true) {
+    thread_array_t thread_list;
+    mach_msg_type_number_t count;
+    kern_return_t kr = task_threads(mach_task_self(), &thread_list, &count);
+    if (kr == KERN_SUCCESS) {
+      vm_deallocate(mach_task_self(), (vm_address_t)thread_list,
+                    count * sizeof(thread_t));
+      if (count <= expected)
+        break;
+    }
+    std::this_thread::yield();
+  }
+#endif
+}
diff --git a/lldb/test/API/functionalities/thread/thread_exit/main.cpp b/lldb/test/API/functionalities/thread/thread_exit/main.cpp
index 6c60a40bb66b0..162fec6e296cc 100644
--- a/lldb/test/API/functionalities/thread/thread_exit/main.cpp
+++ b/lldb/test/API/functionalities/thread/thread_exit/main.cpp
@@ -1,5 +1,6 @@
 // This test verifies the correct handling of child thread exits.
 
+#include "mach_thread.h"
 #include "pseudo_barrier.h"
 #include <thread>
 
@@ -7,59 +8,56 @@ pseudo_barrier_t g_barrier1;
 pseudo_barrier_t g_barrier2;
 pseudo_barrier_t g_barrier3;
 
-void *
-thread1 ()
-{
-    // Synchronize with the main thread.
-    pseudo_barrier_wait(g_barrier1);
+void *thread1() {
+  // Synchronize with the main thread.
+  pseudo_barrier_wait(g_barrier1);
 
-    // Synchronize with the main thread and thread2.
-    pseudo_barrier_wait(g_barrier2);
+  // Synchronize with the main thread and thread2.
+  pseudo_barrier_wait(g_barrier2);
 
-    // Return
-    return NULL;                                      // Set second breakpoint here
+  // Return
+  return NULL; // Set second breakpoint here
 }
 
-void *
-thread2 ()
-{
-    // Synchronize with thread1 and the main thread.
-    pseudo_barrier_wait(g_barrier2);
+void *thread2() {
+  // Synchronize with thread1 and the main thread.
+  pseudo_barrier_wait(g_barrier2);
 
-    // Synchronize with the main thread.
-    pseudo_barrier_wait(g_barrier3);
+  // Synchronize with the main thread.
+  pseudo_barrier_wait(g_barrier3);
 
-    // Return
-    return NULL;
+  // Return
+  return NULL;
 }
 
-int main ()
-{
-    pseudo_barrier_init(g_barrier1, 2);
-    pseudo_barrier_init(g_barrier2, 3);
-    pseudo_barrier_init(g_barrier3, 2);
+int main() {
+  pseudo_barrier_init(g_barrier1, 2);
+  pseudo_barrier_init(g_barrier2, 3);
+  pseudo_barrier_init(g_barrier3, 2);
 
-    // Create a thread.
-    std::thread thread_1(thread1);
+  // Create a thread.
+  std::thread thread_1(thread1);
 
-    // Wait for thread1 to start.
-    pseudo_barrier_wait(g_barrier1);
+  // Wait for thread1 to start.
+  pseudo_barrier_wait(g_barrier1);
 
-    // Create another thread.
-    std::thread thread_2(thread2);  // Set first breakpoint here
+  // Create another thread.
+  std::thread thread_2(thread2); // Set first breakpoint here
 
-    // Wait for thread2 to start.
-    pseudo_barrier_wait(g_barrier2);
+  // Wait for thread2 to start.
+  pseudo_barrier_wait(g_barrier2);
 
-    // Wait for the first thread to finish
-    thread_1.join();
+  // Wait for the first thread to finish
+  thread_1.join();
+  wait_for_thread_cleanup(2);
 
-    // Synchronize with the remaining thread
-    int dummy = 47;                   // Set third breakpoint here
-    pseudo_barrier_wait(g_barrier3);
+  // Synchronize with the remaining thread
+  int dummy = 47; // Set third breakpoint here
+  pseudo_barrier_wait(g_barrier3);
 
-    // Wait for the second thread to finish
-    thread_2.join();
+  // Wait for the second thread to finish
+  thread_2.join();
+  wait_for_thread_cleanup(1);
 
-    return 0;                                         // Set fourth breakpoint here
+  return 0; // Set fourth breakpoint here
 }

``````````

</details>


https://github.com/llvm/llvm-project/pull/190976


More information about the lldb-commits mailing list