On Sun, Nov 08, 2026 at 06:47:52PM +0100, Pepper Gray wrote: > I understand the reasoning against returning an error on UB, but I > think hanging indefinitely is worse because it's much harder to > diagnose. There's already a precedent in musl's pthread_join() > implementation where it explicitly calls a_crash() when it detects > certain invalid thread states. For example, Box64's tests/test11.c > joins the same thread twice. glibc returns an error on the second > join, which the test ignores, while musl crashes because the thread's > resources have already been unmapped. > > So why not deliberately crash on self-join as well, rather than hang > indefinitely? > That would allow for both: avoid allowing execution on UB and making > it easier to diagnose.
Yes, it's something I'd considered, but I'm not sure whether it's better. It's a matter of whether it's easier to attach a debugger and see what's happening in a stuck process or re-run under debugger and wait for crash (or enable core files). The cases where it crashes now are UAF bugs (memory unsafety) but self-join is a case where nothing inherently unsafe has happened; semantically a self-join could be well-defined as an infinite hang (or hang until cancel) for the thread. So there's not really a safety mandate to prefer crash. If crash is deemed the better behavior though I see no reason it couldn't be changed. > On Sat, 7 Nov 2026 at 14:41, Rich Felker <[email protected]> wrote: > > > > On Sat, Nov 07, 2026 at 01:09:03PM +0100, Pepper Gray wrote: > > > According to POSIX, self-joining a thread is undefined behavior: > > > > > > > The behavior is undefined if the value specified by the thread argument > > > > to > > > > pthread_join() refers to the calling thread. > > > > > > ```C > > > #include <pthread.h> > > > #include <stdio.h> > > > #include <string.h> > > > > > > int main(void) { > > > int err = pthread_join(pthread_self(), NULL); // glibc: EDEADLK, > > > musl: never returns > > > printf("returned: %s\n", strerror(err)); > > > } > > > ``` > > > > > > but the rationale recommends detecting it: > > > > > > > If an implementation detects that the value specified by the thread > > > > argument > > > > to pthread_join() refers to the calling thread, it is recommended that > > > > the > > > > function should fail and report an [EDEADLK] error. > > > > > > This is the case in `glibc`, which reports `EDEADLK` while musl just > > > hangs. > > > > > > While hanging is technically correct, the behavior is undesirable in > > > practice, > > > and a behavior like returning `EDEADLK` or even crashing would make it > > > easier to > > > deal with such an app than when it just hangs. > > > > It's very intentional that we do not facilitate forward progress when > > a program has invoked undefined behavior. There is no good reason to > > expect that a programmer has ignored the fact that the operation is UB > > will have taken the effort to check the return value, so just > > returning an error is likely to result in the flow of execution > > continuing to run in an inconsistent and possibly dangerous state. > > > > Self-join suggests a confusion about who owns resources, so this may > > involve data races, etc. But this is a pretty broad principle in musl, > > not specific to pthread_join. At one point my write-up of the > > principle was included in some glibc guidelines too; I can't find the > > link to that at the moment, but FWIW there was consensus that "return > > an error on UB" is bad behavior. > > > > > In C++ this case is defined for `std::thread::join()` > > > ([thread.thread.member](https://eel.is/c++draft/thread.thread.member)): > > > > > > > *Throws:* `system_error` when an exception is required > > > > ([thread.req.exception]). > > > > > > > > *Error conditions:* > > > > - `resource_deadlock_would_occur` — if deadlock is detected or > > > > `get_id() == this_thread::get_id()`. > > > > > > However this doesn't work on `libstdc++` nor `libc++` because they both > > > wrap > > > `pthread_join`, which just hangs on musl. > > > > If the C++ std::thread::join has a contract to throw on self-join then > > it needs to call pthread_equal before pthread_join to check that > > condition.
