diff --git a/dlls/msvcrt/concurrency.c b/dlls/msvcrt/concurrency.c index ba3dc6be4ad..142d578633e 100644 --- a/dlls/msvcrt/concurrency.c +++ b/dlls/msvcrt/concurrency.c @@ -143,6 +143,159 @@ typedef struct { char empty; } _CurrentScheduler; +typedef enum +{ + SPINWAIT_INIT, + SPINWAIT_SPIN, + SPINWAIT_YIELD, + SPINWAIT_DONE +} SpinWait_state; + +typedef void (__cdecl *yield_func)(void); + +typedef struct +{ + ULONG spin; + ULONG unknown; + SpinWait_state state; + yield_func yield_func; +} SpinWait; + +/* keep in sync with msvcp90/msvcp90.h */ +typedef struct cs_queue +{ + struct cs_queue *next; +#if _MSVCR_VER >= 110 + BOOL free; + int unknown; +#endif +} cs_queue; + +typedef struct +{ + ULONG_PTR unk_thread_id; + cs_queue unk_active; +#if _MSVCR_VER >= 110 + void *unknown[2]; +#else + void *unknown[1]; +#endif + cs_queue *head; + void *tail; +} critical_section; + +typedef struct +{ + critical_section *cs; + union { + cs_queue q; + struct { + void *unknown[4]; + int unknown2[2]; + } unknown; + } lock; +} critical_section_scoped_lock; + +typedef struct +{ + critical_section cs; +} _NonReentrantPPLLock; + +typedef struct +{ + _NonReentrantPPLLock *lock; + union { + cs_queue q; + struct { + void *unknown[4]; + int unknown2[2]; + } unknown; + } wait; +} _NonReentrantPPLLock__Scoped_lock; + +typedef struct +{ + critical_section cs; + LONG count; + LONG owner; +} _ReentrantPPLLock; + +typedef struct +{ + _ReentrantPPLLock *lock; + union { + cs_queue q; + struct { + void *unknown[4]; + int unknown2[2]; + } unknown; + } wait; +} _ReentrantPPLLock__Scoped_lock; + +#define EVT_RUNNING (void*)1 +#define EVT_WAITING NULL + +struct thread_wait; +typedef struct thread_wait_entry +{ + struct thread_wait *wait; + struct thread_wait_entry *next; + struct thread_wait_entry *prev; +} thread_wait_entry; + +typedef struct thread_wait +{ + void *signaled; + int pending_waits; + thread_wait_entry entries[1]; +} thread_wait; + +typedef struct +{ + thread_wait_entry *waiters; + INT_PTR signaled; + critical_section cs; +} event; + +#if _MSVCR_VER >= 110 +typedef struct cv_queue { + struct cv_queue *next; + BOOL expired; +} cv_queue; + +typedef struct { + /* cv_queue structure is not binary compatible */ + cv_queue *queue; + critical_section lock; +} _Condition_variable; +#endif + +typedef struct rwl_queue +{ + struct rwl_queue *next; +} rwl_queue; + +#define WRITER_WAITING 0x80000000 +/* FIXME: reader_writer_lock structure is not binary compatible + * it can't exceed 28/56 bytes */ +typedef struct +{ + LONG count; + LONG thread_id; + rwl_queue active; + rwl_queue *writer_head; + rwl_queue *writer_tail; + rwl_queue *reader_head; +} reader_writer_lock; + +typedef struct { + reader_writer_lock *lock; +} reader_writer_lock_scoped_lock; + +typedef struct { + CRITICAL_SECTION cs; +} _ReentrantBlockingLock; + static int context_tls_index = TLS_OUT_OF_INDEXES; static CRITICAL_SECTION default_scheduler_cs; @@ -156,6 +309,8 @@ static CRITICAL_SECTION default_scheduler_cs = { &default_scheduler_cs_debug, -1 static SchedulerPolicy default_scheduler_policy; static ThreadScheduler *default_scheduler; +static HANDLE keyed_event; + static void create_default_scheduler(void); static Context* try_get_current_context(void) @@ -1070,6 +1225,1155 @@ void __cdecl _CurrentScheduler__ScheduleTask(void (__cdecl *proc)(void*), void * CurrentScheduler_ScheduleTask(proc, data); } +/* ?_Value@_SpinCount@details@Concurrency@@SAIXZ */ +unsigned int __cdecl SpinCount__Value(void) +{ + static unsigned int val = -1; + + TRACE("()\n"); + + if(val == -1) { + SYSTEM_INFO si; + + GetSystemInfo(&si); + val = si.dwNumberOfProcessors>1 ? 4000 : 0; + } + + return val; +} + +/* ??0?$_SpinWait@$00@details@Concurrency@@QAE@P6AXXZ@Z */ +/* ??0?$_SpinWait@$00@details@Concurrency@@QEAA@P6AXXZ@Z */ +DEFINE_THISCALL_WRAPPER(SpinWait_ctor_yield, 8) +SpinWait* __thiscall SpinWait_ctor_yield(SpinWait *this, yield_func yf) +{ + TRACE("(%p %p)\n", this, yf); + + this->state = SPINWAIT_INIT; + this->unknown = 1; + this->yield_func = yf; + return this; +} + +/* ??0?$_SpinWait@$0A@@details@Concurrency@@QAE@P6AXXZ@Z */ +/* ??0?$_SpinWait@$0A@@details@Concurrency@@QEAA@P6AXXZ@Z */ +DEFINE_THISCALL_WRAPPER(SpinWait_ctor, 8) +SpinWait* __thiscall SpinWait_ctor(SpinWait *this, yield_func yf) +{ + TRACE("(%p %p)\n", this, yf); + + this->state = SPINWAIT_INIT; + this->unknown = 0; + this->yield_func = yf; + return this; +} + +/* ??_F?$_SpinWait@$00@details@Concurrency@@QAEXXZ */ +/* ??_F?$_SpinWait@$00@details@Concurrency@@QEAAXXZ */ +/* ??_F?$_SpinWait@$0A@@details@Concurrency@@QAEXXZ */ +/* ??_F?$_SpinWait@$0A@@details@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(SpinWait_dtor, 4) +void __thiscall SpinWait_dtor(SpinWait *this) +{ + TRACE("(%p)\n", this); +} + +/* ?_DoYield@?$_SpinWait@$00@details@Concurrency@@IAEXXZ */ +/* ?_DoYield@?$_SpinWait@$00@details@Concurrency@@IEAAXXZ */ +/* ?_DoYield@?$_SpinWait@$0A@@details@Concurrency@@IAEXXZ */ +/* ?_DoYield@?$_SpinWait@$0A@@details@Concurrency@@IEAAXXZ */ +DEFINE_THISCALL_WRAPPER(SpinWait__DoYield, 4) +void __thiscall SpinWait__DoYield(SpinWait *this) +{ + TRACE("(%p)\n", this); + + if(this->unknown) + this->yield_func(); +} + +/* ?_NumberOfSpins@?$_SpinWait@$00@details@Concurrency@@IAEKXZ */ +/* ?_NumberOfSpins@?$_SpinWait@$00@details@Concurrency@@IEAAKXZ */ +/* ?_NumberOfSpins@?$_SpinWait@$0A@@details@Concurrency@@IAEKXZ */ +/* ?_NumberOfSpins@?$_SpinWait@$0A@@details@Concurrency@@IEAAKXZ */ +DEFINE_THISCALL_WRAPPER(SpinWait__NumberOfSpins, 4) +ULONG __thiscall SpinWait__NumberOfSpins(SpinWait *this) +{ + TRACE("(%p)\n", this); + return 1; +} + +/* ?_SetSpinCount@?$_SpinWait@$00@details@Concurrency@@QAEXI@Z */ +/* ?_SetSpinCount@?$_SpinWait@$00@details@Concurrency@@QEAAXI@Z */ +/* ?_SetSpinCount@?$_SpinWait@$0A@@details@Concurrency@@QAEXI@Z */ +/* ?_SetSpinCount@?$_SpinWait@$0A@@details@Concurrency@@QEAAXI@Z */ +DEFINE_THISCALL_WRAPPER(SpinWait__SetSpinCount, 8) +void __thiscall SpinWait__SetSpinCount(SpinWait *this, unsigned int spin) +{ + TRACE("(%p %d)\n", this, spin); + + this->spin = spin; + this->state = spin ? SPINWAIT_SPIN : SPINWAIT_YIELD; +} + +/* ?_Reset@?$_SpinWait@$00@details@Concurrency@@IAEXXZ */ +/* ?_Reset@?$_SpinWait@$00@details@Concurrency@@IEAAXXZ */ +/* ?_Reset@?$_SpinWait@$0A@@details@Concurrency@@IAEXXZ */ +/* ?_Reset@?$_SpinWait@$0A@@details@Concurrency@@IEAAXXZ */ +DEFINE_THISCALL_WRAPPER(SpinWait__Reset, 4) +void __thiscall SpinWait__Reset(SpinWait *this) +{ + SpinWait__SetSpinCount(this, SpinCount__Value()); +} + +/* ?_ShouldSpinAgain@?$_SpinWait@$00@details@Concurrency@@IAE_NXZ */ +/* ?_ShouldSpinAgain@?$_SpinWait@$00@details@Concurrency@@IEAA_NXZ */ +/* ?_ShouldSpinAgain@?$_SpinWait@$0A@@details@Concurrency@@IAE_NXZ */ +/* ?_ShouldSpinAgain@?$_SpinWait@$0A@@details@Concurrency@@IEAA_NXZ */ +DEFINE_THISCALL_WRAPPER(SpinWait__ShouldSpinAgain, 4) +bool __thiscall SpinWait__ShouldSpinAgain(SpinWait *this) +{ + TRACE("(%p)\n", this); + + this->spin--; + return this->spin > 0; +} + +/* ?_SpinOnce@?$_SpinWait@$00@details@Concurrency@@QAE_NXZ */ +/* ?_SpinOnce@?$_SpinWait@$00@details@Concurrency@@QEAA_NXZ */ +/* ?_SpinOnce@?$_SpinWait@$0A@@details@Concurrency@@QAE_NXZ */ +/* ?_SpinOnce@?$_SpinWait@$0A@@details@Concurrency@@QEAA_NXZ */ +DEFINE_THISCALL_WRAPPER(SpinWait__SpinOnce, 4) +bool __thiscall SpinWait__SpinOnce(SpinWait *this) +{ + switch(this->state) { + case SPINWAIT_INIT: + SpinWait__Reset(this); + /* fall through */ + case SPINWAIT_SPIN: + InterlockedDecrement((LONG*)&this->spin); + if(!this->spin) + this->state = this->unknown ? SPINWAIT_YIELD : SPINWAIT_DONE; + return TRUE; + case SPINWAIT_YIELD: + this->state = SPINWAIT_DONE; + this->yield_func(); + return TRUE; + default: + SpinWait__Reset(this); + return FALSE; + } +} + +/* ??0critical_section@Concurrency@@QAE@XZ */ +/* ??0critical_section@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(critical_section_ctor, 4) +critical_section* __thiscall critical_section_ctor(critical_section *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); + } + + this->unk_thread_id = 0; + this->head = this->tail = NULL; + return this; +} + +/* ??1critical_section@Concurrency@@QAE@XZ */ +/* ??1critical_section@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(critical_section_dtor, 4) +void __thiscall critical_section_dtor(critical_section *this) +{ + TRACE("(%p)\n", this); +} + +static void __cdecl spin_wait_yield(void) +{ + Sleep(0); +} + +static inline void spin_wait_for_next_cs(cs_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); +} + +static inline void cs_set_head(critical_section *cs, cs_queue *q) +{ + cs->unk_thread_id = GetCurrentThreadId(); + cs->unk_active.next = q->next; + cs->head = &cs->unk_active; +} + +static inline void cs_lock(critical_section *cs, cs_queue *q) +{ + cs_queue *last; + + if(cs->unk_thread_id == GetCurrentThreadId()) + throw_exception(EXCEPTION_IMPROPER_LOCK, 0, "Already locked"); + + memset(q, 0, sizeof(*q)); + last = InterlockedExchangePointer(&cs->tail, q); + if(last) { + last->next = q; + NtWaitForKeyedEvent(keyed_event, q, 0, NULL); + } + + cs_set_head(cs, q); + if(InterlockedCompareExchangePointer(&cs->tail, &cs->unk_active, q) != q) { + spin_wait_for_next_cs(q); + cs->unk_active.next = q->next; + } +} + +/* ?lock@critical_section@Concurrency@@QAEXXZ */ +/* ?lock@critical_section@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(critical_section_lock, 4) +void __thiscall critical_section_lock(critical_section *this) +{ + cs_queue q; + + TRACE("(%p)\n", this); + cs_lock(this, &q); +} + +/* ?try_lock@critical_section@Concurrency@@QAE_NXZ */ +/* ?try_lock@critical_section@Concurrency@@QEAA_NXZ */ +DEFINE_THISCALL_WRAPPER(critical_section_try_lock, 4) +bool __thiscall critical_section_try_lock(critical_section *this) +{ + cs_queue q; + + TRACE("(%p)\n", this); + + if(this->unk_thread_id == GetCurrentThreadId()) + return FALSE; + + memset(&q, 0, sizeof(q)); + if(!InterlockedCompareExchangePointer(&this->tail, &q, NULL)) { + cs_set_head(this, &q); + if(InterlockedCompareExchangePointer(&this->tail, &this->unk_active, &q) != &q) { + spin_wait_for_next_cs(&q); + this->unk_active.next = q.next; + } + return TRUE; + } + return FALSE; +} + +/* ?unlock@critical_section@Concurrency@@QAEXXZ */ +/* ?unlock@critical_section@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(critical_section_unlock, 4) +void __thiscall critical_section_unlock(critical_section *this) +{ + TRACE("(%p)\n", this); + + this->unk_thread_id = 0; + this->head = NULL; + if(InterlockedCompareExchangePointer(&this->tail, NULL, &this->unk_active) + == &this->unk_active) return; + spin_wait_for_next_cs(&this->unk_active); + +#if _MSVCR_VER >= 110 + while(1) { + cs_queue *next; + + if(!InterlockedExchange(&this->unk_active.next->free, TRUE)) + break; + + next = this->unk_active.next; + if(InterlockedCompareExchangePointer(&this->tail, NULL, next) == next) { + HeapFree(GetProcessHeap(), 0, next); + return; + } + spin_wait_for_next_cs(next); + + this->unk_active.next = next->next; + HeapFree(GetProcessHeap(), 0, next); + } +#endif + + NtReleaseKeyedEvent(keyed_event, this->unk_active.next, 0, NULL); +} + +/* ?native_handle@critical_section@Concurrency@@QAEAAV12@XZ */ +/* ?native_handle@critical_section@Concurrency@@QEAAAEAV12@XZ */ +DEFINE_THISCALL_WRAPPER(critical_section_native_handle, 4) +critical_section* __thiscall critical_section_native_handle(critical_section *this) +{ + TRACE("(%p)\n", this); + return this; +} + +#if _MSVCR_VER >= 110 +/* ?try_lock_for@critical_section@Concurrency@@QAE_NI@Z */ +/* ?try_lock_for@critical_section@Concurrency@@QEAA_NI@Z */ +DEFINE_THISCALL_WRAPPER(critical_section_try_lock_for, 8) +bool __thiscall critical_section_try_lock_for( + critical_section *this, unsigned int timeout) +{ + cs_queue *q, *last; + + TRACE("(%p %d)\n", this, timeout); + + if(this->unk_thread_id == GetCurrentThreadId()) + throw_exception(EXCEPTION_IMPROPER_LOCK, 0, "Already locked"); + + if(!(q = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*q)))) + return critical_section_try_lock(this); + + last = InterlockedExchangePointer(&this->tail, q); + if(last) { + LARGE_INTEGER to; + NTSTATUS status; + FILETIME ft; + + last->next = q; + GetSystemTimeAsFileTime(&ft); + to.QuadPart = ((LONGLONG)ft.dwHighDateTime<<32) + + ft.dwLowDateTime + (LONGLONG)timeout*10000; + status = NtWaitForKeyedEvent(keyed_event, q, 0, &to); + if(status == STATUS_TIMEOUT) { + if(!InterlockedExchange(&q->free, TRUE)) + return FALSE; + /* A thread has signaled the event and is block waiting. */ + /* We need to catch the event to wake the thread. */ + NtWaitForKeyedEvent(keyed_event, q, 0, NULL); + } + } + + cs_set_head(this, q); + if(InterlockedCompareExchangePointer(&this->tail, &this->unk_active, q) != q) { + spin_wait_for_next_cs(q); + this->unk_active.next = q->next; + } + + HeapFree(GetProcessHeap(), 0, q); + return TRUE; +} +#endif + +/* ??0scoped_lock@critical_section@Concurrency@@QAE@AAV12@@Z */ +/* ??0scoped_lock@critical_section@Concurrency@@QEAA@AEAV12@@Z */ +DEFINE_THISCALL_WRAPPER(critical_section_scoped_lock_ctor, 8) +critical_section_scoped_lock* __thiscall critical_section_scoped_lock_ctor( + critical_section_scoped_lock *this, critical_section *cs) +{ + TRACE("(%p %p)\n", this, cs); + this->cs = cs; + cs_lock(this->cs, &this->lock.q); + return this; +} + +/* ??1scoped_lock@critical_section@Concurrency@@QAE@XZ */ +/* ??1scoped_lock@critical_section@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(critical_section_scoped_lock_dtor, 4) +void __thiscall critical_section_scoped_lock_dtor(critical_section_scoped_lock *this) +{ + TRACE("(%p)\n", this); + critical_section_unlock(this->cs); +} + +/* ??0_NonReentrantPPLLock@details@Concurrency@@QAE@XZ */ +/* ??0_NonReentrantPPLLock@details@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(_NonReentrantPPLLock_ctor, 4) +_NonReentrantPPLLock* __thiscall _NonReentrantPPLLock_ctor(_NonReentrantPPLLock *this) +{ + TRACE("(%p)\n", this); + + critical_section_ctor(&this->cs); + return this; +} + +/* ?_Acquire@_NonReentrantPPLLock@details@Concurrency@@QAEXPAX@Z */ +/* ?_Acquire@_NonReentrantPPLLock@details@Concurrency@@QEAAXPEAX@Z */ +DEFINE_THISCALL_WRAPPER(_NonReentrantPPLLock__Acquire, 8) +void __thiscall _NonReentrantPPLLock__Acquire(_NonReentrantPPLLock *this, cs_queue *q) +{ + TRACE("(%p %p)\n", this, q); + cs_lock(&this->cs, q); +} + +/* ?_Release@_NonReentrantPPLLock@details@Concurrency@@QAEXXZ */ +/* ?_Release@_NonReentrantPPLLock@details@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(_NonReentrantPPLLock__Release, 4) +void __thiscall _NonReentrantPPLLock__Release(_NonReentrantPPLLock *this) +{ + TRACE("(%p)\n", this); + critical_section_unlock(&this->cs); +} + +/* ??0_Scoped_lock@_NonReentrantPPLLock@details@Concurrency@@QAE@AAV123@@Z */ +/* ??0_Scoped_lock@_NonReentrantPPLLock@details@Concurrency@@QEAA@AEAV123@@Z */ +DEFINE_THISCALL_WRAPPER(_NonReentrantPPLLock__Scoped_lock_ctor, 8) +_NonReentrantPPLLock__Scoped_lock* __thiscall _NonReentrantPPLLock__Scoped_lock_ctor( + _NonReentrantPPLLock__Scoped_lock *this, _NonReentrantPPLLock *lock) +{ + TRACE("(%p %p)\n", this, lock); + + this->lock = lock; + _NonReentrantPPLLock__Acquire(this->lock, &this->wait.q); + return this; +} + +/* ??1_Scoped_lock@_NonReentrantPPLLock@details@Concurrency@@QAE@XZ */ +/* ??1_Scoped_lock@_NonReentrantPPLLock@details@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(_NonReentrantPPLLock__Scoped_lock_dtor, 4) +void __thiscall _NonReentrantPPLLock__Scoped_lock_dtor(_NonReentrantPPLLock__Scoped_lock *this) +{ + TRACE("(%p)\n", this); + + _NonReentrantPPLLock__Release(this->lock); +} + +/* ??0_ReentrantPPLLock@details@Concurrency@@QAE@XZ */ +/* ??0_ReentrantPPLLock@details@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(_ReentrantPPLLock_ctor, 4) +_ReentrantPPLLock* __thiscall _ReentrantPPLLock_ctor(_ReentrantPPLLock *this) +{ + TRACE("(%p)\n", this); + + critical_section_ctor(&this->cs); + this->count = 0; + this->owner = -1; + return this; +} + +/* ?_Acquire@_ReentrantPPLLock@details@Concurrency@@QAEXPAX@Z */ +/* ?_Acquire@_ReentrantPPLLock@details@Concurrency@@QEAAXPEAX@Z */ +DEFINE_THISCALL_WRAPPER(_ReentrantPPLLock__Acquire, 8) +void __thiscall _ReentrantPPLLock__Acquire(_ReentrantPPLLock *this, cs_queue *q) +{ + TRACE("(%p %p)\n", this, q); + + if(this->owner == GetCurrentThreadId()) { + this->count++; + return; + } + + cs_lock(&this->cs, q); + this->count++; + this->owner = GetCurrentThreadId(); +} + +/* ?_Release@_ReentrantPPLLock@details@Concurrency@@QAEXXZ */ +/* ?_Release@_ReentrantPPLLock@details@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(_ReentrantPPLLock__Release, 4) +void __thiscall _ReentrantPPLLock__Release(_ReentrantPPLLock *this) +{ + TRACE("(%p)\n", this); + + this->count--; + if(this->count) + return; + + this->owner = -1; + critical_section_unlock(&this->cs); +} + +/* ??0_Scoped_lock@_ReentrantPPLLock@details@Concurrency@@QAE@AAV123@@Z */ +/* ??0_Scoped_lock@_ReentrantPPLLock@details@Concurrency@@QEAA@AEAV123@@Z */ +DEFINE_THISCALL_WRAPPER(_ReentrantPPLLock__Scoped_lock_ctor, 8) +_ReentrantPPLLock__Scoped_lock* __thiscall _ReentrantPPLLock__Scoped_lock_ctor( + _ReentrantPPLLock__Scoped_lock *this, _ReentrantPPLLock *lock) +{ + TRACE("(%p %p)\n", this, lock); + + this->lock = lock; + _ReentrantPPLLock__Acquire(this->lock, &this->wait.q); + return this; +} + +/* ??1_Scoped_lock@_ReentrantPPLLock@details@Concurrency@@QAE@XZ */ +/* ??1_Scoped_lock@_ReentrantPPLLock@details@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(_ReentrantPPLLock__Scoped_lock_dtor, 4) +void __thiscall _ReentrantPPLLock__Scoped_lock_dtor(_ReentrantPPLLock__Scoped_lock *this) +{ + TRACE("(%p)\n", this); + + _ReentrantPPLLock__Release(this->lock); +} + +/* ?_GetConcurrency@details@Concurrency@@YAIXZ */ +unsigned int __cdecl _GetConcurrency(void) +{ + static unsigned int val = -1; + + TRACE("()\n"); + + if(val == -1) { + SYSTEM_INFO si; + + GetSystemInfo(&si); + val = si.dwNumberOfProcessors; + } + + return val; +} + +static inline PLARGE_INTEGER evt_timeout(PLARGE_INTEGER pTime, unsigned int timeout) +{ + if(timeout == COOPERATIVE_TIMEOUT_INFINITE) return NULL; + pTime->QuadPart = (ULONGLONG)timeout * -10000; + return pTime; +} + +static void evt_add_queue(thread_wait_entry **head, thread_wait_entry *entry) +{ + entry->next = *head; + entry->prev = NULL; + if(*head) (*head)->prev = entry; + *head = entry; +} + +static void evt_remove_queue(thread_wait_entry **head, thread_wait_entry *entry) +{ + if(entry == *head) + *head = entry->next; + else if(entry->prev) + entry->prev->next = entry->next; + if(entry->next) entry->next->prev = entry->prev; +} + +static size_t evt_end_wait(thread_wait *wait, event **events, int count) +{ + size_t i, ret = COOPERATIVE_WAIT_TIMEOUT; + + for(i = 0; i < count; i++) { + critical_section_lock(&events[i]->cs); + if(events[i] == wait->signaled) ret = i; + evt_remove_queue(&events[i]->waiters, &wait->entries[i]); + critical_section_unlock(&events[i]->cs); + } + + return ret; +} + +static inline int evt_transition(void **state, void *from, void *to) +{ + return InterlockedCompareExchangePointer(state, to, from) == from; +} + +static size_t evt_wait(thread_wait *wait, event **events, int count, bool wait_all, unsigned int timeout) +{ + int i; + NTSTATUS status; + LARGE_INTEGER ntto; + + wait->signaled = EVT_RUNNING; + wait->pending_waits = wait_all ? count : 1; + for(i = 0; i < count; i++) { + wait->entries[i].wait = wait; + + critical_section_lock(&events[i]->cs); + evt_add_queue(&events[i]->waiters, &wait->entries[i]); + if(events[i]->signaled) { + if(!InterlockedDecrement(&wait->pending_waits)) { + wait->signaled = events[i]; + critical_section_unlock(&events[i]->cs); + + return evt_end_wait(wait, events, i+1); + } + } + critical_section_unlock(&events[i]->cs); + } + + if(!timeout) + return evt_end_wait(wait, events, count); + + if(!evt_transition(&wait->signaled, EVT_RUNNING, EVT_WAITING)) + return evt_end_wait(wait, events, count); + + status = NtWaitForKeyedEvent(keyed_event, wait, 0, evt_timeout(&ntto, timeout)); + + if(status && !evt_transition(&wait->signaled, EVT_WAITING, EVT_RUNNING)) + NtWaitForKeyedEvent(keyed_event, wait, 0, NULL); + + return evt_end_wait(wait, events, count); +} + +/* ??0event@Concurrency@@QAE@XZ */ +/* ??0event@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(event_ctor, 4) +event* __thiscall event_ctor(event *this) +{ + TRACE("(%p)\n", this); + + this->waiters = NULL; + this->signaled = FALSE; + critical_section_ctor(&this->cs); + + return this; +} + +/* ??1event@Concurrency@@QAE@XZ */ +/* ??1event@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(event_dtor, 4) +void __thiscall event_dtor(event *this) +{ + TRACE("(%p)\n", this); + critical_section_dtor(&this->cs); + if(this->waiters) + ERR("there's a wait on destroyed event\n"); +} + +/* ?reset@event@Concurrency@@QAEXXZ */ +/* ?reset@event@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(event_reset, 4) +void __thiscall event_reset(event *this) +{ + thread_wait_entry *entry; + + TRACE("(%p)\n", this); + + critical_section_lock(&this->cs); + if(this->signaled) { + this->signaled = FALSE; + for(entry=this->waiters; entry; entry = entry->next) + InterlockedIncrement(&entry->wait->pending_waits); + } + critical_section_unlock(&this->cs); +} + +/* ?set@event@Concurrency@@QAEXXZ */ +/* ?set@event@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(event_set, 4) +void __thiscall event_set(event *this) +{ + thread_wait_entry *wakeup = NULL; + thread_wait_entry *entry, *next; + + TRACE("(%p)\n", this); + + critical_section_lock(&this->cs); + if(!this->signaled) { + this->signaled = TRUE; + for(entry=this->waiters; entry; entry=next) { + next = entry->next; + if(!InterlockedDecrement(&entry->wait->pending_waits)) { + if(InterlockedExchangePointer(&entry->wait->signaled, this) == EVT_WAITING) { + evt_remove_queue(&this->waiters, entry); + evt_add_queue(&wakeup, entry); + } + } + } + } + critical_section_unlock(&this->cs); + + for(entry=wakeup; entry; entry=next) { + next = entry->next; + entry->next = entry->prev = NULL; + NtReleaseKeyedEvent(keyed_event, entry->wait, 0, NULL); + } +} + +/* ?wait@event@Concurrency@@QAEII@Z */ +/* ?wait@event@Concurrency@@QEAA_KI@Z */ +DEFINE_THISCALL_WRAPPER(event_wait, 8) +size_t __thiscall event_wait(event *this, unsigned int timeout) +{ + thread_wait wait; + size_t signaled; + + TRACE("(%p %u)\n", this, timeout); + + critical_section_lock(&this->cs); + signaled = this->signaled; + critical_section_unlock(&this->cs); + + if(!timeout) return signaled ? 0 : COOPERATIVE_WAIT_TIMEOUT; + return signaled ? 0 : evt_wait(&wait, &this, 1, FALSE, timeout); +} + +/* ?wait_for_multiple@event@Concurrency@@SAIPAPAV12@I_NI@Z */ +/* ?wait_for_multiple@event@Concurrency@@SA_KPEAPEAV12@_K_NI@Z */ +int __cdecl event_wait_for_multiple(event **events, size_t count, bool wait_all, unsigned int timeout) +{ + thread_wait *wait; + size_t ret; + + TRACE("(%p %Iu %d %u)\n", events, count, wait_all, timeout); + + if(count == 0) + return 0; + + wait = operator_new(FIELD_OFFSET(thread_wait, entries[count])); + ret = evt_wait(wait, events, count, wait_all, timeout); + operator_delete(wait); + + return ret; +} + +#if _MSVCR_VER >= 110 + +/* ??0_Condition_variable@details@Concurrency@@QAE@XZ */ +/* ??0_Condition_variable@details@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(_Condition_variable_ctor, 4) +_Condition_variable* __thiscall _Condition_variable_ctor(_Condition_variable *this) +{ + TRACE("(%p)\n", this); + + this->queue = NULL; + critical_section_ctor(&this->lock); + return this; +} + +/* ??1_Condition_variable@details@Concurrency@@QAE@XZ */ +/* ??1_Condition_variable@details@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(_Condition_variable_dtor, 4) +void __thiscall _Condition_variable_dtor(_Condition_variable *this) +{ + TRACE("(%p)\n", this); + + while(this->queue) { + cv_queue *next = this->queue->next; + if(!this->queue->expired) + ERR("there's an active wait\n"); + HeapFree(GetProcessHeap(), 0, this->queue); + this->queue = next; + } + critical_section_dtor(&this->lock); +} + +/* ?wait@_Condition_variable@details@Concurrency@@QAEXAAVcritical_section@3@@Z */ +/* ?wait@_Condition_variable@details@Concurrency@@QEAAXAEAVcritical_section@3@@Z */ +DEFINE_THISCALL_WRAPPER(_Condition_variable_wait, 8) +void __thiscall _Condition_variable_wait(_Condition_variable *this, critical_section *cs) +{ + cv_queue q; + + TRACE("(%p, %p)\n", this, cs); + + critical_section_lock(&this->lock); + q.next = this->queue; + q.expired = FALSE; + this->queue = &q; + critical_section_unlock(&this->lock); + + critical_section_unlock(cs); + NtWaitForKeyedEvent(keyed_event, &q, 0, NULL); + critical_section_lock(cs); +} + +/* ?wait_for@_Condition_variable@details@Concurrency@@QAE_NAAVcritical_section@3@I@Z */ +/* ?wait_for@_Condition_variable@details@Concurrency@@QEAA_NAEAVcritical_section@3@I@Z */ +DEFINE_THISCALL_WRAPPER(_Condition_variable_wait_for, 12) +bool __thiscall _Condition_variable_wait_for(_Condition_variable *this, + critical_section *cs, unsigned int timeout) +{ + LARGE_INTEGER to; + NTSTATUS status; + FILETIME ft; + cv_queue *q; + + TRACE("(%p %p %d)\n", this, cs, timeout); + + q = operator_new(sizeof(cv_queue)); + critical_section_lock(&this->lock); + q->next = this->queue; + q->expired = FALSE; + this->queue = q; + critical_section_unlock(&this->lock); + + critical_section_unlock(cs); + + GetSystemTimeAsFileTime(&ft); + to.QuadPart = ((LONGLONG)ft.dwHighDateTime << 32) + + ft.dwLowDateTime + (LONGLONG)timeout * 10000; + status = NtWaitForKeyedEvent(keyed_event, q, 0, &to); + if(status == STATUS_TIMEOUT) { + if(!InterlockedExchange(&q->expired, TRUE)) { + critical_section_lock(cs); + return FALSE; + } + else + NtWaitForKeyedEvent(keyed_event, q, 0, 0); + } + + operator_delete(q); + critical_section_lock(cs); + return TRUE; +} + +/* ?notify_one@_Condition_variable@details@Concurrency@@QAEXXZ */ +/* ?notify_one@_Condition_variable@details@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(_Condition_variable_notify_one, 4) +void __thiscall _Condition_variable_notify_one(_Condition_variable *this) +{ + cv_queue *node; + + TRACE("(%p)\n", this); + + if(!this->queue) + return; + + while(1) { + critical_section_lock(&this->lock); + node = this->queue; + if(!node) { + critical_section_unlock(&this->lock); + return; + } + this->queue = node->next; + critical_section_unlock(&this->lock); + + if(!InterlockedExchange(&node->expired, TRUE)) { + NtReleaseKeyedEvent(keyed_event, node, 0, NULL); + return; + } else { + HeapFree(GetProcessHeap(), 0, node); + } + } +} + +/* ?notify_all@_Condition_variable@details@Concurrency@@QAEXXZ */ +/* ?notify_all@_Condition_variable@details@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(_Condition_variable_notify_all, 4) +void __thiscall _Condition_variable_notify_all(_Condition_variable *this) +{ + cv_queue *ptr; + + TRACE("(%p)\n", this); + + if(!this->queue) + return; + + critical_section_lock(&this->lock); + ptr = this->queue; + this->queue = NULL; + critical_section_unlock(&this->lock); + + while(ptr) { + cv_queue *next = ptr->next; + + if(!InterlockedExchange(&ptr->expired, TRUE)) + NtReleaseKeyedEvent(keyed_event, ptr, 0, NULL); + else + HeapFree(GetProcessHeap(), 0, ptr); + ptr = next; + } +} +#endif + +/* ??0reader_writer_lock@Concurrency@@QAE@XZ */ +/* ??0reader_writer_lock@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(reader_writer_lock_ctor, 4) +reader_writer_lock* __thiscall reader_writer_lock_ctor(reader_writer_lock *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; +} + +/* ??1reader_writer_lock@Concurrency@@QAE@XZ */ +/* ??1reader_writer_lock@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(reader_writer_lock_dtor, 4) +void __thiscall reader_writer_lock_dtor(reader_writer_lock *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); +} + +/* ?lock@reader_writer_lock@Concurrency@@QAEXXZ */ +/* ?lock@reader_writer_lock@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(reader_writer_lock_lock, 4) +void __thiscall reader_writer_lock_lock(reader_writer_lock *this) +{ + rwl_queue q = { NULL }, *last; + + TRACE("(%p)\n", this); + + if (this->thread_id == GetCurrentThreadId()) + throw_exception(EXCEPTION_IMPROPER_LOCK, 0, "Already locked"); + + 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 */ +/* ?lock_read@reader_writer_lock@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(reader_writer_lock_lock_read, 4) +void __thiscall reader_writer_lock_lock_read(reader_writer_lock *this) +{ + rwl_queue q; + + TRACE("(%p)\n", this); + + if (this->thread_id == GetCurrentThreadId()) + throw_exception(EXCEPTION_IMPROPER_LOCK, 0, "Already locked as writer"); + + 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 */ +/* ?try_lock@reader_writer_lock@Concurrency@@QEAA_NXZ */ +DEFINE_THISCALL_WRAPPER(reader_writer_lock_try_lock, 4) +bool __thiscall reader_writer_lock_try_lock(reader_writer_lock *this) +{ + rwl_queue q = { NULL }; + + TRACE("(%p)\n", this); + + if (this->thread_id == GetCurrentThreadId()) + return FALSE; + + 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; +} + +/* ?try_lock_read@reader_writer_lock@Concurrency@@QAE_NXZ */ +/* ?try_lock_read@reader_writer_lock@Concurrency@@QEAA_NXZ */ +DEFINE_THISCALL_WRAPPER(reader_writer_lock_try_lock_read, 4) +bool __thiscall reader_writer_lock_try_lock_read(reader_writer_lock *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; +} + +/* ?unlock@reader_writer_lock@Concurrency@@QAEXXZ */ +/* ?unlock@reader_writer_lock@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(reader_writer_lock_unlock, 4) +void __thiscall reader_writer_lock_unlock(reader_writer_lock *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); +} + +/* ??0scoped_lock@reader_writer_lock@Concurrency@@QAE@AAV12@@Z */ +/* ??0scoped_lock@reader_writer_lock@Concurrency@@QEAA@AEAV12@@Z */ +DEFINE_THISCALL_WRAPPER(reader_writer_lock_scoped_lock_ctor, 8) +reader_writer_lock_scoped_lock* __thiscall reader_writer_lock_scoped_lock_ctor( + reader_writer_lock_scoped_lock *this, reader_writer_lock *lock) +{ + TRACE("(%p %p)\n", this, lock); + + this->lock = lock; + reader_writer_lock_lock(lock); + return this; +} + +/* ??1scoped_lock@reader_writer_lock@Concurrency@@QAE@XZ */ +/* ??1scoped_lock@reader_writer_lock@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(reader_writer_lock_scoped_lock_dtor, 4) +void __thiscall reader_writer_lock_scoped_lock_dtor(reader_writer_lock_scoped_lock *this) +{ + TRACE("(%p)\n", this); + reader_writer_lock_unlock(this->lock); +} + +/* ??0scoped_lock_read@reader_writer_lock@Concurrency@@QAE@AAV12@@Z */ +/* ??0scoped_lock_read@reader_writer_lock@Concurrency@@QEAA@AEAV12@@Z */ +DEFINE_THISCALL_WRAPPER(reader_writer_lock_scoped_lock_read_ctor, 8) +reader_writer_lock_scoped_lock* __thiscall reader_writer_lock_scoped_lock_read_ctor( + reader_writer_lock_scoped_lock *this, reader_writer_lock *lock) +{ + TRACE("(%p %p)\n", this, lock); + + this->lock = lock; + reader_writer_lock_lock_read(lock); + return this; +} + +/* ??1scoped_lock_read@reader_writer_lock@Concurrency@@QAE@XZ */ +/* ??1scoped_lock_read@reader_writer_lock@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(reader_writer_lock_scoped_lock_read_dtor, 4) +void __thiscall reader_writer_lock_scoped_lock_read_dtor(reader_writer_lock_scoped_lock *this) +{ + TRACE("(%p)\n", this); + reader_writer_lock_unlock(this->lock); +} + +/* ??0_ReentrantBlockingLock@details@Concurrency@@QAE@XZ */ +/* ??0_ReentrantBlockingLock@details@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(_ReentrantBlockingLock_ctor, 4) +_ReentrantBlockingLock* __thiscall _ReentrantBlockingLock_ctor(_ReentrantBlockingLock *this) +{ + TRACE("(%p)\n", this); + + InitializeCriticalSection(&this->cs); + this->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": _ReentrantBlockingLock"); + return this; +} + +/* ??1_ReentrantBlockingLock@details@Concurrency@@QAE@XZ */ +/* ??1_ReentrantBlockingLock@details@Concurrency@@QEAA@XZ */ +DEFINE_THISCALL_WRAPPER(_ReentrantBlockingLock_dtor, 4) +void __thiscall _ReentrantBlockingLock_dtor(_ReentrantBlockingLock *this) +{ + TRACE("(%p)\n", this); + + this->cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection(&this->cs); +} + +/* ?_Acquire@_ReentrantBlockingLock@details@Concurrency@@QAEXXZ */ +/* ?_Acquire@_ReentrantBlockingLock@details@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(_ReentrantBlockingLock__Acquire, 4) +void __thiscall _ReentrantBlockingLock__Acquire(_ReentrantBlockingLock *this) +{ + TRACE("(%p)\n", this); + EnterCriticalSection(&this->cs); +} + +/* ?_Release@_ReentrantBlockingLock@details@Concurrency@@QAEXXZ */ +/* ?_Release@_ReentrantBlockingLock@details@Concurrency@@QEAAXXZ */ +DEFINE_THISCALL_WRAPPER(_ReentrantBlockingLock__Release, 4) +void __thiscall _ReentrantBlockingLock__Release(_ReentrantBlockingLock *this) +{ + TRACE("(%p)\n", this); + LeaveCriticalSection(&this->cs); +} + +/* ?_TryAcquire@_ReentrantBlockingLock@details@Concurrency@@QAE_NXZ */ +/* ?_TryAcquire@_ReentrantBlockingLock@details@Concurrency@@QEAA_NXZ */ +DEFINE_THISCALL_WRAPPER(_ReentrantBlockingLock__TryAcquire, 4) +bool __thiscall _ReentrantBlockingLock__TryAcquire(_ReentrantBlockingLock *this) +{ + TRACE("(%p)\n", this); + return TryEnterCriticalSection(&this->cs); +} + +/* ?wait@Concurrency@@YAXI@Z */ +void __cdecl Concurrency_wait(unsigned int time) +{ + static int once; + + if (!once++) FIXME("(%d) stub!\n", time); + + Sleep(time); +} + #ifdef __ASM_USE_THISCALL_WRAPPER #define DEFINE_VTBL_WRAPPER(off) \ @@ -1158,6 +2462,9 @@ void msvcrt_free_concurrency(void) ThreadScheduler_dtor(default_scheduler); operator_delete(default_scheduler); } + + if(keyed_event) + NtClose(keyed_event); } void msvcrt_free_scheduler_thread(void) diff --git a/dlls/msvcrt/lock.c b/dlls/msvcrt/lock.c index 9c96217ca95..216d3d71504 100644 --- a/dlls/msvcrt/lock.c +++ b/dlls/msvcrt/lock.c @@ -118,1311 +118,6 @@ void CDECL _unlock( int locknum ) LeaveCriticalSection( &(lock_table[ locknum ].crit) ); } -#if _MSVCR_VER >= 100 -typedef enum -{ - SPINWAIT_INIT, - SPINWAIT_SPIN, - SPINWAIT_YIELD, - SPINWAIT_DONE -} SpinWait_state; - -typedef void (__cdecl *yield_func)(void); - -typedef struct -{ - ULONG spin; - ULONG unknown; - SpinWait_state state; - yield_func yield_func; -} SpinWait; - -/* ?_Value@_SpinCount@details@Concurrency@@SAIXZ */ -unsigned int __cdecl SpinCount__Value(void) -{ - static unsigned int val = -1; - - TRACE("()\n"); - - if(val == -1) { - SYSTEM_INFO si; - - GetSystemInfo(&si); - val = si.dwNumberOfProcessors>1 ? 4000 : 0; - } - - return val; -} - -/* ??0?$_SpinWait@$00@details@Concurrency@@QAE@P6AXXZ@Z */ -/* ??0?$_SpinWait@$00@details@Concurrency@@QEAA@P6AXXZ@Z */ -DEFINE_THISCALL_WRAPPER(SpinWait_ctor_yield, 8) -SpinWait* __thiscall SpinWait_ctor_yield(SpinWait *this, yield_func yf) -{ - TRACE("(%p %p)\n", this, yf); - - this->state = SPINWAIT_INIT; - this->unknown = 1; - this->yield_func = yf; - return this; -} - -/* ??0?$_SpinWait@$0A@@details@Concurrency@@QAE@P6AXXZ@Z */ -/* ??0?$_SpinWait@$0A@@details@Concurrency@@QEAA@P6AXXZ@Z */ -DEFINE_THISCALL_WRAPPER(SpinWait_ctor, 8) -SpinWait* __thiscall SpinWait_ctor(SpinWait *this, yield_func yf) -{ - TRACE("(%p %p)\n", this, yf); - - this->state = SPINWAIT_INIT; - this->unknown = 0; - this->yield_func = yf; - return this; -} - -/* ??_F?$_SpinWait@$00@details@Concurrency@@QAEXXZ */ -/* ??_F?$_SpinWait@$00@details@Concurrency@@QEAAXXZ */ -/* ??_F?$_SpinWait@$0A@@details@Concurrency@@QAEXXZ */ -/* ??_F?$_SpinWait@$0A@@details@Concurrency@@QEAAXXZ */ -DEFINE_THISCALL_WRAPPER(SpinWait_dtor, 4) -void __thiscall SpinWait_dtor(SpinWait *this) -{ - TRACE("(%p)\n", this); -} - -/* ?_DoYield@?$_SpinWait@$00@details@Concurrency@@IAEXXZ */ -/* ?_DoYield@?$_SpinWait@$00@details@Concurrency@@IEAAXXZ */ -/* ?_DoYield@?$_SpinWait@$0A@@details@Concurrency@@IAEXXZ */ -/* ?_DoYield@?$_SpinWait@$0A@@details@Concurrency@@IEAAXXZ */ -DEFINE_THISCALL_WRAPPER(SpinWait__DoYield, 4) -void __thiscall SpinWait__DoYield(SpinWait *this) -{ - TRACE("(%p)\n", this); - - if(this->unknown) - this->yield_func(); -} - -/* ?_NumberOfSpins@?$_SpinWait@$00@details@Concurrency@@IAEKXZ */ -/* ?_NumberOfSpins@?$_SpinWait@$00@details@Concurrency@@IEAAKXZ */ -/* ?_NumberOfSpins@?$_SpinWait@$0A@@details@Concurrency@@IAEKXZ */ -/* ?_NumberOfSpins@?$_SpinWait@$0A@@details@Concurrency@@IEAAKXZ */ -DEFINE_THISCALL_WRAPPER(SpinWait__NumberOfSpins, 4) -ULONG __thiscall SpinWait__NumberOfSpins(SpinWait *this) -{ - TRACE("(%p)\n", this); - return 1; -} - -/* ?_SetSpinCount@?$_SpinWait@$00@details@Concurrency@@QAEXI@Z */ -/* ?_SetSpinCount@?$_SpinWait@$00@details@Concurrency@@QEAAXI@Z */ -/* ?_SetSpinCount@?$_SpinWait@$0A@@details@Concurrency@@QAEXI@Z */ -/* ?_SetSpinCount@?$_SpinWait@$0A@@details@Concurrency@@QEAAXI@Z */ -DEFINE_THISCALL_WRAPPER(SpinWait__SetSpinCount, 8) -void __thiscall SpinWait__SetSpinCount(SpinWait *this, unsigned int spin) -{ - TRACE("(%p %d)\n", this, spin); - - this->spin = spin; - this->state = spin ? SPINWAIT_SPIN : SPINWAIT_YIELD; -} - -/* ?_Reset@?$_SpinWait@$00@details@Concurrency@@IAEXXZ */ -/* ?_Reset@?$_SpinWait@$00@details@Concurrency@@IEAAXXZ */ -/* ?_Reset@?$_SpinWait@$0A@@details@Concurrency@@IAEXXZ */ -/* ?_Reset@?$_SpinWait@$0A@@details@Concurrency@@IEAAXXZ */ -DEFINE_THISCALL_WRAPPER(SpinWait__Reset, 4) -void __thiscall SpinWait__Reset(SpinWait *this) -{ - SpinWait__SetSpinCount(this, SpinCount__Value()); -} - -/* ?_ShouldSpinAgain@?$_SpinWait@$00@details@Concurrency@@IAE_NXZ */ -/* ?_ShouldSpinAgain@?$_SpinWait@$00@details@Concurrency@@IEAA_NXZ */ -/* ?_ShouldSpinAgain@?$_SpinWait@$0A@@details@Concurrency@@IAE_NXZ */ -/* ?_ShouldSpinAgain@?$_SpinWait@$0A@@details@Concurrency@@IEAA_NXZ */ -DEFINE_THISCALL_WRAPPER(SpinWait__ShouldSpinAgain, 4) -bool __thiscall SpinWait__ShouldSpinAgain(SpinWait *this) -{ - TRACE("(%p)\n", this); - - this->spin--; - return this->spin > 0; -} - -/* ?_SpinOnce@?$_SpinWait@$00@details@Concurrency@@QAE_NXZ */ -/* ?_SpinOnce@?$_SpinWait@$00@details@Concurrency@@QEAA_NXZ */ -/* ?_SpinOnce@?$_SpinWait@$0A@@details@Concurrency@@QAE_NXZ */ -/* ?_SpinOnce@?$_SpinWait@$0A@@details@Concurrency@@QEAA_NXZ */ -DEFINE_THISCALL_WRAPPER(SpinWait__SpinOnce, 4) -bool __thiscall SpinWait__SpinOnce(SpinWait *this) -{ - switch(this->state) { - case SPINWAIT_INIT: - SpinWait__Reset(this); - /* fall through */ - case SPINWAIT_SPIN: - InterlockedDecrement((LONG*)&this->spin); - if(!this->spin) - this->state = this->unknown ? SPINWAIT_YIELD : SPINWAIT_DONE; - return TRUE; - case SPINWAIT_YIELD: - this->state = SPINWAIT_DONE; - this->yield_func(); - return TRUE; - default: - SpinWait__Reset(this); - return FALSE; - } -} - -static HANDLE keyed_event; - -/* keep in sync with msvcp90/msvcp90.h */ -typedef struct cs_queue -{ - struct cs_queue *next; -#if _MSVCR_VER >= 110 - BOOL free; - int unknown; -#endif -} cs_queue; - -typedef struct -{ - ULONG_PTR unk_thread_id; - cs_queue unk_active; -#if _MSVCR_VER >= 110 - void *unknown[2]; -#else - void *unknown[1]; -#endif - cs_queue *head; - void *tail; -} critical_section; - -/* ??0critical_section@Concurrency@@QAE@XZ */ -/* ??0critical_section@Concurrency@@QEAA@XZ */ -DEFINE_THISCALL_WRAPPER(critical_section_ctor, 4) -critical_section* __thiscall critical_section_ctor(critical_section *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); - } - - this->unk_thread_id = 0; - this->head = this->tail = NULL; - return this; -} - -/* ??1critical_section@Concurrency@@QAE@XZ */ -/* ??1critical_section@Concurrency@@QEAA@XZ */ -DEFINE_THISCALL_WRAPPER(critical_section_dtor, 4) -void __thiscall critical_section_dtor(critical_section *this) -{ - TRACE("(%p)\n", this); -} - -static void __cdecl spin_wait_yield(void) -{ - Sleep(0); -} - -static inline void spin_wait_for_next_cs(cs_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); -} - -static inline void cs_set_head(critical_section *cs, cs_queue *q) -{ - cs->unk_thread_id = GetCurrentThreadId(); - cs->unk_active.next = q->next; - cs->head = &cs->unk_active; -} - -static inline void cs_lock(critical_section *cs, cs_queue *q) -{ - cs_queue *last; - - if(cs->unk_thread_id == GetCurrentThreadId()) - throw_exception(EXCEPTION_IMPROPER_LOCK, 0, "Already locked"); - - memset(q, 0, sizeof(*q)); - last = InterlockedExchangePointer(&cs->tail, q); - if(last) { - last->next = q; - NtWaitForKeyedEvent(keyed_event, q, 0, NULL); - } - - cs_set_head(cs, q); - if(InterlockedCompareExchangePointer(&cs->tail, &cs->unk_active, q) != q) { - spin_wait_for_next_cs(q); - cs->unk_active.next = q->next; - } -} - -/* ?lock@critical_section@Concurrency@@QAEXXZ */ -/* ?lock@critical_section@Concurrency@@QEAAXXZ */ -DEFINE_THISCALL_WRAPPER(critical_section_lock, 4) -void __thiscall critical_section_lock(critical_section *this) -{ - cs_queue q; - - TRACE("(%p)\n", this); - cs_lock(this, &q); -} - -/* ?try_lock@critical_section@Concurrency@@QAE_NXZ */ -/* ?try_lock@critical_section@Concurrency@@QEAA_NXZ */ -DEFINE_THISCALL_WRAPPER(critical_section_try_lock, 4) -bool __thiscall critical_section_try_lock(critical_section *this) -{ - cs_queue q; - - TRACE("(%p)\n", this); - - if(this->unk_thread_id == GetCurrentThreadId()) - return FALSE; - - memset(&q, 0, sizeof(q)); - if(!InterlockedCompareExchangePointer(&this->tail, &q, NULL)) { - cs_set_head(this, &q); - if(InterlockedCompareExchangePointer(&this->tail, &this->unk_active, &q) != &q) { - spin_wait_for_next_cs(&q); - this->unk_active.next = q.next; - } - return TRUE; - } - return FALSE; -} - -/* ?unlock@critical_section@Concurrency@@QAEXXZ */ -/* ?unlock@critical_section@Concurrency@@QEAAXXZ */ -DEFINE_THISCALL_WRAPPER(critical_section_unlock, 4) -void __thiscall critical_section_unlock(critical_section *this) -{ - TRACE("(%p)\n", this); - - this->unk_thread_id = 0; - this->head = NULL; - if(InterlockedCompareExchangePointer(&this->tail, NULL, &this->unk_active) - == &this->unk_active) return; - spin_wait_for_next_cs(&this->unk_active); - -#if _MSVCR_VER >= 110 - while(1) { - cs_queue *next; - - if(!InterlockedExchange(&this->unk_active.next->free, TRUE)) - break; - - next = this->unk_active.next; - if(InterlockedCompareExchangePointer(&this->tail, NULL, next) == next) { - HeapFree(GetProcessHeap(), 0, next); - return; - } - spin_wait_for_next_cs(next); - - this->unk_active.next = next->next; - HeapFree(GetProcessHeap(), 0, next); - } -#endif - - NtReleaseKeyedEvent(keyed_event, this->unk_active.next, 0, NULL); -} - -/* ?native_handle@critical_section@Concurrency@@QAEAAV12@XZ */ -/* ?native_handle@critical_section@Concurrency@@QEAAAEAV12@XZ */ -DEFINE_THISCALL_WRAPPER(critical_section_native_handle, 4) -critical_section* __thiscall critical_section_native_handle(critical_section *this) -{ - TRACE("(%p)\n", this); - return this; -} - -#if _MSVCR_VER >= 110 -/* ?try_lock_for@critical_section@Concurrency@@QAE_NI@Z */ -/* ?try_lock_for@critical_section@Concurrency@@QEAA_NI@Z */ -DEFINE_THISCALL_WRAPPER(critical_section_try_lock_for, 8) -bool __thiscall critical_section_try_lock_for( - critical_section *this, unsigned int timeout) -{ - cs_queue *q, *last; - - TRACE("(%p %d)\n", this, timeout); - - if(this->unk_thread_id == GetCurrentThreadId()) - throw_exception(EXCEPTION_IMPROPER_LOCK, 0, "Already locked"); - - if(!(q = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*q)))) - return critical_section_try_lock(this); - - last = InterlockedExchangePointer(&this->tail, q); - if(last) { - LARGE_INTEGER to; - NTSTATUS status; - FILETIME ft; - - last->next = q; - GetSystemTimeAsFileTime(&ft); - to.QuadPart = ((LONGLONG)ft.dwHighDateTime<<32) + - ft.dwLowDateTime + (LONGLONG)timeout*10000; - status = NtWaitForKeyedEvent(keyed_event, q, 0, &to); - if(status == STATUS_TIMEOUT) { - if(!InterlockedExchange(&q->free, TRUE)) - return FALSE; - /* A thread has signaled the event and is block waiting. */ - /* We need to catch the event to wake the thread. */ - NtWaitForKeyedEvent(keyed_event, q, 0, NULL); - } - } - - cs_set_head(this, q); - if(InterlockedCompareExchangePointer(&this->tail, &this->unk_active, q) != q) { - spin_wait_for_next_cs(q); - this->unk_active.next = q->next; - } - - HeapFree(GetProcessHeap(), 0, q); - return TRUE; -} -#endif - -typedef struct -{ - critical_section *cs; - union { - cs_queue q; - struct { - void *unknown[4]; - int unknown2[2]; - } unknown; - } lock; -} critical_section_scoped_lock; - -/* ??0scoped_lock@critical_section@Concurrency@@QAE@AAV12@@Z */ -/* ??0scoped_lock@critical_section@Concurrency@@QEAA@AEAV12@@Z */ -DEFINE_THISCALL_WRAPPER(critical_section_scoped_lock_ctor, 8) -critical_section_scoped_lock* __thiscall critical_section_scoped_lock_ctor( - critical_section_scoped_lock *this, critical_section *cs) -{ - TRACE("(%p %p)\n", this, cs); - this->cs = cs; - cs_lock(this->cs, &this->lock.q); - return this; -} - -/* ??1scoped_lock@critical_section@Concurrency@@QAE@XZ */ -/* ??1scoped_lock@critical_section@Concurrency@@QEAA@XZ */ -DEFINE_THISCALL_WRAPPER(critical_section_scoped_lock_dtor, 4) -void __thiscall critical_section_scoped_lock_dtor(critical_section_scoped_lock *this) -{ - TRACE("(%p)\n", this); - critical_section_unlock(this->cs); -} - -typedef struct -{ - critical_section cs; -} _NonReentrantPPLLock; - -/* ??0_NonReentrantPPLLock@details@Concurrency@@QAE@XZ */ -/* ??0_NonReentrantPPLLock@details@Concurrency@@QEAA@XZ */ -DEFINE_THISCALL_WRAPPER(_NonReentrantPPLLock_ctor, 4) -_NonReentrantPPLLock* __thiscall _NonReentrantPPLLock_ctor(_NonReentrantPPLLock *this) -{ - TRACE("(%p)\n", this); - - critical_section_ctor(&this->cs); - return this; -} - -/* ?_Acquire@_NonReentrantPPLLock@details@Concurrency@@QAEXPAX@Z */ -/* ?_Acquire@_NonReentrantPPLLock@details@Concurrency@@QEAAXPEAX@Z */ -DEFINE_THISCALL_WRAPPER(_NonReentrantPPLLock__Acquire, 8) -void __thiscall _NonReentrantPPLLock__Acquire(_NonReentrantPPLLock *this, cs_queue *q) -{ - TRACE("(%p %p)\n", this, q); - cs_lock(&this->cs, q); -} - -/* ?_Release@_NonReentrantPPLLock@details@Concurrency@@QAEXXZ */ -/* ?_Release@_NonReentrantPPLLock@details@Concurrency@@QEAAXXZ */ -DEFINE_THISCALL_WRAPPER(_NonReentrantPPLLock__Release, 4) -void __thiscall _NonReentrantPPLLock__Release(_NonReentrantPPLLock *this) -{ - TRACE("(%p)\n", this); - critical_section_unlock(&this->cs); -} - -typedef struct -{ - _NonReentrantPPLLock *lock; - union { - cs_queue q; - struct { - void *unknown[4]; - int unknown2[2]; - } unknown; - } wait; -} _NonReentrantPPLLock__Scoped_lock; - -/* ??0_Scoped_lock@_NonReentrantPPLLock@details@Concurrency@@QAE@AAV123@@Z */ -/* ??0_Scoped_lock@_NonReentrantPPLLock@details@Concurrency@@QEAA@AEAV123@@Z */ -DEFINE_THISCALL_WRAPPER(_NonReentrantPPLLock__Scoped_lock_ctor, 8) -_NonReentrantPPLLock__Scoped_lock* __thiscall _NonReentrantPPLLock__Scoped_lock_ctor( - _NonReentrantPPLLock__Scoped_lock *this, _NonReentrantPPLLock *lock) -{ - TRACE("(%p %p)\n", this, lock); - - this->lock = lock; - _NonReentrantPPLLock__Acquire(this->lock, &this->wait.q); - return this; -} - -/* ??1_Scoped_lock@_NonReentrantPPLLock@details@Concurrency@@QAE@XZ */ -/* ??1_Scoped_lock@_NonReentrantPPLLock@details@Concurrency@@QEAA@XZ */ -DEFINE_THISCALL_WRAPPER(_NonReentrantPPLLock__Scoped_lock_dtor, 4) -void __thiscall _NonReentrantPPLLock__Scoped_lock_dtor(_NonReentrantPPLLock__Scoped_lock *this) -{ - TRACE("(%p)\n", this); - - _NonReentrantPPLLock__Release(this->lock); -} - -typedef struct -{ - critical_section cs; - LONG count; - LONG owner; -} _ReentrantPPLLock; - -/* ??0_ReentrantPPLLock@details@Concurrency@@QAE@XZ */ -/* ??0_ReentrantPPLLock@details@Concurrency@@QEAA@XZ */ -DEFINE_THISCALL_WRAPPER(_ReentrantPPLLock_ctor, 4) -_ReentrantPPLLock* __thiscall _ReentrantPPLLock_ctor(_ReentrantPPLLock *this) -{ - TRACE("(%p)\n", this); - - critical_section_ctor(&this->cs); - this->count = 0; - this->owner = -1; - return this; -} - -/* ?_Acquire@_ReentrantPPLLock@details@Concurrency@@QAEXPAX@Z */ -/* ?_Acquire@_ReentrantPPLLock@details@Concurrency@@QEAAXPEAX@Z */ -DEFINE_THISCALL_WRAPPER(_ReentrantPPLLock__Acquire, 8) -void __thiscall _ReentrantPPLLock__Acquire(_ReentrantPPLLock *this, cs_queue *q) -{ - TRACE("(%p %p)\n", this, q); - - if(this->owner == GetCurrentThreadId()) { - this->count++; - return; - } - - cs_lock(&this->cs, q); - this->count++; - this->owner = GetCurrentThreadId(); -} - -/* ?_Release@_ReentrantPPLLock@details@Concurrency@@QAEXXZ */ -/* ?_Release@_ReentrantPPLLock@details@Concurrency@@QEAAXXZ */ -DEFINE_THISCALL_WRAPPER(_ReentrantPPLLock__Release, 4) -void __thiscall _ReentrantPPLLock__Release(_ReentrantPPLLock *this) -{ - TRACE("(%p)\n", this); - - this->count--; - if(this->count) - return; - - this->owner = -1; - critical_section_unlock(&this->cs); -} - -typedef struct -{ - _ReentrantPPLLock *lock; - union { - cs_queue q; - struct { - void *unknown[4]; - int unknown2[2]; - } unknown; - } wait; -} _ReentrantPPLLock__Scoped_lock; - -/* ??0_Scoped_lock@_ReentrantPPLLock@details@Concurrency@@QAE@AAV123@@Z */ -/* ??0_Scoped_lock@_ReentrantPPLLock@details@Concurrency@@QEAA@AEAV123@@Z */ -DEFINE_THISCALL_WRAPPER(_ReentrantPPLLock__Scoped_lock_ctor, 8) -_ReentrantPPLLock__Scoped_lock* __thiscall _ReentrantPPLLock__Scoped_lock_ctor( - _ReentrantPPLLock__Scoped_lock *this, _ReentrantPPLLock *lock) -{ - TRACE("(%p %p)\n", this, lock); - - this->lock = lock; - _ReentrantPPLLock__Acquire(this->lock, &this->wait.q); - return this; -} - -/* ??1_Scoped_lock@_ReentrantPPLLock@details@Concurrency@@QAE@XZ */ -/* ??1_Scoped_lock@_ReentrantPPLLock@details@Concurrency@@QEAA@XZ */ -DEFINE_THISCALL_WRAPPER(_ReentrantPPLLock__Scoped_lock_dtor, 4) -void __thiscall _ReentrantPPLLock__Scoped_lock_dtor(_ReentrantPPLLock__Scoped_lock *this) -{ - TRACE("(%p)\n", this); - - _ReentrantPPLLock__Release(this->lock); -} - -/* ?_GetConcurrency@details@Concurrency@@YAIXZ */ -unsigned int __cdecl _GetConcurrency(void) -{ - static unsigned int val = -1; - - TRACE("()\n"); - - if(val == -1) { - SYSTEM_INFO si; - - GetSystemInfo(&si); - val = si.dwNumberOfProcessors; - } - - return val; -} - -#define EVT_RUNNING (void*)1 -#define EVT_WAITING NULL - -struct thread_wait; -typedef struct thread_wait_entry -{ - struct thread_wait *wait; - struct thread_wait_entry *next; - struct thread_wait_entry *prev; -} thread_wait_entry; - -typedef struct thread_wait -{ - void *signaled; - int pending_waits; - thread_wait_entry entries[1]; -} thread_wait; - -typedef struct -{ - thread_wait_entry *waiters; - INT_PTR signaled; - critical_section cs; -} event; - -static inline PLARGE_INTEGER evt_timeout(PLARGE_INTEGER pTime, unsigned int timeout) -{ - if(timeout == COOPERATIVE_TIMEOUT_INFINITE) return NULL; - pTime->QuadPart = (ULONGLONG)timeout * -10000; - return pTime; -} - -static void evt_add_queue(thread_wait_entry **head, thread_wait_entry *entry) -{ - entry->next = *head; - entry->prev = NULL; - if(*head) (*head)->prev = entry; - *head = entry; -} - -static void evt_remove_queue(thread_wait_entry **head, thread_wait_entry *entry) -{ - if(entry == *head) - *head = entry->next; - else if(entry->prev) - entry->prev->next = entry->next; - if(entry->next) entry->next->prev = entry->prev; -} - -static size_t evt_end_wait(thread_wait *wait, event **events, int count) -{ - size_t i, ret = COOPERATIVE_WAIT_TIMEOUT; - - for(i = 0; i < count; i++) { - critical_section_lock(&events[i]->cs); - if(events[i] == wait->signaled) ret = i; - evt_remove_queue(&events[i]->waiters, &wait->entries[i]); - critical_section_unlock(&events[i]->cs); - } - - return ret; -} - -static inline int evt_transition(void **state, void *from, void *to) -{ - return InterlockedCompareExchangePointer(state, to, from) == from; -} - -static size_t evt_wait(thread_wait *wait, event **events, int count, bool wait_all, unsigned int timeout) -{ - int i; - NTSTATUS status; - LARGE_INTEGER ntto; - - wait->signaled = EVT_RUNNING; - wait->pending_waits = wait_all ? count : 1; - for(i = 0; i < count; i++) { - wait->entries[i].wait = wait; - - critical_section_lock(&events[i]->cs); - evt_add_queue(&events[i]->waiters, &wait->entries[i]); - if(events[i]->signaled) { - if(!InterlockedDecrement(&wait->pending_waits)) { - wait->signaled = events[i]; - critical_section_unlock(&events[i]->cs); - - return evt_end_wait(wait, events, i+1); - } - } - critical_section_unlock(&events[i]->cs); - } - - if(!timeout) - return evt_end_wait(wait, events, count); - - if(!evt_transition(&wait->signaled, EVT_RUNNING, EVT_WAITING)) - return evt_end_wait(wait, events, count); - - status = NtWaitForKeyedEvent(keyed_event, wait, 0, evt_timeout(&ntto, timeout)); - - if(status && !evt_transition(&wait->signaled, EVT_WAITING, EVT_RUNNING)) - NtWaitForKeyedEvent(keyed_event, wait, 0, NULL); - - return evt_end_wait(wait, events, count); -} - -/* ??0event@Concurrency@@QAE@XZ */ -/* ??0event@Concurrency@@QEAA@XZ */ -DEFINE_THISCALL_WRAPPER(event_ctor, 4) -event* __thiscall event_ctor(event *this) -{ - TRACE("(%p)\n", this); - - this->waiters = NULL; - this->signaled = FALSE; - critical_section_ctor(&this->cs); - - return this; -} - -/* ??1event@Concurrency@@QAE@XZ */ -/* ??1event@Concurrency@@QEAA@XZ */ -DEFINE_THISCALL_WRAPPER(event_dtor, 4) -void __thiscall event_dtor(event *this) -{ - TRACE("(%p)\n", this); - critical_section_dtor(&this->cs); - if(this->waiters) - ERR("there's a wait on destroyed event\n"); -} - -/* ?reset@event@Concurrency@@QAEXXZ */ -/* ?reset@event@Concurrency@@QEAAXXZ */ -DEFINE_THISCALL_WRAPPER(event_reset, 4) -void __thiscall event_reset(event *this) -{ - thread_wait_entry *entry; - - TRACE("(%p)\n", this); - - critical_section_lock(&this->cs); - if(this->signaled) { - this->signaled = FALSE; - for(entry=this->waiters; entry; entry = entry->next) - InterlockedIncrement(&entry->wait->pending_waits); - } - critical_section_unlock(&this->cs); -} - -/* ?set@event@Concurrency@@QAEXXZ */ -/* ?set@event@Concurrency@@QEAAXXZ */ -DEFINE_THISCALL_WRAPPER(event_set, 4) -void __thiscall event_set(event *this) -{ - thread_wait_entry *wakeup = NULL; - thread_wait_entry *entry, *next; - - TRACE("(%p)\n", this); - - critical_section_lock(&this->cs); - if(!this->signaled) { - this->signaled = TRUE; - for(entry=this->waiters; entry; entry=next) { - next = entry->next; - if(!InterlockedDecrement(&entry->wait->pending_waits)) { - if(InterlockedExchangePointer(&entry->wait->signaled, this) == EVT_WAITING) { - evt_remove_queue(&this->waiters, entry); - evt_add_queue(&wakeup, entry); - } - } - } - } - critical_section_unlock(&this->cs); - - for(entry=wakeup; entry; entry=next) { - next = entry->next; - entry->next = entry->prev = NULL; - NtReleaseKeyedEvent(keyed_event, entry->wait, 0, NULL); - } -} - -/* ?wait@event@Concurrency@@QAEII@Z */ -/* ?wait@event@Concurrency@@QEAA_KI@Z */ -DEFINE_THISCALL_WRAPPER(event_wait, 8) -size_t __thiscall event_wait(event *this, unsigned int timeout) -{ - thread_wait wait; - size_t signaled; - - TRACE("(%p %u)\n", this, timeout); - - critical_section_lock(&this->cs); - signaled = this->signaled; - critical_section_unlock(&this->cs); - - if(!timeout) return signaled ? 0 : COOPERATIVE_WAIT_TIMEOUT; - return signaled ? 0 : evt_wait(&wait, &this, 1, FALSE, timeout); -} - -/* ?wait_for_multiple@event@Concurrency@@SAIPAPAV12@I_NI@Z */ -/* ?wait_for_multiple@event@Concurrency@@SA_KPEAPEAV12@_K_NI@Z */ -int __cdecl event_wait_for_multiple(event **events, size_t count, bool wait_all, unsigned int timeout) -{ - thread_wait *wait; - size_t ret; - - TRACE("(%p %Iu %d %u)\n", events, count, wait_all, timeout); - - if(count == 0) - return 0; - - wait = operator_new(FIELD_OFFSET(thread_wait, entries[count])); - ret = evt_wait(wait, events, count, wait_all, timeout); - operator_delete(wait); - - return ret; -} -#endif - -#if _MSVCR_VER >= 110 -typedef struct cv_queue { - struct cv_queue *next; - BOOL expired; -} cv_queue; - -typedef struct { - /* cv_queue structure is not binary compatible */ - cv_queue *queue; - critical_section lock; -} _Condition_variable; - -/* ??0_Condition_variable@details@Concurrency@@QAE@XZ */ -/* ??0_Condition_variable@details@Concurrency@@QEAA@XZ */ -DEFINE_THISCALL_WRAPPER(_Condition_variable_ctor, 4) -_Condition_variable* __thiscall _Condition_variable_ctor(_Condition_variable *this) -{ - TRACE("(%p)\n", this); - - this->queue = NULL; - critical_section_ctor(&this->lock); - return this; -} - -/* ??1_Condition_variable@details@Concurrency@@QAE@XZ */ -/* ??1_Condition_variable@details@Concurrency@@QEAA@XZ */ -DEFINE_THISCALL_WRAPPER(_Condition_variable_dtor, 4) -void __thiscall _Condition_variable_dtor(_Condition_variable *this) -{ - TRACE("(%p)\n", this); - - while(this->queue) { - cv_queue *next = this->queue->next; - if(!this->queue->expired) - ERR("there's an active wait\n"); - HeapFree(GetProcessHeap(), 0, this->queue); - this->queue = next; - } - critical_section_dtor(&this->lock); -} - -/* ?wait@_Condition_variable@details@Concurrency@@QAEXAAVcritical_section@3@@Z */ -/* ?wait@_Condition_variable@details@Concurrency@@QEAAXAEAVcritical_section@3@@Z */ -DEFINE_THISCALL_WRAPPER(_Condition_variable_wait, 8) -void __thiscall _Condition_variable_wait(_Condition_variable *this, critical_section *cs) -{ - cv_queue q; - - TRACE("(%p, %p)\n", this, cs); - - critical_section_lock(&this->lock); - q.next = this->queue; - q.expired = FALSE; - this->queue = &q; - critical_section_unlock(&this->lock); - - critical_section_unlock(cs); - NtWaitForKeyedEvent(keyed_event, &q, 0, NULL); - critical_section_lock(cs); -} - -/* ?wait_for@_Condition_variable@details@Concurrency@@QAE_NAAVcritical_section@3@I@Z */ -/* ?wait_for@_Condition_variable@details@Concurrency@@QEAA_NAEAVcritical_section@3@I@Z */ -DEFINE_THISCALL_WRAPPER(_Condition_variable_wait_for, 12) -bool __thiscall _Condition_variable_wait_for(_Condition_variable *this, - critical_section *cs, unsigned int timeout) -{ - LARGE_INTEGER to; - NTSTATUS status; - FILETIME ft; - cv_queue *q; - - TRACE("(%p %p %d)\n", this, cs, timeout); - - q = operator_new(sizeof(cv_queue)); - critical_section_lock(&this->lock); - q->next = this->queue; - q->expired = FALSE; - this->queue = q; - critical_section_unlock(&this->lock); - - critical_section_unlock(cs); - - GetSystemTimeAsFileTime(&ft); - to.QuadPart = ((LONGLONG)ft.dwHighDateTime << 32) + - ft.dwLowDateTime + (LONGLONG)timeout * 10000; - status = NtWaitForKeyedEvent(keyed_event, q, 0, &to); - if(status == STATUS_TIMEOUT) { - if(!InterlockedExchange(&q->expired, TRUE)) { - critical_section_lock(cs); - return FALSE; - } - else - NtWaitForKeyedEvent(keyed_event, q, 0, 0); - } - - operator_delete(q); - critical_section_lock(cs); - return TRUE; -} - -/* ?notify_one@_Condition_variable@details@Concurrency@@QAEXXZ */ -/* ?notify_one@_Condition_variable@details@Concurrency@@QEAAXXZ */ -DEFINE_THISCALL_WRAPPER(_Condition_variable_notify_one, 4) -void __thiscall _Condition_variable_notify_one(_Condition_variable *this) -{ - cv_queue *node; - - TRACE("(%p)\n", this); - - if(!this->queue) - return; - - while(1) { - critical_section_lock(&this->lock); - node = this->queue; - if(!node) { - critical_section_unlock(&this->lock); - return; - } - this->queue = node->next; - critical_section_unlock(&this->lock); - - if(!InterlockedExchange(&node->expired, TRUE)) { - NtReleaseKeyedEvent(keyed_event, node, 0, NULL); - return; - } else { - HeapFree(GetProcessHeap(), 0, node); - } - } -} - -/* ?notify_all@_Condition_variable@details@Concurrency@@QAEXXZ */ -/* ?notify_all@_Condition_variable@details@Concurrency@@QEAAXXZ */ -DEFINE_THISCALL_WRAPPER(_Condition_variable_notify_all, 4) -void __thiscall _Condition_variable_notify_all(_Condition_variable *this) -{ - cv_queue *ptr; - - TRACE("(%p)\n", this); - - if(!this->queue) - return; - - critical_section_lock(&this->lock); - ptr = this->queue; - this->queue = NULL; - critical_section_unlock(&this->lock); - - while(ptr) { - cv_queue *next = ptr->next; - - if(!InterlockedExchange(&ptr->expired, TRUE)) - NtReleaseKeyedEvent(keyed_event, ptr, 0, NULL); - else - HeapFree(GetProcessHeap(), 0, ptr); - ptr = next; - } -} -#endif - -#if _MSVCR_VER >= 100 -typedef struct rwl_queue -{ - struct rwl_queue *next; -} rwl_queue; - -#define WRITER_WAITING 0x80000000 -/* FIXME: reader_writer_lock structure is not binary compatible - * it can't exceed 28/56 bytes */ -typedef struct -{ - LONG count; - LONG thread_id; - rwl_queue active; - rwl_queue *writer_head; - rwl_queue *writer_tail; - rwl_queue *reader_head; -} reader_writer_lock; - -/* ??0reader_writer_lock@Concurrency@@QAE@XZ */ -/* ??0reader_writer_lock@Concurrency@@QEAA@XZ */ -DEFINE_THISCALL_WRAPPER(reader_writer_lock_ctor, 4) -reader_writer_lock* __thiscall reader_writer_lock_ctor(reader_writer_lock *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; -} - -/* ??1reader_writer_lock@Concurrency@@QAE@XZ */ -/* ??1reader_writer_lock@Concurrency@@QEAA@XZ */ -DEFINE_THISCALL_WRAPPER(reader_writer_lock_dtor, 4) -void __thiscall reader_writer_lock_dtor(reader_writer_lock *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); -} - -/* ?lock@reader_writer_lock@Concurrency@@QAEXXZ */ -/* ?lock@reader_writer_lock@Concurrency@@QEAAXXZ */ -DEFINE_THISCALL_WRAPPER(reader_writer_lock_lock, 4) -void __thiscall reader_writer_lock_lock(reader_writer_lock *this) -{ - rwl_queue q = { NULL }, *last; - - TRACE("(%p)\n", this); - - if (this->thread_id == GetCurrentThreadId()) - throw_exception(EXCEPTION_IMPROPER_LOCK, 0, "Already locked"); - - 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 */ -/* ?lock_read@reader_writer_lock@Concurrency@@QEAAXXZ */ -DEFINE_THISCALL_WRAPPER(reader_writer_lock_lock_read, 4) -void __thiscall reader_writer_lock_lock_read(reader_writer_lock *this) -{ - rwl_queue q; - - TRACE("(%p)\n", this); - - if (this->thread_id == GetCurrentThreadId()) - throw_exception(EXCEPTION_IMPROPER_LOCK, 0, "Already locked as writer"); - - 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 */ -/* ?try_lock@reader_writer_lock@Concurrency@@QEAA_NXZ */ -DEFINE_THISCALL_WRAPPER(reader_writer_lock_try_lock, 4) -bool __thiscall reader_writer_lock_try_lock(reader_writer_lock *this) -{ - rwl_queue q = { NULL }; - - TRACE("(%p)\n", this); - - if (this->thread_id == GetCurrentThreadId()) - return FALSE; - - 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; -} - -/* ?try_lock_read@reader_writer_lock@Concurrency@@QAE_NXZ */ -/* ?try_lock_read@reader_writer_lock@Concurrency@@QEAA_NXZ */ -DEFINE_THISCALL_WRAPPER(reader_writer_lock_try_lock_read, 4) -bool __thiscall reader_writer_lock_try_lock_read(reader_writer_lock *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; -} - -/* ?unlock@reader_writer_lock@Concurrency@@QAEXXZ */ -/* ?unlock@reader_writer_lock@Concurrency@@QEAAXXZ */ -DEFINE_THISCALL_WRAPPER(reader_writer_lock_unlock, 4) -void __thiscall reader_writer_lock_unlock(reader_writer_lock *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); -} - -typedef struct { - reader_writer_lock *lock; -} reader_writer_lock_scoped_lock; - -/* ??0scoped_lock@reader_writer_lock@Concurrency@@QAE@AAV12@@Z */ -/* ??0scoped_lock@reader_writer_lock@Concurrency@@QEAA@AEAV12@@Z */ -DEFINE_THISCALL_WRAPPER(reader_writer_lock_scoped_lock_ctor, 8) -reader_writer_lock_scoped_lock* __thiscall reader_writer_lock_scoped_lock_ctor( - reader_writer_lock_scoped_lock *this, reader_writer_lock *lock) -{ - TRACE("(%p %p)\n", this, lock); - - this->lock = lock; - reader_writer_lock_lock(lock); - return this; -} - -/* ??1scoped_lock@reader_writer_lock@Concurrency@@QAE@XZ */ -/* ??1scoped_lock@reader_writer_lock@Concurrency@@QEAA@XZ */ -DEFINE_THISCALL_WRAPPER(reader_writer_lock_scoped_lock_dtor, 4) -void __thiscall reader_writer_lock_scoped_lock_dtor(reader_writer_lock_scoped_lock *this) -{ - TRACE("(%p)\n", this); - reader_writer_lock_unlock(this->lock); -} - -/* ??0scoped_lock_read@reader_writer_lock@Concurrency@@QAE@AAV12@@Z */ -/* ??0scoped_lock_read@reader_writer_lock@Concurrency@@QEAA@AEAV12@@Z */ -DEFINE_THISCALL_WRAPPER(reader_writer_lock_scoped_lock_read_ctor, 8) -reader_writer_lock_scoped_lock* __thiscall reader_writer_lock_scoped_lock_read_ctor( - reader_writer_lock_scoped_lock *this, reader_writer_lock *lock) -{ - TRACE("(%p %p)\n", this, lock); - - this->lock = lock; - reader_writer_lock_lock_read(lock); - return this; -} - -/* ??1scoped_lock_read@reader_writer_lock@Concurrency@@QAE@XZ */ -/* ??1scoped_lock_read@reader_writer_lock@Concurrency@@QEAA@XZ */ -DEFINE_THISCALL_WRAPPER(reader_writer_lock_scoped_lock_read_dtor, 4) -void __thiscall reader_writer_lock_scoped_lock_read_dtor(reader_writer_lock_scoped_lock *this) -{ - TRACE("(%p)\n", this); - reader_writer_lock_unlock(this->lock); -} - -typedef struct { - CRITICAL_SECTION cs; -} _ReentrantBlockingLock; - -/* ??0_ReentrantBlockingLock@details@Concurrency@@QAE@XZ */ -/* ??0_ReentrantBlockingLock@details@Concurrency@@QEAA@XZ */ -DEFINE_THISCALL_WRAPPER(_ReentrantBlockingLock_ctor, 4) -_ReentrantBlockingLock* __thiscall _ReentrantBlockingLock_ctor(_ReentrantBlockingLock *this) -{ - TRACE("(%p)\n", this); - - InitializeCriticalSection(&this->cs); - this->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": _ReentrantBlockingLock"); - return this; -} - -/* ??1_ReentrantBlockingLock@details@Concurrency@@QAE@XZ */ -/* ??1_ReentrantBlockingLock@details@Concurrency@@QEAA@XZ */ -DEFINE_THISCALL_WRAPPER(_ReentrantBlockingLock_dtor, 4) -void __thiscall _ReentrantBlockingLock_dtor(_ReentrantBlockingLock *this) -{ - TRACE("(%p)\n", this); - - this->cs.DebugInfo->Spare[0] = 0; - DeleteCriticalSection(&this->cs); -} - -/* ?_Acquire@_ReentrantBlockingLock@details@Concurrency@@QAEXXZ */ -/* ?_Acquire@_ReentrantBlockingLock@details@Concurrency@@QEAAXXZ */ -DEFINE_THISCALL_WRAPPER(_ReentrantBlockingLock__Acquire, 4) -void __thiscall _ReentrantBlockingLock__Acquire(_ReentrantBlockingLock *this) -{ - TRACE("(%p)\n", this); - EnterCriticalSection(&this->cs); -} - -/* ?_Release@_ReentrantBlockingLock@details@Concurrency@@QAEXXZ */ -/* ?_Release@_ReentrantBlockingLock@details@Concurrency@@QEAAXXZ */ -DEFINE_THISCALL_WRAPPER(_ReentrantBlockingLock__Release, 4) -void __thiscall _ReentrantBlockingLock__Release(_ReentrantBlockingLock *this) -{ - TRACE("(%p)\n", this); - LeaveCriticalSection(&this->cs); -} - -/* ?_TryAcquire@_ReentrantBlockingLock@details@Concurrency@@QAE_NXZ */ -/* ?_TryAcquire@_ReentrantBlockingLock@details@Concurrency@@QEAA_NXZ */ -DEFINE_THISCALL_WRAPPER(_ReentrantBlockingLock__TryAcquire, 4) -bool __thiscall _ReentrantBlockingLock__TryAcquire(_ReentrantBlockingLock *this) -{ - TRACE("(%p)\n", this); - return TryEnterCriticalSection(&this->cs); -} - -/* ?wait@Concurrency@@YAXI@Z */ -void __cdecl Concurrency_wait(unsigned int time) -{ - static int once; - - if (!once++) FIXME("(%d) stub!\n", time); - - Sleep(time); -} -#endif - #if _MSVCR_VER == 110 static LONG shared_ptr_lock; @@ -1465,9 +160,4 @@ void msvcrt_free_locks(void) msvcrt_uninitialize_mlock( i ); } } - -#if _MSVCR_VER >= 100 - if(keyed_event) - NtClose(keyed_event); -#endif }