Is assignment to C++ int and pointer variables atomic?

D

Dave Stallard

Pardon if this is the wrong newsgroup for this question, and/or if this
question is naive.

I have a multi-threaded Windows application in which certain
variables/object fields are shared: one thread may write the variable,
and the other thread read it. The variables in question may have int or
int* types. Question: Is it safe to do this? Or is it possible a read
that happens at the same time as a write may retrieve a scrambled value,
in which, say, two of the bytes are from the old value and two of the
bytes from the new value?

In Java I know that such atomicity is guaranteed for ints, but not for
longs or doubles. So, perhaps I shouldn't assume it in a less
well-standardized environment

The application, btw, is a circular buffer with a read pointer and a
write pointer, which are moved forward by their respective threads. The
read thread needs to check to make sure that the read pointer is not on
the write pointer before reading. Using mutexes right now, but I worry
about their overhead...

Dave
 
R

red floyd

Dave said:
Pardon if this is the wrong newsgroup for this question, and/or if this
question is naive.

It's beyond the scope. The Standard doesn't specify threading behavior,
therefore any answer is implementation specific. You might want to ask
in a newsgroup dedicated to your platform.

http://www.parashift.com/c++-faq-lite/how-to-post.html#faq-5.9

That said, I believe int is defined as the native integral type for an
implementation. So it *could* most likely be atomic. But then there's
alignment issues.

So the answer is "it depends, and check your compiler documentation".

Sorry we can't be more help.
 
R

robertwessel2

red said:
That said, I believe int is defined as the native integral type for an
implementation. So it *could* most likely be atomic. But then there's
alignment issues.


While int *could* be atomic on a given platform, it is certainly not
required. Consider, for example, an implementation of C on a typical
8-bit CPU - all ints will have to be accessed with a pair of loads or
stores.
 
A

Alan Johnson

Dave said:
Pardon if this is the wrong newsgroup for this question, and/or if this
question is naive.

I have a multi-threaded Windows application in which certain
variables/object fields are shared: one thread may write the variable,
and the other thread read it. The variables in question may have int or
int* types. Question: Is it safe to do this? Or is it possible a read
that happens at the same time as a write may retrieve a scrambled value,
in which, say, two of the bytes are from the old value and two of the
bytes from the new value?

In Java I know that such atomicity is guaranteed for ints, but not for
longs or doubles. So, perhaps I shouldn't assume it in a less
well-standardized environment

The application, btw, is a circular buffer with a read pointer and a
write pointer, which are moved forward by their respective threads. The
read thread needs to check to make sure that the read pointer is not on
the write pointer before reading. Using mutexes right now, but I worry
about their overhead...

Dave

As others have mentioned, C++ doesn't make any such guarantees.
However, since you mention you are running on Windows, you might look up
the InterlockedExchange, InterlockedExhangePointer,
InterlockedCompareExchange, etc. They can give you some guarantees at
the cost of portability between OSes.

See http://msdn2.microsoft.com/en-us/library/ms686360.aspx for details.
 
G

Gianni Mariani

Note that I have added comp.programming.threads to this post.

Dave said:
Pardon if this is the wrong newsgroup for this question, and/or if this
question is naive.

I have a multi-threaded Windows application in which certain
variables/object fields are shared: one thread may write the variable,
and the other thread read it. The variables in question may have int or
int* types. Question: Is it safe to do this? Or is it possible a read
that happens at the same time as a write may retrieve a scrambled value,
in which, say, two of the bytes are from the old value and two of the
bytes from the new value?


comp.programming.threads is probably the right place to ask such questions.

Sharing variables like this depends on "visibility". Modern CPU's
heavily depend on cache and out of order execution for performance.
Normally, these effects are undetectable in a single threaded program
but they become critical when you're dealing with multi-threaded code.

So, once, a long long time ago if you wrote :

char ring_buffer[1024];
int wloc;
int rloc;

....writer...
ring_buffer[wloc] = ch; // S1
++wloc; // S2

....reader...
if ( wloc > rloc )
{
val = ring_buffer[rloc]; //S3
++rloc; //S4
}

you could be guarenteed that effects of S1 would be visible before S2 to
the other thread. This is not the case now. S2 MAY be visible before
S1 which means that S3 may not see the value in "ch".

