[ruby-core:20999] Supporting Thread.critical=with native threads

View: New views
20 Messages — Rating Filter:   Alert me  
< Prev | 1 - 2 | Next >

[ruby-core:20999] Supporting Thread.critical=with native threads

by Shri Borde :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Some parts of this message have been removed. Learn more about Nabble's security policy.

Hi,

 

Thread.critical= is supposed to not schedule any other thread, in addition to guaranteeing that only one thread is ever in the block with Thread.critical==true. This is easier to support with green threads.

 

With native threads, it is harder to do. I am working on IronRuby, and have some questions:

 

1. Should it be by spec that other threads are not allowed to be scheduled? Thread#inspect for the other threads actually says “run”, not “sleep”.

 

2. How would you test that the other threads are not scheduled. It can be done by having the other threads increment some class variable, and by checking that its value does not change. So this is mostly a rhetorical question.

 

3. Given that implementations with native threads cannot support this perfectly, how many apps would actually be affected? The few gems I have looked at only need that Thread.critical=true only behave like a critical section. The apps will work even if the other threads are scheduled, as long the other threads block when they themselves try to set Thread.critical=true. According to http://www.megasolutions.net/ruby/basic-threading-question_can-ruby-use-real-threads_-64225.aspx, JRuby supports this by having every thread periodically do a checkpoint to see if it should suspend itself. It seems that most apps would continue working if there was only one checkpoint,in Thread.critical=.

 

Thanks,

Shri

 


[ruby-core:21000] Re: Supporting Thread.critical=with native threads

by Charles Oliver Nutter-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Shri Borde wrote:
> Thread.critical= is supposed to not schedule any other thread, in
> addition to guaranteeing that only one thread is ever in the block with
> Thread.critical==true. This is easier to support with green threads.
>
> With native threads, it is harder to do. I am working on IronRuby, and
> have some questions:

I'll go one further...with native threads (running in parallel) it's
*impossible* to do reliably.

> 1. Should it be by spec that other threads are not allowed to be
> scheduled? Thread#inspect for the other threads actually says “run”, not
> “sleep”.
>
> 2. How would you test that the other threads are not scheduled. It can
> be done by having the other threads increment some class variable, and
> by checking that its value does not change. So this is mostly a
> rhetorical question.
>
> 3. Given that implementations with native threads cannot support this
> perfectly, how many apps would actually be affected? The few gems I have
> looked at only need that Thread.critical=true only behave like a
> critical section. The apps will work even if the other threads are
> scheduled, as long the other threads block when they themselves try to
> set Thread.critical=true. According to
> http://www.megasolutions.net/ruby/basic-threading-question_can-ruby-use-real-threads_-64225.aspx,
> JRuby supports this by having every thread periodically do a checkpoint
> to see if it should suspend itself. It seems that most apps would
> continue working if there was only one checkpoint,in Thread.critical=.

Scripts that use critical= tend to expect that they're guaranteeing more
than just the code in the critical section. For example, if they're
initializing an instance variable they expect nobody else will access it
during initialization. There's probably many cases that just want a
critical section, but others that expect more.

The bottom line, however, is that critical= is *bad*, and that's why it
went away in 1.9. Any code out there that uses it should *not* use it,
and since it's impossible to support unless your implementation does not
support parallel execution of threads, I'd also argue it shouldn't ever
be expected to work.

We have it working in JRuby, and would certainly rather remove it.
However we also need thread checkpoints to support Thread#kill and
Thread#raise, so it wouldn't eliminate checkpointing entirely. I believe
.NET allows threads to be killed (wrongly) so you may not have this problem.

- Charlie


[ruby-core:21001] Re: Supporting Thread.critical=with native threads

by Charles Oliver Nutter-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Charles Oliver Nutter wrote:
> We have it working in JRuby, and would certainly rather remove it.
> However we also need thread checkpoints to support Thread#kill and
> Thread#raise, so it wouldn't eliminate checkpointing entirely. I believe
> .NET allows threads to be killed (wrongly) so you may not have this
> problem.

I should have said we "sorta" have it working in JRuby. It depends on
execution reaching a checkpoint in the other threads, which is certainly
not guaranteed and which is impossible if they make calls into Java
libraries or long-running core class methods. So again, critical= is
broken. Nobody should ever use it, ever.

- Charlie


[ruby-core:21002] Re: Supporting Thread.critical=with native threads

by Eric Hodel :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

On Dec 30, 2008, at 15:00 PM, Shri Borde wrote:
> Thread.critical= is supposed to not schedule any other thread, in  
> addition to guaranteeing that only one thread is ever in the block  
> with Thread.critical==true. This is easier to support with green  
> threads.

This is not true, Thread.critical= does not automatically schedule  
other threads.  The programmer can schedule other threads as they  
desire, however:

$ cat thr.rb
Thread.critical = true

t2 = Thread.start { puts "in other thread" }

puts "t2 finished: #{t2.inspect}"
$ ruby thr.rb
in other thread
t2 finished: #<Thread:0x29324 dead>




[ruby-core:21010] Re: Supporting Thread.critical=with native threads

