I am developing a C application in Visual Studio 2022, using winapi. I have some issues regarding logging (for debug purposes) from callbacks when using winapi thread pools. Below is a minimal example (without error handling, for shorter code):
VOID CALLBACK WorkCallback(PTP_CALLBACK_INSTANCE instance, PVOID parameter, PTP_WORK work)
{
UNREFERENCED_PARAMETER(instance);
UNREFERENCED_PARAMETER(parameter);
UNREFERENCED_PARAMETER(work);
printf("Hello from WorkCallback\n\r");
}
VOID Test()
{
printf("Hello from Test\n\r");
TP_CALLBACK_ENVIRON callbackEnviron;
InitializeThreadpoolEnvironment(&callbackEnviron);
PTP_POOL pool = CreateThreadpool(NULL);
SetThreadpoolThreadMinimum(pool, 1);
SetThreadpoolThreadMaximum(pool, 1);
SetThreadpoolCallbackPool(&callbackEnviron, pool);
PTP_CLEANUP_GROUP cleanupGroup = CreateThreadpoolCleanupGroup();
SetThreadpoolCallbackCleanupGroup(&callbackEnviron, cleanupGroup, NULL);
PTP_WORK work = CreateThreadpoolWork(WorkCallback, NULL, &callbackEnviron);
SubmitThreadpoolWork(work);
CloseThreadpool(pool);
CloseThreadpoolCleanupGroupMembers(cleanupGroup, FALSE, NULL);
CloseThreadpoolCleanupGroup(cleanupGroup);
}
int CDECL
main()
{
Test();
getchar();
return 0;
}
When I execute this command without debugging, or with debugging but without breakpoints, the only output I get is Hello from Test. The only way I found to print Hello from WorkCallback is to put a breakpoint before SubmitThreadpoolWork
, another one in WorkCallback
, to step into SubmitThreadpoolWork
when debugging, and to use the Parallel Stacks window (Debug->Windows->Parallel Stacks).
What is odd to me is that if I simply put a breakpoint in WorkCallback
, it is not enough. And that behaviour under specific debug conditions is different than in regular execution. In some other SO questions I saw people pointing out that different threads might not be able to print to console, so I tried writing to files, but I observed the same behaviour as I described before.
Am I missing something related to Thread Pools? Why is this happening?
I suggest closing the pool at the very end of the provided sample. The CloseThreadpoolCleanupGroupMembers function waits for all the cleanup group members to terminate before releasing them. If you close the pool beforehand, as described, the callback function may never be invoked.