I was chatting a bit about this on IRC with @agrover, and I think there are two relevant things that I haven’t seen in previous discussions of Rust and thread cancellation:
The unwind-safety story is a little better defined (or at least better explored) after the huge discussion about
recover, and there’s a little more understanding of what
Drop implementations have to do. So being able to tell a remote thread to start panicking is probably sufficiently well-defined now to be permissible.
This particular use case is an OS API that cannot be interrupted other than by sending it a signal, and cannot be made async except by running it in a thread. If you signal the remote thread (with anything, as long as there’s a signal handler), it will interrupt the ioctl and return
EINTR. It seems useful for Rust to at least make that easy, even if it doesn’t provide full cancellation support. If it were the case that all OS interfaces supported a non-blocking / async mode, Rust could just tell people to use that.
I haven’t yet found the discussions about why there is no thread cancellation support, other than a 2011 post from Graydon which talks at least somewhat favorably about cancellation in general, though not
pthread_cancel as an implementation thereof.
There was some discussion on irc about how the stdlib loops if it receives EINTR but from looking at the pthread_cancel manpage it doesn’t appear to me that the thread would ever return from the function that was the cancellation point
Rust isn’t bound to pthread semantics, but as far as I can tell: the implementation of
pthread_cancel (in glibc / NPTL) seems to send an unconditional signal to the remote thread, which can cause any syscall to return early with
EINTR. glibc’s syscall wrappers typically use the
SYSCALL_CANCEL macro, which appears to temporarily set a flag that tells the signal handler to actually do the unwinding. But, as far as I can tell, if you make a raw syscall and bypass glibc’s wrapper, it will just return early with
EINTR, and only start unwinding at the next cancellation point (in libc code) when it notices the thread was supposed to be cancelled.
If we were to implement cancellation, we would choose how this works in a way that makes sense for Rust, which might be to match pthreads but might not be. (And on Windows, it might involve … sending it an exception or something? instead of
TerminateThread, and handling that exception by doing a regular Rust panic.)
In particular, for your use case, returning early from the syscall via
EINTR but not unwinding seems to solve the problem, and also avoids all the tricky questions about unwind safety etc. That’s why I think that standardizing a way to signal a remote thread would be useful. (However, most libstd functions loop on
EINTR, so maybe there would be some need to control that.)