by Shri Borde :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

http://www.ruby-doc.org/core/classes/Thread.html#M000461 says:

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.

So new threads can be scheduled, as in your example. However, my question is mainly about existing threads. With native threads, it is very hard to put to sleep existing threads. What will the impact to apps be if they go to sleep only if and when they call Thread.critical= ? Will this satisfy the spec?

-----Original Message-----
From: Eric Hodel [mailto:drbrain@...]
Sent: Tuesday, December 30, 2008 5:20 PM
To: ruby-core@...
Subject: [ruby-core:21002] Re: Supporting Thread.critical=with native threads

On Dec 30, 2008, at 15:00 PM, Shri Borde wrote:
> Thread.critical= is supposed to not schedule any other thread, in
> addition to guaranteeing that only one thread is ever in the block
> with Thread.critical==true. This is easier to support with green
> threads.

This is not true, Thread.critical= does not automatically schedule
other threads.  The programmer can schedule other threads as they
desire, however:

$ cat thr.rb
Thread.critical = true

t2 = Thread.start { puts "in other thread" }

puts "t2 finished: #{t2.inspect}"
$ ruby thr.rb
in other thread
t2 finished: #<Thread:0x29324 dead>





[ruby-core:21011] Re: Supporting Thread.critical=with native threads

by Shri Borde :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Yes, Thread#kill and Thread#raise can be implemented in IronRuby by using System.Threading.Thread.Abort (http://msdn.microsoft.com/en-us/library/system.threading.thread.abort.aspx). It is fragile (like you say, its impossible to support such operations reliably on native threads), and can be deferred indefinitely under some circumstances. So I think we ideally need to use both Thread.Abort and checkpoints.

About Thread.critical, you said:
> Scripts that use critical= tend to expect that they're guaranteeing more
> than just the code in the critical section. For example, if they're
> initializing an instance variable they expect nobody else will access it
> during initialization
It would seem such code will not work even with MRI. If thread 1 sets Thread.critical=true and starts initializing the instance variable, and if thread 2 is accessing the instance variable without setting Thread.critical=true, then couldn't thread 2 run into a problem even if its not scheduled, since it could be anywhere in the middle of accessing the instance variable? To be fully correct, wouldn't thread 2 have to set Thread.critical=true, even on MRI?

By the way, what is the level of atomicity supported by MRI? For example, CPython uses the global interpreter lock (GIL) for each bytecode. However, since MRI does not compile to bytecode, is the AST nodes the correct level of atomicity?

-----Original Message-----
From: Charles.O.Nutter@... [mailto:Charles.O.Nutter@...] On Behalf Of Charles Oliver Nutter
Sent: Tuesday, December 30, 2008 3:19 PM
To: ruby-core@...
Cc: Jim Deville
Subject: [ruby-core:21000] Re: Supporting Thread.critical=with native threads

Shri Borde wrote:
> Thread.critical= is supposed to not schedule any other thread, in
> addition to guaranteeing that only one thread is ever in the block with
> Thread.critical==true. This is easier to support with green threads.
>
> With native threads, it is harder to do. I am working on IronRuby, and
> have some questions:

I'll go one further...with native threads (running in parallel) it's
*impossible* to do reliably.

> 1. Should it be by spec that other threads are not allowed to be
> scheduled? Thread#inspect for the other threads actually says “run”, not
> “sleep”.
>
> 2. How would you test that the other threads are not scheduled. It can
> be done by having the other threads increment some class variable, and
> by checking that its value does not change. So this is mostly a
> rhetorical question.
>
> 3. Given that implementations with native threads cannot support this
> perfectly, how many apps would actually be affected? The few gems I have
> looked at only need that Thread.critical=true only behave like a
> critical section. The apps will work even if the other threads are
> scheduled, as long the other threads block when they themselves try to
> set Thread.critical=true. According to
> http://www.megasolutions.net/ruby/basic-threading-question_can-ruby-use-real-threads_-64225.aspx,
> JRuby supports this by having every thread periodically do a checkpoint
> to see if it should suspend itself. It seems that most apps would
> continue working if there was only one checkpoint,in Thread.critical=.

Scripts that use critical= tend to expect that they're guaranteeing more
than just the code in the critical section. For example, if they're
initializing an instance variable they expect nobody else will access it
during initialization. There's probably many cases that just want a
critical section, but others that expect more.

The bottom line, however, is that critical= is *bad*, and that's why it
went away in 1.9. Any code out there that uses it should *not* use it,
and since it's impossible to support unless your implementation does not
support parallel execution of threads, I'd also argue it shouldn't ever
be expected to work.

We have it working in JRuby, and would certainly rather remove it.
However we also need thread checkpoints to support Thread#kill and
Thread#raise, so it wouldn't eliminate checkpointing entirely. I believe
.NET allows threads to be killed (wrongly) so you may not have this problem.

- Charlie


[ruby-core:21012] Re: Supporting Thread.critical=with native threads

