Commit df096e6b authored by Daniel Lehman's avatar Daniel Lehman Committed by Alexandre Julliard

msvcp120: Implement _Cnd_* functions.

parent c2533ecb
......@@ -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;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment