Why Ruby’s Timeout is dangerous and Thread.raise is terrifying
jvns.ca
jvns.ca
The sad thing is there are few viable alternatives. I forgot which one but open or read_timeout in the net::http library just uses timeout as well. So there's really no way to safely crawl a url and have it time out if it takes > x seconds.
Similar wisdom hides in the pthread_join(3) man page:
There is no pthreads analog of waitpid(-1, &status, 0), that is, "join
with any terminated thread". If you believe you need this functional‐
ity, you probably need to rethink your application design.Or are you saying daemon threads are still broken even with the fix, because of the underlying pthreads stuff?
If you really want something to time out, there are two options. Build into your library the ability to supply a timeout, and reliably time out; or run the operation in a separate process and kill it when the deadline is exceeded.
Go has a nice "context" idiom for carrying around cancellation and deadline information, so the chances are if you set a timeout on the context, it's likely to be obeyed because nearly every call that performs IO accepts the context and cleanly aborts when the deadline or cancellation signal arrives (presumably propagating err back to you). Though this is not perfect; disk writes do not appear to accept a context, which shows how careful you have to be when designing something to time out. ("Disk" writes can easily be network RPCs; consider NFS or FUSE.)
This is comparing Apples to ICBMs. Blocking I/O is very common, but OS signals aren't - in Java, all you need is usually a Runtime.addShutdownHook() and you're good to go.
Julia Evans describes an implementation that is inherently racy, there is no way this could work 100% correct all of the time.
Failing to handle EINTR is not good, but at least it fails cleanly. This is still way, way better than throwing an exception in random places, potentially in code that "can't throw," and leaving a trail of corruption behind.
But it's a good point that it creates a greater risk of raising an exception in the cleanup part of the context manager or try/finally, where one might normally try to avoid it by being extra careful:
try:
try:
time.sleep(10) # or some interesting function
except KeyboardInterrupt:
print("Unexpectedly aborted, but that's fine.")
finally:
print("Cleaning up!")
time.sleep(10) # or some important cleanup
except KeyboardInterrupt:
print("Abort was aborted! Now things are all screwed up!")
So one should probably be careful not to press ^C more than once when aborting a Python program, I guess.> Java has a Thread.interrupt method, which sends InterruptedException to a thread. But an InterruptedException is only allowed to be thrown at specific times, for instance during Thread.sleep. Otherwise the thread needs to explicitly call Thread.interrupted() to see if it’s supposed to stop.
I remember when I read about it back then when I played a bit with Java.
I felt bit baffled that they would make something so stupid like `stop()` ... after all Delphi that I learned some years before already had this solution that you only can notify the thread it should stop, through Termintated flag on the thread object and you had to write the thread code to voluntarily end when the flag is set.
(Seriously; Thread.raise is just COMEFROM with a non-deterministic line number.)