From 4e4723f1fcd2163e594b67985b6a356458acf4d8 Mon Sep 17 00:00:00 2001 From: Daniel Lehman Date: Tue, 3 Nov 2015 17:10:16 -0800 Subject: [PATCH 1/2] msvcp120: implement _Cnd_* functions a rip-off of the RtlSleepConditionVariableCS implementation that uses _Mtx_t for locks instead of CS Signed-off-by: Daniel Lehman --- dlls/msvcp110/msvcp110.spec | 12 +-- dlls/msvcp120/msvcp120.spec | 12 +-- dlls/msvcp120/tests/msvcp120.c | 184 +++++++++++++++++++++++++++++++++++- dlls/msvcp120_app/msvcp120_app.spec | 12 +-- dlls/msvcp90/misc.c | 104 ++++++++++++++++++++ dlls/msvcp90/msvcp90.h | 2 + dlls/msvcp90/msvcp_main.c | 1 + 7 files changed, 308 insertions(+), 19 deletions(-) diff --git a/dlls/msvcp110/msvcp110.spec b/dlls/msvcp110/msvcp110.spec index dab4cd2..a77a5c8 100644 --- a/dlls/msvcp110/msvcp110.spec +++ b/dlls/msvcp110/msvcp110.spec @@ -3729,15 +3729,15 @@ @ cdecl -arch=win64 ?xsputn@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MEAA_JPEB_W_J@Z(ptr ptr long) basic_streambuf_wchar_xsputn @ cdecl _Call_once(ptr ptr) @ cdecl _Call_onceEx(ptr ptr ptr) -@ stub _Cnd_broadcast -@ stub _Cnd_destroy +@ cdecl _Cnd_broadcast(ptr) +@ cdecl _Cnd_destroy(ptr) @ stub _Cnd_do_broadcast_at_thread_exit -@ stub _Cnd_init +@ cdecl _Cnd_init(ptr) @ stub _Cnd_register_at_thread_exit -@ stub _Cnd_signal -@ stub _Cnd_timedwait +@ cdecl _Cnd_signal(ptr) +@ cdecl _Cnd_timedwait(ptr ptr ptr) @ stub _Cnd_unregister_at_thread_exit -@ stub _Cnd_wait +@ cdecl _Cnd_wait(ptr ptr) @ stub _Cosh @ extern _Denorm @ stub _Dint diff --git a/dlls/msvcp120/msvcp120.spec b/dlls/msvcp120/msvcp120.spec index 4448764..4668129 100644 --- a/dlls/msvcp120/msvcp120.spec +++ b/dlls/msvcp120/msvcp120.spec @@ -3670,15 +3670,15 @@ @ cdecl -arch=win64 ?xsputn@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MEAA_JPEB_W_J@Z(ptr ptr long) basic_streambuf_wchar_xsputn @ cdecl _Call_once(ptr ptr) @ cdecl _Call_onceEx(ptr ptr ptr) -@ stub _Cnd_broadcast -@ stub _Cnd_destroy +@ cdecl _Cnd_broadcast(ptr) +@ cdecl _Cnd_destroy(ptr) @ stub _Cnd_do_broadcast_at_thread_exit -@ stub _Cnd_init +@ cdecl _Cnd_init(ptr) @ stub _Cnd_register_at_thread_exit -@ stub _Cnd_signal -@ stub _Cnd_timedwait +@ cdecl _Cnd_signal(ptr) +@ cdecl _Cnd_timedwait(ptr ptr ptr) @ stub _Cnd_unregister_at_thread_exit -@ stub _Cnd_wait +@ cdecl _Cnd_wait(ptr ptr) @ stub _Cosh @ extern _Denorm @ stub _Dint diff --git a/dlls/msvcp120/tests/msvcp120.c b/dlls/msvcp120/tests/msvcp120.c index 3f02051..e69405f 100644 --- a/dlls/msvcp120/tests/msvcp120.c +++ b/dlls/msvcp120/tests/msvcp120.c @@ -137,7 +137,7 @@ typedef struct DWORD id; } _Thrd_t; -#define TIMEDELTA 150 /* 150 ms uncertainty allowed */ +#define TIMEDELTA 250 /* 250 ms uncertainty allowed */ typedef int (__cdecl *_Thrd_start_t)(void*); @@ -161,6 +161,24 @@ _Thrd_t __cdecl i386_Thrd_current(void) } #endif +/* mtx */ +typedef void *_Mtx_t; +static int (__cdecl *p__Mtx_init)(_Mtx_t*, int); +static void (__cdecl *p__Mtx_destroy)(_Mtx_t*); +static int (__cdecl *p__Mtx_lock)(_Mtx_t*); +static int (__cdecl *p__Mtx_unlock)(_Mtx_t*); + +/* cnd */ +typedef void *_Cnd_t; + +static int (__cdecl *p__Cnd_init)(_Cnd_t*); +static void (__cdecl *p__Cnd_destroy)(_Cnd_t*); +static int (__cdecl *p__Cnd_wait)(_Cnd_t*, _Mtx_t*); +static int (__cdecl *p__Cnd_timedwait)(_Cnd_t*, _Mtx_t*, const xtime*); +static int (__cdecl *p__Cnd_broadcast)(_Cnd_t*); +static int (__cdecl *p__Cnd_signal)(_Cnd_t*); + + static HMODULE msvcp; #define SETNOFAIL(x,y) x = (void*)GetProcAddress(msvcp,y) #define SET(x,y) do { SETNOFAIL(x,y); ok(x != NULL, "Export '%s' not found\n", y); } while(0) @@ -305,6 +323,28 @@ static BOOL init(void) SET(p__Thrd_join, "_Thrd_join"); + SET(p__Mtx_init, + "_Mtx_init"); + SET(p__Mtx_destroy, + "_Mtx_destroy"); + SET(p__Mtx_lock, + "_Mtx_lock"); + SET(p__Mtx_unlock, + "_Mtx_unlock"); + + SET(p__Cnd_init, + "_Cnd_init"); + SET(p__Cnd_destroy, + "_Cnd_destroy"); + SET(p__Cnd_wait, + "_Cnd_wait"); + SET(p__Cnd_timedwait, + "_Cnd_timedwait"); + SET(p__Cnd_broadcast, + "_Cnd_broadcast"); + SET(p__Cnd_signal, + "_Cnd_signal"); + msvcr = GetModuleHandleA("msvcr120.dll"); p_setlocale = (void*)GetProcAddress(msvcr, "setlocale"); p__setmbcp = (void*)GetProcAddress(msvcr, "_setmbcp"); @@ -1260,6 +1300,147 @@ static void test_thrd(void) ok(!CloseHandle(ta.hnd), "handle %p not closed\n", ta.hnd); } +#define NUM_THREADS 10 +struct cndmtx +{ + HANDLE initialized; + int started; + int thread_no; + + _Cnd_t cnd; + _Mtx_t mtx; + BOOL timed_wait; +}; + +static int __cdecl cnd_wait_thread(void *arg) +{ + struct cndmtx *cm = arg; + int r; + + p__Mtx_lock(&cm->mtx); + + if(InterlockedIncrement(&cm->started) == cm->thread_no) + SetEvent(cm->initialized); + + if(cm->timed_wait) { + xtime xt; + + p_xtime_get(&xt, 1); + xt.sec += 2; + r = p__Cnd_timedwait(&cm->cnd, &cm->mtx, &xt); + ok(!r, "timed wait failed\n"); + } else { + r = p__Cnd_wait(&cm->cnd, &cm->mtx); + ok(!r, "wait failed\n"); + } + + p__Mtx_unlock(&cm->mtx); + return 0; +} + +static void test_cnd(void) +{ + _Thrd_t threads[NUM_THREADS]; + xtime xt, before, after; + MSVCRT_long diff; + struct cndmtx cm; + _Cnd_t cnd; + _Mtx_t mtx; + int r, i; + + r = p__Cnd_init(&cnd); + ok(!r, "failed to init cnd\n"); + + r = p__Mtx_init(&mtx, 0); + ok(!r, "failed to init mtx\n"); + + if (0) /* crash on Windows */ + { + p__Cnd_init(NULL); + p__Cnd_wait(NULL, &mtx); + p__Cnd_wait(&cnd, NULL); + p__Cnd_timedwait(NULL, &mtx, &xt); + p__Cnd_timedwait(&cnd, &mtx, &xt); + } + p__Cnd_destroy(NULL); + + /* test _Cnd_signal/_Cnd_wait */ + cm.initialized = CreateEventW(NULL, FALSE, FALSE, NULL); + cm.started = 0; + cm.thread_no = 1; + cm.cnd = cnd; + cm.mtx = mtx; + cm.timed_wait = FALSE; + p__Thrd_create(&threads[0], cnd_wait_thread, (void*)&cm); + + WaitForSingleObject(cm.initialized, INFINITE); + p__Mtx_lock(&mtx); + p__Mtx_unlock(&mtx); + + r = p__Cnd_signal(&cm.cnd); + ok(!r, "failed to signal\n"); + p__Thrd_join(threads[0], NULL); + + /* test _Cnd_timedwait time out */ + p__Mtx_lock(&mtx); + p_xtime_get(&before, 1); + xt = before; + xt.sec += 1; + r = p__Cnd_timedwait(&cnd, &mtx, &xt); + p_xtime_get(&after, 1); + p__Mtx_unlock(&mtx); + + diff = p__Xtime_diff_to_millis2(&after, &before); + ok(r == 2, "should have timed out\n"); + ok(diff > 1000 - TIMEDELTA, "got %d\n", diff); + + /* test _Cnd_timedwait */ + cm.started = 0; + cm.timed_wait = TRUE; + p__Thrd_create(&threads[0], cnd_wait_thread, (void*)&cm); + + WaitForSingleObject(cm.initialized, INFINITE); + p__Mtx_lock(&mtx); + p__Mtx_unlock(&mtx); + + r = p__Cnd_signal(&cm.cnd); + ok(!r, "failed to signal\n"); + p__Thrd_join(threads[0], NULL); + + /* test _Cnd_broadcast */ + cm.started = 0; + cm.thread_no = NUM_THREADS; + cm.timed_wait = FALSE; + + for(i = 0; i < cm.thread_no; i++) + p__Thrd_create(&threads[i], cnd_wait_thread, (void*)&cm); + + WaitForSingleObject(cm.initialized, INFINITE); + p__Mtx_lock(&mtx); + p__Mtx_unlock(&mtx); + + r = p__Cnd_broadcast(&cnd); + ok(!r, "failed to broadcast\n"); + for(i = 0; i < cm.thread_no; i++) + p__Thrd_join(threads[i], NULL); + + /* test broadcast with _Cnd_destroy */ + cm.started = 0; + for(i = 0; i < cm.thread_no; i++) + p__Thrd_create(&threads[i], cnd_wait_thread, (void*)&cm); + + WaitForSingleObject(cm.initialized, INFINITE); + p__Mtx_lock(&mtx); + p__Mtx_unlock(&mtx); + + p__Cnd_destroy(&cnd); + for(i = 0; i < cm.thread_no; i++) + p__Thrd_join(threads[i], NULL); + + p__Mtx_destroy(&mtx); + CloseHandle(cm.initialized); +} + START_TEST(msvcp120) { if(!init()) return; @@ -1285,6 +1466,7 @@ START_TEST(msvcp120) test_tr2_sys__Last_write_time(); test_thrd(); + test_cnd(); FreeLibrary(msvcp); } diff --git a/dlls/msvcp120_app/msvcp120_app.spec b/dlls/msvcp120_app/msvcp120_app.spec index 7217a46..93a9fbf 100644 --- a/dlls/msvcp120_app/msvcp120_app.spec +++ b/dlls/msvcp120_app/msvcp120_app.spec @@ -3670,15 +3670,15 @@ @ cdecl -arch=win64 ?xsputn@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MEAA_JPEB_W_J@Z(ptr ptr long) msvcp120.?xsputn@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MEAA_JPEB_W_J@Z @ cdecl _Call_once(ptr ptr) msvcp120._Call_once @ cdecl _Call_onceEx(ptr ptr ptr) msvcp120._Call_onceEx -@ stub _Cnd_broadcast -@ stub _Cnd_destroy +@ cdecl _Cnd_broadcast(ptr) msvcp120._Cnd_broadcast +@ cdecl _Cnd_destroy(ptr) msvcp120._Cnd_destroy @ stub _Cnd_do_broadcast_at_thread_exit -@ stub _Cnd_init +@ cdecl _Cnd_init(ptr) msvcp120._Cnd_init @ stub _Cnd_register_at_thread_exit -@ stub _Cnd_signal -@ stub _Cnd_timedwait +@ cdecl _Cnd_signal(ptr) msvcp120._Cnd_signal +@ cdecl _Cnd_timedwait(ptr ptr ptr) msvcp120._Cnd_timedwait @ stub _Cnd_unregister_at_thread_exit -@ stub _Cnd_wait +@ cdecl _Cnd_wait(ptr ptr) msvcp120._Cnd_wait @ stub _Cosh @ extern _Denorm msvcp120._Denorm @ stub _Dint diff --git a/dlls/msvcp90/misc.c b/dlls/msvcp90/misc.c index 25b3f23..b2adc72 100644 --- a/dlls/msvcp90/misc.c +++ b/dlls/msvcp90/misc.c @@ -25,6 +25,7 @@ #include "windef.h" #include "winbase.h" +#include "winternl.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(msvcp); @@ -527,6 +528,101 @@ critical_section* __cdecl _Mtx_getconcrtcs(_Mtx_t *mtx) { return &(*mtx)->cs; } + +static inline LONG interlocked_dec_if_nonzero( LONG *dest ) +{ + LONG val, tmp; + for (val = *dest;; val = tmp) + { + if (!val || (tmp = InterlockedCompareExchange( dest, val - 1, val )) == val) + break; + } + return val; +} + +#define CND_TIMEDOUT 2 + +typedef struct +{ + CONDITION_VARIABLE cv; +} *_Cnd_t; + +static HANDLE keyed_event; + +int __cdecl _Cnd_init(_Cnd_t *cnd) +{ + *cnd = MSVCRT_operator_new(sizeof(**cnd)); + InitializeConditionVariable(&(*cnd)->cv); + + if(!keyed_event) { + HANDLE event; + + NtCreateKeyedEvent(&event, GENERIC_READ|GENERIC_WRITE, NULL, 0); + if(InterlockedCompareExchangePointer(&keyed_event, event, NULL) != NULL) + NtClose(event); + } + + return 0; +} + +int __cdecl _Cnd_wait(_Cnd_t *cnd, _Mtx_t *mtx) +{ + CONDITION_VARIABLE *cv = &(*cnd)->cv; + + InterlockedExchangeAdd( (LONG *)&cv->Ptr, 1 ); + _Mtx_unlock(mtx); + + NtWaitForKeyedEvent(keyed_event, &cv->Ptr, FALSE, NULL); + + _Mtx_lock(mtx); + return 0; +} + +int __cdecl _Cnd_timedwait(_Cnd_t *cnd, _Mtx_t *mtx, const xtime *xt) +{ + CONDITION_VARIABLE *cv = &(*cnd)->cv; + LARGE_INTEGER timeout; + NTSTATUS status; + + InterlockedExchangeAdd( (LONG *)&cv->Ptr, 1 ); + _Mtx_unlock(mtx); + + timeout.QuadPart = (ULONGLONG)_Xtime_diff_to_millis(xt) * -10000; + status = NtWaitForKeyedEvent(keyed_event, &cv->Ptr, FALSE, &timeout); + if (status) + { + if (!interlocked_dec_if_nonzero( (LONG *)&cv->Ptr )) + status = NtWaitForKeyedEvent( keyed_event, &cv->Ptr, FALSE, NULL ); + } + + _Mtx_lock(mtx); + return status ? CND_TIMEDOUT : 0; +} + +int __cdecl _Cnd_broadcast(_Cnd_t *cnd) +{ + CONDITION_VARIABLE *cv = &(*cnd)->cv; + LONG val = InterlockedExchange( (LONG *)&cv->Ptr, 0 ); + while (val-- > 0) + NtReleaseKeyedEvent( keyed_event, &cv->Ptr, FALSE, NULL ); + return 0; +} + +int __cdecl _Cnd_signal(_Cnd_t *cnd) +{ + CONDITION_VARIABLE *cv = &(*cnd)->cv; + if (interlocked_dec_if_nonzero( (LONG *)&cv->Ptr )) + NtReleaseKeyedEvent( keyed_event, &cv->Ptr, FALSE, NULL ); + return 0; +} + +void __cdecl _Cnd_destroy(_Cnd_t *cnd) +{ + if(cnd) { + _Cnd_broadcast(cnd); + MSVCRT_operator_delete(*cnd); + } +} #endif #if _MSVCP_VER == 100 @@ -684,6 +780,14 @@ void init_misc(void *base) #endif } +void free_misc(void) +{ +#if _MSVCP_VER >= 110 + if(keyed_event) + NtClose(keyed_event); +#endif +} + #if _MSVCP_VER >= 110 typedef struct { diff --git a/dlls/msvcp90/msvcp90.h b/dlls/msvcp90/msvcp90.h index aa5c23a..e098c3d 100644 --- a/dlls/msvcp90/msvcp90.h +++ b/dlls/msvcp90/msvcp90.h @@ -629,3 +629,5 @@ static inline int mbstowcs_wrapper( size_t *ret, wchar_t *wcs, size_t size, cons #define mbstowcs_s( ret, wcs, size, mbs, count ) mbstowcs_wrapper( ret, wcs, size, mbs, count ) #define hypotf( x, y ) ((float)hypot( (double)(x), (double)(y) )) #endif + +void free_misc(void); diff --git a/dlls/msvcp90/msvcp_main.c b/dlls/msvcp90/msvcp_main.c index 13d3ff2..7ccf6f4 100644 --- a/dlls/msvcp90/msvcp_main.c +++ b/dlls/msvcp90/msvcp_main.c @@ -134,6 +134,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) free_io(); free_locale(); free_lockit(); + free_misc(); break; } -- 1.9.5