Example #1
0
HRESULT HttpListenerInitializeThreadPool(PHTTP_LISTENER listener)
{	
	DWORD dwResult;

	TP_CALLBACK_ENVIRON tpCalbackEnviron;
	PTP_POOL pThreadPool;	

	//I nitialize the threadPool environment
	ZeroMemory(&( tpCalbackEnviron ), sizeof( TP_CALLBACK_ENVIRON ) );  
	InitializeThreadpoolEnvironment(&tpCalbackEnviron);

	// Create the threadpool
	pThreadPool = CreateThreadpool(NULL);
	if(pThreadPool == NULL)
	{
		return HRESULT_FROM_WIN32(GetLastError());
	}
	else
	{
		SetThreadpoolCallbackPool(&tpCalbackEnviron, pThreadPool);	
		dwResult = HRESULT_FROM_WIN32(GetLastError());
	}

	listener->pThreadPool = pThreadPool;
	listener->errorCode = dwResult;
	return dwResult;
}
Example #2
0
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);  
}
Example #3
0
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;
}
Example #4
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

}
Example #5
0
bool test_new_thread_pool() {//success is true, failure is false
	int sockErr;

	WSADATA lpWSAData={0};
	sockErr=WSAStartup(0x202, &lpWSAData);
	sListen=WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED);
	if(INVALID_SOCKET==sListen){
		DebugBreak();
	}
	GUID GuidDisconnectEx = WSAID_DISCONNECTEX;
	DWORD dwBytes;
	WSAIoctl(sListen, SIO_GET_EXTENSION_FUNCTION_POINTER,
             &GuidDisconnectEx, sizeof (GuidDisconnectEx), 
             &DisconnectEx, sizeof (DisconnectEx), 
             &dwBytes, NULL, NULL);
	

	InitializeThreadpoolEnvironment(&tEnvrion);
	pMainPool=CreateThreadpool(NULL);
	pCleanup=CreateThreadpoolCleanupGroup();
	SetThreadpoolCallbackCleanupGroup(&tEnvrion, pCleanup, 0);
	SetThreadpoolCallbackPool(&tEnvrion, pMainPool);

	pListen=CreateThreadpoolIo((HANDLE)sListen, acceptingIoThreadProc, 0, &tEnvrion);

	sockaddr_in service={0};
	service.sin_family=AF_INET;
	service.sin_port=htons(8080);

	sockErr=bind(sListen, (SOCKADDR *) &service, sizeof(service));
	if(SOCKET_ERROR==sockErr){
		DebugBreak();
	}
	sockErr=listen(sListen, SOMAXCONN);
	if(SOCKET_ERROR==sockErr){
		DebugBreak();
	}
	for(int i=0; i<numSockets; i++){
		ntpAddAcceptSocket(sListen);
	}
	OutputDebugString(TEXT("CloseThreadpoolCleanupGroupMembers waiting\n"));
	SleepEx(INFINITE, TRUE);
	CloseThreadpoolCleanupGroupMembers(pCleanup, FALSE, NULL);
	OutputDebugString(TEXT("CloseThreadpoolCleanupGroupMembers done\n"));
	
	return false;
}
UINT32 HlprThreadPoolDataPopulate(_Inout_ THREADPOOL_DATA* pThreadPoolData,
                                  _In_ const PTP_CLEANUP_GROUP_CANCEL_CALLBACK pGroupCancelFn)
{
   ASSERT(pThreadPoolData);

   UINT32 status = NO_ERROR;

   InitializeThreadpoolEnvironment(&(pThreadPoolData->callbackEnvironment));

   pThreadPoolData->pThreadPool = CreateThreadpool(0);
   if(pThreadPoolData->pThreadPool == 0)
   {
      status = GetLastError();

      HlprLogError(L"HlprThreadPoolDataPopulate : CreateThreadPool() [status: %#x]",
                   status);

      HLPR_BAIL;
   }

   pThreadPoolData->pCleanupGroup = CreateThreadpoolCleanupGroup();
   if(pThreadPoolData->pCleanupGroup == 0)
   {
      status = GetLastError();

      HlprLogError(L"HlprThreadPoolDataPopulate : CreateThreadPool() [status: %#x]",
                   status);

      HLPR_BAIL;
   }  

   SetThreadpoolCallbackPool(&(pThreadPoolData->callbackEnvironment),
                             pThreadPoolData->pThreadPool);

   SetThreadpoolCallbackCleanupGroup(&(pThreadPoolData->callbackEnvironment),
                                     pThreadPoolData->pCleanupGroup,
                                     pGroupCancelFn);

   HLPR_BAIL_LABEL:

   if(status != NO_ERROR)
      HlprThreadPoolDataPurge(pThreadPoolData);

   return status;
}
Example #7
0
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;
}
Example #8
0
__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;
}
Example #10
0
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;
}
Example #11
0
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;
}
Example #12
0
void VSyncWin::VSyncInit()
{
	Self = this;

	CoInitialize(NULL);

	HRESULT hr;
	//hr = CreateDXGIFactory2(__uuidof(IDXGIFactory1), (void**)(&pFactory));

	//if (FAILED(hr))
	//{
	//	exit(0);
	//}
	//UINT i = 0;
	////IDXGIAdapter1*> vAdapters;

	//while (pFactory->EnumAdapters1(i, &m_pAdapter) != DXGI_ERROR_NOT_FOUND)
	//{
	//	//vAdapters.push_back(pAdapter);
	//	//pAdapter->EnumOutputs()
	//	++i;
	//	break; //we want the first adapter
	//}

	//if (m_pAdapter->EnumOutputs(0, &m_pOutput) == DXGI_ERROR_NOT_FOUND)
	//{
	//	//error
	//	exit(0);
	//}

	RECT rc;
	GetClientRect(gHwnd, &rc);
	UINT width = rc.right - rc.left;
	UINT height = rc.bottom - rc.top;

	// for d2d support.. make it 0 not work
	UINT createDeviceFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;


	D3D_DRIVER_TYPE driverTypes[] =
	{
		D3D_DRIVER_TYPE_HARDWARE,
		D3D_DRIVER_TYPE_WARP,
		D3D_DRIVER_TYPE_REFERENCE,
	};
	UINT numDriverTypes = ARRAYSIZE(driverTypes);

	D3D_FEATURE_LEVEL featureLevels[] =
	{
		D3D_FEATURE_LEVEL_11_1,
		D3D_FEATURE_LEVEL_11_0,
		D3D_FEATURE_LEVEL_10_1,
		D3D_FEATURE_LEVEL_10_0,
		D3D_FEATURE_LEVEL_9_3,
		D3D_FEATURE_LEVEL_9_2,
		D3D_FEATURE_LEVEL_9_1
	};
	UINT numFeatureLevels = ARRAYSIZE(featureLevels);

	for (UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++)
	{
		mDriverType = driverTypes[driverTypeIndex];
		hr = D3D11CreateDevice(nullptr, mDriverType, nullptr, createDeviceFlags, featureLevels, numFeatureLevels,
			D3D11_SDK_VERSION, &m_pD3dDevice, &mFeatureLevel, &m_pImmediateContext);

		if (hr == E_INVALIDARG)
		{
			// DirectX 11.0 platforms will not recognize D3D_FEATURE_LEVEL_11_1 so we need to retry without it
			hr = D3D11CreateDevice(nullptr, mDriverType, nullptr, createDeviceFlags, &featureLevels[1], numFeatureLevels - 1,
				D3D11_SDK_VERSION, &m_pD3dDevice, &mFeatureLevel, &m_pImmediateContext);
		}

		if (SUCCEEDED(hr))
			break;
	}
	if (FAILED(hr))
		exit(0);
	
	
	const double rate = 1000 / 60.0;
	mSoftwareVsyncRate = FromMilliseconds(rate);
	mPrevVsync = Now();

	/*hr = pFactory->CreateSwapChain(m_pD3dDevice, &scd, &m_pSwapChain);

	if ( FAILED(hr) )
		exit(0);
*/
	
	hr = m_pD3dDevice->QueryInterface(__uuidof(IDXGIDevice2), (void **)&m_pDXGIDevice);

	if (FAILED(hr))
	{
		exit(0);
	}

	IDXGIAdapter * pDXGIAdapter;
	hr = m_pDXGIDevice->GetParent(__uuidof(IDXGIAdapter), (void **)&pDXGIAdapter);

	if (FAILED(hr))
	{
		exit(0);
	}

	pDXGIAdapter->GetParent(__uuidof(IDXGIFactory2), (void **)&	pFactory);


	if (FAILED(hr))
	{
		exit(0);
	}


	hr = pFactory->EnumAdapters1(0, &m_pAdapter);
	if (FAILED(hr))
		exit(0);

	hr = m_pAdapter->EnumOutputs(0, &m_pOutput);
	if (FAILED(hr))
		exit(0);

	DXGI_OUTPUT_DESC desc;
	hr = m_pOutput->GetDesc(&desc);
	if (FAILED(hr))
		exit(0);

	RECT r;
	GetClientRect(gHwnd, &r);

	gHeight = r.bottom - r.top;
	gWidth = r.right - r.left;
	DXGI_SWAP_CHAIN_DESC1 scd = { 0 };
	//scd.BufferDesc.Width = gWidth;
	//scd.BufferDesc.Height = gHeight;
	//scd.BufferDesc.RefreshRate.Numerator = 60;
	//scd.BufferDesc.RefreshRate.Denominator = 1;
	//scd.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
	//scd.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
	//scd.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;

	//scd.SampleDesc.Count = 1;
	//scd.SampleDesc.Quality = 0;

	//scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
	//scd.BufferCount = 3; //1
	//scd.OutputWindow = gHwnd;
	//scd.Windowed = true;
	//scd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;// DXGI_SWAP_EFFECT_SEQUENTIAL;//DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
	//scd.Flags = DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE;
	scd.BufferUsage = DXGI_USAGE_BACK_BUFFER | DXGI_USAGE_RENDER_TARGET_OUTPUT;    // how the swap chain should be used
	scd.BufferCount = 3;                                  // a front buffer and a back buffer
	scd.Format = DXGI_FORMAT_B8G8R8A8_UNORM;              // the most common swap chain format
	scd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;    // the recommended flip mode
	scd.SampleDesc.Count = 1;
	scd.Scaling = DXGI_SCALING_NONE;
	scd.Flags = DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE;
	scd.Width = gWidth;
	scd.Height = gHeight;
	hr = pFactory->CreateSwapChainForHwnd(m_pD3dDevice, gHwnd, &scd, NULL, NULL, &m_pSwapChain);

	if (FAILED(hr))
	{
		exit(0);
	}

	hr = m_pSwapChain->GetBuffer(0, __uuidof(m_pBackBuffer), reinterpret_cast<void**>(&m_pBackBuffer));
	if (FAILED(hr))
		exit(0);

	hr = m_pD3dDevice->CreateRenderTargetView(m_pBackBuffer, NULL, &m_pRenderTarget ); 
	if (FAILED(hr))
		exit(0);

	m_pImmediateContext->OMSetRenderTargets(1, &m_pRenderTarget, NULL);

	DXGI_MODE_DESC requestedMode;
	requestedMode.Width = gWidth;
	requestedMode.Height = gHeight;
	requestedMode.RefreshRate.Numerator = 0;
	requestedMode.RefreshRate.Denominator = 0;
	requestedMode.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
	requestedMode.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
	requestedMode.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;

	DXGI_MODE_DESC mode;
	hr = m_pOutput->FindClosestMatchingMode(&requestedMode, &mode, m_pD3dDevice);
	if (FAILED(hr))
		throw - 1;
	
	hr = m_pSwapChain->ResizeTarget(&mode);
	if (FAILED(hr))
		throw -1; 


	mode.RefreshRate.Numerator = 0; 
	mode.RefreshRate.Denominator = 0;
	hr = m_pSwapChain->ResizeTarget(&mode);
	if (FAILED(hr))
		throw - 1;


	D2D1_FACTORY_OPTIONS options;
	ZeroMemory(&options, sizeof(D2D1_FACTORY_OPTIONS));
	hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,&m_pD2D1Factory);

	if (FAILED(hr))
	{
		exit(0);
	}

	hr = m_pD2D1Factory->CreateDevice(m_pDXGIDevice, &m_pD2D1Device);
	if (FAILED(hr))
	{
		exit(0);
	}

	
	hr = m_pD2D1Device->CreateDeviceContext(
		D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
		&m_pD2DContext
		);
	
	if (FAILED(hr))
	{
		exit(0);
	}
	
	

	// Direct2D needs the dxgi version of the backbuffer surface pointer.

	hr = m_pSwapChain->GetBuffer(0, IID_PPV_ARGS(&m_pDxgiBackBuffer));

	if (FAILED(hr))
		exit(0);

	auto d2dRTProps = D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(DXGI_FORMAT_UNKNOWN, D2D1_ALPHA_MODE_PREMULTIPLIED), 96.0, 96.0);


	/*D2D1_BITMAP_PROPERTIES1 bitmapProperties = D2D1::BitmapProperties1(
		D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
		D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
		96.0,
		96.0
		);*/


	// Get a D2D surface from the DXGI back buffer to use as the D2D render target.
	/*hr = m_pD2DContext->CreateBitmapFromDxgiSurface(
		m_pDxgiBackBuffer,
		d2dRTProps,
		&m_pD2DTargetBitmap
		);
	if (FAILED(hr))
		exit(0);*/

	hr = m_pD2D1Factory->CreateDxgiSurfaceRenderTarget(m_pDxgiBackBuffer, &d2dRTProps, &m_pD2DRenderTarget);
	
	if (FAILED(hr))
		exit(0);
	//m_pD2DContext->SetTarget(m_pD2DTargetBitmap);

	//m_pDXGIDevice->SetMaximumFrameLatency(3);

