Brent Roman wrote: > Ignoring the :always and :never states for a moment... > > External exceptions would be enqueued by the Ruby interpreter > whenever the target thread's interruptible status was false. > A thread's interruptible status would be set to true just before it blocked. > The status would remain set to true until it unblocked for any reason. > Thus, each act of setting interruptible status to true would cause > at most one pending exception to be raised in the target thread. > Recall that my original proposal stipulated that the interruptible status > would revert to false whenever an external exception was processed. This still raises "unknown" or "unexpected" exceptions in the target thread, yes? Which would interfere with ensured blocks unless they were uninterruptible. So if we have begin ... ensure blocking_operation critical_cleanup end If an exception were raised immediately after the blocking operation completed, wouldn't it prevent the ensure from running to completion? > I, like many people, feel that Ruby's rich exception processing syntax is > one of its strong points. Thread#raise seems to have been intended > to allow exceptions to propagate among threads so that they > might be used for asynchronous inter-thread communication as well > as synchronous error handling. I think it is worthwhile to make these > existing mechanisms work deterministically (i.e. "safely"), without > requiring logic changes to the great majority of threaded Ruby > applications. I would disagree that Thread#raise is intended as a way to pass any sort of information. I think it's a way to kick a thread back to responsiveness without killing it. But it's still unsafe as it exists today. I do agree that it's worthwhile to explore ways to make it safe, if such ways exist. But I don't see that there's any way to fix it without modifying existing Ruby code. I direct you to timeout.rb again, which can cause a target thread to raise at any time in any method or block of code, possibly neatly cleaving transaction cleanup in half. Your uninterruptible could make it safer, but only if it was the default and threads had to say they wanted to be interrupted in order for it to work, since entering an ensure and then setting uninterruptible would not be an atomic operation. If I'm missing something, please be patient with me and point it out. > I believe that the interruptible status as described *does* solve this > core problem because each thread can determine its own policy concerning > when it will accept an interrupt. The default "magic" policy will work well > for any thread that is not compute bound. Application programmers > will only have to take care in situations where they intend to block > while objects are in a "damaged" state. Recall that: > > Thread.interruptible=:never > > could still be used for threads that wanted to explicitly control all > interactions with others. So perhaps if you want a thread to be safe (does anyone not?) you would set Thread.interruptible = :never and only opt-in for exceptions. That seems like the only way to make it actually safe. But again, it seems like if you're going to spend the two lines to make your thread interruptible and then opt-in for exceptions that might be raised you might as well use thread-synchronization constructs designed for that. > The "opt in" approach is exactly analogous to Thread.interruptible=:never > in that each thread must explicitly poll for interactions of others at > every conceivable synchronization. This leads to code that is peppered > with checkpoints. Failing to include one can result on a thread's appearing > to hang, which will often be perceived as a system failure. > > Further, with "opt in", I cannot see how to reawaken a thread that is > blocked on I/O when another thread might determines that the operation > has timed out or is for some other reason no longer relevant. > How would an "opt in" only model deal with this? It wouldn't, and it can't. It's also provably unsafe to be able to arbitrarily interrupt a blocking operation. Does it leave a file locked? a channel open? In Java, interrupting a thread that's blocking on low-level IO forces the IO channel into a dead state, since it's impossible to guarantee that it hasn't been irreversibly corrupted. The way to solve this--and really the only way to make it safe--is to use non-blocking operations instead. This is how all major multi-threaded workhorse applications are designed...they avoid blocking as much as possible and simulate blocking operations by feeding operations to a non-blocking worker and waiting for responses. - Charlie