Note that there are some explanatory texts on larger screens.

plurals
  1. POCause of race condition in producer/consumer stack
    primarykey
    data
    text
    <p>I've tried to create a producer-consumer stack, based on notification events, that would allow a single thread to push data, and another thread to pop data.</p> <p>When the buffer is full/empty, one thread waits for another until it is able to continue.</p> <p>I'm detecting a race condition (the program breaks where I have marked <code>***ERROR HERE***</code>) but I don't understand why it can happen.</p> <p>How can <code>size</code> go higher than <code>capacity</code> in this program?</p> <pre><code>#include &lt;process.h&gt; #include &lt;cstdlib&gt; #include &lt;vector&gt; #include &lt;windows.h&gt; template&lt;typename T, typename Ax = std::allocator&lt;T&gt; &gt; class rwstack { // It is assumed that only ONE thread will push data // and only ONE thread will pop data. public: typedef T value_type; typedef Ax allocator_type; typedef rwstack&lt;value_type, allocator_type&gt; this_type; typedef std::vector&lt;value_type, allocator_type&gt; container_type; private: allocator_type allocator; value_type *items; size_t volatile count; size_t const capacity; HANDLE hEventNotEmpty, hEventNotFull; rwstack(const this_type &amp;other) { __debugbreak(); /*Don't allow*/ } public: rwstack(const size_t capacity = 4096) : allocator(allocator_type()), items(allocator.allocate(capacity, NULL)), count(0), capacity(capacity), hEventNotEmpty(CreateEvent(NULL, TRUE, FALSE, NULL)), hEventNotFull(CreateEvent(NULL, TRUE, TRUE, NULL)) { } virtual ~rwstack() // Not actually used in the example { CloseHandle(hEventNotEmpty); CloseHandle(hEventNotFull); for (size_t i = 0; i &lt; count; i++) { allocator.destroy(&amp;items[InterlockedDecrementSizeT(&amp;count) - i]); } allocator.deallocate(items, capacity); } value_type &amp;push(const value_type &amp;value) { const ULONG waitResult = WaitForSingleObject(hEventNotFull, INFINITE); if (waitResult != WAIT_OBJECT_0) { __debugbreak(); } const size_t newSize = InterlockedIncrementSizeT(&amp;count); try { if (newSize &gt; capacity) { __debugbreak(); } // ****ERROR HERE**** if (newSize &gt;= capacity) { ResetEvent(hEventNotFull); } allocator.construct(&amp;items[newSize - 1], value); SetEvent(hEventNotEmpty); return items[newSize - 1]; } catch (...) { InterlockedDecrementSizeT(&amp;count); throw; } } void pop(value_type *pValue = NULL) { const ULONG waitResult = WaitForSingleObject(hEventNotEmpty, INFINITE); if (waitResult != WAIT_OBJECT_0) { __debugbreak(); } const size_t newSize = InterlockedDecrementSizeT(&amp;count); try { if (newSize &gt; capacity) { __debugbreak(); } // ****ERROR HERE**** if (newSize &lt;= 0) { ResetEvent(hEventNotEmpty); } if (pValue != NULL) { *pValue = items[newSize]; } allocator.destroy(&amp;items[newSize]); SetEvent(hEventNotFull); } catch (...) { InterlockedIncrementSizeT(&amp;count); throw; } } }; static size_t InterlockedIncrementSizeT(size_t volatile *p) { #if _M_X64 return InterlockedIncrement64(reinterpret_cast&lt;long long volatile *&gt;(p)); #elif _M_IX86 return InterlockedIncrement(reinterpret_cast&lt;long volatile *&gt;(p)); #endif } static size_t InterlockedDecrementSizeT(size_t volatile *p) { #if _M_X64 return InterlockedDecrement64(reinterpret_cast&lt;long long volatile *&gt;(p)); #elif _M_IX86 return InterlockedDecrement(reinterpret_cast&lt;long volatile *&gt;(p)); #endif } </code></pre> <p>Test code:</p> <pre><code>typedef rwstack&lt;int&gt; TTestStack; void __cdecl testPush(void *context) { TTestStack::value_type v; for (;;) static_cast&lt;TTestStack *&gt;(context)-&gt;pop(&amp;v); } void __cdecl testPop(void *context) { for (TTestStack::value_type v = 0; ; v++) static_cast&lt;TTestStack *&gt;(context)-&gt;push(v); } int main() { TTestStack rw; HANDLE hThreads[2] = { reinterpret_cast&lt;HANDLE&gt;(_beginthread(&amp;testPush, 0, &amp;rw)), reinterpret_cast&lt;HANDLE&gt;(_beginthread(&amp;testPop, 0, &amp;rw)), }; const ULONG nThreads = sizeof(hThreads) / sizeof(*hThreads) WaitForMultipleObjects(nThreads, hThreads, TRUE, INFINITE); return 0; } </code></pre>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload