[Lldb-commits] [lldb] a3292e8 - [lldb] Fix TestThreadExit.py flakiness (#190976)
via lldb-commits
lldb-commits at lists.llvm.org
Fri Apr 17 14:00:19 PDT 2026
Author: Jonas Devlieghere
Date: 2026-04-17T14:00:14-07:00
New Revision: a3292e8924528420989c23a8e79174f9b9cb3840
URL: https://github.com/llvm/llvm-project/commit/a3292e8924528420989c23a8e79174f9b9cb3840
DIFF: https://github.com/llvm/llvm-project/commit/a3292e8924528420989c23a8e79174f9b9cb3840.diff
LOG: [lldb] Fix TestThreadExit.py flakiness (#190976)
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.
Added:
lldb/packages/Python/lldbsuite/test/make/mach_thread.h
Modified:
lldb/test/API/functionalities/thread/thread_exit/main.cpp
Removed:
################################################################################
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..3106b0017a581
--- /dev/null
+++ b/lldb/packages/Python/lldbsuite/test/make/mach_thread.h
@@ -0,0 +1,42 @@
+#ifdef __APPLE__
+#include <mach/mach.h>
+#include <pthread.h>
+#include <thread>
+
+// Get the Mach thread port for a std::thread. Must be called before join(),
+// because the native_handle is invalidated after join.
+mach_port_t get_mach_thread(std::thread &t) {
+ return pthread_mach_thread_np(t.native_handle());
+}
+
+// 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 specific Mach thread is gone from the task's thread list.
+void wait_for_thread_cleanup(mach_port_t mach_thread) {
+ 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)
+ break;
+ bool found = false;
+ for (mach_msg_type_number_t i = 0; i < count; i++) {
+ if (thread_list[i] == mach_thread)
+ found = true;
+ mach_port_deallocate(mach_task_self(), thread_list[i]);
+ }
+ vm_deallocate(mach_task_self(), (vm_address_t)thread_list,
+ count * sizeof(thread_t));
+ if (!found)
+ break;
+ std::this_thread::yield();
+ }
+}
+#else
+#include <thread>
+typedef unsigned int mach_port_t;
+inline mach_port_t get_mach_thread(std::thread &t) { return 0; }
+inline void wait_for_thread_cleanup(mach_port_t) {}
+#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..6a351d4ebaa7d 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,58 @@ 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
+ mach_port_t mach_thread_1 = get_mach_thread(thread_1);
+ thread_1.join();
+ wait_for_thread_cleanup(mach_thread_1);
- // 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
+ mach_port_t mach_thread_2 = get_mach_thread(thread_2);
+ thread_2.join();
+ wait_for_thread_cleanup(mach_thread_2);
- return 0; // Set fourth breakpoint here
+ return 0; // Set fourth breakpoint here
}
More information about the lldb-commits
mailing list