assignments and thread safety

C

Csaba Henk

Hi there?

Is "$a ||= 1" thread safe? I'd guess so, as "||=" is one piece of
operator. (And one operator/function call can't be interruted by the
scheduler, I'd guess so... is that right?)

And what's about "$a or $a = 1" ? Now I don't see any guarantee that if
such a code runs in multiple threads, then assignment occurs only once.

However, I couldn't produce such a case. The code

ruby -e '20.times { |i| Thread.new {
sleep 1
$foo ? p($foo) : $foo = i
}
}; (Thread.list - [Thread.current]).each{|t| t.join}'

always accurately prints 19 lines (and similary for other numbers: if I
use some n instead of 20, I get n-1 lines).

Is it just an accident, or we can know somehow that this code is thread
safe? Where do the limits of thread safety lie?

Csaba
 
Y

Yohanes Santoso

Csaba Henk said:
Is "$a ||= 1" thread safe? I'd guess so, as "||=" is one piece of

It is not guaranteed to be thread-safe, although in current official
ruby vm it is 'atomic'.
Is it just an accident, or we can know somehow that this code is thread
safe? Where do the limits of thread safety lie?

Treat it as an accident and don't depend on this behaviour. You should
synchronise access to shared resources.

Yours,

YS.
 
N

nobu.nokada

Hi,

At Sun, 27 Feb 2005 17:43:47 +0900,
Yohanes Santoso wrote in [ruby-talk:132474]:
It is not guaranteed to be thread-safe, although in current official
ruby vm it is 'atomic'.

It is impossilbe.

What do you expect for:

$a ||= very.long.operation?
 
C

Csaba Henk

It is not guaranteed to be thread-safe, although in current official
ruby vm it is 'atomic'.

So that means it thread safe now, but it's not guaranteed to stay so?
Is there a real chance/reason to make it non-atomic (either during the
development of the current implementation, or in a new implementation

Then how to do the following: I need a method which inserts an element
into an array, named by an instance variable. The variable is possibly
undefined. I want to make it thread-safe. The best I came up with is

@a ||= []
@a << foo

which then thread-safe by the current implementation, but not guaranteed
to stay so...

Note that I can't do "@a = []" in #initialize (as the code lies within a
block passed to #define_method within an instance method definition of
Module itself; so it's not just a simple static method definition for a
certain class).

If I had @mutex,

@mutex.synchronize { @a ||= [] }
@a << foo

would do the job perfectly; but having @mutex is exactly the same
problem as having @a ...

Having @@mutex in the class would be fine as far as thread safety goes, but
then all instances would use that same @@mutex on each call, absolutely
unnecessarily after the first time... Maybe

@@mutex.synchronize { @a ||= [] } unless @a
@a << foo

? I don't have any better idea...
Treat it as an accident and don't depend on this behaviour. You should
synchronise access to shared resources.

Ah, thanks. I thought it's just this way.

Regards,
Csaba
 
C

Csaba Henk

Hi,

At Sun, 27 Feb 2005 17:43:47 +0900,
Yohanes Santoso wrote in [ruby-talk:132474]:
It is not guaranteed to be thread-safe, although in current official
ruby vm it is 'atomic'.

It is impossilbe.

What do you expect for:

$a ||= very.long.operation?

Well, I had there "$a ||= 1", and fetching 1 is not a
very.long.operation.

But I see the point... even if concretely with 1 being the assinged
value, "$a ||= 1" happens atomically (which could be the case for other
literals and variables on the right hand side), it's necessarily
accidental, so to say. "||=" can't have the value of the right hand side
at its invocation...

Csaba
 
E

ES

It is not guaranteed to be thread-safe, although in current official
ruby vm it is 'atomic'.

So that means it thread safe now, but it's not guaranteed to stay so?
Is there a real chance/reason to make it non-atomic (either during the
development of the current implementation, or in a new implementation

Then how to do the following: I need a method which inserts an element
into an array, named by an instance variable. The variable is possibly
undefined. I want to make it thread-safe. The best I came up with is

@a ||= []
@a << foo

which then thread-safe by the current implementation, but not guaranteed
to stay so...

Note that I can't do "@a = []" in #initialize (as the code lies within a
block passed to #define_method within an instance method definition of
Module itself; so it's not just a simple static method definition for a
certain class).

If I had @mutex,

@mutex.synchronize { @a ||= [] }
@a << foo

would do the job perfectly; but having @mutex is exactly the same
problem as having @a ...

Having @@mutex in the class would be fine as far as thread safety goes, but
then all instances would use that same @@mutex on each call, absolutely
unnecessarily after the first time... Maybe