by Charles Oliver Nutter-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Shri Borde wrote:
> Yes, Thread#kill and Thread#raise can be implemented in IronRuby by using System.Threading.Thread.Abort (http://msdn.microsoft.com/en-us/library/system.threading.thread.abort.aspx). It is fragile (like you say, its impossible to support such operations reliably on native threads), and can be deferred indefinitely under some circumstances. So I think we ideally need to use both Thread.Abort and checkpoints.

As under the JVM (and under any system with parallel-executing threads)
System.Threading.Thread.Abort is provably unsafe, so if that's a concern
you'll definitely need to checkpoint to support Ruby's Thread#raise and
Thread#kill.

> About Thread.critical, you said:
>> Scripts that use critical= tend to expect that they're guaranteeing more
>> than just the code in the critical section. For example, if they're
>> initializing an instance variable they expect nobody else will access it
>> during initialization
> It would seem such code will not work even with MRI. If thread 1 sets Thread.critical=true and starts initializing the instance variable, and if thread 2 is accessing the instance variable without setting Thread.critical=true, then couldn't thread 2 run into a problem even if its not scheduled, since it could be anywhere in the middle of accessing the instance variable? To be fully correct, wouldn't thread 2 have to set Thread.critical=true, even on MRI?

Once critical=true is set in a thread, all current threads will be taken
out of scheduling, so in theory they would not even get the opportunity
to access any instance variables.

I'll grant that it's pretty fuzzy; I think people use Thread.critical=
in a lot of cases simply because they don't really know what they're
doing. If critical= could be specified as simply a thread-reentrant lock
against a special global mutex, it would probably be reasonable. As it
is, the implications are far greater than simply a critical section,
since it not only prevents other existing threads from running that
given piece of code, but also from running *at all*, and code has been
written, released, and tested with that assumption.

> By the way, what is the level of atomicity supported by MRI? For example, CPython uses the global interpreter lock (GIL) for each bytecode. However, since MRI does not compile to bytecode, is the AST nodes the correct level of atomicity?

The AST node is not a fine enough measure of atomicity; for example, for
a += b, there's a couple levels of node granularity, but obviously the
outermose "op assignment" can't be atomic. And when it comes down to
that, a large number of nodes are dependent on making method calls,
which usually can't be guaranteed atomic. I think it needs to be
considered on a case-by-case basis. JRuby guarantees instance vars can
be accessed and modified concurrently, for example, but makes no
guarantees about @a += b producing the result you expect in a
multi-threaded environment.

- Charlie


Re: [ruby-core:20999] Supporting Thread.critical=with native threads

by Brent Roman :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Hi Shri,

While it is true that Thread.critical= cannot be realized if multiple threads run in parallel,
this is not the case for the current MRI.  At least, when I last looked, even Ruby 1.9 was
running "Native Thread with Giant VM lock".  If this is still the case, the attached patch
will implement the Thread.critical= method.  Please note, however, that this patch was
last tested about one year ago.  It should be used as a guide.  Hope it helps, or at least
gives you some ideas.

- brent

ruby19_thcritical.patch

Shri Borde wrote:
Hi,

Thread.critical= is supposed to not schedule any other thread, in addition to guaranteeing that only one thread is ever in the block with Thread.critical==true. This is easier to support with green threads.

With native threads, it is harder to do. I am working on IronRuby, and have some questions:

...

[ruby-core:21025] Re: Supporting Thread.critical=with native threads

by Evan Phoenix :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


On Dec 30, 2008, at 11:55 PM, Charles Oliver Nutter wrote:

> Shri Borde wrote:
>> Yes, Thread#kill and Thread#raise can be implemented in IronRuby by  
>> using System.Threading.Thread.Abort (http://msdn.microsoft.com/en-us/library/system.threading.thread.abort.aspx 
>> ). It is fragile (like you say, its impossible to support such  
>> operations reliably on native threads), and can be deferred  
>> indefinitely under some circumstances. So I think we ideally need  
>> to use both Thread.Abort and checkpoints.
>
> As under the JVM (and under any system with parallel-executing  
> threads) System.Threading.Thread.Abort is provably unsafe, so if  
> that's a concern you'll definitely need to checkpoint to support  
> Ruby's Thread#raise and Thread#kill.
>
>> About Thread.critical, you said:
>>> Scripts that use critical= tend to expect that they're  
>>> guaranteeing more
>>> than just the code in the critical section. For example, if they're
>>> initializing an instance variable they expect nobody else will  
>>> access it
>>> during initialization
>> It would seem such code will not work even with MRI. If thread 1  
>> sets Thread.critical=true and starts initializing the instance  
>> variable, and if thread 2 is accessing the instance variable  
>> without setting Thread.critical=true, then couldn't thread 2 run  
>> into a problem even if its not scheduled, since it could be  
>> anywhere in the middle of accessing the instance variable? To be  
>> fully correct, wouldn't thread 2 have to set Thread.critical=true,  
>> even on MRI?
>
> Once critical=true is set in a thread, all current threads will be  
> taken out of scheduling, so in theory they would not even get the  
> opportunity to access any instance variables.

