void OnCreatePool() { // Sanity checks if (g_pThreadPool != NULL) { OnDeletePool(); } // Initialize the private thread pool g_pThreadPool = CreateThreadpool(NULL); if (g_pThreadPool == NULL) { MessageBox( NULL, TEXT("Impossible to create private thread pool"), TEXT("Initialization error"), MB_ICONERROR | MB_OK ); return; } // Change min=2 / max=4 thread pool parameters SetThreadpoolThreadMaximum(g_pThreadPool, 4); if (!SetThreadpoolThreadMinimum(g_pThreadPool, 2)) { MessageBox( NULL, TEXT("SetThreadpoolThreadMinimum failed."), TEXT("Initialization error"), MB_ICONERROR | MB_OK ); return; } // Reset the callback environment // Note: this is an inline function from winbase.h // that calls TpInitializeCallbackEnviron() in winnt.h. // --> // CallbackEnviron->Version = 1; // CallbackEnviron->Pool = NULL; // CallbackEnviron->CleanupGroup = NULL; // CallbackEnviron->CleanupGroupCancelCallback = NULL; // CallbackEnviron->RaceDll = NULL; // CallbackEnviron->ActivationContext = NULL; // CallbackEnviron->FinalizationCallback = NULL; // CallbackEnviron->u.Flags = 0; InitializeThreadpoolEnvironment(&g_callbackEnvironment); // Associate our private thread pool with the callback environment // Note: this is an inline function from winbase.h // that calls TpSetCallbackThreadpool() in winnt.h. // --> // CallbackEnviron->Pool = Pool; SetThreadpoolCallbackPool(&g_callbackEnvironment, g_pThreadPool); }
int TestPoolThread(int argc, char* argv[]) { TP_POOL* pool; pool = CreateThreadpool(NULL); SetThreadpoolThreadMinimum(pool, 8); /* default is 0 */ SetThreadpoolThreadMaximum(pool, 64); /* default is 500 */ CloseThreadpool(pool); return 0; }
void CThreadPools::CreateThreadPools(int nThreadCount) { BOOL bRet = FALSE; InitializeThreadpoolEnvironment(&m_CallBackEnviron); // Create a custom, dedicated thread pool m_pPool = CreateThreadpool(NULL); if (NULL==m_pPool) { CloseThreadPools(); } m_nRollback = 1; // pool creation succeeded // The thread pool is made persistent simply by setting // both the minimum and maximum threads to 1. SetThreadpoolThreadMaximum(m_pPool, nThreadCount); bRet = SetThreadpoolThreadMinimum(m_pPool, 1); if (FALSE==bRet) { CloseThreadPools(); } //Create a cleanup group for this thread pool m_pCleanupGroup = CreateThreadpoolCleanupGroup(); if (NULL==m_pCleanupGroup) { CloseThreadPools(); } m_nRollback = 2; // Cleanup group creation succeeded // Associate the callback environment with our thread pool. SetThreadpoolCallbackPool(&m_CallBackEnviron, m_pPool); // Associate the cleanup group with our thread pool. // Objects created with the same callback environment // as the cleanup group become members of the cleanup group. SetThreadpoolCallbackCleanupGroup(&m_CallBackEnviron, m_pCleanupGroup, NULL); m_nRollback = 3; // Creation of work succeeded }
BOOL CTreadPool::Create( int nMaxThreads, int nMinThreads ) { m_pPool = CreateThreadpool(NULL); if (m_pPool==NULL) { OutputDebugString(TEXT("Create Thread Pool failed \n")); return FALSE; } m_nMinThreads = nMinThreads; m_nMaxThreads = nMaxThreads; SetThreadpoolThreadMaximum(m_pPool, nMaxThreads); SetThreadpoolThreadMinimum(m_pPool, nMinThreads); m_pTp_callback_environ = new TP_CALLBACK_ENVIRON; InitializeThreadpoolEnvironment(m_pTp_callback_environ); SetThreadpoolCallbackPool(m_pTp_callback_environ, m_pPool); return TRUE; }
__declspec(noinline) bool benchmark_ntp_fs_stat() { TP_CALLBACK_ENVIRON env; InitializeThreadpoolEnvironment(&env); PTP_POOL pool{nullptr}; pool = CreateThreadpool(nullptr); SetThreadpoolThreadMaximum(pool, 48); SetThreadpoolThreadMinimum(pool, 12); PTP_CLEANUP_GROUP group = CreateThreadpoolCleanupGroup(); SetThreadpoolCallbackPool(&env, pool); SetThreadpoolCallbackCleanupGroup(&env, group, nullptr); PTP_WORK work_fs_stat = CreateThreadpoolWork(WorkCallback_fs_stat, nullptr, &env); SubmitThreadpoolWork(work_fs_stat); WaitForThreadpoolWorkCallbacks(work_fs_stat, false); CloseThreadpoolWork(work_fs_stat); CloseThreadpool(pool); return false; }
HRESULT FxInterruptThreadpool::Initialize( ) { HRESULT hr = S_OK; DWORD error; BOOL bRet; // // Create a thread pool using win32 APIs // m_Pool = CreateThreadpool(NULL); if (m_Pool == NULL) { error = GetLastError(); hr = HRESULT_FROM_WIN32(error); DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP, "Threadpool creation failed, %!winerr!", error); return hr; } // // Set maximum thread count to equal the total number of interrupts. // Set minimum thread count (persistent threads) to equal the lower of // number of interrupt and number of processors. // // We want minimum number of persistent threads to be at least equal to the // number of interrupt objects so that there is no delay in servicing the // interrupt if there are processors available (the delay can come due to // thread pool delay in allocating and initializing a new thread). However, // there is not much benefit in having more persistent threads than // processors, as threads would have to wait for processor to become // available anyways. Therefore, we chose to have the number of persistent // equal to the Min(number of interrupts, number of processors). // // In the current design, if there are more interrupts than // processors, as soon as one thread finishes servicing the interrupt, both // the thread and processor will be available to service the next queued // interrupt. Note that thread pool will queue all the waits and will // satisfy them in a FIFO manner. // // Since we don't know the number of interrupts in the beginning, we will // start with one and update it when we know the actual number of interrupts // after processing driver's OnPrepareHardware callback. // // Note on interrupt servicing: // When fx connects the interrupt, it queues an event-based wait block // to thread pool for each interrupt, so that the interrupt can get serviced // using one of the thread pool threads when the auto-reset event shared // with reflector is set by reflector in the DpcForIsr routine queued by Isr. // While the interrupt is being serviced by one of the threads, no wait block // is queued to thread pool for that interrupt, so arrival of same interrupt // signals the event in DpcForIsr but doesn't cause new threads to pick up // the servicing. Note that other interrupts still have their wait blocks // queued so they will be serviced as they arrive. // // When previous servicing is over (i.e. the ISR callback has been // invoked and it has returned), Fx queues another wait block to thread pool // for that interrupt. If the event is already signalled, it would result in // the same thread (or another) picking up servicing immediately. // // This means the interrupt ISR routine can never run concurrently // for same interrupt. Therefore, there is no need to have more than one // thread for each interrupt. // SetThreadpoolThreadMaximum(m_Pool, 1); // // Create one persistent thread since atleast one interrupt is the most // likely scenario. We will update this number to have either the same // number of threads as there are interrupts, or the number of // processors on the machine. // bRet = SetThreadpoolThreadMinimum(m_Pool, m_MinimumThreadCount); if (bRet == FALSE) { error = GetLastError(); hr = HRESULT_FROM_WIN32(error); DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP, "%!FUNC!: Failed to set minimum threads (%d) in threadpool," " %!winerr!", m_MinimumThreadCount, error); goto cleanup; } // // Associate thread pool with callback environment. // SetThreadpoolCallbackPool(&m_CallbackEnvironment, m_Pool); cleanup: if (FAILED(hr)) { CloseThreadpool(m_Pool); m_Pool = NULL; } return hr; }
HRESULT FxInterruptThreadpool::UpdateThreadPoolThreadLimits( _In_ ULONG InterruptCount ) { BOOL bRet; HRESULT hr = S_OK; SYSTEM_INFO sysInfo; ULONG minThreadCount = 0; ULONG procs; DWORD error; FX_VERIFY(INTERNAL, CHECK_NOT_NULL(m_Pool)); // // if there are more than one interrupts then we need to update minimum // thread count. // if (m_MinimumThreadCount >= InterruptCount) { // // nothing to do // return S_OK; } // // We want to have number of minimum persistent threads // = Min(number of interrupts, number of processors). // See comments in Initialize routine for details. // GetSystemInfo(&sysInfo); procs = sysInfo.dwNumberOfProcessors; minThreadCount = min(InterruptCount, procs); if (m_MinimumThreadCount < minThreadCount) { // // Set threadpool min // bRet = SetThreadpoolThreadMinimum(m_Pool, minThreadCount); if (bRet == FALSE) { error = GetLastError(); hr = HRESULT_FROM_WIN32(error); DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP, "Failed to set minimum threads in threadpool," " TP_POOL 0x%p to %d %!winerr!", m_Pool, minThreadCount, error); return hr; } m_MinimumThreadCount = minThreadCount; } // // set thread pool max to max number of interrupts // SetThreadpoolThreadMaximum(m_Pool, InterruptCount); DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP, "Threads in thread pool TP_POOL 0x%p updated" " to Max %d Min %d threads", m_Pool, InterruptCount, minThreadCount); return hr; }
RFX_CONTEXT* rfx_context_new(void) { HKEY hKey; LONG status; DWORD dwType; DWORD dwSize; DWORD dwValue; SYSTEM_INFO sysinfo; RFX_CONTEXT* context; context = (RFX_CONTEXT*) malloc(sizeof(RFX_CONTEXT)); ZeroMemory(context, sizeof(RFX_CONTEXT)); context->priv = (RFX_CONTEXT_PRIV*) malloc(sizeof(RFX_CONTEXT_PRIV)); ZeroMemory(context->priv, sizeof(RFX_CONTEXT_PRIV)); context->priv->TilePool = Queue_New(TRUE, -1, -1); context->priv->TileQueue = Queue_New(TRUE, -1, -1); /* * align buffers to 16 byte boundary (needed for SSE/NEON instructions) * * y_r_buffer, cb_g_buffer, cr_b_buffer: 64 * 64 * 4 = 16384 (0x4000) * dwt_buffer: 32 * 32 * 2 * 2 * 4 = 16384, maximum sub-band width is 32 */ context->priv->BufferPool = BufferPool_New(TRUE, 16384, 16); #ifdef _WIN32 { BOOL isVistaOrLater; OSVERSIONINFOA verinfo; ZeroMemory(&verinfo, sizeof(OSVERSIONINFOA)); verinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); GetVersionExA(&verinfo); isVistaOrLater = ((verinfo.dwMajorVersion >= 6) && (verinfo.dwMinorVersion >= 0)) ? TRUE : FALSE; context->priv->UseThreads = isVistaOrLater; } #else context->priv->UseThreads = TRUE; #endif GetNativeSystemInfo(&sysinfo); context->priv->MinThreadCount = sysinfo.dwNumberOfProcessors; context->priv->MaxThreadCount = 0; status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\FreeRDP\\RemoteFX"), 0, KEY_READ | KEY_WOW64_64KEY, &hKey); if (status == ERROR_SUCCESS) { if (RegQueryValueEx(hKey, _T("UseThreads"), NULL, &dwType, (BYTE*) &dwValue, &dwSize) == ERROR_SUCCESS) context->priv->UseThreads = dwValue ? 1 : 0; if (RegQueryValueEx(hKey, _T("MinThreadCount"), NULL, &dwType, (BYTE*) &dwValue, &dwSize) == ERROR_SUCCESS) context->priv->MinThreadCount = dwValue; if (RegQueryValueEx(hKey, _T("MaxThreadCount"), NULL, &dwType, (BYTE*) &dwValue, &dwSize) == ERROR_SUCCESS) context->priv->MaxThreadCount = dwValue; RegCloseKey(hKey); } if (context->priv->UseThreads) { /* Call primitives_get here in order to avoid race conditions when using primitives_get */ /* from multiple threads. This call will initialize all function pointers correctly */ /* before any decoding threads are started */ primitives_get(); context->priv->ThreadPool = CreateThreadpool(NULL); InitializeThreadpoolEnvironment(&context->priv->ThreadPoolEnv); SetThreadpoolCallbackPool(&context->priv->ThreadPoolEnv, context->priv->ThreadPool); if (context->priv->MinThreadCount) SetThreadpoolThreadMinimum(context->priv->ThreadPool, context->priv->MinThreadCount); if (context->priv->MaxThreadCount) SetThreadpoolThreadMaximum(context->priv->ThreadPool, context->priv->MaxThreadCount); } /* initialize the default pixel format */ rfx_context_set_pixel_format(context, RDP_PIXEL_FORMAT_B8G8R8A8); /* create profilers for default decoding routines */ rfx_profiler_create(context); /* set up default routines */ context->quantization_decode = rfx_quantization_decode; context->quantization_encode = rfx_quantization_encode; context->dwt_2d_decode = rfx_dwt_2d_decode; context->dwt_2d_encode = rfx_dwt_2d_encode; RFX_INIT_SIMD(context); return context; }
int TestPoolWork(int argc, char* argv[]) { int index; PTP_POOL pool; PTP_WORK work; PTP_CLEANUP_GROUP cleanupGroup; TP_CALLBACK_ENVIRON environment; printf("Global Thread Pool\n"); work = CreateThreadpoolWork((PTP_WORK_CALLBACK) test_WorkCallback, "world", NULL); if (!work) { printf("CreateThreadpoolWork failure\n"); return -1; } /** * You can post a work object one or more times (up to MAXULONG) without waiting for prior callbacks to complete. * The callbacks will execute in parallel. To improve efficiency, the thread pool may throttle the threads. */ for (index = 0; index < 10; index++) SubmitThreadpoolWork(work); WaitForThreadpoolWorkCallbacks(work, FALSE); CloseThreadpoolWork(work); printf("Private Thread Pool\n"); if (!(pool = CreateThreadpool(NULL))) { printf("CreateThreadpool failure\n"); return -1; } if (!SetThreadpoolThreadMinimum(pool, 4)) { printf("SetThreadpoolThreadMinimum failure\n"); return -1; } SetThreadpoolThreadMaximum(pool, 8); InitializeThreadpoolEnvironment(&environment); SetThreadpoolCallbackPool(&environment, pool); cleanupGroup = CreateThreadpoolCleanupGroup(); if (!cleanupGroup) { printf("CreateThreadpoolCleanupGroup failure\n"); return -1; } SetThreadpoolCallbackCleanupGroup(&environment, cleanupGroup, NULL); work = CreateThreadpoolWork((PTP_WORK_CALLBACK) test_WorkCallback, "world", &environment); if (!work) { printf("CreateThreadpoolWork failure\n"); return -1; } for (index = 0; index < 10; index++) SubmitThreadpoolWork(work); WaitForThreadpoolWorkCallbacks(work, FALSE); CloseThreadpoolCleanupGroupMembers(cleanupGroup, TRUE, NULL); CloseThreadpoolCleanupGroup(cleanupGroup); DestroyThreadpoolEnvironment(&environment); /** * See Remarks at https://msdn.microsoft.com/en-us/library/windows/desktop/ms682043(v=vs.85).aspx * If there is a cleanup group associated with the work object, * it is not necessary to call CloseThreadpoolWork ! * calling the CloseThreadpoolCleanupGroupMembers function releases the work, wait, * and timer objects associated with the cleanup group. */ /* CloseThreadpoolWork(work); // this would segfault, see comment above. */ CloseThreadpool(pool); return 0; }
RFX_CONTEXT* rfx_context_new(BOOL encoder) { HKEY hKey; LONG status; DWORD dwType; DWORD dwSize; DWORD dwValue; SYSTEM_INFO sysinfo; RFX_CONTEXT* context; wObject *pool; RFX_CONTEXT_PRIV *priv; context = (RFX_CONTEXT*)calloc(1, sizeof(RFX_CONTEXT)); if (!context) return NULL; context->encoder = encoder; context->priv = priv = (RFX_CONTEXT_PRIV *)calloc(1, sizeof(RFX_CONTEXT_PRIV) ); if (!priv) goto error_priv; WLog_Init(); priv->log = WLog_Get("com.freerdp.codec.rfx"); WLog_OpenAppender(priv->log); #ifdef WITH_DEBUG_RFX WLog_SetLogLevel(priv->log, WLOG_DEBUG); #endif priv->TilePool = ObjectPool_New(TRUE); if (!priv->TilePool) goto error_tilePool; pool = ObjectPool_Object(priv->TilePool); pool->fnObjectInit = (OBJECT_INIT_FN) rfx_tile_init; if (context->encoder) { pool->fnObjectNew = (OBJECT_NEW_FN) rfx_encoder_tile_new; pool->fnObjectFree = (OBJECT_FREE_FN) rfx_encoder_tile_free; } else { pool->fnObjectNew = (OBJECT_NEW_FN) rfx_decoder_tile_new; pool->fnObjectFree = (OBJECT_FREE_FN) rfx_decoder_tile_free; } /* * align buffers to 16 byte boundary (needed for SSE/NEON instructions) * * y_r_buffer, cb_g_buffer, cr_b_buffer: 64 * 64 * sizeof(INT16) = 8192 (0x2000) * dwt_buffer: 32 * 32 * 2 * 2 * sizeof(INT16) = 8192, maximum sub-band width is 32 * * Additionally we add 32 bytes (16 in front and 16 at the back of the buffer) * in order to allow optimized functions (SEE, NEON) to read from positions * that are actually in front/beyond the buffer. Offset calculations are * performed at the BufferPool_Take function calls in rfx_encode/decode.c. * * We then multiply by 3 to use a single, partioned buffer for all 3 channels. */ priv->BufferPool = BufferPool_New(TRUE, (8192 + 32) * 3, 16); if (!priv->BufferPool) goto error_BufferPool; #ifdef _WIN32 { BOOL isVistaOrLater; OSVERSIONINFOA verinfo; ZeroMemory(&verinfo, sizeof(OSVERSIONINFOA)); verinfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); GetVersionExA(&verinfo); isVistaOrLater = ((verinfo.dwMajorVersion >= 6) && (verinfo.dwMinorVersion >= 0)) ? TRUE : FALSE; priv->UseThreads = isVistaOrLater; } #else priv->UseThreads = TRUE; #endif GetNativeSystemInfo(&sysinfo); priv->MinThreadCount = sysinfo.dwNumberOfProcessors; priv->MaxThreadCount = 0; status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\FreeRDP\\RemoteFX"), 0, KEY_READ | KEY_WOW64_64KEY, &hKey); if (status == ERROR_SUCCESS) { dwSize = sizeof(dwValue); if (RegQueryValueEx(hKey, _T("UseThreads"), NULL, &dwType, (BYTE*) &dwValue, &dwSize) == ERROR_SUCCESS) priv->UseThreads = dwValue ? 1 : 0; if (RegQueryValueEx(hKey, _T("MinThreadCount"), NULL, &dwType, (BYTE*) &dwValue, &dwSize) == ERROR_SUCCESS) priv->MinThreadCount = dwValue; if (RegQueryValueEx(hKey, _T("MaxThreadCount"), NULL, &dwType, (BYTE*) &dwValue, &dwSize) == ERROR_SUCCESS) priv->MaxThreadCount = dwValue; RegCloseKey(hKey); } if (priv->UseThreads) { /* Call primitives_get here in order to avoid race conditions when using primitives_get */ /* from multiple threads. This call will initialize all function pointers correctly */ /* before any decoding threads are started */ primitives_get(); priv->ThreadPool = CreateThreadpool(NULL); if (!priv->ThreadPool) goto error_threadPool; InitializeThreadpoolEnvironment(&priv->ThreadPoolEnv); SetThreadpoolCallbackPool(&priv->ThreadPoolEnv, priv->ThreadPool); if (priv->MinThreadCount) if (!SetThreadpoolThreadMinimum(priv->ThreadPool, priv->MinThreadCount)) goto error_threadPool_minimum; if (priv->MaxThreadCount) SetThreadpoolThreadMaximum(priv->ThreadPool, priv->MaxThreadCount); } /* initialize the default pixel format */ rfx_context_set_pixel_format(context, RDP_PIXEL_FORMAT_B8G8R8A8); /* create profilers for default decoding routines */ rfx_profiler_create(context); /* set up default routines */ context->quantization_decode = rfx_quantization_decode; context->quantization_encode = rfx_quantization_encode; context->dwt_2d_decode = rfx_dwt_2d_decode; context->dwt_2d_encode = rfx_dwt_2d_encode; RFX_INIT_SIMD(context); context->state = RFX_STATE_SEND_HEADERS; return context; error_threadPool_minimum: CloseThreadpool(priv->ThreadPool); error_threadPool: BufferPool_Free(priv->BufferPool); error_BufferPool: ObjectPool_Free(priv->TilePool); error_tilePool: free(priv); error_priv: free(context); return NULL; }
int TestPoolWork(int argc, char* argv[]) { int index; PTP_POOL pool; PTP_WORK work; PTP_CLEANUP_GROUP cleanupGroup; TP_CALLBACK_ENVIRON environment; printf("Global Thread Pool\n"); work = CreateThreadpoolWork((PTP_WORK_CALLBACK) test_WorkCallback, "world", NULL); if (!work) { printf("CreateThreadpoolWork failure\n"); return -1; } /** * You can post a work object one or more times (up to MAXULONG) without waiting for prior callbacks to complete. * The callbacks will execute in parallel. To improve efficiency, the thread pool may throttle the threads. */ for (index = 0; index < 10; index++) SubmitThreadpoolWork(work); WaitForThreadpoolWorkCallbacks(work, FALSE); CloseThreadpoolWork(work); printf("Private Thread Pool\n"); pool = CreateThreadpool(NULL); SetThreadpoolThreadMinimum(pool, 4); SetThreadpoolThreadMaximum(pool, 8); InitializeThreadpoolEnvironment(&environment); SetThreadpoolCallbackPool(&environment, pool); cleanupGroup = CreateThreadpoolCleanupGroup(); if (!cleanupGroup) { printf("CreateThreadpoolCleanupGroup failure\n"); return -1; } SetThreadpoolCallbackCleanupGroup(&environment, cleanupGroup, NULL); work = CreateThreadpoolWork((PTP_WORK_CALLBACK) test_WorkCallback, "world", &environment); if (!work) { printf("CreateThreadpoolWork failure\n"); return -1; } for (index = 0; index < 10; index++) SubmitThreadpoolWork(work); WaitForThreadpoolWorkCallbacks(work, FALSE); CloseThreadpoolCleanupGroupMembers(cleanupGroup, TRUE, NULL); CloseThreadpoolCleanupGroup(cleanupGroup); DestroyThreadpoolEnvironment(&environment); CloseThreadpoolWork(work); CloseThreadpool(pool); return 0; }
int _tmain1(int argc, _TCHAR* argv[]) { if (depth == 0) { Display("%d(%d)************\n", GetCurrentProcessId(), GetCurrentThreadId()); return 1; } int multiply = 1; if (argc > 2) { multiply = _ttoi(argv[2]); if (multiply < 1) { Display("multiply should be greater than 1\n"); return -2; } } TP_CALLBACK_ENVIRON CallBackEnviron; InitializeThreadpoolEnvironment(&CallBackEnviron); PTP_POOL pool = CreateThreadpool(NULL); if (NULL == pool) { _tprintf(_T("CreateThreadpool failed. LastError: %u\n"), GetLastError()); } SetThreadpoolThreadMaximum(pool, 100); BOOL bRet = SetThreadpoolThreadMinimum(pool, 20); if (!bRet) { _tprintf(_T("SetThreadpoolThreadMinimum failed. LastError: %u\n"), GetLastError()); } PTP_CLEANUP_GROUP cleanupgroup = CreateThreadpoolCleanupGroup(); if (NULL == cleanupgroup) { _tprintf(_T("CreateThreadpoolCleanupGroup failed. LastError: %u\n"), GetLastError()); } SetThreadpoolCallbackPool(&CallBackEnviron, pool); SetThreadpoolCallbackCleanupGroup(&CallBackEnviron, cleanupgroup, NULL); struct ProcSetup { PROCESS_INFORMATION pi; HANDLE hEvent; HANDLE hWait; HANDLE hOutputRead; HANDLE hErrorRead; HANDLE hInputWrite; int rc; ProcSetup(PROCESS_INFORMATION _pi, HANDLE _hEvent, HANDLE _hWait, HANDLE _hOutputRead, HANDLE _hErrorRead, HANDLE _hInputWrite) : pi(_pi), hEvent(_hEvent), hWait(_hWait), hOutputRead(_hOutputRead), hErrorRead(_hErrorRead), hInputWrite(_hInputWrite) {} }; ProcSetup* pss = (ProcSetup*)calloc(multiply, sizeof(ProcSetup)); for (int i = 0; i < multiply; i++) { Display("%d(%d) Multiply %d out of %d\n", GetCurrentProcessId(), GetCurrentThreadId(), i, multiply); HANDLE hOutputRead, hOutputWrite; HANDLE hErrorRead, hErrorWrite; HANDLE hInputRead, hInputWrite; SECURITY_ATTRIBUTES sa; // Set up the security attributes struct. sa.nLength= sizeof(SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; STARTUPINFO startup_info; ZeroMemory(&startup_info, sizeof(startup_info)); startup_info.cb = sizeof(startup_info); startup_info.dwFlags = STARTF_USESTDHANDLES; PROCESS_INFORMATION process_info; ZeroMemory(&process_info, sizeof(process_info)); TCHAR cmd[1024]; _stprintf(cmd, _T("%s %d"), argv[0], depth-1); BOOL result = CreateProcess(NULL, // ApplicationName cmd, NULL, // ProcessAttributes NULL, // ThreadAttributes TRUE, // InheritHandles CREATE_SUSPENDED, // | CREATE_BREAKAWAY_FROM_JOB, // CreationFlags NULL, NULL, &startup_info, &process_info); pss[i].hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); pss[i].pi = process_info; PTP_WAIT myWait = CreateThreadpoolWait(callback, new WaiterParams(process_info, pss[i].hEvent, &pss[i].rc), &CallBackEnviron); SetThreadpoolWait(myWait, process_info.hProcess, NULL); ResumeThread(process_info.hThread); } HANDLE* events = (HANDLE*)calloc(multiply, sizeof(HANDLE)); for (int i = 0; i < multiply; i++) { events[i] = pss[i].hEvent; } WaitForMultipleObjects(multiply, events, true, INFINITE); int exit_code; for (int i = 0; i < multiply; i++) { PROCESS_INFORMATION* process_info = &pss[i].pi; int texit_code; BOOL ok = GetExitCodeThread(process_info->hThread, reinterpret_cast<DWORD*>(&texit_code)); if (!ok) { printf("*** GetExitCodeThread failed %d\n", GetLastError()); } ok = GetExitCodeProcess(process_info->hProcess, reinterpret_cast<DWORD*>(&exit_code)); if (!ok) { printf("*** GetExitCodeProcess failed %d\n", GetLastError()); } if (texit_code != exit_code) { printf("*** Thread %d exit code %x didn't match process %d exit code %x\n", process_info->dwThreadId, texit_code, process_info->dwProcessId, exit_code); } CloseHandle(process_info->hProcess); CloseHandle(process_info->hThread); // *wp->pRC = exit_code; // exit_code = pss[i].rc; if (exit_code != 1) { printf("Failed to get correct exit code. Got %d instead\n", exit_code); } } for (int i = 0; i < multiply; i++) { CloseHandle(pss[i].hEvent); } free(events); CloseThreadpoolCleanupGroupMembers(cleanupgroup, FALSE, NULL); CloseThreadpoolCleanupGroup(cleanupgroup); CloseThreadpool(pool); DestroyThreadpoolEnvironment(&CallBackEnviron); return exit_code; }