Just answering to this point: there's a trick called the double-checked
locking pattern that avoids the unnecessary lock. I'm not sure if it helps
any in your situation:

@@mutex.synchronize {@a ||= [] unless @a} unless @a
@@mutex.synchronize { @a ||= [] } unless @a
@a << foo

? I don't have any better idea...
Treat it as an accident and don't depend on this behaviour. You should
synchronise access to shared resources.

Ah, thanks. I thought it's just this way.

Regards,
Csaba

E
 
C

Csaba Henk

there's a trick called the double-checked
locking pattern that avoids the unnecessary lock. I'm not sure if it helps
any in your situation:

@@mutex.synchronize {@a ||= [] unless @a} unless @a
@@mutex.synchronize { @a ||= [] } unless @a
@a << foo

Thanks!

If you see my solution (which is already in your mail as a quote, just
in the line after the one proposed by you), it's almost the same, just
there is no "unless" in the scope of the mutex. So I kinda figured out
that this is the way to go... Is there any use of that inner "unless"?
I'd guess no...

Regards,
Csaba
 
R

Robert Klemme

ES said:
Is "$a ||= 1" thread safe? I'd guess so, as "||=" is one piece of

It is not guaranteed to be thread-safe, although in current official
ruby vm it is 'atomic'.

So that means it thread safe now, but it's not guaranteed to stay so?
Is there a real chance/reason to make it non-atomic (either during the
development of the current implementation, or in a new implementation

Then how to do the following: I need a method which inserts an element
into an array, named by an instance variable. The variable is possibly
undefined. I want to make it thread-safe. The best I came up with is

@a ||= []
@a << foo

which then thread-safe by the current implementation, but not guaranteed
to stay so...

Note that I can't do "@a = []" in #initialize (as the code lies within a
block passed to #define_method within an instance method definition of
Module itself; so it's not just a simple static method definition for a
certain class).

If I had @mutex,

@mutex.synchronize { @a ||= [] }
@a << foo

would do the job perfectly; but having @mutex is exactly the same
problem as having @a ...

Having @@mutex in the class would be fine as far as thread safety goes, but
then all instances would use that same @@mutex on each call, absolutely
unnecessarily after the first time... Maybe

Just answering to this point: there's a trick called the double-checked
locking pattern that avoids the unnecessary lock. I'm not sure if it helps
any in your situation:

@@mutex.synchronize {@a ||= [] unless @a} unless @a

There is one test too much. I'm sure you wanted this:

@@mutex.synchronize {@a ||= []} unless @a

It might work in Ruby, but it doesn't in other languages - Java for
example. I'd rather not use it as it makes code more complicated. Use
this only if you have a serious performance problem. Even then I'm not
sure whether the difference is big enough to matter. Plus, you might run
into the same problems as with double checked locking on Java when Ruby
runs on different platforms (a JVM for example using native threads).

Kind regards

robert
 
C

Csaba Henk

@@mutex.synchronize {@a ||= []} unless @a

It might work in Ruby, but it doesn't in other languages - Java for
example. I'd rather not use it as it makes code more complicated. Use
this only if you have a serious performance problem. Even then I'm not
sure whether the difference is big enough to matter. Plus, you might run
into the same problems as with double checked locking on Java when Ruby
runs on different platforms (a JVM for example using native threads).

Thanks for the pointer.

I don't know much about JVMs and how they work...

When you write a general lib, you have to be careful.

The potential users won't be interested in technical details. Not in the
first turn. They just ask "is this such-and-such? Yes/no?"

So, I want to be able to say just simply "yes" if one asks about thread
safety. The performance hit is not that big typically: each object locks
the mutex only once.

Csaba
 
R

Robert Klemme

Csaba Henk said:
@@mutex.synchronize {@a ||= []} unless @a

It might work in Ruby, but it doesn't in other languages - Java for
example. I'd rather not use it as it makes code more complicated. Use
this only if you have a serious performance problem. Even then I'm not
sure whether the difference is big enough to matter. Plus, you might run
into the same problems as with double checked locking on Java when Ruby
runs on different platforms (a JVM for example using native threads).

Thanks for the pointer.

I don't know much about JVMs and how they work...

If you're interested in the topic look out for keywords "Java", "JVM" and
"Memory Model". There are quite some resources with indepth explanations
out there. Also, you'll find some articles with "why double checked locking
is broken".
When you write a general lib, you have to be careful.

The potential users won't be interested in technical details. Not in the
first turn. They just ask "is this such-and-such? Yes/no?"

So, I want to be able to say just simply "yes" if one asks about thread
safety. The performance hit is not that big typically: each object locks
the mutex only once.

In that case I'd start with the proper synchronized version and optimize
later.

Regards

robert
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top