There are two reasons this may happen, one is the hardware (CPU) and the
other is the optimizing compiler using the volatile keyword. The second
is to make sure that the hardware compiles. This is totally hardware
dependant - no standard exists yet.

First thing to do is tell the compiler to not mess with the order:

volatile char ring_buffer[1024];
volatile int wloc;
volatile int rloc;

....writer...
ring_buffer[wloc] = ch; // S1
FENCE(); // make sure that S1 is visible
++wloc; // S2

....reader...
FENCE(); // make sure we see everything first
if ( wloc > rloc )
{
val = ring_buffer[rloc]; //S3
++rloc; //S4
}

An excellent paper on the issues involved in standardizing a solution:
http://www.hpl.hp.com/techreports/2004/HPL-2004-209.html

Wikipedia gives an excellent high level perspective.
http://en.wikipedia.org/wiki/Memory_barrier

So, most code uses mutexes and these generally guarentee a memory
barrier, so if you use mutexes, you don't run into sequencing issues,
however they can be a significant performance hit. Having said that,
many and quite probably most applications will never need to worry about
the performance hit of mutexes.
 
R

Rolf Magnus

red said:
It's beyond the scope. The Standard doesn't specify threading behavior,
therefore any answer is implementation specific. You might want to ask
in a newsgroup dedicated to your platform.

http://www.parashift.com/c++-faq-lite/how-to-post.html#faq-5.9

That said, I believe int is defined as the native integral type for an
implementation. So it *could* most likely be atomic. But then there's
alignment issues.

Another thing that you probably need to worry about are caches on a
multiprocessor/multicore system. If you write an int in one thread, even
after the write operation is finished, the other thread might still see the
old value if it runs on the other CPU core because the change hasn't
propagated from one cache through RAM into the other cache.
 
?

=?ISO-8859-1?Q?Erik_Wikstr=F6m?=

Pardon if this is the wrong newsgroup for this question, and/or if this
question is naive.

I have a multi-threaded Windows application in which certain
variables/object fields are shared: one thread may write the variable,
and the other thread read it. The variables in question may have int or
int* types. Question: Is it safe to do this? Or is it possible a read
that happens at the same time as a write may retrieve a scrambled value,
in which, say, two of the bytes are from the old value and two of the
bytes from the new value?

I can give you no guarantees but I'm sure it's quite safe to assume that
both reading and writing to an int (getting and setting its value) is
atomic. That, however, have very few applications, only when one thread
is writing only and the other thread is reading only. In most practical
applications you'll what to have a thread to first read the value, then
perhaps perform some check and then modify the value and write the
modified value back. These kinds of operations are not atomic on any
platform that I know about* not even in Java. The problem being that the
value can change between the read of the value and the write of the
modified value. To solve these issues you'll need to call syncronization
functions that are platform-specific.

* There are some architectures that supports a few selected atomic
operations, like compare and swap or compare and add.
 
C

Chris Thomasson

Gianni Mariani said:
Note that I have added comp.programming.threads to this post.




comp.programming.threads is probably the right place to ask such
questions.

Sharing variables like this depends on "visibility". Modern CPU's heavily
depend on cache and out of order execution for performance. Normally,
these effects are undetectable in a single threaded program but they
become critical when you're dealing with multi-threaded code.

http://appcore.home.comcast.net/vzdoc/atomic/static-init/
(section 2 deals with some of the issues involved)
 
D

Dave Stallard

Gianni said:
An excellent paper on the issues involved in standardizing a solution:
http://www.hpl.hp.com/techreports/2004/HPL-2004-209.html

Wikipedia gives an excellent high level perspective.
http://en.wikipedia.org/wiki/Memory_barrier

So, most code uses mutexes and these generally guarentee a memory
barrier, so if you use mutexes, you don't run into sequencing issues,
however they can be a significant performance hit. Having said that,
many and quite probably most applications will never need to worry about
the performance hit of mutexes.

Thanks, Gianni, for your very informative response, and thank you
everyone else who answered. I've taken my lesson, and will lock, lock,
lock in the future.

Dave
 

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,755
Messages
2,569,535
Members
45,007
Latest member
obedient dusk

Latest Threads

Top