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.

Reply via email to