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.

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.

Reply via email to