The way that I talk about what Thread.critical does is manipulate the  
preemptive Thread scheduler. So, it inhibits Threads from switching  
"randomly" at any point. Instead, Threads can only be scheduled  
explicitly. This means via Thread methods (inspect, value, join, run),  
as well as some IO methods that explicitly switch Threads.

So, to use terms that people working with green threads use:

"Thread.critical = false" == preemptive scheduling enabled
"Thread.critical = true"  == cooperative scheduling enabled

Obviously the idea with using cooperative scheduling is that the user  
knows what operations cause Threads to switch. Sadly in MRI, that  
knowledge completely known.

>
>
> I'll grant that it's pretty fuzzy; I think people use  
> Thread.critical= in a lot of cases simply because they don't really  
> know what they're doing. If critical= could be specified as simply a  
> thread-reentrant lock against a special global mutex, it would  
> probably be reasonable. As it is, the implications are far greater  
> than simply a critical section, since it not only prevents other  
> existing threads from running that given piece of code, but also  
> from running *at all*, and code has been written, released, and  
> tested with that assumption.
>
>> By the way, what is the level of atomicity supported by MRI? For  
>> example, CPython uses the global interpreter lock (GIL) for each  
>> bytecode. However, since MRI does not compile to bytecode, is the  
>> AST nodes the correct level of atomicity?
>
> The AST node is not a fine enough measure of atomicity; for example,  
> for a += b, there's a couple levels of node granularity, but  
> obviously the outermose "op assignment" can't be atomic. And when it  
> comes down to that, a large number of nodes are dependent on making  
> method calls, which usually can't be guaranteed atomic. I think it  
> needs to be considered on a case-by-case basis. JRuby guarantees  
> instance vars can be accessed and modified concurrently, for  
> example, but makes no guarantees about @a += b producing the result  
> you expect in a multi-threaded environment.
>
> - Charlie
>



[ruby-core:21026] Re: Supporting Thread.critical=with native threads

by David Flanagan :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Charlie, Evan, Shri:

Just to be clear here, would you all agree that Thread.critical= is a
deprecated feature--removed in Ruby 1.9, and that your discussion of it
is limited to compatibility for Ruby 1.8 programs?  None of you are
proposing to revive it as a non-deprecated feature in your
implementations, are you?

        David Flanagan

Evan Phoenix wrote:

