2 * Unit test suite for thread pool functions
4 * Copyright 2015-2016 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 NTSTATUS (WINAPI
*pTpAllocCleanupGroup
)(TP_CLEANUP_GROUP
**);
24 static NTSTATUS (WINAPI
*pTpAllocIoCompletion
)(TP_IO
**,HANDLE
,PTP_IO_CALLBACK
,void *,TP_CALLBACK_ENVIRON
*);
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
*pTpCancelAsyncIoOperation
)(TP_IO
*);
32 static VOID (WINAPI
*pTpDisassociateCallback
)(TP_CALLBACK_INSTANCE
*);
33 static BOOL (WINAPI
*pTpIsTimerSet
)(TP_TIMER
*);
34 static VOID (WINAPI
*pTpPostWork
)(TP_WORK
*);
35 static NTSTATUS (WINAPI
*pTpQueryPoolStackInformation
)(TP_POOL
*,TP_POOL_STACK_INFORMATION
*);
36 static VOID (WINAPI
*pTpReleaseCleanupGroup
)(TP_CLEANUP_GROUP
*);
37 static VOID (WINAPI
*pTpReleaseCleanupGroupMembers
)(TP_CLEANUP_GROUP
*,BOOL
,PVOID
);
38 static void (WINAPI
*pTpReleaseIoCompletion
)(TP_IO
*);
39 static VOID (WINAPI
*pTpReleasePool
)(TP_POOL
*);
40 static VOID (WINAPI
*pTpReleaseTimer
)(TP_TIMER
*);
41 static VOID (WINAPI
*pTpReleaseWait
)(TP_WAIT
*);
42 static VOID (WINAPI
*pTpReleaseWork
)(TP_WORK
*);
43 static VOID (WINAPI
*pTpSetPoolMaxThreads
)(TP_POOL
*,DWORD
);
44 static NTSTATUS (WINAPI
*pTpSetPoolStackInformation
)(TP_POOL
*,TP_POOL_STACK_INFORMATION
*);
45 static VOID (WINAPI
*pTpSetTimer
)(TP_TIMER
*,LARGE_INTEGER
*,LONG
,LONG
);
46 static VOID (WINAPI
*pTpSetWait
)(TP_WAIT
*,HANDLE
,LARGE_INTEGER
*);
47 static NTSTATUS (WINAPI
*pTpSimpleTryPost
)(PTP_SIMPLE_CALLBACK
,PVOID
,TP_CALLBACK_ENVIRON
*);
48 static void (WINAPI
*pTpStartAsyncIoOperation
)(TP_IO
*);
49 static void (WINAPI
*pTpWaitForIoCompletion
)(TP_IO
*,BOOL
);
50 static VOID (WINAPI
*pTpWaitForTimer
)(TP_TIMER
*,BOOL
);
51 static VOID (WINAPI
*pTpWaitForWait
)(TP_WAIT
*,BOOL
);
52 static VOID (WINAPI
*pTpWaitForWork
)(TP_WORK
*,BOOL
);
54 static void (WINAPI
*pCancelThreadpoolIo
)(TP_IO
*);
55 static void (WINAPI
*pCloseThreadpoolIo
)(TP_IO
*);
56 static TP_IO
*(WINAPI
*pCreateThreadpoolIo
)(HANDLE
, PTP_WIN32_IO_CALLBACK
, void *, TP_CALLBACK_ENVIRON
*);
57 static void (WINAPI
*pStartThreadpoolIo
)(TP_IO
*);
58 static void (WINAPI
*pWaitForThreadpoolIoCallbacks
)(TP_IO
*, BOOL
);
60 #define GET_PROC(func) \
63 p ## func = (void *)GetProcAddress(module, #func); \
64 if (!p ## func) trace("Failed to get address for %s\n", #func); \
68 static BOOL
init_threadpool(void)
70 HMODULE module
= GetModuleHandleA("ntdll");
71 GET_PROC(TpAllocCleanupGroup
);
72 GET_PROC(TpAllocIoCompletion
);
73 GET_PROC(TpAllocPool
);
74 GET_PROC(TpAllocTimer
);
75 GET_PROC(TpAllocWait
);
76 GET_PROC(TpAllocWork
);
77 GET_PROC(TpCallbackMayRunLong
);
78 GET_PROC(TpCallbackReleaseSemaphoreOnCompletion
);
79 GET_PROC(TpCancelAsyncIoOperation
);
80 GET_PROC(TpDisassociateCallback
);
81 GET_PROC(TpIsTimerSet
);
83 GET_PROC(TpQueryPoolStackInformation
);
84 GET_PROC(TpReleaseCleanupGroup
);
85 GET_PROC(TpReleaseCleanupGroupMembers
);
86 GET_PROC(TpReleaseIoCompletion
);
87 GET_PROC(TpReleasePool
);
88 GET_PROC(TpReleaseTimer
);
89 GET_PROC(TpReleaseWait
);
90 GET_PROC(TpReleaseWork
);
91 GET_PROC(TpSetPoolMaxThreads
);
92 GET_PROC(TpSetPoolStackInformation
);
95 GET_PROC(TpSimpleTryPost
);
96 GET_PROC(TpStartAsyncIoOperation
);
97 GET_PROC(TpWaitForIoCompletion
);
98 GET_PROC(TpWaitForTimer
);
99 GET_PROC(TpWaitForWait
);
100 GET_PROC(TpWaitForWork
);
102 module
= GetModuleHandleA("kernel32");
103 GET_PROC(CancelThreadpoolIo
);
104 GET_PROC(CloseThreadpoolIo
);
105 GET_PROC(CreateThreadpoolIo
);
106 GET_PROC(StartThreadpoolIo
);
107 GET_PROC(WaitForThreadpoolIoCallbacks
);
111 win_skip("Threadpool functions not supported, skipping tests\n");
118 #undef NTDLL_GET_PROC
121 static DWORD CALLBACK
rtl_work_cb(void *userdata
)
123 HANDLE semaphore
= userdata
;
124 ReleaseSemaphore(semaphore
, 1, NULL
);
128 static void test_RtlQueueWorkItem(void)
134 semaphore
= CreateSemaphoreA(NULL
, 0, 1, NULL
);
135 ok(semaphore
!= NULL
, "CreateSemaphoreA failed %lu\n", GetLastError());
137 status
= RtlQueueWorkItem(rtl_work_cb
, semaphore
, WT_EXECUTEDEFAULT
);
138 ok(!status
, "RtlQueueWorkItem failed with status %lx\n", status
);
139 result
= WaitForSingleObject(semaphore
, 1000);
140 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
142 status
= RtlQueueWorkItem(rtl_work_cb
, semaphore
, WT_EXECUTEINIOTHREAD
);
143 ok(!status
, "RtlQueueWorkItem failed with status %lx\n", status
);
144 result
= WaitForSingleObject(semaphore
, 1000);
145 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
147 status
= RtlQueueWorkItem(rtl_work_cb
, semaphore
, WT_EXECUTEINPERSISTENTTHREAD
);
148 ok(!status
, "RtlQueueWorkItem failed with status %lx\n", status
);
149 result
= WaitForSingleObject(semaphore
, 1000);
150 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
152 status
= RtlQueueWorkItem(rtl_work_cb
, semaphore
, WT_EXECUTELONGFUNCTION
);
153 ok(!status
, "RtlQueueWorkItem failed with status %lx\n", status
);
154 result
= WaitForSingleObject(semaphore
, 1000);
155 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
157 status
= RtlQueueWorkItem(rtl_work_cb
, semaphore
, WT_TRANSFER_IMPERSONATION
);
158 ok(!status
, "RtlQueueWorkItem failed with status %lx\n", status
);
159 result
= WaitForSingleObject(semaphore
, 1000);
160 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
162 CloseHandle(semaphore
);
174 static void CALLBACK
rtl_wait_cb(void *userdata
, BOOLEAN timeout
)
176 struct rtl_wait_info
*info
= userdata
;
180 InterlockedIncrement(&info
->userdata
);
182 InterlockedExchangeAdd(&info
->userdata
, 0x10000);
183 info
->threadid
= GetCurrentThreadId();
184 ReleaseSemaphore(info
->semaphore1
, 1, NULL
);
186 if (info
->semaphore2
)
188 result
= WaitForSingleObject(info
->semaphore2
, 200);
189 ok(result
== info
->wait_result
, "expected %lu, got %lu\n", info
->wait_result
, result
);
190 ReleaseSemaphore(info
->semaphore1
, 1, NULL
);
194 static HANDLE rtl_wait_apc_semaphore
;
196 static void CALLBACK
rtl_wait_apc_cb(ULONG_PTR userdata
)
198 if (rtl_wait_apc_semaphore
)
199 ReleaseSemaphore(rtl_wait_apc_semaphore
, 1, NULL
);
202 static void test_RtlRegisterWait(void)
204 HANDLE wait1
, event
, thread
;
205 struct rtl_wait_info info
;
206 HANDLE semaphores
[2];
208 DWORD result
, threadid
;
210 semaphores
[0] = CreateSemaphoreW(NULL
, 0, 2, NULL
);
211 ok(semaphores
[0] != NULL
, "failed to create semaphore\n");
212 semaphores
[1] = CreateSemaphoreW(NULL
, 0, 1, NULL
);
213 ok(semaphores
[1] != NULL
, "failed to create semaphore\n");
214 info
.semaphore1
= semaphores
[0];
215 info
.semaphore2
= NULL
;
217 event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
218 ok(event
!= NULL
, "failed to create event\n");
220 /* basic test for RtlRegisterWait and RtlDeregisterWait */
223 status
= RtlRegisterWait(&wait1
, semaphores
[1], rtl_wait_cb
, &info
, INFINITE
, WT_EXECUTEDEFAULT
);
224 ok(!status
, "RtlRegisterWait failed with status %lx\n", status
);
225 ok(wait1
!= NULL
, "expected wait1 != NULL\n");
226 status
= RtlDeregisterWait(wait1
);
227 ok(!status
, "RtlDeregisterWait failed with status %lx\n", status
);
228 ok(info
.userdata
== 0, "expected info.userdata = 0, got %lu\n", info
.userdata
);
230 /* infinite timeout, signal the semaphore two times */
232 status
= RtlRegisterWait(&wait1
, semaphores
[1], rtl_wait_cb
, &info
, INFINITE
, WT_EXECUTEDEFAULT
);
233 ok(!status
, "RtlRegisterWait failed with status %lx\n", status
);
234 ReleaseSemaphore(semaphores
[1], 1, NULL
);
235 result
= WaitForSingleObject(semaphores
[0], 100);
236 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
237 ok(info
.userdata
== 1, "expected info.userdata = 1, got %lu\n", info
.userdata
);
238 ReleaseSemaphore(semaphores
[1], 1, NULL
);
239 result
= WaitForSingleObject(semaphores
[0], 100);
240 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
241 ok(info
.userdata
== 2, "expected info.userdata = 2, got %lu\n", info
.userdata
);
242 result
= WaitForSingleObject(semaphores
[1], 0);
243 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
245 status
= RtlDeregisterWait(wait1
);
246 ok(!status
, "RtlDeregisterWait failed with status %lx\n", status
);
248 /* repeat test with WT_EXECUTEONLYONCE */
250 status
= RtlRegisterWait(&wait1
, semaphores
[1], rtl_wait_cb
, &info
, INFINITE
, WT_EXECUTEONLYONCE
);
251 ok(!status
, "RtlRegisterWait failed with status %lx\n", status
);
252 ReleaseSemaphore(semaphores
[1], 1, NULL
);
253 result
= WaitForSingleObject(semaphores
[0], 100);
254 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
255 ok(info
.userdata
== 1, "expected info.userdata = 1, got %lu\n", info
.userdata
);
256 ReleaseSemaphore(semaphores
[1], 1, NULL
);
257 result
= WaitForSingleObject(semaphores
[0], 100);
258 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
259 ok(info
.userdata
== 1, "expected info.userdata = 1, got %lu\n", info
.userdata
);
260 result
= WaitForSingleObject(semaphores
[1], 0);
261 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
263 status
= RtlDeregisterWait(wait1
);
264 ok(!status
, "RtlDeregisterWait failed with status %lx\n", status
);
266 /* finite timeout, no event */
268 status
= RtlRegisterWait(&wait1
, semaphores
[1], rtl_wait_cb
, &info
, 200, WT_EXECUTEDEFAULT
);
269 ok(!status
, "RtlRegisterWait failed with status %lx\n", status
);
270 result
= WaitForSingleObject(semaphores
[0], 100);
271 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
272 ok(info
.userdata
== 0, "expected info.userdata = 0, got %lu\n", info
.userdata
);
273 result
= WaitForSingleObject(semaphores
[0], 200);
274 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
275 ok(info
.userdata
== 0x10000, "expected info.userdata = 0x10000, got %lu\n", info
.userdata
);
276 result
= WaitForSingleObject(semaphores
[1], 0);
277 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
279 status
= RtlDeregisterWait(wait1
);
280 ok(!status
, "RtlDeregisterWait failed with status %lx\n", status
);
282 /* finite timeout, with event */
284 status
= RtlRegisterWait(&wait1
, semaphores
[1], rtl_wait_cb
, &info
, 200, WT_EXECUTEDEFAULT
);
285 ok(!status
, "RtlRegisterWait failed with status %lx\n", status
);
286 result
= WaitForSingleObject(semaphores
[0], 100);
287 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
288 ok(info
.userdata
== 0, "expected info.userdata = 0, got %lu\n", info
.userdata
);
289 ReleaseSemaphore(semaphores
[1], 1, NULL
);
290 result
= WaitForSingleObject(semaphores
[0], 100);
291 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
292 ok(info
.userdata
== 1, "expected info.userdata = 1, got %lu\n", info
.userdata
);
293 result
= WaitForSingleObject(semaphores
[1], 0);
294 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
296 status
= RtlDeregisterWait(wait1
);
297 ok(!status
, "RtlDeregisterWait failed with status %lx\n", status
);
299 /* test RtlRegisterWait WT_EXECUTEINWAITTHREAD flag */
302 status
= RtlRegisterWait(&wait1
, semaphores
[1], rtl_wait_cb
, &info
, 200, WT_EXECUTEINWAITTHREAD
|WT_EXECUTEONLYONCE
);
303 ok(!status
, "RtlRegisterWait failed with status %lx\n", status
);
304 ReleaseSemaphore(semaphores
[1], 1, NULL
);
305 result
= WaitForSingleObject(semaphores
[0], 200);
306 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
307 ok(info
.userdata
== 1, "expected info.userdata = 1, got %lu\n", info
.userdata
);
308 ok(info
.threadid
&& info
.threadid
!= GetCurrentThreadId(), "unexpected wait thread id %lx\n", info
.threadid
);
309 threadid
= info
.threadid
;
310 result
= WaitForSingleObject(semaphores
[1], 0);
311 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
313 status
= RtlDeregisterWait(wait1
);
314 ok(!status
, "RtlDeregisterWait failed with status %lx\n", status
);
318 status
= RtlRegisterWait(&wait1
, semaphores
[1], rtl_wait_cb
, &info
, 200, WT_EXECUTEINWAITTHREAD
|WT_EXECUTEONLYONCE
);
319 ok(!status
, "RtlRegisterWait failed with status %lx\n", status
);
320 ReleaseSemaphore(semaphores
[1], 1, NULL
);
321 result
= WaitForSingleObject(semaphores
[0], 200);
322 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
323 ok(info
.userdata
== 1, "expected info.userdata = 1, got %lu\n", info
.userdata
);
324 ok(info
.threadid
== threadid
, "unexpected different wait thread id %lx\n", info
.threadid
);
325 result
= WaitForSingleObject(semaphores
[1], 0);
326 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
328 status
= RtlDeregisterWait(wait1
);
329 ok(!status
, "RtlDeregisterWait failed with status %lx\n", status
);
331 /* test RtlRegisterWait WT_EXECUTEINWAITTHREAD flag with 0 timeout */
334 status
= RtlRegisterWait(&wait1
, semaphores
[1], rtl_wait_cb
, &info
, 0, WT_EXECUTEINWAITTHREAD
|WT_EXECUTEONLYONCE
);
335 ok(!status
, "RtlRegisterWait failed with status %lx\n", status
);
336 result
= WaitForSingleObject(semaphores
[0], 100);
337 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
338 ok(info
.userdata
== 0x10000, "expected info.userdata = 0x10000, got %lu\n", info
.userdata
);
339 ok(info
.threadid
== threadid
, "unexpected different wait thread id %lx\n", info
.threadid
);
340 result
= WaitForSingleObject(semaphores
[1], 0);
341 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
343 status
= RtlDeregisterWait(wait1
);
344 ok(!status
, "RtlDeregisterWait failed with status %lx\n", status
);
346 /* test RtlRegisterWait WT_EXECUTEINWAITTHREAD flag with already signaled event */
349 ReleaseSemaphore(semaphores
[1], 1, NULL
);
350 status
= RtlRegisterWait(&wait1
, semaphores
[1], rtl_wait_cb
, &info
, 200, WT_EXECUTEINWAITTHREAD
|WT_EXECUTEONLYONCE
);
351 ok(!status
, "RtlRegisterWait failed with status %lx\n", status
);
352 result
= WaitForSingleObject(semaphores
[0], 200);
353 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
354 ok(info
.userdata
== 1, "expected info.userdata = 1, got %lu\n", info
.userdata
);
355 ok(info
.threadid
== threadid
, "unexpected different wait thread id %lx\n", info
.threadid
);
356 result
= WaitForSingleObject(semaphores
[1], 0);
357 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
359 status
= RtlDeregisterWait(wait1
);
360 ok(!status
, "RtlDeregisterWait failed with status %lx\n", status
);
362 /* test for IO threads */
365 status
= RtlRegisterWait(&wait1
, semaphores
[1], rtl_wait_cb
, &info
, INFINITE
, WT_EXECUTEINIOTHREAD
);
366 ok(!status
, "RtlRegisterWait failed with status %lx\n", status
);
367 ReleaseSemaphore(semaphores
[1], 1, NULL
);
368 result
= WaitForSingleObject(semaphores
[0], 100);
369 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
370 ok(info
.userdata
== 1, "expected info.userdata = 1, got %lu\n", info
.userdata
);
371 ok(info
.threadid
!= 0, "expected info.threadid != 0, got %lu\n", info
.threadid
);
372 thread
= OpenThread(THREAD_SET_CONTEXT
, FALSE
, info
.threadid
);
373 ok(thread
!= NULL
, "OpenThread failed with %lu\n", GetLastError());
374 rtl_wait_apc_semaphore
= semaphores
[0];
375 result
= QueueUserAPC(rtl_wait_apc_cb
, thread
, 0);
376 ok(result
!= 0, "QueueUserAPC failed with %lu\n", GetLastError());
377 result
= WaitForSingleObject(semaphores
[0], 200);
378 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
379 rtl_wait_apc_semaphore
= 0;
381 ReleaseSemaphore(semaphores
[1], 1, NULL
);
382 result
= WaitForSingleObject(semaphores
[0], 100);
383 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
384 ok(info
.userdata
== 2, "expected info.userdata = 2, got %lu\n", info
.userdata
);
386 status
= RtlDeregisterWait(wait1
);
387 ok(!status
, "RtlDeregisterWait failed with status %lx\n", status
);
391 status
= RtlRegisterWait(&wait1
, semaphores
[1], rtl_wait_cb
, &info
, INFINITE
, WT_EXECUTEDEFAULT
);
392 ok(!status
, "RtlRegisterWait failed with status %lx\n", status
);
393 ReleaseSemaphore(semaphores
[1], 1, NULL
);
394 result
= WaitForSingleObject(semaphores
[0], 100);
395 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
396 ok(info
.userdata
== 1, "expected info.userdata = 1, got %lu\n", info
.userdata
);
397 ok(info
.threadid
!= 0, "expected info.threadid != 0, got %lu\n", info
.threadid
);
398 thread
= OpenThread(THREAD_SET_CONTEXT
, FALSE
, info
.threadid
);
399 ok(thread
!= NULL
, "OpenThread failed with %lu\n", GetLastError());
400 rtl_wait_apc_semaphore
= semaphores
[0];
401 result
= QueueUserAPC(rtl_wait_apc_cb
, thread
, 0);
402 ok(result
!= 0, "QueueUserAPC failed with %lu\n", GetLastError());
403 result
= WaitForSingleObject(semaphores
[0], 200);
404 ok(result
== WAIT_TIMEOUT
|| broken(result
== WAIT_OBJECT_0
) /* >= Win Vista */,
405 "WaitForSingleObject returned %lu\n", result
);
406 rtl_wait_apc_semaphore
= 0;
408 ReleaseSemaphore(semaphores
[1], 1, NULL
);
409 result
= WaitForSingleObject(semaphores
[0], 100);
410 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
411 ok(info
.userdata
== 2, "expected info.userdata = 2, got %lu\n", info
.userdata
);
413 status
= RtlDeregisterWait(wait1
);
414 ok(!status
, "RtlDeregisterWait failed with status %lx\n", status
);
416 /* test RtlDeregisterWaitEx before wait expired */
418 status
= RtlRegisterWait(&wait1
, semaphores
[1], rtl_wait_cb
, &info
, INFINITE
, WT_EXECUTEDEFAULT
);
419 ok(!status
, "RtlRegisterWait failed with status %lx\n", status
);
420 status
= RtlDeregisterWaitEx(wait1
, NULL
);
421 ok(!status
, "RtlDeregisterWaitEx failed with status %lx\n", status
);
422 ok(info
.userdata
== 0, "expected info.userdata = 0, got %lu\n", info
.userdata
);
425 status
= RtlRegisterWait(&wait1
, semaphores
[1], rtl_wait_cb
, &info
, INFINITE
, WT_EXECUTEDEFAULT
);
426 ok(!status
, "RtlRegisterWait failed with status %lx\n", status
);
427 status
= RtlDeregisterWaitEx(wait1
, INVALID_HANDLE_VALUE
);
428 ok(!status
, "RtlDeregisterWaitEx failed with status %lx\n", status
);
429 ok(info
.userdata
== 0, "expected info.userdata = 0, got %lu\n", info
.userdata
);
432 status
= RtlRegisterWait(&wait1
, semaphores
[1], rtl_wait_cb
, &info
, INFINITE
, WT_EXECUTEDEFAULT
);
433 ok(!status
, "RtlRegisterWait failed with status %lx\n", status
);
434 status
= RtlDeregisterWaitEx(wait1
, event
);
435 ok(!status
, "RtlDeregisterWaitEx failed with status %lx\n", status
);
436 ok(info
.userdata
== 0, "expected info.userdata = 0, got %lu\n", info
.userdata
);
437 result
= WaitForSingleObject(event
, 200);
438 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
440 /* test RtlDeregisterWaitEx after wait expired */
442 status
= RtlRegisterWait(&wait1
, semaphores
[1], rtl_wait_cb
, &info
, 0, WT_EXECUTEONLYONCE
);
443 ok(!status
, "RtlRegisterWait failed with status %lx\n", status
);
444 result
= WaitForSingleObject(semaphores
[0], 100);
445 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
446 ok(info
.userdata
== 0x10000, "expected info.userdata = 0x10000, got %lu\n", info
.userdata
);
448 status
= RtlDeregisterWaitEx(wait1
, NULL
);
449 ok(!status
, "RtlDeregisterWaitEx failed with status %lx\n", status
);
450 ok(info
.userdata
== 0x10000, "expected info.userdata = 0x10000, got %lu\n", info
.userdata
);
453 status
= RtlRegisterWait(&wait1
, semaphores
[1], rtl_wait_cb
, &info
, 0, WT_EXECUTEONLYONCE
);
454 ok(!status
, "RtlRegisterWait failed with status %lx\n", status
);
455 result
= WaitForSingleObject(semaphores
[0], 100);
456 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
457 ok(info
.userdata
== 0x10000, "expected info.userdata = 0x10000, got %lu\n", info
.userdata
);
459 status
= RtlDeregisterWaitEx(wait1
, INVALID_HANDLE_VALUE
);
460 ok(!status
, "RtlDeregisterWaitEx failed with status %lx\n", status
);
461 ok(info
.userdata
== 0x10000, "expected info.userdata = 0x10000, got %lu\n", info
.userdata
);
464 status
= RtlRegisterWait(&wait1
, semaphores
[1], rtl_wait_cb
, &info
, 0, WT_EXECUTEONLYONCE
);
465 ok(!status
, "RtlRegisterWait failed with status %lx\n", status
);
466 result
= WaitForSingleObject(semaphores
[0], 100);
467 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
468 ok(info
.userdata
== 0x10000, "expected info.userdata = 0x10000, got %lu\n", info
.userdata
);
470 status
= RtlDeregisterWaitEx(wait1
, event
);
471 ok(!status
, "RtlDeregisterWaitEx failed with status %lx\n", status
);
472 ok(info
.userdata
== 0x10000, "expected info.userdata = 0x10000, got %lu\n", info
.userdata
);
473 result
= WaitForSingleObject(event
, 200);
474 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
476 /* test RtlDeregisterWaitEx while callback is running */
477 info
.semaphore2
= semaphores
[1];
478 info
.wait_result
= WAIT_OBJECT_0
;
481 status
= RtlRegisterWait(&wait1
, semaphores
[1], rtl_wait_cb
, &info
, INFINITE
, WT_EXECUTEONLYONCE
);
482 ok(!status
, "RtlRegisterWait failed with status %lx\n", status
);
483 ReleaseSemaphore(semaphores
[1], 1, NULL
);
484 result
= WaitForSingleObject(semaphores
[0], 1000);
485 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
486 ok(info
.userdata
== 1, "expected info.userdata = 1, got %lu\n", info
.userdata
);
487 status
= RtlDeregisterWait(wait1
);
488 ok(status
== STATUS_PENDING
, "expected STATUS_PENDING, got %lx\n", status
);
489 ReleaseSemaphore(semaphores
[1], 1, NULL
);
490 result
= WaitForSingleObject(semaphores
[0], 1000);
491 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
494 status
= RtlRegisterWait(&wait1
, semaphores
[1], rtl_wait_cb
, &info
, INFINITE
, WT_EXECUTEINWAITTHREAD
|WT_EXECUTEONLYONCE
);
495 ok(!status
, "RtlRegisterWait failed with status %lx\n", status
);
496 ReleaseSemaphore(semaphores
[1], 1, NULL
);
497 result
= WaitForSingleObject(semaphores
[0], 1000);
498 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
499 ok(info
.userdata
== 1, "expected info.userdata = 1, got %lu\n", info
.userdata
);
500 status
= RtlDeregisterWait(wait1
);
501 ok(status
== STATUS_PENDING
, "expected STATUS_PENDING, got %lx\n", status
);
502 ReleaseSemaphore(semaphores
[1], 1, NULL
);
503 result
= WaitForSingleObject(semaphores
[0], 1000);
504 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
507 status
= RtlRegisterWait(&wait1
, semaphores
[1], rtl_wait_cb
, &info
, INFINITE
, WT_EXECUTEONLYONCE
);
508 ok(!status
, "RtlRegisterWait failed with status %lx\n", status
);
509 ReleaseSemaphore(semaphores
[1], 1, NULL
);
510 result
= WaitForSingleObject(semaphores
[0], 1000);
511 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
512 ok(info
.userdata
== 1, "expected info.userdata = 1, got %lu\n", info
.userdata
);
513 status
= RtlDeregisterWaitEx(wait1
, NULL
);
514 ok(status
== STATUS_PENDING
, "expected STATUS_PENDING, got %lx\n", status
);
515 ReleaseSemaphore(semaphores
[1], 1, NULL
);
516 result
= WaitForSingleObject(semaphores
[0], 1000);
517 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
519 info
.wait_result
= WAIT_TIMEOUT
;
521 status
= RtlRegisterWait(&wait1
, semaphores
[1], rtl_wait_cb
, &info
, INFINITE
, WT_EXECUTEONLYONCE
);
522 ok(!status
, "RtlRegisterWait failed with status %lx\n", status
);
523 ReleaseSemaphore(semaphores
[1], 1, NULL
);
524 result
= WaitForSingleObject(semaphores
[0], 1000);
525 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
526 ok(info
.userdata
== 1, "expected info.userdata = 1, got %lu\n", info
.userdata
);
527 status
= RtlDeregisterWaitEx(wait1
, INVALID_HANDLE_VALUE
);
528 ok(!status
, "RtlDeregisterWaitEx failed with status %lx\n", status
);
529 result
= WaitForSingleObject(semaphores
[0], 0);
530 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
532 info
.wait_result
= WAIT_TIMEOUT
;
534 status
= RtlRegisterWait(&wait1
, semaphores
[1], rtl_wait_cb
, &info
, INFINITE
, WT_EXECUTEINWAITTHREAD
|WT_EXECUTEONLYONCE
);
535 ok(!status
, "RtlRegisterWait failed with status %lx\n", status
);
536 ReleaseSemaphore(semaphores
[1], 1, NULL
);
537 result
= WaitForSingleObject(semaphores
[0], 1000);
538 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
539 ok(info
.userdata
== 1, "expected info.userdata = 1, got %lu\n", info
.userdata
);
540 status
= RtlDeregisterWaitEx(wait1
, INVALID_HANDLE_VALUE
);
541 ok(!status
, "RtlDeregisterWaitEx failed with status %lx\n", status
);
542 result
= WaitForSingleObject(semaphores
[0], 0);
543 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
545 info
.wait_result
= WAIT_OBJECT_0
;
547 status
= RtlRegisterWait(&wait1
, semaphores
[1], rtl_wait_cb
, &info
, INFINITE
, WT_EXECUTEONLYONCE
);
548 ok(!status
, "RtlRegisterWait failed with status %lx\n", status
);
549 ReleaseSemaphore(semaphores
[1], 1, NULL
);
550 result
= WaitForSingleObject(semaphores
[0], 1000);
551 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
552 ok(info
.userdata
== 1, "expected info.userdata = 1, got %lu\n", info
.userdata
);
553 status
= RtlDeregisterWaitEx(wait1
, event
);
554 ok(status
== STATUS_PENDING
, "expected STATUS_PENDING, got %lx\n", status
);
555 ReleaseSemaphore(semaphores
[1], 1, NULL
);
556 result
= WaitForSingleObject(event
, 1000);
557 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
558 result
= WaitForSingleObject(semaphores
[0], 0);
559 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
561 CloseHandle(semaphores
[0]);
562 CloseHandle(semaphores
[1]);
566 static void CALLBACK
simple_cb(TP_CALLBACK_INSTANCE
*instance
, void *userdata
)
568 HANDLE semaphore
= userdata
;
569 ReleaseSemaphore(semaphore
, 1, NULL
);
572 static void CALLBACK
simple2_cb(TP_CALLBACK_INSTANCE
*instance
, void *userdata
)
575 InterlockedIncrement((LONG
*)userdata
);
578 static void test_tp_simple(void)
580 IMAGE_NT_HEADERS
*nt
= RtlImageNtHeader( NtCurrentTeb()->Peb
->ImageBaseAddress
);
581 TP_POOL_STACK_INFORMATION stack_info
;
582 TP_CALLBACK_ENVIRON environment
;
583 TP_CALLBACK_ENVIRON_V3 environment3
;
584 TP_CLEANUP_GROUP
*group
;
592 semaphore
= CreateSemaphoreA(NULL
, 0, 1, NULL
);
593 ok(semaphore
!= NULL
, "CreateSemaphoreA failed %lu\n", GetLastError());
595 /* post the callback using the default threadpool */
596 memset(&environment
, 0, sizeof(environment
));
597 environment
.Version
= 1;
598 environment
.Pool
= NULL
;
599 status
= pTpSimpleTryPost(simple_cb
, semaphore
, &environment
);
600 ok(!status
, "TpSimpleTryPost failed with status %lx\n", status
);
601 result
= WaitForSingleObject(semaphore
, 1000);
602 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
604 /* allocate new threadpool */
606 status
= pTpAllocPool(&pool
, NULL
);
607 ok(!status
, "TpAllocPool failed with status %lx\n", status
);
608 ok(pool
!= NULL
, "expected pool != NULL\n");
610 /* post the callback using the new threadpool */
611 memset(&environment
, 0, sizeof(environment
));
612 environment
.Version
= 1;
613 environment
.Pool
= pool
;
614 status
= pTpSimpleTryPost(simple_cb
, semaphore
, &environment
);
615 ok(!status
, "TpSimpleTryPost failed with status %lx\n", status
);
616 result
= WaitForSingleObject(semaphore
, 1000);
617 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
619 /* test with environment version 3 */
620 memset(&environment3
, 0, sizeof(environment3
));
621 environment3
.Version
= 3;
622 environment3
.Pool
= pool
;
623 environment3
.Size
= sizeof(environment3
);
625 for (i
= 0; i
< 3; ++i
)
627 environment3
.CallbackPriority
= TP_CALLBACK_PRIORITY_HIGH
+ i
;
628 status
= pTpSimpleTryPost(simple_cb
, semaphore
, (TP_CALLBACK_ENVIRON
*)&environment3
);
629 ok(!status
, "TpSimpleTryPost failed with status %lx\n", status
);
630 result
= WaitForSingleObject(semaphore
, 1000);
631 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
634 environment3
.CallbackPriority
= 10;
635 status
= pTpSimpleTryPost(simple_cb
, semaphore
, (TP_CALLBACK_ENVIRON
*)&environment3
);
636 ok(status
== STATUS_INVALID_PARAMETER
|| broken(!status
) /* Vista does not support priorities */,
637 "TpSimpleTryPost failed with status %lx\n", status
);
639 /* test with invalid version number */
640 memset(&environment
, 0, sizeof(environment
));
641 environment
.Version
= 9999;
642 environment
.Pool
= pool
;
643 status
= pTpSimpleTryPost(simple_cb
, semaphore
, &environment
);
645 ok(status
== STATUS_INVALID_PARAMETER
|| broken(!status
) /* Vista/2008 */,
646 "TpSimpleTryPost unexpectedly returned status %lx\n", status
);
649 result
= WaitForSingleObject(semaphore
, 1000);
650 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
653 /* allocate a cleanup group for synchronization */
655 status
= pTpAllocCleanupGroup(&group
);
656 ok(!status
, "TpAllocCleanupGroup failed with status %lx\n", status
);
657 ok(group
!= NULL
, "expected pool != NULL\n");
659 /* use cleanup group to wait for a simple callback */
661 memset(&environment
, 0, sizeof(environment
));
662 environment
.Version
= 1;
663 environment
.Pool
= pool
;
664 environment
.CleanupGroup
= group
;
665 status
= pTpSimpleTryPost(simple2_cb
, &userdata
, &environment
);
666 ok(!status
, "TpSimpleTryPost failed with status %lx\n", status
);
667 pTpReleaseCleanupGroupMembers(group
, FALSE
, NULL
);
668 ok(userdata
== 1, "expected userdata = 1, got %lu\n", userdata
);
670 /* test cancellation of pending simple callbacks */
672 pTpSetPoolMaxThreads(pool
, 10);
673 memset(&environment
, 0, sizeof(environment
));
674 environment
.Version
= 1;
675 environment
.Pool
= pool
;
676 environment
.CleanupGroup
= group
;
677 for (i
= 0; i
< 100; i
++)
679 status
= pTpSimpleTryPost(simple2_cb
, &userdata
, &environment
);
680 ok(!status
, "TpSimpleTryPost failed with status %lx\n", status
);
682 pTpReleaseCleanupGroupMembers(group
, TRUE
, NULL
);
683 ok(userdata
< 100, "expected userdata < 100, got %lu\n", userdata
);
685 /* test querying and setting the stack size */
686 status
= pTpQueryPoolStackInformation(pool
, &stack_info
);
687 ok(!status
, "TpQueryPoolStackInformation failed: %lx\n", status
);
688 ok(stack_info
.StackReserve
== nt
->OptionalHeader
.SizeOfStackReserve
, "expected default StackReserve, got %Ix\n", stack_info
.StackReserve
);
689 ok(stack_info
.StackCommit
== nt
->OptionalHeader
.SizeOfStackCommit
, "expected default StackCommit, got %Ix\n", stack_info
.StackCommit
);
691 /* threadpool does not validate the stack size values */
692 stack_info
.StackReserve
= stack_info
.StackCommit
= 1;
693 status
= pTpSetPoolStackInformation(pool
, &stack_info
);
694 ok(!status
, "TpSetPoolStackInformation failed: %lx\n", status
);
696 status
= pTpQueryPoolStackInformation(pool
, &stack_info
);
697 ok(!status
, "TpQueryPoolStackInformation failed: %lx\n", status
);
698 ok(stack_info
.StackReserve
== 1, "expected 1 byte StackReserve, got %ld\n", (ULONG
)stack_info
.StackReserve
);
699 ok(stack_info
.StackCommit
== 1, "expected 1 byte StackCommit, got %ld\n", (ULONG
)stack_info
.StackCommit
);
702 pTpReleaseCleanupGroup(group
);
703 pTpReleasePool(pool
);
704 CloseHandle(semaphore
);
707 static void CALLBACK
work_cb(TP_CALLBACK_INSTANCE
*instance
, void *userdata
, TP_WORK
*work
)
710 InterlockedIncrement((LONG
*)userdata
);
713 static void CALLBACK
work2_cb(TP_CALLBACK_INSTANCE
*instance
, void *userdata
, TP_WORK
*work
)
716 InterlockedExchangeAdd((LONG
*)userdata
, 0x10000);
719 static void test_tp_work(void)
721 TP_CALLBACK_ENVIRON environment
;
728 /* allocate new threadpool with only one thread */
730 status
= pTpAllocPool(&pool
, NULL
);
731 ok(!status
, "TpAllocPool failed with status %lx\n", status
);
732 ok(pool
!= NULL
, "expected pool != NULL\n");
733 pTpSetPoolMaxThreads(pool
, 1);
735 /* allocate new work item */
737 memset(&environment
, 0, sizeof(environment
));
738 environment
.Version
= 1;
739 environment
.Pool
= pool
;
740 status
= pTpAllocWork(&work
, work_cb
, &userdata
, &environment
);
741 ok(!status
, "TpAllocWork failed with status %lx\n", status
);
742 ok(work
!= NULL
, "expected work != NULL\n");
744 /* post 5 identical work items at once */
746 for (i
= 0; i
< 5; i
++)
748 pTpWaitForWork(work
, FALSE
);
749 ok(userdata
== 5, "expected userdata = 5, got %lu\n", userdata
);
751 /* add more tasks and cancel them immediately */
753 for (i
= 0; i
< 10; i
++)
755 pTpWaitForWork(work
, TRUE
);
756 ok(userdata
< 10, "expected userdata < 10, got %lu\n", userdata
);
759 pTpReleaseWork(work
);
760 pTpReleasePool(pool
);
763 static void test_tp_work_scheduler(void)
765 TP_CALLBACK_ENVIRON environment
;
766 TP_CLEANUP_GROUP
*group
;
767 TP_WORK
*work
, *work2
;
773 /* allocate new threadpool with only one thread */
775 status
= pTpAllocPool(&pool
, NULL
);
776 ok(!status
, "TpAllocPool failed with status %lx\n", status
);
777 ok(pool
!= NULL
, "expected pool != NULL\n");
778 pTpSetPoolMaxThreads(pool
, 1);
780 /* create a cleanup group */
782 status
= pTpAllocCleanupGroup(&group
);
783 ok(!status
, "TpAllocCleanupGroup failed with status %lx\n", status
);
784 ok(group
!= NULL
, "expected pool != NULL\n");
786 /* the first work item has no cleanup group associated */
788 memset(&environment
, 0, sizeof(environment
));
789 environment
.Version
= 1;
790 environment
.Pool
= pool
;
791 status
= pTpAllocWork(&work
, work_cb
, &userdata
, &environment
);
792 ok(!status
, "TpAllocWork failed with status %lx\n", status
);
793 ok(work
!= NULL
, "expected work != NULL\n");
795 /* allocate a second work item with a cleanup group */
797 memset(&environment
, 0, sizeof(environment
));
798 environment
.Version
= 1;
799 environment
.Pool
= pool
;
800 environment
.CleanupGroup
= group
;
801 status
= pTpAllocWork(&work2
, work2_cb
, &userdata
, &environment
);
802 ok(!status
, "TpAllocWork failed with status %lx\n", status
);
803 ok(work2
!= NULL
, "expected work2 != NULL\n");
805 /* the 'work' callbacks are not blocking execution of 'work2' callbacks */
807 for (i
= 0; i
< 10; i
++)
809 for (i
= 0; i
< 10; i
++)
812 pTpWaitForWork(work
, TRUE
);
813 pTpWaitForWork(work2
, TRUE
);
814 ok(userdata
& 0xffff, "expected userdata & 0xffff != 0, got %lu\n", userdata
& 0xffff);
815 ok(userdata
>> 16, "expected userdata >> 16 != 0, got %lu\n", userdata
>> 16);
817 /* test TpReleaseCleanupGroupMembers on a work item */
819 for (i
= 0; i
< 10; i
++)
821 for (i
= 0; i
< 3; i
++)
823 pTpReleaseCleanupGroupMembers(group
, FALSE
, NULL
);
824 pTpWaitForWork(work
, TRUE
);
825 ok((userdata
& 0xffff) < 10, "expected userdata & 0xffff < 10, got %lu\n", userdata
& 0xffff);
826 ok((userdata
>> 16) == 3, "expected userdata >> 16 == 3, got %lu\n", userdata
>> 16);
829 pTpReleaseWork(work
);
830 pTpReleaseCleanupGroup(group
);
831 pTpReleasePool(pool
);
834 static void CALLBACK
simple_release_cb(TP_CALLBACK_INSTANCE
*instance
, void *userdata
)
836 HANDLE
*semaphores
= userdata
;
837 ReleaseSemaphore(semaphores
, 1, NULL
);
838 Sleep(200); /* wait until main thread is in TpReleaseCleanupGroupMembers */
841 static void CALLBACK
work_release_cb(TP_CALLBACK_INSTANCE
*instance
, void *userdata
, TP_WORK
*work
)
843 HANDLE semaphore
= userdata
;
844 ReleaseSemaphore(semaphore
, 1, NULL
);
845 Sleep(200); /* wait until main thread is in TpReleaseCleanupGroupMembers */
846 pTpReleaseWork(work
);
849 static void CALLBACK
timer_release_cb(TP_CALLBACK_INSTANCE
*instance
, void *userdata
, TP_TIMER
*timer
)
851 HANDLE semaphore
= userdata
;
852 ReleaseSemaphore(semaphore
, 1, NULL
);
853 Sleep(200); /* wait until main thread is in TpReleaseCleanupGroupMembers */
854 pTpReleaseTimer(timer
);
857 static void CALLBACK
wait_release_cb(TP_CALLBACK_INSTANCE
*instance
, void *userdata
,
858 TP_WAIT
*wait
, TP_WAIT_RESULT result
)
860 HANDLE semaphore
= userdata
;
861 ReleaseSemaphore(semaphore
, 1, NULL
);
862 Sleep(200); /* wait until main thread is in TpReleaseCleanupGroupMembers */
863 pTpReleaseWait(wait
);
866 static void test_tp_group_wait(void)
868 TP_CALLBACK_ENVIRON environment
;
869 TP_CLEANUP_GROUP
*group
;
879 semaphore
= CreateSemaphoreA(NULL
, 0, 1, NULL
);
880 ok(semaphore
!= NULL
, "CreateSemaphoreA failed %lu\n", GetLastError());
882 /* allocate new threadpool */
884 status
= pTpAllocPool(&pool
, NULL
);
885 ok(!status
, "TpAllocPool failed with status %lx\n", status
);
886 ok(pool
!= NULL
, "expected pool != NULL\n");
888 /* allocate a cleanup group */
890 status
= pTpAllocCleanupGroup(&group
);
891 ok(!status
, "TpAllocCleanupGroup failed with status %lx\n", status
);
892 ok(group
!= NULL
, "expected pool != NULL\n");
894 /* release work object during TpReleaseCleanupGroupMembers */
896 memset(&environment
, 0, sizeof(environment
));
897 environment
.Version
= 1;
898 environment
.Pool
= pool
;
899 environment
.CleanupGroup
= group
;
900 status
= pTpAllocWork(&work
, work_release_cb
, semaphore
, &environment
);
901 ok(!status
, "TpAllocWork failed with status %lx\n", status
);
902 ok(work
!= NULL
, "expected work != NULL\n");
904 result
= WaitForSingleObject(semaphore
, 1000);
905 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
906 pTpReleaseCleanupGroupMembers(group
, FALSE
, NULL
);
908 /* release timer object during TpReleaseCleanupGroupMembers */
910 memset(&environment
, 0, sizeof(environment
));
911 environment
.Version
= 1;
912 environment
.Pool
= pool
;
913 environment
.CleanupGroup
= group
;
914 status
= pTpAllocTimer(&timer
, timer_release_cb
, semaphore
, &environment
);
915 ok(!status
, "TpAllocTimer failed with status %lx\n", status
);
916 ok(timer
!= NULL
, "expected timer != NULL\n");
918 pTpSetTimer(timer
, &when
, 0, 0);
919 result
= WaitForSingleObject(semaphore
, 1000);
920 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
921 pTpReleaseCleanupGroupMembers(group
, FALSE
, NULL
);
923 /* release wait object during TpReleaseCleanupGroupMembers */
925 memset(&environment
, 0, sizeof(environment
));
926 environment
.Version
= 1;
927 environment
.Pool
= pool
;
928 environment
.CleanupGroup
= group
;
929 status
= pTpAllocWait(&wait
, wait_release_cb
, semaphore
, &environment
);
930 ok(!status
, "TpAllocWait failed with status %lx\n", status
);
931 ok(wait
!= NULL
, "expected wait != NULL\n");
933 pTpSetWait(wait
, INVALID_HANDLE_VALUE
, &when
);
934 result
= WaitForSingleObject(semaphore
, 1000);
935 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
936 pTpReleaseCleanupGroupMembers(group
, FALSE
, NULL
);
939 pTpReleaseCleanupGroup(group
);
940 pTpReleasePool(pool
);
941 CloseHandle(semaphore
);
944 static DWORD group_cancel_tid
;
946 static void CALLBACK
simple_group_cancel_cb(TP_CALLBACK_INSTANCE
*instance
, void *userdata
)
948 HANDLE
*semaphores
= userdata
;
953 status
= pTpCallbackMayRunLong(instance
);
954 ok(status
== STATUS_TOO_MANY_THREADS
|| broken(status
== 1) /* Win Vista / 2008 */,
955 "expected STATUS_TOO_MANY_THREADS, got %08lx\n", status
);
957 ReleaseSemaphore(semaphores
[1], 1, NULL
);
958 for (i
= 0; i
< 4; i
++)
960 result
= WaitForSingleObject(semaphores
[0], 1000);
961 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
963 ReleaseSemaphore(semaphores
[1], 1, NULL
);
966 static void CALLBACK
work_group_cancel_cb(TP_CALLBACK_INSTANCE
*instance
, void *userdata
, TP_WORK
*work
)
968 HANDLE
*semaphores
= userdata
;
971 ReleaseSemaphore(semaphores
[1], 1, NULL
);
972 result
= WaitForSingleObject(semaphores
[0], 200);
973 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
976 static void CALLBACK
group_cancel_cleanup_release_cb(void *object
, void *userdata
)
978 HANDLE
*semaphores
= userdata
;
979 group_cancel_tid
= GetCurrentThreadId();
980 ok(object
== (void *)0xdeadbeef, "expected 0xdeadbeef, got %p\n", object
);
981 ReleaseSemaphore(semaphores
[0], 1, NULL
);
984 static void CALLBACK
group_cancel_cleanup_release2_cb(void *object
, void *userdata
)
986 HANDLE
*semaphores
= userdata
;
987 group_cancel_tid
= GetCurrentThreadId();
988 ok(object
== userdata
, "expected %p, got %p\n", userdata
, object
);
989 ReleaseSemaphore(semaphores
[0], 1, NULL
);
992 static void CALLBACK
group_cancel_cleanup_increment_cb(void *object
, void *userdata
)
994 group_cancel_tid
= GetCurrentThreadId();
995 InterlockedIncrement((LONG
*)userdata
);
998 static void CALLBACK
unexpected_simple_cb(TP_CALLBACK_INSTANCE
*instance
, void *userdata
)
1000 ok(0, "Unexpected callback\n");
1003 static void CALLBACK
unexpected_work_cb(TP_CALLBACK_INSTANCE
*instance
, void *userdata
, TP_WORK
*work
)
1005 ok(0, "Unexpected callback\n");
1008 static void CALLBACK
unexpected_timer_cb(TP_CALLBACK_INSTANCE
*instance
, void *userdata
, TP_TIMER
*timer
)
1010 ok(0, "Unexpected callback\n");
1013 static void CALLBACK
unexpected_wait_cb(TP_CALLBACK_INSTANCE
*instance
, void *userdata
,
1014 TP_WAIT
*wait
, TP_WAIT_RESULT result
)
1016 ok(0, "Unexpected callback\n");
1019 static void CALLBACK
unexpected_group_cancel_cleanup_cb(void *object
, void *userdata
)
1021 ok(0, "Unexpected callback\n");
1024 static void test_tp_group_cancel(void)
1026 TP_CALLBACK_ENVIRON environment
;
1027 TP_CLEANUP_GROUP
*group
;
1028 LONG userdata
, userdata2
;
1029 HANDLE semaphores
[2];
1038 semaphores
[0] = CreateSemaphoreA(NULL
, 0, 4, NULL
);
1039 ok(semaphores
[0] != NULL
, "CreateSemaphoreA failed %lu\n", GetLastError());
1040 semaphores
[1] = CreateSemaphoreA(NULL
, 0, 1, NULL
);
1041 ok(semaphores
[1] != NULL
, "CreateSemaphoreA failed %lu\n", GetLastError());
1043 /* allocate new threadpool with only one thread */
1045 status
= pTpAllocPool(&pool
, NULL
);
1046 ok(!status
, "TpAllocPool failed with status %lx\n", status
);
1047 ok(pool
!= NULL
, "expected pool != NULL\n");
1048 pTpSetPoolMaxThreads(pool
, 1);
1050 /* allocate a cleanup group */
1052 status
= pTpAllocCleanupGroup(&group
);
1053 ok(!status
, "TpAllocCleanupGroup failed with status %lx\n", status
);
1054 ok(group
!= NULL
, "expected pool != NULL\n");
1056 /* test execution of cancellation callback */
1057 memset(&environment
, 0, sizeof(environment
));
1058 environment
.Version
= 1;
1059 environment
.Pool
= pool
;
1060 status
= pTpSimpleTryPost(simple_group_cancel_cb
, semaphores
, &environment
);
1061 ok(!status
, "TpSimpleTryPost failed with status %lx\n", status
);
1062 result
= WaitForSingleObject(semaphores
[1], 1000);
1063 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1065 memset(&environment
, 0, sizeof(environment
));
1066 environment
.Version
= 1;
1067 environment
.Pool
= pool
;
1068 environment
.CleanupGroup
= group
;
1069 environment
.CleanupGroupCancelCallback
= group_cancel_cleanup_release_cb
;
1070 status
= pTpSimpleTryPost(unexpected_simple_cb
, (void *)0xdeadbeef, &environment
);
1071 ok(!status
, "TpSimpleTryPost failed with status %lx\n", status
);
1074 status
= pTpAllocWork(&work
, unexpected_work_cb
, (void *)0xdeadbeef, &environment
);
1075 ok(!status
, "TpAllocWork failed with status %lx\n", status
);
1076 ok(work
!= NULL
, "expected work != NULL\n");
1079 status
= pTpAllocTimer(&timer
, unexpected_timer_cb
, (void *)0xdeadbeef, &environment
);
1080 ok(!status
, "TpAllocTimer failed with status %lx\n", status
);
1081 ok(timer
!= NULL
, "expected timer != NULL\n");
1084 status
= pTpAllocWait(&wait
, unexpected_wait_cb
, (void *)0xdeadbeef, &environment
);
1085 ok(!status
, "TpAllocWait failed with status %lx\n", status
);
1086 ok(wait
!= NULL
, "expected wait != NULL\n");
1088 group_cancel_tid
= 0xdeadbeef;
1089 pTpReleaseCleanupGroupMembers(group
, TRUE
, semaphores
);
1090 result
= WaitForSingleObject(semaphores
[1], 1000);
1091 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1092 ok(group_cancel_tid
== GetCurrentThreadId(), "expected tid %lx, got %lx\n",
1093 GetCurrentThreadId(), group_cancel_tid
);
1095 /* test if cancellation callbacks are executed before or after wait */
1097 memset(&environment
, 0, sizeof(environment
));
1098 environment
.Version
= 1;
1099 environment
.Pool
= pool
;
1100 environment
.CleanupGroup
= group
;
1101 environment
.CleanupGroupCancelCallback
= group_cancel_cleanup_release2_cb
;
1102 status
= pTpAllocWork(&work
, work_group_cancel_cb
, semaphores
, &environment
);
1103 ok(!status
, "TpAllocWork failed with status %lx\n", status
);
1104 ok(work
!= NULL
, "expected work != NULL\n");
1108 result
= WaitForSingleObject(semaphores
[1], 1000);
1109 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1111 group_cancel_tid
= 0xdeadbeef;
1112 pTpReleaseCleanupGroupMembers(group
, TRUE
, semaphores
);
1113 result
= WaitForSingleObject(semaphores
[0], 1000);
1114 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1115 ok(group_cancel_tid
== GetCurrentThreadId(), "expected tid %lx, got %lx\n",
1116 GetCurrentThreadId(), group_cancel_tid
);
1118 /* group cancel callback is not executed if object is destroyed while waiting */
1120 memset(&environment
, 0, sizeof(environment
));
1121 environment
.Version
= 1;
1122 environment
.Pool
= pool
;
1123 environment
.CleanupGroup
= group
;
1124 environment
.CleanupGroupCancelCallback
= unexpected_group_cancel_cleanup_cb
;
1125 status
= pTpAllocWork(&work
, work_release_cb
, semaphores
[1], &environment
);
1126 ok(!status
, "TpAllocWork failed with status %lx\n", status
);
1127 ok(work
!= NULL
, "expected work != NULL\n");
1130 result
= WaitForSingleObject(semaphores
[1], 1000);
1131 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1132 pTpReleaseCleanupGroupMembers(group
, TRUE
, NULL
);
1134 /* terminated simple callbacks should not trigger the group cancel callback */
1135 memset(&environment
, 0, sizeof(environment
));
1136 environment
.Version
= 1;
1137 environment
.Pool
= pool
;
1138 environment
.CleanupGroup
= group
;
1139 environment
.CleanupGroupCancelCallback
= unexpected_group_cancel_cleanup_cb
;
1140 status
= pTpSimpleTryPost(simple_release_cb
, semaphores
[1], &environment
);
1141 ok(!status
, "TpSimpleTryPost failed with status %lx\n", status
);
1142 result
= WaitForSingleObject(semaphores
[1], 1000);
1143 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1144 pTpReleaseCleanupGroupMembers(group
, TRUE
, semaphores
);
1146 /* test cancellation callback for objects with multiple instances */
1148 memset(&environment
, 0, sizeof(environment
));
1149 environment
.Version
= 1;
1150 environment
.Pool
= pool
;
1151 environment
.CleanupGroup
= group
;
1152 environment
.CleanupGroupCancelCallback
= group_cancel_cleanup_increment_cb
;
1153 status
= pTpAllocWork(&work
, work_cb
, &userdata
, &environment
);
1154 ok(!status
, "TpAllocWork failed with status %lx\n", status
);
1155 ok(work
!= NULL
, "expected work != NULL\n");
1157 /* post 10 identical work items at once */
1158 userdata
= userdata2
= 0;
1159 for (i
= 0; i
< 10; i
++)
1162 /* check if we get multiple cancellation callbacks */
1163 group_cancel_tid
= 0xdeadbeef;
1164 pTpReleaseCleanupGroupMembers(group
, TRUE
, &userdata2
);
1165 ok(userdata
<= 5, "expected userdata <= 5, got %lu\n", userdata
);
1166 ok(userdata2
== 1, "expected only one cancellation callback, got %lu\n", userdata2
);
1167 ok(group_cancel_tid
== GetCurrentThreadId(), "expected tid %lx, got %lx\n",
1168 GetCurrentThreadId(), group_cancel_tid
);
1171 pTpReleaseCleanupGroup(group
);
1172 pTpReleasePool(pool
);
1173 CloseHandle(semaphores
[0]);
1174 CloseHandle(semaphores
[1]);
1177 static void CALLBACK
instance_semaphore_completion_cb(TP_CALLBACK_INSTANCE
*instance
, void *userdata
)
1179 HANDLE
*semaphores
= userdata
;
1180 pTpCallbackReleaseSemaphoreOnCompletion(instance
, semaphores
[0], 1);
1183 static void CALLBACK
instance_finalization_cb(TP_CALLBACK_INSTANCE
*instance
, void *userdata
)
1185 HANDLE
*semaphores
= userdata
;
1186 ReleaseSemaphore(semaphores
[1], 1, NULL
);
1189 static void test_tp_instance(void)
1191 TP_CALLBACK_ENVIRON environment
;
1192 HANDLE semaphores
[2];
1197 semaphores
[0] = CreateSemaphoreW(NULL
, 0, 1, NULL
);
1198 ok(semaphores
[0] != NULL
, "failed to create semaphore\n");
1199 semaphores
[1] = CreateSemaphoreW(NULL
, 0, 1, NULL
);
1200 ok(semaphores
[1] != NULL
, "failed to create semaphore\n");
1202 /* allocate new threadpool */
1204 status
= pTpAllocPool(&pool
, NULL
);
1205 ok(!status
, "TpAllocPool failed with status %lx\n", status
);
1206 ok(pool
!= NULL
, "expected pool != NULL\n");
1208 /* test for TpCallbackReleaseSemaphoreOnCompletion */
1209 memset(&environment
, 0, sizeof(environment
));
1210 environment
.Version
= 1;
1211 environment
.Pool
= pool
;
1212 status
= pTpSimpleTryPost(instance_semaphore_completion_cb
, semaphores
, &environment
);
1213 ok(!status
, "TpSimpleTryPost failed with status %lx\n", status
);
1214 result
= WaitForSingleObject(semaphores
[0], 1000);
1215 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1217 /* test for finalization callback */
1218 memset(&environment
, 0, sizeof(environment
));
1219 environment
.Version
= 1;
1220 environment
.Pool
= pool
;
1221 environment
.FinalizationCallback
= instance_finalization_cb
;
1222 status
= pTpSimpleTryPost(instance_semaphore_completion_cb
, semaphores
, &environment
);
1223 ok(!status
, "TpSimpleTryPost failed with status %lx\n", status
);
1224 result
= WaitForSingleObject(semaphores
[0], 1000);
1225 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1226 result
= WaitForSingleObject(semaphores
[1], 1000);
1227 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1230 pTpReleasePool(pool
);
1231 CloseHandle(semaphores
[0]);
1232 CloseHandle(semaphores
[1]);
1235 static void CALLBACK
disassociate_cb(TP_CALLBACK_INSTANCE
*instance
, void *userdata
, TP_WORK
*work
)
1237 HANDLE
*semaphores
= userdata
;
1240 pTpDisassociateCallback(instance
);
1241 result
= WaitForSingleObject(semaphores
[0], 1000);
1242 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1243 ReleaseSemaphore(semaphores
[1], 1, NULL
);
1246 static void CALLBACK
disassociate2_cb(TP_CALLBACK_INSTANCE
*instance
, void *userdata
, TP_WORK
*work
)
1248 HANDLE
*semaphores
= userdata
;
1251 pTpDisassociateCallback(instance
);
1252 result
= WaitForSingleObject(semaphores
[0], 100);
1253 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
1254 ReleaseSemaphore(semaphores
[1], 1, NULL
);
1257 static void CALLBACK
disassociate3_cb(TP_CALLBACK_INSTANCE
*instance
, void *userdata
)
1259 HANDLE
*semaphores
= userdata
;
1262 pTpDisassociateCallback(instance
);
1263 result
= WaitForSingleObject(semaphores
[0], 100);
1264 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
1265 ReleaseSemaphore(semaphores
[1], 1, NULL
);
1268 static void test_tp_disassociate(void)
1270 TP_CALLBACK_ENVIRON environment
;
1271 TP_CLEANUP_GROUP
*group
;
1272 HANDLE semaphores
[2];
1278 semaphores
[0] = CreateSemaphoreW(NULL
, 0, 1, NULL
);
1279 ok(semaphores
[0] != NULL
, "failed to create semaphore\n");
1280 semaphores
[1] = CreateSemaphoreW(NULL
, 0, 1, NULL
);
1281 ok(semaphores
[1] != NULL
, "failed to create semaphore\n");
1283 /* allocate new threadpool and cleanup group */
1285 status
= pTpAllocPool(&pool
, NULL
);
1286 ok(!status
, "TpAllocPool failed with status %lx\n", status
);
1287 ok(pool
!= NULL
, "expected pool != NULL\n");
1290 status
= pTpAllocCleanupGroup(&group
);
1291 ok(!status
, "TpAllocCleanupGroup failed with status %lx\n", status
);
1292 ok(group
!= NULL
, "expected pool != NULL\n");
1294 /* test TpDisassociateCallback on work objects without group */
1296 memset(&environment
, 0, sizeof(environment
));
1297 environment
.Version
= 1;
1298 environment
.Pool
= pool
;
1299 status
= pTpAllocWork(&work
, disassociate_cb
, semaphores
, &environment
);
1300 ok(!status
, "TpAllocWork failed with status %lx\n", status
);
1301 ok(work
!= NULL
, "expected work != NULL\n");
1304 pTpWaitForWork(work
, FALSE
);
1306 result
= WaitForSingleObject(semaphores
[1], 100);
1307 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
1308 ReleaseSemaphore(semaphores
[0], 1, NULL
);
1309 result
= WaitForSingleObject(semaphores
[1], 1000);
1310 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1311 pTpReleaseWork(work
);
1313 /* test TpDisassociateCallback on work objects with group (1) */
1315 memset(&environment
, 0, sizeof(environment
));
1316 environment
.Version
= 1;
1317 environment
.Pool
= pool
;
1318 environment
.CleanupGroup
= group
;
1319 status
= pTpAllocWork(&work
, disassociate_cb
, semaphores
, &environment
);
1320 ok(!status
, "TpAllocWork failed with status %lx\n", status
);
1321 ok(work
!= NULL
, "expected work != NULL\n");
1324 pTpWaitForWork(work
, FALSE
);
1326 result
= WaitForSingleObject(semaphores
[1], 100);
1327 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
1328 ReleaseSemaphore(semaphores
[0], 1, NULL
);
1329 result
= WaitForSingleObject(semaphores
[1], 1000);
1330 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1331 pTpReleaseCleanupGroupMembers(group
, FALSE
, NULL
);
1333 /* test TpDisassociateCallback on work objects with group (2) */
1335 memset(&environment
, 0, sizeof(environment
));
1336 environment
.Version
= 1;
1337 environment
.Pool
= pool
;
1338 environment
.CleanupGroup
= group
;
1339 status
= pTpAllocWork(&work
, disassociate2_cb
, semaphores
, &environment
);
1340 ok(!status
, "TpAllocWork failed with status %lx\n", status
);
1341 ok(work
!= NULL
, "expected work != NULL\n");
1344 pTpReleaseCleanupGroupMembers(group
, FALSE
, NULL
);
1346 ReleaseSemaphore(semaphores
[0], 1, NULL
);
1347 result
= WaitForSingleObject(semaphores
[1], 1000);
1348 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1349 result
= WaitForSingleObject(semaphores
[0], 1000);
1350 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1352 /* test TpDisassociateCallback on simple callbacks */
1353 memset(&environment
, 0, sizeof(environment
));
1354 environment
.Version
= 1;
1355 environment
.Pool
= pool
;
1356 environment
.CleanupGroup
= group
;
1357 status
= pTpSimpleTryPost(disassociate3_cb
, semaphores
, &environment
);
1358 ok(!status
, "TpSimpleTryPost failed with status %lx\n", status
);
1360 pTpReleaseCleanupGroupMembers(group
, FALSE
, NULL
);
1362 ReleaseSemaphore(semaphores
[0], 1, NULL
);
1363 result
= WaitForSingleObject(semaphores
[1], 1000);
1364 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1365 result
= WaitForSingleObject(semaphores
[0], 1000);
1366 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1369 pTpReleaseCleanupGroup(group
);
1370 pTpReleasePool(pool
);
1371 CloseHandle(semaphores
[0]);
1372 CloseHandle(semaphores
[1]);
1375 static void CALLBACK
timer_cb(TP_CALLBACK_INSTANCE
*instance
, void *userdata
, TP_TIMER
*timer
)
1377 HANDLE semaphore
= userdata
;
1378 ReleaseSemaphore(semaphore
, 1, NULL
);
1381 static void test_tp_timer(void)
1383 TP_CALLBACK_ENVIRON environment
;
1384 DWORD result
, ticks
;
1393 semaphore
= CreateSemaphoreA(NULL
, 0, 1, NULL
);
1394 ok(semaphore
!= NULL
, "CreateSemaphoreA failed %lu\n", GetLastError());
1396 /* allocate new threadpool */
1398 status
= pTpAllocPool(&pool
, NULL
);
1399 ok(!status
, "TpAllocPool failed with status %lx\n", status
);
1400 ok(pool
!= NULL
, "expected pool != NULL\n");
1402 /* allocate new timer */
1404 memset(&environment
, 0, sizeof(environment
));
1405 environment
.Version
= 1;
1406 environment
.Pool
= pool
;
1407 status
= pTpAllocTimer(&timer
, timer_cb
, semaphore
, &environment
);
1408 ok(!status
, "TpAllocTimer failed with status %lx\n", status
);
1409 ok(timer
!= NULL
, "expected timer != NULL\n");
1411 success
= pTpIsTimerSet(timer
);
1412 ok(!success
, "TpIsTimerSet returned TRUE\n");
1414 /* test timer with a relative timeout */
1415 when
.QuadPart
= (ULONGLONG
)200 * -10000;
1416 pTpSetTimer(timer
, &when
, 0, 0);
1417 success
= pTpIsTimerSet(timer
);
1418 ok(success
, "TpIsTimerSet returned FALSE\n");
1420 pTpWaitForTimer(timer
, FALSE
);
1422 result
= WaitForSingleObject(semaphore
, 100);
1423 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
1424 result
= WaitForSingleObject(semaphore
, 200);
1425 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1426 success
= pTpIsTimerSet(timer
);
1427 ok(success
, "TpIsTimerSet returned FALSE\n");
1429 /* test timer with an absolute timeout */
1430 NtQuerySystemTime( &when
);
1431 when
.QuadPart
+= (ULONGLONG
)200 * 10000;
1432 pTpSetTimer(timer
, &when
, 0, 0);
1433 success
= pTpIsTimerSet(timer
);
1434 ok(success
, "TpIsTimerSet returned FALSE\n");
1436 pTpWaitForTimer(timer
, FALSE
);
1438 result
= WaitForSingleObject(semaphore
, 100);
1439 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
1440 result
= WaitForSingleObject(semaphore
, 200);
1441 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1442 success
= pTpIsTimerSet(timer
);
1443 ok(success
, "TpIsTimerSet returned FALSE\n");
1445 /* test timer with zero timeout */
1447 pTpSetTimer(timer
, &when
, 0, 0);
1448 success
= pTpIsTimerSet(timer
);
1449 ok(success
, "TpIsTimerSet returned FALSE\n");
1451 pTpWaitForTimer(timer
, FALSE
);
1453 result
= WaitForSingleObject(semaphore
, 50);
1454 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1455 success
= pTpIsTimerSet(timer
);
1456 ok(success
, "TpIsTimerSet returned FALSE\n");
1458 /* unset the timer */
1459 pTpSetTimer(timer
, NULL
, 0, 0);
1460 success
= pTpIsTimerSet(timer
);
1461 ok(!success
, "TpIsTimerSet returned TRUE\n");
1462 pTpWaitForTimer(timer
, TRUE
);
1464 pTpReleaseTimer(timer
);
1465 CloseHandle(semaphore
);
1467 semaphore
= CreateSemaphoreA(NULL
, 0, 3, NULL
);
1468 ok(semaphore
!= NULL
, "CreateSemaphoreA failed %lu\n", GetLastError());
1470 /* allocate a new timer */
1472 memset(&environment
, 0, sizeof(environment
));
1473 environment
.Version
= 1;
1474 environment
.Pool
= pool
;
1475 status
= pTpAllocTimer(&timer
, timer_cb
, semaphore
, &environment
);
1476 ok(!status
, "TpAllocTimer failed with status %lx\n", status
);
1477 ok(timer
!= NULL
, "expected timer != NULL\n");
1479 /* test a relative timeout repeated periodically */
1480 when
.QuadPart
= (ULONGLONG
)200 * -10000;
1481 pTpSetTimer(timer
, &when
, 200, 0);
1482 success
= pTpIsTimerSet(timer
);
1483 ok(success
, "TpIsTimerSet returned FALSE\n");
1485 /* wait until the timer was triggered three times */
1486 ticks
= GetTickCount();
1487 for (i
= 0; i
< 3; i
++)
1489 result
= WaitForSingleObject(semaphore
, 1000);
1490 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1492 ticks
= GetTickCount() - ticks
;
1493 ok(ticks
>= 500 && (ticks
<= 700 || broken(ticks
<= 750)) /* Win 7 */,
1494 "expected approximately 600 ticks, got %lu\n", ticks
);
1496 /* unset the timer */
1497 pTpSetTimer(timer
, NULL
, 0, 0);
1498 success
= pTpIsTimerSet(timer
);
1499 ok(!success
, "TpIsTimerSet returned TRUE\n");
1500 pTpWaitForTimer(timer
, TRUE
);
1503 pTpReleaseTimer(timer
);
1504 pTpReleasePool(pool
);
1505 CloseHandle(semaphore
);
1508 struct window_length_info
1514 static void CALLBACK
window_length_cb(TP_CALLBACK_INSTANCE
*instance
, void *userdata
, TP_TIMER
*timer
)
1516 struct window_length_info
*info
= userdata
;
1517 info
->ticks
= GetTickCount();
1518 ReleaseSemaphore(info
->semaphore
, 1, NULL
);
1521 static void test_tp_window_length(void)
1523 struct window_length_info info1
, info2
;
1524 TP_CALLBACK_ENVIRON environment
;
1525 TP_TIMER
*timer1
, *timer2
;
1533 semaphore
= CreateSemaphoreA(NULL
, 0, 2, NULL
);
1534 ok(semaphore
!= NULL
, "CreateSemaphoreA failed %lu\n", GetLastError());
1536 /* allocate new threadpool */
1538 status
= pTpAllocPool(&pool
, NULL
);
1539 ok(!status
, "TpAllocPool failed with status %lx\n", status
);
1540 ok(pool
!= NULL
, "expected pool != NULL\n");
1542 /* allocate two identical timers */
1543 memset(&environment
, 0, sizeof(environment
));
1544 environment
.Version
= 1;
1545 environment
.Pool
= pool
;
1548 info1
.semaphore
= semaphore
;
1549 status
= pTpAllocTimer(&timer1
, window_length_cb
, &info1
, &environment
);
1550 ok(!status
, "TpAllocTimer failed with status %lx\n", status
);
1551 ok(timer1
!= NULL
, "expected timer1 != NULL\n");
1554 info2
.semaphore
= semaphore
;
1555 status
= pTpAllocTimer(&timer2
, window_length_cb
, &info2
, &environment
);
1556 ok(!status
, "TpAllocTimer failed with status %lx\n", status
);
1557 ok(timer2
!= NULL
, "expected timer2 != NULL\n");
1559 /* choose parameters so that timers are not merged */
1563 NtQuerySystemTime( &when
);
1564 when
.QuadPart
+= (ULONGLONG
)250 * 10000;
1565 pTpSetTimer(timer2
, &when
, 0, 0);
1567 when
.QuadPart
-= (ULONGLONG
)150 * 10000;
1568 pTpSetTimer(timer1
, &when
, 0, 75);
1570 result
= WaitForSingleObject(semaphore
, 1000);
1571 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1572 result
= WaitForSingleObject(semaphore
, 1000);
1573 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1574 ok(info1
.ticks
!= 0 && info2
.ticks
!= 0, "expected that ticks are nonzero\n");
1575 ok(info2
.ticks
>= info1
.ticks
+ 75 || broken(info2
.ticks
< info1
.ticks
+ 75) /* Win 2008 */,
1576 "expected that timers are not merged\n");
1578 /* timers will be merged */
1582 NtQuerySystemTime( &when
);
1583 when
.QuadPart
+= (ULONGLONG
)250 * 10000;
1584 pTpSetTimer(timer2
, &when
, 0, 0);
1586 when
.QuadPart
-= (ULONGLONG
)150 * 10000;
1587 pTpSetTimer(timer1
, &when
, 0, 200);
1589 result
= WaitForSingleObject(semaphore
, 1000);
1590 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1591 result
= WaitForSingleObject(semaphore
, 1000);
1592 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1593 ok(info1
.ticks
!= 0 && info2
.ticks
!= 0, "expected that ticks are nonzero\n");
1594 merged
= info2
.ticks
>= info1
.ticks
- 50 && info2
.ticks
<= info1
.ticks
+ 50;
1595 ok(merged
|| broken(!merged
) /* Win 10 */, "expected that timers are merged\n");
1597 /* on Windows the timers also get merged in this case */
1601 NtQuerySystemTime( &when
);
1602 when
.QuadPart
+= (ULONGLONG
)100 * 10000;
1603 pTpSetTimer(timer1
, &when
, 0, 200);
1605 when
.QuadPart
+= (ULONGLONG
)150 * 10000;
1606 pTpSetTimer(timer2
, &when
, 0, 0);
1608 result
= WaitForSingleObject(semaphore
, 1000);
1609 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1610 result
= WaitForSingleObject(semaphore
, 1000);
1611 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1612 ok(info1
.ticks
!= 0 && info2
.ticks
!= 0, "expected that ticks are nonzero\n");
1613 merged
= info2
.ticks
>= info1
.ticks
- 50 && info2
.ticks
<= info1
.ticks
+ 50;
1615 ok(merged
|| broken(!merged
) /* Win 10 */, "expected that timers are merged\n");
1618 pTpReleaseTimer(timer1
);
1619 pTpReleaseTimer(timer2
);
1620 pTpReleasePool(pool
);
1621 CloseHandle(semaphore
);
1630 static void CALLBACK
wait_cb(TP_CALLBACK_INSTANCE
*instance
, void *userdata
,
1631 TP_WAIT
*wait
, TP_WAIT_RESULT result
)
1633 struct wait_info
*info
= userdata
;
1634 if (result
== WAIT_OBJECT_0
)
1635 InterlockedIncrement(&info
->userdata
);
1636 else if (result
== WAIT_TIMEOUT
)
1637 InterlockedExchangeAdd(&info
->userdata
, 0x10000);
1639 ok(0, "unexpected result %lu\n", result
);
1640 ReleaseSemaphore(info
->semaphore
, 1, NULL
);
1643 static void test_tp_wait(void)
1645 TP_CALLBACK_ENVIRON environment
;
1646 TP_WAIT
*wait1
, *wait2
;
1647 struct wait_info info
;
1648 HANDLE semaphores
[2];
1654 semaphores
[0] = CreateSemaphoreW(NULL
, 0, 2, NULL
);
1655 ok(semaphores
[0] != NULL
, "failed to create semaphore\n");
1656 semaphores
[1] = CreateSemaphoreW(NULL
, 0, 1, NULL
);
1657 ok(semaphores
[1] != NULL
, "failed to create semaphore\n");
1658 info
.semaphore
= semaphores
[0];
1660 /* allocate new threadpool */
1662 status
= pTpAllocPool(&pool
, NULL
);
1663 ok(!status
, "TpAllocPool failed with status %lx\n", status
);
1664 ok(pool
!= NULL
, "expected pool != NULL\n");
1666 /* allocate new wait items */
1667 memset(&environment
, 0, sizeof(environment
));
1668 environment
.Version
= 1;
1669 environment
.Pool
= pool
;
1672 status
= pTpAllocWait(&wait1
, wait_cb
, &info
, &environment
);
1673 ok(!status
, "TpAllocWait failed with status %lx\n", status
);
1674 ok(wait1
!= NULL
, "expected wait1 != NULL\n");
1677 status
= pTpAllocWait(&wait2
, wait_cb
, &info
, &environment
);
1678 ok(!status
, "TpAllocWait failed with status %lx\n", status
);
1679 ok(wait2
!= NULL
, "expected wait2 != NULL\n");
1681 /* infinite timeout, signal the semaphore immediately */
1683 pTpSetWait(wait1
, semaphores
[1], NULL
);
1684 ReleaseSemaphore(semaphores
[1], 1, NULL
);
1685 result
= WaitForSingleObject(semaphores
[0], 100);
1686 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1687 ok(info
.userdata
== 1, "expected info.userdata = 1, got %lu\n", info
.userdata
);
1688 result
= WaitForSingleObject(semaphores
[1], 0);
1689 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
1691 /* relative timeout, no event */
1693 when
.QuadPart
= (ULONGLONG
)200 * -10000;
1694 pTpSetWait(wait1
, semaphores
[1], &when
);
1695 result
= WaitForSingleObject(semaphores
[0], 100);
1696 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
1697 ok(info
.userdata
== 0, "expected info.userdata = 0, got %lu\n", info
.userdata
);
1698 result
= WaitForSingleObject(semaphores
[0], 200);
1699 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1700 ok(info
.userdata
== 0x10000, "expected info.userdata = 0x10000, got %lu\n", info
.userdata
);
1701 result
= WaitForSingleObject(semaphores
[1], 0);
1702 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
1704 /* repeat test with call to TpWaitForWait(..., TRUE) */
1706 when
.QuadPart
= (ULONGLONG
)200 * -10000;
1707 pTpSetWait(wait1
, semaphores
[1], &when
);
1708 result
= WaitForSingleObject(semaphores
[0], 100);
1709 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
1710 pTpWaitForWait(wait1
, TRUE
);
1711 ok(info
.userdata
== 0, "expected info.userdata = 0, got %lu\n", info
.userdata
);
1712 result
= WaitForSingleObject(semaphores
[0], 200);
1713 ok(result
== WAIT_OBJECT_0
|| broken(result
== WAIT_TIMEOUT
) /* Win 8 */,
1714 "WaitForSingleObject returned %lu\n", result
);
1715 if (result
== WAIT_OBJECT_0
)
1716 ok(info
.userdata
== 0x10000, "expected info.userdata = 0x10000, got %lu\n", info
.userdata
);
1718 ok(info
.userdata
== 0, "expected info.userdata = 0, got %lu\n", info
.userdata
);
1719 result
= WaitForSingleObject(semaphores
[1], 0);
1720 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
1722 /* relative timeout, with event */
1724 when
.QuadPart
= (ULONGLONG
)200 * -10000;
1725 pTpSetWait(wait1
, semaphores
[1], &when
);
1726 result
= WaitForSingleObject(semaphores
[0], 100);
1727 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
1728 ok(info
.userdata
== 0, "expected info.userdata = 0, got %lu\n", info
.userdata
);
1729 ReleaseSemaphore(semaphores
[1], 1, NULL
);
1730 result
= WaitForSingleObject(semaphores
[0], 100);
1731 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1732 ok(info
.userdata
== 1, "expected info.userdata = 1, got %lu\n", info
.userdata
);
1733 result
= WaitForSingleObject(semaphores
[1], 0);
1734 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
1736 /* repeat test with call to TpWaitForWait(..., TRUE) */
1738 when
.QuadPart
= (ULONGLONG
)200 * -10000;
1739 pTpSetWait(wait1
, semaphores
[1], &when
);
1740 result
= WaitForSingleObject(semaphores
[0], 100);
1741 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
1742 pTpWaitForWait(wait1
, TRUE
);
1743 ok(info
.userdata
== 0, "expected info.userdata = 0, got %lu\n", info
.userdata
);
1744 ReleaseSemaphore(semaphores
[1], 1, NULL
);
1745 result
= WaitForSingleObject(semaphores
[0], 100);
1746 ok(result
== WAIT_OBJECT_0
|| broken(result
== WAIT_TIMEOUT
) /* Win 8 */,
1747 "WaitForSingleObject returned %lu\n", result
);
1748 if (result
== WAIT_OBJECT_0
)
1750 ok(info
.userdata
== 1, "expected info.userdata = 1, got %lu\n", info
.userdata
);
1751 result
= WaitForSingleObject(semaphores
[1], 0);
1752 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
1756 ok(info
.userdata
== 0, "expected info.userdata = 0, got %lu\n", info
.userdata
);
1757 result
= WaitForSingleObject(semaphores
[1], 0);
1758 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1761 /* absolute timeout, no event */
1763 NtQuerySystemTime( &when
);
1764 when
.QuadPart
+= (ULONGLONG
)200 * 10000;
1765 pTpSetWait(wait1
, semaphores
[1], &when
);
1766 result
= WaitForSingleObject(semaphores
[0], 100);
1767 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
1768 ok(info
.userdata
== 0, "expected info.userdata = 0, got %lu\n", info
.userdata
);
1769 result
= WaitForSingleObject(semaphores
[0], 200);
1770 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1771 ok(info
.userdata
== 0x10000, "expected info.userdata = 0x10000, got %lu\n", info
.userdata
);
1772 result
= WaitForSingleObject(semaphores
[1], 0);
1773 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
1775 /* absolute timeout, with event */
1777 NtQuerySystemTime( &when
);
1778 when
.QuadPart
+= (ULONGLONG
)200 * 10000;
1779 pTpSetWait(wait1
, semaphores
[1], &when
);
1780 result
= WaitForSingleObject(semaphores
[0], 100);
1781 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
1782 ok(info
.userdata
== 0, "expected info.userdata = 0, got %lu\n", info
.userdata
);
1783 ReleaseSemaphore(semaphores
[1], 1, NULL
);
1784 result
= WaitForSingleObject(semaphores
[0], 100);
1785 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1786 ok(info
.userdata
== 1, "expected info.userdata = 1, got %lu\n", info
.userdata
);
1787 result
= WaitForSingleObject(semaphores
[1], 0);
1788 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
1790 /* test timeout of zero */
1793 pTpSetWait(wait1
, semaphores
[1], &when
);
1794 result
= WaitForSingleObject(semaphores
[0], 100);
1795 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1796 ok(info
.userdata
== 0x10000, "expected info.userdata = 0x10000, got %lu\n", info
.userdata
);
1797 result
= WaitForSingleObject(semaphores
[1], 0);
1798 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
1800 /* cancel a pending wait */
1802 when
.QuadPart
= (ULONGLONG
)250 * -10000;
1803 pTpSetWait(wait1
, semaphores
[1], &when
);
1804 result
= WaitForSingleObject(semaphores
[0], 100);
1805 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
1806 pTpSetWait(wait1
, NULL
, (void *)0xdeadbeef);
1808 ReleaseSemaphore(semaphores
[1], 1, NULL
);
1809 result
= WaitForSingleObject(semaphores
[0], 100);
1810 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
1811 ok(info
.userdata
== 0, "expected info.userdata = 0, got %lu\n", info
.userdata
);
1812 result
= WaitForSingleObject(semaphores
[1], 0);
1813 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1815 /* test with INVALID_HANDLE_VALUE */
1818 pTpSetWait(wait1
, INVALID_HANDLE_VALUE
, &when
);
1819 result
= WaitForSingleObject(semaphores
[0], 100);
1820 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1821 ok(info
.userdata
== 0x10000, "expected info.userdata = 0x10000, got %lu\n", info
.userdata
);
1823 /* cancel a pending wait with INVALID_HANDLE_VALUE */
1825 when
.QuadPart
= (ULONGLONG
)250 * -10000;
1826 pTpSetWait(wait1
, semaphores
[1], &when
);
1827 result
= WaitForSingleObject(semaphores
[0], 100);
1828 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
1830 pTpSetWait(wait1
, INVALID_HANDLE_VALUE
, &when
);
1832 ReleaseSemaphore(semaphores
[1], 1, NULL
);
1833 result
= WaitForSingleObject(semaphores
[0], 100);
1834 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1835 ok(info
.userdata
== 0x10000, "expected info.userdata = 0x10000, got %lu\n", info
.userdata
);
1836 result
= WaitForSingleObject(semaphores
[1], 0);
1837 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1839 CloseHandle(semaphores
[1]);
1840 semaphores
[1] = CreateSemaphoreW(NULL
, 0, 2, NULL
);
1841 ok(semaphores
[1] != NULL
, "failed to create semaphore\n");
1843 /* add two wait objects with the same semaphore */
1845 pTpSetWait(wait1
, semaphores
[1], NULL
);
1846 pTpSetWait(wait2
, semaphores
[1], NULL
);
1848 ReleaseSemaphore(semaphores
[1], 1, NULL
);
1849 result
= WaitForSingleObject(semaphores
[0], 100);
1850 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1851 result
= WaitForSingleObject(semaphores
[0], 100);
1852 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
1853 ok(info
.userdata
== 1, "expected info.userdata = 1, got %lu\n", info
.userdata
);
1854 result
= WaitForSingleObject(semaphores
[1], 0);
1855 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
1857 /* repeat test above with release count 2 */
1859 pTpSetWait(wait1
, semaphores
[1], NULL
);
1860 pTpSetWait(wait2
, semaphores
[1], NULL
);
1862 result
= ReleaseSemaphore(semaphores
[1], 2, NULL
);
1863 result
= WaitForSingleObject(semaphores
[0], 100);
1864 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1865 result
= WaitForSingleObject(semaphores
[0], 100);
1866 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1867 ok(info
.userdata
== 2, "expected info.userdata = 2, got %lu\n", info
.userdata
);
1868 result
= WaitForSingleObject(semaphores
[1], 0);
1869 ok(result
== WAIT_TIMEOUT
, "WaitForSingleObject returned %lu\n", result
);
1872 pTpReleaseWait(wait1
);
1873 pTpReleaseWait(wait2
);
1874 pTpReleasePool(pool
);
1875 CloseHandle(semaphores
[0]);
1876 CloseHandle(semaphores
[1]);
1885 static void CALLBACK
multi_wait_cb(TP_CALLBACK_INSTANCE
*instance
, void *userdata
, TP_WAIT
*wait
, TP_WAIT_RESULT result
)
1887 DWORD index
= (DWORD
)(DWORD_PTR
)userdata
;
1889 if (result
== WAIT_OBJECT_0
)
1890 multi_wait_info
.result
= index
;
1891 else if (result
== WAIT_TIMEOUT
)
1892 multi_wait_info
.result
= 0x10000 | index
;
1894 ok(0, "unexpected result %lu\n", result
);
1895 ReleaseSemaphore(multi_wait_info
.semaphore
, 1, NULL
);
1898 static void test_tp_multi_wait(void)
1900 TP_POOL_STACK_INFORMATION stack_info
;
1901 TP_CALLBACK_ENVIRON environment
;
1902 HANDLE semaphores
[512];
1903 TP_WAIT
*waits
[512];
1911 semaphore
= CreateSemaphoreW(NULL
, 0, 512, NULL
);
1912 ok(semaphore
!= NULL
, "failed to create semaphore\n");
1913 multi_wait_info
.semaphore
= semaphore
;
1915 /* allocate new threadpool */
1917 status
= pTpAllocPool(&pool
, NULL
);
1918 ok(!status
, "TpAllocPool failed with status %lx\n", status
);
1919 ok(pool
!= NULL
, "expected pool != NULL\n");
1920 /* many threads -> use the smallest stack possible */
1921 stack_info
.StackReserve
= 256 * 1024;
1922 stack_info
.StackCommit
= 4 * 1024;
1923 status
= pTpSetPoolStackInformation(pool
, &stack_info
);
1924 ok(!status
, "TpQueryPoolStackInformation failed: %lx\n", status
);
1926 memset(&environment
, 0, sizeof(environment
));
1927 environment
.Version
= 1;
1928 environment
.Pool
= pool
;
1930 /* create semaphores and corresponding wait objects */
1931 for (i
= 0; i
< ARRAY_SIZE(semaphores
); i
++)
1933 semaphores
[i
] = CreateSemaphoreW(NULL
, 0, 1, NULL
);
1934 ok(semaphores
[i
] != NULL
, "failed to create semaphore %i\n", i
);
1937 status
= pTpAllocWait(&waits
[i
], multi_wait_cb
, (void *)(DWORD_PTR
)i
, &environment
);
1938 ok(!status
, "TpAllocWait failed with status %lx\n", status
);
1939 ok(waits
[i
] != NULL
, "expected waits[%d] != NULL\n", i
);
1941 pTpSetWait(waits
[i
], semaphores
[i
], NULL
);
1944 /* release all semaphores and wait for callback */
1945 for (i
= 0; i
< ARRAY_SIZE(semaphores
); i
++)
1947 multi_wait_info
.result
= 0;
1948 ReleaseSemaphore(semaphores
[i
], 1, NULL
);
1950 result
= WaitForSingleObject(semaphore
, 2000);
1951 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1952 ok(multi_wait_info
.result
== i
, "expected result %d, got %lu\n", i
, multi_wait_info
.result
);
1954 pTpSetWait(waits
[i
], semaphores
[i
], NULL
);
1957 /* repeat the same test in reverse order */
1958 for (i
= ARRAY_SIZE(semaphores
) - 1; i
>= 0; i
--)
1960 multi_wait_info
.result
= 0;
1961 ReleaseSemaphore(semaphores
[i
], 1, NULL
);
1963 result
= WaitForSingleObject(semaphore
, 2000);
1964 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1965 ok(multi_wait_info
.result
== i
, "expected result %d, got %lu\n", i
, multi_wait_info
.result
);
1967 pTpSetWait(waits
[i
], semaphores
[i
], NULL
);
1970 /* test timeout of wait objects */
1971 multi_wait_info
.result
= 0;
1972 for (i
= 0; i
< ARRAY_SIZE(semaphores
); i
++)
1974 when
.QuadPart
= (ULONGLONG
)50 * -10000;
1975 pTpSetWait(waits
[i
], semaphores
[i
], &when
);
1978 for (i
= 0; i
< ARRAY_SIZE(semaphores
); i
++)
1980 result
= WaitForSingleObject(semaphore
, 2000);
1981 ok(result
== WAIT_OBJECT_0
, "WaitForSingleObject returned %lu\n", result
);
1984 ok(multi_wait_info
.result
>> 16, "expected multi_wait_info.result >> 16 != 0\n");
1986 /* destroy the wait objects and semaphores while waiting */
1987 for (i
= 0; i
< ARRAY_SIZE(semaphores
); i
++)
1989 pTpSetWait(waits
[i
], semaphores
[i
], NULL
);
1994 for (i
= 0; i
< ARRAY_SIZE(semaphores
); i
++)
1996 pTpReleaseWait(waits
[i
]);
1997 NtClose(semaphores
[i
]);
2000 pTpReleasePool(pool
);
2001 CloseHandle(semaphore
);
2013 static void CALLBACK
io_cb(TP_CALLBACK_INSTANCE
*instance
, void *userdata
,
2014 void *cvalue
, IO_STATUS_BLOCK
*iosb
, TP_IO
*io
)
2016 struct io_cb_ctx
*ctx
= userdata
;
2019 ctx
->ret
= iosb
->Status
;
2020 ctx
->length
= iosb
->Information
;
2024 static DWORD WINAPI
io_wait_thread(void *arg
)
2027 pTpWaitForIoCompletion(io
, FALSE
);
2031 static void test_tp_io(void)
2033 TP_CALLBACK_ENVIRON environment
= {.Version
= 1};
2034 OVERLAPPED ovl
= {}, ovl2
= {};
2035 HANDLE client
, server
, thread
;
2036 struct io_cb_ctx userdata
;
2045 ovl
.hEvent
= CreateEventW(NULL
, TRUE
, FALSE
, NULL
);
2047 status
= pTpAllocPool(&pool
, NULL
);
2048 ok(!status
, "failed to allocate pool, status %#lx\n", status
);
2050 server
= CreateNamedPipeA("\\\\.\\pipe\\wine_tp_test",
2051 PIPE_ACCESS_DUPLEX
| FILE_FLAG_OVERLAPPED
, 0, 1, 1024, 1024, 0, NULL
);
2052 ok(server
!= INVALID_HANDLE_VALUE
, "Failed to create server pipe, error %lu.\n", GetLastError());
2053 client
= CreateFileA("\\\\.\\pipe\\wine_tp_test", GENERIC_READ
| GENERIC_WRITE
,
2054 0, NULL
, OPEN_EXISTING
, 0, 0);
2055 ok(client
!= INVALID_HANDLE_VALUE
, "Failed to create client pipe, error %lu.\n", GetLastError());
2057 environment
.Pool
= pool
;
2059 status
= pTpAllocIoCompletion(&io
, server
, io_cb
, &userdata
, &environment
);
2060 ok(!status
, "got %#lx\n", status
);
2061 ok(!!io
, "expected non-NULL TP_IO\n");
2063 pTpWaitForIoCompletion(io
, FALSE
);
2066 pTpStartAsyncIoOperation(io
);
2068 thread
= CreateThread(NULL
, 0, io_wait_thread
, io
, 0, NULL
);
2069 ok(WaitForSingleObject(thread
, 100) == WAIT_TIMEOUT
, "TpWaitForIoCompletion() should not return\n");
2071 ret
= ReadFile(server
, in
, sizeof(in
), NULL
, &ovl
);
2072 ok(!ret
, "wrong ret %d\n", ret
);
2073 ok(GetLastError() == ERROR_IO_PENDING
, "wrong error %lu\n", GetLastError());
2075 ret
= WriteFile(client
, out
, sizeof(out
), &ret_size
, NULL
);
2076 ok(ret
, "WriteFile() failed, error %lu\n", GetLastError());
2078 pTpWaitForIoCompletion(io
, FALSE
);
2079 ok(userdata
.count
== 1, "callback ran %u times\n", userdata
.count
);
2080 ok(userdata
.ovl
== &ovl
, "expected %p, got %p\n", &ovl
, userdata
.ovl
);
2081 ok(userdata
.ret
== STATUS_SUCCESS
, "got status %#lx\n", userdata
.ret
);
2082 ok(userdata
.length
== 1, "got length %Iu\n", userdata
.length
);
2083 ok(userdata
.io
== io
, "expected %p, got %p\n", io
, userdata
.io
);
2085 ok(!WaitForSingleObject(thread
, 1000), "wait timed out\n");
2086 CloseHandle(thread
);
2089 pTpStartAsyncIoOperation(io
);
2090 pTpStartAsyncIoOperation(io
);
2092 ret
= ReadFile(server
, in
, sizeof(in
), NULL
, &ovl
);
2093 ok(!ret
, "wrong ret %d\n", ret
);
2094 ok(GetLastError() == ERROR_IO_PENDING
, "wrong error %lu\n", GetLastError());
2095 ret
= ReadFile(server
, in2
, sizeof(in2
), NULL
, &ovl2
);
2096 ok(!ret
, "wrong ret %d\n", ret
);
2097 ok(GetLastError() == ERROR_IO_PENDING
, "wrong error %lu\n", GetLastError());
2099 ret
= WriteFile(client
, out
, sizeof(out
), &ret_size
, NULL
);
2100 ok(ret
, "WriteFile() failed, error %lu\n", GetLastError());
2101 ret
= WriteFile(client
, out
, sizeof(out
), &ret_size
, NULL
);
2102 ok(ret
, "WriteFile() failed, error %lu\n", GetLastError());
2104 pTpWaitForIoCompletion(io
, FALSE
);
2105 ok(userdata
.count
== 2, "callback ran %u times\n", userdata
.count
);
2106 ok(userdata
.ret
== STATUS_SUCCESS
, "got status %#lx\n", userdata
.ret
);
2107 ok(userdata
.length
== 1, "got length %Iu\n", userdata
.length
);
2108 ok(userdata
.io
== io
, "expected %p, got %p\n", io
, userdata
.io
);
2110 /* The documentation is a bit unclear about passing TRUE to
2111 * WaitForThreadpoolIoCallbacks()—"pending I/O requests are not canceled"
2112 * [as with CancelIoEx()], but pending threadpool callbacks are, even those
2113 * which have not yet reached the completion port [as with
2114 * TpCancelAsyncIoOperation()]. */
2116 pTpStartAsyncIoOperation(io
);
2118 pTpWaitForIoCompletion(io
, TRUE
);
2119 ok(!userdata
.count
, "callback ran %u times\n", userdata
.count
);
2121 pTpStartAsyncIoOperation(io
);
2123 ret
= WriteFile(client
, out
, sizeof(out
), &ret_size
, NULL
);
2124 ok(ret
, "WriteFile() failed, error %lu\n", GetLastError());
2126 ret
= ReadFile(server
, in
, sizeof(in
), NULL
, &ovl
);
2127 ok(ret
, "wrong ret %d\n", ret
);
2129 pTpWaitForIoCompletion(io
, FALSE
);
2130 ok(userdata
.count
== 1, "callback ran %u times\n", userdata
.count
);
2131 ok(userdata
.ovl
== &ovl
, "expected %p, got %p\n", &ovl
, userdata
.ovl
);
2132 ok(userdata
.ret
== STATUS_SUCCESS
, "got status %#lx\n", userdata
.ret
);
2133 ok(userdata
.length
== 1, "got length %Iu\n", userdata
.length
);
2134 ok(userdata
.io
== io
, "expected %p, got %p\n", io
, userdata
.io
);
2137 pTpStartAsyncIoOperation(io
);
2139 ret
= ReadFile(server
, NULL
, 1, NULL
, &ovl
);
2140 ok(!ret
, "wrong ret %d\n", ret
);
2141 ok(GetLastError() == ERROR_NOACCESS
, "wrong error %lu\n", GetLastError());
2143 pTpCancelAsyncIoOperation(io
);
2144 pTpWaitForIoCompletion(io
, FALSE
);
2145 ok(!userdata
.count
, "callback ran %u times\n", userdata
.count
);
2148 pTpStartAsyncIoOperation(io
);
2150 ret
= ReadFile(server
, in
, sizeof(in
), NULL
, &ovl
);
2151 ok(!ret
, "wrong ret %d\n", ret
);
2152 ok(GetLastError() == ERROR_IO_PENDING
, "wrong error %lu\n", GetLastError());
2153 ret
= CancelIo(server
);
2154 ok(ret
, "CancelIo() failed, error %lu\n", GetLastError());
2156 pTpWaitForIoCompletion(io
, FALSE
);
2157 ok(userdata
.count
== 1, "callback ran %u times\n", userdata
.count
);
2158 ok(userdata
.ovl
== &ovl
, "expected %p, got %p\n", &ovl
, userdata
.ovl
);
2159 ok(userdata
.ret
== STATUS_CANCELLED
, "got status %#lx\n", userdata
.ret
);
2160 ok(!userdata
.length
, "got length %Iu\n", userdata
.length
);
2161 ok(userdata
.io
== io
, "expected %p, got %p\n", io
, userdata
.io
);
2164 pTpStartAsyncIoOperation(io
);
2165 pTpCancelAsyncIoOperation(io
);
2166 ret
= ReadFile(server
, in
, sizeof(in
), NULL
, &ovl
);
2167 ok(!ret
, "wrong ret %d\n", ret
);
2168 ret
= WriteFile(client
, out
, sizeof(out
), &ret_size
, NULL
);
2169 ok(ret
, "WriteFile() failed, error %lu\n", GetLastError());
2170 ok(GetLastError() == ERROR_IO_PENDING
, "wrong error %lu\n", GetLastError());
2172 pTpWaitForIoCompletion(io
, FALSE
);
2175 /* Add a sleep to check that callback is not called later. Commented out to
2176 * save the test time. */
2179 ok(userdata
.count
== 0, "callback ran %u times\n", userdata
.count
);
2181 pTpReleaseIoCompletion(io
);
2182 CloseHandle(server
);
2184 /* Test TPIO object destruction. */
2185 server
= CreateNamedPipeA("\\\\.\\pipe\\wine_tp_test",
2186 PIPE_ACCESS_DUPLEX
| FILE_FLAG_OVERLAPPED
, 0, 1, 1024, 1024, 0, NULL
);
2187 ok(server
!= INVALID_HANDLE_VALUE
, "Failed to create server pipe, error %lu.\n", GetLastError());
2189 status
= pTpAllocIoCompletion(&io
, server
, io_cb
, &userdata
, &environment
);
2190 ok(!status
, "got %#lx\n", status
);
2192 ret
= HeapValidate(GetProcessHeap(), 0, io
);
2193 ok(ret
, "Got unexpected ret %#x.\n", ret
);
2194 pTpReleaseIoCompletion(io
);
2195 ret
= HeapValidate(GetProcessHeap(), 0, io
);
2196 ok(!ret
, "Got unexpected ret %#x.\n", ret
);
2197 CloseHandle(server
);
2198 CloseHandle(client
);
2200 server
= CreateNamedPipeA("\\\\.\\pipe\\wine_tp_test",
2201 PIPE_ACCESS_DUPLEX
| FILE_FLAG_OVERLAPPED
, 0, 1, 1024, 1024, 0, NULL
);
2202 ok(server
!= INVALID_HANDLE_VALUE
, "Failed to create server pipe, error %lu.\n", GetLastError());
2203 client
= CreateFileA("\\\\.\\pipe\\wine_tp_test", GENERIC_READ
| GENERIC_WRITE
,
2204 0, NULL
, OPEN_EXISTING
, 0, 0);
2205 ok(client
!= INVALID_HANDLE_VALUE
, "Failed to create client pipe, error %lu.\n", GetLastError());
2208 status
= pTpAllocIoCompletion(&io
, server
, io_cb
, &userdata
, &environment
);
2209 ok(!status
, "got %#lx\n", status
);
2210 pTpStartAsyncIoOperation(io
);
2211 pTpWaitForIoCompletion(io
, TRUE
);
2212 ret
= HeapValidate(GetProcessHeap(), 0, io
);
2213 ok(ret
, "Got unexpected ret %#x.\n", ret
);
2214 pTpReleaseIoCompletion(io
);
2215 ret
= HeapValidate(GetProcessHeap(), 0, io
);
2216 ok(ret
, "Got unexpected ret %#x.\n", ret
);
2220 /* Object destruction will wait until one completion arrives (which was started but not cancelled).
2221 * Commented out to save test time. */
2223 ret
= HeapValidate(GetProcessHeap(), 0, io
);
2224 ok(ret
, "Got unexpected ret %#x.\n", ret
);
2225 ret
= ReadFile(server
, in
, sizeof(in
), NULL
, &ovl
);
2226 ok(!ret
, "wrong ret %d\n", ret
);
2227 ret
= WriteFile(client
, out
, sizeof(out
), &ret_size
, NULL
);
2228 ok(ret
, "WriteFile() failed, error %lu\n", GetLastError());
2230 ret
= HeapValidate(GetProcessHeap(), 0, io
);
2231 ok(!ret
, "Got unexpected ret %#x.\n", ret
);
2234 CloseHandle(server
);
2235 CloseHandle(ovl
.hEvent
);
2236 CloseHandle(client
);
2237 pTpReleasePool(pool
);
2240 static void CALLBACK
kernel32_io_cb(TP_CALLBACK_INSTANCE
*instance
, void *userdata
,
2241 void *ovl
, ULONG ret
, ULONG_PTR length
, TP_IO
*io
)
2243 struct io_cb_ctx
*ctx
= userdata
;
2247 ctx
->length
= length
;
2251 static void test_kernel32_tp_io(void)
2253 TP_CALLBACK_ENVIRON environment
= {.Version
= 1};
2254 OVERLAPPED ovl
= {}, ovl2
= {};
2255 HANDLE client
, server
, thread
;
2256 struct io_cb_ctx userdata
;
2265 ovl
.hEvent
= CreateEventW(NULL
, TRUE
, FALSE
, NULL
);
2267 status
= pTpAllocPool(&pool
, NULL
);
2268 ok(!status
, "failed to allocate pool, status %#lx\n", status
);
2270 server
= CreateNamedPipeA("\\\\.\\pipe\\wine_tp_test",
2271 PIPE_ACCESS_DUPLEX
| FILE_FLAG_OVERLAPPED
, 0, 1, 1024, 1024, 0, NULL
);
2272 ok(server
!= INVALID_HANDLE_VALUE
, "Failed to create server pipe, error %lu.\n", GetLastError());
2273 client
= CreateFileA("\\\\.\\pipe\\wine_tp_test", GENERIC_READ
| GENERIC_WRITE
,
2274 0, NULL
, OPEN_EXISTING
, 0, 0);
2275 ok(client
!= INVALID_HANDLE_VALUE
, "Failed to create client pipe, error %lu.\n", GetLastError());
2277 environment
.Pool
= pool
;
2279 io
= pCreateThreadpoolIo(server
, kernel32_io_cb
, &userdata
, &environment
);
2280 ok(!!io
, "expected non-NULL TP_IO\n");
2282 pWaitForThreadpoolIoCallbacks(io
, FALSE
);
2285 pStartThreadpoolIo(io
);
2287 thread
= CreateThread(NULL
, 0, io_wait_thread
, io
, 0, NULL
);
2288 ok(WaitForSingleObject(thread
, 100) == WAIT_TIMEOUT
, "TpWaitForIoCompletion() should not return\n");
2290 ret
= ReadFile(server
, in
, sizeof(in
), NULL
, &ovl
);
2291 ok(!ret
, "wrong ret %d\n", ret
);
2292 ok(GetLastError() == ERROR_IO_PENDING
, "wrong error %lu\n", GetLastError());
2294 ret
= WriteFile(client
, out
, sizeof(out
), &ret_size
, NULL
);
2295 ok(ret
, "WriteFile() failed, error %lu\n", GetLastError());
2297 pWaitForThreadpoolIoCallbacks(io
, FALSE
);
2298 ok(userdata
.count
== 1, "callback ran %u times\n", userdata
.count
);
2299 ok(userdata
.ovl
== &ovl
, "expected %p, got %p\n", &ovl
, userdata
.ovl
);
2300 ok(userdata
.ret
== ERROR_SUCCESS
, "got status %#lx\n", userdata
.ret
);
2301 ok(userdata
.length
== 1, "got length %Iu\n", userdata
.length
);
2302 ok(userdata
.io
== io
, "expected %p, got %p\n", io
, userdata
.io
);
2304 ok(!WaitForSingleObject(thread
, 1000), "wait timed out\n");
2305 CloseHandle(thread
);
2308 pStartThreadpoolIo(io
);
2309 pStartThreadpoolIo(io
);
2311 ret
= ReadFile(server
, in
, sizeof(in
), NULL
, &ovl
);
2312 ok(!ret
, "wrong ret %d\n", ret
);
2313 ok(GetLastError() == ERROR_IO_PENDING
, "wrong error %lu\n", GetLastError());
2314 ret
= ReadFile(server
, in2
, sizeof(in2
), NULL
, &ovl2
);
2315 ok(!ret
, "wrong ret %d\n", ret
);
2316 ok(GetLastError() == ERROR_IO_PENDING
, "wrong error %lu\n", GetLastError());
2318 ret
= WriteFile(client
, out
, sizeof(out
), &ret_size
, NULL
);
2319 ok(ret
, "WriteFile() failed, error %lu\n", GetLastError());
2320 ret
= WriteFile(client
, out
, sizeof(out
), &ret_size
, NULL
);
2321 ok(ret
, "WriteFile() failed, error %lu\n", GetLastError());
2323 pWaitForThreadpoolIoCallbacks(io
, FALSE
);
2324 ok(userdata
.count
== 2, "callback ran %u times\n", userdata
.count
);
2325 ok(userdata
.ret
== STATUS_SUCCESS
, "got status %#lx\n", userdata
.ret
);
2326 ok(userdata
.length
== 1, "got length %Iu\n", userdata
.length
);
2327 ok(userdata
.io
== io
, "expected %p, got %p\n", io
, userdata
.io
);
2330 pStartThreadpoolIo(io
);
2331 pWaitForThreadpoolIoCallbacks(io
, TRUE
);
2332 ok(!userdata
.count
, "callback ran %u times\n", userdata
.count
);
2334 pStartThreadpoolIo(io
);
2336 ret
= WriteFile(client
, out
, sizeof(out
), &ret_size
, NULL
);
2337 ok(ret
, "WriteFile() failed, error %lu\n", GetLastError());
2339 ret
= ReadFile(server
, in
, sizeof(in
), NULL
, &ovl
);
2340 ok(ret
, "wrong ret %d\n", ret
);
2342 pWaitForThreadpoolIoCallbacks(io
, FALSE
);
2343 ok(userdata
.count
== 1, "callback ran %u times\n", userdata
.count
);
2344 ok(userdata
.ovl
== &ovl
, "expected %p, got %p\n", &ovl
, userdata
.ovl
);
2345 ok(userdata
.ret
== ERROR_SUCCESS
, "got status %#lx\n", userdata
.ret
);
2346 ok(userdata
.length
== 1, "got length %Iu\n", userdata
.length
);
2347 ok(userdata
.io
== io
, "expected %p, got %p\n", io
, userdata
.io
);
2350 pStartThreadpoolIo(io
);
2352 ret
= ReadFile(server
, NULL
, 1, NULL
, &ovl
);
2353 ok(!ret
, "wrong ret %d\n", ret
);
2354 ok(GetLastError() == ERROR_NOACCESS
, "wrong error %lu\n", GetLastError());
2356 pCancelThreadpoolIo(io
);
2357 pWaitForThreadpoolIoCallbacks(io
, FALSE
);
2358 ok(!userdata
.count
, "callback ran %u times\n", userdata
.count
);
2361 pStartThreadpoolIo(io
);
2363 ret
= ReadFile(server
, in
, sizeof(in
), NULL
, &ovl
);
2364 ok(!ret
, "wrong ret %d\n", ret
);
2365 ok(GetLastError() == ERROR_IO_PENDING
, "wrong error %lu\n", GetLastError());
2366 ret
= CancelIo(server
);
2367 ok(ret
, "CancelIo() failed, error %lu\n", GetLastError());
2369 pWaitForThreadpoolIoCallbacks(io
, FALSE
);
2370 ok(userdata
.count
== 1, "callback ran %u times\n", userdata
.count
);
2371 ok(userdata
.ovl
== &ovl
, "expected %p, got %p\n", &ovl
, userdata
.ovl
);
2372 ok(userdata
.ret
== ERROR_OPERATION_ABORTED
, "got status %#lx\n", userdata
.ret
);
2373 ok(!userdata
.length
, "got length %Iu\n", userdata
.length
);
2374 ok(userdata
.io
== io
, "expected %p, got %p\n", io
, userdata
.io
);
2376 CloseHandle(ovl
.hEvent
);
2377 CloseHandle(client
);
2378 CloseHandle(server
);
2379 pCloseThreadpoolIo(io
);
2380 pTpReleasePool(pool
);
2383 START_TEST(threadpool
)
2385 test_RtlQueueWorkItem();
2386 test_RtlRegisterWait();
2388 if (!init_threadpool())
2393 test_tp_work_scheduler();
2394 test_tp_group_wait();
2395 test_tp_group_cancel();
2397 test_tp_disassociate();
2399 test_tp_window_length();
2401 test_tp_multi_wait();
2403 test_kernel32_tp_io();