The Interlocked on the Edge of Forever

This is a discussion on The Interlocked on the Edge of Forever within the Framework and Interface Programming forums in category; I've got a quick question that's been bugging me for a long, long time: Let's say I've got a member variable (in a heavily threaded app): private int _firstTime = 0; To make changes to this is easy enough: InterlockedExchange(ref _firstTime, 1); .... or I can use the InterlockedCompareExchange: int oldValue = InterlockedCompareExchange(ref _firstTime, 1, 0); No problems here. .... but, if I just want to read the value, I CANNOT do: int myValue = _firstTime; or if (_fistTime==0) Doing these requires a memory barrier of some type (volatile variable, monitor, etc) to have any degree of reliability. Now, reading ...

Go Back   Application Development Forum > Framework and Interface Programming

Object Mix

Register FAQ Calendar Search Today's Posts Mark Forums Read
  #1  
Old 04-30-2007, 04:14 PM
Chris Mullins [MVP]
Guest
 
Default The Interlocked on the Edge of Forever

I've got a quick question that's been bugging me for a long, long time:

Let's say I've got a member variable (in a heavily threaded app):
private int _firstTime = 0;

To make changes to this is easy enough:
InterlockedExchange(ref _firstTime, 1);

.... or I can use the InterlockedCompareExchange:
int oldValue = InterlockedCompareExchange(ref _firstTime, 1, 0);

No problems here.

.... but, if I just want to read the value, I CANNOT do:

int myValue = _firstTime;
or
if (_fistTime==0)

Doing these requires a memory barrier of some type (volatile variable,
monitor, etc) to have any degree of reliability.

Now, reading the value out using InterlockedCompareExchange always seemed a
bit silly to me. I don't want to always write:

int myValue = InterlockedCompareExchange(ref _firstTime, Int32.MinValue,
Int32.MinValue);

I've never found (although not for lack of looking):
int myValue = InterlockedRead(ref _firstTime);

Of the code that I write, reading the value, and branching based on it's
value, is much more common than writing to the value.

I have, in the past, used volitile variables for this - but I've stopped due
to the various issues regarding volatiles (non atomic reads & writes).

Is there a concensus on the best way to do this? It seems like all the
solutions are.... incomplete.

Essentially I'm looking for a volitile varaiable that is atomically read and
written. Unfortuantly, these don't exist in any language I've yet used.

--
Chris Mullins, MCSD.NET, MCPD:Enterprise, Microsoft C# MVP
http://www.coversant.com/blogs/cmullins


Reply With Quote
  #2  