>
> On Dec 30, 2008, at 11:55 PM, Charles Oliver Nutter wrote:
>
>> Shri Borde wrote:
>>> Yes, Thread#kill and Thread#raise can be implemented in IronRuby by
>>> using System.Threading.Thread.Abort
>>> (http://msdn.microsoft.com/en-us/library/system.threading.thread.abort.aspx).
>>> It is fragile (like you say, its impossible to support such
>>> operations reliably on native threads), and can be deferred
>>> indefinitely under some circumstances. So I think we ideally need to
>>> use both Thread.Abort and checkpoints.
>>
>> As under the JVM (and under any system with parallel-executing
>> threads) System.Threading.Thread.Abort is provably unsafe, so if
>> that's a concern you'll definitely need to checkpoint to support
>> Ruby's Thread#raise and Thread#kill.
>>
>>> About Thread.critical, you said:
>>>> Scripts that use critical= tend to expect that they're guaranteeing
>>>> more
>>>> than just the code in the critical section. For example, if they're
>>>> initializing an instance variable they expect nobody else will
>>>> access it
>>>> during initialization
>>> It would seem such code will not work even with MRI. If thread 1 sets
>>> Thread.critical=true and starts initializing the instance variable,
>>> and if thread 2 is accessing the instance variable without setting
>>> Thread.critical=true, then couldn't thread 2 run into a problem even
>>> if its not scheduled, since it could be anywhere in the middle of
>>> accessing the instance variable? To be fully correct, wouldn't thread
>>> 2 have to set Thread.critical=true, even on MRI?
>>
>> Once critical=true is set in a thread, all current threads will be
>> taken out of scheduling, so in theory they would not even get the
>> opportunity to access any instance variables.
>
> The way that I talk about what Thread.critical does is manipulate the
> preemptive Thread scheduler. So, it inhibits Threads from switching
> "randomly" at any point. Instead, Threads can only be scheduled
> explicitly. This means via Thread methods (inspect, value, join, run),
> as well as some IO methods that explicitly switch Threads.
>
> So, to use terms that people working with green threads use:
>
> "Thread.critical = false" == preemptive scheduling enabled
> "Thread.critical = true"  == cooperative scheduling enabled
>
> Obviously the idea with using cooperative scheduling is that the user
> knows what operations cause Threads to switch. Sadly in MRI, that
> knowledge completely known.
>
>>
>>
>> I'll grant that it's pretty fuzzy; I think people use Thread.critical=
>> in a lot of cases simply because they don't really know what they're
>> doing. If critical= could be specified as simply a thread-reentrant
>> lock against a special global mutex, it would probably be reasonable.
>> As it is, the implications are far greater than simply a critical
>> section, since it not only prevents other existing threads from
>> running that given piece of code, but also from running *at all*, and
>> code has been written, released, and tested with that assumption.
>>
>>> By the way, what is the level of atomicity supported by MRI? For
>>> example, CPython uses the global interpreter lock (GIL) for each
>>> bytecode. However, since MRI does not compile to bytecode, is the AST
>>> nodes the correct level of atomicity?
>>
>> The AST node is not a fine enough measure of atomicity; for example,
>> for a += b, there's a couple levels of node granularity, but obviously
>> the outermose "op assignment" can't be atomic. And when it comes down
>> to that, a large number of nodes are dependent on making method calls,
>> which usually can't be guaranteed atomic. I think it needs to be
>> considered on a case-by-case basis. JRuby guarantees instance vars can
>> be accessed and modified concurrently, for example, but makes no
>> guarantees about @a += b producing the result you expect in a
>> multi-threaded environment.
>>
>> - Charlie
>>
>
>
>



[ruby-core:21027] Re: Supporting Thread.critical=with native threads

by Evan Phoenix :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


On Dec 31, 2008, at 1:19 PM, David Flanagan wrote:

> Charlie, Evan, Shri:
>
> Just to be clear here, would you all agree that Thread.critical= is  
> a deprecated feature--removed in Ruby 1.9, and that your discussion  
> of it is limited to compatibility for Ruby 1.8 programs?  None of  
> you are proposing to revive it as a non-deprecated feature in your  
> implementations, are you?

Matz has pretty clearly stated that Thread.critical was an  
implementation specific feature of 1.8 that is deprecated/removed in  
1.9. I whole heartedly agree with this, since we have sufficient  
replacements to use (Mutex, Sync, etc) that are easily implemented on  
all implementations.

  - Evan

>
>
> David Flanagan
>
> Evan Phoenix wrote:
>> On Dec 30, 2008, at 11:55 PM, Charles Oliver Nutter wrote:
>>> Shri Borde wrote:
>>>> Yes, Thread#kill and Thread#raise can be implemented in IronRuby  
>>>> by using System.Threading.Thread.Abort (http://msdn.microsoft.com/en-us/library/system.threading.thread.abort.aspx 
>>>> ). It is fragile (like you say, its impossible to support such  
>>>> operations reliably on native threads), and can be deferred  
>>>> indefinitely under some circumstances. So I think we ideally need  
>>>> to use both Thread.Abort and checkpoints.
>>>
>>> As under the JVM (and under any system with parallel-executing  
>>> threads) System.Threading.Thread.Abort is provably unsafe, so if  
>>> that's a concern you'll definitely need to checkpoint to support  
>>> Ruby's Thread#raise and Thread#kill.
>>>
>>>> About Thread.critical, you said:
>>>>> Scripts that use critical= tend to expect that they're  
>>>>> guaranteeing more
>>>>> than just the code in the critical section. For example, if  
>>>>> they're
>>>>> initializing an instance variable they expect nobody else will  
>>>>> access it
>>>>> during initialization
>>>> It would seem such code will not work even with MRI. If thread 1  
>>>> sets Thread.critical=true and starts initializing the instance  
>>>> variable, and if thread 2 is accessing the instance variable  
>>>> without setting Thread.critical=true, then couldn't thread 2 run  
>>>> into a problem even if its not scheduled, since it could be  
>>>> anywhere in the middle of accessing the instance variable? To be  
>>>> fully correct, wouldn't thread 2 have to set  
>>>> Thread.critical=true, even on MRI?
>>>
>>> Once critical=true is set in a thread, all current threads will be  
>>> taken out of scheduling, so in theory they would not even get the  
>>> opportunity to access any instance variables.
>> The way that I talk about what Thread.critical does is manipulate  
>> the preemptive Thread scheduler. So, it inhibits Threads from  
>> switching "randomly" at any point. Instead, Threads can only be  
>> scheduled explicitly. This means via Thread methods (inspect,  
>> value, join, run), as well as some IO methods that explicitly  
>> switch Threads.
>> So, to use terms that people working with green threads use:
>> "Thread.critical = false" == preemptive scheduling enabled
>> "Thread.critical = true"  == cooperative scheduling enabled
>> Obviously the idea with using cooperative scheduling is that the  
>> user knows what operations cause Threads to switch. Sadly in MRI,  
>> that knowledge completely known.
>>>
>>>
>>> I'll grant that it's pretty fuzzy; I think people use  
>>> Thread.critical= in a lot of cases simply because they don't  
>>> really know what they're doing. If critical= could be specified as  
>>> simply a thread-reentrant lock against a special global mutex, it  
>>> would probably be reasonable. As it is, the implications are far  
>>> greater than simply a critical section, since it not only prevents  
>>> other existing threads from running that given piece of code, but  
>>> also from running *at all*, and code has been written, released,  
>>> and tested with that assumption.
>>>
>>>> By the way, what is the level of atomicity supported by MRI? For  
>>>> example, CPython uses the global interpreter lock (GIL) for each  
>>>> bytecode. However, since MRI does not compile to bytecode, is the  
>>>> AST nodes the correct level of atomicity?
>>>
>>> The AST node is not a fine enough measure of atomicity; for  
>>> example, for a += b, there's a couple levels of node granularity,  
>>> but obviously the outermose "op assignment" can't be atomic. And  
>>> when it comes down to that, a large number of nodes are dependent  
>>> on making method calls, which usually can't be guaranteed atomic.  
>>> I think it needs to be considered on a case-by-case basis. JRuby  
>>> guarantees instance vars can be accessed and modified  
>>> concurrently, for example, but makes no guarantees about @a += b  
>>> producing the result you expect in a multi-threaded environment.
>>>
>>> - Charlie
>>>
>
>



[ruby-core:21036] Re: Supporting Thread.critical=with native threads

by Charles Oliver Nutter-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Evan Phoenix wrote:

> The way that I talk about what Thread.critical does is manipulate the
> preemptive Thread scheduler. So, it inhibits Threads from switching
> "randomly" at any point. Instead, Threads can only be scheduled
> explicitly. This means via Thread methods (inspect, value, join, run),
> as well as some IO methods that explicitly switch Threads.
>
> So, to use terms that people working with green threads use:
>
> "Thread.critical = false" == preemptive scheduling enabled
> "Thread.critical = true"  == cooperative scheduling enabled
>
> Obviously the idea with using cooperative scheduling is that the user
> knows what operations cause Threads to switch. Sadly in MRI, that
> knowledge completely known.

Not a bad way to describe it at all. I'd add "new" to the list of
explicit scheduling operations. It would surely be nice to make this
list official, of course :)

