From 5aaeefbfbb7cedf66efc20339f3c98119b590912 Mon Sep 17 00:00:00 2001 From: Daniel Lehman Date: Tue, 3 Nov 2015 17:10:16 -0800 Subject: [PATCH] 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 | 161 ++++++++++++++++++++++++++++++++++++ dlls/msvcp120_app/msvcp120_app.spec | 12 +-- dlls/msvcp90/misc.c | 101 ++++++++++++++++++++++ dlls/msvcp90/msvcp90.h | 2 + dlls/msvcp90/msvcp_main.c | 1 + 7 files changed, 283 insertions(+), 18 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..8991420 100644 --- a/dlls/msvcp120/tests/msvcp120.c +++ b/dlls/msvcp120/tests/msvcp120.c @@ -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*); +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,126 @@ static void test_thrd(void) ok(!CloseHandle(ta.hnd), "handle %p not closed\n", ta.hnd); } +struct cndmtx +{ + _Cnd_t cnd; + _Mtx_t mtx; + int go; +}; + +static int __cdecl cnd_bcast_thread(void *arg) +{ + struct cndmtx *cm = arg; + int r; + + p__Mtx_lock(&cm->mtx); + while(!cm->go) { + r = p__Cnd_wait(&cm->cnd, &cm->mtx); + ok(!r, "failed to wait\n"); + } + p__Mtx_unlock(&cm->mtx); + return 0; +} + +static int __cdecl cnd_signal_thread(void *arg) +{ + struct cndmtx *cm = arg; + int r; + + p__Mtx_lock(&cm->mtx); + cm->go = 1; + p__Mtx_unlock(&cm->mtx); + r = p__Cnd_signal(&cm->cnd); + ok(!r, "failed to signal\n"); + return 0; +} + +#define NUM_THREADS 10 +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); + 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); + } + + /* test broadcast */ + cm.cnd = cnd; + cm.mtx = mtx; + cm.go = 0; + for(i = 0; i < NUM_THREADS; i++) + p__Thrd_create(&threads[i], cnd_bcast_thread, (void*)&cm); + + if (0) /* causes crash in later _Cnd_* calls on Windows */ + p__Cnd_destroy(&cm.cnd); + + p__Mtx_lock(&mtx); + cm.go = 1; + p__Mtx_unlock(&mtx); + r = p__Cnd_broadcast(&cnd); + ok(!r, "failed to broadcast\n"); + for(i = 0; i < NUM_THREADS; i++) + p__Thrd_join(threads[i], NULL); + + /* test signal */ + cm.go = 0; + p__Thrd_create(&threads[0], cnd_signal_thread, (void*)&cm); + p__Mtx_lock(&mtx); + while(!cm.go) { + r = p__Cnd_wait(&cnd, &mtx); + ok(!r, "failed to wait\n"); + } + p__Mtx_unlock(&mtx); + p__Thrd_join(threads[0], NULL); + + /* test time out on purpose */ + p_xtime_get(&xt, 1); + xt.sec += 2; + + p__Mtx_lock(&mtx); + p_xtime_get(&before, 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 > 2000 - TIMEDELTA, "got %d\n", diff); + + /* test timed wait that succeeds */ + cm.go = 0; + p__Thrd_create(&threads[0], cnd_signal_thread, (void*)&cm); + p_xtime_get(&xt, 1); + xt.sec += 5; + p__Mtx_lock(&mtx); + while(!cm.go) { + r = p__Cnd_timedwait(&cnd, &mtx, &xt); + ok(!r, "failed timed wait\n"); + } + p__Mtx_unlock(&mtx); + p__Thrd_join(threads[0], NULL); + + p__Cnd_destroy(&cnd); + p__Mtx_destroy(&mtx); +} + START_TEST(msvcp120) { if(!init()) return; @@ -1285,6 +1445,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..0ec520a 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,98 @@ 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; +} + +void __cdecl _Cnd_destroy(_Cnd_t *cnd) +{ + MSVCRT_operator_delete(*cnd); +} + +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; +} #endif #if _MSVCP_VER == 100 @@ -684,6 +777,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