Old 04-30-2007, 06:05 PM
Jon Skeet [C# MVP]
Guest
 
Default Re: The Interlocked on the Edge of Forever

Chris Mullins [MVP] <cmullins@yahoo.com> wrote:

<snip>

> Essentially I'm looking for a volitile varaiable that is atomically
> read and written. Unfortuantly, these don't exist in any language
> I've yet used.


I'm not sure what you're after that a simple volatile *wouldn't* give
you here, given that *any* Int32 variable will be accessed atomically,
whether it's volatile or not. You can't do atomic increments without
something like Interlocked, but simple reads and writes are atomic.

--
Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Reply With Quote
  #3  
Old 04-30-2007, 06:05 PM
Jon Skeet [C# MVP]
Guest
 
Default Re: The Interlocked on the Edge of Forever

Chris Mullins [MVP] <cmullins@yahoo.com> wrote:

<snip>

> Essentially I'm looking for a volitile varaiable that is atomically
> read and written. Unfortuantly, these don't exist in any language
> I've yet used.


I'm not sure what you're after that a simple volatile *wouldn't* give
you here, given that *any* Int32 variable will be accessed atomically,
whether it's volatile or not. You can't do atomic increments without
something like Interlocked, but simple reads and writes are atomic.

--
Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too
Reply With Quote
  #4  
Old 04-30-2007, 09:16 PM
Peter Duniho
Guest
 
Default Re: The Interlocked on the Edge of Forever

On Mon, 30 Apr 2007 13:13:47 -0700, Chris Mullins [MVP]
<cmullins@yahoo.com> wrote:

> [...]
> Of the code that I write, reading the value, and branching based on it's
> value, is much more common than writing to the value.
>
> I have, in the past, used volitile variables for this - but I've stopped
> due to the various issues regarding volatiles (non atomic reads &
> writes).


Jon's reply matches what I believed to be true. Can you explain why it is
you believe that a non-atomic read or write is possible for a 32-bit
value? If that's true, it's a pretty significant error in the way I've
approached multi-threaded programming. Which is not out of the question
by any means, but if that's the case I really want to know about it and
know why it's the case.

Pete
Reply With Quote
  #5  
Old 04-30-2007, 09:16 PM
Peter Duniho
Guest
 
Default Re: The Interlocked on the Edge of Forever

On Mon, 30 Apr 2007 13:13:47 -0700, Chris Mullins [MVP]
<cmullins@yahoo.com> wrote:

> [...]
> Of the code that I write, reading the value, and branching based on it's
> value, is much more common than writing to the value.
>
> I have, in the past, used volitile variables for this - but I've stopped
> due to the various issues regarding volatiles (non atomic reads &
> writes).


Jon's reply matches what I believed to be true. Can you explain why it is
you believe that a non-atomic read or write is possible for a 32-bit
value? If that's true, it's a pretty significant error in the way I've
approached multi-threaded programming. Which is not out of the question
by any means, but if that's the case I really want to know about it and
know why it's the case.

Pete
Reply With Quote
  #6  
Old 05-01-2007, 12:09 AM
Barry Kelly
Guest
 
Default Re: The Interlocked on the Edge of Forever

Chris Mullins [MVP] wrote:

> I've got a quick question that's been bugging me for a long, long time:
>
> Let's say I've got a member variable (in a heavily threaded app):
> private int _firstTime = 0;


> Doing these requires a memory barrier of some type (volatile variable,
> monitor, etc) to have any degree of reliability.


Yes.

> I've never found (although not for lack of looking):
> int myValue = InterlockedRead(ref _firstTime);


Perhaps Thread.VolatileRead() is what you're looking for?

> I have, in the past, used volitile variables for this - but I've stopped due
> to the various issues regarding volatiles (non atomic reads & writes).


Can you expand on your objection to volatile variables?

> Is there a concensus on the best way to do this? It seems like all the
> solutions are.... incomplete.
>
> Essentially I'm looking for a volitile varaiable that is atomically read and
> written. Unfortuantly, these don't exist in any language I've yet used.


I'm not aware of any circumstances where normally aligned volatile
variables of the machine word size or smaller are read or written
non-atomically on the CLR. Can you explain more?

-- Barry

--
http://barrkel.blogspot.com/
Reply With Quote
  #7  
Old 05-01-2007, 12:09 AM
Barry Kelly
Guest
 
Default Re: The Interlocked on the Edge of Forever

Chris Mullins [MVP] wrote:

> I've got a quick question that's been bugging me for a long, long time:
>
> Let's say I've got a member variable (in a heavily threaded app):
> private int _firstTime = 0;


> Doing these requires a memory barrier of some type (volatile variable,
> monitor, etc) to have any degree of reliability.


Yes.

> I've never found (although not for lack of looking):
> int myValue = InterlockedRead(ref _firstTime);


Perhaps Thread.VolatileRead() is what you're looking for?

> I have, in the past, used volitile variables for this - but I've stopped due
> to the various issues regarding volatiles (non atomic reads & writes).


Can you expand on your objection to volatile variables?

> Is there a concensus on the best way to do this? It seems like all the
> solutions are.... incomplete.
>
> Essentially I'm looking for a volitile varaiable that is atomically read and
> written. Unfortuantly, these don't exist in any language I've yet used.


I'm not aware of any circumstances where normally aligned volatile
variables of the machine word size or smaller are read or written
non-atomically on the CLR. Can you explain more?

-- Barry

--
http://barrkel.blogspot.com/
Reply With Quote
  #8  
Old 05-05-2007, 06:08 PM
Holger Grund
Guest
 
Default Re: The Interlocked on the Edge of Forever

"Chris Mullins [MVP]" <cmullins@yahoo.com> wrote
> I've got a quick question that's been bugging me for a long, long time:
>
> Let's say I've got a member variable (in a heavily threaded app):
> private int _firstTime = 0;
>
> To make changes to this is easy enough:
> InterlockedExchange(ref _firstTime, 1);
>
> ... or I can use the InterlockedCompareExchange:
> int oldValue = InterlockedCompareExchange(ref _firstTime, 1, 0);
>
> No problems here.
>
> ... but, if I just want to read the value, I CANNOT do:
>
> int myValue = _firstTime;
> or
> if (_fistTime==0)
>

Why?

> Doing these requires a memory barrier of some type (volatile variable,
> monitor, etc) to have any degree of reliability.
>

I don't see the need for a memory barrier, unless you need memory
ordering guarantees. Volatile access would make sense since you really want
to read from memory. However, this only affects how the Jitter generates
code.

> Now, reading the value out using InterlockedCompareExchange always seemed
> a bit silly to me. I don't want to always write:
>
> int myValue = InterlockedCompareExchange(ref _firstTime, Int32.MinValue,
> Int32.MinValue);


This looks quite pointless. If you don't need memory ordering guarantees a
volatile read is good enough. I don't find the spec now, but I suspect
InterlockedCompareExchange has probably both acquire and release
semantics.

>
> I've never found (although not for lack of looking):
> int myValue = InterlockedRead(ref _firstTime);
>

This is just a memory fence plus a volatile read. And it's not clear that
you
actually need the former.

> Of the code that I write, reading the value, and branching based on it's
> value, is much more common than writing to the value.
>

Again volatile read is what you want.

> I have, in the past, used volitile variables for this - but I've stopped
> due to the various issues regarding volatiles (non atomic reads & writes).
>

What makes you believe that either reads or writes are non-atomic?
They certainly are at CLR instruction level.

> Is there a concensus on the best way to do this? It seems like all the
> solutions are.... incomplete.


For a one-time init thing you will probably need atomic compare
exchange for acquiring the construction lock and release semantics when
signaling successful construction.

If you need to wait for construction you'd probably have three states:
- uninitialized
- under_construction
- constructed

Checking whether the shared object is constructed is done by a volatile
read from the guard variable. If the result is "constructed" you're done.
Just a few cycles - maybe satisified from cache.

If not, use atomic compare exchange to acquire the "under-construction"
lock. If an other thread was faster wait. If not construct object and
use store with release semantics to signal object constructed.

I'm not sure if there is a really efficient Wait method (in this particular
context) in the CLR.

Anyway, the standard case (object already constructed) is very fast.
>
> Essentially I'm looking for a volitile varaiable that is atomically read
> and written. Unfortuantly, these don't exist in any language I've yet
> used.
>

I think the CLI standard requires that to a certain extent. That said, the
text is vague. Bottom line I think you can rely on reads or writes being
volatile, but not more complex operations that both read and write,
e.g.:

volatile int i;
do_something_with(i); // ok
i = some_value(); // ok
i++; // bad no guarantees here

But one might argue that this is really a language thing.

-hg


Reply With Quote
  #9  
Old 05-05-2007, 06:08 PM
Holger Grund
Guest
 
Default Re: The Interlocked on the Edge of Forever

"Chris Mullins [MVP]" <cmullins@yahoo.com> wrote
> I've got a quick question that's been bugging me for a long, long time:
>
> Let's say I've got a member variable (in a heavily threaded app):
> private int _firstTime = 0;
>
> To make changes to this is easy enough:
> InterlockedExchange(ref _firstTime, 1);
>
> ... or I can use the InterlockedCompareExchange:
> int oldValue = InterlockedCompareExchange(ref _firstTime, 1, 0);
>
> No problems here.
>
> ... but, if I just want to read the value, I CANNOT do:
>
> int myValue = _firstTime;
> or
> if (_fistTime==0)
>

Why?

> Doing these requires a memory barrier of some type (volatile variable,
> monitor, etc) to have any degree of reliability.
>

I don't see the need for a memory barrier, unless you need memory
ordering guarantees. Volatile access would make sense since you really want
to read from memory. However, this only affects how the Jitter generates
code.

> Now, reading the value out using InterlockedCompareExchange always seemed
> a bit silly to me. I don't want to always write:
>
> int myValue = InterlockedCompareExchange(ref _firstTime, Int32.MinValue,
> Int32.MinValue);


This looks quite pointless. If you don't need memory ordering guarantees a
volatile read is good enough. I don't find the spec now, but I suspect
InterlockedCompareExchange has probably both acquire and release
semantics.

>
> I've never found (although not for lack of looking):
> int myValue = InterlockedRead(ref _firstTime);
>

This is just a memory fence plus a volatile read. And it's not clear that
you
actually need the former.

> Of the code that I write, reading the value, and branching based on it's
> value, is much more common than writing to the value.
>

Again volatile read is what you want.

> I have, in the past, used volitile variables for this - but I've stopped
> due to the various issues regarding volatiles (non atomic reads & writes).
>

What makes you believe that either reads or writes are non-atomic?
They certainly are at CLR instruction level.

> Is there a concensus on the best way to do this? It seems like all the
> solutions are.... incomplete.


For a one-time init thing you will probably need atomic compare
exchange for acquiring the construction lock and release semantics when
signaling successful construction.

If you need to wait for construction you'd probably have three states:
- uninitialized
- under_construction
- constructed

Checking whether the shared object is constructed is done by a volatile
read from the guard variable. If the result is "constructed" you're done.
Just a few cycles - maybe satisified from cache.

If not, use atomic compare exchange to acquire the "under-construction"
lock. If an other thread was faster wait. If not construct object and
use store with release semantics to signal object constructed.

I'm not sure if there is a really efficient Wait method (in this particular
context) in the CLR.