#if 0//VSYNC
	//Creating a thread
	std::thread vsync([this] { this->VSyncLoop(); });
	vsync.detach();
#endif

#if 0//USING_DWM_FLUSH
	std::thread VSyncBlit([this] { this->BlittingThread(); });
	VSyncBlit.detach();
#endif

	HDC hdc = GetDC(gHwnd);

	mDCBitmap1 = CreateCompatibleDC(hdc);
	mDCBitmap2 = CreateCompatibleDC(hdc);
	mDCBitmap3 = CreateCompatibleDC(hdc);

	//HDC hdc = GetDC(hWnd);

	RECT rect;
	GetWindowRect(gHwnd, &rect);

	gWidth = rect.right - rect.left;
	gHeight = rect.bottom - rect.top;
	mBitmap1 = CreateCompatibleBitmap(hdc, gWidth, gHeight);
	mBitmap2 = CreateCompatibleBitmap(hdc, gWidth, gHeight);
	mBitmap3 = CreateCompatibleBitmap(hdc, gWidth, gHeight);

	SelectObject(mDCBitmap1, mBitmap1);
	SelectObject(mDCBitmap2, mBitmap2);
	SelectObject(mDCBitmap3, mBitmap3);

	DeleteDC(hdc);


	InitializeThreadpoolEnvironment(&mCallBackEnviron);

	mPool = CreateThreadpool(NULL);

	if (NULL == mPool) {
		OutputDebugStringA("CreateThreadpool failed. LastError: %u\n");
		exit(0);
	}

	//SetThreadpoolThreadMaximum(mPool, 1);
	//SetThreadpoolCallbackPool(&mCallBackEnviron, mPool);

}
Example #13
0
File: rfx.c Project: C4rt/FreeRDP
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;
}
Example #14
0
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;
}
//
// This function is invoked only with Airplane mode change, but not with NFC radio state change.
//
STDMETHODIMP CNfcRadioManager::OnSystemRadioStateChange(
        _In_opt_ SYSTEM_RADIO_STATE sysRadioState,
        _In_ UINT32 uTimeoutSec)
{
    UNREFERENCED_PARAMETER(sysRadioState);
    UNREFERENCED_PARAMETER(uTimeoutSec);

    TRACE_METHOD_ENTRY(LEVEL_VERBOSE);

    HRESULT hr = S_OK;
    PTP_POOL threadPool = NULL;
    TP_CALLBACK_ENVIRON callbackEnviron;
    PTP_CLEANUP_GROUP ptpCleanupGroup = NULL;
    
    // Create threadpool to handle all of the radios
    threadPool = CreateThreadpool(NULL);
    if (NULL == threadPool)
    {
        hr = HRESULT_FROM_WIN32(GetLastError());
    }

    if (SUCCEEDED(hr))
    {
        SetThreadpoolThreadMaximum(threadPool, 50);
        InitializeThreadpoolEnvironment(&callbackEnviron);
        SetThreadpoolCallbackPool(&callbackEnviron, threadPool);
        ptpCleanupGroup = CreateThreadpoolCleanupGroup();

        if (NULL == ptpCleanupGroup)
        {
            hr = HRESULT_FROM_WIN32(GetLastError());
        }
        else
        {
            // Associate the cleanup group with our thread pool
            SetThreadpoolCallbackCleanupGroup(&callbackEnviron,
                                                ptpCleanupGroup,
                                                NULL );
        }
    }

    // Lock so that any adds or removes will not cause list changes during system airplane mode
    EnterCriticalSection(&m_csAddRemoveLock);

    if (SUCCEEDED(hr))
    {
        UINT32 i, count = 0;
        SYSTEM_STATE_SWITCH_CONTEXT* pContext = NULL;

        hr = m_nfcRadioCollection->GetCount(&count);

        if (count > 0)
        {
            if (SUCCEEDED(hr))
            {
                pContext = (SYSTEM_STATE_SWITCH_CONTEXT*)malloc(count * sizeof(SYSTEM_STATE_SWITCH_CONTEXT));
                if (NULL == pContext)
                {
                    hr = E_OUTOFMEMORY;
                }
            }
            
            for (i = 0; (SUCCEEDED(hr) && (i < count)); i++)
            {
                PTP_WORK ptpThreadWork = NULL;
                
                pContext[i].sysRadioState = sysRadioState;
                m_nfcRadioCollection->GetAt(i, (IRadioInstance**)&(pContext[i].pRadioInstance));
                
                ptpThreadWork = CreateThreadpoolWork( 
                                     (PTP_WORK_CALLBACK)&CNfcRadioManager::AsyncRadioChange, 
                                     &(pContext[i]), 
                                     &(callbackEnviron) );
                
                if (ptpThreadWork == NULL)
                {   
                    // pRadioInstance context was not successfully added to threadpool.
                    hr = pContext[i].pRadioInstance->SetSystemState(sysRadioState);
                    pContext[i].pRadioInstance->Release();
                }
                else
                {
                    ::SubmitThreadpoolWork(ptpThreadWork);
                }
            }
        }

        // Wait for all threadpools to drain and clean up threads
        CloseThreadpoolCleanupGroupMembers(ptpCleanupGroup, FALSE, NULL);

        if (NULL != pContext)
        {
            free(pContext);
        }
    }
    else
    {
        // Failed to set up threadpool for parallel system radio change. Only choice is to do it serially now
        UINT32 i, count = 0;
        CNfcRadioInstance* pRadioInstance = NULL;

        hr = m_nfcRadioCollection->GetCount(&count);
        for (i = 0; (SUCCEEDED(hr) && (i < count)); i++)
        {
            hr = m_nfcRadioCollection->GetAt(i, (IRadioInstance**)&pRadioInstance);
            if (SUCCEEDED(hr))
            {
                hr = pRadioInstance->SetSystemState(sysRadioState);
            }
            
            pRadioInstance->Release();
        }
    }

    LeaveCriticalSection(&m_csAddRemoveLock);

    DestroyThreadpoolEnvironment(&callbackEnviron);

    if(ptpCleanupGroup)
    {
        CloseThreadpoolCleanupGroup(ptpCleanupGroup);
        ptpCleanupGroup = NULL;
    }
  
    if(threadPool)
    {
        CloseThreadpool(threadPool);
        threadPool = NULL;
    }

    TRACE_METHOD_EXIT_HR(LEVEL_COND, hr);
    return hr;
}
Example #16
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;
}