Module: wine Branch: master Commit: a9c215cee3fee0df604c993e5ced26d4d525c7c2 URL: http://source.winehq.org/git/wine.git/?a=commit;h=a9c215cee3fee0df604c993e5c...
Author: Piotr Caban piotr@codeweavers.com Date: Thu Jan 26 17:30:30 2017 +0100
msvcr100: Add reader_writer_lock implementation.
Signed-off-by: Piotr Caban piotr@codeweavers.com Signed-off-by: Alexandre Julliard julliard@winehq.org
---
dlls/msvcrt/lock.c | 177 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 170 insertions(+), 7 deletions(-)
diff --git a/dlls/msvcrt/lock.c b/dlls/msvcrt/lock.c index 0aa1f1f..8c1061a 100644 --- a/dlls/msvcrt/lock.c +++ b/dlls/msvcrt/lock.c @@ -746,7 +746,17 @@ typedef struct DEFINE_THISCALL_WRAPPER(reader_writer_lock_ctor, 4) reader_writer_lock* __thiscall reader_writer_lock_ctor(reader_writer_lock *this) { - FIXME("(%p) stub\n", this); + TRACE("(%p)\n", this); + + if (!keyed_event) { + HANDLE event; + + NtCreateKeyedEvent(&event, GENERIC_READ|GENERIC_WRITE, NULL, 0); + if (InterlockedCompareExchangePointer(&keyed_event, event, NULL) != NULL) + NtClose(event); + } + + memset(this, 0, sizeof(*this)); return this; }
@@ -755,7 +765,42 @@ reader_writer_lock* __thiscall reader_writer_lock_ctor(reader_writer_lock *this) DEFINE_THISCALL_WRAPPER(reader_writer_lock_dtor, 4) void __thiscall reader_writer_lock_dtor(reader_writer_lock *this) { - FIXME("(%p) stub\n", this); + TRACE("(%p)\n", this); + + if (this->thread_id != 0 || this->count) + WARN("destroying locked reader_writer_lock\n"); +} + +static inline void spin_wait_for_next_rwl(rwl_queue *q) +{ + SpinWait sw; + + if(q->next) return; + + SpinWait_ctor(&sw, &spin_wait_yield); + SpinWait__Reset(&sw); + while(!q->next) + SpinWait__SpinOnce(&sw); + SpinWait_dtor(&sw); +} + +/* Remove when proper InterlockedOr implementation is added to wine */ +static LONG InterlockedOr(LONG *d, LONG v) +{ + LONG l; + while (~(l = *d) & v) + if (InterlockedCompareExchange(d, l|v, l) == l) break; + return l; +} + +static LONG InterlockedAnd(LONG *d, LONG v) +{ + LONG l = *d, old; + while ((l & v) != l) { + if((old = InterlockedCompareExchange(d, l&v, l)) == l) break; + l = old; + } + return l; }
/* ?lock@reader_writer_lock@Concurrency@@QAEXXZ */ @@ -763,7 +808,30 @@ void __thiscall reader_writer_lock_dtor(reader_writer_lock *this) DEFINE_THISCALL_WRAPPER(reader_writer_lock_lock, 4) void __thiscall reader_writer_lock_lock(reader_writer_lock *this) { - FIXME("(%p) stub\n", this); + rwl_queue q = { NULL }, *last; + + TRACE("(%p)\n", this); + + if (this->thread_id == GetCurrentThreadId()) + FIXME("throw improper_lock exception\n"); + + last = InterlockedExchangePointer((void**)&this->writer_tail, &q); + if (last) { + last->next = &q; + NtWaitForKeyedEvent(keyed_event, &q, 0, NULL); + } else { + this->writer_head = &q; + if (InterlockedOr(&this->count, WRITER_WAITING)) + NtWaitForKeyedEvent(keyed_event, &q, 0, NULL); + } + + this->thread_id = GetCurrentThreadId(); + this->writer_head = &this->active; + this->active.next = NULL; + if (InterlockedCompareExchangePointer((void**)&this->writer_tail, &this->active, &q) != &q) { + spin_wait_for_next_rwl(&q); + this->active.next = q.next; + } }
/* ?lock_read@reader_writer_lock@Concurrency@@QAEXXZ */ @@ -771,7 +839,37 @@ void __thiscall reader_writer_lock_lock(reader_writer_lock *this) DEFINE_THISCALL_WRAPPER(reader_writer_lock_lock_read, 4) void __thiscall reader_writer_lock_lock_read(reader_writer_lock *this) { - FIXME("(%p) stub\n", this); + rwl_queue q; + + TRACE("(%p)\n", this); + + if (this->thread_id == GetCurrentThreadId()) + FIXME("throw improper_lock exception\n"); + + do { + q.next = this->reader_head; + } while(InterlockedCompareExchangePointer((void**)&this->reader_head, &q, q.next) != q.next); + + if (!q.next) { + rwl_queue *head; + LONG count; + + while (!((count = this->count) & WRITER_WAITING)) + if (InterlockedCompareExchange(&this->count, count+1, count) == count) break; + + if (count & WRITER_WAITING) + NtWaitForKeyedEvent(keyed_event, &q, 0, NULL); + + head = InterlockedExchangePointer((void**)&this->reader_head, NULL); + while(head && head != &q) { + rwl_queue *next = head->next; + InterlockedIncrement(&this->count); + NtReleaseKeyedEvent(keyed_event, head, 0, NULL); + head = next; + } + } else { + NtWaitForKeyedEvent(keyed_event, &q, 0, NULL); + } }
/* ?try_lock@reader_writer_lock@Concurrency@@QAE_NXZ */ @@ -779,7 +877,37 @@ void __thiscall reader_writer_lock_lock_read(reader_writer_lock *this) DEFINE_THISCALL_WRAPPER(reader_writer_lock_try_lock, 4) MSVCRT_bool __thiscall reader_writer_lock_try_lock(reader_writer_lock *this) { - FIXME("(%p) stub\n", this); + rwl_queue q = { NULL }; + + TRACE("(%p)\n", this); + + if (this->thread_id == GetCurrentThreadId()) + FIXME("throw improper_lock exception\n"); + + if (InterlockedCompareExchangePointer((void**)&this->writer_tail, &q, NULL)) + return FALSE; + this->writer_head = &q; + if (!InterlockedCompareExchange(&this->count, WRITER_WAITING, 0)) { + this->thread_id = GetCurrentThreadId(); + this->writer_head = &this->active; + this->active.next = NULL; + if (InterlockedCompareExchangePointer((void**)&this->writer_tail, &this->active, &q) != &q) { + spin_wait_for_next_rwl(&q); + this->active.next = q.next; + } + return TRUE; + } + + if (InterlockedCompareExchangePointer((void**)&this->writer_tail, NULL, &q) == &q) + return FALSE; + spin_wait_for_next_rwl(&q); + this->writer_head = q.next; + if (!InterlockedOr(&this->count, WRITER_WAITING)) { + this->thread_id = GetCurrentThreadId(); + this->writer_head = &this->active; + this->active.next = q.next; + return TRUE; + } return FALSE; }
@@ -788,7 +916,12 @@ MSVCRT_bool __thiscall reader_writer_lock_try_lock(reader_writer_lock *this) DEFINE_THISCALL_WRAPPER(reader_writer_lock_try_lock_read, 4) MSVCRT_bool __thiscall reader_writer_lock_try_lock_read(reader_writer_lock *this) { - FIXME("(%p) stub\n", this); + LONG count; + + TRACE("(%p)\n", this); + + while (!((count = this->count) & WRITER_WAITING)) + if (InterlockedCompareExchange(&this->count, count+1, count) == count) return TRUE; return FALSE; }
@@ -797,7 +930,37 @@ MSVCRT_bool __thiscall reader_writer_lock_try_lock_read(reader_writer_lock *this DEFINE_THISCALL_WRAPPER(reader_writer_lock_unlock, 4) void __thiscall reader_writer_lock_unlock(reader_writer_lock *this) { - FIXME("(%p) stub\n", this); + LONG count; + rwl_queue *head, *next; + + TRACE("(%p)\n", this); + + if ((count = this->count) & ~WRITER_WAITING) { + count = InterlockedDecrement(&this->count); + if (count != WRITER_WAITING) + return; + NtReleaseKeyedEvent(keyed_event, this->writer_head, 0, NULL); + return; + } + + this->thread_id = 0; + next = this->writer_head->next; + if (next) { + NtReleaseKeyedEvent(keyed_event, next, 0, NULL); + return; + } + InterlockedAnd(&this->count, ~WRITER_WAITING); + head = InterlockedExchangePointer((void**)&this->reader_head, NULL); + while (head) { + next = head->next; + InterlockedIncrement(&this->count); + NtReleaseKeyedEvent(keyed_event, head, 0, NULL); + head = next; + } + + if (InterlockedCompareExchangePointer((void**)&this->writer_tail, NULL, this->writer_head) == this->writer_head) + return; + InterlockedOr(&this->count, WRITER_WAITING); } #endif