> Date: Sat, 13 Dec 2025 00:56:39 +0000 (UTC) > From: RVP <[email protected]> > > Hmm. In FreeBSD, OpenBSD & Linux, pthread_sigmask(3) seems to apply > per-thread, but on NetBSD, the whole process gets blocked--not just > the thread which called pthread_sigmask():
This is not accurate: NetBSD pthread_sigmask correctly blocks signals only in the thread, not in the whole process. (NetBSD doesn't even store a process-wide signal mask at all, only per-thread signal masks.) But your test program isn't testing that. When one thread has a signal unblocked, and another thread has that signal blocked but is waiting for it in sigwait(), it is arbitrary which of those threads the signal is delivered to, just like if two threads both have the signal unblocked. Quoth POSIX.1-2024: > Signals generated for the process shall be delivered to exactly one > of those threads within the process which is in a call to a > sigwait() function selecting that signal or has not blocked delivery > of the signal. https://pubs.opengroup.org/onlinepubs/9799919799/functions/V2_chap02.html#tag_16_04_01 It's not clear from the short ktrace fragment what's going on, and https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py looks way too sketchy for me to bother running it myself, but maybe someone who is willing to run it can produce a larger ktrace fragment that covers both the sigaction/pthread_sigmask/sigwait _and_ the signal delivery to see what's going on. Note: When you examine `ps -o sigmask', it's different on NetBSD and others just because we don't use the same per-thread state to record the signals that sigwait() is waiting for. POSIX requires a signal to be blocked in a thread in order for that thread to wait for it with sigwait(), so it's not surprising that you see the signal blocked in ps(1) output: > The signals defined by set shall have been blocked at the time of > the call to sigwait(); otherwise, the behavior is undefined. https://pubs.opengroup.org/onlinepubs/9799919799/functions/sigwait.html Internally, NetBSD stores a linked list of threads in sigwait() per process (p_sigwaiters), and a set of signals to wait for per thread (l_sigwaited), separately from the per-thread signal mask (l_sigmask) where the signal remains internally recorded as blocked during sigwait(). The signal delivery logic (arbitrarily) checks for threads waiting in sigwait() before trying other threads: - sigwait() sets up the current thread on p_sigwaiters: https://nxr.netbsd.org/xref/src/sys/kern/sys_sig.c?r=1.61#811 - Signal delivery checks p_sigwaiters first before trying non-sigwait() delivery: https://nxr.netbsd.org/xref/src/sys/kern/kern_sig.c?r=1.411#1378 https://nxr.netbsd.org/xref/src/sys/kern/kern_sig.c?r=1.411#1218 In contrast, in FreeBSD, sigwait() unblocks the signal internally, and signal delivery (arbitrarily) checks for the _first_ thread with the signal unblocked before determining whether it's waiting in sigwait() or not, so it's not surprising that on FreeBSD, ps(1) reports the signals unblocked while in sigwait(): - sigwait() internally unblocks the signal and waits for a wakeup: https://cgit.freebsd.org/src/tree/sys/kern/kern_sig.c?id=4f184fd35d81bbd85284d47d2a65aeece67e87d4#n1380 - Signal delivery checks for the first thread with signal unblocked, queues it, and wakes up that thread -- it's up to the thread to react by running the signal handler or by waking up in sigwait(): https://cgit.freebsd.org/src/tree/sys/kern/kern_sig.c?id=4f184fd35d81bbd85284d47d2a65aeece67e87d4#n2310 https://cgit.freebsd.org/src/tree/sys/kern/kern_sig.c?id=4f184fd35d81bbd85284d47d2a65aeece67e87d4#n2167
