ntdll/tests: Add basic tests for threadpool wait objects.
[wine/multimedia.git] / dlls / ntdll / tests / threadpool.c
blobb40e0c10f2e3f39eeee71ceba5caa4ec3a35c1bc
1 /*
2 * Unit test suite for thread pool functions
4 * Copyright 2015 Sebastian Lackner
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #include "ntdll_test.h"
23 static HMODULE hntdll = 0;
24 static NTSTATUS (WINAPI *pTpAllocCleanupGroup)(TP_CLEANUP_GROUP **);
25 static NTSTATUS (WINAPI *pTpAllocPool)(TP_POOL **,PVOID);
26 static NTSTATUS (WINAPI *pTpAllocTimer)(TP_TIMER **,PTP_TIMER_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *);
27 static NTSTATUS (WINAPI *pTpAllocWait)(TP_WAIT **,PTP_WAIT_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *);
28 static NTSTATUS (WINAPI *pTpAllocWork)(TP_WORK **,PTP_WORK_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *);
29 static NTSTATUS (WINAPI *pTpCallbackMayRunLong)(TP_CALLBACK_INSTANCE *);
30 static VOID (WINAPI *pTpCallbackReleaseSemaphoreOnCompletion)(TP_CALLBACK_INSTANCE *,HANDLE,DWORD);
31 static VOID (WINAPI *pTpDisassociateCallback)(TP_CALLBACK_INSTANCE *);
32 static BOOL (WINAPI *pTpIsTimerSet)(TP_TIMER *);
33 static VOID (WINAPI *pTpReleaseWait)(TP_WAIT *);
34 static VOID (WINAPI *pTpPostWork)(TP_WORK *);
35 static VOID (WINAPI *pTpReleaseCleanupGroup)(TP_CLEANUP_GROUP *);
36 static VOID (WINAPI *pTpReleaseCleanupGroupMembers)(TP_CLEANUP_GROUP *,BOOL,PVOID);
37 static VOID (WINAPI *pTpReleasePool)(TP_POOL *);
38 static VOID (WINAPI *pTpReleaseTimer)(TP_TIMER *);
39 static VOID (WINAPI *pTpReleaseWork)(TP_WORK *);
40 static VOID (WINAPI *pTpSetPoolMaxThreads)(TP_POOL *,DWORD);
41 static VOID (WINAPI *pTpSetTimer)(TP_TIMER *,LARGE_INTEGER *,LONG,LONG);
42 static VOID (WINAPI *pTpSetWait)(TP_WAIT *,HANDLE,LARGE_INTEGER *);
43 static NTSTATUS (WINAPI *pTpSimpleTryPost)(PTP_SIMPLE_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *);
44 static VOID (WINAPI *pTpWaitForTimer)(TP_TIMER *,BOOL);
45 static VOID (WINAPI *pTpWaitForWait)(TP_WAIT *,BOOL);
46 static VOID (WINAPI *pTpWaitForWork)(TP_WORK *,BOOL);
48 #define NTDLL_GET_PROC(func) \
49 do \
50 { \
51 p ## func = (void *)GetProcAddress(hntdll, #func); \
52 if (!p ## func) trace("Failed to get address for %s\n", #func); \
53 } \
54 while (0)
56 static BOOL init_threadpool(void)
58 hntdll = GetModuleHandleA("ntdll");
59 if (!hntdll)
61 win_skip("Could not load ntdll\n");
62 return FALSE;
65 NTDLL_GET_PROC(TpAllocCleanupGroup);
66 NTDLL_GET_PROC(TpAllocPool);
67 NTDLL_GET_PROC(TpAllocTimer);
68 NTDLL_GET_PROC(TpAllocWait);
69 NTDLL_GET_PROC(TpAllocWork);
70 NTDLL_GET_PROC(TpCallbackMayRunLong);
71 NTDLL_GET_PROC(TpCallbackReleaseSemaphoreOnCompletion);
72 NTDLL_GET_PROC(TpDisassociateCallback);
73 NTDLL_GET_PROC(TpIsTimerSet);
74 NTDLL_GET_PROC(TpPostWork);
75 NTDLL_GET_PROC(TpReleaseCleanupGroup);
76 NTDLL_GET_PROC(TpReleaseCleanupGroupMembers);
77 NTDLL_GET_PROC(TpReleasePool);
78 NTDLL_GET_PROC(TpReleaseTimer);
79 NTDLL_GET_PROC(TpReleaseWait);
80 NTDLL_GET_PROC(TpReleaseWork);
81 NTDLL_GET_PROC(TpSetPoolMaxThreads);
82 NTDLL_GET_PROC(TpSetTimer);
83 NTDLL_GET_PROC(TpSetWait);
84 NTDLL_GET_PROC(TpSimpleTryPost);
85 NTDLL_GET_PROC(TpWaitForTimer);
86 NTDLL_GET_PROC(TpWaitForWait);
87 NTDLL_GET_PROC(TpWaitForWork);
89 if (!pTpAllocPool)
91 win_skip("Threadpool functions not supported, skipping tests\n");
92 return FALSE;
95 return TRUE;
98 #undef NTDLL_GET_PROC
101 static void CALLBACK simple_cb(TP_CALLBACK_INSTANCE *instance, void *userdata)
103 HANDLE semaphore = userdata;
104 trace("Running simple callback\n");
105 ReleaseSemaphore(semaphore, 1, NULL);
108 static void CALLBACK simple2_cb(TP_CALLBACK_INSTANCE *instance, void *userdata)
110 trace("Running simple2 callback\n");
111 Sleep(50);
112 InterlockedIncrement((LONG *)userdata);
115 static void test_tp_simple(void)
117 TP_CALLBACK_ENVIRON environment;
118 TP_CLEANUP_GROUP *group;
119 HANDLE semaphore;
120 NTSTATUS status;
121 TP_POOL *pool;
122 LONG userdata;
123 DWORD result;
124 int i;
126 semaphore = CreateSemaphoreA(NULL, 0, 1, NULL);
127 ok(semaphore != NULL, "CreateSemaphoreA failed %u\n", GetLastError());
129 /* post the callback using the default threadpool */
130 memset(&environment, 0, sizeof(environment));
131 environment.Version = 1;
132 environment.Pool = NULL;
133 status = pTpSimpleTryPost(simple_cb, semaphore, &environment);
134 ok(!status, "TpSimpleTryPost failed with status %x\n", status);
135 result = WaitForSingleObject(semaphore, 1000);
136 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
138 /* allocate new threadpool */
139 pool = NULL;
140 status = pTpAllocPool(&pool, NULL);
141 ok(!status, "TpAllocPool failed with status %x\n", status);
142 ok(pool != NULL, "expected pool != NULL\n");
144 /* post the callback using the new threadpool */
145 memset(&environment, 0, sizeof(environment));
146 environment.Version = 1;
147 environment.Pool = pool;
148 status = pTpSimpleTryPost(simple_cb, semaphore, &environment);
149 ok(!status, "TpSimpleTryPost failed with status %x\n", status);
150 result = WaitForSingleObject(semaphore, 1000);
151 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
153 /* test with invalid version number */
154 memset(&environment, 0, sizeof(environment));
155 environment.Version = 9999;
156 environment.Pool = pool;
157 status = pTpSimpleTryPost(simple_cb, semaphore, &environment);
158 todo_wine
159 ok(status == STATUS_INVALID_PARAMETER || broken(!status) /* Vista/2008 */,
160 "TpSimpleTryPost unexpectedly returned status %x\n", status);
161 if (!status)
163 result = WaitForSingleObject(semaphore, 1000);
164 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
167 /* allocate a cleanup group for synchronization */
168 group = NULL;
169 status = pTpAllocCleanupGroup(&group);
170 ok(!status, "TpAllocCleanupGroup failed with status %x\n", status);
171 ok(group != NULL, "expected pool != NULL\n");
173 /* use cleanup group to wait for a simple callback */
174 userdata = 0;
175 memset(&environment, 0, sizeof(environment));
176 environment.Version = 1;
177 environment.Pool = pool;
178 environment.CleanupGroup = group;
179 status = pTpSimpleTryPost(simple2_cb, &userdata, &environment);
180 ok(!status, "TpSimpleTryPost failed with status %x\n", status);
181 pTpReleaseCleanupGroupMembers(group, FALSE, NULL);
182 ok(userdata == 1, "expected userdata = 1, got %u\n", userdata);
184 /* test cancellation of pending simple callbacks */
185 userdata = 0;
186 memset(&environment, 0, sizeof(environment));
187 environment.Version = 1;
188 environment.Pool = pool;
189 environment.CleanupGroup = group;
190 for (i = 0; i < 100; i++)
192 status = pTpSimpleTryPost(simple2_cb, &userdata, &environment);
193 ok(!status, "TpSimpleTryPost failed with status %x\n", status);
195 pTpReleaseCleanupGroupMembers(group, TRUE, NULL);
196 ok(userdata < 100, "expected userdata < 100, got %u\n", userdata);
198 /* cleanup */
199 pTpReleaseCleanupGroup(group);
200 pTpReleasePool(pool);
201 CloseHandle(semaphore);
204 static void CALLBACK work_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_WORK *work)
206 trace("Running work callback\n");
207 Sleep(10);
208 InterlockedIncrement((LONG *)userdata);
211 static void CALLBACK work2_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_WORK *work)
213 trace("Running work2 callback\n");
214 Sleep(10);
215 InterlockedExchangeAdd((LONG *)userdata, 0x10000);
218 static void test_tp_work(void)
220 TP_CALLBACK_ENVIRON environment;
221 TP_WORK *work;
222 TP_POOL *pool;
223 NTSTATUS status;
224 LONG userdata;
225 int i;
227 /* allocate new threadpool */
228 pool = NULL;
229 status = pTpAllocPool(&pool, NULL);
230 ok(!status, "TpAllocPool failed with status %x\n", status);
231 ok(pool != NULL, "expected pool != NULL\n");
233 /* allocate new work item */
234 work = NULL;
235 memset(&environment, 0, sizeof(environment));
236 environment.Version = 1;
237 environment.Pool = pool;
238 status = pTpAllocWork(&work, work_cb, &userdata, &environment);
239 ok(!status, "TpAllocWork failed with status %x\n", status);
240 ok(work != NULL, "expected work != NULL\n");
242 /* post 10 identical work items at once */
243 userdata = 0;
244 for (i = 0; i < 10; i++)
245 pTpPostWork(work);
246 pTpWaitForWork(work, FALSE);
247 ok(userdata == 10, "expected userdata = 10, got %u\n", userdata);
249 /* add more tasks and cancel them immediately */
250 userdata = 0;
251 for (i = 0; i < 10; i++)
252 pTpPostWork(work);
253 pTpWaitForWork(work, TRUE);
254 ok(userdata < 10, "expected userdata < 10, got %u\n", userdata);
256 /* cleanup */
257 pTpReleaseWork(work);
258 pTpReleasePool(pool);
261 static void test_tp_work_scheduler(void)
263 TP_CALLBACK_ENVIRON environment;
264 TP_CLEANUP_GROUP *group;
265 TP_WORK *work, *work2;
266 TP_POOL *pool;
267 NTSTATUS status;
268 LONG userdata;
269 int i;
271 /* allocate new threadpool */
272 pool = NULL;
273 status = pTpAllocPool(&pool, NULL);
274 ok(!status, "TpAllocPool failed with status %x\n", status);
275 ok(pool != NULL, "expected pool != NULL\n");
277 /* we limit the pool to a single thread */
278 pTpSetPoolMaxThreads(pool, 1);
280 /* create a cleanup group */
281 group = NULL;
282 status = pTpAllocCleanupGroup(&group);
283 ok(!status, "TpAllocCleanupGroup failed with status %x\n", status);
284 ok(group != NULL, "expected pool != NULL\n");
286 /* the first work item has no cleanup group associated */
287 work = NULL;
288 memset(&environment, 0, sizeof(environment));
289 environment.Version = 1;
290 environment.Pool = pool;
291 status = pTpAllocWork(&work, work_cb, &userdata, &environment);
292 ok(!status, "TpAllocWork failed with status %x\n", status);
293 ok(work != NULL, "expected work != NULL\n");
295 /* allocate a second work item with a cleanup group */
296 work2 = NULL;
297 memset(&environment, 0, sizeof(environment));
298 environment.Version = 1;
299 environment.Pool = pool;
300 environment.CleanupGroup = group;
301 status = pTpAllocWork(&work2, work2_cb, &userdata, &environment);
302 ok(!status, "TpAllocWork failed with status %x\n", status);
303 ok(work2 != NULL, "expected work2 != NULL\n");
305 /* the 'work' callbacks are not blocking execution of 'work2' callbacks */
306 userdata = 0;
307 for (i = 0; i < 10; i++)
308 pTpPostWork(work);
309 for (i = 0; i < 10; i++)
310 pTpPostWork(work2);
311 Sleep(30);
312 pTpWaitForWork(work, TRUE);
313 pTpWaitForWork(work2, TRUE);
314 ok(userdata & 0xffff, "expected userdata & 0xffff != 0, got %u\n", userdata & 0xffff);
315 ok(userdata >> 16, "expected userdata >> 16 != 0, got %u\n", userdata >> 16);
317 /* test TpReleaseCleanupGroupMembers on a work item */
318 userdata = 0;
319 for (i = 0; i < 100; i++)
320 pTpPostWork(work);
321 for (i = 0; i < 10; i++)
322 pTpPostWork(work2);
323 pTpReleaseCleanupGroupMembers(group, FALSE, NULL);
324 pTpWaitForWork(work, TRUE);
325 ok((userdata & 0xffff) < 100, "expected userdata & 0xffff < 100, got %u\n", userdata & 0xffff);
326 ok((userdata >> 16) == 10, "expected userdata >> 16 == 10, got %u\n", userdata >> 16);
328 /* cleanup */
329 pTpReleaseWork(work);
330 pTpReleaseCleanupGroup(group);
331 pTpReleasePool(pool);
334 static DWORD group_cancel_tid;
336 static void CALLBACK group_cancel_cb(TP_CALLBACK_INSTANCE *instance, void *userdata)
338 HANDLE *semaphores = userdata;
339 NTSTATUS status;
340 DWORD result;
342 trace("Running group cancel callback\n");
344 status = pTpCallbackMayRunLong(instance);
345 ok(status == STATUS_TOO_MANY_THREADS || broken(status == 1) /* Win Vista / 2008 */,
346 "expected STATUS_TOO_MANY_THREADS, got %08x\n", status);
348 result = WaitForSingleObject(semaphores[0], 1000);
349 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
350 ReleaseSemaphore(semaphores[1], 1, NULL);
353 static void CALLBACK group_cancel_cleanup_release_cb(void *object, void *userdata)
355 HANDLE *semaphores = userdata;
356 trace("Running group cancel cleanup release callback\n");
357 group_cancel_tid = GetCurrentThreadId();
358 ReleaseSemaphore(semaphores[0], 1, NULL);
361 static void CALLBACK group_cancel_cleanup_increment_cb(void *object, void *userdata)
363 trace("Running group cancel cleanup increment callback\n");
364 group_cancel_tid = GetCurrentThreadId();
365 InterlockedIncrement((LONG *)userdata);
368 static void CALLBACK unexpected_cb(TP_CALLBACK_INSTANCE *instance, void *userdata)
370 ok(0, "Unexpected callback\n");
373 static void test_tp_group_cancel(void)
375 TP_CALLBACK_ENVIRON environment;
376 TP_CLEANUP_GROUP *group;
377 LONG userdata, userdata2;
378 HANDLE semaphores[2];
379 NTSTATUS status;
380 TP_WORK *work;
381 TP_POOL *pool;
382 DWORD result;
383 int i;
385 semaphores[0] = CreateSemaphoreA(NULL, 0, 1, NULL);
386 ok(semaphores[0] != NULL, "CreateSemaphoreA failed %u\n", GetLastError());
387 semaphores[1] = CreateSemaphoreA(NULL, 0, 1, NULL);
388 ok(semaphores[1] != NULL, "CreateSemaphoreA failed %u\n", GetLastError());
390 /* allocate new threadpool with only one thread */
391 pool = NULL;
392 status = pTpAllocPool(&pool, NULL);
393 ok(!status, "TpAllocPool failed with status %x\n", status);
394 ok(pool != NULL, "expected pool != NULL\n");
395 pTpSetPoolMaxThreads(pool, 1);
397 /* allocate a cleanup group */
398 group = NULL;
399 status = pTpAllocCleanupGroup(&group);
400 ok(!status, "TpAllocCleanupGroup failed with status %x\n", status);
401 ok(group != NULL, "expected pool != NULL\n");
403 /* test execution of cancellation callback */
404 memset(&environment, 0, sizeof(environment));
405 environment.Version = 1;
406 environment.Pool = pool;
407 status = pTpSimpleTryPost(group_cancel_cb, semaphores, &environment);
408 ok(!status, "TpSimpleTryPost failed with status %x\n", status);
410 memset(&environment, 0, sizeof(environment));
411 environment.Version = 1;
412 environment.Pool = pool;
413 environment.CleanupGroup = group;
414 environment.CleanupGroupCancelCallback = group_cancel_cleanup_release_cb;
415 status = pTpSimpleTryPost(unexpected_cb, NULL, &environment);
416 ok(!status, "TpSimpleTryPost failed with status %x\n", status);
418 group_cancel_tid = 0xdeadbeef;
419 pTpReleaseCleanupGroupMembers(group, TRUE, semaphores);
420 result = WaitForSingleObject(semaphores[1], 1000);
421 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
422 ok(group_cancel_tid == GetCurrentThreadId(), "expected tid %x, got %x\n",
423 GetCurrentThreadId(), group_cancel_tid);
425 /* test cancellation callback for objects with multiple instances */
426 work = NULL;
427 memset(&environment, 0, sizeof(environment));
428 environment.Version = 1;
429 environment.Pool = pool;
430 environment.CleanupGroup = group;
431 environment.CleanupGroupCancelCallback = group_cancel_cleanup_increment_cb;
432 status = pTpAllocWork(&work, work_cb, &userdata, &environment);
433 ok(!status, "TpAllocWork failed with status %x\n", status);
434 ok(work != NULL, "expected work != NULL\n");
436 /* post 10 identical work items at once */
437 userdata = userdata2 = 0;
438 for (i = 0; i < 10; i++)
439 pTpPostWork(work);
441 /* check if we get multiple cancellation callbacks */
442 group_cancel_tid = 0xdeadbeef;
443 pTpReleaseCleanupGroupMembers(group, TRUE, &userdata2);
444 ok(userdata <= 8, "expected userdata <= 8, got %u\n", userdata);
445 ok(userdata2 == 1, "expected only one cancellation callback, got %u\n", userdata2);
446 ok(group_cancel_tid == GetCurrentThreadId(), "expected tid %x, got %x\n",
447 GetCurrentThreadId(), group_cancel_tid);
449 /* cleanup */
450 pTpReleaseCleanupGroup(group);
451 pTpReleasePool(pool);
452 CloseHandle(semaphores[0]);
453 CloseHandle(semaphores[1]);
456 static void CALLBACK instance_semaphore_completion_cb(TP_CALLBACK_INSTANCE *instance, void *userdata)
458 HANDLE *semaphores = userdata;
459 trace("Running instance completion callback\n");
460 pTpCallbackReleaseSemaphoreOnCompletion(instance, semaphores[0], 1);
463 static void CALLBACK instance_finalization_cb(TP_CALLBACK_INSTANCE *instance, void *userdata)
465 HANDLE *semaphores = userdata;
466 DWORD result;
468 trace("Running instance finalization callback\n");
470 result = WaitForSingleObject(semaphores[0], 100);
471 ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
472 ReleaseSemaphore(semaphores[1], 1, NULL);
475 static void test_tp_instance(void)
477 TP_CALLBACK_ENVIRON environment;
478 HANDLE semaphores[2];
479 NTSTATUS status;
480 TP_POOL *pool;
481 DWORD result;
483 semaphores[0] = CreateSemaphoreW(NULL, 0, 1, NULL);
484 ok(semaphores[0] != NULL, "failed to create semaphore\n");
485 semaphores[1] = CreateSemaphoreW(NULL, 0, 1, NULL);
486 ok(semaphores[1] != NULL, "failed to create semaphore\n");
488 /* allocate new threadpool */
489 pool = NULL;
490 status = pTpAllocPool(&pool, NULL);
491 ok(!status, "TpAllocPool failed with status %x\n", status);
492 ok(pool != NULL, "expected pool != NULL\n");
494 /* test for TpCallbackReleaseSemaphoreOnCompletion */
495 memset(&environment, 0, sizeof(environment));
496 environment.Version = 1;
497 environment.Pool = pool;
498 status = pTpSimpleTryPost(instance_semaphore_completion_cb, semaphores, &environment);
499 ok(!status, "TpSimpleTryPost failed with status %x\n", status);
500 result = WaitForSingleObject(semaphores[0], 1000);
501 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
503 /* test for finalization callback */
504 memset(&environment, 0, sizeof(environment));
505 environment.Version = 1;
506 environment.Pool = pool;
507 environment.FinalizationCallback = instance_finalization_cb;
508 status = pTpSimpleTryPost(instance_semaphore_completion_cb, semaphores, &environment);
509 ok(!status, "TpSimpleTryPost failed with status %x\n", status);
510 result = WaitForSingleObject(semaphores[0], 1000);
511 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
512 result = WaitForSingleObject(semaphores[1], 1000);
513 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
515 /* cleanup */
516 pTpReleasePool(pool);
517 CloseHandle(semaphores[0]);
518 CloseHandle(semaphores[1]);
521 static void CALLBACK disassociate_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_WORK *work)
523 HANDLE *semaphores = userdata;
524 DWORD result;
526 trace("Running disassociate callback\n");
528 pTpDisassociateCallback(instance);
529 result = WaitForSingleObject(semaphores[0], 1000);
530 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
531 ReleaseSemaphore(semaphores[1], 1, NULL);
534 static void CALLBACK disassociate2_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_WORK *work)
536 HANDLE *semaphores = userdata;
537 DWORD result;
539 trace("Running disassociate2 callback\n");
541 pTpDisassociateCallback(instance);
542 result = WaitForSingleObject(semaphores[0], 100);
543 ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
544 ReleaseSemaphore(semaphores[1], 1, NULL);
547 static void CALLBACK disassociate3_cb(TP_CALLBACK_INSTANCE *instance, void *userdata)
549 HANDLE *semaphores = userdata;
550 DWORD result;
552 trace("Running disassociate3 callback\n");
554 pTpDisassociateCallback(instance);
555 result = WaitForSingleObject(semaphores[0], 100);
556 ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
557 ReleaseSemaphore(semaphores[1], 1, NULL);
560 static void test_tp_disassociate(void)
562 TP_CALLBACK_ENVIRON environment;
563 TP_CLEANUP_GROUP *group;
564 HANDLE semaphores[2];
565 NTSTATUS status;
566 TP_POOL *pool;
567 TP_WORK *work;
568 DWORD result;
570 semaphores[0] = CreateSemaphoreW(NULL, 0, 1, NULL);
571 ok(semaphores[0] != NULL, "failed to create semaphore\n");
572 semaphores[1] = CreateSemaphoreW(NULL, 0, 1, NULL);
573 ok(semaphores[1] != NULL, "failed to create semaphore\n");
575 /* allocate new threadpool and cleanup group */
576 pool = NULL;
577 status = pTpAllocPool(&pool, NULL);
578 ok(!status, "TpAllocPool failed with status %x\n", status);
579 ok(pool != NULL, "expected pool != NULL\n");
581 group = NULL;
582 status = pTpAllocCleanupGroup(&group);
583 ok(!status, "TpAllocCleanupGroup failed with status %x\n", status);
584 ok(group != NULL, "expected pool != NULL\n");
586 /* test TpDisassociateCallback on work objects without group */
587 work = NULL;
588 memset(&environment, 0, sizeof(environment));
589 environment.Version = 1;
590 environment.Pool = pool;
591 status = pTpAllocWork(&work, disassociate_cb, semaphores, &environment);
592 ok(!status, "TpAllocWork failed with status %x\n", status);
593 ok(work != NULL, "expected work != NULL\n");
595 pTpPostWork(work);
596 pTpWaitForWork(work, FALSE);
598 result = WaitForSingleObject(semaphores[1], 100);
599 ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
600 ReleaseSemaphore(semaphores[0], 1, NULL);
601 result = WaitForSingleObject(semaphores[1], 1000);
602 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
603 pTpReleaseWork(work);
605 /* test TpDisassociateCallback on work objects with group (1) */
606 work = NULL;
607 memset(&environment, 0, sizeof(environment));
608 environment.Version = 1;
609 environment.Pool = pool;
610 environment.CleanupGroup = group;
611 status = pTpAllocWork(&work, disassociate_cb, semaphores, &environment);
612 ok(!status, "TpAllocWork failed with status %x\n", status);
613 ok(work != NULL, "expected work != NULL\n");
615 pTpPostWork(work);
616 pTpWaitForWork(work, FALSE);
618 result = WaitForSingleObject(semaphores[1], 100);
619 ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
620 ReleaseSemaphore(semaphores[0], 1, NULL);
621 result = WaitForSingleObject(semaphores[1], 1000);
622 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
623 pTpReleaseCleanupGroupMembers(group, FALSE, NULL);
625 /* test TpDisassociateCallback on work objects with group (2) */
626 work = NULL;
627 memset(&environment, 0, sizeof(environment));
628 environment.Version = 1;
629 environment.Pool = pool;
630 environment.CleanupGroup = group;
631 status = pTpAllocWork(&work, disassociate2_cb, semaphores, &environment);
632 ok(!status, "TpAllocWork failed with status %x\n", status);
633 ok(work != NULL, "expected work != NULL\n");
635 pTpPostWork(work);
636 pTpReleaseCleanupGroupMembers(group, FALSE, NULL);
638 ReleaseSemaphore(semaphores[0], 1, NULL);
639 result = WaitForSingleObject(semaphores[1], 1000);
640 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
641 result = WaitForSingleObject(semaphores[0], 1000);
642 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
644 /* test TpDisassociateCallback on simple callbacks */
645 memset(&environment, 0, sizeof(environment));
646 environment.Version = 1;
647 environment.Pool = pool;
648 environment.CleanupGroup = group;
649 status = pTpSimpleTryPost(disassociate3_cb, semaphores, &environment);
650 ok(!status, "TpSimpleTryPost failed with status %x\n", status);
652 pTpReleaseCleanupGroupMembers(group, FALSE, NULL);
654 ReleaseSemaphore(semaphores[0], 1, NULL);
655 result = WaitForSingleObject(semaphores[1], 1000);
656 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
657 result = WaitForSingleObject(semaphores[0], 1000);
658 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
660 /* cleanup */
661 pTpReleaseCleanupGroup(group);
662 pTpReleasePool(pool);
663 CloseHandle(semaphores[0]);
664 CloseHandle(semaphores[1]);
667 static void CALLBACK timer_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_TIMER *timer)
669 HANDLE semaphore = userdata;
670 trace("Running timer callback\n");
671 ReleaseSemaphore(semaphore, 1, NULL);
674 static void test_tp_timer(void)
676 TP_CALLBACK_ENVIRON environment;
677 DWORD result, ticks;
678 LARGE_INTEGER when;
679 HANDLE semaphore;
680 NTSTATUS status;
681 TP_TIMER *timer;
682 TP_POOL *pool;
683 BOOL success;
684 int i;
686 semaphore = CreateSemaphoreA(NULL, 0, 1, NULL);
687 ok(semaphore != NULL, "CreateSemaphoreA failed %u\n", GetLastError());
689 /* allocate new threadpool */
690 pool = NULL;
691 status = pTpAllocPool(&pool, NULL);
692 ok(!status, "TpAllocPool failed with status %x\n", status);
693 ok(pool != NULL, "expected pool != NULL\n");
695 /* allocate new timer */
696 timer = NULL;
697 memset(&environment, 0, sizeof(environment));
698 environment.Version = 1;
699 environment.Pool = pool;
700 status = pTpAllocTimer(&timer, timer_cb, semaphore, &environment);
701 ok(!status, "TpAllocTimer failed with status %x\n", status);
702 ok(timer != NULL, "expected timer != NULL\n");
704 success = pTpIsTimerSet(timer);
705 ok(!success, "TpIsTimerSet returned TRUE\n");
707 /* test timer with a relative timeout */
708 when.QuadPart = (ULONGLONG)200 * -10000;
709 pTpSetTimer(timer, &when, 0, 0);
710 success = pTpIsTimerSet(timer);
711 ok(success, "TpIsTimerSet returned FALSE\n");
713 pTpWaitForTimer(timer, FALSE);
715 result = WaitForSingleObject(semaphore, 100);
716 ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
717 result = WaitForSingleObject(semaphore, 200);
718 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
719 success = pTpIsTimerSet(timer);
720 ok(success, "TpIsTimerSet returned FALSE\n");
722 /* test timer with an absolute timeout */
723 NtQuerySystemTime( &when );
724 when.QuadPart += (ULONGLONG)200 * 10000;
725 pTpSetTimer(timer, &when, 0, 0);
726 success = pTpIsTimerSet(timer);
727 ok(success, "TpIsTimerSet returned FALSE\n");
729 pTpWaitForTimer(timer, FALSE);
731 result = WaitForSingleObject(semaphore, 100);
732 ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
733 result = WaitForSingleObject(semaphore, 200);
734 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
735 success = pTpIsTimerSet(timer);
736 ok(success, "TpIsTimerSet returned FALSE\n");
738 /* test timer with zero timeout */
739 when.QuadPart = 0;
740 pTpSetTimer(timer, &when, 0, 0);
741 success = pTpIsTimerSet(timer);
742 ok(success, "TpIsTimerSet returned FALSE\n");
744 pTpWaitForTimer(timer, FALSE);
746 result = WaitForSingleObject(semaphore, 50);
747 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
748 success = pTpIsTimerSet(timer);
749 ok(success, "TpIsTimerSet returned FALSE\n");
751 /* unset the timer */
752 pTpSetTimer(timer, NULL, 0, 0);
753 success = pTpIsTimerSet(timer);
754 ok(!success, "TpIsTimerSet returned TRUE\n");
755 pTpWaitForTimer(timer, TRUE);
757 pTpReleaseTimer(timer);
758 CloseHandle(semaphore);
760 semaphore = CreateSemaphoreA(NULL, 0, 3, NULL);
761 ok(semaphore != NULL, "CreateSemaphoreA failed %u\n", GetLastError());
763 /* allocate a new timer */
764 timer = NULL;
765 memset(&environment, 0, sizeof(environment));
766 environment.Version = 1;
767 environment.Pool = pool;
768 status = pTpAllocTimer(&timer, timer_cb, semaphore, &environment);
769 ok(!status, "TpAllocTimer failed with status %x\n", status);
770 ok(timer != NULL, "expected timer != NULL\n");
772 /* test a relative timeout repeated periodically */
773 when.QuadPart = (ULONGLONG)200 * -10000;
774 pTpSetTimer(timer, &when, 200, 0);
775 success = pTpIsTimerSet(timer);
776 ok(success, "TpIsTimerSet returned FALSE\n");
778 /* wait until the timer was triggered three times */
779 ticks = GetTickCount();
780 for (i = 0; i < 3; i++)
782 result = WaitForSingleObject(semaphore, 1000);
783 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
785 ticks = GetTickCount() - ticks;
786 ok(ticks >= 500 && (ticks <= 700 || broken(ticks <= 750)) /* Win 7 */,
787 "expected approximately 600 ticks, got %u\n", ticks);
789 /* unset the timer */
790 pTpSetTimer(timer, NULL, 0, 0);
791 success = pTpIsTimerSet(timer);
792 ok(!success, "TpIsTimerSet returned TRUE\n");
793 pTpWaitForTimer(timer, TRUE);
795 /* cleanup */
796 pTpReleaseTimer(timer);
797 pTpReleasePool(pool);
798 CloseHandle(semaphore);
801 struct window_length_info
803 HANDLE semaphore;
804 DWORD ticks;
807 static void CALLBACK window_length_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, TP_TIMER *timer)
809 struct window_length_info *info = userdata;
810 trace("Running window length callback\n");
811 info->ticks = GetTickCount();
812 ReleaseSemaphore(info->semaphore, 1, NULL);
815 static void test_tp_window_length(void)
817 struct window_length_info info1, info2;
818 TP_CALLBACK_ENVIRON environment;
819 TP_TIMER *timer1, *timer2;
820 LARGE_INTEGER when;
821 HANDLE semaphore;
822 NTSTATUS status;
823 TP_POOL *pool;
824 DWORD result;
826 semaphore = CreateSemaphoreA(NULL, 0, 2, NULL);
827 ok(semaphore != NULL, "CreateSemaphoreA failed %u\n", GetLastError());
829 /* allocate new threadpool */
830 pool = NULL;
831 status = pTpAllocPool(&pool, NULL);
832 ok(!status, "TpAllocPool failed with status %x\n", status);
833 ok(pool != NULL, "expected pool != NULL\n");
835 /* allocate two identical timers */
836 memset(&environment, 0, sizeof(environment));
837 environment.Version = 1;
838 environment.Pool = pool;
840 timer1 = NULL;
841 info1.semaphore = semaphore;
842 status = pTpAllocTimer(&timer1, window_length_cb, &info1, &environment);
843 ok(!status, "TpAllocTimer failed with status %x\n", status);
844 ok(timer1 != NULL, "expected timer1 != NULL\n");
846 timer2 = NULL;
847 info2.semaphore = semaphore;
848 status = pTpAllocTimer(&timer2, window_length_cb, &info2, &environment);
849 ok(!status, "TpAllocTimer failed with status %x\n", status);
850 ok(timer2 != NULL, "expected timer2 != NULL\n");
852 /* choose parameters so that timers are not merged */
853 info1.ticks = 0;
854 info2.ticks = 0;
856 NtQuerySystemTime( &when );
857 when.QuadPart += (ULONGLONG)250 * 10000;
858 pTpSetTimer(timer2, &when, 0, 0);
859 Sleep(50);
860 when.QuadPart -= (ULONGLONG)150 * 10000;
861 pTpSetTimer(timer1, &when, 0, 75);
863 result = WaitForSingleObject(semaphore, 1000);
864 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
865 result = WaitForSingleObject(semaphore, 1000);
866 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
867 ok(info1.ticks != 0 && info2.ticks != 0, "expected that ticks are nonzero\n");
868 ok(info2.ticks >= info1.ticks + 75 || broken(info2.ticks < info1.ticks + 75) /* Win 2008 */,
869 "expected that timers are not merged\n");
871 /* timers will be merged */
872 info1.ticks = 0;
873 info2.ticks = 0;
875 NtQuerySystemTime( &when );
876 when.QuadPart += (ULONGLONG)250 * 10000;
877 pTpSetTimer(timer2, &when, 0, 0);
878 Sleep(50);
879 when.QuadPart -= (ULONGLONG)150 * 10000;
880 pTpSetTimer(timer1, &when, 0, 200);
882 result = WaitForSingleObject(semaphore, 1000);
883 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
884 result = WaitForSingleObject(semaphore, 1000);
885 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
886 ok(info1.ticks != 0 && info2.ticks != 0, "expected that ticks are nonzero\n");
887 ok(info2.ticks >= info1.ticks - 50 && info2.ticks <= info1.ticks + 50,
888 "expected that timers are merged\n");
890 /* on Windows the timers also get merged in this case */
891 info1.ticks = 0;
892 info2.ticks = 0;
894 NtQuerySystemTime( &when );
895 when.QuadPart += (ULONGLONG)100 * 10000;
896 pTpSetTimer(timer1, &when, 0, 200);
897 Sleep(50);
898 when.QuadPart += (ULONGLONG)150 * 10000;
899 pTpSetTimer(timer2, &when, 0, 0);
901 result = WaitForSingleObject(semaphore, 1000);
902 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
903 result = WaitForSingleObject(semaphore, 1000);
904 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
905 ok(info1.ticks != 0 && info2.ticks != 0, "expected that ticks are nonzero\n");
906 todo_wine
907 ok(info2.ticks >= info1.ticks - 50 && info2.ticks <= info1.ticks + 50,
908 "expected that timers are merged\n");
910 /* cleanup */
911 pTpReleaseTimer(timer1);
912 pTpReleaseTimer(timer2);
913 pTpReleasePool(pool);
914 CloseHandle(semaphore);
917 struct wait_info
919 HANDLE semaphore;
920 LONG userdata;
923 static void CALLBACK wait_cb(TP_CALLBACK_INSTANCE *instance, void *userdata,
924 TP_WAIT *wait, TP_WAIT_RESULT result)
926 struct wait_info *info = userdata;
927 trace("Running wait callback\n");
929 if (result == WAIT_OBJECT_0)
930 InterlockedIncrement(&info->userdata);
931 else if (result == WAIT_TIMEOUT)
932 InterlockedExchangeAdd(&info->userdata, 0x10000);
933 else
934 ok(0, "unexpected result %u\n", result);
935 ReleaseSemaphore(info->semaphore, 1, NULL);
938 static void test_tp_wait(void)
940 TP_CALLBACK_ENVIRON environment;
941 TP_WAIT *wait1, *wait2;
942 struct wait_info info;
943 HANDLE semaphores[2];
944 LARGE_INTEGER when;
945 NTSTATUS status;
946 TP_POOL *pool;
947 DWORD result;
949 semaphores[0] = CreateSemaphoreW(NULL, 0, 2, NULL);
950 ok(semaphores[0] != NULL, "failed to create semaphore\n");
951 semaphores[1] = CreateSemaphoreW(NULL, 0, 1, NULL);
952 ok(semaphores[1] != NULL, "failed to create semaphore\n");
953 info.semaphore = semaphores[0];
955 /* allocate new threadpool */
956 pool = NULL;
957 status = pTpAllocPool(&pool, NULL);
958 ok(!status, "TpAllocPool failed with status %x\n", status);
959 ok(pool != NULL, "expected pool != NULL\n");
961 /* allocate new wait items */
962 memset(&environment, 0, sizeof(environment));
963 environment.Version = 1;
964 environment.Pool = pool;
966 wait1 = NULL;
967 status = pTpAllocWait(&wait1, wait_cb, &info, &environment);
968 ok(!status, "TpAllocWait failed with status %x\n", status);
969 ok(wait1 != NULL, "expected wait1 != NULL\n");
971 wait2 = NULL;
972 status = pTpAllocWait(&wait2, wait_cb, &info, &environment);
973 ok(!status, "TpAllocWait failed with status %x\n", status);
974 ok(wait2 != NULL, "expected wait2 != NULL\n");
976 /* infinite timeout, signal the semaphore immediately */
977 info.userdata = 0;
978 pTpSetWait(wait1, semaphores[1], NULL);
979 ReleaseSemaphore(semaphores[1], 1, NULL);
980 result = WaitForSingleObject(semaphores[0], 100);
981 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
982 ok(info.userdata == 1, "expected info.userdata = 1, got %u\n", info.userdata);
983 result = WaitForSingleObject(semaphores[1], 0);
984 ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
986 /* relative timeout, no event */
987 info.userdata = 0;
988 when.QuadPart = (ULONGLONG)200 * -10000;
989 pTpSetWait(wait1, semaphores[1], &when);
990 result = WaitForSingleObject(semaphores[0], 100);
991 ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
992 ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata);
993 result = WaitForSingleObject(semaphores[0], 200);
994 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
995 ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %u\n", info.userdata);
996 result = WaitForSingleObject(semaphores[1], 0);
997 ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
999 /* repeat test with call to TpWaitForWait(..., TRUE) */
1000 info.userdata = 0;
1001 when.QuadPart = (ULONGLONG)200 * -10000;
1002 pTpSetWait(wait1, semaphores[1], &when);
1003 result = WaitForSingleObject(semaphores[0], 100);
1004 ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
1005 pTpWaitForWait(wait1, TRUE);
1006 ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata);
1007 result = WaitForSingleObject(semaphores[0], 200);
1008 ok(result == WAIT_OBJECT_0 || broken(result == WAIT_TIMEOUT) /* Win 8 */,
1009 "WaitForSingleObject returned %u\n", result);
1010 if (result == WAIT_OBJECT_0)
1011 ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %u\n", info.userdata);
1012 else
1013 ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata);
1014 result = WaitForSingleObject(semaphores[1], 0);
1015 ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
1017 /* relative timeout, with event */
1018 info.userdata = 0;
1019 when.QuadPart = (ULONGLONG)200 * -10000;
1020 pTpSetWait(wait1, semaphores[1], &when);
1021 result = WaitForSingleObject(semaphores[0], 100);
1022 ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
1023 ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata);
1024 ReleaseSemaphore(semaphores[1], 1, NULL);
1025 result = WaitForSingleObject(semaphores[0], 100);
1026 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
1027 ok(info.userdata == 1, "expected info.userdata = 1, got %u\n", info.userdata);
1028 result = WaitForSingleObject(semaphores[1], 0);
1029 ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
1031 /* repeat test with call to TpWaitForWait(..., TRUE) */
1032 info.userdata = 0;
1033 when.QuadPart = (ULONGLONG)200 * -10000;
1034 pTpSetWait(wait1, semaphores[1], &when);
1035 result = WaitForSingleObject(semaphores[0], 100);
1036 ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
1037 pTpWaitForWait(wait1, TRUE);
1038 ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata);
1039 ReleaseSemaphore(semaphores[1], 1, NULL);
1040 result = WaitForSingleObject(semaphores[0], 100);
1041 ok(result == WAIT_OBJECT_0 || broken(result == WAIT_TIMEOUT) /* Win 8 */,
1042 "WaitForSingleObject returned %u\n", result);
1043 if (result == WAIT_OBJECT_0)
1045 ok(info.userdata == 1, "expected info.userdata = 1, got %u\n", info.userdata);
1046 result = WaitForSingleObject(semaphores[1], 0);
1047 ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
1049 else
1051 ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata);
1052 result = WaitForSingleObject(semaphores[1], 0);
1053 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
1056 /* absolute timeout, no event */
1057 info.userdata = 0;
1058 NtQuerySystemTime( &when );
1059 when.QuadPart += (ULONGLONG)200 * 10000;
1060 pTpSetWait(wait1, semaphores[1], &when);
1061 result = WaitForSingleObject(semaphores[0], 100);
1062 ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
1063 ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata);
1064 result = WaitForSingleObject(semaphores[0], 200);
1065 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
1066 ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %u\n", info.userdata);
1067 result = WaitForSingleObject(semaphores[1], 0);
1068 ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
1070 /* absolute timeout, with event */
1071 info.userdata = 0;
1072 NtQuerySystemTime( &when );
1073 when.QuadPart += (ULONGLONG)200 * 10000;
1074 pTpSetWait(wait1, semaphores[1], &when);
1075 result = WaitForSingleObject(semaphores[0], 100);
1076 ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
1077 ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata);
1078 ReleaseSemaphore(semaphores[1], 1, NULL);
1079 result = WaitForSingleObject(semaphores[0], 100);
1080 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
1081 ok(info.userdata == 1, "expected info.userdata = 1, got %u\n", info.userdata);
1082 result = WaitForSingleObject(semaphores[1], 0);
1083 ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
1085 /* test timeout of zero */
1086 info.userdata = 0;
1087 when.QuadPart = 0;
1088 pTpSetWait(wait1, semaphores[1], &when);
1089 result = WaitForSingleObject(semaphores[0], 100);
1090 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
1091 ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %u\n", info.userdata);
1092 result = WaitForSingleObject(semaphores[1], 0);
1093 ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
1095 /* cancel a pending wait */
1096 info.userdata = 0;
1097 when.QuadPart = (ULONGLONG)250 * -10000;
1098 pTpSetWait(wait1, semaphores[1], &when);
1099 result = WaitForSingleObject(semaphores[0], 100);
1100 ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
1101 pTpSetWait(wait1, NULL, (void *)0xdeadbeef);
1102 Sleep(50);
1103 ReleaseSemaphore(semaphores[1], 1, NULL);
1104 result = WaitForSingleObject(semaphores[0], 100);
1105 ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
1106 ok(info.userdata == 0, "expected info.userdata = 0, got %u\n", info.userdata);
1107 result = WaitForSingleObject(semaphores[1], 0);
1108 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
1110 /* test with INVALID_HANDLE_VALUE */
1111 info.userdata = 0;
1112 when.QuadPart = 0;
1113 pTpSetWait(wait1, INVALID_HANDLE_VALUE, &when);
1114 result = WaitForSingleObject(semaphores[0], 100);
1115 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
1116 ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %u\n", info.userdata);
1118 /* cancel a pending wait with INVALID_HANDLE_VALUE */
1119 info.userdata = 0;
1120 when.QuadPart = (ULONGLONG)250 * -10000;
1121 pTpSetWait(wait1, semaphores[1], &when);
1122 result = WaitForSingleObject(semaphores[0], 100);
1123 ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
1124 when.QuadPart = 0;
1125 pTpSetWait(wait1, INVALID_HANDLE_VALUE, &when);
1126 Sleep(50);
1127 ReleaseSemaphore(semaphores[1], 1, NULL);
1128 result = WaitForSingleObject(semaphores[0], 100);
1129 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
1130 ok(info.userdata == 0x10000, "expected info.userdata = 0x10000, got %u\n", info.userdata);
1131 result = WaitForSingleObject(semaphores[1], 0);
1132 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
1134 CloseHandle(semaphores[1]);
1135 semaphores[1] = CreateSemaphoreW(NULL, 0, 2, NULL);
1136 ok(semaphores[1] != NULL, "failed to create semaphore\n");
1138 /* add two wait objects with the same semaphore */
1139 info.userdata = 0;
1140 pTpSetWait(wait1, semaphores[1], NULL);
1141 pTpSetWait(wait2, semaphores[1], NULL);
1142 Sleep(50);
1143 ReleaseSemaphore(semaphores[1], 1, NULL);
1144 result = WaitForSingleObject(semaphores[0], 100);
1145 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
1146 result = WaitForSingleObject(semaphores[0], 100);
1147 ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
1148 ok(info.userdata == 1, "expected info.userdata = 1, got %u\n", info.userdata);
1149 result = WaitForSingleObject(semaphores[1], 0);
1150 ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
1152 /* repeat test above with release count 2 */
1153 info.userdata = 0;
1154 pTpSetWait(wait1, semaphores[1], NULL);
1155 pTpSetWait(wait2, semaphores[1], NULL);
1156 Sleep(50);
1157 result = ReleaseSemaphore(semaphores[1], 2, NULL);
1158 result = WaitForSingleObject(semaphores[0], 100);
1159 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
1160 result = WaitForSingleObject(semaphores[0], 100);
1161 ok(result == WAIT_OBJECT_0, "WaitForSingleObject returned %u\n", result);
1162 ok(info.userdata == 2, "expected info.userdata = 2, got %u\n", info.userdata);
1163 result = WaitForSingleObject(semaphores[1], 0);
1164 ok(result == WAIT_TIMEOUT, "WaitForSingleObject returned %u\n", result);
1166 /* cleanup */
1167 pTpReleaseWait(wait1);
1168 pTpReleaseWait(wait2);
1169 pTpReleasePool(pool);
1170 CloseHandle(semaphores[0]);
1171 CloseHandle(semaphores[1]);
1174 START_TEST(threadpool)
1176 if (!init_threadpool())
1177 return;
1179 test_tp_simple();
1180 test_tp_work();
1181 test_tp_work_scheduler();
1182 test_tp_group_cancel();
1183 test_tp_instance();
1184 test_tp_disassociate();
1185 test_tp_timer();
1186 test_tp_window_length();
1187 test_tp_wait();