Piotr Caban : msvcr100: Add reader_writer_lock implementation.
Alexandre Julliard
julliard at winehq.org
Thu Jan 26 14:53:15 CST 2017
Module: wine
Branch: master
Commit: a9c215cee3fee0df604c993e5ced26d4d525c7c2
URL: http://source.winehq.org/git/wine.git/?a=commit;h=a9c215cee3fee0df604c993e5ced26d4d525c7c2
Author: Piotr Caban <piotr at codeweavers.com>
Date: Thu Jan 26 17:30:30 2017 +0100
msvcr100: Add reader_writer_lock implementation.
Signed-off-by: Piotr Caban <piotr at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at 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 at 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 at 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 at 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
More information about the wine-cvs
mailing list