I've learned that InterlockedCompareExchange()
is used to read interlocked variables.
Also, InterlockedCompareExchange()
is preferable rather than InterlockedOr()
by Raymond Chen from the comments of reference:
interlocked reading a 64-bit variable
Now, I am facing too many InterlockedCompareExchange()
function calls in my thread code, and this doesn't seem right.
For instance, below is simple thread code that uses GetAsyncKeyState()
and stores the key event to a queue (Container::CQueue
is a custom circular queue for academic purpose). GetAsyncKeyState()
and storing the key event is just an example. So please don't focus on why I'm not using a Window message queue and WndProc()
.
unsigned long __stdcall Thread(void* const _pParameter)
{
Container::CQueue<EKeyEvent>* pKeyEventQueue = nullptr;
bool bMouseLeftDown = false;
while (true)
{
// Check Exit Loop Interlocked Variable
if (InterlockedCompareExchange16(&m_sThreadExit, 0i16, 0i16) != 0i16)
{
break;
}
if (GetAsyncKeyState(VK_LBUTTON) & 0x8000i16)
{
if (bMouseLeftDown == false)
{
bMouseLeftDown = true;
if (static_cast<Container::CQueue<EKeyEvent>*>(InterlockedCompareExchangePointer(reinterpret_cast<void**>(&m_pCurrentKeyEventQueue), nullptr, nullptr)) == nullptr)
{
break;
}
if (static_cast<Container::CQueue<EKeyEvent>*>(InterlockedCompareExchangePointer(reinterpret_cast<void**>(&m_pCurrentKeyEventQueue), nullptr, nullptr))->Enqueue(EKeyEvent::MouseLeft_Down) != Container::EQueueResult::Success)
{
break;
}
}
}
else
{
if (bMouseLeftDown)
{
bMouseLeftDown = false;
if (static_cast<Container::CQueue<EKeyEvent>*>(InterlockedCompareExchangePointer(reinterpret_cast<void**>(&m_pCurrentKeyEventQueue), nullptr, nullptr)) == nullptr)
{
break;
}
if (static_cast<Container::CQueue<EKeyEvent>*>(InterlockedCompareExchangePointer(reinterpret_cast<void**>(&m_pCurrentKeyEventQueue), nullptr, nullptr))->Enqueue(EKeyEvent::MouseLeft_Up) != Container::EQueueResult::Success)
{
break;
}
}
}
}
return 0UL;
}
You may have noticed that this keeps coming up and it deteriorates readability:
static_cast<Container::CQueue<EKeyEvent>*>(InterlockedCompareExchangePointer(reinterpret_cast<void**>(&m_pCurrentKeyEventQueue), nullptr, nullptr))
Q1. If there is a pointer type shared resource, is it right to use the return value of InterlockedCompareExchangePointer()
?
For ex, use = InterlockedCompareExchangePointer(&InterlockedPointer, nullptr, nullptr);
I thought so because directly accessing InterlockedCompareExchangePointer()
gives a C28112 warning. Many people told me that it is a false positive, though.
Q2. If I need to get a pointer type interlocked resource and do some operation with it, how can it be done safely and cleanly?
if (static_cast<Container::CQueue<EKeyEvent>*>(InterlockedCompareExchangePointer(reinterpret_cast<void**>(&m_pCurrentKeyEventQueue), nullptr, nullptr)) == nullptr)
{
break;
}
if (static_cast<Container::CQueue<EKeyEvent>*>(InterlockedCompareExchangePointer(reinterpret_cast<void**>(&m_pCurrentKeyEventQueue), nullptr, nullptr))->Enqueue(EKeyEvent::MouseLeft_Down) != Container::EQueueResult::Success)
{
break;
}
This doesn't seem safe, because if m_pCurrentKeyEventQueue
became nullptr
right after the null check, second static_cast<Container::CQueue<EKeyEvent>*>(InterlockedCompareExchangePointer(reinterpret_cast<void**>(&m_pCurrentKeyEventQueue), nullptr, nullptr))
will confront a nullptr
exception.