msvcp120: Implement _Cnd_* functions.
Signed-off-by: Daniel Lehman <dlehman@esri.com> Signed-off-by: Piotr Caban <piotr@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
c2533ecb73
commit
df096e6b60
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -134,6 +134,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
|
|||
free_io();
|
||||
free_locale();
|
||||
free_lockit();
|
||||
free_misc();
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue