Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Old question, but this is something that should work. It doesn't spin on contention. Readers incur limited extra cost if they have little or no contention, because <code>SetEvent</code> is called lazily (look at the edit history for a more heavyweight version that doesn't have this optimization).</p> <pre><code>#include &lt;windows.h&gt; typedef struct _RW_LOCK { CRITICAL_SECTION countsLock; CRITICAL_SECTION writerLock; HANDLE noReaders; int readerCount; BOOL waitingWriter; } RW_LOCK, *PRW_LOCK; void rwlock_init(PRW_LOCK rwlock) { InitializeCriticalSection(&amp;rwlock-&gt;writerLock); InitializeCriticalSection(&amp;rwlock-&gt;countsLock); /* * Could use a semaphore as well. There can only be one waiter ever, * so I'm showing an auto-reset event here. */ rwlock-&gt;noReaders = CreateEvent (NULL, FALSE, FALSE, NULL); } void rwlock_rdlock(PRW_LOCK rwlock) { /* * We need to lock the writerLock too, otherwise a writer could * do the whole of rwlock_wrlock after the readerCount changed * from 0 to 1, but before the event was reset. */ EnterCriticalSection(&amp;rwlock-&gt;writerLock); EnterCriticalSection(&amp;rwlock-&gt;countsLock); ++rwlock-&gt;readerCount; LeaveCriticalSection(&amp;rwlock-&gt;countsLock); LeaveCriticalSection(&amp;rwlock-&gt;writerLock); } int rwlock_wrlock(PRW_LOCK rwlock) { EnterCriticalSection(&amp;rwlock-&gt;writerLock); /* * readerCount cannot become non-zero within the writerLock CS, * but it can become zero... */ if (rwlock-&gt;readerCount &gt; 0) { EnterCriticalSection(&amp;rwlock-&gt;countsLock); /* ... so test it again. */ if (rwlock-&gt;readerCount &gt; 0) { rwlock-&gt;waitingWriter = TRUE; LeaveCriticalSection(&amp;rwlock-&gt;countsLock); WaitForSingleObject(rwlock-&gt;noReaders, INFINITE); } else { /* How lucky, no need to wait. */ LeaveCriticalSection(&amp;rwlock-&gt;countsLock); } } /* writerLock remains locked. */ } void rwlock_rdunlock(PRW_LOCK rwlock) { EnterCriticalSection(&amp;rwlock-&gt;countsLock); assert (rwlock-&gt;readerCount &gt; 0); if (--rwlock-&gt;readerCount == 0) { if (rwlock-&gt;waitingWriter) { /* * Clear waitingWriter here to avoid taking countsLock * again in wrlock. */ rwlock-&gt;waitingWriter = FALSE; SetEvent(rwlock-&gt;noReaders); } } LeaveCriticalSection(&amp;rwlock-&gt;countsLock); } void rwlock_wrunlock(PRW_LOCK rwlock) { LeaveCriticalSection(&amp;rwlock-&gt;writerLock); } </code></pre> <p>You could decrease the cost for readers by using a single <code>CRITICAL_SECTION</code>:</p> <ul> <li><p><code>countsLock</code> is replaced with <code>writerLock</code> in rdlock and rdunlock</p></li> <li><p><code>rwlock-&gt;waitingWriter = FALSE</code> is removed in wrunlock</p></li> <li><p>wrlock's body is changed to</p> <pre><code>EnterCriticalSection(&amp;rwlock-&gt;writerLock); rwlock-&gt;waitingWriter = TRUE; while (rwlock-&gt;readerCount &gt; 0) { LeaveCriticalSection(&amp;rwlock-&gt;writerLock); WaitForSingleObject(rwlock-&gt;noReaders, INFINITE); EnterCriticalSection(&amp;rwlock-&gt;writerLock); } rwlock-&gt;waitingWriter = FALSE; /* writerLock remains locked. */ </code></pre></li> </ul> <p>However this loses in fairness, so I prefer the above solution.</p>
    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.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      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