Something went wrong. Try again.
Reactos
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421/* * Unit test suite for thread pool functions * * Copyright 2015-2016 Sebastian Lackner * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
#include "ntdll_test.h"
#ifdef __REACTOS__typedef void (CALLBACK *PTP_IO_CALLBACK)(PTP_CALLBACK_INSTANCE,void*,void*,IO_STATUS_BLOCK*,PTP_IO);#endif
static NTSTATUS (WINAPI *pTpAllocCleanupGroup)(TP_CLEANUP_GROUP **);static NTSTATUS (WINAPI *pTpAllocIoCompletion)(TP_IO **,HANDLE,PTP_IO_CALLBACK,void *,TP_CALLBACK_ENVIRON *);static NTSTATUS (WINAPI *pTpAllocPool)(TP_POOL **,PVOID);static NTSTATUS (WINAPI *pTpAllocTimer)(TP_TIMER **,PTP_TIMER_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *);static NTSTATUS (WINAPI *pTpAllocWait)(TP_WAIT **,PTP_WAIT_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *);static NTSTATUS (WINAPI *pTpAllocWork)(TP_WORK **,PTP_WORK_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *);static NTSTATUS (WINAPI *pTpCallbackMayRunLong)(TP_CALLBACK_INSTANCE *);static VOID (WINAPI *pTpCallbackReleaseSemaphoreOnCompletion)(TP_CALLBACK_INSTANCE *,HANDLE,DWORD);static void (WINAPI *pTpCancelAsyncIoOperation)(TP_IO *);static VOID (WINAPI *pTpDisassociateCallback)(TP_CALLBACK_INSTANCE *);static BOOL (WINAPI *pTpIsTimerSet)(TP_TIMER *);static VOID (WINAPI *pTpPostWork)(TP_WORK *);static NTSTATUS (WINAPI *pTpQueryPoolStackInformation)(TP_POOL *,TP_POOL_STACK_INFORMATION *);static VOID (WINAPI *pTpReleaseCleanupGroup)(TP_CLEANUP_GROUP *);static VOID (WINAPI *pTpReleaseCleanupGroupMembers)(TP_CLEANUP_GROUP *,BOOL,PVOID);static void (WINAPI *pTpReleaseIoCompletion)(TP_IO *);static VOID (WINAPI *pTpReleasePool)(TP_POOL *);static VOID (WINAPI *pTpReleaseTimer)(TP_TIMER *);static VOID (WINAPI *pTpReleaseWait)(TP_WAIT *);static VOID (WINAPI *pTpReleaseWork)(TP_WORK *);static VOID (WINAPI *pTpSetPoolMaxThreads)(TP_POOL *,DWORD);static NTSTATUS (WINAPI *pTpSetPoolStackInformation)(TP_POOL *,TP_POOL_STACK_INFORMATION *);static VOID (WINAPI *pTpSetTimer)(TP_TIMER *,LARGE_INTEGER *,LONG,LONG);static VOID (WINAPI *pTpSetWait)(TP_WAIT *,HANDLE,LARGE_INTEGER *);static NTSTATUS (WINAPI *pTpSimpleTryPost)(PTP_SIMPLE_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *);static void (WINAPI *pTpStartAsyncIoOperation)(TP_IO *);static void (WINAPI *pTpWaitForIoCompletion)(TP_IO *,BOOL);static VOID (WINAPI *pTpWaitForTimer)(TP_TIMER *,BOOL);static VOID (WINAPI *pTpWaitForWait)(TP_WAIT *,BOOL);static VOID (WINAPI *pTpWaitForWork)(TP_WORK *,BOOL);
static void (WINAPI *pCancelThreadpoolIo)(TP_IO *);static void (WINAPI *pCloseThreadpoolIo)(TP_IO *);static TP_IO *(WINAPI *pCreateThreadpoolIo)(HANDLE, PTP_WIN32_IO_CALLBACK, void *, TP_CALLBACK_ENVIRON *);static void (WINAPI *pStartThreadpoolIo)(TP_IO *);static void (WINAPI *pWaitForThreadpoolIoCallbacks)(TP_IO *, BOOL);
#define GET_PROC(func) \ do \ { \ p ## func = (void *)GetProcAddress(module, #func); \ if (!p ## func) trace("Failed to get address for %s\n", #func); \ } \ while (0)
static BOOL init_threadpool(void){ HMODULE module = GetModuleHandleA("ntdll"); GET_PROC(TpAllocCleanupGroup); GET_PROC(TpAllocIoCompletion); GET_PROC(TpAllocPool); GET_PROC(TpAllocTimer); GET_PROC(TpAllocWait); GET_PROC(TpAllocWork); GET_PROC(TpCallbackMayRunLong); GET_PROC(TpCallbackReleaseSemaphoreOnCompletion); GET_PROC(TpCancelAsyncIoOperation); GET_PROC(TpDisassociateCallback); GET_PROC(TpIsTimerSet); GET_PROC(TpPostWork); GET_PROC(TpQueryPoolStackInformation); GET_PROC(TpReleaseCleanupGroup); GET_PROC(TpReleaseCleanupGroupMembers); GET_PROC(TpReleaseIoCompletion); GET_PROC(TpReleasePool); GET_PROC(TpReleaseTimer); GET_PROC(TpReleaseWait); GET_PROC(TpReleaseWork); GET_PROC(TpSetPoolMaxThreads); GET_PROC(TpSetPoolStackInformation); GET_PROC(TpSetTimer); GET_PROC(TpSetWait); GET_PROC(TpSimpleTryPost); GET_PROC(TpStartAsyncIoOperation); GET_PROC(TpWaitForIoCompletion); GET_PROC(TpWaitForTimer); GET_PROC(TpWaitForWait); GET_PROC(TpWaitForWork);
module = GetModuleHandleA("kernel32"); GET_PROC(CancelThreadpoolIo); GET_PROC(CloseThreadpoolIo); GET_PROC(CreateThreadpoolIo); GET_PROC(StartThreadpoolIo); GET_PROC(WaitForThreadpoolIoCallbacks);
if (!pTpAllocPool) { win_skip("Threadpool functions not supported, skipping tests\n"); return FALSE; }
return TRUE;}
#undef NTDLL_GET_PROC
static DWORD CALLBACK rtl_work_cb(void *userdata){ HANDLE semaphore = userdata; ReleaseSemaphore(semaphore, 1, NULL); return 0;}
static void test_RtlQueueWorkItem(void){ HANDLE semaphore; NTSTATUS status; DWORD result;
semaphore = CreateSemaphoreA(NULL, 0, 1, NULL); ok(semaphore != NULL, "CreateSemaphoreA failed %lu\n", GetLastError());
status = RtlQueueWorkItem(rtl_work_cb, semaphore, WT_EXECUTEDEFAULT); ok(!status, "RtlQueueWorkItem failed with status %lx\n", status); result = WaitForSingleObject(semaphore, 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result);
status = RtlQueueWorkItem(rtl_work_cb, semaphore, WT_EXECUTEINIOTHREAD); ok(!status, "RtlQueueWorkItem failed with status %lx\n", status); result = WaitForSingleObject(semaphore, 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result);
status = RtlQueueWorkItem(rtl_work_cb, semaphore, WT_EXECUTEINPERSISTENTTHREAD); ok(!status, "RtlQueueWorkItem failed with status %lx\n", status); result = WaitForSingleObject(semaphore, 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result);
status = RtlQueueWorkItem(rtl_work_cb, semaphore, WT_EXECUTELONGFUNCTION); ok(!status, "RtlQueueWorkItem failed with status %lx\n", status); result = WaitForSingleObject(semaphore, 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result);
status = RtlQueueWorkItem(rtl_work_cb, semaphore, WT_TRANSFER_IMPERSONATION); ok(!status, "RtlQueueWorkItem failed with status %lx\n", status); result = WaitForSingleObject(semaphore, 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result);
CloseHandle(semaphore);}
struct rtl_wait_info{ HANDLE semaphore1; HANDLE semaphore2; DWORD wait_result; DWORD threadid; LONG userdata;};
static void CALLBACK rtl_wait_cb(void *userdata, BOOLEAN timeout){ struct rtl_wait_info *info = userdata; DWORD result;
if (!timeout) InterlockedIncrement(&info->userdata); else InterlockedExchangeAdd(&info->userdata, 0x10000); info->threadid = GetCurrentThreadId(); ReleaseSemaphore(info->semaphore1, 1, NULL);
if (info->semaphore2) { result = WaitForSingleObject(info->semaphore2, 200); ok(result == info->wait_result, "expected %lu, got %lu\n", info->wait_result, result); ReleaseSemaphore(info->semaphore1, 1, NULL); }}
static HANDLE rtl_wait_apc_semaphore;
static void CALLBACK rtl_wait_apc_cb(ULONG_PTR userdata){ if (rtl_wait_apc_semaphore) ReleaseSemaphore(rtl_wait_apc_semaphore, 1, NULL);}
static void test_RtlRegisterWait(void){ HANDLE wait1, event, thread; struct rtl_wait_info info; HANDLE semaphores[2]; NTSTATUS status; DWORD result, threadid;
semaphores[0] = CreateSemaphoreW(NULL, 0, 2, NULL); ok(semaphores[0] != NULL, "failed to create semaphore\n"); semaphores[1] = CreateSemaphoreW(NULL, 0, 1, NULL); ok(semaphores[1] != NULL, "failed to create semaphore\n"); info.semaphore1 = semaphores[0]; info.semaphore2 = NULL;
event = CreateEventW(NULL, FALSE, FALSE, NULL); ok(event != NULL, "failed to create event\n");
/* basic test for RtlRegisterWait and RtlDeregisterWait */ wait1 = NULL; info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEDEFAULT); ok(!status, "RtlRegisterWait failed with status %lx\n", status); ok(wait1 != NULL, "expected wait1 != NULL\n"); status = RtlDeregisterWait(wait1); ok(!status, "RtlDeregisterWait failed with status %lx\n", status); ok(info.userdata == 0, "expected info.userdata = 0, got %lu\n", info.userdata);
/* infinite timeout, signal the semaphore two times */ info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEDEFAULT); ok(!status, "RtlRegisterWait failed with status %lx\n", status); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 2, "expected info.userdata = 2, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); Sleep(50); status = RtlDeregisterWait(wait1); ok(!status, "RtlDeregisterWait failed with status %lx\n", status);
/* repeat test with WT_EXECUTEONLYONCE */ info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEONLYONCE); ok(!status, "RtlRegisterWait failed with status %lx\n", status); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); Sleep(50); status = RtlDeregisterWait(wait1); ok(!status, "RtlDeregisterWait failed with status %lx\n", status);
/* finite timeout, no event */ info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, 200, WT_EXECUTEDEFAULT); ok(!status, "RtlRegisterWait failed with status %lx\n", status); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 0, "expected info.userdata = 0, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[0], 200); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); Sleep(50); status = RtlDeregisterWait(wait1); ok(!status, "RtlDeregisterWait failed with status %lx\n", status);
/* finite timeout, with event */ info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, 200, WT_EXECUTEDEFAULT); ok(!status, "RtlRegisterWait failed with status %lx\n", status); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 0, "expected info.userdata = 0, got %lu\n", info.userdata); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); Sleep(50); status = RtlDeregisterWait(wait1); ok(!status, "RtlDeregisterWait failed with status %lx\n", status);
/* test RtlRegisterWait WT_EXECUTEINWAITTHREAD flag */ info.userdata = 0; info.threadid = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, 200, WT_EXECUTEINWAITTHREAD|WT_EXECUTEONLYONCE); ok(!status, "RtlRegisterWait failed with status %lx\n", status); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 200); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); ok(info.threadid && info.threadid != GetCurrentThreadId(), "unexpected wait thread id %lx\n", info.threadid); threadid = info.threadid; result = WaitForSingleObject(semaphores[1], 0); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); Sleep(50); status = RtlDeregisterWait(wait1); ok(!status, "RtlDeregisterWait failed with status %lx\n", status);
info.userdata = 0; info.threadid = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, 200, WT_EXECUTEINWAITTHREAD|WT_EXECUTEONLYONCE); ok(!status, "RtlRegisterWait failed with status %lx\n", status); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 200); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); ok(info.threadid == threadid, "unexpected different wait thread id %lx\n", info.threadid); result = WaitForSingleObject(semaphores[1], 0); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); Sleep(50); status = RtlDeregisterWait(wait1); ok(!status, "RtlDeregisterWait failed with status %lx\n", status);
/* test RtlRegisterWait WT_EXECUTEINWAITTHREAD flag with 0 timeout */ info.userdata = 0; info.threadid = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, 0, WT_EXECUTEINWAITTHREAD|WT_EXECUTEONLYONCE); ok(!status, "RtlRegisterWait failed with status %lx\n", status); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %lu\n", info.userdata); ok(info.threadid == threadid, "unexpected different wait thread id %lx\n", info.threadid); result = WaitForSingleObject(semaphores[1], 0); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); Sleep(50); status = RtlDeregisterWait(wait1); ok(!status, "RtlDeregisterWait failed with status %lx\n", status);
/* test RtlRegisterWait WT_EXECUTEINWAITTHREAD flag with already signaled event */ info.userdata = 0; info.threadid = 0; ReleaseSemaphore(semaphores[1], 1, NULL); status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, 200, WT_EXECUTEINWAITTHREAD|WT_EXECUTEONLYONCE); ok(!status, "RtlRegisterWait failed with status %lx\n", status); result = WaitForSingleObject(semaphores[0], 200); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); ok(info.threadid == threadid, "unexpected different wait thread id %lx\n", info.threadid); result = WaitForSingleObject(semaphores[1], 0); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); Sleep(50); status = RtlDeregisterWait(wait1); ok(!status, "RtlDeregisterWait failed with status %lx\n", status);
/* test for IO threads */ info.userdata = 0; info.threadid = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEINIOTHREAD); ok(!status, "RtlRegisterWait failed with status %lx\n", status); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); ok(info.threadid != 0, "expected info.threadid != 0, got %lu\n", info.threadid); thread = OpenThread(THREAD_SET_CONTEXT, FALSE, info.threadid); ok(thread != NULL, "OpenThread failed with %lu\n", GetLastError()); rtl_wait_apc_semaphore = semaphores[0]; result = QueueUserAPC(rtl_wait_apc_cb, thread, 0); ok(result != 0, "QueueUserAPC failed with %lu\n", GetLastError()); result = WaitForSingleObject(semaphores[0], 200); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); rtl_wait_apc_semaphore = 0; CloseHandle(thread); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 2, "expected info.userdata = 2, got %lu\n", info.userdata); Sleep(50); status = RtlDeregisterWait(wait1); ok(!status, "RtlDeregisterWait failed with status %lx\n", status);
info.userdata = 0; info.threadid = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEDEFAULT); ok(!status, "RtlRegisterWait failed with status %lx\n", status); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); ok(info.threadid != 0, "expected info.threadid != 0, got %lu\n", info.threadid); thread = OpenThread(THREAD_SET_CONTEXT, FALSE, info.threadid); ok(thread != NULL, "OpenThread failed with %lu\n", GetLastError()); rtl_wait_apc_semaphore = semaphores[0]; result = QueueUserAPC(rtl_wait_apc_cb, thread, 0); ok(result != 0, "QueueUserAPC failed with %lu\n", GetLastError()); result = WaitForSingleObject(semaphores[0], 200); ok(result == WAIT_TIMEOUT || broken(result == WAIT_OBJECT_0) /* >= Win Vista */, "WaitForSingleObject returned %lu\n", result); rtl_wait_apc_semaphore = 0; CloseHandle(thread); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 2, "expected info.userdata = 2, got %lu\n", info.userdata); Sleep(50); status = RtlDeregisterWait(wait1); ok(!status, "RtlDeregisterWait failed with status %lx\n", status);
/* test RtlDeregisterWaitEx before wait expired */ info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEDEFAULT); ok(!status, "RtlRegisterWait failed with status %lx\n", status); status = RtlDeregisterWaitEx(wait1, NULL); ok(!status, "RtlDeregisterWaitEx failed with status %lx\n", status); ok(info.userdata == 0, "expected info.userdata = 0, got %lu\n", info.userdata);
info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEDEFAULT); ok(!status, "RtlRegisterWait failed with status %lx\n", status); status = RtlDeregisterWaitEx(wait1, INVALID_HANDLE_VALUE); ok(!status, "RtlDeregisterWaitEx failed with status %lx\n", status); ok(info.userdata == 0, "expected info.userdata = 0, got %lu\n", info.userdata);
info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEDEFAULT); ok(!status, "RtlRegisterWait failed with status %lx\n", status); status = RtlDeregisterWaitEx(wait1, event); ok(!status, "RtlDeregisterWaitEx failed with status %lx\n", status); ok(info.userdata == 0, "expected info.userdata = 0, got %lu\n", info.userdata); result = WaitForSingleObject(event, 200); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result);
/* test RtlDeregisterWaitEx after wait expired */ info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, 0, WT_EXECUTEONLYONCE); ok(!status, "RtlRegisterWait failed with status %lx\n", status); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %lu\n", info.userdata); Sleep(50); status = RtlDeregisterWaitEx(wait1, NULL); ok(!status, "RtlDeregisterWaitEx failed with status %lx\n", status); ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %lu\n", info.userdata);
info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, 0, WT_EXECUTEONLYONCE); ok(!status, "RtlRegisterWait failed with status %lx\n", status); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %lu\n", info.userdata); Sleep(50); status = RtlDeregisterWaitEx(wait1, INVALID_HANDLE_VALUE); ok(!status, "RtlDeregisterWaitEx failed with status %lx\n", status); ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %lu\n", info.userdata);
info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, 0, WT_EXECUTEONLYONCE); ok(!status, "RtlRegisterWait failed with status %lx\n", status); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %lu\n", info.userdata); Sleep(50); status = RtlDeregisterWaitEx(wait1, event); ok(!status, "RtlDeregisterWaitEx failed with status %lx\n", status); ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %lu\n", info.userdata); result = WaitForSingleObject(event, 200); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result);
/* test RtlDeregisterWaitEx while callback is running */ info.semaphore2 = semaphores[1]; info.wait_result = WAIT_OBJECT_0;
info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEONLYONCE); ok(!status, "RtlRegisterWait failed with status %lx\n", status); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); status = RtlDeregisterWait(wait1); ok(status == STATUS_PENDING, "expected STATUS_PENDING, got %lx\n", status); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result);
info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEINWAITTHREAD|WT_EXECUTEONLYONCE); ok(!status, "RtlRegisterWait failed with status %lx\n", status); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); status = RtlDeregisterWait(wait1); ok(status == STATUS_PENDING, "expected STATUS_PENDING, got %lx\n", status); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result);
info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEONLYONCE); ok(!status, "RtlRegisterWait failed with status %lx\n", status); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); status = RtlDeregisterWaitEx(wait1, NULL); ok(status == STATUS_PENDING, "expected STATUS_PENDING, got %lx\n", status); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result);
info.wait_result = WAIT_TIMEOUT; info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEONLYONCE); ok(!status, "RtlRegisterWait failed with status %lx\n", status); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); status = RtlDeregisterWaitEx(wait1, INVALID_HANDLE_VALUE); ok(!status, "RtlDeregisterWaitEx failed with status %lx\n", status); result = WaitForSingleObject(semaphores[0], 0); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result);
info.wait_result = WAIT_TIMEOUT; info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEINWAITTHREAD|WT_EXECUTEONLYONCE); ok(!status, "RtlRegisterWait failed with status %lx\n", status); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); status = RtlDeregisterWaitEx(wait1, INVALID_HANDLE_VALUE); ok(!status, "RtlDeregisterWaitEx failed with status %lx\n", status); result = WaitForSingleObject(semaphores[0], 0); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result);
info.wait_result = WAIT_OBJECT_0; info.userdata = 0; status = RtlRegisterWait(&wait1, semaphores[1], rtl_wait_cb, &info, INFINITE, WT_EXECUTEONLYONCE); ok(!status, "RtlRegisterWait failed with status %lx\n", status); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); status = RtlDeregisterWaitEx(wait1, event); ok(status == STATUS_PENDING, "expected STATUS_PENDING, got %lx\n", status); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(event, 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); result = WaitForSingleObject(semaphores[0], 0); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result);
CloseHandle(semaphores[0]); CloseHandle(semaphores[1]); CloseHandle(event);}
static void CALLBACK simple_cb(TP_CALLBACK_INSTANCE *instance, void *userdata){ HANDLE semaphore = userdata; ReleaseSemaphore(semaphore, 1, NULL);}
static void CALLBACK simple2_cb(TP_CALLBACK_INSTANCE *instance, void *userdata){ Sleep(50); InterlockedIncrement((LONG *)userdata);}
static void test_tp_simple(void){ IMAGE_NT_HEADERS *nt = RtlImageNtHeader( NtCurrentTeb()->Peb->ImageBaseAddress ); TP_POOL_STACK_INFORMATION stack_info; TP_CALLBACK_ENVIRON environment;#ifndef __REACTOS__ TP_CALLBACK_ENVIRON_V3 environment3;#endif TP_CLEANUP_GROUP *group; HANDLE semaphore; NTSTATUS status; TP_POOL *pool; LONG userdata; DWORD result; int i;
semaphore = CreateSemaphoreA(NULL, 0, 1, NULL); ok(semaphore != NULL, "CreateSemaphoreA failed %lu\n", GetLastError());
/* post the callback using the default threadpool */ memset(&environment, 0, sizeof(environment)); environment.Version = 1; environment.Pool = NULL; status = pTpSimpleTryPost(simple_cb, semaphore, &environment); ok(!status, "TpSimpleTryPost failed with status %lx\n", status); result = WaitForSingleObject(semaphore, 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result);
/* allocate new threadpool */ pool = NULL; status = pTpAllocPool(&pool, NULL); ok(!status, "TpAllocPool failed with status %lx\n", status); ok(pool != NULL, "expected pool != NULL\n");
/* post the callback using the new threadpool */ memset(&environment, 0, sizeof(environment)); environment.Version = 1; environment.Pool = pool; status = pTpSimpleTryPost(simple_cb, semaphore, &environment); ok(!status, "TpSimpleTryPost failed with status %lx\n", status); result = WaitForSingleObject(semaphore, 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result);
#ifndef __REACTOS__ // Windows 7 /* test with environment version 3 */ memset(&environment3, 0, sizeof(environment3)); environment3.Version = 3; environment3.Pool = pool; environment3.Size = sizeof(environment3);
for (i = 0; i < 3; ++i) { environment3.CallbackPriority = TP_CALLBACK_PRIORITY_HIGH + i; status = pTpSimpleTryPost(simple_cb, semaphore, (TP_CALLBACK_ENVIRON *)&environment3); ok(!status, "TpSimpleTryPost failed with status %lx\n", status); result = WaitForSingleObject(semaphore, 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); }
environment3.CallbackPriority = 10; status = pTpSimpleTryPost(simple_cb, semaphore, (TP_CALLBACK_ENVIRON *)&environment3); ok(status == STATUS_INVALID_PARAMETER || broken(!status) /* Vista does not support priorities */, "TpSimpleTryPost failed with status %lx\n", status);#endif
/* test with invalid version number */ memset(&environment, 0, sizeof(environment)); environment.Version = 9999; environment.Pool = pool; status = pTpSimpleTryPost(simple_cb, semaphore, &environment); todo_wine ok(status == STATUS_INVALID_PARAMETER || broken(!status) /* Vista/2008 */, "TpSimpleTryPost unexpectedly returned status %lx\n", status); if (!status) { result = WaitForSingleObject(semaphore, 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); }
/* allocate a cleanup group for synchronization */ group = NULL; status = pTpAllocCleanupGroup(&group); ok(!status, "TpAllocCleanupGroup failed with status %lx\n", status); ok(group != NULL, "expected pool != NULL\n");
/* use cleanup group to wait for a simple callback */ userdata = 0; memset(&environment, 0, sizeof(environment)); environment.Version = 1; environment.Pool = pool; environment.CleanupGroup = group; status = pTpSimpleTryPost(simple2_cb, &userdata, &environment); ok(!status, "TpSimpleTryPost failed with status %lx\n", status); pTpReleaseCleanupGroupMembers(group, FALSE, NULL); ok(userdata == 1, "expected userdata = 1, got %lu\n", userdata);
/* test cancellation of pending simple callbacks */ userdata = 0; pTpSetPoolMaxThreads(pool, 10); memset(&environment, 0, sizeof(environment)); environment.Version = 1; environment.Pool = pool; environment.CleanupGroup = group; for (i = 0; i < 100; i++) { status = pTpSimpleTryPost(simple2_cb, &userdata, &environment); ok(!status, "TpSimpleTryPost failed with status %lx\n", status); } pTpReleaseCleanupGroupMembers(group, TRUE, NULL); ok(userdata < 100, "expected userdata < 100, got %lu\n", userdata);
/* test querying and setting the stack size */ status = pTpQueryPoolStackInformation(pool, &stack_info); ok(!status, "TpQueryPoolStackInformation failed: %lx\n", status); ok(stack_info.StackReserve == nt->OptionalHeader.SizeOfStackReserve, "expected default StackReserve, got %Ix\n", stack_info.StackReserve); ok(stack_info.StackCommit == nt->OptionalHeader.SizeOfStackCommit, "expected default StackCommit, got %Ix\n", stack_info.StackCommit);
/* threadpool does not validate the stack size values */ stack_info.StackReserve = stack_info.StackCommit = 1; status = pTpSetPoolStackInformation(pool, &stack_info); ok(!status, "TpSetPoolStackInformation failed: %lx\n", status);
status = pTpQueryPoolStackInformation(pool, &stack_info); ok(!status, "TpQueryPoolStackInformation failed: %lx\n", status); ok(stack_info.StackReserve == 1, "expected 1 byte StackReserve, got %ld\n", (ULONG)stack_info.StackReserve); ok(stack_info.StackCommit == 1, "expected 1 byte StackCommit, got %ld\n", (ULONG)stack_info.StackCommit);
/* cleanup */ pTpReleaseCleanupGroup(group); pTpReleasePool(pool); CloseHandle(semaphore);}
static void CALLBACK work_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_WORK *work){ Sleep(100); InterlockedIncrement((LONG *)userdata);}
static void CALLBACK work2_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_WORK *work){ Sleep(100); InterlockedExchangeAdd((LONG *)userdata, 0x10000);}
static void test_tp_work(void){ TP_CALLBACK_ENVIRON environment; TP_WORK *work; TP_POOL *pool; NTSTATUS status; LONG userdata; int i;
/* allocate new threadpool with only one thread */ pool = NULL; status = pTpAllocPool(&pool, NULL); ok(!status, "TpAllocPool failed with status %lx\n", status); ok(pool != NULL, "expected pool != NULL\n"); pTpSetPoolMaxThreads(pool, 1);
/* allocate new work item */ work = NULL; memset(&environment, 0, sizeof(environment)); environment.Version = 1; environment.Pool = pool; status = pTpAllocWork(&work, work_cb, &userdata, &environment); ok(!status, "TpAllocWork failed with status %lx\n", status); ok(work != NULL, "expected work != NULL\n");
/* post 5 identical work items at once */ userdata = 0; for (i = 0; i < 5; i++) pTpPostWork(work); pTpWaitForWork(work, FALSE); ok(userdata == 5, "expected userdata = 5, got %lu\n", userdata);
/* add more tasks and cancel them immediately */ userdata = 0; for (i = 0; i < 10; i++) pTpPostWork(work); pTpWaitForWork(work, TRUE); ok(userdata < 10, "expected userdata < 10, got %lu\n", userdata);
/* cleanup */ pTpReleaseWork(work); pTpReleasePool(pool);}
static void test_tp_work_scheduler(void){ TP_CALLBACK_ENVIRON environment; TP_CLEANUP_GROUP *group; TP_WORK *work, *work2; TP_POOL *pool; NTSTATUS status; LONG userdata; int i;
/* allocate new threadpool with only one thread */ pool = NULL; status = pTpAllocPool(&pool, NULL); ok(!status, "TpAllocPool failed with status %lx\n", status); ok(pool != NULL, "expected pool != NULL\n"); pTpSetPoolMaxThreads(pool, 1);
/* create a cleanup group */ group = NULL; status = pTpAllocCleanupGroup(&group); ok(!status, "TpAllocCleanupGroup failed with status %lx\n", status); ok(group != NULL, "expected pool != NULL\n");
/* the first work item has no cleanup group associated */ work = NULL; memset(&environment, 0, sizeof(environment)); environment.Version = 1; environment.Pool = pool; status = pTpAllocWork(&work, work_cb, &userdata, &environment); ok(!status, "TpAllocWork failed with status %lx\n", status); ok(work != NULL, "expected work != NULL\n");
/* allocate a second work item with a cleanup group */ work2 = NULL; memset(&environment, 0, sizeof(environment)); environment.Version = 1; environment.Pool = pool; environment.CleanupGroup = group; status = pTpAllocWork(&work2, work2_cb, &userdata, &environment); ok(!status, "TpAllocWork failed with status %lx\n", status); ok(work2 != NULL, "expected work2 != NULL\n");
/* the 'work' callbacks are not blocking execution of 'work2' callbacks */ userdata = 0; for (i = 0; i < 10; i++) pTpPostWork(work); for (i = 0; i < 10; i++) pTpPostWork(work2); Sleep(500); pTpWaitForWork(work, TRUE); pTpWaitForWork(work2, TRUE); ok(userdata & 0xffff, "expected userdata & 0xffff != 0, got %lu\n", userdata & 0xffff); ok(userdata >> 16, "expected userdata >> 16 != 0, got %lu\n", userdata >> 16);
/* test TpReleaseCleanupGroupMembers on a work item */ userdata = 0; for (i = 0; i < 10; i++) pTpPostWork(work); for (i = 0; i < 3; i++) pTpPostWork(work2); pTpReleaseCleanupGroupMembers(group, FALSE, NULL); pTpWaitForWork(work, TRUE); ok((userdata & 0xffff) < 10, "expected userdata & 0xffff < 10, got %lu\n", userdata & 0xffff); ok((userdata >> 16) == 3, "expected userdata >> 16 == 3, got %lu\n", userdata >> 16);
/* cleanup */ pTpReleaseWork(work); pTpReleaseCleanupGroup(group); pTpReleasePool(pool);}
static void CALLBACK simple_release_cb(TP_CALLBACK_INSTANCE *instance, void *userdata){ HANDLE *semaphores = userdata; ReleaseSemaphore(semaphores, 1, NULL); Sleep(200); /* wait until main thread is in TpReleaseCleanupGroupMembers */}
static void CALLBACK work_release_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_WORK *work){ HANDLE semaphore = userdata; ReleaseSemaphore(semaphore, 1, NULL); Sleep(200); /* wait until main thread is in TpReleaseCleanupGroupMembers */ pTpReleaseWork(work);}
static void CALLBACK timer_release_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_TIMER *timer){ HANDLE semaphore = userdata; ReleaseSemaphore(semaphore, 1, NULL); Sleep(200); /* wait until main thread is in TpReleaseCleanupGroupMembers */ pTpReleaseTimer(timer);}
static void CALLBACK wait_release_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_WAIT *wait, TP_WAIT_RESULT result){ HANDLE semaphore = userdata; ReleaseSemaphore(semaphore, 1, NULL); Sleep(200); /* wait until main thread is in TpReleaseCleanupGroupMembers */ pTpReleaseWait(wait);}
static void test_tp_group_wait(void){ TP_CALLBACK_ENVIRON environment; TP_CLEANUP_GROUP *group; LARGE_INTEGER when; HANDLE semaphore; NTSTATUS status; TP_TIMER *timer; TP_WAIT *wait; TP_WORK *work; TP_POOL *pool; DWORD result;
semaphore = CreateSemaphoreA(NULL, 0, 1, NULL); ok(semaphore != NULL, "CreateSemaphoreA failed %lu\n", GetLastError());
/* allocate new threadpool */ pool = NULL; status = pTpAllocPool(&pool, NULL); ok(!status, "TpAllocPool failed with status %lx\n", status); ok(pool != NULL, "expected pool != NULL\n");
/* allocate a cleanup group */ group = NULL; status = pTpAllocCleanupGroup(&group); ok(!status, "TpAllocCleanupGroup failed with status %lx\n", status); ok(group != NULL, "expected pool != NULL\n");
/* release work object during TpReleaseCleanupGroupMembers */ work = NULL; memset(&environment, 0, sizeof(environment)); environment.Version = 1; environment.Pool = pool; environment.CleanupGroup = group; status = pTpAllocWork(&work, work_release_cb, semaphore, &environment); ok(!status, "TpAllocWork failed with status %lx\n", status); ok(work != NULL, "expected work != NULL\n"); pTpPostWork(work); result = WaitForSingleObject(semaphore, 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); pTpReleaseCleanupGroupMembers(group, FALSE, NULL);
/* release timer object during TpReleaseCleanupGroupMembers */ timer = NULL; memset(&environment, 0, sizeof(environment)); environment.Version = 1; environment.Pool = pool; environment.CleanupGroup = group; status = pTpAllocTimer(&timer, timer_release_cb, semaphore, &environment); ok(!status, "TpAllocTimer failed with status %lx\n", status); ok(timer != NULL, "expected timer != NULL\n"); when.QuadPart = 0; pTpSetTimer(timer, &when, 0, 0); result = WaitForSingleObject(semaphore, 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); pTpReleaseCleanupGroupMembers(group, FALSE, NULL);
/* release wait object during TpReleaseCleanupGroupMembers */ wait = NULL; memset(&environment, 0, sizeof(environment)); environment.Version = 1; environment.Pool = pool; environment.CleanupGroup = group; status = pTpAllocWait(&wait, wait_release_cb, semaphore, &environment); ok(!status, "TpAllocWait failed with status %lx\n", status); ok(wait != NULL, "expected wait != NULL\n"); when.QuadPart = 0; pTpSetWait(wait, INVALID_HANDLE_VALUE, &when); result = WaitForSingleObject(semaphore, 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); pTpReleaseCleanupGroupMembers(group, FALSE, NULL);
/* cleanup */ pTpReleaseCleanupGroup(group); pTpReleasePool(pool); CloseHandle(semaphore);}
static DWORD group_cancel_tid;
static void CALLBACK simple_group_cancel_cb(TP_CALLBACK_INSTANCE *instance, void *userdata){ HANDLE *semaphores = userdata; NTSTATUS status; DWORD result; int i;
status = pTpCallbackMayRunLong(instance); ok(status == STATUS_TOO_MANY_THREADS || broken(status == 1) /* Win Vista / 2008 */, "expected STATUS_TOO_MANY_THREADS, got %08lx\n", status);
ReleaseSemaphore(semaphores[1], 1, NULL); for (i = 0; i < 4; i++) { result = WaitForSingleObject(semaphores[0], 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); } ReleaseSemaphore(semaphores[1], 1, NULL);}
static void CALLBACK work_group_cancel_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_WORK *work){ HANDLE *semaphores = userdata; DWORD result;
ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 200); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result);}
static void CALLBACK group_cancel_cleanup_release_cb(void *object, void *userdata){ HANDLE *semaphores = userdata; group_cancel_tid = GetCurrentThreadId(); ok(object == (void *)0xdeadbeef, "expected 0xdeadbeef, got %p\n", object); ReleaseSemaphore(semaphores[0], 1, NULL);}
static void CALLBACK group_cancel_cleanup_release2_cb(void *object, void *userdata){ HANDLE *semaphores = userdata; group_cancel_tid = GetCurrentThreadId(); ok(object == userdata, "expected %p, got %p\n", userdata, object); ReleaseSemaphore(semaphores[0], 1, NULL);}
static void CALLBACK group_cancel_cleanup_increment_cb(void *object, void *userdata){ group_cancel_tid = GetCurrentThreadId(); InterlockedIncrement((LONG *)userdata);}
static void CALLBACK unexpected_simple_cb(TP_CALLBACK_INSTANCE *instance, void *userdata){ ok(0, "Unexpected callback\n");}
static void CALLBACK unexpected_work_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_WORK *work){ ok(0, "Unexpected callback\n");}
static void CALLBACK unexpected_timer_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_TIMER *timer){ ok(0, "Unexpected callback\n");}
static void CALLBACK unexpected_wait_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_WAIT *wait, TP_WAIT_RESULT result){ ok(0, "Unexpected callback\n");}
static void CALLBACK unexpected_group_cancel_cleanup_cb(void *object, void *userdata){ ok(0, "Unexpected callback\n");}
static void test_tp_group_cancel(void){ TP_CALLBACK_ENVIRON environment; TP_CLEANUP_GROUP *group; LONG userdata, userdata2; HANDLE semaphores[2]; NTSTATUS status; TP_TIMER *timer; TP_WAIT *wait; TP_WORK *work; TP_POOL *pool; DWORD result; int i;
semaphores[0] = CreateSemaphoreA(NULL, 0, 4, NULL); ok(semaphores[0] != NULL, "CreateSemaphoreA failed %lu\n", GetLastError()); semaphores[1] = CreateSemaphoreA(NULL, 0, 1, NULL); ok(semaphores[1] != NULL, "CreateSemaphoreA failed %lu\n", GetLastError());
/* allocate new threadpool with only one thread */ pool = NULL; status = pTpAllocPool(&pool, NULL); ok(!status, "TpAllocPool failed with status %lx\n", status); ok(pool != NULL, "expected pool != NULL\n"); pTpSetPoolMaxThreads(pool, 1);
/* allocate a cleanup group */ group = NULL; status = pTpAllocCleanupGroup(&group); ok(!status, "TpAllocCleanupGroup failed with status %lx\n", status); ok(group != NULL, "expected pool != NULL\n");
/* test execution of cancellation callback */ memset(&environment, 0, sizeof(environment)); environment.Version = 1; environment.Pool = pool; status = pTpSimpleTryPost(simple_group_cancel_cb, semaphores, &environment); ok(!status, "TpSimpleTryPost failed with status %lx\n", status); result = WaitForSingleObject(semaphores[1], 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result);
memset(&environment, 0, sizeof(environment)); environment.Version = 1; environment.Pool = pool; environment.CleanupGroup = group; environment.CleanupGroupCancelCallback = group_cancel_cleanup_release_cb; status = pTpSimpleTryPost(unexpected_simple_cb, (void *)0xdeadbeef, &environment); ok(!status, "TpSimpleTryPost failed with status %lx\n", status);
work = NULL; status = pTpAllocWork(&work, unexpected_work_cb, (void *)0xdeadbeef, &environment); ok(!status, "TpAllocWork failed with status %lx\n", status); ok(work != NULL, "expected work != NULL\n");
timer = NULL; status = pTpAllocTimer(&timer, unexpected_timer_cb, (void *)0xdeadbeef, &environment); ok(!status, "TpAllocTimer failed with status %lx\n", status); ok(timer != NULL, "expected timer != NULL\n");
wait = NULL; status = pTpAllocWait(&wait, unexpected_wait_cb, (void *)0xdeadbeef, &environment); ok(!status, "TpAllocWait failed with status %lx\n", status); ok(wait != NULL, "expected wait != NULL\n");
group_cancel_tid = 0xdeadbeef; pTpReleaseCleanupGroupMembers(group, TRUE, semaphores); result = WaitForSingleObject(semaphores[1], 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(group_cancel_tid == GetCurrentThreadId(), "expected tid %lx, got %lx\n", GetCurrentThreadId(), group_cancel_tid);
/* test if cancellation callbacks are executed before or after wait */ work = NULL; memset(&environment, 0, sizeof(environment)); environment.Version = 1; environment.Pool = pool; environment.CleanupGroup = group; environment.CleanupGroupCancelCallback = group_cancel_cleanup_release2_cb; status = pTpAllocWork(&work, work_group_cancel_cb, semaphores, &environment); ok(!status, "TpAllocWork failed with status %lx\n", status); ok(work != NULL, "expected work != NULL\n"); pTpPostWork(work); pTpPostWork(work);
result = WaitForSingleObject(semaphores[1], 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result);
group_cancel_tid = 0xdeadbeef; pTpReleaseCleanupGroupMembers(group, TRUE, semaphores); result = WaitForSingleObject(semaphores[0], 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(group_cancel_tid == GetCurrentThreadId(), "expected tid %lx, got %lx\n", GetCurrentThreadId(), group_cancel_tid);
/* group cancel callback is not executed if object is destroyed while waiting */ work = NULL; memset(&environment, 0, sizeof(environment)); environment.Version = 1; environment.Pool = pool; environment.CleanupGroup = group; environment.CleanupGroupCancelCallback = unexpected_group_cancel_cleanup_cb; status = pTpAllocWork(&work, work_release_cb, semaphores[1], &environment); ok(!status, "TpAllocWork failed with status %lx\n", status); ok(work != NULL, "expected work != NULL\n"); pTpPostWork(work);
result = WaitForSingleObject(semaphores[1], 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); pTpReleaseCleanupGroupMembers(group, TRUE, NULL);
/* terminated simple callbacks should not trigger the group cancel callback */ memset(&environment, 0, sizeof(environment)); environment.Version = 1; environment.Pool = pool; environment.CleanupGroup = group; environment.CleanupGroupCancelCallback = unexpected_group_cancel_cleanup_cb; status = pTpSimpleTryPost(simple_release_cb, semaphores[1], &environment); ok(!status, "TpSimpleTryPost failed with status %lx\n", status); result = WaitForSingleObject(semaphores[1], 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); pTpReleaseCleanupGroupMembers(group, TRUE, semaphores);
/* test cancellation callback for objects with multiple instances */ work = NULL; memset(&environment, 0, sizeof(environment)); environment.Version = 1; environment.Pool = pool; environment.CleanupGroup = group; environment.CleanupGroupCancelCallback = group_cancel_cleanup_increment_cb; status = pTpAllocWork(&work, work_cb, &userdata, &environment); ok(!status, "TpAllocWork failed with status %lx\n", status); ok(work != NULL, "expected work != NULL\n");
/* post 10 identical work items at once */ userdata = userdata2 = 0; for (i = 0; i < 10; i++) pTpPostWork(work);
/* check if we get multiple cancellation callbacks */ group_cancel_tid = 0xdeadbeef; pTpReleaseCleanupGroupMembers(group, TRUE, &userdata2); ok(userdata <= 5, "expected userdata <= 5, got %lu\n", userdata); ok(userdata2 == 1, "expected only one cancellation callback, got %lu\n", userdata2); ok(group_cancel_tid == GetCurrentThreadId(), "expected tid %lx, got %lx\n", GetCurrentThreadId(), group_cancel_tid);
/* cleanup */ pTpReleaseCleanupGroup(group); pTpReleasePool(pool); CloseHandle(semaphores[0]); CloseHandle(semaphores[1]);}
static void CALLBACK instance_semaphore_completion_cb(TP_CALLBACK_INSTANCE *instance, void *userdata){ HANDLE *semaphores = userdata; pTpCallbackReleaseSemaphoreOnCompletion(instance, semaphores[0], 1);}
static void CALLBACK instance_finalization_cb(TP_CALLBACK_INSTANCE *instance, void *userdata){ HANDLE *semaphores = userdata; ReleaseSemaphore(semaphores[1], 1, NULL);}
static void test_tp_instance(void){ TP_CALLBACK_ENVIRON environment; HANDLE semaphores[2]; NTSTATUS status; TP_POOL *pool; DWORD result;
semaphores[0] = CreateSemaphoreW(NULL, 0, 1, NULL); ok(semaphores[0] != NULL, "failed to create semaphore\n"); semaphores[1] = CreateSemaphoreW(NULL, 0, 1, NULL); ok(semaphores[1] != NULL, "failed to create semaphore\n");
/* allocate new threadpool */ pool = NULL; status = pTpAllocPool(&pool, NULL); ok(!status, "TpAllocPool failed with status %lx\n", status); ok(pool != NULL, "expected pool != NULL\n");
/* test for TpCallbackReleaseSemaphoreOnCompletion */ memset(&environment, 0, sizeof(environment)); environment.Version = 1; environment.Pool = pool; status = pTpSimpleTryPost(instance_semaphore_completion_cb, semaphores, &environment); ok(!status, "TpSimpleTryPost failed with status %lx\n", status); result = WaitForSingleObject(semaphores[0], 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result);
/* test for finalization callback */ memset(&environment, 0, sizeof(environment)); environment.Version = 1; environment.Pool = pool; environment.FinalizationCallback = instance_finalization_cb; status = pTpSimpleTryPost(instance_semaphore_completion_cb, semaphores, &environment); ok(!status, "TpSimpleTryPost failed with status %lx\n", status); result = WaitForSingleObject(semaphores[0], 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); result = WaitForSingleObject(semaphores[1], 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result);
/* cleanup */ pTpReleasePool(pool); CloseHandle(semaphores[0]); CloseHandle(semaphores[1]);}
static void CALLBACK disassociate_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_WORK *work){ HANDLE *semaphores = userdata; DWORD result;
pTpDisassociateCallback(instance); result = WaitForSingleObject(semaphores[0], 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ReleaseSemaphore(semaphores[1], 1, NULL);}
static void CALLBACK disassociate2_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_WORK *work){ HANDLE *semaphores = userdata; DWORD result;
pTpDisassociateCallback(instance); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); ReleaseSemaphore(semaphores[1], 1, NULL);}
static void CALLBACK disassociate3_cb(TP_CALLBACK_INSTANCE *instance, void *userdata){ HANDLE *semaphores = userdata; DWORD result;
pTpDisassociateCallback(instance); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); ReleaseSemaphore(semaphores[1], 1, NULL);}
static void test_tp_disassociate(void){ TP_CALLBACK_ENVIRON environment; TP_CLEANUP_GROUP *group; HANDLE semaphores[2]; NTSTATUS status; TP_POOL *pool; TP_WORK *work; DWORD result;
semaphores[0] = CreateSemaphoreW(NULL, 0, 1, NULL); ok(semaphores[0] != NULL, "failed to create semaphore\n"); semaphores[1] = CreateSemaphoreW(NULL, 0, 1, NULL); ok(semaphores[1] != NULL, "failed to create semaphore\n");
/* allocate new threadpool and cleanup group */ pool = NULL; status = pTpAllocPool(&pool, NULL); ok(!status, "TpAllocPool failed with status %lx\n", status); ok(pool != NULL, "expected pool != NULL\n");
group = NULL; status = pTpAllocCleanupGroup(&group); ok(!status, "TpAllocCleanupGroup failed with status %lx\n", status); ok(group != NULL, "expected pool != NULL\n");
/* test TpDisassociateCallback on work objects without group */ work = NULL; memset(&environment, 0, sizeof(environment)); environment.Version = 1; environment.Pool = pool; status = pTpAllocWork(&work, disassociate_cb, semaphores, &environment); ok(!status, "TpAllocWork failed with status %lx\n", status); ok(work != NULL, "expected work != NULL\n");
pTpPostWork(work); pTpWaitForWork(work, FALSE);
result = WaitForSingleObject(semaphores[1], 100); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); ReleaseSemaphore(semaphores[0], 1, NULL); result = WaitForSingleObject(semaphores[1], 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); pTpReleaseWork(work);
/* test TpDisassociateCallback on work objects with group (1) */ work = NULL; memset(&environment, 0, sizeof(environment)); environment.Version = 1; environment.Pool = pool; environment.CleanupGroup = group; status = pTpAllocWork(&work, disassociate_cb, semaphores, &environment); ok(!status, "TpAllocWork failed with status %lx\n", status); ok(work != NULL, "expected work != NULL\n");
pTpPostWork(work); pTpWaitForWork(work, FALSE);
result = WaitForSingleObject(semaphores[1], 100); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); ReleaseSemaphore(semaphores[0], 1, NULL); result = WaitForSingleObject(semaphores[1], 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); pTpReleaseCleanupGroupMembers(group, FALSE, NULL);
/* test TpDisassociateCallback on work objects with group (2) */ work = NULL; memset(&environment, 0, sizeof(environment)); environment.Version = 1; environment.Pool = pool; environment.CleanupGroup = group; status = pTpAllocWork(&work, disassociate2_cb, semaphores, &environment); ok(!status, "TpAllocWork failed with status %lx\n", status); ok(work != NULL, "expected work != NULL\n");
pTpPostWork(work); pTpReleaseCleanupGroupMembers(group, FALSE, NULL);
ReleaseSemaphore(semaphores[0], 1, NULL); result = WaitForSingleObject(semaphores[1], 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); result = WaitForSingleObject(semaphores[0], 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result);
/* test TpDisassociateCallback on simple callbacks */ memset(&environment, 0, sizeof(environment)); environment.Version = 1; environment.Pool = pool; environment.CleanupGroup = group; status = pTpSimpleTryPost(disassociate3_cb, semaphores, &environment); ok(!status, "TpSimpleTryPost failed with status %lx\n", status);
pTpReleaseCleanupGroupMembers(group, FALSE, NULL);
ReleaseSemaphore(semaphores[0], 1, NULL); result = WaitForSingleObject(semaphores[1], 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); result = WaitForSingleObject(semaphores[0], 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result);
/* cleanup */ pTpReleaseCleanupGroup(group); pTpReleasePool(pool); CloseHandle(semaphores[0]); CloseHandle(semaphores[1]);}
static void CALLBACK timer_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_TIMER *timer){ HANDLE semaphore = userdata; ReleaseSemaphore(semaphore, 1, NULL);}
static void test_tp_timer(void){ TP_CALLBACK_ENVIRON environment; DWORD result, ticks; LARGE_INTEGER when; HANDLE semaphore; NTSTATUS status; TP_TIMER *timer; TP_POOL *pool; BOOL success; int i;
semaphore = CreateSemaphoreA(NULL, 0, 1, NULL); ok(semaphore != NULL, "CreateSemaphoreA failed %lu\n", GetLastError());
/* allocate new threadpool */ pool = NULL; status = pTpAllocPool(&pool, NULL); ok(!status, "TpAllocPool failed with status %lx\n", status); ok(pool != NULL, "expected pool != NULL\n");
/* allocate new timer */ timer = NULL; memset(&environment, 0, sizeof(environment)); environment.Version = 1; environment.Pool = pool; status = pTpAllocTimer(&timer, timer_cb, semaphore, &environment); ok(!status, "TpAllocTimer failed with status %lx\n", status); ok(timer != NULL, "expected timer != NULL\n");
success = pTpIsTimerSet(timer); ok(!success, "TpIsTimerSet returned TRUE\n");
/* test timer with a relative timeout */ when.QuadPart = (ULONGLONG)200 * -10000; pTpSetTimer(timer, &when, 0, 0); success = pTpIsTimerSet(timer); ok(success, "TpIsTimerSet returned FALSE\n");
pTpWaitForTimer(timer, FALSE);
result = WaitForSingleObject(semaphore, 100); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); result = WaitForSingleObject(semaphore, 200); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); success = pTpIsTimerSet(timer); ok(success, "TpIsTimerSet returned FALSE\n");
/* test timer with an absolute timeout */ NtQuerySystemTime( &when ); when.QuadPart += (ULONGLONG)200 * 10000; pTpSetTimer(timer, &when, 0, 0); success = pTpIsTimerSet(timer); ok(success, "TpIsTimerSet returned FALSE\n");
pTpWaitForTimer(timer, FALSE);
result = WaitForSingleObject(semaphore, 100); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); result = WaitForSingleObject(semaphore, 200); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); success = pTpIsTimerSet(timer); ok(success, "TpIsTimerSet returned FALSE\n");
/* test timer with zero timeout */ when.QuadPart = 0; pTpSetTimer(timer, &when, 0, 0); success = pTpIsTimerSet(timer); ok(success, "TpIsTimerSet returned FALSE\n");
pTpWaitForTimer(timer, FALSE);
result = WaitForSingleObject(semaphore, 50); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); success = pTpIsTimerSet(timer); ok(success, "TpIsTimerSet returned FALSE\n");
/* unset the timer */ pTpSetTimer(timer, NULL, 0, 0); success = pTpIsTimerSet(timer); ok(!success, "TpIsTimerSet returned TRUE\n"); pTpWaitForTimer(timer, TRUE);
pTpReleaseTimer(timer); CloseHandle(semaphore);
semaphore = CreateSemaphoreA(NULL, 0, 3, NULL); ok(semaphore != NULL, "CreateSemaphoreA failed %lu\n", GetLastError());
/* allocate a new timer */ timer = NULL; memset(&environment, 0, sizeof(environment)); environment.Version = 1; environment.Pool = pool; status = pTpAllocTimer(&timer, timer_cb, semaphore, &environment); ok(!status, "TpAllocTimer failed with status %lx\n", status); ok(timer != NULL, "expected timer != NULL\n");
/* test a relative timeout repeated periodically */ when.QuadPart = (ULONGLONG)200 * -10000; pTpSetTimer(timer, &when, 200, 0); success = pTpIsTimerSet(timer); ok(success, "TpIsTimerSet returned FALSE\n");
/* wait until the timer was triggered three times */ ticks = GetTickCount(); for (i = 0; i < 3; i++) { result = WaitForSingleObject(semaphore, 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); } ticks = GetTickCount() - ticks; ok(ticks >= 500 && (ticks <= 700 || broken(ticks <= 750)) /* Win 7 */, "expected approximately 600 ticks, got %lu\n", ticks);
/* unset the timer */ pTpSetTimer(timer, NULL, 0, 0); success = pTpIsTimerSet(timer); ok(!success, "TpIsTimerSet returned TRUE\n"); pTpWaitForTimer(timer, TRUE);
/* cleanup */ pTpReleaseTimer(timer); pTpReleasePool(pool); CloseHandle(semaphore);}
struct window_length_info{ HANDLE semaphore; DWORD ticks;};
static void CALLBACK window_length_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_TIMER *timer){ struct window_length_info *info = userdata; info->ticks = GetTickCount(); ReleaseSemaphore(info->semaphore, 1, NULL);}
static void test_tp_window_length(void){ struct window_length_info info1, info2; TP_CALLBACK_ENVIRON environment; TP_TIMER *timer1, *timer2; LARGE_INTEGER when; HANDLE semaphore; NTSTATUS status; TP_POOL *pool; DWORD result; BOOL merged;
semaphore = CreateSemaphoreA(NULL, 0, 2, NULL); ok(semaphore != NULL, "CreateSemaphoreA failed %lu\n", GetLastError());
/* allocate new threadpool */ pool = NULL; status = pTpAllocPool(&pool, NULL); ok(!status, "TpAllocPool failed with status %lx\n", status); ok(pool != NULL, "expected pool != NULL\n");
/* allocate two identical timers */ memset(&environment, 0, sizeof(environment)); environment.Version = 1; environment.Pool = pool;
timer1 = NULL; info1.semaphore = semaphore; status = pTpAllocTimer(&timer1, window_length_cb, &info1, &environment); ok(!status, "TpAllocTimer failed with status %lx\n", status); ok(timer1 != NULL, "expected timer1 != NULL\n");
timer2 = NULL; info2.semaphore = semaphore; status = pTpAllocTimer(&timer2, window_length_cb, &info2, &environment); ok(!status, "TpAllocTimer failed with status %lx\n", status); ok(timer2 != NULL, "expected timer2 != NULL\n");
/* choose parameters so that timers are not merged */ info1.ticks = 0; info2.ticks = 0;
NtQuerySystemTime( &when ); when.QuadPart += (ULONGLONG)250 * 10000; pTpSetTimer(timer2, &when, 0, 0); Sleep(50); when.QuadPart -= (ULONGLONG)150 * 10000; pTpSetTimer(timer1, &when, 0, 75);
result = WaitForSingleObject(semaphore, 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); result = WaitForSingleObject(semaphore, 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info1.ticks != 0 && info2.ticks != 0, "expected that ticks are nonzero\n"); ok(info2.ticks >= info1.ticks + 75 || broken(info2.ticks < info1.ticks + 75) /* Win 2008 */, "expected that timers are not merged\n");
/* timers will be merged */ info1.ticks = 0; info2.ticks = 0;
NtQuerySystemTime( &when ); when.QuadPart += (ULONGLONG)250 * 10000; pTpSetTimer(timer2, &when, 0, 0); Sleep(50); when.QuadPart -= (ULONGLONG)150 * 10000; pTpSetTimer(timer1, &when, 0, 200);
result = WaitForSingleObject(semaphore, 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); result = WaitForSingleObject(semaphore, 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info1.ticks != 0 && info2.ticks != 0, "expected that ticks are nonzero\n"); merged = info2.ticks >= info1.ticks - 50 && info2.ticks <= info1.ticks + 50; ok(merged || broken(!merged) /* Win 10 */, "expected that timers are merged\n");
/* on Windows the timers also get merged in this case */ info1.ticks = 0; info2.ticks = 0;
NtQuerySystemTime( &when ); when.QuadPart += (ULONGLONG)100 * 10000; pTpSetTimer(timer1, &when, 0, 200); Sleep(50); when.QuadPart += (ULONGLONG)150 * 10000; pTpSetTimer(timer2, &when, 0, 0);
result = WaitForSingleObject(semaphore, 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); result = WaitForSingleObject(semaphore, 1000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info1.ticks != 0 && info2.ticks != 0, "expected that ticks are nonzero\n"); merged = info2.ticks >= info1.ticks - 50 && info2.ticks <= info1.ticks + 50; todo_wine ok(merged || broken(!merged) /* Win 10 */, "expected that timers are merged\n");
/* cleanup */ pTpReleaseTimer(timer1); pTpReleaseTimer(timer2); pTpReleasePool(pool); CloseHandle(semaphore);}
struct wait_info{ HANDLE semaphore; LONG userdata;};
static void CALLBACK wait_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_WAIT *wait, TP_WAIT_RESULT result){ struct wait_info *info = userdata; if (result == WAIT_OBJECT_0) InterlockedIncrement(&info->userdata); else if (result == WAIT_TIMEOUT) InterlockedExchangeAdd(&info->userdata, 0x10000); else ok(0, "unexpected result %lu\n", result); ReleaseSemaphore(info->semaphore, 1, NULL);}
static void test_tp_wait(void){ TP_CALLBACK_ENVIRON environment; TP_WAIT *wait1, *wait2; struct wait_info info; HANDLE semaphores[2]; LARGE_INTEGER when; NTSTATUS status; TP_POOL *pool; DWORD result;
semaphores[0] = CreateSemaphoreW(NULL, 0, 2, NULL); ok(semaphores[0] != NULL, "failed to create semaphore\n"); semaphores[1] = CreateSemaphoreW(NULL, 0, 1, NULL); ok(semaphores[1] != NULL, "failed to create semaphore\n"); info.semaphore = semaphores[0];
/* allocate new threadpool */ pool = NULL; status = pTpAllocPool(&pool, NULL); ok(!status, "TpAllocPool failed with status %lx\n", status); ok(pool != NULL, "expected pool != NULL\n");
/* allocate new wait items */ memset(&environment, 0, sizeof(environment)); environment.Version = 1; environment.Pool = pool;
wait1 = NULL; status = pTpAllocWait(&wait1, wait_cb, &info, &environment); ok(!status, "TpAllocWait failed with status %lx\n", status); ok(wait1 != NULL, "expected wait1 != NULL\n");
wait2 = NULL; status = pTpAllocWait(&wait2, wait_cb, &info, &environment); ok(!status, "TpAllocWait failed with status %lx\n", status); ok(wait2 != NULL, "expected wait2 != NULL\n");
/* infinite timeout, signal the semaphore immediately */ info.userdata = 0; pTpSetWait(wait1, semaphores[1], NULL); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result);
/* relative timeout, no event */ info.userdata = 0; when.QuadPart = (ULONGLONG)200 * -10000; pTpSetWait(wait1, semaphores[1], &when); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 0, "expected info.userdata = 0, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[0], 200); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result);
/* repeat test with call to TpWaitForWait(..., TRUE) */ info.userdata = 0; when.QuadPart = (ULONGLONG)200 * -10000; pTpSetWait(wait1, semaphores[1], &when); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); pTpWaitForWait(wait1, TRUE); ok(info.userdata == 0, "expected info.userdata = 0, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[0], 200); ok(result == WAIT_OBJECT_0 || broken(result == WAIT_TIMEOUT) /* Win 8 */, "WaitForSingleObject returned %lu\n", result); if (result == WAIT_OBJECT_0) ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %lu\n", info.userdata); else ok(info.userdata == 0, "expected info.userdata = 0, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result);
/* relative timeout, with event */ info.userdata = 0; when.QuadPart = (ULONGLONG)200 * -10000; pTpSetWait(wait1, semaphores[1], &when); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 0, "expected info.userdata = 0, got %lu\n", info.userdata); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result);
/* repeat test with call to TpWaitForWait(..., TRUE) */ info.userdata = 0; when.QuadPart = (ULONGLONG)200 * -10000; pTpSetWait(wait1, semaphores[1], &when); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); pTpWaitForWait(wait1, TRUE); ok(info.userdata == 0, "expected info.userdata = 0, got %lu\n", info.userdata); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_OBJECT_0 || broken(result == WAIT_TIMEOUT) /* Win 8 */, "WaitForSingleObject returned %lu\n", result); if (result == WAIT_OBJECT_0) { ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); } else { ok(info.userdata == 0, "expected info.userdata = 0, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); }
/* absolute timeout, no event */ info.userdata = 0; NtQuerySystemTime( &when ); when.QuadPart += (ULONGLONG)200 * 10000; pTpSetWait(wait1, semaphores[1], &when); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 0, "expected info.userdata = 0, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[0], 200); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result);
/* absolute timeout, with event */ info.userdata = 0; NtQuerySystemTime( &when ); when.QuadPart += (ULONGLONG)200 * 10000; pTpSetWait(wait1, semaphores[1], &when); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 0, "expected info.userdata = 0, got %lu\n", info.userdata); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result);
/* test timeout of zero */ info.userdata = 0; when.QuadPart = 0; pTpSetWait(wait1, semaphores[1], &when); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result);
/* cancel a pending wait */ info.userdata = 0; when.QuadPart = (ULONGLONG)250 * -10000; pTpSetWait(wait1, semaphores[1], &when); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); pTpSetWait(wait1, NULL, (void *)0xdeadbeef); Sleep(50); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 0, "expected info.userdata = 0, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result);
/* test with INVALID_HANDLE_VALUE */ info.userdata = 0; when.QuadPart = 0; pTpSetWait(wait1, INVALID_HANDLE_VALUE, &when); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %lu\n", info.userdata);
/* cancel a pending wait with INVALID_HANDLE_VALUE */ info.userdata = 0; when.QuadPart = (ULONGLONG)250 * -10000; pTpSetWait(wait1, semaphores[1], &when); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); when.QuadPart = 0; pTpSetWait(wait1, INVALID_HANDLE_VALUE, &when); Sleep(50); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result);
CloseHandle(semaphores[1]); semaphores[1] = CreateSemaphoreW(NULL, 0, 2, NULL); ok(semaphores[1] != NULL, "failed to create semaphore\n");
/* add two wait objects with the same semaphore */ info.userdata = 0; pTpSetWait(wait1, semaphores[1], NULL); pTpSetWait(wait2, semaphores[1], NULL); Sleep(50); ReleaseSemaphore(semaphores[1], 1, NULL); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 1, "expected info.userdata = 1, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result);
/* repeat test above with release count 2 */ info.userdata = 0; pTpSetWait(wait1, semaphores[1], NULL); pTpSetWait(wait2, semaphores[1], NULL); Sleep(50); result = ReleaseSemaphore(semaphores[1], 2, NULL); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); result = WaitForSingleObject(semaphores[0], 100); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(info.userdata == 2, "expected info.userdata = 2, got %lu\n", info.userdata); result = WaitForSingleObject(semaphores[1], 0); ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %lu\n", result);
/* cleanup */ pTpReleaseWait(wait1); pTpReleaseWait(wait2); pTpReleasePool(pool); CloseHandle(semaphores[0]); CloseHandle(semaphores[1]);}
static struct{ HANDLE semaphore; DWORD result;} multi_wait_info;
static void CALLBACK multi_wait_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_WAIT *wait, TP_WAIT_RESULT result){ DWORD index = (DWORD)(DWORD_PTR)userdata;
if (result == WAIT_OBJECT_0) multi_wait_info.result = index; else if (result == WAIT_TIMEOUT) multi_wait_info.result = 0x10000 | index; else ok(0, "unexpected result %lu\n", result); ReleaseSemaphore(multi_wait_info.semaphore, 1, NULL);}
static void test_tp_multi_wait(void){ TP_POOL_STACK_INFORMATION stack_info; TP_CALLBACK_ENVIRON environment; HANDLE semaphores[512]; TP_WAIT *waits[512]; LARGE_INTEGER when; HANDLE semaphore; NTSTATUS status; TP_POOL *pool; DWORD result; int i;
semaphore = CreateSemaphoreW(NULL, 0, 512, NULL); ok(semaphore != NULL, "failed to create semaphore\n"); multi_wait_info.semaphore = semaphore;
/* allocate new threadpool */ pool = NULL; status = pTpAllocPool(&pool, NULL); ok(!status, "TpAllocPool failed with status %lx\n", status); ok(pool != NULL, "expected pool != NULL\n"); /* many threads -> use the smallest stack possible */ stack_info.StackReserve = 256 * 1024; stack_info.StackCommit = 4 * 1024; status = pTpSetPoolStackInformation(pool, &stack_info); ok(!status, "TpQueryPoolStackInformation failed: %lx\n", status);
memset(&environment, 0, sizeof(environment)); environment.Version = 1; environment.Pool = pool;
/* create semaphores and corresponding wait objects */ for (i = 0; i < ARRAY_SIZE(semaphores); i++) { semaphores[i] = CreateSemaphoreW(NULL, 0, 1, NULL); ok(semaphores[i] != NULL, "failed to create semaphore %i\n", i);
waits[i] = NULL; status = pTpAllocWait(&waits[i], multi_wait_cb, (void *)(DWORD_PTR)i, &environment); ok(!status, "TpAllocWait failed with status %lx\n", status); ok(waits[i] != NULL, "expected waits[%d] != NULL\n", i);
pTpSetWait(waits[i], semaphores[i], NULL); }
/* release all semaphores and wait for callback */ for (i = 0; i < ARRAY_SIZE(semaphores); i++) { multi_wait_info.result = 0; ReleaseSemaphore(semaphores[i], 1, NULL);
result = WaitForSingleObject(semaphore, 2000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(multi_wait_info.result == i, "expected result %d, got %lu\n", i, multi_wait_info.result);
pTpSetWait(waits[i], semaphores[i], NULL); }
/* repeat the same test in reverse order */ for (i = ARRAY_SIZE(semaphores) - 1; i >= 0; i--) { multi_wait_info.result = 0; ReleaseSemaphore(semaphores[i], 1, NULL);
result = WaitForSingleObject(semaphore, 2000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); ok(multi_wait_info.result == i, "expected result %d, got %lu\n", i, multi_wait_info.result);
pTpSetWait(waits[i], semaphores[i], NULL); }
/* test timeout of wait objects */ multi_wait_info.result = 0; for (i = 0; i < ARRAY_SIZE(semaphores); i++) { when.QuadPart = (ULONGLONG)50 * -10000; pTpSetWait(waits[i], semaphores[i], &when); }
for (i = 0; i < ARRAY_SIZE(semaphores); i++) { result = WaitForSingleObject(semaphore, 2000); ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %lu\n", result); }
ok(multi_wait_info.result >> 16, "expected multi_wait_info.result >> 16 != 0\n");
/* destroy the wait objects and semaphores while waiting */ for (i = 0; i < ARRAY_SIZE(semaphores); i++) { pTpSetWait(waits[i], semaphores[i], NULL); }
Sleep(50);
for (i = 0; i < ARRAY_SIZE(semaphores); i++) { pTpReleaseWait(waits[i]); NtClose(semaphores[i]); }
pTpReleasePool(pool); CloseHandle(semaphore);}
struct io_cb_ctx{ unsigned int count; void *ovl; NTSTATUS ret; ULONG_PTR length; TP_IO *io;};
static void CALLBACK io_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, void *cvalue, IO_STATUS_BLOCK *iosb, TP_IO *io){ struct io_cb_ctx *ctx = userdata; ++ctx->count; ctx->ovl = cvalue; ctx->ret = iosb->Status; ctx->length = iosb->Information; ctx->io = io;}
static DWORD WINAPI io_wait_thread(void *arg){ TP_IO *io = arg; pTpWaitForIoCompletion(io, FALSE); return 0;}
static void test_tp_io(void){ TP_CALLBACK_ENVIRON environment = {.Version = 1};#ifdef __REACTOS__ OVERLAPPED ovl = {0}, ovl2 = {0};#else OVERLAPPED ovl = {}, ovl2 = {};#endif HANDLE client, server, thread; struct io_cb_ctx userdata; char in[1], in2[1]; const char out[1]; NTSTATUS status; DWORD ret_size; TP_POOL *pool; TP_IO *io; BOOL ret;
ovl.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
status = pTpAllocPool(&pool, NULL); ok(!status, "failed to allocate pool, status %#lx\n", status);
server = CreateNamedPipeA("\\\\.\\pipe\\wine_tp_test", PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 0, 1, 1024, 1024, 0, NULL); ok(server != INVALID_HANDLE_VALUE, "Failed to create server pipe, error %lu.\n", GetLastError()); client = CreateFileA("\\\\.\\pipe\\wine_tp_test", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0); ok(client != INVALID_HANDLE_VALUE, "Failed to create client pipe, error %lu.\n", GetLastError());
environment.Pool = pool; io = NULL; status = pTpAllocIoCompletion(&io, server, io_cb, &userdata, &environment); ok(!status, "got %#lx\n", status); ok(!!io, "expected non-NULL TP_IO\n");
pTpWaitForIoCompletion(io, FALSE);
userdata.count = 0; pTpStartAsyncIoOperation(io);
thread = CreateThread(NULL, 0, io_wait_thread, io, 0, NULL); ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "TpWaitForIoCompletion() should not return\n");
ret = ReadFile(server, in, sizeof(in), NULL, &ovl); ok(!ret, "wrong ret %d\n", ret); ok(GetLastError() == ERROR_IO_PENDING, "wrong error %lu\n", GetLastError());
ret = WriteFile(client, out, sizeof(out), &ret_size, NULL); ok(ret, "WriteFile() failed, error %lu\n", GetLastError());
pTpWaitForIoCompletion(io, FALSE); ok(userdata.count == 1, "callback ran %u times\n", userdata.count); ok(userdata.ovl == &ovl, "expected %p, got %p\n", &ovl, userdata.ovl); ok(userdata.ret == STATUS_SUCCESS, "got status %#lx\n", userdata.ret); ok(userdata.length == 1, "got length %Iu\n", userdata.length); ok(userdata.io == io, "expected %p, got %p\n", io, userdata.io);
ok(!WaitForSingleObject(thread, 1000), "wait timed out\n"); CloseHandle(thread);
userdata.count = 0; pTpStartAsyncIoOperation(io); pTpStartAsyncIoOperation(io);
ret = ReadFile(server, in, sizeof(in), NULL, &ovl); ok(!ret, "wrong ret %d\n", ret); ok(GetLastError() == ERROR_IO_PENDING, "wrong error %lu\n", GetLastError()); ret = ReadFile(server, in2, sizeof(in2), NULL, &ovl2); ok(!ret, "wrong ret %d\n", ret); ok(GetLastError() == ERROR_IO_PENDING, "wrong error %lu\n", GetLastError());
ret = WriteFile(client, out, sizeof(out), &ret_size, NULL); ok(ret, "WriteFile() failed, error %lu\n", GetLastError()); ret = WriteFile(client, out, sizeof(out), &ret_size, NULL); ok(ret, "WriteFile() failed, error %lu\n", GetLastError());
pTpWaitForIoCompletion(io, FALSE); ok(userdata.count == 2, "callback ran %u times\n", userdata.count); ok(userdata.ret == STATUS_SUCCESS, "got status %#lx\n", userdata.ret); ok(userdata.length == 1, "got length %Iu\n", userdata.length); ok(userdata.io == io, "expected %p, got %p\n", io, userdata.io);
/* The documentation is a bit unclear about passing TRUE to * WaitForThreadpoolIoCallbacks()—"pending I/O requests are not canceled" * [as with CancelIoEx()], but pending threadpool callbacks are, even those * which have not yet reached the completion port [as with * TpCancelAsyncIoOperation()]. */ userdata.count = 0; pTpStartAsyncIoOperation(io);
pTpWaitForIoCompletion(io, TRUE); ok(!userdata.count, "callback ran %u times\n", userdata.count);
pTpStartAsyncIoOperation(io);
ret = WriteFile(client, out, sizeof(out), &ret_size, NULL); ok(ret, "WriteFile() failed, error %lu\n", GetLastError());
ret = ReadFile(server, in, sizeof(in), NULL, &ovl); ok(ret, "wrong ret %d\n", ret);
pTpWaitForIoCompletion(io, FALSE); ok(userdata.count == 1, "callback ran %u times\n", userdata.count); ok(userdata.ovl == &ovl, "expected %p, got %p\n", &ovl, userdata.ovl); ok(userdata.ret == STATUS_SUCCESS, "got status %#lx\n", userdata.ret); ok(userdata.length == 1, "got length %Iu\n", userdata.length); ok(userdata.io == io, "expected %p, got %p\n", io, userdata.io);
userdata.count = 0; pTpStartAsyncIoOperation(io);
ret = ReadFile(server, NULL, 1, NULL, &ovl); ok(!ret, "wrong ret %d\n", ret); ok(GetLastError() == ERROR_NOACCESS, "wrong error %lu\n", GetLastError());
pTpCancelAsyncIoOperation(io); pTpWaitForIoCompletion(io, FALSE); ok(!userdata.count, "callback ran %u times\n", userdata.count);
userdata.count = 0; pTpStartAsyncIoOperation(io);
ret = ReadFile(server, in, sizeof(in), NULL, &ovl); ok(!ret, "wrong ret %d\n", ret); ok(GetLastError() == ERROR_IO_PENDING, "wrong error %lu\n", GetLastError()); ret = CancelIo(server); ok(ret, "CancelIo() failed, error %lu\n", GetLastError());
pTpWaitForIoCompletion(io, FALSE); ok(userdata.count == 1, "callback ran %u times\n", userdata.count); ok(userdata.ovl == &ovl, "expected %p, got %p\n", &ovl, userdata.ovl); ok(userdata.ret == STATUS_CANCELLED, "got status %#lx\n", userdata.ret); ok(!userdata.length, "got length %Iu\n", userdata.length); ok(userdata.io == io, "expected %p, got %p\n", io, userdata.io);
userdata.count = 0; pTpStartAsyncIoOperation(io); pTpCancelAsyncIoOperation(io); ret = ReadFile(server, in, sizeof(in), NULL, &ovl); ok(!ret, "wrong ret %d\n", ret); ret = WriteFile(client, out, sizeof(out), &ret_size, NULL); ok(ret, "WriteFile() failed, error %lu\n", GetLastError()); ok(GetLastError() == ERROR_IO_PENDING, "wrong error %lu\n", GetLastError());
pTpWaitForIoCompletion(io, FALSE); if (0) { /* Add a sleep to check that callback is not called later. Commented out to * save the test time. */ Sleep(200); } ok(userdata.count == 0, "callback ran %u times\n", userdata.count);
pTpReleaseIoCompletion(io); CloseHandle(server);
/* Test TPIO object destruction. */ server = CreateNamedPipeA("\\\\.\\pipe\\wine_tp_test", PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 0, 1, 1024, 1024, 0, NULL); ok(server != INVALID_HANDLE_VALUE, "Failed to create server pipe, error %lu.\n", GetLastError()); io = NULL; status = pTpAllocIoCompletion(&io, server, io_cb, &userdata, &environment); ok(!status, "got %#lx\n", status);
ret = HeapValidate(GetProcessHeap(), 0, io); ok(ret, "Got unexpected ret %#x.\n", ret); pTpReleaseIoCompletion(io); ret = HeapValidate(GetProcessHeap(), 0, io); ok(!ret, "Got unexpected ret %#x.\n", ret); CloseHandle(server); CloseHandle(client);
server = CreateNamedPipeA("\\\\.\\pipe\\wine_tp_test", PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 0, 1, 1024, 1024, 0, NULL); ok(server != INVALID_HANDLE_VALUE, "Failed to create server pipe, error %lu.\n", GetLastError()); client = CreateFileA("\\\\.\\pipe\\wine_tp_test", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0); ok(client != INVALID_HANDLE_VALUE, "Failed to create client pipe, error %lu.\n", GetLastError());
io = NULL; status = pTpAllocIoCompletion(&io, server, io_cb, &userdata, &environment); ok(!status, "got %#lx\n", status); pTpStartAsyncIoOperation(io); pTpWaitForIoCompletion(io, TRUE); ret = HeapValidate(GetProcessHeap(), 0, io); ok(ret, "Got unexpected ret %#x.\n", ret); pTpReleaseIoCompletion(io); ret = HeapValidate(GetProcessHeap(), 0, io); ok(ret, "Got unexpected ret %#x.\n", ret);
if (0) { /* Object destruction will wait until one completion arrives (which was started but not cancelled). * Commented out to save test time. */ Sleep(1000); ret = HeapValidate(GetProcessHeap(), 0, io); ok(ret, "Got unexpected ret %#x.\n", ret); ret = ReadFile(server, in, sizeof(in), NULL, &ovl); ok(!ret, "wrong ret %d\n", ret); ret = WriteFile(client, out, sizeof(out), &ret_size, NULL); ok(ret, "WriteFile() failed, error %lu\n", GetLastError()); Sleep(2000); ret = HeapValidate(GetProcessHeap(), 0, io); ok(!ret, "Got unexpected ret %#x.\n", ret); }
CloseHandle(server); CloseHandle(ovl.hEvent); CloseHandle(client); pTpReleasePool(pool);}
static void CALLBACK kernel32_io_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, void *ovl, ULONG ret, ULONG_PTR length, TP_IO *io){ struct io_cb_ctx *ctx = userdata; ++ctx->count; ctx->ovl = ovl; ctx->ret = ret; ctx->length = length; ctx->io = io;}
static void test_kernel32_tp_io(void){ TP_CALLBACK_ENVIRON environment = {.Version = 1};#ifdef __REACTOS__ OVERLAPPED ovl = {0}, ovl2 = {0};#else OVERLAPPED ovl = {}, ovl2 = {};#endif HANDLE client, server, thread; struct io_cb_ctx userdata; char in[1], in2[1]; const char out[1]; NTSTATUS status; DWORD ret_size; TP_POOL *pool; TP_IO *io; BOOL ret;
ovl.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
status = pTpAllocPool(&pool, NULL); ok(!status, "failed to allocate pool, status %#lx\n", status);
server = CreateNamedPipeA("\\\\.\\pipe\\wine_tp_test", PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 0, 1, 1024, 1024, 0, NULL); ok(server != INVALID_HANDLE_VALUE, "Failed to create server pipe, error %lu.\n", GetLastError()); client = CreateFileA("\\\\.\\pipe\\wine_tp_test", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0); ok(client != INVALID_HANDLE_VALUE, "Failed to create client pipe, error %lu.\n", GetLastError());
environment.Pool = pool; io = NULL; io = pCreateThreadpoolIo(server, kernel32_io_cb, &userdata, &environment); ok(!!io, "expected non-NULL TP_IO\n");
pWaitForThreadpoolIoCallbacks(io, FALSE);
userdata.count = 0; pStartThreadpoolIo(io);
thread = CreateThread(NULL, 0, io_wait_thread, io, 0, NULL); ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "TpWaitForIoCompletion() should not return\n");
ret = ReadFile(server, in, sizeof(in), NULL, &ovl); ok(!ret, "wrong ret %d\n", ret); ok(GetLastError() == ERROR_IO_PENDING, "wrong error %lu\n", GetLastError());
ret = WriteFile(client, out, sizeof(out), &ret_size, NULL); ok(ret, "WriteFile() failed, error %lu\n", GetLastError());
pWaitForThreadpoolIoCallbacks(io, FALSE); ok(userdata.count == 1, "callback ran %u times\n", userdata.count); ok(userdata.ovl == &ovl, "expected %p, got %p\n", &ovl, userdata.ovl); ok(userdata.ret == ERROR_SUCCESS, "got status %#lx\n", userdata.ret); ok(userdata.length == 1, "got length %Iu\n", userdata.length); ok(userdata.io == io, "expected %p, got %p\n", io, userdata.io);
ok(!WaitForSingleObject(thread, 1000), "wait timed out\n"); CloseHandle(thread);
userdata.count = 0; pStartThreadpoolIo(io); pStartThreadpoolIo(io);
ret = ReadFile(server, in, sizeof(in), NULL, &ovl); ok(!ret, "wrong ret %d\n", ret); ok(GetLastError() == ERROR_IO_PENDING, "wrong error %lu\n", GetLastError()); ret = ReadFile(server, in2, sizeof(in2), NULL, &ovl2); ok(!ret, "wrong ret %d\n", ret); ok(GetLastError() == ERROR_IO_PENDING, "wrong error %lu\n", GetLastError());
ret = WriteFile(client, out, sizeof(out), &ret_size, NULL); ok(ret, "WriteFile() failed, error %lu\n", GetLastError()); ret = WriteFile(client, out, sizeof(out), &ret_size, NULL); ok(ret, "WriteFile() failed, error %lu\n", GetLastError());
pWaitForThreadpoolIoCallbacks(io, FALSE); ok(userdata.count == 2, "callback ran %u times\n", userdata.count); ok(userdata.ret == STATUS_SUCCESS, "got status %#lx\n", userdata.ret); ok(userdata.length == 1, "got length %Iu\n", userdata.length); ok(userdata.io == io, "expected %p, got %p\n", io, userdata.io);
userdata.count = 0; pStartThreadpoolIo(io); pWaitForThreadpoolIoCallbacks(io, TRUE); ok(!userdata.count, "callback ran %u times\n", userdata.count);
pStartThreadpoolIo(io);
ret = WriteFile(client, out, sizeof(out), &ret_size, NULL); ok(ret, "WriteFile() failed, error %lu\n", GetLastError());
ret = ReadFile(server, in, sizeof(in), NULL, &ovl); ok(ret, "wrong ret %d\n", ret);
pWaitForThreadpoolIoCallbacks(io, FALSE); ok(userdata.count == 1, "callback ran %u times\n", userdata.count); ok(userdata.ovl == &ovl, "expected %p, got %p\n", &ovl, userdata.ovl); ok(userdata.ret == ERROR_SUCCESS, "got status %#lx\n", userdata.ret); ok(userdata.length == 1, "got length %Iu\n", userdata.length); ok(userdata.io == io, "expected %p, got %p\n", io, userdata.io);
userdata.count = 0; pStartThreadpoolIo(io);
ret = ReadFile(server, NULL, 1, NULL, &ovl); ok(!ret, "wrong ret %d\n", ret); ok(GetLastError() == ERROR_NOACCESS, "wrong error %lu\n", GetLastError());
pCancelThreadpoolIo(io); pWaitForThreadpoolIoCallbacks(io, FALSE); ok(!userdata.count, "callback ran %u times\n", userdata.count);
userdata.count = 0; pStartThreadpoolIo(io);
ret = ReadFile(server, in, sizeof(in), NULL, &ovl); ok(!ret, "wrong ret %d\n", ret); ok(GetLastError() == ERROR_IO_PENDING, "wrong error %lu\n", GetLastError()); ret = CancelIo(server); ok(ret, "CancelIo() failed, error %lu\n", GetLastError());
pWaitForThreadpoolIoCallbacks(io, FALSE); ok(userdata.count == 1, "callback ran %u times\n", userdata.count); ok(userdata.ovl == &ovl, "expected %p, got %p\n", &ovl, userdata.ovl); ok(userdata.ret == ERROR_OPERATION_ABORTED, "got status %#lx\n", userdata.ret); ok(!userdata.length, "got length %Iu\n", userdata.length); ok(userdata.io == io, "expected %p, got %p\n", io, userdata.io);
CloseHandle(ovl.hEvent); CloseHandle(client); CloseHandle(server); pCloseThreadpoolIo(io); pTpReleasePool(pool);}
START_TEST(threadpool){ test_RtlQueueWorkItem(); test_RtlRegisterWait();
if (!init_threadpool()) return;
test_tp_simple(); test_tp_work(); test_tp_work_scheduler(); test_tp_group_wait(); test_tp_group_cancel(); test_tp_instance(); test_tp_disassociate(); test_tp_timer(); test_tp_window_length(); test_tp_wait(); test_tp_multi_wait(); test_tp_io(); test_kernel32_tp_io();}