- Charlie


[ruby-core:21047] Re: Supporting Thread.critical=with native threads

by Charles Oliver Nutter-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

David Flanagan wrote:
> Charlie, Evan, Shri:
>
> Just to be clear here, would you all agree that Thread.critical= is a
> deprecated feature--removed in Ruby 1.9, and that your discussion of it
> is limited to compatibility for Ruby 1.8 programs?  None of you are
> proposing to revive it as a non-deprecated feature in your
> implementations, are you?

Absolutely not. I'd rather we not have to support it on 1.8 either :)

- Charlie


[ruby-core:21245] Re: Supporting Thread.critical=with native threads

by Charles Oliver Nutter-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

I'm starting come around to Shri's idea of critical= being represented
as simply a global lock. Shri makes a very strong case that the
descheduling behavior of critical is a side effect that almost nobody
depends on. I'd go further and say that nobody *should* depend on it,
since on e.g JRuby there's no guarantee when or if the other threads
will completely deschedule.

Representing critical= as a global reentrant mutex would also reduce the
overhead parallel-threaded impls deal with to constantly checkpoint, and
would codify a very clear and specific meaning for critical sections. It
would not be a recommended way to criticalize a block of code, since it
could bottleneck if several threads try to criticalize. But it would at
least be more reasonable to support on parallel-threaded impls than what
we have today.

Interestingly enough, a global critical lock also is compatible with
what 1.8 does right now, since 1.8's total descheduling of other threads
is a superset of global lock behavior. The only bits that are fuzzy
would be threads explicitly scheduled or spun up during a critical
section, and we'd need to discuss those.

Thoughts? I almost want to just make this change in JRuby right now,
since it's so much cleaner.

- Charlie


[ruby-core:21353] Re: Supporting Thread.critical=with native threads

by Shri Borde :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Is opening a bug the recommended way to get the spec changed for 1.9*?

IMO, nobody *should* depend on descheduling of other threads even on MRI. Given that you do not know where the other threads may be when they were descheduled, I think it is close to impossible to access a piece of data with Thread.critical==true sometimes, and sometimes without setting Thread.critical=true.

I have written up the details of the proposed behavior here so that we can think of all the interactions with other APIs etc:

http://blogs.msdn.com/shrib/archive/2009/01/07/proposed-spec-for-ruby-s-thread-critical.aspx

Thanks,
Shri


-----Original Message-----
From: Charles.O.Nutter@... [mailto:Charles.O.Nutter@...] On Behalf Of Charles Oliver Nutter
Sent: Saturday, January 10, 2009 7:21 AM
To: ruby-core@...
Subject: [ruby-core:21245] Re: Supporting Thread.critical=with native threads

I'm starting come around to Shri's idea of critical= being represented
as simply a global lock. Shri makes a very strong case that the
descheduling behavior of critical is a side effect that almost nobody
depends on. I'd go further and say that nobody *should* depend on it,
since on e.g JRuby there's no guarantee when or if the other threads
will completely deschedule.

Representing critical= as a global reentrant mutex would also reduce the
overhead parallel-threaded impls deal with to constantly checkpoint, and
would codify a very clear and specific meaning for critical sections. It
would not be a recommended way to criticalize a block of code, since it
could bottleneck if several threads try to criticalize. But it would at
least be more reasonable to support on parallel-threaded impls than what
we have today.

Interestingly enough, a global critical lock also is compatible with
what 1.8 does right now, since 1.8's total descheduling of other threads
is a superset of global lock behavior. The only bits that are fuzzy
would be threads explicitly scheduled or spun up during a critical
section, and we'd need to discuss those.

Thoughts? I almost want to just make this change in JRuby right now,
since it's so much cleaner.

