From: Francois Gouget fgouget@codeweavers.com
--- dlls/ntdll/threadpool.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/threadpool.c b/dlls/ntdll/threadpool.c index 99525f831e1..c50983e83d4 100644 --- a/dlls/ntdll/threadpool.c +++ b/dlls/ntdll/threadpool.c @@ -1152,7 +1152,8 @@ static NTSTATUS tp_new_worker_thread( struct threadpool *pool ) HANDLE thread; NTSTATUS status;
- status = RtlCreateUserThread( GetCurrentProcess(), NULL, FALSE, 0, 0, 0, + status = RtlCreateUserThread( GetCurrentProcess(), NULL, FALSE, 0, + pool->stack_info.StackReserve, pool->stack_info.StackCommit, threadpool_worker_proc, pool, &thread, NULL ); if (status == STATUS_SUCCESS) {
From: Francois Gouget fgouget@codeweavers.com
--- dlls/ntdll/tests/threadpool.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/tests/threadpool.c b/dlls/ntdll/tests/threadpool.c index 008755e6e5d..c2991a2ad57 100644 --- a/dlls/ntdll/tests/threadpool.c +++ b/dlls/ntdll/tests/threadpool.c @@ -33,15 +33,17 @@ static VOID (WINAPI *pTpCallbackReleaseSemaphoreOnCompletion)(TP_CALLBACK_IN 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 NTSTATUS (WINAPI *pTpQueryPoolStackInformation)(TP_POOL *,TP_POOL_STACK_INFORMATION *); static VOID (WINAPI *pTpReleaseCleanupGroup)(TP_CLEANUP_GROUP *); static VOID (WINAPI *pTpReleaseCleanupGroupMembers)(TP_CLEANUP_GROUP *,BOOL,PVOID); static void (WINAPI *pTpReleaseIoCompletion)(TP_IO *); static VOID (WINAPI *pTpReleasePool)(TP_POOL *); static VOID (WINAPI *pTpReleaseTimer)(TP_TIMER *); +static VOID (WINAPI *pTpReleaseWait)(TP_WAIT *); static VOID (WINAPI *pTpReleaseWork)(TP_WORK *); static VOID (WINAPI *pTpSetPoolMaxThreads)(TP_POOL *,DWORD); +static NTSTATUS (WINAPI *pTpSetPoolStackInformation)(TP_POOL *,TP_POOL_STACK_INFORMATION *); static VOID (WINAPI *pTpSetTimer)(TP_TIMER *,LARGE_INTEGER *,LONG,LONG); static VOID (WINAPI *pTpSetWait)(TP_WAIT *,HANDLE,LARGE_INTEGER *); static NTSTATUS (WINAPI *pTpSimpleTryPost)(PTP_SIMPLE_CALLBACK,PVOID,TP_CALLBACK_ENVIRON *); @@ -80,6 +82,7 @@ static BOOL init_threadpool(void) GET_PROC(TpDisassociateCallback); GET_PROC(TpIsTimerSet); GET_PROC(TpPostWork); + GET_PROC(TpQueryPoolStackInformation); GET_PROC(TpReleaseCleanupGroup); GET_PROC(TpReleaseCleanupGroupMembers); GET_PROC(TpReleaseIoCompletion); @@ -88,6 +91,7 @@ static BOOL init_threadpool(void) GET_PROC(TpReleaseWait); GET_PROC(TpReleaseWork); GET_PROC(TpSetPoolMaxThreads); + GET_PROC(TpSetPoolStackInformation); GET_PROC(TpSetTimer); GET_PROC(TpSetWait); GET_PROC(TpSimpleTryPost); @@ -575,6 +579,7 @@ static void CALLBACK simple2_cb(TP_CALLBACK_INSTANCE *instance, void *userdata)
static void test_tp_simple(void) { + TP_POOL_STACK_INFORMATION stack_info; TP_CALLBACK_ENVIRON environment; TP_CALLBACK_ENVIRON_V3 environment3; TP_CLEANUP_GROUP *group; @@ -678,6 +683,22 @@ static void test_tp_simple(void) pTpReleaseCleanupGroupMembers(group, TRUE, NULL); ok(userdata < 100, "expected userdata < 100, got %lu\n", userdata);
+ /* test querying and setting the stack size */ + status = pTpQueryPoolStackInformation(pool, &stack_info); + ok(!status, "TpQueryPoolStackInformation failed: %lx\n", status); + ok(stack_info.StackReserve == 2 * 1024 * 1024, "expected default StackReserve, got %ld\n", (ULONG)stack_info.StackReserve); + ok(stack_info.StackCommit == 4 * 1024, "expected default StackCommit, got %ld\n", (ULONG)stack_info.StackCommit); + + /* threadpool does not validate the stack size values */ + stack_info.StackReserve = stack_info.StackCommit = 1; + status = pTpSetPoolStackInformation(pool, &stack_info); + ok(!status, "TpQueryPoolStackInformation failed: %lx\n", status); + + status = pTpQueryPoolStackInformation(pool, &stack_info); + ok(!status, "TpQueryPoolStackInformation failed: %lx\n", status); + ok(stack_info.StackReserve == 1, "expected 1 byte StackReserve, got %ld\n", (ULONG)stack_info.StackReserve); + ok(stack_info.StackCommit == 1, "expected 1 byte StackCommit, got %ld\n", (ULONG)stack_info.StackCommit); + /* cleanup */ pTpReleaseCleanupGroup(group); pTpReleasePool(pool);
From: Francois Gouget fgouget@codeweavers.com
This test creates 512 threads so the default 2 MB stack size requires 1 GB which can cause out-of-virtual-memory-space issues. So cause threadpool to use the smallest possible stack size, which should be 1 MB, halving the virtual memory usage. --- dlls/ntdll/tests/threadpool.c | 6 ++++++ 1 file changed, 6 insertions(+)
diff --git a/dlls/ntdll/tests/threadpool.c b/dlls/ntdll/tests/threadpool.c index c2991a2ad57..0fd10ad196f 100644 --- a/dlls/ntdll/tests/threadpool.c +++ b/dlls/ntdll/tests/threadpool.c @@ -1898,6 +1898,7 @@ static void CALLBACK multi_wait_cb(TP_CALLBACK_INSTANCE *instance, void *userdat
static void test_tp_multi_wait(void) { + TP_POOL_STACK_INFORMATION stack_info; TP_CALLBACK_ENVIRON environment; HANDLE semaphores[512]; TP_WAIT *waits[512]; @@ -1917,6 +1918,11 @@ static void test_tp_multi_wait(void) status = pTpAllocPool(&pool, NULL); ok(!status, "TpAllocPool failed with status %lx\n", status); ok(pool != NULL, "expected pool != NULL\n"); + /* many threads -> use the smallest stack possible */ + stack_info.StackReserve = 256 * 1024; + stack_info.StackCommit = 4 * 1024; + status = pTpSetPoolStackInformation(pool, &stack_info); + ok(!status, "TpQueryPoolStackInformation failed: %lx\n", status);
memset(&environment, 0, sizeof(environment)); environment.Version = 1;
The ole32:clipboard failure in the GitLab CI is unrelated (bug 54005).
FWIW I suspect TP multi waits in recent versions of Windows are implemented in terms of an undocumented NT object type called "wait completion packets," which I noticed in the list of NTDLL exports (`NtCreateWaitCompletionPacket` and the like). See also: https://devblogs.microsoft.com/oldnewthing/20220406-00/?p=106434
FWIW I suspect TP multi waits in recent versions of Windows are implemented in terms of an undocumented NT object type called "wait completion packets," which I noticed in the list of NTDLL exports (`NtCreateWaitCompletionPacket` and the like). See also: https://devblogs.microsoft.com/oldnewthing/20220406-00/?p=106434
I don't think that's relevant here. The problem isn't the wait threads, it's the callback threads.
Minimizing the stack in the last commit is meant to help with bug 54064 which I should have noted somewhere: https://bugs.winehq.org/show_bug.cgi?id=54064
I have not confirmed that it helps with bug 54064 since I don't think I can actually reproduce that failure mode here. But if it makes sense maybe it can be committed anyway and then we'll see if the failure goes away.
Also I submitted the first two patches in MR!2182 since they can be useful independently from the last commit (it's just tests and actually using the stack size set by TpSetPoolStackInformation()). https://gitlab.winehq.org/wine/wine/-/merge_requests/2182