12/12/2025 at 5:50:34 PM
The first linked article was recently discussed here: RIP pthread_cancel (https://news.ycombinator.com/item?id=45233713)In that discussion, most of the same points as in this article were already discussed, specifically some async DNS alternatives.
See also here the discussion: https://github.com/crystal-lang/crystal/issues/13619
by albertzeyer
12/12/2025 at 6:39:29 PM
I am always amused when folks rediscover the bad idea that is `pthread_cancel()` — it’s amazing that it was ever part of the standard.We knew it was a bad idea at the time it was standardized in the 1990s, but politics — and the inevitable allure of a very convenient sounding (but very bad) idea — meant that the bad idea won.
Funny enough, while Java has deprecated their version of thread cancellation for the same reasons, Haskell still has theirs. When you’re writing code in IO, you have to be prepared for async cancellation anywhere, at any time.
This leads to common bugs in the standard library that you really wouldn’t expect from a language like Haskell; e.g. https://github.com/haskell/process/issues/183 (withCreateProcess async exception safety)
by frumplestlatz
12/12/2025 at 7:27:10 PM
What's crazy is that it's almost good. All they had to do was make the next syscall return ECANCELED (already a defined error code!) rather than terminating the thread.Musl has an undocumented extension that does exactly this: PTHREAD_CANCEL_MASKED passed to pthread_setcancelstate.
It's great and it should be standardized.
by AndyKelley
12/12/2025 at 9:07:45 PM
That would have been fantastic. My worry is if we standardized it now, a lot of library code would be unexpectedly dealing with ECANCELED from APIs that previously were guaranteed to never fail outside of programmer error, e.g. `pthread_mutex_lock()`.Looking at some of my shipping code, there's a fair bit that triggers a runtime `assert()` if `pthread_mutex_lock()` fails, as that should never occur outside of a locking bug of my own making.
by frumplestlatz
12/12/2025 at 9:19:09 PM
You can sort of emulate that with pthread_kill and EINTR but you need to control all code that can call interruptable sys calls to correctly return without retry (or longjmp/throw from the signal handler, but then we are back in phtread_cancel territory)by gpderetta
12/12/2025 at 11:04:24 PM
There's a second problem here that musl also solves. If the signal is delivered in between checking for cancelation and the syscall machine code instruction, the interrupt is missed. This can cause a deadlock if the syscall was going to wait indefinitely and the application relies on cancelation for interruption.Musl solves this problem by inspecting the program counter in the interrupt handler and checking if it falls specifically in that range, and if so, modifying registers such that when it returns from the signal, it returns to instructions that cause ECANCELED to be returned.
Blew my mind when I learned this last month.
by AndyKelley
12/13/2025 at 4:04:25 AM
Introspection windows from a interrupting context are a neat technique. You can use it to implement “atomic transaction” guarantees for the interruptee as long as you control all potential interrupters. You can also implement “non-interruption” sections and bailout logic.by Veserv
12/13/2025 at 3:10:02 AM
In particular you need to control the signal handlers. You can't do that easily in a library.by cryptonector
12/13/2025 at 3:09:30 AM
`pthread_cancel()` was meant for interrupting long computations, not I/O.by cryptonector
12/13/2025 at 3:21:31 AM
It always surprised me that in the path of so many glibc functions are calls to open() items in /etc and then parse their output into some kind of value to use or possibly return.The initialization of these objects should have been separate and then used as a parameter to the functions that operate on them. Then you could load the /etc/gai.conf configuration, parse it, then pass that to getaddrinfo(). The fact that multiple cancellation points are discreetly buried in the paths of these functions is an element of unfortunate design.
by themafia
12/12/2025 at 7:47:33 PM
It’s extremely easy to write application code in Haskell that handles async cancellation correctly without even thinking about it. The async library provides high level abstractions. However your point is still valid as I do think if you write library code at a low level of abstraction (the standard library must) it is just as error prone as in Java or C.by kccqzy
12/13/2025 at 3:03:26 AM
`pthread_cancel()` is necessary _only_ to interrupt compute-only code without killing the entire process. That's it. The moment you try to use it to interrupt _I/O_ you lose -- you lose BIG.by cryptonector
12/14/2025 at 7:54:57 PM
there is a better way - in any unbounded compute loop, add some code to check for cancellation. it can be very very very cheapthis is not possible if you are calling third party code that you can't modify. in this case it's probably a better idea to run it on another process and use shared memory to communicate back results. this can even be done in an airtight sandboxed manner (browsers do this for example), something that can't really be done with threads
by nextaccountic
12/12/2025 at 7:43:29 PM
IO can fail at any point though, so that’s not particularly bad.by paulddraper
12/13/2025 at 1:41:25 AM
It's particularly bad because thread interruptions are funneled into the same system as IO errors, so it's easy to consume them by mistake.Java has that same issue.
by marcosdumay