Atomic operations involving __int64 on 32 bit machines
-
Hello! Let's say we have the following:
class MyClass
{
public:
void SetValue( __int64 a_i64Value );
__int64 GetValue( void ) const;private:
__int64 m_i64Value;
};void MyClass::SetValue( __int64 a_i64Value )
{
m_i64Value = a_i64Value;
}__int64 MyClass::GetValue( void ) const
{
return m_i64Value;
}We also have two threads:
ThreadA
andThreadB
, each setting the value ofm_i64Value
to something different. Let's assume thatThreadA
executes andSetValue
is called. It writes 32 bits ofm_i64Value
,ThreadB
executes, it callsSetValue
which also writes 32 bits ofm_i64Value
, thenThreadA
resumes and continues writing the other 32 bits ofm_i64Value
. Finally,ThreadB
also writes the other half ofm_i64Value
. Eventually,m_i64Value
contains garbage, invalid data. Question 1: Is this scenario valid? Can it happen on a 32 bit machine? Anyway, this can be solved usingInterlockedExchange64
, right? But let's suppose there is aThreadC
which needs to read that value, using the member functionGetValue
. When returning fromGetValue
, 32 bits ofm_i64Value
get written toEAX
register and 32 bits toEDX
. Question 2: What if 32 bits get written toEAX
,ThreadB
resumes and writes tom_i64Value
and after that,ThreadC
resumes and the other 32 bits (changed byThreadB
) go toEDX
? Is this also a possibility on 32 bit machines? If yes, what is the best way to return such a value (__int64, in this example)? I guess one of the solutions could be this one:void GetValue( __int64 *a_pi64Value )
{
if ( NULL != a_pi64Value )
{
InterlockedExchange64( *a_pi64Value, m_i64Value );
}
}Question 3: But what if we still want to actually return the value and not copy it to the memory pointed by
a_pi64Value
? Can this be done somehow thread-safely and using the return instruction? Thanks in advance! -
Hello! Let's say we have the following:
class MyClass
{
public:
void SetValue( __int64 a_i64Value );
__int64 GetValue( void ) const;private:
__int64 m_i64Value;
};void MyClass::SetValue( __int64 a_i64Value )
{
m_i64Value = a_i64Value;
}__int64 MyClass::GetValue( void ) const
{
return m_i64Value;
}We also have two threads:
ThreadA
andThreadB
, each setting the value ofm_i64Value
to something different. Let's assume thatThreadA
executes andSetValue
is called. It writes 32 bits ofm_i64Value
,ThreadB
executes, it callsSetValue
which also writes 32 bits ofm_i64Value
, thenThreadA
resumes and continues writing the other 32 bits ofm_i64Value
. Finally,ThreadB
also writes the other half ofm_i64Value
. Eventually,m_i64Value
contains garbage, invalid data. Question 1: Is this scenario valid? Can it happen on a 32 bit machine? Anyway, this can be solved usingInterlockedExchange64
, right? But let's suppose there is aThreadC
which needs to read that value, using the member functionGetValue
. When returning fromGetValue
, 32 bits ofm_i64Value
get written toEAX
register and 32 bits toEDX
. Question 2: What if 32 bits get written toEAX
,ThreadB
resumes and writes tom_i64Value
and after that,ThreadC
resumes and the other 32 bits (changed byThreadB
) go toEDX
? Is this also a possibility on 32 bit machines? If yes, what is the best way to return such a value (__int64, in this example)? I guess one of the solutions could be this one:void GetValue( __int64 *a_pi64Value )
{
if ( NULL != a_pi64Value )
{
InterlockedExchange64( *a_pi64Value, m_i64Value );
}
}Question 3: But what if we still want to actually return the value and not copy it to the memory pointed by
a_pi64Value
? Can this be done somehow thread-safely and using the return instruction? Thanks in advance!Eikthrynir wrote:
Question 1: Is this scenario valid? Can it happen on a 32 bit machine?
Yes as the MSDN states in the article Interlocked Variable Access[^]: Reads and writes to 64-bit values are not guaranteed to be atomic on 32-bit Windows. Reads and writes to variables of other sizes are not guaranteed to be atomic on any platform.
Eikthrynir wrote:
Question 2: What if 32 bits get written to EAX, ThreadB resumes and writes to m_i64Value and after that, ThreadC resumes and the other 32 bits (changed by ThreadB) go to EDX? Is this also a possibility on 32 bit machines? If yes, what is the best way to return such a value (__int64, in this example)?
Eikthrynir wrote:
Question 3: But what if we still want to actually return the value and not copy it to the memory pointed by a_pi64Value? Can this be done somehow thread-safely and using the return instruction?
I would personally do it like this:
//Add zero to the value and return the initial value. LONGLONG GetValue(__int64 *a_pi64Value) { return InterlockedExchangeAdd64(a_pi64Value,0); }
Best Wishes, -David Delaunemodified on Wednesday, May 13, 2009 11:41 AM
-
Eikthrynir wrote:
Question 1: Is this scenario valid? Can it happen on a 32 bit machine?
Yes as the MSDN states in the article Interlocked Variable Access[^]: Reads and writes to 64-bit values are not guaranteed to be atomic on 32-bit Windows. Reads and writes to variables of other sizes are not guaranteed to be atomic on any platform.
Eikthrynir wrote:
Question 2: What if 32 bits get written to EAX, ThreadB resumes and writes to m_i64Value and after that, ThreadC resumes and the other 32 bits (changed by ThreadB) go to EDX? Is this also a possibility on 32 bit machines? If yes, what is the best way to return such a value (__int64, in this example)?
Eikthrynir wrote:
Question 3: But what if we still want to actually return the value and not copy it to the memory pointed by a_pi64Value? Can this be done somehow thread-safely and using the return instruction?
I would personally do it like this:
//Add zero to the value and return the initial value. LONGLONG GetValue(__int64 *a_pi64Value) { return InterlockedExchangeAdd64(a_pi64Value,0); }
Best Wishes, -David Delaunemodified on Wednesday, May 13, 2009 11:41 AM
Hello! Thanks for the reply! I would like to make a few comments regarding your version of
GetValue
... 1. You call InterlockedExchange64 witha_pi64Value
as the first parameter, so the__int64
variable pointed bya_pi64Value
gets0
. Then, you return the previous value of that__int64
variable which is certainly not the one we are interested in,m_i64Value
. 2.GetValue
returns aLONGLONG
value, so we find ourselves in exactly the same situation from Question 2 (concerning the EAX and EDX registers)... Best regards! -
Hello! Thanks for the reply! I would like to make a few comments regarding your version of
GetValue
... 1. You call InterlockedExchange64 witha_pi64Value
as the first parameter, so the__int64
variable pointed bya_pi64Value
gets0
. Then, you return the previous value of that__int64
variable which is certainly not the one we are interested in,m_i64Value
. 2.GetValue
returns aLONGLONG
value, so we find ourselves in exactly the same situation from Question 2 (concerning the EAX and EDX registers)... Best regards!Eikthrynir wrote:
1. You call InterlockedExchange64 with a_pi64Value as the first parameter, so the __int64 variable pointed by a_pi64Value gets 0. Then, you return the previous value of that __int64 variable which is certainly not the one we are interested in, m_i64Value.
If the 64 bit integer variable you want to read atomically is m_i64Value Then the correct way to atomically read the value is:
__int64 i64Val = InterlockedExchangeAdd64(m_i64Value,0);
Eikthrynir wrote:
2. GetValue returns a LONGLONG value, so we find ourselves in exactly the same situation from Question 2 (concerning the EAX and EDX registers)...
This statement does not make any sense to me. Best Wishes, -David Delaune