Thread-safety and atomic reading in Windows OS
I have this piece of code in my application. I suspect that it is not thread-safe, so decide to ask the SOers.
int * volatile int_ptr;
int count;
Thread 1:
void grow(int new_count)
{
if(new_count <= count) return;
int * new_ptr = new int[new_count];
memset(new_ptr, 0 , new_count * sizeof(int));
memcpy(new_ptr,int_ptr,count);
int * dum_ptr = (int *)InterlockedExchange((volatile long *)&int_ptr,(long)new_ptr)
count = new_count;
delete [] dum_ptr;
}
Thread 2:
int get_value(int index)
{
return int_ptr[index];
}
I know that CRITICAL_SECTION can be used but Thread 1 will work maybe once in a week whereas Thread 2 will work millions of times in a day. In the 99.99999%开发者_JS百科 of the attemps to access the int_ptr
, 2nd thread will enter and exit the critical section for nothing. It just does not make sense to me. The application will be working only in Windows 2000 and later with Intel processors obviously with multicores.
Is this code thread-safe? If not, what should I do to make it thread-safe? How can I read int_ptr atomically ? i. e. :
int * dummy_ptr = read_atomic<int *>(int_ptr);
return dummy_ptr[index];
A solution, including std::vector
, makes me happier and more comfortable.
No, this is not safe. get_value
can read the value of int_ptr
, then get scheduled out. Then another thread can swap out int_ptr
and delete
the old value. When get_value
is swapped back in, it tries to dereference the value it read from int_ptr
- which has already been deleted.
A better approach would be based on "Read-copy-update" (RCU), which has already been used to great effect in Linux. The basic principle is that you don't delete the old value right away - you wait until some point in time when you can be conservatively sure that nothing still has the old pointer value, then delete it.
Unfortunately, there's no RCU library implementation on Windows yet. You could try porting urcu to Windows though, I suppose.
No, its not. One scenario is that thread2 is inside the get_value
function and is doing int_ptr[index]
. Since its not atomic this may take several instructions. Half way through these instruction a thread context switch happens and thread1 starts executing withgrow
. This will delete[]
the int_ptr
. Now when thread2 starts it will encounter an access violation. You can use CCriticalSection
to solve this problem. Since it's not a kernel object, the performance is not so bad on Windows OS.
Consider making thread 1 ask thread 2 to do the update or to wait while the update is being performed.
For this to be useful, thread 2 must listen for some sort of signal or message from the rest of the system. A message passing mechanism or condition variable can be extended with a new message. Also consider APCs (somewhat like Unix signals).
This needs to be actually asking and not forcibly suspending. Forcibly suspending a thread does not solve the problem as the thread may be suspended at any point, including between reading int_ptr
and reading int_ptr[index]
.
精彩评论