From: Yifu Wang Subject: [PATCH] msvcr120: Implemented Concurrency::details::_Condition_variable. Message-Id: Date: Sat, 14 Feb 2015 00:52:51 +0000 Implementation of Concurrency::details::_Condition_variable along with a producer-consumer test case. The class is used in msvcp120.dll by _Cnd_t functions, which are used in std::condition_variable and std::thread. --- dlls/msvcr120/msvcr120.spec | 36 +++--- dlls/msvcr120/tests/msvcr120.c | 277 +++++++++++++++++++++++++++++++++++++++++ dlls/msvcrt/lock.c | 171 +++++++++++++++++++++++++ 3 files changed, 466 insertions(+), 18 deletions(-) From 1557be9ab58d75aebdc49e6fcef51f1b8cc7e196 Mon Sep 17 00:00:00 2001 From: Yifu Wang Date: Mon, 5 Jan 2015 10:18:05 -0800 Subject: [PATCH] msvcr120: Implemented Concurrency::details::_Condition_variable. --- dlls/msvcr120/msvcr120.spec | 36 +++--- dlls/msvcr120/tests/msvcr120.c | 277 +++++++++++++++++++++++++++++++++++++++++ dlls/msvcrt/lock.c | 171 +++++++++++++++++++++++++ 3 files changed, 466 insertions(+), 18 deletions(-) diff --git a/dlls/msvcr120/msvcr120.spec b/dlls/msvcr120/msvcr120.spec index 58e708d..531a21b 100644 --- a/dlls/msvcr120/msvcr120.spec +++ b/dlls/msvcr120/msvcr120.spec @@ -17,9 +17,9 @@ @ stub -arch=arm ??0_Cancellation_beacon@details@Concurrency@@QAA@XZ @ stub -arch=i386 ??0_Cancellation_beacon@details@Concurrency@@QAE@XZ @ stub -arch=win64 ??0_Cancellation_beacon@details@Concurrency@@QEAA@XZ -@ stub -arch=arm ??0_Condition_variable@details@Concurrency@@QAA@XZ -@ stub -arch=i386 ??0_Condition_variable@details@Concurrency@@QAE@XZ -@ stub -arch=win64 ??0_Condition_variable@details@Concurrency@@QEAA@XZ +@ cdecl -arch=arm ??0_Condition_variable@details@Concurrency@@QAA@XZ(ptr) _Condition_variable_ctor +@ thiscall -arch=i386 ??0_Condition_variable@details@Concurrency@@QAE@XZ(ptr) _Condition_variable_ctor +@ cdecl -arch=win64 ??0_Condition_variable@details@Concurrency@@QEAA@XZ(ptr) _Condition_variable_ctor @ stub -arch=arm ??0_Context@details@Concurrency@@QAA@PAVContext@2@@Z @ stub -arch=i386 ??0_Context@details@Concurrency@@QAE@PAVContext@2@@Z @ stub -arch=win64 ??0_Context@details@Concurrency@@QEAA@PEAVContext@2@@Z @@ -272,9 +272,9 @@ @ stub -arch=arm ??1_Cancellation_beacon@details@Concurrency@@QAA@XZ @ stub -arch=i386 ??1_Cancellation_beacon@details@Concurrency@@QAE@XZ @ stub -arch=win64 ??1_Cancellation_beacon@details@Concurrency@@QEAA@XZ -@ stub -arch=arm ??1_Condition_variable@details@Concurrency@@QAA@XZ -@ stub -arch=i386 ??1_Condition_variable@details@Concurrency@@QAE@XZ -@ stub -arch=win64 ??1_Condition_variable@details@Concurrency@@QEAA@XZ +@ cdecl -arch=arm ??1_Condition_variable@details@Concurrency@@QAA@XZ(ptr) _Condition_variable_dtor +@ thiscall -arch=i386 ??1_Condition_variable@details@Concurrency@@QAE@XZ(ptr) _Condition_variable_dtor +@ cdecl -arch=win64 ??1_Condition_variable@details@Concurrency@@QEAA@XZ(ptr) _Condition_variable_dtor @ stub -arch=arm ??1_NonReentrantBlockingLock@details@Concurrency@@QAA@XZ @ stub -arch=i386 ??1_NonReentrantBlockingLock@details@Concurrency@@QAE@XZ @ stub -arch=win64 ??1_NonReentrantBlockingLock@details@Concurrency@@QEAA@XZ @@ -741,12 +741,12 @@ @ cdecl -arch=arm ?native_handle@critical_section@Concurrency@@QAAAAV12@XZ(ptr) critical_section_native_handle @ thiscall -arch=i386 ?native_handle@critical_section@Concurrency@@QAEAAV12@XZ(ptr) critical_section_native_handle @ cdecl -arch=win64 ?native_handle@critical_section@Concurrency@@QEAAAEAV12@XZ(ptr) critical_section_native_handle -@ stub -arch=arm ?notify_all@_Condition_variable@details@Concurrency@@QAAXXZ -@ stub -arch=i386 ?notify_all@_Condition_variable@details@Concurrency@@QAEXXZ -@ stub -arch=win64 ?notify_all@_Condition_variable@details@Concurrency@@QEAAXXZ -@ stub -arch=arm ?notify_one@_Condition_variable@details@Concurrency@@QAAXXZ -@ stub -arch=i386 ?notify_one@_Condition_variable@details@Concurrency@@QAEXXZ -@ stub -arch=win64 ?notify_one@_Condition_variable@details@Concurrency@@QEAAXXZ +@ cdecl -arch=arm ?notify_all@_Condition_variable@details@Concurrency@@QAAXXZ(ptr) _Condition_variable_notify_all +@ thiscall -arch=i386 ?notify_all@_Condition_variable@details@Concurrency@@QAEXXZ(ptr) _Condition_variable_notify_all +@ cdecl -arch=win64 ?notify_all@_Condition_variable@details@Concurrency@@QEAAXXZ(ptr) _Condition_variable_notify_all +@ cdecl -arch=arm ?notify_one@_Condition_variable@details@Concurrency@@QAAXXZ(ptr) _Condition_variable_notify_one +@ thiscall -arch=i386 ?notify_one@_Condition_variable@details@Concurrency@@QAEXXZ(ptr) _Condition_variable_notify_one +@ cdecl -arch=win64 ?notify_one@_Condition_variable@details@Concurrency@@QEAAXXZ(ptr) _Condition_variable_notify_one @ cdecl -arch=arm ?raw_name@type_info@@QBAPBDXZ(ptr) MSVCRT_type_info_raw_name @ thiscall -arch=i386 ?raw_name@type_info@@QBEPBDXZ(ptr) MSVCRT_type_info_raw_name @ cdecl -arch=win64 ?raw_name@type_info@@QEBAPEBDXZ(ptr) MSVCRT_type_info_raw_name @@ -789,15 +789,15 @@ @ stub -arch=win64 ?unlock@reader_writer_lock@Concurrency@@QEAAXXZ @ cdecl ?vswprintf@@YAHPA_WIPB_WPAD@Z(ptr long wstr ptr) MSVCRT_vsnwprintf @ stub ?wait@Concurrency@@YAXI@Z -@ stub -arch=arm ?wait@_Condition_variable@details@Concurrency@@QAAXAAVcritical_section@3@@Z -@ stub -arch=i386 ?wait@_Condition_variable@details@Concurrency@@QAEXAAVcritical_section@3@@Z -@ stub -arch=win64 ?wait@_Condition_variable@details@Concurrency@@QEAAXAEAVcritical_section@3@@Z +@ cdecl -arch=arm ?wait@_Condition_variable@details@Concurrency@@QAAXAAVcritical_section@3@@Z(ptr ptr) _Condition_variable_wait +@ thiscall -arch=i386 ?wait@_Condition_variable@details@Concurrency@@QAEXAAVcritical_section@3@@Z(ptr ptr) _Condition_variable_wait +@ cdecl -arch=win64 ?wait@_Condition_variable@details@Concurrency@@QEAAXAEAVcritical_section@3@@Z(ptr ptr) _Condition_variable_wait @ stub -arch=arm ?wait@event@Concurrency@@QAAII@Z @ stub -arch=i386 ?wait@event@Concurrency@@QAEII@Z @ stub -arch=win64 ?wait@event@Concurrency@@QEAA_KI@Z -@ stub -arch=arm ?wait_for@_Condition_variable@details@Concurrency@@QAA_NAAVcritical_section@3@I@Z -@ stub -arch=i386 ?wait_for@_Condition_variable@details@Concurrency@@QAE_NAAVcritical_section@3@I@Z -@ stub -arch=win64 ?wait_for@_Condition_variable@details@Concurrency@@QEAA_NAEAVcritical_section@3@I@Z +@ cdecl -arch=arm ?wait_for@_Condition_variable@details@Concurrency@@QAA_NAAVcritical_section@3@I@Z(ptr ptr ptr) _Condition_variable_wait_for +@ thiscall -arch=i386 ?wait_for@_Condition_variable@details@Concurrency@@QAE_NAAVcritical_section@3@I@Z(ptr ptr ptr) _Condition_variable_wait_for +@ cdecl -arch=win64 ?wait_for@_Condition_variable@details@Concurrency@@QEAA_NAEAVcritical_section@3@I@Z(ptr ptr ptr) _Condition_variable_wait_for @ stub -arch=win32 ?wait_for_multiple@event@Concurrency@@SAIPAPAV12@I_NI@Z @ stub -arch=win64 ?wait_for_multiple@event@Concurrency@@SA_KPEAPEAV12@_K_NI@Z @ cdecl -arch=arm ?what@exception@std@@UBAPBDXZ(ptr) MSVCRT_what_exception diff --git a/dlls/msvcr120/tests/msvcr120.c b/dlls/msvcr120/tests/msvcr120.c index c0a582f..e6c1688 100644 --- a/dlls/msvcr120/tests/msvcr120.c +++ b/dlls/msvcr120/tests/msvcr120.c @@ -28,6 +28,64 @@ #include +/* Emulate a __thiscall */ +#ifdef __i386__ + +#include "pshpack1.h" +struct thiscall_thunk +{ + BYTE pop_eax; /* popl %eax (ret addr) */ + BYTE pop_edx; /* popl %edx (func) */ + BYTE pop_ecx; /* popl %ecx (this) */ + BYTE push_eax; /* pushl %eax */ + WORD jmp_edx; /* jmp *%edx */ +}; +#include "poppack.h" + +static ULONG_PTR (WINAPI *call_thiscall_func1)( void *func, void *this ); +static ULONG_PTR (WINAPI *call_thiscall_func2)( void *func, void *this, const void *a ); +static ULONG_PTR (WINAPI *call_thiscall_func3)( void *func, void *this, const void *a, const void *b ); +static ULONG_PTR (WINAPI *call_thiscall_func2_uint)( void *func, void *this, unsigned int a); + +static void init_thiscall_thunk(void) +{ + struct thiscall_thunk *thunk = VirtualAlloc( NULL, sizeof(*thunk), + MEM_COMMIT, PAGE_EXECUTE_READWRITE ); + thunk->pop_eax = 0x58; /* popl %eax */ + thunk->pop_edx = 0x5a; /* popl %edx */ + thunk->pop_ecx = 0x59; /* popl %ecx */ + thunk->push_eax = 0x50; /* pushl %eax */ + thunk->jmp_edx = 0xe2ff; /* jmp *%edx */ + call_thiscall_func1 = (void *)thunk; + call_thiscall_func2 = (void *)thunk; + call_thiscall_func3 = (void *)thunk; + call_thiscall_func2_uint = (void *)thunk; +} + +#define call_func1(func,_this) call_thiscall_func1(func,_this) +#define call_func2(func,_this,a) call_thiscall_func2(func,_this,(const void*)a) +#define call_func3(func,_this,a,b) call_thiscall_func3(func,_this,(const void*)a,(const void*)b) +#define call_func2_uint(func,_this,a) call_thiscall_func2_uint(func,_this,a) + +#else + +#define init_thiscall_thunk() +#define call_func1(func,_this) func(_this) +#define call_func2(func,_this,a) func(_this,a) +#define call_func3(func,_this,a,b) func(_this,a,b) +#define call_func2_uint(func,_this,a) func(_this,a) + +#endif /* __i386__ */ + +#undef __thiscall +#ifdef __i386__ +#define __thiscall __stdcall +#else +#define __thiscall __cdecl +#endif + +typedef unsigned char MSVCRT_bool; + struct MSVCRT_lconv { char* decimal_point; @@ -62,6 +120,48 @@ static char* (CDECL *p_setlocale)(int category, const char* locale); static struct MSVCRT_lconv* (CDECL *p_localeconv)(void); static size_t (CDECL *p_wcstombs_s)(size_t *ret, char* dest, size_t sz, const wchar_t* src, size_t max); +typedef struct cs_queue +{ + struct cs_queue *next; + BOOL free; + int unknown; +} cs_queue; + +typedef struct +{ + ULONG_PTR unk_thread_id; + cs_queue unk_active; + void *unknown[2]; + cs_queue *head; + void *tail; +} critical_section; + +typedef struct cv_queue { + struct cv_queue *next; + BOOL expire; +} cv_queue; + +typedef struct { + cv_queue head; + cv_queue *tail; +} wait_chain; + +typedef struct { + wait_chain *chain; + critical_section lock; +} _Condition_variable; + +static _Condition_variable* (__thiscall *p__Condition_variable_ctor)(_Condition_variable*); +static void (__thiscall *p__Condition_variable_dtor)(_Condition_variable*); +static void (__thiscall *p__Condition_variable_wait)(_Condition_variable*, critical_section*); +static MSVCRT_bool (__thiscall *p__Condition_variable_wait_for)(_Condition_variable*, critical_section*, unsigned int); +static void (__thiscall *p__Condition_variable_notify_one)(_Condition_variable*); +static void (__thiscall *p__Condition_variable_notify_all)(_Condition_variable*); +static void (__thiscall *p_critical_section_ctor)(critical_section*); +static void (__thiscall *p_critical_section_dtor)(critical_section*); +static void (__thiscall *p_critical_section_lock)(critical_section*); +static void (__thiscall *p_critical_section_unlock)(critical_section*); + static BOOL init(void) { HMODULE module; @@ -76,6 +176,51 @@ static BOOL init(void) p_setlocale = (void*)GetProcAddress(module, "setlocale"); p_localeconv = (void*)GetProcAddress(module, "localeconv"); p_wcstombs_s = (void*)GetProcAddress(module, "wcstombs_s"); + + if(sizeof(void*) == 8) + { + p__Condition_variable_ctor = (void*)GetProcAddress(module, "??0_Condition_variable@details@Concurrency@@QEAA@XZ"); + p__Condition_variable_dtor = (void*)GetProcAddress(module, "??1_Condition_variable@details@Concurrency@@QEAA@XZ"); + p__Condition_variable_wait = (void*)GetProcAddress(module, "?wait@_Condition_variable@details@Concurrency@@QEAAXAEAVcritical_section@3@@Z"); + p__Condition_variable_wait_for = (void*)GetProcAddress(module, "?wait_for@_Condition_variable@details@Concurrency@@QEAA_NAEAVcritical_section@3@I@Z"); + p__Condition_variable_notify_one = (void*)GetProcAddress(module, "?notify_one@_Condition_variable@details@Concurrency@@QEAAXXZ"); + p__Condition_variable_notify_all = (void*)GetProcAddress(module, "?notify_all@_Condition_variable@details@Concurrency@@QEAAXXZ"); + + p_critical_section_ctor = (void*)GetProcAddress(module, "??0critical_section@Concurrency@@QEAA@XZ"); + p_critical_section_dtor = (void*)GetProcAddress(module, "??1critical_section@Concurrency@@QEAA@XZ"); + p_critical_section_lock = (void*)GetProcAddress(module, "?lock@critical_section@Concurrency@@QEAAXXZ"); + p_critical_section_unlock = (void*)GetProcAddress(module, "?unlock@critical_section@Concurrency@@QEAAXXZ"); + } + else + { +#ifdef __arm__ + p__Condition_variable_ctor = (void*)GetProcAddress(module, "??0_Condition_variable@details@Concurrency@@QAA@XZ"); + p__Condition_variable_dtor = (void*)GetProcAddress(module, "??1_Condition_variable@details@Concurrency@@QAA@XZ"); + p__Condition_variable_wait = (void*)GetProcAddress(module, "?wait@_Condition_variable@details@Concurrency@@QAAXAAVcritical_section@3@@Z"); + p__Condition_variable_wait_for = (void*)GetProcAddress(module, "?wait_for@_Condition_variable@details@Concurrency@@QAA_NAAVcritical_section@3@I@Z"); + p__Condition_variable_notify_one = (void*)GetProcAddress(module, "?notify_one@_Condition_variable@details@Concurrency@@QAAXXZ"); + p__Condition_variable_notify_all = (void*)GetProcAddress(module, "?notify_all@_Condition_variable@details@Concurrency@@QAAXXZ"); + + p_critical_section_ctor = (void*)GetProcAddress(module, "??0critical_section@Concurrency@@QAA@XZ"); + p_critical_section_dtor = (void*)GetProcAddress(module, "??1critical_section@Concurrency@@QAA@XZ"); + p_critical_section_lock = (void*)GetProcAddress(module, "?lock@critical_section@Concurrency@@QAAXXZ"); + p_critical_section_unlock = (void*)GetProcAddress(module, "?unlock@critical_section@Concurrency@@QAAXXZ"); +#else + p__Condition_variable_ctor = (void*)GetProcAddress(module, "??0_Condition_variable@details@Concurrency@@QAE@XZ"); + p__Condition_variable_dtor = (void*)GetProcAddress(module, "??1_Condition_variable@details@Concurrency@@QAE@XZ"); + p__Condition_variable_wait = (void*)GetProcAddress(module, "?wait@_Condition_variable@details@Concurrency@@QAEXAAVcritical_section@3@@Z"); + p__Condition_variable_wait_for = (void*)GetProcAddress(module, "?wait_for@_Condition_variable@details@Concurrency@@QAE_NAAVcritical_section@3@I@Z"); + p__Condition_variable_notify_one = (void*)GetProcAddress(module, "?notify_one@_Condition_variable@details@Concurrency@@QAEXXZ"); + p__Condition_variable_notify_all = (void*)GetProcAddress(module, "?notify_all@_Condition_variable@details@Concurrency@@QAEXXZ"); + + p_critical_section_ctor = (void*)GetProcAddress(module, "??0critical_section@Concurrency@@QAE@XZ"); + p_critical_section_dtor = (void*)GetProcAddress(module, "??1critical_section@Concurrency@@QAE@XZ"); + p_critical_section_lock = (void*)GetProcAddress(module, "?lock@critical_section@Concurrency@@QAEXXZ"); + p_critical_section_unlock = (void*)GetProcAddress(module, "?unlock@critical_section@Concurrency@@QAEXXZ"); +#endif + } + init_thiscall_thunk(); + return TRUE; } @@ -148,8 +293,140 @@ static void test_lconv(void) test_lconv_helper(locstrs[i]); } +static critical_section buf_cs; +static _Condition_variable buf_not_empty, buf_not_full; +static long buf_len = 0, produced = 0, consumed = 0; + +#define NUM_PRODUCER 5 +#define NUM_CONSUMER 5 +#define BUFFER_SIZE 5 +#define MAX_PRODUCE 20000 + +static DWORD WINAPI producer_thread(LPVOID broadcast) +{ + while(1) { + call_func1(p_critical_section_lock, &buf_cs); + + if(produced == MAX_PRODUCE) { + call_func1(p_critical_section_unlock, &buf_cs); + break; + } + + ok(buf_len >= 0 && buf_len <= BUFFER_SIZE, "buffer overflowed\n"); + if(buf_len == BUFFER_SIZE) { + do { + call_func2(p__Condition_variable_wait, &buf_not_full, &buf_cs); + } while(buf_len == BUFFER_SIZE); + } + + if(produced == MAX_PRODUCE) { + call_func1(p_critical_section_unlock, &buf_cs); + break; + } + + ++ buf_len; + ++ produced; + + if(broadcast) + call_func1(p__Condition_variable_notify_all, &buf_not_empty); + else + call_func1(p__Condition_variable_notify_one, &buf_not_empty); + call_func1(p_critical_section_unlock, &buf_cs); + } + return 0; +} + +static DWORD WINAPI consumer_thread(LPVOID broadcast) +{ + while(1) { + call_func1(p_critical_section_lock, &buf_cs); + + if(consumed == MAX_PRODUCE) { + ok(buf_len == 0, "buffer has remain\n"); + /* wake up rest of consumers */ + call_func1(p__Condition_variable_notify_all, &buf_not_empty); + call_func1(p_critical_section_unlock, &buf_cs); + break; + } + + ok(buf_len >= 0 && buf_len <= BUFFER_SIZE, "buffer overflowed\n"); + if(buf_len == 0) { + do { + call_func2(p__Condition_variable_wait, &buf_not_empty, &buf_cs); + } while(buf_len == 0); + } + + /* check if test finished */ + if(consumed == MAX_PRODUCE) { + ok(buf_len == 0, "buffer has remain\n"); + /* wake up rest of consumers */ + call_func1(p__Condition_variable_notify_all, &buf_not_empty); + call_func1(p_critical_section_unlock, &buf_cs); + break; + } + + -- buf_len; + ++ consumed; + + if(broadcast) + call_func1(p__Condition_variable_notify_all, &buf_not_full); + else + call_func1(p__Condition_variable_notify_one, &buf_not_full); + call_func1(p_critical_section_unlock, &buf_cs); + } + return 0; +} + +static void test__Condition_variable(void) +{ + int i; + MSVCRT_bool wake; + HANDLE t[NUM_PRODUCER + NUM_CONSUMER]; + + call_func1(p_critical_section_ctor, &buf_cs); + call_func1(p__Condition_variable_ctor, &buf_not_empty); + call_func1(p__Condition_variable_ctor, &buf_not_full); + + /* signal */ + produced = consumed = 0; + for(i = 0; i < NUM_PRODUCER; ++ i) + t[i] = CreateThread(0, 0, producer_thread, (LPVOID)0, 0, 0); + for(i = NUM_PRODUCER; i < NUM_PRODUCER + NUM_CONSUMER; ++ i) + t[i] = CreateThread(0, 0, consumer_thread, (LPVOID)0, 0, 0); + + WaitForMultipleObjects(NUM_PRODUCER + NUM_CONSUMER, t, TRUE, INFINITE); + ok(produced == consumed, "testing notify: produced != consumed, (%ld, %ld)\n", produced, consumed); + + for(i = 0; i < NUM_PRODUCER + NUM_CONSUMER; ++ i) CloseHandle(t[i]); + + /* broadcast */ + produced = consumed = 0; + for(i = 0; i < NUM_PRODUCER; ++ i) + t[i] = CreateThread(0, 0, producer_thread, (LPVOID)1, 0, 0); + for(i = NUM_PRODUCER; i < NUM_PRODUCER + NUM_CONSUMER; ++ i) + t[i] = CreateThread(0, 0, consumer_thread, (LPVOID)1, 0, 0); + + WaitForMultipleObjects(NUM_PRODUCER + NUM_CONSUMER, t, TRUE, INFINITE); + ok(produced == consumed, "testing notify: produced != consumed, (%ld, %ld)\n", produced, consumed); + + for(i = 0; i < NUM_PRODUCER + NUM_CONSUMER; ++ i) CloseHandle(t[i]); + + /* a quick sanity test on timed wait */ + for(i = 0; i < 200; i += 10) { + call_func1(p_critical_section_lock, &buf_cs); + wake = (MSVCRT_bool)call_func3(p__Condition_variable_wait_for, &buf_not_empty, &buf_cs, i); + ok(wake == FALSE, "_Condition_variable not supposed to wake\n"); + call_func1(p_critical_section_unlock, &buf_cs); + } + + call_func1(p_critical_section_dtor, &buf_cs); + call_func1(p__Condition_variable_dtor, &buf_not_empty); + call_func1(p__Condition_variable_dtor, &buf_not_full); +} + START_TEST(msvcr120) { if (!init()) return; test_lconv(); + test__Condition_variable(); } diff --git a/dlls/msvcrt/lock.c b/dlls/msvcrt/lock.c index 6d903dc..1c48f43 100644 --- a/dlls/msvcrt/lock.c +++ b/dlls/msvcrt/lock.c @@ -539,6 +539,177 @@ void __thiscall critical_section_scoped_lock_dtor(critical_section_scoped_lock * } #endif +#if _MSVCR_VER >= 110 +typedef struct cv_queue { + struct cv_queue *next; + BOOL expire; +} cv_queue; + +typedef struct { + cv_queue head; + cv_queue *tail; +} wait_chain; + +typedef struct { + wait_chain *chain; + critical_section lock; +} _Condition_variable; + +/* ??0_Condition_variable@details@Concurrency@@QEAA@XZ */ +/* ??0_Condition_variable@details@Concurrency@@QAE@XZ */ +/* ??0_Condition_variable@details@Concurrency@@QAA@XZ */ +DEFINE_THISCALL_WRAPPER(_Condition_variable_ctor, 4) +_Condition_variable* __thiscall _Condition_variable_ctor(_Condition_variable *this) +{ + TRACE("(%p)\n", this); + + memset(this, 0, sizeof(_Condition_variable)); + if(!(this->chain = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(wait_chain)))) { + throw_bad_alloc("bad allocation"); + } + this->chain->tail = &this->chain->head; + /* share the global keyed event with critical_section */ + critical_section_ctor(&this->lock); + return this; +} + +/* ??1_Condition_variable@details@Concurrency@@QEAA@XZ */ +/* ??1_Condition_variable@details@Concurrency@@QAE@XZ */ +/* ??1_Condition_variable@details@Concurrency@@QAA@XZ */ +DEFINE_THISCALL_WRAPPER(_Condition_variable_dtor, 4) +void __thiscall _Condition_variable_dtor(_Condition_variable *this) +{ + TRACE("(%p)\n", this); + + HeapFree(GetProcessHeap(), 0, this->chain); + critical_section_dtor(&this->lock); +} + +/* ?wait@_Condition_variable@details@Concurrency@@QEAAXAEAVcritical_section@3@@Z */ +/* ?wait@_Condition_variable@details@Concurrency@@QAEXAAVcritical_section@3@@Z */ +/* ?wait@_Condition_variable@details@Concurrency@@QAAXAAVcritical_section@3@@Z */ +DEFINE_THISCALL_WRAPPER(_Condition_variable_wait, 8) +void __thiscall _Condition_variable_wait(_Condition_variable *this, critical_section *lck) +{ + cv_queue q; + + TRACE("(%p, %p)\n", this, lck); + + memset(&q, 0, sizeof(cv_queue)); + + critical_section_lock(&this->lock); + this->chain->tail->next = &q; + this->chain->tail = &q; + critical_section_unlock(&this->lock); + + critical_section_unlock(lck); + NtWaitForKeyedEvent(keyed_event, &q, 0, NULL); + critical_section_lock(lck); +} + +/* ?wait_for@_Condition_variable@details@Concurrency@@QEAA_NAEAVcritical_section@3@I@Z */ +/* ?wait_for@_Condition_variable@details@Concurrency@@QAE_NAAVcritical_section@3@I@Z */ +/* ?wait_for@_Condition_variable@details@Concurrency@@QAA_NAAVcritical_section@3@I@Z */ +DEFINE_THISCALL_WRAPPER(_Condition_variable_wait_for, 12) +MSVCRT_bool __thiscall _Condition_variable_wait_for(_Condition_variable *this, critical_section *lck, unsigned int timeout) +{ + LARGE_INTEGER to; + NTSTATUS status; + FILETIME ft; + cv_queue *q; + + TRACE("(%p %p %d)\n", this, lck, timeout); + + if(!(q = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(cv_queue)))) { + throw_bad_alloc("bad allocation"); + } + + critical_section_lock(&this->lock); + this->chain->tail->next = q; + this->chain->tail = q; + critical_section_unlock(&this->lock); + + critical_section_unlock(lck); + + 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->expire, TRUE)) { + critical_section_lock(lck); + return FALSE; + } + else + NtWaitForKeyedEvent(keyed_event, q, 0, 0); + } + + HeapFree(GetProcessHeap(), 0, q); + critical_section_lock(lck); + return TRUE; +} + +/* ?notify_one@_Condition_variable@details@Concurrency@@QEAAXXZ */ +/* ?notify_one@_Condition_variable@details@Concurrency@@QAEXXZ */ +/* ?notify_one@_Condition_variable@details@Concurrency@@QAAXXZ */ +DEFINE_THISCALL_WRAPPER(_Condition_variable_notify_one, 4) +void __thiscall _Condition_variable_notify_one(_Condition_variable *this) +{ + cv_queue *node; + + TRACE("(%p)\n", this); + + while(1) { + critical_section_lock(&this->lock); + node = this->chain->head.next; + if(!node) { + critical_section_unlock(&this->lock); + return; + } + this->chain->head.next = + this->chain->head.next->next; + if(node == this->chain->tail) + this->chain->tail = &this->chain->head; + critical_section_unlock(&this->lock); + + if(!InterlockedExchange(&node->expire, TRUE)) { + NtReleaseKeyedEvent(keyed_event, node, 0, NULL); + return; + } else { + HeapFree(GetProcessHeap(), 0, node); + } + } +} + +/* ?notify_all@_Condition_variable@details@Concurrency@@QEAAXXZ */ +/* ?notify_all@_Condition_variable@details@Concurrency@@QAEXXZ */ +/* ?notify_all@_Condition_variable@details@Concurrency@@QAAXXZ */ +DEFINE_THISCALL_WRAPPER(_Condition_variable_notify_all, 4) +void __thiscall _Condition_variable_notify_all(_Condition_variable *this) +{ + cv_queue *ptr; + + TRACE("(%p)\n", this); + + critical_section_lock(&this->lock); + ptr = this->chain->head.next; + this->chain->head.next = NULL; + this->chain->tail = &this->chain->head; + critical_section_unlock(&this->lock); + + while(ptr) { + if(!InterlockedExchange(&ptr->expire, TRUE)) { + NtReleaseKeyedEvent(keyed_event, ptr, 0, NULL); + ptr = ptr->next; + } else { + cv_queue *stale = ptr; + ptr = ptr->next; + HeapFree(GetProcessHeap(), 0, stale); + } + } +} +#endif + /********************************************************************** * msvcrt_free_locks (internal) * -- 2.2.1