Pass different values to callback function when using CreateThreadpoolWork/ SubmitThreadpoolWork

Viewed 237

I'm currently learning about the Threadpool API as part of the WinAPI. I successfully created a threadpool and used SubmitThreadpoolWork to execute worker threads. However, so far I'm only able to pass one static value to all the worker threads (specified as the 2. parameter of the CreateThreadpoolWork function). How can I pass different values to each worker thread?

My callback function:

void CALLBACK test(PTP_CALLBACK_INSTANCE Instance, PVOID Parameter, PTP_WORK Work) {

    Sleep(2000);
    int* val = (int*)Parameter;
    std::wcout << (*val) << std::endl;
}

My main function:

int main() {

    TP_CALLBACK_ENVIRON callBackEnviron;
    PTP_POOL pool = NULL;
    PTP_CLEANUP_GROUP cleanupgroup = NULL;
    PTP_WORK_CALLBACK workcallback = test;
    PTP_TIMER timer = NULL;
    PTP_WORK work = NULL;

    InitializeThreadpoolEnvironment(&callBackEnviron);
    pool = CreateThreadpool(NULL);
    SetThreadpoolThreadMaximum(pool, 8);
    cleanupgroup = CreateThreadpoolCleanupGroup();
    SetThreadpoolCallbackPool(&callBackEnviron, pool);
    SetThreadpoolCallbackCleanupGroup(&callBackEnviron, cleanupgroup, NULL);

    for (int i = 0; i < 20; ++i)
    {
        work = CreateThreadpoolWork(workcallback, &i, &callBackEnviron);
        SubmitThreadpoolWork(work);
    }

    WaitForThreadpoolWorkCallbacks(work, FALSE);
    CloseThreadpoolWork(work);
    CloseThreadpoolCleanupGroupMembers(cleanupgroup, FALSE, NULL);
    CloseThreadpoolCleanupGroup(cleanupgroup);
    CloseThreadpool(pool);
}

Note that every worker thread outputs "20" which is the final value of i before the for-loop is finished. However, I want each worker thread to output the value of i when the respective SubmitThreadpoolWork call is executed. I'm also fairly certain that CreateThreadpoolWork is not supposed to be called in a loop, but I wasn't sure on how to attempt it otherwise.

1 Answers

You are passing each work item a pointer to your loop's counter variable. That variable will go out of scope and be destroyed when the loop finishes. So, any workers that run before that time will be acting on shared memory, and any workers that run after that time will be acting on a dangling pointer to invalid memory, which is undefined behavior.

You need to either:

  • dynamically allocate a new variable for each worker, and free the variable inside of your callback:
void CALLBACK test(PTP_CALLBACK_INSTANCE Instance, PVOID Parameter, PTP_WORK Work) {
    Sleep(2000);
    int* val = static_cast<int*>(Parameter);
    std::wcout << *val << std::endl;
    delete val;
}

for (int i = 0; i < 20; ++i)
{
    int *val = new int(i);
    work = CreateThreadpoolWork(workcallback, val, &CallBackEnviron);
    if (work)
        SubmitThreadpoolWork(work);
    else
        delete val;
}
  • type-cast the value of the integer into a pointer, then type-cast it back into an integer inside of your callback:
void CALLBACK test(PTP_CALLBACK_INSTANCE Instance, PVOID Parameter, PTP_WORK Work) {
    Sleep(2000);
    int val = reinterpret_cast<int>(Parameter);
    std::wcout << val << std::endl;
}

for (int i = 0; i < 20; ++i)
{
    work = CreateThreadpoolWork(workcallback, reinterpret_cast<void*>(i), &CallBackEnviron);
    if (work)
        SubmitThreadpoolWork(work);
}
Related