Anyway, the standard case (object already constructed) is very fast.
>
> Essentially I'm looking for a volitile varaiable that is atomically read
> and written. Unfortuantly, these don't exist in any language I've yet
> used.
>

I think the CLI standard requires that to a certain extent. That said, the
text is vague. Bottom line I think you can rely on reads or writes being
volatile, but not more complex operations that both read and write,
e.g.:

volatile int i;
do_something_with(i); // ok
i = some_value(); // ok
i++; // bad no guarantees here

But one might argue that this is really a language thing.

-hg


Reply With Quote
  #10  
Old 05-06-2007, 01:38 AM
William Stacey [C# MVP]
Guest
 
Default Re: The Interlocked on the Edge of Forever

| To make changes to this is easy enough:
| InterlockedExchange(ref _firstTime, 1);
|
| ... or I can use the InterlockedCompareExchange:
| int oldValue = InterlockedCompareExchange(ref _firstTime, 1, 0);
|
| No problems here.
|
| ... but, if I just want to read the value, I CANNOT do:
|
| int myValue = _firstTime;
| or
| if (_fistTime==0)
|
| Doing these requires a memory barrier of some type (volatile variable,
| monitor, etc) to have any degree of reliability.

you can. The Interlocked write ensures this across all cpus and cache. Any
thread reading the var after the Interlocked write will see the correct var
so it is a memory barrier without a volatile modifier. All locks are
ultimatley built on interlocked at the lowest level to get this behavior.
So for reads, you can just go "if (var == 2){}". Naturally, this works for
a single var. If you have multiple invariants you need to protect, you
probabaly need a lock.


Reply With Quote
Reply


Thread Tools
Display Modes


All times are GMT -5. The time now is 03:25 AM.


Powered by vBulletin® Version 3.7.2
Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO 3.2.0
vB Ad Management by =RedTyger=

In an effort to better serve ads to our visitors, cookies are used on objectmix.com. For more information, check out our Privacy Policy.