- Charlie



Re: [ruby-core:21353] Re: Supporting Thread.critical=with native threads

by Brent Roman :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Shri & Charlie,

I just had a look at the proposed spec for Thread.critical= at
  http://blogs.msdn.com/shrib/archive/2009/01/07/proposed-spec-for-ruby-s-thread-critical.aspx

When Ruby 1.6.8 first appeared, all the thread locking primitives had Thread.critical=
at their core.  It was the only thread locking mechanism available.

No one is claiming that it is good or modern.  The only reason to keep supporting it
is to allow old Ruby scripts to run in newer implementations.  

To that end, I'd like to point out that:
  1)  Thread.critical= is not a block construct.  Talking about it being reentrant or not makes no sense.
       It is a global attribute, atomically set true or false.
         --> Thread.exclusive {} is a block construct based on Thread.critical.  Is that the confusion?

  2)  If a thread executes Thread.critical=true when it is already true, absolutely nothing changes.
 
  3)  Thread.critical=false exits the critical section, regardless of how many times it had been been redundantly set true.

  4)  Thread.critical=false when it is already false does absolutely nothing.

  5)  If a thread dies while Thread.critical=true, it remains set.  It does not throw an exception.

  6)  Thread.stop sets Thread.critical=false and blocks the current thread in one, atomic operation.

There should be no wriggle room in the spec about this.  The existing behavior may not be optimal, but it is simple and well established.  Old code depends on it, warts and all.  New code should avoid the use the Thread.critical entirely.

For native threaded implementations, all of this can, and probably should, be implemented with a single, non-reentrant mutex.  Thread.critical=true when it was false would lock the global mutex and may block waiting for it.  Thread.critical=false when it was true would unlock it.

I think this approach is straightforward, allows native threaded interpreters to avoid expensive checkpointing and maintains very good compatibility with existing Ruby scripts.  I would suggest that JRuby keep its existing checkpointing available as a run-time option for maximum compatibility, analogous to its support of ObjectSpace.  Thus, JRuby could continue to support those few scripts that do depend on the fact that, in MRI and other green threaded implementations, Thread.critical=true can be used to keep all other threads from running.

- brent

[ruby-core:21361] Re: Supporting Thread.critical=with native threads

by Shri Borde :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Brent, we are looking for Ruby to officially allow Thread.critical= to be implemented as a single non-reentrant mutex. This would help convince people writing Ruby libraries in the future to use Thread.critical= without assuming that other threads will be descheduled. Otherwise, they can push back on JRuby and IronRuby as not being fully-compatible.

The proposed spec I wrote up would clean things up even more. For example, you said "Thread.critical=false exits the critical section, regardless of how many times it had been been redundantly set true." However, consider the case where thread A sets Thread.critical=true, and then thread B does Thread.critical=false. (Note that even MRI will schedule other threads under certain circumstances like a newly created thread or if thread A does Kernel#sleep). In this case, you cannot force thread A to release the mutex. Your wording could possibly still be supported, for example, by having thread B create a new mutex and publish that as the single non-reentrant mutex to be used henceforth. I think such clean up would be good without causing too much incompatilibiles.

However, we don't have to do all of these changes if compatability is a significant concern. As long as descheduling of other threads was not a requirement, that would go a long way.

Regards,
Shri



[ruby-core:21362] Re: Supporting Thread.critical=with native threads

by Jim Deville :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

One of the points in the spec is that with native threads, there is no way to guarantee that all other threads stop when Thread.critical=true is called. If you want to allow Threads to block waiting for a global mutex, you risk deadlocks, which isn't an acceptable alternative.

I thought that either Charlie or Shri had looked and not currently found a piece of code expecting Thread.critical to unscheduled other threads. Do you have a counter example?

JD

-----Original Message-----
From: Brent Roman [mailto:brent@...]
Sent: Wednesday, January 14, 2009 8:58 PM
To: ruby-core@...
Subject: [ruby-core:21359] Re: Supporting Thread.critical=with native threads


Shri & Charlie,

I just had a look at the proposed spec for Thread.critical= at

http://blogs.msdn.com/shrib/archive/2009/01/07/proposed-spec-for-ruby-s-thread-critical.aspx

When Ruby 1.6.8 first appeared, all the thread locking primitives had
Thread.critical=
at their core.  It was the only thread locking mechanism available.

No one is claiming that it is good or modern.  The only reason to keep
supporting it
is to allow old Ruby scripts to run in newer implementations.

To that end, I'd like to point out that:
  1)  Thread.critical= is not a block construct.  Talking about it being
reentrant or not makes no sense.
       It is a global attribute, atomically set true or false.
         --> Thread.exclusive {} is a block construct based on
Thread.critical.  Is that the confusion?

  2)  If a thread executes Thread.critical=true when it is already true,
absolutely nothing changes.

  3)  Thread.critical=false exits the critical section, regardless of how
many times it had been been redundantly set true.

  4)  Thread.critical=false when it is already false does absolutely
nothing.

  5)  If a thread dies while Thread.critical=true, it remains set.  It does
