diff --git a/dlls/msvcr120/tests/msvcr120.c b/dlls/msvcr120/tests/msvcr120.c index a3161266db4..26b363de6c5 100644 --- a/dlls/msvcr120/tests/msvcr120.c +++ b/dlls/msvcr120/tests/msvcr120.c @@ -162,6 +162,27 @@ typedef struct { Context *ctx; } _Context; +typedef struct _StructuredTaskCollection +{ + void *unk1; + unsigned int unk2; + void *unk3; + Context *context; + volatile LONG count; + volatile LONG finished; + void *unk4; + void *event; +} _StructuredTaskCollection; + +typedef struct _UnrealizedChore +{ + const vtable_ptr *vtable; + void (__cdecl *proc)(struct _UnrealizedChore*); + struct _StructuredTaskCollection *task_collection; + void (__cdecl *proc_wrapper)(struct _UnrealizedChore*); + void *unk[6]; +} _UnrealizedChore; + 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); @@ -225,6 +246,11 @@ static void (__thiscall *p__Condition_variable_notify_all)(_Condition_variable*) static Context* (__cdecl *p_Context_CurrentContext)(void); static _Context* (__cdecl *p__Context__CurrentContext)(_Context*); +static _StructuredTaskCollection* (__thiscall *p__StructuredTaskCollection_ctor)(_StructuredTaskCollection*, void*); +static void (__thiscall *p__StructuredTaskCollection_dtor)(_StructuredTaskCollection*); +static void (__thiscall *p__StructuredTaskCollection__Schedule)(_StructuredTaskCollection*, _UnrealizedChore*); +static int (__stdcall *p__StructuredTaskCollection__RunAndWait)(_StructuredTaskCollection*, _UnrealizedChore*); + #define SETNOFAIL(x,y) x = (void*)GetProcAddress(module,y) #define SET(x,y) do { SETNOFAIL(x,y); ok(x != NULL, "Export '%s' not found\n", y); } while(0) @@ -279,6 +305,14 @@ static BOOL init(void) SET(p_towctrans, "towctrans"); SET(p__Context__CurrentContext, "?_CurrentContext@_Context@details@Concurrency@@SA?AV123@XZ"); if(sizeof(void*) == 8) { /* 64-bit initialization */ + SET(p__StructuredTaskCollection_ctor, + "??0_StructuredTaskCollection@details@Concurrency@@QEAA@PEAV_CancellationTokenState@12@@Z"); + SET(p__StructuredTaskCollection_dtor, + "??1_StructuredTaskCollection@details@Concurrency@@QEAA@XZ"); + SET(p__StructuredTaskCollection__Schedule, + "?_Schedule@_StructuredTaskCollection@details@Concurrency@@QEAAXPEAV_UnrealizedChore@23@@Z"); + SET(p__StructuredTaskCollection__RunAndWait, + "?_RunAndWait@_StructuredTaskCollection@details@Concurrency@@QEAA?AW4_TaskCollectionStatus@23@PEAV_UnrealizedChore@23@@Z"); SET(p_critical_section_ctor, "??0critical_section@Concurrency@@QEAA@XZ"); SET(p_critical_section_dtor, @@ -313,6 +347,14 @@ static BOOL init(void) "?CurrentContext@Context@Concurrency@@SAPEAV12@XZ"); } else { #ifdef __arm__ + SET(p__StructuredTaskCollection_ctor, + "??0_StructuredTaskCollection@details@Concurrency@@QAA@PAV_CancellationTokenState@12@@Z"); + SET(p__StructuredTaskCollection_dtor, + "??1_StructuredTaskCollection@details@Concurrency@@QAA@XZ"); + SET(p__StructuredTaskCollection__Schedule, + "?_Schedule@_StructuredTaskCollection@details@Concurrency@@QAAXPAV_UnrealizedChore@23@@Z"); + SET(p__StructuredTaskCollection__RunAndWait, + "?_RunAndWait@_StructuredTaskCollection@details@Concurrency@@QAA?AW4_TaskCollectionStatus@23@PAV_UnrealizedChore@23@@Z"); SET(p_critical_section_ctor, "??0critical_section@Concurrency@@QAA@XZ"); SET(p_critical_section_dtor, @@ -344,6 +386,14 @@ static BOOL init(void) SET(p__Condition_variable_notify_all, "?notify_all@_Condition_variable@details@Concurrency@@QAAXXZ"); #else + SET(p__StructuredTaskCollection_ctor, + "??0_StructuredTaskCollection@details@Concurrency@@QAE@PAV_CancellationTokenState@12@@Z"); + SET(p__StructuredTaskCollection_dtor, + "??1_StructuredTaskCollection@details@Concurrency@@QAE@XZ"); + SET(p__StructuredTaskCollection__Schedule, + "?_Schedule@_StructuredTaskCollection@details@Concurrency@@QAEXPAV_UnrealizedChore@23@@Z"); + SET(p__StructuredTaskCollection__RunAndWait, + "?_RunAndWait@_StructuredTaskCollection@details@Concurrency@@QAG?AW4_TaskCollectionStatus@23@PAV_UnrealizedChore@23@@Z"); SET(p_critical_section_ctor, "??0critical_section@Concurrency@@QAE@XZ"); SET(p_critical_section_dtor, @@ -1270,6 +1320,224 @@ static void test_CurrentContext(void) ok(ret == &_ctx, "expected %p, got %p\n", &_ctx, ret); } +static void _UnrealizedChore_ctor(_UnrealizedChore *chore, void (__cdecl *proc)(_UnrealizedChore*)) +{ + memset(chore, 0, sizeof(*chore)); + chore->proc = proc; +} + +struct chore +{ + _UnrealizedChore chore; + BOOL executed; + HANDLE start_event; + HANDLE set_event; + HANDLE wait_event; + DWORD main_tid; +}; + +static void __cdecl chore_proc(_UnrealizedChore *_this) +{ + struct chore *chore = CONTAINING_RECORD(_this, struct chore, chore); + + if (chore->start_event) + { + DWORD ret = WaitForSingleObject(chore->start_event, 5000); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %ld\n", ret); + } + + ok(!chore->executed, "Chore was already executed\n"); + + if (chore->main_tid) + { + ok(GetCurrentThreadId() == chore->main_tid, + "Main chore is not running on the main thread: 0x%lx != 0x%lx\n", + GetCurrentThreadId(), chore->main_tid); + } + + if (!chore->wait_event) + chore->executed = TRUE; + + if (chore->set_event) + { + BOOL b = SetEvent(chore->set_event); + ok(b, "SetEvent failed\n"); + } + + if (chore->wait_event) + { + DWORD ret = WaitForSingleObject(chore->wait_event, 5000); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %ld\n", ret); + chore->executed = TRUE; + } +} + +static void chore_ctor(struct chore *chore) +{ + memset(chore, 0, sizeof(*chore)); + _UnrealizedChore_ctor(&chore->chore, chore_proc); +} + +static void test_StructuredTaskCollection(void) +{ + HANDLE chore_start_evt, chore_evt1, chore_evt2; + _StructuredTaskCollection task_coll; + struct chore chore1, chore2; + DWORD main_thread_id; + Context *context; + int status; + DWORD ret; + BOOL b; + + main_thread_id = GetCurrentThreadId(); + context = p_Context_CurrentContext(); + + memset(&task_coll, 0x55, sizeof(task_coll)); + if (!call_func2(p__StructuredTaskCollection_ctor, &task_coll, NULL)) + { + skip("_StructuredTaskCollection constructor not implemented\n"); + return; + } + ok(task_coll.unk2 == 0x1fffffff, + "_StructuredTaskCollection ctor set wrong unk2: 0x%x != 0x1fffffff\n", task_coll.unk2); + ok(task_coll.unk3 == NULL, + "_StructuredTaskCollection ctor set wrong unk3: %p != NULL\n", task_coll.unk3); + ok(task_coll.context == NULL, + "_StructuredTaskCollection ctor set wrong context: %p != NULL\n", task_coll.context); + ok(task_coll.count == 0, + "_StructuredTaskCollection ctor set wrong count: %ld != 0\n", task_coll.count); + ok(task_coll.finished == LONG_MIN, + "_StructuredTaskCollection ctor set wrong finished: %ld != %ld\n", task_coll.finished, LONG_MIN); + ok(task_coll.unk4 == NULL, + "_StructuredTaskCollection ctor set wrong unk4: %p != NULL\n", task_coll.unk4); + + chore_start_evt = CreateEventW(NULL, TRUE, FALSE, NULL); + ok(chore_start_evt != NULL, "CreateEvent failed: 0x%lx\n", GetLastError()); + chore_evt1 = CreateEventW(NULL, FALSE, FALSE, NULL); + ok(chore_evt1 != NULL, "CreateEvent failed: 0x%lx\n", GetLastError()); + chore_evt2 = CreateEventW(NULL, FALSE, FALSE, NULL); + ok(chore_evt2 != NULL, "CreateEvent failed: 0x%lx\n", GetLastError()); + + /* test running chores (only main) */ + chore_ctor(&chore1); + chore1.main_tid = main_thread_id; + + status = p__StructuredTaskCollection__RunAndWait(&task_coll, &chore1.chore); + ok(status == 1, "_StructuredTaskCollection::_RunAndWait failed: %d\n", status); + ok(chore1.executed, "Scheduled chore was not executed\n"); + call_func1(p__StructuredTaskCollection_dtor, &task_coll); + + /* test running chores (main + scheduled) */ + call_func2(p__StructuredTaskCollection_ctor, &task_coll, NULL); + ResetEvent(chore_start_evt); + chore_ctor(&chore1); + chore1.start_event = chore_start_evt; + chore_ctor(&chore2); + chore2.main_tid = main_thread_id; + + call_func2(p__StructuredTaskCollection__Schedule, &task_coll, &chore1.chore); + ok(chore1.chore.task_collection == &task_coll, "Wrong chore #1 task_collection: expected %p, actual %p\n", + &task_coll, chore1.chore.task_collection); + ok(chore1.chore.proc_wrapper != NULL, "Chore #1 proc_wrapper was not set\n"); + ok(task_coll.count == 1, "Wrong chore count: %ld != 1\n", task_coll.count); + ok(task_coll.context == context, "Unexpected context: %p != %p\n", task_coll.context, context); + + b = SetEvent(chore_start_evt); + ok(b, "SetEvent failed\n"); + + status = p__StructuredTaskCollection__RunAndWait(&task_coll, &chore2.chore); + ok(status == 1, "_StructuredTaskCollection::_RunAndWait failed: %d\n", status); + ok(chore1.executed, "Scheduled chore was not executed\n"); + ok(chore2.executed, "Main chore was not executed\n"); + call_func1(p__StructuredTaskCollection_dtor, &task_coll); + + /* test that scheduled chores may run in parallel */ + call_func2(p__StructuredTaskCollection_ctor, &task_coll, NULL); + ResetEvent(chore_start_evt); + chore_ctor(&chore1); + chore1.start_event = chore_start_evt; + chore1.set_event = chore_evt2; + chore1.wait_event = chore_evt1; + chore_ctor(&chore2); + chore2.start_event = chore_start_evt; + chore2.set_event = chore_evt1; + chore2.wait_event = chore_evt2; + + call_func2(p__StructuredTaskCollection__Schedule, &task_coll, &chore1.chore); + ok(chore1.chore.task_collection == &task_coll, "Wrong chore #1 task_collection: expected %p, actual %p\n", + &task_coll, chore1.chore.task_collection); + ok(chore1.chore.proc_wrapper != NULL, "Chore #1 proc_wrapper was not set\n"); + ok(task_coll.count == 1, "Wrong chore count: %ld != 1\n", task_coll.count); + ok(task_coll.context == context, "Unexpected context: %p != %p\n", task_coll.context, context); + + call_func2(p__StructuredTaskCollection__Schedule, &task_coll, &chore2.chore); + ok(chore2.chore.task_collection == &task_coll, "Wrong chore #2 task_collection: expected %p, actual %p\n", + &task_coll, chore2.chore.task_collection); + ok(chore2.chore.proc_wrapper != NULL, "Chore #2 proc_wrapper was not set\n"); + ok(task_coll.count == 2, "Wrong chore count: %ld != 2\n", task_coll.count); + ok(task_coll.context == context, "Unexpected context: %p != %p\n", task_coll.context, context); + + b = SetEvent(chore_start_evt); + ok(b, "SetEvent failed\n"); + + status = p__StructuredTaskCollection__RunAndWait(&task_coll, NULL); + ok(status == 1, "_StructuredTaskCollection::_RunAndWait failed: %d\n", status); + ok(chore1.executed, "Chore #1 was not executed\n"); + ok(chore2.executed, "Chore #2 was not executed\n"); + call_func1(p__StructuredTaskCollection_dtor, &task_coll); + + /* test that scheduled chores are executed even if _RunAndWait is not called */ + call_func2(p__StructuredTaskCollection_ctor, &task_coll, NULL); + ResetEvent(chore_start_evt); + chore_ctor(&chore1); + chore1.start_event = chore_start_evt; + chore1.set_event = chore_evt1; + + call_func2(p__StructuredTaskCollection__Schedule, &task_coll, &chore1.chore); + ok(chore1.chore.task_collection == &task_coll, "Wrong chore #1 task_collection: expected %p, actual %p\n", + &task_coll, chore1.chore.task_collection); + ok(chore1.chore.proc_wrapper != NULL, "Chore #1 proc_wrapper was not set\n"); + ok(task_coll.count == 1, "Wrong chore count: %ld != 1\n", task_coll.count); + ok(task_coll.context == context, "Unexpected context: %p != %p\n", task_coll.context, context); + + b = SetEvent(chore_start_evt); + ok(b, "SetEvent failed\n"); + + ret = WaitForSingleObject(chore_evt1, 5000); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObject returned %ld\n", ret); + ok(chore1.executed, "Chore was not executed\n"); + + status = p__StructuredTaskCollection__RunAndWait(&task_coll, NULL); + ok(status == 1, "_StructuredTaskCollection::_RunAndWait failed: %d\n", status); + ok(chore1.chore.task_collection == NULL, "Chore's task collection was not reset after execution\n"); + call_func1(p__StructuredTaskCollection_dtor, &task_coll); + + /* test that finished chores can be scheduled again */ + call_func2(p__StructuredTaskCollection_ctor, &task_coll, NULL); + ResetEvent(chore_start_evt); + chore1.set_event = NULL; + chore1.executed = FALSE; + + call_func2(p__StructuredTaskCollection__Schedule, &task_coll, &chore1.chore); + ok(chore1.chore.task_collection == &task_coll, "Wrong chore #1 task_collection: expected %p, actual %p\n", + &task_coll, chore1.chore.task_collection); + ok(chore1.chore.proc_wrapper != NULL, "Chore #1 proc_wrapper was not set\n"); + ok(task_coll.count == 1, "Wrong chore count: %ld != 1\n", task_coll.count); + ok(task_coll.context == context, "Unexpected context: %p != %p\n", task_coll.context, context); + + b = SetEvent(chore_start_evt); + ok(b, "SetEvent failed\n"); + + status = p__StructuredTaskCollection__RunAndWait(&task_coll, NULL); + ok(status == 1, "_StructuredTaskCollection::_RunAndWait failed: %d\n", status); + ok(chore1.executed, "Chore was not executed\n"); + call_func1(p__StructuredTaskCollection_dtor, &task_coll); + + CloseHandle(chore_start_evt); + CloseHandle(chore_evt1); + CloseHandle(chore_evt2); +} + START_TEST(msvcr120) { if (!init()) return; @@ -1292,4 +1560,5 @@ START_TEST(msvcr120) test_nexttoward(); test_towctrans(); test_CurrentContext(); + test_StructuredTaskCollection(); }