Ruby has green threads, and so its implementation of Thread.critical= is speced as follows:

Sets the status of the global ``thread critical’’ condition and returns it. When set to true, prohibits scheduling of any existing thread. Does not block new threads from being created and run. Certain thread operations (such as stopping or killing a thread, sleeping in the current thread, and raising an exception) may cause a thread to be scheduled even when in a critical section. Thread::critical is not intended for daily use: it is primarily there to support folks writing threading libraries.

The requirement of descheduling other threads complicates things for Ruby implementations like IronRuby and JRuby which use native threads. Furthermore, most uses of Thread.critical= do not actually depend on the descheduling of other threads. This is canonical usage:

def get_unique_cookie()

begin

Thread.critical = true

cookie = @@counter

@@counter += 1

ensure

Thread.critical = false

end

cookie

end

So most usages expect Thread.critical to behave like a critical section. Given this, here is the proposed new behavior. This behavior can be efficiently implemented even on native threads, and also works for most usage scenarios providing users with a simple locking construct.

Sets the status of the global ``thread critical’’ condition and returns it. When set to true, blocks any other thread that tries to set Thread.critical=true. It is recommend to use an ensure clause to set Thread.critical to false to exit the "global critical section".

If a thread terminates while it is in the global critical section (either because of an uncaught exception, because of normal termination at the end of the block, or because of a call to Thread.kill), it leaves the process in an undefined state. Some implementations might leave the global critical section allowing other threads to enter it; some implementations might do nothing causing the app to hang if any other thread tries to set Thread.critical=true; some might exit the process.

Calling "sleep" in the critical section does nothing. Calling "Thread.stop" in the global critical section also does nothing (it sets Thread.critical to false in Ruby 1.8.6).

Thread.critical= is not reentrant. If a thread in the global critical section tries to set Thread.critical=true again, the behavior is undefined. Some implementations might require a single Thread.critical=false to leave the global critical section; some might require a matching number of Thread.critical=false; some might throw an exception.

The behavior when calling Thread.critical=false from a thread that is not in the global critical section causes undefined behavior. Some implementations might ignore the call; some might cause the thread in the global critical section (if any) to exit the critical section; some might throw an exception.

The code that executes in the global critical section should be kept to a minimum, and should ideally not call any user-defined code as it opens up the possibility of deadlocks. Since its hard to know when user-defined code can execute (because any library method may have been monkey-patched), Thread.critical= should be used very carefully. Thread::critical is not intended for daily use: it is primarily there to support folks writing threading libraries.

We intend to propose this to ruby-core for approval.

Update: Ruby 1.9 does not support Thread#critical! So this issue should go away in the future. For 1.8.*, here is the ruby-core discussion thread. There may be a few scenarios which rely on thread descheduling, but it does seem to be marginal. JRuby is going to try to implement Thread#critical= as a global mutex. IronRuby is likely to stay on that plan too.