not throw an exception.

  6)  Thread.stop sets Thread.critical=false and blocks the current thread
in one, atomic operation.

There should be no wriggle room in the spec about this.  The existing
behavior may not be optimal, but it is simple and well established.  Old
code depends on it, warts and all.  New code should avoid the use the
Thread.critical entirely.

For native threaded implementations, all of this can, and probably should,
be implemented with a single, non-reentrant mutex.  Thread.critical=true
when it was false would lock the global mutex and may block waiting for it.
Thread.critical=false when it was true would unlock it.

I think this approach is straightforward, allows native threaded
interpreters to avoid expensive checkpointing and maintains very good
compatibility with existing Ruby scripts.  I would suggest that JRuby keep
its existing checkpointing available as a run-time option for maximum
compatibility, analogous to its support of ObjectSpace.  Thus, JRuby could
continue to support those few scripts that do depend on the fact that, in
MRI and other green threaded implementations, Thread.critical=true can be
used to keep all other threads from running.

- brent

--
View this message in context: http://www.nabble.com/-ruby-core%3A20999--Supporting-Thread.critical%3Dwith-native-threads-tp21224464p21471393.html
Sent from the ruby-core mailing list archive at Nabble.com.





Re: [ruby-core:21361] Re: Supporting Thread.critical=with native threads

by Brent Roman :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

Shri,

Please see my comments below --->

Shri Borde wrote:
Brent, we are looking for Ruby to officially allow Thread.critical= to be implemented as a single non-reentrant mutex. This would help convince people writing Ruby libraries in the future to use Thread.critical= without assuming that other threads will be descheduled. Otherwise, they can push back on JRuby and IronRuby as not being fully-compatible.

---> I understand this and agree with your general plan.

The proposed spec I wrote up would clean things up even more. For example, you said "Thread.critical=false exits the critical section, regardless of how many times it had been been redundantly set true." However, consider the case where thread A sets Thread.critical=true, and then thread B does Thread.critical=false. (Note that even MRI will schedule other threads under certain circumstances like a newly created thread or if thread A does Kernel#sleep). In this case, you cannot force thread A to release the mutex. Your wording could possibly still be supported, for example, by having thread B create a new mutex and publish that as the single non-reentrant mutex to be used henceforth. I think such clean up would be good without causing too much incompatilibiles.

---> My point was that "clean up" is not an appropriate goal for this very old, established interface.
       Your goal should be to support it, warts and all, as best you can with native threading.

       Your example about Thread A and Thread B needs to support the current Thread.critical= behavior.
       Some apps (that are not open source) will rely on it.  I realize this behavior may be harder to
       implement, but it is required to maintain compatibility.

       Kernel#sleep does not cause Thread.critical to be set to false.  Try running:
         Thread.critical=true; sleep 1; puts Thread.critical
       I happen to believe that this should output "false", but it outputs "true" on 1.6.8 and 1.8.7
       I ended up writing a trivial extension called "doze" that sets Thread.critical=false before sleeping.
       I believe Thread.critical should be set to false before every blocking operation and the 1.9 patch
       I sent you to implement Thread.critical does this, but that's not how things work now.

However, we don't have to do all of these changes if compatability is a significant concern. As long as descheduling of other threads was not a requirement, that would go a long way.

--->  I believe I do have some code that relies on descheduling.  I certainly rely on it when hitting
        a breakpoint as a means up "stopping the world" while I investigate system state.  I also suspect
        that I rely on it when creating a new thread as way of ensuring that no other thread will see
        its state before it is completely initialized.
        However, I recognize that descheduling is more or less impossible with native threads.
        So, we have to be pragmatic and move on.

- brent



[ruby-core:21384] Re: Supporting Thread.critical=with native threads

by Shri Borde :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

OK. I understand that MRI cannot make breaking changes lightly (though I do think the API is hard to use the way it is currently speced). We will treat this as an incompatibility in IronRuby and deal with it the best we can.


FWIW, Thread.pass and Kernel#sleep do not set Thread.critical to false. However, they do cause other threads to be scheduled. See this code snippet which I tried with MRI 1.8.6 on Windows Vista.

t = Thread.new do
  Thread.critical = true
  $after_critical = true
  Thread.pass
  $after_critical = true
  Thread.critical = false
end

puts $after_critical # prints true
Thread.pass
puts Thread.critical # prints false

So in your scenario, when you hit a breakpoint, if you call some utility code that happens to do Thread.pass, it will "restart the world" which you will not be expecting.

Also, if you create a new thread expecting no one to see its uninitialized state, I think this is fragile design that will be prone to bugs. If any shared data is accessed from two threads, it should be done only if the threads have set Thread.critical=true (ie. use Thread.critical as a mutex without relying on descheduling of other threads).

I would be curious to see real-world patterns where shared data is accessed safely from two threads, and which relied on descheduling of other threads - to see if it was indeed done safely. (I don't mean to suggest that your particular code has bugs but ...) I think many people who try to use this pattern will end up with latent bugs, and I would like to see if this is the case or not.

Thanks for all the information.

Regards,
Shri


< Prev | 1 - 2 | Next >