Quote:Original post by BradDaBug
Why would it matter if m_head changes between those two lines? Wouldn't the InterlockedCompareExchangePointer() detect that n is now different than m_head, then when it returned the current version of m_head it would be different than n, and the loop would continue and try again?
Yeah it won't be a big deal, unless m_Head has been changed to NULL ;) It slightly worse performance-wise as well, due to m_Head being volatile (whereas n is not).
To handle that case (where there is one item on the stack, and two threads simultaneously pop it), you'd have to do something like:
n = m_head; if( !n ) {//one of: return RemoveMessage(); //for RemoveMessage, try again return false;//for TryRemoveMessage, return failure } Node* next = n->Next;
[EDIT]Actually, does the semaphore take care of this for you? Just to make sure, you could put in: if(!n)printf("oh noes!!!"); ;)[/EDIT]
Quote:On a hopefully unrelated note, I've been doing some testing with several threads, and I've started getting an error message that says something like "Damage before 0xSomeMemoryAddress which was allocated by aligned routine".
Not exactly sure, but it sounds like a memory-overrun problem. Double-check you're not looping past the end of any arrays, using any pointers after they've been deleted, or doing any dangerous casts (e.g. allocating a Foo and casting the pointer to a Bar*).
Actually, the above bug, if it occurs, would lead to calling free twice on the same pointer, which is going to do god-knows-what to memory.
[edit #2]
Found another bug :( As before, it probably won't do anything bad because the Interlocked Exchange will save you... but you are potentially accessing invalid memory, which AFAIK is undefined behaviour.
This problem is actually written about in most of the papers on lock-free linked list structures... It's really hard to safely deallocate the nodes, because other threads may still hold pointers to them.
One (rough) solution is to garbage collect the nodes - instead of deallocating them instantly, you put them into a queue to be deleted in "some suitable amount of time in the future".
Here's the scenario:
// Two threads call this function at the same time: T RemoveMessage() { SDL_SemWait(m_semaphore); Node* n = NULL; while(true) { n = m_head; //Both threads get here at the same time, they both read the same value of m_head into 'n' //Thread #1 then goes to sleep here (see below where it wakes up in this scenario) Node* next = n->Next; //Thread #1 just accessed unallocated memory, hopefully the system didn't notice... if (InterlockedCompareExchangePointer((volatile PVOID*)&m_head, next, n) == n) break; } T t = n->Data; _aligned_free(n); //When Thread#2 reaches this point, Thread#1 wakes back up //'n' has now been deallocated, but Thread#1 is about to access 'n.Next', which is now undefined! return t; }
[Edited by - Hodgman on December 7, 2009 5:15:55 PM]