Signed-off-by: Zebediah Figura z.figura12@gmail.com --- Thanks Jacek Caban for rebasing these patches onto master.
dlls/ntdll/threadpool.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-)
diff --git a/dlls/ntdll/threadpool.c b/dlls/ntdll/threadpool.c index 4b4bdf2cd7..a6e4749e10 100644 --- a/dlls/ntdll/threadpool.c +++ b/dlls/ntdll/threadpool.c @@ -2023,6 +2023,17 @@ static void tp_object_cancel( struct threadpool_object *object ) tp_object_release( object ); }
+static BOOL object_is_finished( struct threadpool_object *object, BOOL group ) +{ + if (object->num_pending_callbacks) + return FALSE; + + if (group) + return !object->num_running_callbacks; + else + return !object->num_associated_callbacks; +} + /*********************************************************************** * tp_object_wait (internal) * @@ -2034,14 +2045,11 @@ static void tp_object_wait( struct threadpool_object *object, BOOL group_wait ) struct threadpool *pool = object->pool;
RtlEnterCriticalSection( &pool->cs ); - if (group_wait) + while (!object_is_finished( object, group_wait )) { - while (object->num_pending_callbacks || object->num_running_callbacks) + if (group_wait) RtlSleepConditionVariableCS( &object->group_finished_event, &pool->cs, NULL ); - } - else - { - while (object->num_pending_callbacks || object->num_associated_callbacks) + else RtlSleepConditionVariableCS( &object->finished_event, &pool->cs, NULL ); } RtlLeaveCriticalSection( &pool->cs ); @@ -2261,13 +2269,13 @@ static void CALLBACK threadpool_worker_proc( void *param ) }
object->num_running_callbacks--; - if (!object->num_pending_callbacks && !object->num_running_callbacks) + if (object_is_finished( object, TRUE )) RtlWakeAllConditionVariable( &object->group_finished_event );
if (instance.associated) { object->num_associated_callbacks--; - if (!object->num_pending_callbacks && !object->num_associated_callbacks) + if (object_is_finished( object, FALSE )) RtlWakeAllConditionVariable( &object->finished_event ); }
@@ -2567,7 +2575,7 @@ VOID WINAPI TpDisassociateCallback( TP_CALLBACK_INSTANCE *instance ) RtlEnterCriticalSection( &pool->cs );
object->num_associated_callbacks--; - if (!object->num_pending_callbacks && !object->num_associated_callbacks) + if (object_is_finished( object, FALSE )) RtlWakeAllConditionVariable( &object->finished_event );
RtlLeaveCriticalSection( &pool->cs );
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ntdll/ntdll.spec | 4 + dlls/ntdll/ntdll_misc.h | 7 + dlls/ntdll/tests/threadpool.c | 179 ++++++++++++++++++ dlls/ntdll/threadpool.c | 335 +++++++++++++++++++++++++++++++++- include/winternl.h | 8 +- 5 files changed, 523 insertions(+), 10 deletions(-)
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec index a43e419ab8..0be6ec6bfc 100644 --- a/dlls/ntdll/ntdll.spec +++ b/dlls/ntdll/ntdll.spec @@ -1060,6 +1060,7 @@ @ stdcall RtlxUnicodeStringToAnsiSize(ptr) RtlUnicodeStringToAnsiSize @ stdcall RtlxUnicodeStringToOemSize(ptr) RtlUnicodeStringToOemSize @ stdcall TpAllocCleanupGroup(ptr) +@ stdcall TpAllocIoCompletion(ptr ptr ptr ptr ptr) @ stdcall TpAllocPool(ptr ptr) @ stdcall TpAllocTimer(ptr ptr ptr ptr) @ stdcall TpAllocWait(ptr ptr ptr ptr) @@ -1070,12 +1071,14 @@ @ stdcall TpCallbackReleaseSemaphoreOnCompletion(ptr long long) @ stdcall TpCallbackSetEventOnCompletion(ptr long) @ stdcall TpCallbackUnloadDllOnCompletion(ptr ptr) +@ stdcall TpCancelAsyncIoOperation(ptr) @ stdcall TpDisassociateCallback(ptr) @ stdcall TpIsTimerSet(ptr) @ stdcall TpPostWork(ptr) @ stdcall TpQueryPoolStackInformation(ptr ptr) @ stdcall TpReleaseCleanupGroup(ptr) @ stdcall TpReleaseCleanupGroupMembers(ptr long ptr) +@ stdcall TpReleaseIoCompletion(ptr) @ stdcall TpReleasePool(ptr) @ stdcall TpReleaseTimer(ptr) @ stdcall TpReleaseWait(ptr) @@ -1087,6 +1090,7 @@ @ stdcall TpSetWait(ptr long ptr) @ stdcall TpSimpleTryPost(ptr ptr ptr) @ stdcall TpStartAsyncIoOperation(ptr) +@ stdcall TpWaitForIoCompletion(ptr long) @ stdcall TpWaitForTimer(ptr long) @ stdcall TpWaitForWait(ptr long) @ stdcall TpWaitForWork(ptr long) diff --git a/dlls/ntdll/ntdll_misc.h b/dlls/ntdll/ntdll_misc.h index 1396e1c19c..b0a73fcbe9 100644 --- a/dlls/ntdll/ntdll_misc.h +++ b/dlls/ntdll/ntdll_misc.h @@ -30,6 +30,13 @@ #include "wine/server.h" #include "wine/asm.h"
+#define DECLARE_CRITICAL_SECTION(cs) \ + static RTL_CRITICAL_SECTION cs; \ + static RTL_CRITICAL_SECTION_DEBUG cs##_debug = \ + { 0, 0, &cs, { &cs##_debug.ProcessLocksList, &cs##_debug.ProcessLocksList }, \ + 0, 0, { (DWORD_PTR)(__FILE__ ": " # cs) }}; \ + static RTL_CRITICAL_SECTION cs = { &cs##_debug, -1, 0, 0, 0, 0 }; + #define MAX_NT_PATH_LENGTH 277
#define MAX_DOS_DRIVES 26 diff --git a/dlls/ntdll/tests/threadpool.c b/dlls/ntdll/tests/threadpool.c index bf5493cac0..32d4c3eac2 100644 --- a/dlls/ntdll/tests/threadpool.c +++ b/dlls/ntdll/tests/threadpool.c @@ -18,22 +18,27 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#define NONAMELESSSTRUCT +#define NONAMELESSUNION #include "ntdll_test.h"
static HMODULE hntdll = 0; static NTSTATUS (WINAPI *pTpAllocCleanupGroup)(TP_CLEANUP_GROUP **); +static NTSTATUS (WINAPI *pTpAllocIoCompletion)(TP_IO **,HANDLE,PTP_IO_CALLBACK,void *,TP_CALLBACK_ENVIRON *); static NTSTATUS (WINAPI *pTpAllocPool)(TP_POOL **,PVOID); static NTSTATUS (WINAPI *pTpAllocTimer)(TP_TIMER **,PTP_TIMER_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *); static NTSTATUS (WINAPI *pTpAllocWait)(TP_WAIT **,PTP_WAIT_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *); static NTSTATUS (WINAPI *pTpAllocWork)(TP_WORK **,PTP_WORK_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *); static NTSTATUS (WINAPI *pTpCallbackMayRunLong)(TP_CALLBACK_INSTANCE *); static VOID (WINAPI *pTpCallbackReleaseSemaphoreOnCompletion)(TP_CALLBACK_INSTANCE *,HANDLE,DWORD); +static void (WINAPI *pTpCancelAsyncIoOperation)(TP_IO *); static VOID (WINAPI *pTpDisassociateCallback)(TP_CALLBACK_INSTANCE *); static BOOL (WINAPI *pTpIsTimerSet)(TP_TIMER *); static VOID (WINAPI *pTpReleaseWait)(TP_WAIT *); static VOID (WINAPI *pTpPostWork)(TP_WORK *); static VOID (WINAPI *pTpReleaseCleanupGroup)(TP_CLEANUP_GROUP *); static VOID (WINAPI *pTpReleaseCleanupGroupMembers)(TP_CLEANUP_GROUP *,BOOL,PVOID); +static void (WINAPI *pTpReleaseIoCompletion)(TP_IO *); static VOID (WINAPI *pTpReleasePool)(TP_POOL *); static VOID (WINAPI *pTpReleaseTimer)(TP_TIMER *); static VOID (WINAPI *pTpReleaseWork)(TP_WORK *); @@ -41,6 +46,8 @@ static VOID (WINAPI *pTpSetPoolMaxThreads)(TP_POOL *,DWORD); static VOID (WINAPI *pTpSetTimer)(TP_TIMER *,LARGE_INTEGER *,LONG,LONG); static VOID (WINAPI *pTpSetWait)(TP_WAIT *,HANDLE,LARGE_INTEGER *); static NTSTATUS (WINAPI *pTpSimpleTryPost)(PTP_SIMPLE_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *); +static void (WINAPI *pTpStartAsyncIoOperation)(TP_IO *); +static void (WINAPI *pTpWaitForIoCompletion)(TP_IO *,BOOL); static VOID (WINAPI *pTpWaitForTimer)(TP_TIMER *,BOOL); static VOID (WINAPI *pTpWaitForWait)(TP_WAIT *,BOOL); static VOID (WINAPI *pTpWaitForWork)(TP_WORK *,BOOL); @@ -63,10 +70,12 @@ static BOOL init_threadpool(void) }
NTDLL_GET_PROC(TpAllocCleanupGroup); + NTDLL_GET_PROC(TpAllocIoCompletion); NTDLL_GET_PROC(TpAllocPool); NTDLL_GET_PROC(TpAllocTimer); NTDLL_GET_PROC(TpAllocWait); NTDLL_GET_PROC(TpAllocWork); + NTDLL_GET_PROC(TpCancelAsyncIoOperation); NTDLL_GET_PROC(TpCallbackMayRunLong); NTDLL_GET_PROC(TpCallbackReleaseSemaphoreOnCompletion); NTDLL_GET_PROC(TpDisassociateCallback); @@ -74,6 +83,7 @@ static BOOL init_threadpool(void) NTDLL_GET_PROC(TpPostWork); NTDLL_GET_PROC(TpReleaseCleanupGroup); NTDLL_GET_PROC(TpReleaseCleanupGroupMembers); + NTDLL_GET_PROC(TpReleaseIoCompletion); NTDLL_GET_PROC(TpReleasePool); NTDLL_GET_PROC(TpReleaseTimer); NTDLL_GET_PROC(TpReleaseWait); @@ -82,6 +92,8 @@ static BOOL init_threadpool(void) NTDLL_GET_PROC(TpSetTimer); NTDLL_GET_PROC(TpSetWait); NTDLL_GET_PROC(TpSimpleTryPost); + NTDLL_GET_PROC(TpStartAsyncIoOperation); + NTDLL_GET_PROC(TpWaitForIoCompletion); NTDLL_GET_PROC(TpWaitForTimer); NTDLL_GET_PROC(TpWaitForWait); NTDLL_GET_PROC(TpWaitForWork); @@ -1906,6 +1918,172 @@ static void test_tp_multi_wait(void) CloseHandle(semaphore); }
+struct io_cb_ctx +{ + unsigned int count; + void *ovl; + NTSTATUS ret; + ULONG_PTR length; + TP_IO *io; +}; + +static void CALLBACK io_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, + void *cvalue, IO_STATUS_BLOCK *iosb, TP_IO *io) +{ + struct io_cb_ctx *ctx = userdata; + ++ctx->count; + ctx->ovl = cvalue; + ctx->ret = iosb->u.Status; + ctx->length = iosb->Information; + ctx->io = io; +} + +static DWORD WINAPI io_wait_thread(void *arg) +{ + TP_IO *io = arg; + pTpWaitForIoCompletion(io, FALSE); + return 0; +} + +static void test_tp_io(void) +{ + TP_CALLBACK_ENVIRON environment = {.Version = 1}; + OVERLAPPED ovl = {}, ovl2 = {}; + HANDLE client, server, thread; + struct io_cb_ctx userdata; + char in[1], in2[1]; + const char out[1]; + NTSTATUS status; + DWORD ret_size; + TP_POOL *pool; + TP_IO *io; + BOOL ret; + + ovl.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); + + status = pTpAllocPool(&pool, NULL); + ok(!status, "failed to allocate pool, status %#x\n", status); + + server = CreateNamedPipeA("\\.\pipe\wine_tp_test", + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 0, 1, 1024, 1024, 0, NULL); + ok(server != INVALID_HANDLE_VALUE, "Failed to create server pipe, error %u.\n", GetLastError()); + client = CreateFileA("\\.\pipe\wine_tp_test", GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, 0, 0); + ok(client != INVALID_HANDLE_VALUE, "Failed to create client pipe, error %u.\n", GetLastError()); + + environment.Pool = pool; + io = NULL; + status = pTpAllocIoCompletion(&io, server, io_cb, &userdata, &environment); + ok(!status, "got %#x\n", status); + ok(!!io, "expected non-NULL TP_IO\n"); + + pTpWaitForIoCompletion(io, FALSE); + + userdata.count = 0; + pTpStartAsyncIoOperation(io); + + thread = CreateThread(NULL, 0, io_wait_thread, io, 0, NULL); + ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "TpWaitForIoCompletion() should not return\n"); + + ret = ReadFile(server, in, sizeof(in), NULL, &ovl); + ok(!ret, "wrong ret %d\n", ret); + ok(GetLastError() == ERROR_IO_PENDING, "wrong error %u\n", GetLastError()); + + ret = WriteFile(client, out, sizeof(out), &ret_size, NULL); + ok(ret, "WriteFile() failed, error %u\n", GetLastError()); + + pTpWaitForIoCompletion(io, FALSE); + ok(userdata.count == 1, "callback ran %u times\n", userdata.count); + ok(userdata.ovl == &ovl, "expected %p, got %p\n", &ovl, userdata.ovl); + ok(userdata.ret == STATUS_SUCCESS, "got status %#x\n", userdata.ret); + ok(userdata.length == 1, "got length %lu\n", userdata.length); + ok(userdata.io == io, "expected %p, got %p\n", io, userdata.io); + + ok(!WaitForSingleObject(thread, 1000), "wait timed out\n"); + CloseHandle(thread); + + userdata.count = 0; + pTpStartAsyncIoOperation(io); + pTpStartAsyncIoOperation(io); + + ret = ReadFile(server, in, sizeof(in), NULL, &ovl); + ok(!ret, "wrong ret %d\n", ret); + ok(GetLastError() == ERROR_IO_PENDING, "wrong error %u\n", GetLastError()); + ret = ReadFile(server, in2, sizeof(in2), NULL, &ovl2); + ok(!ret, "wrong ret %d\n", ret); + ok(GetLastError() == ERROR_IO_PENDING, "wrong error %u\n", GetLastError()); + + ret = WriteFile(client, out, sizeof(out), &ret_size, NULL); + ok(ret, "WriteFile() failed, error %u\n", GetLastError()); + ret = WriteFile(client, out, sizeof(out), &ret_size, NULL); + ok(ret, "WriteFile() failed, error %u\n", GetLastError()); + + pTpWaitForIoCompletion(io, FALSE); + ok(userdata.count == 2, "callback ran %u times\n", userdata.count); + ok(userdata.ret == STATUS_SUCCESS, "got status %#x\n", userdata.ret); + ok(userdata.length == 1, "got length %lu\n", userdata.length); + ok(userdata.io == io, "expected %p, got %p\n", io, userdata.io); + + /* The documentation is a bit unclear about passing TRUE to + * WaitForThreadpoolIoCallbacks()—"pending I/O requests are not canceled" + * [as with CancelIoEx()], but pending threadpool callbacks are, even those + * which have not yet reached the completion port [as with + * TpCancelAsyncIoOperation()]. */ + userdata.count = 0; + pTpStartAsyncIoOperation(io); + + pTpWaitForIoCompletion(io, TRUE); + ok(!userdata.count, "callback ran %u times\n", userdata.count); + + pTpStartAsyncIoOperation(io); + + ret = WriteFile(client, out, sizeof(out), &ret_size, NULL); + ok(ret, "WriteFile() failed, error %u\n", GetLastError()); + + ret = ReadFile(server, in, sizeof(in), NULL, &ovl); + ok(ret, "wrong ret %d\n", ret); + + pTpWaitForIoCompletion(io, FALSE); + ok(userdata.count == 1, "callback ran %u times\n", userdata.count); + ok(userdata.ovl == &ovl, "expected %p, got %p\n", &ovl, userdata.ovl); + ok(userdata.ret == STATUS_SUCCESS, "got status %#x\n", userdata.ret); + ok(userdata.length == 1, "got length %lu\n", userdata.length); + ok(userdata.io == io, "expected %p, got %p\n", io, userdata.io); + + userdata.count = 0; + pTpStartAsyncIoOperation(io); + + ret = ReadFile(server, NULL, 1, NULL, &ovl); + ok(!ret, "wrong ret %d\n", ret); + ok(GetLastError() == ERROR_NOACCESS, "wrong error %u\n", GetLastError()); + + pTpCancelAsyncIoOperation(io); + pTpWaitForIoCompletion(io, FALSE); + ok(!userdata.count, "callback ran %u times\n", userdata.count); + + userdata.count = 0; + pTpStartAsyncIoOperation(io); + + ret = ReadFile(server, in, sizeof(in), NULL, &ovl); + ok(!ret, "wrong ret %d\n", ret); + ok(GetLastError() == ERROR_IO_PENDING, "wrong error %u\n", GetLastError()); + ret = CancelIo(server); + ok(ret, "CancelIo() failed, error %u\n", GetLastError()); + + pTpWaitForIoCompletion(io, FALSE); + ok(userdata.count == 1, "callback ran %u times\n", userdata.count); + ok(userdata.ovl == &ovl, "expected %p, got %p\n", &ovl, userdata.ovl); + ok(userdata.ret == STATUS_CANCELLED, "got status %#x\n", userdata.ret); + ok(!userdata.length, "got length %lu\n", userdata.length); + ok(userdata.io == io, "expected %p, got %p\n", io, userdata.io); + + CloseHandle(ovl.hEvent); + CloseHandle(client); + CloseHandle(server); + pTpReleaseIoCompletion(io); + pTpReleasePool(pool); +} + START_TEST(threadpool) { test_RtlQueueWorkItem(); @@ -1925,4 +2103,5 @@ START_TEST(threadpool) test_tp_window_length(); test_tp_wait(); test_tp_multi_wait(); + test_tp_io(); } diff --git a/dlls/ntdll/threadpool.c b/dlls/ntdll/threadpool.c index a6e4749e10..215a5e9c53 100644 --- a/dlls/ntdll/threadpool.c +++ b/dlls/ntdll/threadpool.c @@ -131,6 +131,7 @@ struct threadpool int min_workers; int num_workers; int num_busy_workers; + HANDLE compl_port; TP_POOL_STACK_INFORMATION stack_info; };
@@ -139,7 +140,14 @@ enum threadpool_objtype TP_OBJECT_TYPE_SIMPLE, TP_OBJECT_TYPE_WORK, TP_OBJECT_TYPE_TIMER, - TP_OBJECT_TYPE_WAIT + TP_OBJECT_TYPE_WAIT, + TP_OBJECT_TYPE_IO, +}; + +struct io_completion +{ + IO_STATUS_BLOCK iosb; + ULONG_PTR cvalue; };
/* internal threadpool object representation */ @@ -201,6 +209,13 @@ struct threadpool_object ULONGLONG timeout; HANDLE handle; } wait; + struct + { + PTP_IO_CALLBACK callback; + /* locked via .pool->cs */ + unsigned int pending_count, completion_count, completion_max; + struct io_completion *completions; + } io; } u; };
@@ -291,6 +306,29 @@ struct waitqueue_bucket HANDLE update_event; };
+/* global I/O completion queue object */ +static RTL_CRITICAL_SECTION_DEBUG ioqueue_debug; + +static struct +{ + CRITICAL_SECTION cs; + LONG objcount; + BOOL thread_running; + HANDLE port; + RTL_CONDITION_VARIABLE update_event; +} +ioqueue = +{ + .cs = { &ioqueue_debug, -1, 0, 0, 0, 0 }, +}; + +static RTL_CRITICAL_SECTION_DEBUG ioqueue_debug = +{ + 0, 0, &ioqueue.cs, + { &ioqueue_debug.ProcessLocksList, &ioqueue_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": ioqueue.cs") } +}; + static inline struct threadpool *impl_from_TP_POOL( TP_POOL *pool ) { return (struct threadpool *)pool; @@ -317,6 +355,13 @@ static inline struct threadpool_object *impl_from_TP_WAIT( TP_WAIT *wait ) return object; }
+static inline struct threadpool_object *impl_from_TP_IO( TP_IO *io ) +{ + struct threadpool_object *object = (struct threadpool_object *)io; + assert( object->type == TP_OBJECT_TYPE_IO ); + return object; +} + static inline struct threadpool_group *impl_from_TP_CLEANUP_GROUP( TP_CLEANUP_GROUP *group ) { return (struct threadpool_group *)group; @@ -343,6 +388,33 @@ static inline LONG interlocked_dec( PLONG dest ) return interlocked_xchg_add( dest, -1 ) - 1; }
+static BOOL array_reserve(void **elements, unsigned int *capacity, unsigned int count, unsigned int size) +{ + unsigned int new_capacity, max_capacity; + void *new_elements; + + if (count <= *capacity) + return TRUE; + + max_capacity = ~(SIZE_T)0 / size; + if (count > max_capacity) + return FALSE; + + new_capacity = max(4, *capacity); + while (new_capacity < count && new_capacity <= max_capacity / 2) + new_capacity *= 2; + if (new_capacity < count) + new_capacity = max_capacity; + + if (!(new_elements = RtlReAllocateHeap( GetProcessHeap(), 0, *elements, new_capacity * size ))) + return FALSE; + + *elements = new_elements; + *capacity = new_capacity; + + return TRUE; +} + static void CALLBACK process_rtl_work_item( TP_CALLBACK_INSTANCE *instance, void *userdata ) { struct rtl_work_item *item = userdata; @@ -1642,6 +1714,127 @@ static void tp_waitqueue_unlock( struct threadpool_object *wait ) RtlLeaveCriticalSection( &waitqueue.cs ); }
+static void CALLBACK ioqueue_thread_proc( void *param ) +{ + struct io_completion *completion; + struct threadpool_object *io; + IO_STATUS_BLOCK iosb; + ULONG_PTR key, value; + NTSTATUS status; + + TRACE( "starting I/O completion thread\n" ); + + RtlEnterCriticalSection( &ioqueue.cs ); + + for (;;) + { + RtlLeaveCriticalSection( &ioqueue.cs ); + if ((status = NtRemoveIoCompletion( ioqueue.port, &key, &value, &iosb, NULL ))) + ERR("NtRemoveIoCompletion failed, status %#x.\n", status); + RtlEnterCriticalSection( &ioqueue.cs ); + + if (key) + { + io = (struct threadpool_object *)key; + + RtlEnterCriticalSection( &io->pool->cs ); + + if (!array_reserve((void **)&io->u.io.completions, &io->u.io.completion_max, + io->u.io.completion_count + 1, sizeof(*io->u.io.completions))) + { + ERR("Failed to allocate memory.\n"); + RtlLeaveCriticalSection( &io->pool->cs ); + continue; + } + + completion = &io->u.io.completions[io->u.io.completion_count++]; + completion->iosb = iosb; + completion->cvalue = value; + + tp_object_submit( io, FALSE ); + + RtlLeaveCriticalSection( &io->pool->cs ); + } + + if (!ioqueue.objcount) + { + /* All I/O objects have been destroyed; if no new objects are + * created within some amount of time, then we can shutdown this + * thread. */ + LARGE_INTEGER timeout = {.QuadPart = (ULONGLONG)THREADPOOL_WORKER_TIMEOUT * -10000}; + if (RtlSleepConditionVariableCS( &ioqueue.update_event, &ioqueue.cs, + &timeout) == STATUS_TIMEOUT && !ioqueue.objcount) + break; + } + } + + RtlLeaveCriticalSection( &ioqueue.cs ); + + TRACE( "terminating I/O completion thread\n" ); + + RtlExitUserThread( 0 ); +} + +static NTSTATUS tp_ioqueue_lock( struct threadpool_object *io, HANDLE file ) +{ + NTSTATUS status = STATUS_SUCCESS; + + assert( io->type == TP_OBJECT_TYPE_IO ); + + RtlEnterCriticalSection( &ioqueue.cs ); + + if (!ioqueue.port && (status = NtCreateIoCompletion( &ioqueue.port, + IO_COMPLETION_ALL_ACCESS, NULL, 0 ))) + { + RtlLeaveCriticalSection( &ioqueue.cs ); + return status; + } + + if (!ioqueue.thread_running) + { + HANDLE thread; + + if (!(status = RtlCreateUserThread( GetCurrentProcess(), NULL, FALSE, + NULL, 0, 0, ioqueue_thread_proc, NULL, &thread, NULL ))) + { + ioqueue.thread_running = TRUE; + NtClose( thread ); + } + } + + if (status == STATUS_SUCCESS) + { + FILE_COMPLETION_INFORMATION info; + IO_STATUS_BLOCK iosb; + + info.CompletionPort = ioqueue.port; + info.CompletionKey = (ULONG_PTR)io; + + status = NtSetInformationFile( file, &iosb, &info, sizeof(info), FileCompletionInformation ); + } + + if (status == STATUS_SUCCESS) + { + if (!ioqueue.objcount++) + RtlWakeConditionVariable( &ioqueue.update_event ); + } + + RtlLeaveCriticalSection( &ioqueue.cs ); + return status; +} + +static void tp_ioqueue_unlock( struct threadpool_object *io ) +{ + assert( io->type == TP_OBJECT_TYPE_IO ); + + RtlEnterCriticalSection( &ioqueue.cs ); + + if (!--ioqueue.objcount) + NtSetIoCompletion( ioqueue.port, 0, 0, STATUS_SUCCESS, 0 ); + + RtlLeaveCriticalSection( &ioqueue.cs ); +} + /*********************************************************************** * tp_threadpool_alloc (internal) * @@ -2017,6 +2210,8 @@ static void tp_object_cancel( struct threadpool_object *object ) if (object->type == TP_OBJECT_TYPE_WAIT) object->u.wait.signaled = 0; } + if (object->type == TP_OBJECT_TYPE_IO) + object->u.io.pending_count = 0; RtlLeaveCriticalSection( &pool->cs );
while (pending_callbacks--) @@ -2027,6 +2222,8 @@ static BOOL object_is_finished( struct threadpool_object *object, BOOL group ) { if (object->num_pending_callbacks) return FALSE; + if (object->type == TP_OBJECT_TYPE_IO && object->u.io.pending_count) + return FALSE;
if (group) return !object->num_running_callbacks; @@ -2066,6 +2263,8 @@ static void tp_object_prepare_shutdown( struct threadpool_object *object ) tp_timerqueue_unlock( object ); else if (object->type == TP_OBJECT_TYPE_WAIT) tp_waitqueue_unlock( object ); + else if (object->type == TP_OBJECT_TYPE_IO) + tp_ioqueue_unlock( object ); }
/*********************************************************************** @@ -2131,6 +2330,7 @@ static void CALLBACK threadpool_worker_proc( void *param ) { TP_CALLBACK_INSTANCE *callback_instance; struct threadpool_instance instance; + struct io_completion completion; struct threadpool *pool = param; TP_WAIT_RESULT wait_result = 0; LARGE_INTEGER timeout; @@ -2160,6 +2360,12 @@ static void CALLBACK threadpool_worker_proc( void *param ) wait_result = object->u.wait.signaled ? WAIT_OBJECT_0 : WAIT_TIMEOUT; if (wait_result == WAIT_OBJECT_0) object->u.wait.signaled--; } + else if (object->type == TP_OBJECT_TYPE_IO) + { + assert( object->u.io.completion_count ); + completion = object->u.io.completions[--object->u.io.completion_count]; + object->u.io.pending_count--; + }
/* Leave critical section and do the actual callback. */ object->num_associated_callbacks++; @@ -2218,6 +2424,17 @@ static void CALLBACK threadpool_worker_proc( void *param ) break; }
+ case TP_OBJECT_TYPE_IO: + { + TRACE( "executing I/O callback %p(%p, %p, %#lx, %p, %p)\n", + object->u.io.callback, callback_instance, object->userdata, + completion.cvalue, &completion.iosb, (TP_IO *)object ); + object->u.io.callback( callback_instance, object->userdata, + (void *)completion.cvalue, &completion.iosb, (TP_IO *)object ); + TRACE( "callback %p returned\n", object->u.io.callback ); + break; + } + default: assert(0); break; @@ -2317,6 +2534,50 @@ NTSTATUS WINAPI TpAllocCleanupGroup( TP_CLEANUP_GROUP **out ) return tp_group_alloc( (struct threadpool_group **)out ); }
+/*********************************************************************** + * TpAllocIoCompletion (NTDLL.@) + */ +NTSTATUS WINAPI TpAllocIoCompletion( TP_IO **out, HANDLE file, PTP_IO_CALLBACK callback, + void *userdata, TP_CALLBACK_ENVIRON *environment ) +{ + struct threadpool_object *object; + struct threadpool *pool; + NTSTATUS status; + + TRACE( "%p %p %p %p %p\n", out, file, callback, userdata, environment ); + + if (!(object = RtlAllocateHeap( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*object) ))) + return STATUS_NO_MEMORY; + + if ((status = tp_threadpool_lock( &pool, environment ))) + { + RtlFreeHeap( GetProcessHeap(), 0, object ); + return status; + } + + object->type = TP_OBJECT_TYPE_IO; + object->u.io.callback = callback; + if (!(object->u.io.completions = RtlAllocateHeap( GetProcessHeap(), 0, 8 * sizeof(*object->u.io.completions) ))) + { + tp_threadpool_unlock( pool ); + RtlFreeHeap( GetProcessHeap(), 0, object ); + return status; + } + + if ((status = tp_ioqueue_lock( object, file ))) + { + tp_threadpool_unlock( pool ); + RtlFreeHeap( GetProcessHeap(), 0, object->u.io.completions ); + RtlFreeHeap( GetProcessHeap(), 0, object ); + return status; + } + + tp_object_initialize( object, pool, userdata, environment ); + + *out = (TP_IO *)object; + return STATUS_SUCCESS; +} + /*********************************************************************** * TpAllocPool (NTDLL.@) */ @@ -2441,6 +2702,26 @@ NTSTATUS WINAPI TpAllocWork( TP_WORK **out, PTP_WORK_CALLBACK callback, PVOID us return STATUS_SUCCESS; }
+/*********************************************************************** + * TpCancelAsyncIoOperation (NTDLL.@) + */ +void WINAPI TpCancelAsyncIoOperation( TP_IO *io ) +{ + struct threadpool_object *this = impl_from_TP_IO( io ); + + TRACE( "%p\n", io ); + + RtlEnterCriticalSection( &this->pool->cs ); + + this->u.io.pending_count--; + if (object_is_finished( this, TRUE )) + RtlWakeAllConditionVariable( &this->group_finished_event ); + if (object_is_finished( this, FALSE )) + RtlWakeAllConditionVariable( &this->finished_event ); + + RtlLeaveCriticalSection( &this->pool->cs ); +} + /*********************************************************************** * TpCallbackLeaveCriticalSectionOnCompletion (NTDLL.@) */ @@ -2692,6 +2973,20 @@ VOID WINAPI TpReleaseCleanupGroupMembers( TP_CLEANUP_GROUP *group, BOOL cancel_p } }
+/*********************************************************************** + * TpReleaseIoCompletion (NTDLL.@) + */ +void WINAPI TpReleaseIoCompletion( TP_IO *io ) +{ + struct threadpool_object *this = impl_from_TP_IO( io ); + + TRACE( "%p\n", io ); + + tp_object_prepare_shutdown( this ); + this->shutdown = TRUE; + tp_object_release( this ); +} + /*********************************************************************** * TpReleasePool (NTDLL.@) */ @@ -2960,6 +3255,36 @@ NTSTATUS WINAPI TpSimpleTryPost( PTP_SIMPLE_CALLBACK callback, PVOID userdata, return STATUS_SUCCESS; }
+/*********************************************************************** + * TpStartAsyncIoOperation (NTDLL.@) + */ +void WINAPI TpStartAsyncIoOperation( TP_IO *io ) +{ + struct threadpool_object *this = impl_from_TP_IO( io ); + + TRACE( "%p\n", io ); + + RtlEnterCriticalSection( &this->pool->cs ); + + this->u.io.pending_count++; + + RtlLeaveCriticalSection( &this->pool->cs ); +} + +/*********************************************************************** + * TpWaitForIoCompletion (NTDLL.@) + */ +void WINAPI TpWaitForIoCompletion( TP_IO *io, BOOL cancel_pending ) +{ + struct threadpool_object *this = impl_from_TP_IO( io ); + + TRACE( "%p %d\n", io, cancel_pending ); + + if (cancel_pending) + tp_object_cancel( this ); + tp_object_wait( this, FALSE ); +} + /*********************************************************************** * TpWaitForTimer (NTDLL.@) */ @@ -3039,11 +3364,3 @@ NTSTATUS WINAPI TpQueryPoolStackInformation( TP_POOL *pool, TP_POOL_STACK_INFORM
return STATUS_SUCCESS; } - -/*********************************************************************** - * TpStartAsyncIoOperation (NTDLL.@) - */ -void WINAPI TpStartAsyncIoOperation( TP_IO *io ) -{ - FIXME( "%p\n", io ); -} diff --git a/include/winternl.h b/include/winternl.h index e4c611e4af..ac045c1709 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -2351,6 +2351,8 @@ typedef struct _SYSTEM_MODULE_INFORMATION
typedef LONG (CALLBACK *PRTL_EXCEPTION_FILTER)(PEXCEPTION_POINTERS);
+typedef void (CALLBACK *PTP_IO_CALLBACK)(PTP_CALLBACK_INSTANCE,void*,void*,IO_STATUS_BLOCK*,PTP_IO); + /*********************************************************************** * Function declarations */ @@ -3013,6 +3015,7 @@ NTSYSAPI NTSTATUS WINAPI RtlLargeIntegerToChar(const ULONGLONG *,ULONG,ULONG,PC /* Threadpool functions */
NTSYSAPI NTSTATUS WINAPI TpAllocCleanupGroup(TP_CLEANUP_GROUP **); +NTSYSAPI NTSTATUS WINAPI TpAllocIoCompletion(TP_IO **,HANDLE,PTP_IO_CALLBACK,void *,TP_CALLBACK_ENVIRON *); NTSYSAPI NTSTATUS WINAPI TpAllocPool(TP_POOL **,PVOID); NTSYSAPI NTSTATUS WINAPI TpAllocTimer(TP_TIMER **,PTP_TIMER_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *); NTSYSAPI NTSTATUS WINAPI TpAllocWait(TP_WAIT **,PTP_WAIT_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *); @@ -3023,23 +3026,26 @@ NTSYSAPI void WINAPI TpCallbackReleaseMutexOnCompletion(TP_CALLBACK_INSTANC NTSYSAPI void WINAPI TpCallbackReleaseSemaphoreOnCompletion(TP_CALLBACK_INSTANCE *,HANDLE,DWORD); NTSYSAPI void WINAPI TpCallbackSetEventOnCompletion(TP_CALLBACK_INSTANCE *,HANDLE); NTSYSAPI void WINAPI TpCallbackUnloadDllOnCompletion(TP_CALLBACK_INSTANCE *,HMODULE); +NTSYSAPI void WINAPI TpCancelAsyncIoOperation(TP_IO *); NTSYSAPI void WINAPI TpDisassociateCallback(TP_CALLBACK_INSTANCE *); NTSYSAPI BOOL WINAPI TpIsTimerSet(TP_TIMER *); NTSYSAPI void WINAPI TpPostWork(TP_WORK *); NTSYSAPI NTSTATUS WINAPI TpQueryPoolStackInformation(TP_POOL *, TP_POOL_STACK_INFORMATION *stack_info); NTSYSAPI void WINAPI TpReleaseCleanupGroup(TP_CLEANUP_GROUP *); NTSYSAPI void WINAPI TpReleaseCleanupGroupMembers(TP_CLEANUP_GROUP *,BOOL,PVOID); +NTSYSAPI void WINAPI TpReleaseIoCompletion(TP_IO *); NTSYSAPI void WINAPI TpReleasePool(TP_POOL *); NTSYSAPI void WINAPI TpReleaseTimer(TP_TIMER *); NTSYSAPI void WINAPI TpReleaseWait(TP_WAIT *); NTSYSAPI void WINAPI TpReleaseWork(TP_WORK *); -NTSYSAPI void WINAPI TpStartAsyncIoOperation(TP_IO *); NTSYSAPI void WINAPI TpSetPoolMaxThreads(TP_POOL *,DWORD); NTSYSAPI BOOL WINAPI TpSetPoolMinThreads(TP_POOL *,DWORD); NTSYSAPI NTSTATUS WINAPI TpSetPoolStackInformation(TP_POOL *, TP_POOL_STACK_INFORMATION *stack_info); NTSYSAPI void WINAPI TpSetTimer(TP_TIMER *, LARGE_INTEGER *,LONG,LONG); NTSYSAPI void WINAPI TpSetWait(TP_WAIT *,HANDLE,LARGE_INTEGER *); NTSYSAPI NTSTATUS WINAPI TpSimpleTryPost(PTP_SIMPLE_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *); +NTSYSAPI void WINAPI TpStartAsyncIoOperation(TP_IO *); +NTSYSAPI void WINAPI TpWaitForIoCompletion(TP_IO *,BOOL); NTSYSAPI void WINAPI TpWaitForTimer(TP_TIMER *,BOOL); NTSYSAPI void WINAPI TpWaitForWait(TP_WAIT *,BOOL); NTSYSAPI void WINAPI TpWaitForWork(TP_WORK *,BOOL);
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=69374
Your paranoid android.
=== w8adm (32 bit report) ===
ntdll: threadpool.c:329: Test failed: threadpool.c:172: expected info.userdata = 1, got 0 threadpool.c:330: Test failed: expected info.threadid != 0, got 0 threadpool.c:332: Test failed: OpenThread failed with 87 threadpool.c:335: Test failed: QueueUserAPC failed with 6
=== w1064v1507 (32 bit report) ===
ntdll: threadpool.c:316: Test failed: WaitForSingleObject returned 258 threadpool.c:329: Test failed: expected info.userdata = 1, got 0 threadpool.c:330: Test failed: expected info.threadid != 0, got 0 threadpool.c:332: Test failed: OpenThread failed with 87 threadpool.c:335: Test failed: QueueUserAPC failed with 6
=== w1064v1809_2scr (32 bit report) ===
ntdll: threadpool.c:316: Test failed: WaitForSingleObject returned 258
=== w1064v1507 (64 bit report) ===
ntdll: threadpool.c:316: Test failed: WaitForSingleObject returned 258 threadpool.c:329: Test failed: expected info.userdata = 1, got 0 threadpool.c:330: Test failed: expected info.threadid != 0, got 0 threadpool.c:332: Test failed: OpenThread failed with 87 threadpool.c:335: Test failed: QueueUserAPC failed with 6 threadpool.c:172: threadpool.c:390: Test failed: WaitForSingleObject returned 258 threadpool.c:391: Test failed: expected info.userdata = 0x10000, got 0 threadpool.c:402: Test failed: expected info.userdata = 0x10000, got 0 threadpool.c:172: threadpool.c:405: Test failed: RtlDeregisterWaitEx failed with status 103 threadpool.c:406: Test failed: expected info.userdata = 0x10000, got 0 threadpool.c:421: Test failed: threadpool.c:172: Running rtl_wait callback threadpool.c:434: Test failed: expected info.userdata = 1, got 0 threadpool.c:448: Test failed: expected info.userdata = 1, got 0 threadpool.c:461: Test failed: expected info.userdata = 1, got 0 threadpool.c:1297: threadpool.c:1361: Test failed: WaitForSingleObject returned 258 threadpool.c:1773: Test failed: WaitForSingleObject returned 258 threadpool.c:1775: Test failed: WaitForSingleObject returned 0
Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/ntdll/tests/threadpool.c | 228 ++++++++++++++++++++++++++++------ 1 file changed, 190 insertions(+), 38 deletions(-)
diff --git a/dlls/ntdll/tests/threadpool.c b/dlls/ntdll/tests/threadpool.c index 32d4c3eac2..2984983a06 100644 --- a/dlls/ntdll/tests/threadpool.c +++ b/dlls/ntdll/tests/threadpool.c @@ -22,7 +22,6 @@ #define NONAMELESSUNION #include "ntdll_test.h"
-static HMODULE hntdll = 0; static NTSTATUS (WINAPI *pTpAllocCleanupGroup)(TP_CLEANUP_GROUP **); static NTSTATUS (WINAPI *pTpAllocIoCompletion)(TP_IO **,HANDLE,PTP_IO_CALLBACK,void *,TP_CALLBACK_ENVIRON *); static NTSTATUS (WINAPI *pTpAllocPool)(TP_POOL **,PVOID); @@ -52,51 +51,58 @@ static VOID (WINAPI *pTpWaitForTimer)(TP_TIMER *,BOOL); static VOID (WINAPI *pTpWaitForWait)(TP_WAIT *,BOOL); static VOID (WINAPI *pTpWaitForWork)(TP_WORK *,BOOL);
-#define NTDLL_GET_PROC(func) \ +static void (WINAPI *pCancelThreadpoolIo)(TP_IO *); +static void (WINAPI *pCloseThreadpoolIo)(TP_IO *); +static TP_IO *(WINAPI *pCreateThreadpoolIo)(HANDLE, PTP_WIN32_IO_CALLBACK, void *, TP_CALLBACK_ENVIRON *); +static void (WINAPI *pStartThreadpoolIo)(TP_IO *); +static void (WINAPI *pWaitForThreadpoolIoCallbacks)(TP_IO *, BOOL); + +#define GET_PROC(func) \ do \ { \ - p ## func = (void *)GetProcAddress(hntdll, #func); \ + p ## func = (void *)GetProcAddress(module, #func); \ if (!p ## func) trace("Failed to get address for %s\n", #func); \ } \ while (0)
static BOOL init_threadpool(void) { - hntdll = GetModuleHandleA("ntdll"); - if (!hntdll) - { - win_skip("Could not load ntdll\n"); - return FALSE; - } - - NTDLL_GET_PROC(TpAllocCleanupGroup); - NTDLL_GET_PROC(TpAllocIoCompletion); - NTDLL_GET_PROC(TpAllocPool); - NTDLL_GET_PROC(TpAllocTimer); - NTDLL_GET_PROC(TpAllocWait); - NTDLL_GET_PROC(TpAllocWork); - NTDLL_GET_PROC(TpCancelAsyncIoOperation); - NTDLL_GET_PROC(TpCallbackMayRunLong); - NTDLL_GET_PROC(TpCallbackReleaseSemaphoreOnCompletion); - NTDLL_GET_PROC(TpDisassociateCallback); - NTDLL_GET_PROC(TpIsTimerSet); - NTDLL_GET_PROC(TpPostWork); - NTDLL_GET_PROC(TpReleaseCleanupGroup); - NTDLL_GET_PROC(TpReleaseCleanupGroupMembers); - NTDLL_GET_PROC(TpReleaseIoCompletion); - NTDLL_GET_PROC(TpReleasePool); - NTDLL_GET_PROC(TpReleaseTimer); - NTDLL_GET_PROC(TpReleaseWait); - NTDLL_GET_PROC(TpReleaseWork); - NTDLL_GET_PROC(TpSetPoolMaxThreads); - NTDLL_GET_PROC(TpSetTimer); - NTDLL_GET_PROC(TpSetWait); - NTDLL_GET_PROC(TpSimpleTryPost); - NTDLL_GET_PROC(TpStartAsyncIoOperation); - NTDLL_GET_PROC(TpWaitForIoCompletion); - NTDLL_GET_PROC(TpWaitForTimer); - NTDLL_GET_PROC(TpWaitForWait); - NTDLL_GET_PROC(TpWaitForWork); + HMODULE module = GetModuleHandleA("ntdll"); + GET_PROC(TpAllocCleanupGroup); + GET_PROC(TpAllocIoCompletion); + GET_PROC(TpAllocPool); + GET_PROC(TpAllocTimer); + GET_PROC(TpAllocWait); + GET_PROC(TpAllocWork); + GET_PROC(TpCallbackMayRunLong); + GET_PROC(TpCallbackReleaseSemaphoreOnCompletion); + GET_PROC(TpCancelAsyncIoOperation); + GET_PROC(TpDisassociateCallback); + GET_PROC(TpIsTimerSet); + GET_PROC(TpPostWork); + GET_PROC(TpReleaseCleanupGroup); + GET_PROC(TpReleaseCleanupGroupMembers); + GET_PROC(TpReleaseIoCompletion); + GET_PROC(TpReleasePool); + GET_PROC(TpReleaseTimer); + GET_PROC(TpReleaseWait); + GET_PROC(TpReleaseWork); + GET_PROC(TpSetPoolMaxThreads); + GET_PROC(TpSetTimer); + GET_PROC(TpSetWait); + GET_PROC(TpSimpleTryPost); + GET_PROC(TpStartAsyncIoOperation); + GET_PROC(TpWaitForIoCompletion); + GET_PROC(TpWaitForTimer); + GET_PROC(TpWaitForWait); + GET_PROC(TpWaitForWork); + + module = GetModuleHandleA("kernel32"); + GET_PROC(CancelThreadpoolIo); + GET_PROC(CloseThreadpoolIo); + GET_PROC(CreateThreadpoolIo); + GET_PROC(StartThreadpoolIo); + GET_PROC(WaitForThreadpoolIoCallbacks);
if (!pTpAllocPool) { @@ -2084,6 +2090,151 @@ static void test_tp_io(void) pTpReleasePool(pool); }
+static void CALLBACK kernel32_io_cb(TP_CALLBACK_INSTANCE *instance, void *userdata, + void *ovl, ULONG ret, ULONG_PTR length, TP_IO *io) +{ + struct io_cb_ctx *ctx = userdata; + ++ctx->count; + ctx->ovl = ovl; + ctx->ret = ret; + ctx->length = length; + ctx->io = io; +} + +static void test_kernel32_tp_io(void) +{ + TP_CALLBACK_ENVIRON environment = {.Version = 1}; + OVERLAPPED ovl = {}, ovl2 = {}; + HANDLE client, server, thread; + struct io_cb_ctx userdata; + char in[1], in2[1]; + const char out[1]; + NTSTATUS status; + DWORD ret_size; + TP_POOL *pool; + TP_IO *io; + BOOL ret; + + ovl.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); + + status = pTpAllocPool(&pool, NULL); + ok(!status, "failed to allocate pool, status %#x\n", status); + + server = CreateNamedPipeA("\\.\pipe\wine_tp_test", + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, 0, 1, 1024, 1024, 0, NULL); + ok(server != INVALID_HANDLE_VALUE, "Failed to create server pipe, error %u.\n", GetLastError()); + client = CreateFileA("\\.\pipe\wine_tp_test", GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, 0, 0); + ok(client != INVALID_HANDLE_VALUE, "Failed to create client pipe, error %u.\n", GetLastError()); + + environment.Pool = pool; + io = NULL; + io = pCreateThreadpoolIo(server, kernel32_io_cb, &userdata, &environment); + todo_wine ok(!!io, "expected non-NULL TP_IO\n"); + if (!io) + return; + + pWaitForThreadpoolIoCallbacks(io, FALSE); + + userdata.count = 0; + pStartThreadpoolIo(io); + + thread = CreateThread(NULL, 0, io_wait_thread, io, 0, NULL); + ok(WaitForSingleObject(thread, 100) == WAIT_TIMEOUT, "TpWaitForIoCompletion() should not return\n"); + + ret = ReadFile(server, in, sizeof(in), NULL, &ovl); + ok(!ret, "wrong ret %d\n", ret); + ok(GetLastError() == ERROR_IO_PENDING, "wrong error %u\n", GetLastError()); + + ret = WriteFile(client, out, sizeof(out), &ret_size, NULL); + ok(ret, "WriteFile() failed, error %u\n", GetLastError()); + + pWaitForThreadpoolIoCallbacks(io, FALSE); + ok(userdata.count == 1, "callback ran %u times\n", userdata.count); + ok(userdata.ovl == &ovl, "expected %p, got %p\n", &ovl, userdata.ovl); + ok(userdata.ret == ERROR_SUCCESS, "got status %#x\n", userdata.ret); + ok(userdata.length == 1, "got length %lu\n", userdata.length); + ok(userdata.io == io, "expected %p, got %p\n", io, userdata.io); + + ok(!WaitForSingleObject(thread, 1000), "wait timed out\n"); + CloseHandle(thread); + + userdata.count = 0; + pStartThreadpoolIo(io); + pStartThreadpoolIo(io); + + ret = ReadFile(server, in, sizeof(in), NULL, &ovl); + ok(!ret, "wrong ret %d\n", ret); + ok(GetLastError() == ERROR_IO_PENDING, "wrong error %u\n", GetLastError()); + ret = ReadFile(server, in2, sizeof(in2), NULL, &ovl2); + ok(!ret, "wrong ret %d\n", ret); + ok(GetLastError() == ERROR_IO_PENDING, "wrong error %u\n", GetLastError()); + + ret = WriteFile(client, out, sizeof(out), &ret_size, NULL); + ok(ret, "WriteFile() failed, error %u\n", GetLastError()); + ret = WriteFile(client, out, sizeof(out), &ret_size, NULL); + ok(ret, "WriteFile() failed, error %u\n", GetLastError()); + + pWaitForThreadpoolIoCallbacks(io, FALSE); + ok(userdata.count == 2, "callback ran %u times\n", userdata.count); + ok(userdata.ret == STATUS_SUCCESS, "got status %#x\n", userdata.ret); + ok(userdata.length == 1, "got length %lu\n", userdata.length); + ok(userdata.io == io, "expected %p, got %p\n", io, userdata.io); + + userdata.count = 0; + pStartThreadpoolIo(io); + pWaitForThreadpoolIoCallbacks(io, TRUE); + ok(!userdata.count, "callback ran %u times\n", userdata.count); + + pStartThreadpoolIo(io); + + ret = WriteFile(client, out, sizeof(out), &ret_size, NULL); + ok(ret, "WriteFile() failed, error %u\n", GetLastError()); + + ret = ReadFile(server, in, sizeof(in), NULL, &ovl); + ok(ret, "wrong ret %d\n", ret); + + pWaitForThreadpoolIoCallbacks(io, FALSE); + ok(userdata.count == 1, "callback ran %u times\n", userdata.count); + ok(userdata.ovl == &ovl, "expected %p, got %p\n", &ovl, userdata.ovl); + ok(userdata.ret == ERROR_SUCCESS, "got status %#x\n", userdata.ret); + ok(userdata.length == 1, "got length %lu\n", userdata.length); + ok(userdata.io == io, "expected %p, got %p\n", io, userdata.io); + + userdata.count = 0; + pStartThreadpoolIo(io); + + ret = ReadFile(server, NULL, 1, NULL, &ovl); + ok(!ret, "wrong ret %d\n", ret); + ok(GetLastError() == ERROR_NOACCESS, "wrong error %u\n", GetLastError()); + + pCancelThreadpoolIo(io); + pWaitForThreadpoolIoCallbacks(io, FALSE); + ok(!userdata.count, "callback ran %u times\n", userdata.count); + + userdata.count = 0; + pStartThreadpoolIo(io); + + ret = ReadFile(server, in, sizeof(in), NULL, &ovl); + ok(!ret, "wrong ret %d\n", ret); + ok(GetLastError() == ERROR_IO_PENDING, "wrong error %u\n", GetLastError()); + ret = CancelIo(server); + ok(ret, "CancelIo() failed, error %u\n", GetLastError()); + + pWaitForThreadpoolIoCallbacks(io, FALSE); + ok(userdata.count == 1, "callback ran %u times\n", userdata.count); + ok(userdata.ovl == &ovl, "expected %p, got %p\n", &ovl, userdata.ovl); + ok(userdata.ret == ERROR_OPERATION_ABORTED, "got status %#x\n", userdata.ret); + ok(!userdata.length, "got length %lu\n", userdata.length); + ok(userdata.io == io, "expected %p, got %p\n", io, userdata.io); + + CloseHandle(ovl.hEvent); + CloseHandle(client); + CloseHandle(server); + pCloseThreadpoolIo(io); + pTpReleasePool(pool); +} + START_TEST(threadpool) { test_RtlQueueWorkItem(); @@ -2104,4 +2255,5 @@ START_TEST(threadpool) test_tp_wait(); test_tp_multi_wait(); test_tp_io(); + test_kernel32_tp_io(); }
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=69375
Your paranoid android.
=== w8adm (32 bit report) ===
ntdll: threadpool.c:335: Test failed: expected info.userdata = 1, got 0 threadpool.c:336: Test failed: threadpool.c:178: expected info.threadid != 0, got 0 threadpool.c:338: Test failed: OpenThread failed with 87 threadpool.c:341: Test failed: QueueUserAPC failed with 6 threadpool.c:396: Test failed: WaitForSingleObject returned 258 threadpool.c:408: Test failed: threadpool.c:178: Running rtl_wait callback threadpool.c:427: Test failed: expected info.userdata = 1, got 0 threadpool.c:440: Test failed: expected info.userdata = 1, got 0 threadpool.c:454: Test failed: expected info.userdata = 1, got 0 threadpool.c:467: Test failed: expected info.userdata = 1, got 0
=== w1064v1507 (32 bit report) ===
ntdll: threadpool.c:334: Test failed: WaitForSingleObject returned 258 threadpool.c:1561: threadpool.c:1628: Test failed: WaitForSingleObject returned 258 threadpool.c:1629: Test failed: expected info.userdata = 0x10000, got 0 threadpool.c:1638: Test failed: WaitForSingleObject returned 0 threadpool.c:1640: Test failed: expected info.userdata = 0, got 65536 threadpool.c:1647: Test failed: expected info.userdata = 0, got 65536 threadpool.c:1749: Test failed: WaitForSingleObject returned 258 threadpool.c:1757: Test failed: threadpool.c:1561: Running wait callback threadpool.c:1764: Test failed: expected info.userdata = 0x10000, got 131072 threadpool.c:1561: threadpool.c:1782: Test failed: expected info.userdata = 1, got 0
=== w1064v1809 (32 bit report) ===
ntdll: threadpool.c:322: Test failed: WaitForSingleObject returned 258 threadpool.c:335: Test failed: expected info.userdata = 1, got 0
=== w1064v1809_2scr (32 bit report) ===
ntdll: threadpool.c:334: Test failed: WaitForSingleObject returned 258 threadpool.c:349: Test failed: WaitForSingleObject returned 258 threadpool.c:386: Test failed: expected info.userdata = 0x10000, got 0 threadpool.c:397: Test failed: expected info.userdata = 0x10000, got 0 threadpool.c:408: Test failed: expected info.userdata = 0x10000, got 0 threadpool.c:178: threadpool.c:427: Test failed: Running rtl_wait callback threadpool.c:440: Test failed: expected info.userdata = 1, got 0 threadpool.c:454: Test failed: expected info.userdata = 1, got 0 threadpool.c:467: Test failed: expected info.userdata = 1, got 0
=== w1064v1809_ar (32 bit report) ===
ntdll: threadpool.c:1628: Test failed: WaitForSingleObject returned 258 threadpool.c:1638: Test failed: WaitForSingleObject returned 0
=== w1064v1809_ja (32 bit report) ===
ntdll: threadpool.c:322: Test failed: WaitForSingleObject returned 258 threadpool.c:335: Test failed: expected info.userdata = 1, got 0 threadpool.c:336: Test failed: threadpool.c:178: Running rtl_wait callback threadpool.c:1628: Test failed: WaitForSingleObject returned 258 threadpool.c:1638: Test failed: WaitForSingleObject returned 0
=== w1064v1809_zh_CN (32 bit report) ===
ntdll: threadpool.c:322: Test failed: WaitForSingleObject returned 258 threadpool.c:335: Test failed: expected info.userdata = 1, got 0 threadpool.c:336: Test failed: expected info.threadid != 0, got 0 threadpool.c:338: Test failed: OpenThread failed with 87 threadpool.c:341: Test failed: QueueUserAPC failed with 6
=== w864 (64 bit report) ===
ntdll: threadpool.c:178: threadpool.c:385: Test failed: WaitForSingleObject returned 258 threadpool.c:386: Test failed: expected info.userdata = 0x10000, got 0 threadpool.c:389: Test failed: RtlDeregisterWaitEx failed with status 103 threadpool.c:397: Test failed: threadpool.c:178: expected info.userdata = 0x10000, got 0 threadpool.c:408: Test failed: threadpool.c:178: Running rtl_wait callback threadpool.c:427: Test failed: expected info.userdata = 1, got 0 threadpool.c:440: Test failed: expected info.userdata = 1, got 0 threadpool.c:190: Test failed: expected 258, got 0 threadpool.c:467: Test failed: expected info.userdata = 1, got 0 threadpool.c:716: Test failed: expected userdata >> 16 != 0, got 0
=== w1064v1507 (64 bit report) ===
ntdll: threadpool.c:322: Test failed: WaitForSingleObject returned 258 threadpool.c:335: Test failed: expected info.userdata = 1, got 0 threadpool.c:336: Test failed: expected info.threadid != 0, got 0 threadpool.c:338: Test failed: OpenThread failed with 87 threadpool.c:341: Test failed: QueueUserAPC failed with 6 threadpool.c:1714: Test failed: WaitForSingleObject returned 258 threadpool.c:1725: Test failed: expected info.userdata = 0x10000, got 0 threadpool.c:1734: Test failed: WaitForSingleObject returned 0 threadpool.c:1740: Test failed: expected info.userdata = 0, got 65536 threadpool.c:1763: Test failed: WaitForSingleObject returned 258 threadpool.c:1781: Test failed: WaitForSingleObject returned 0 threadpool.c:1793: Test failed: WaitForSingleObject returned 258
=== w1064v1809 (64 bit report) ===
ntdll: threadpool.c:334: Test failed: WaitForSingleObject returned 258 threadpool.c:407: Test failed: WaitForSingleObject returned 258 threadpool.c:427: Test failed: expected info.userdata = 1, got 0 threadpool.c:440: Test failed: expected info.userdata = 1, got 0 threadpool.c:454: Test failed: expected info.userdata = 1, got 0 threadpool.c:467: Test failed: expected info.userdata = 1, got 0
On 4/11/20 12:58 AM, Marvin wrote:
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=69375
Your paranoid android.
None of these test failures are in the added tests (and a whitespace patch produces most of the same ones).
I do find it odd, however, that so many of these tests fail when run with the testbot, whereas none show up on test.winehq.org. Maybe it's a winetest vs testlauncher issue?
On Sat, 11 Apr 2020, Zebediah Figura wrote: [...]
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=69375
[...]
None of these test failures are in the added tests (and a whitespace patch produces most of the same ones).
I do find it odd, however, that so many of these tests fail when run with the testbot, whereas none show up on test.winehq.org. Maybe it's a winetest vs testlauncher issue?
That does not seem likely. Both WineTest and TestLauncher perform some checks to decide whether to run the test but then it's just CreateProcess()+WaitFor*Object():
https://source.winehq.org/git/wine.git/blob/HEAD:/programs/winetest/main.c#l... https://source.winehq.org/git/tools.git/blob/HEAD:/testbot/src/TestLauncher/...
WineTest does specify the current directory but that does not seem likely to cause wait timeouts (and in any case tests should work no matter what the initial cwd is).
I think it's more a difference in VM uptime: in the WineTest case the VM has been up and running for 5-10 minutes by the time it runs the ntdll tests, whereas in the standalone case it has been up for seconds at most.