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.