Uint64 RenderDeviceD3D12Impl::CloseAndExecuteCommandContext(CommandContext *pCtx)
{
    CComPtr<ID3D12CommandAllocator> pAllocator;
	auto *pCmdList = pCtx->Close(&pAllocator);

	uint64_t FenceValue = 0;
    {
	    std::lock_guard<std::mutex> LockGuard(m_FenceMutex);

	    // Kickoff the command list
        ID3D12CommandList *const ppCmdLists[] = {pCmdList};
	    m_pd3d12CmdQueue->ExecuteCommandLists(1, ppCmdLists);

	    // Signal the next fence value (with the GPU)
	    m_pd3d12CmdQueue->Signal(m_pFence, m_NextFenceValue);

	    // And increment the fence value.  
	    FenceValue = m_NextFenceValue++;
    }

	m_CmdListManager.DiscardAllocator(FenceValue, pAllocator);
    pCtx->DiscardUsedDescriptorHeaps(m_CurrentFrame);

    {
	    std::lock_guard<std::mutex> LockGuard(m_ContextAllocationMutex);
    	m_AvailableContexts.push_back(pCtx);
    }

	return FenceValue;
}
void CommandListManager::RequestAllocator(ID3D12CommandAllocator** ppAllocator)
{
	std::lock_guard<std::mutex> LockGuard(m_AllocatorMutex);

    VERIFY( (*ppAllocator) == nullptr, "Allocator pointer is not null" );
    (*ppAllocator) = nullptr;

    if (!m_DiscardedAllocators.empty())
	{
		auto& AllocatorPair = m_DiscardedAllocators.front();

		if (m_pDeviceD3D12->IsFenceComplete(AllocatorPair.first))
		{
			*ppAllocator = AllocatorPair.second.Detach();
			auto hr = (*ppAllocator)->Reset();
            VERIFY_EXPR(SUCCEEDED(hr));
			m_DiscardedAllocators.pop_front();
		}
	}

	// If no allocators were ready to be reused, create a new one
	if ((*ppAllocator) == nullptr)
	{
        auto *pd3d12Device = m_pDeviceD3D12->GetD3D12Device();
		auto hr = pd3d12Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, __uuidof(*ppAllocator), reinterpret_cast<void**>(ppAllocator));
        VERIFY(SUCCEEDED(hr), "Failed to create command allocator")
		//wchar_t AllocatorName[32];
		//swprintf(AllocatorName, 32, L"CommandAllocator %zu", m_AllocatorPool.size());
		//(*ppAllocator)->SetName(AllocatorName);
	}
void CommandAllocatorPool::DiscardAllocator(uint64_t FenceValue, ID3D12CommandAllocator * Allocator)
{
	std::lock_guard<std::mutex> LockGuard(m_AllocatorMutex);

	// That fence value indicates we are free to reset the allocator
	m_ReadyAllocators.push(std::make_pair(FenceValue, Allocator));
}
CommandContext* RenderDeviceD3D12Impl::AllocateCommandContext(const Char *ID)
{
	std::lock_guard<std::mutex> LockGuard(m_ContextAllocationMutex);

	CommandContext* ret = nullptr;
	if (m_AvailableContexts.empty())
	{
        auto &CmdCtxAllocator = GetRawAllocator();
        auto *pRawMem = ALLOCATE(CmdCtxAllocator, "CommandContext instance", sizeof(CommandContext));
		ret = new (pRawMem) CommandContext( GetRawAllocator(), m_CmdListManager, m_GPUDescriptorHeaps, m_DynamicDescriptorAllocationChunkSize);
		m_ContextPool.emplace_back(ret, STDDeleterRawMem<CommandContext>(CmdCtxAllocator) );
	}
	else
	{
		ret = m_AvailableContexts.front();
		m_AvailableContexts.pop_front();
		ret->Reset(m_CmdListManager);
	}
	VERIFY_EXPR(ret != nullptr);
	ret->SetID(ID);
	//if ( ID != nullptr && *ID != 0 )
	//	EngineProfiling::BeginBlock(ID, NewContext);

	return ret;
}
void RenderDeviceD3D12Impl::IdleGPU(bool ReleaseStaleObjects) 
{ 
    Uint64 FenceValue = 0;
    Uint64 CmdListNumber = 0;
    {
        // Lock the command queue to avoid other threads interfering with the GPU
        std::lock_guard<std::mutex> LockGuard(m_CmdQueueMutex);
        FenceValue = m_pCommandQueue->GetNextFenceValue();
        m_pCommandQueue->IdleGPU();
        // Increment cmd list number while keeping queue locked. 
        // This guarantees that any D3D12 object released after the lock
        // is released, will be associated with the incremented cmd list number
        CmdListNumber = m_NextCmdListNumber;
        Atomics::AtomicIncrement(m_NextCmdListNumber);
    }
    
    if (ReleaseStaleObjects)
    {
        // Do not wait until the end of the frame and force deletion. 
        // This is necessary to release outstanding references to the
        // swap chain buffers when it is resized in the middle of the frame.
        // Since GPU has been idled, it it is safe to do so
        DiscardStaleD3D12Objects(CmdListNumber, FenceValue);
        ProcessReleaseQueue(FenceValue);
    }
}
ID3D12CommandAllocator * CommandAllocatorPool::RequestAllocator(uint64_t CompletedFenceValue)
{
	std::lock_guard<std::mutex> LockGuard(m_AllocatorMutex);

	ID3D12CommandAllocator* pAllocator = nullptr;

	if (!m_ReadyAllocators.empty())
	{
		std::pair<uint64_t, ID3D12CommandAllocator*>& AllocatorPair = m_ReadyAllocators.front();

		if (AllocatorPair.first <= CompletedFenceValue)
		{
			pAllocator = AllocatorPair.second;
			ASSERT_SUCCEEDED(pAllocator->Reset());
			m_ReadyAllocators.pop();
		}
	}

	// If no allocator's were ready to be reused, create a new one
	if (pAllocator == nullptr)
	{
		ASSERT_SUCCEEDED(m_Device->CreateCommandAllocator(m_cCommandListType, MY_IID_PPV_ARGS(&pAllocator)));
		wchar_t AllocatorName[32];
		swprintf(AllocatorName, 32, L"CommandAllocator %zu", m_AllocatorPool.size());
		pAllocator->SetName(AllocatorName);
		m_AllocatorPool.push_back(pAllocator);
	}

	return pAllocator;
}
Exemplo n.º 7
0
ID3D12CommandAllocator* CommandAllocatorPool::RequestAllocator( uint64_t CompletedFenceValue )
{
	CriticalSectionScope LockGuard( &m_AllocatorCS );

	HRESULT hr;
	ID3D12CommandAllocator* pAllocator = nullptr;

	if (!m_ReadyAllocators.empty())
	{
		std::pair<uint64_t, ID3D12CommandAllocator*>& AllocatorPair = m_ReadyAllocators.front();
		if (AllocatorPair.first <= CompletedFenceValue)
		{
			pAllocator = AllocatorPair.second;
			V( pAllocator->Reset() );
			m_ReadyAllocators.pop();
		}
		Graphics::g_stats.allocatorReady[m_cCommandListType] = (uint16_t)m_ReadyAllocators.size();
	}
	if (pAllocator == nullptr)
	{
		V( m_pDevice->CreateCommandAllocator( m_cCommandListType, IID_PPV_ARGS( &pAllocator ) ) );
		wchar_t AllocatorName[32];
		swprintf( AllocatorName, 32, L"CommandAllocator %zu", m_AllocatorPool.size() );
		pAllocator->SetName( AllocatorName );
		m_AllocatorPool.push_back( pAllocator );
		Graphics::g_stats.allocatorCreated[m_cCommandListType] = (uint16_t)m_AllocatorPool.size();
	}

	return pAllocator;
}
void RenderDeviceD3D12Impl::SafeReleaseD3D12Object(ID3D12Object* pObj)
{
    // When D3D12 object is released, it is first moved into the
    // stale objects list. The list is moved into a release queue
    // when the next command list is executed. 
    std::lock_guard<std::mutex> LockGuard(m_StaleObjectsMutex);
    m_StaleD3D12Objects.emplace_back( m_NextCmdListNumber, CComPtr<ID3D12Object>(pObj) );
}
Exemplo n.º 9
0
uint64_t CommandQueue::ExecuteCommandList( ID3D12CommandList* List )
{
	CriticalSectionScope LockGuard( &m_FenceCS );
	HRESULT hr;
	V( ((ID3D12GraphicsCommandList*)List)->Close() );

	m_CommandQueue->ExecuteCommandLists( 1, &List );
	m_CommandQueue->Signal( m_pFence, m_NextFenceValue );
	return m_NextFenceValue++;
}
Exemplo n.º 10
0
void RenderDeviceD3D12Impl::ProcessReleaseQueue(Uint64 CompletedFenceValue)
{
    std::lock_guard<std::mutex> LockGuard(m_ReleaseQueueMutex);

    // Release all objects whose associated fence value is at most CompletedFenceValue
    // See http://diligentgraphics.com/diligent-engine/architecture/d3d12/managing-resource-lifetimes/
    while (!m_D3D12ObjReleaseQueue.empty())
    {
        auto &FirstObj = m_D3D12ObjReleaseQueue.front();
        if (FirstObj.first <= CompletedFenceValue)
            m_D3D12ObjReleaseQueue.pop_front();
        else
            break;
    }
}
uint64_t CommandQueue::ExecuteCommandList( ID3D12CommandList* List )
{
	std::lock_guard<std::mutex> LockGuard(m_FenceMutex);

	ASSERT_SUCCEEDED(((ID3D12GraphicsCommandList*)List)->Close());

	// Kickoff the command list
	m_CommandQueue->ExecuteCommandLists(1, &List);

	// Signal the next fence value (with the GPU)
	m_CommandQueue->Signal(m_pFence, m_NextFenceValue);

	// And increment the fence value.  
	return m_NextFenceValue++;
}
void RenderDeviceD3D12Impl::ProcessReleaseQueue(bool ForceRelease)
{
    std::lock_guard<std::mutex> LockGuard(m_ReleasedObjectsMutex);

    // Release all objects whose frame number value < number of completed frames
    while (m_D3D12ObjReleaseQueue.size())
    {
        auto &FirstObj = m_D3D12ObjReleaseQueue.front();
        // GPU must have been idled when ForceRelease == true 
        if (FirstObj.first < m_NumCompletedFrames || ForceRelease)
            m_D3D12ObjReleaseQueue.pop_front();
        else
            break;
    }
}
Exemplo n.º 13
0
bool GuidGenerator::Init(tuint16 nWorldId, tuint8 nType, tuint8 nCarry, tuint32 nSerial)
{
	bstMutexScopedLock LockGuard(m_bsMutex);

	m_WorldId = nWorldId;
	m_Type = nType;
	m_NextAvailableSerial.first = nCarry;
	m_NextAvailableSerial.second = nSerial;
	m_CurrentSavedSerial.first = nCarry;
	m_CurrentSavedSerial.second = nSerial;

	m_bInited = true;

	return true;
}
Exemplo n.º 14
0
DynamicUploadHeap* RenderDeviceD3D12Impl::RequestUploadHeap()
{
    std::lock_guard<std::mutex> LockGuard(m_UploadHeapMutex);

#ifdef _DEBUG
    size_t InitialSize = 1024+64;
#else
    size_t InitialSize = 64<<10;//16<<20;
#endif

    auto &UploadHeapAllocator = GetRawAllocator();
    auto *pRawMem = ALLOCATE(UploadHeapAllocator, "DynamicUploadHeap instance", sizeof(DynamicUploadHeap));
    auto *pNewHeap = new (pRawMem) DynamicUploadHeap(GetRawAllocator(), true, this, InitialSize);
    m_UploadHeaps.emplace_back( pNewHeap, STDDeleterRawMem<DynamicUploadHeap>(UploadHeapAllocator) );
    return pNewHeap;
}
void RenderDeviceD3D12Impl::WaitForFence(Uint64 FenceValue)
{
	if (IsFenceComplete(FenceValue))
		return;

	// TODO:  Think about how this might affect a multi-threaded situation.  Suppose thread A
	// wants to wait for fence 100, then thread B comes along and wants to wait for 99.  If
	// the fence can only have one event set on completion, then thread B has to wait for 
	// 100 before it knows 99 is ready.  Maybe insert sequential events?
	{
		std::lock_guard<std::mutex> LockGuard(m_EventMutex);

		m_pFence->SetEventOnCompletion(FenceValue, m_FenceEventHandle);
		WaitForSingleObject(m_FenceEventHandle, INFINITE);
		m_LastCompletedFenceValue = FenceValue;
	}
}
Exemplo n.º 16
0
void GuidGenerator::Gen()
{
	bstMutexScopedLock LockGuard(bstMutex);

	/*
	减少存盘的频率,没创建512个GUID才存盘一次
	*/
	if (m_NextAvailableSerial >= m_CurrentSavedSerial)
	{
		IncreaseSerial(m_CurrentSavedSerial, 512);
		SaveSerial();
	}

	Guid64 newGuid(m_WorldId, m_Type, m_NextAvailableSerial.first, m_NextAvailableSerial.second);
	IncreaseSerial(m_NextAvailableSerial, 1);//每次新建GUID都是新增1

	return newGuid;
}
Exemplo n.º 17
0
void CommandQueue::WaitForFence( uint64_t FenceValue )
{
	if (IsFenceCompelete( FenceValue ))
		return;
	{
		CriticalSectionScope LockGuard( &m_EventCS );
		m_pFence->SetEventOnCompletion( FenceValue, m_FenceEventHandle );
		int64_t startTick, endTick;
		LARGE_INTEGER currentTick;
		QueryPerformanceCounter( &currentTick );
		startTick = static_cast<int64_t>(currentTick.QuadPart);
		WaitForSingleObject( m_FenceEventHandle, INFINITE );
		QueryPerformanceCounter( &currentTick );
		endTick = static_cast<int64_t>(currentTick.QuadPart);

		Graphics::g_stats.cpuStallCountPerFrame++;
		Graphics::g_stats.cpuStallTimePerFrame += (double)(endTick - startTick) / Core::g_tickesPerSecond * 1000.f;
		m_LastCompletedFenceValue = FenceValue;
	}
}
uint64_t CommandQueue::IncrementFence(void)
{
	std::lock_guard<std::mutex> LockGuard(m_FenceMutex);
	m_CommandQueue->Signal(m_pFence, m_NextFenceValue);
	return m_NextFenceValue++;
}
Exemplo n.º 19
0
uint64_t CommandQueue::IncrementFence()
{
	CriticalSectionScope LockGuard( &m_FenceCS );
	m_CommandQueue->Signal( m_pFence, m_NextFenceValue );
	return m_NextFenceValue++;
}
Exemplo n.º 20
0
void RenderDeviceD3D12Impl::CloseAndExecuteCommandContext(CommandContext *pCtx, bool DiscardStaleObjects)
{
    CComPtr<ID3D12CommandAllocator> pAllocator;
	auto *pCmdList = pCtx->Close(&pAllocator);

    Uint64 FenceValue = 0;
    Uint64 CmdListNumber = 0;
    {
	    std::lock_guard<std::mutex> LockGuard(m_CmdQueueMutex);
        auto NextFenceValue = m_pCommandQueue->GetNextFenceValue();
	    // Submit the command list to the queue
        FenceValue = m_pCommandQueue->ExecuteCommandList(pCmdList);
        VERIFY(FenceValue >= NextFenceValue, "Fence value of the executed command list is less than the next fence value previously queried through GetNextFenceValue()");
        FenceValue = std::max(FenceValue, NextFenceValue);
        CmdListNumber = m_NextCmdListNumber;
        Atomics::AtomicIncrement(m_NextCmdListNumber);
    }

    if (DiscardStaleObjects)
    {
        // The following basic requirement guarantees correctness of resource deallocation:
        //
        //        A resource is never released before the last draw command referencing it is invoked on the immediate context
        //
        // See http://diligentgraphics.com/diligent-engine/architecture/d3d12/managing-resource-lifetimes/

        // Stale objects should only be discarded when submitting cmd list from 
        // the immediate context, otherwise the basic requirement may be violated
        // as in the following scenario
        //                                                           
        //  Signaled        |                                        |
        //  Fence Value     |        Immediate Context               |            InitContext            |
        //                  |                                        |                                   |
        //    N             |  Draw(ResourceX)                       |                                   |
        //                  |  Release(ResourceX)                    |                                   |
        //                  |   - (ResourceX, N) -> Release Queue    |                                   |
        //                  |                                        | CopyResource()                    |
        //   N+1            |                                        | CloseAndExecuteCommandContext()   |
        //                  |                                        |                                   |
        //   N+2            |  CloseAndExecuteCommandContext()       |                                   |
        //                  |   - Cmd list is submitted with number  |                                   |
        //                  |     N+1, but resource it references    |                                   |
        //                  |     was added to the delete queue      |                                   |
        //                  |     with number N                      |                                   |

        // Move stale objects into a release queue.
        // Note that objects are moved from stale list to release queue based on the
        // cmd list number, not the fence value. This makes sure that basic requirement
        // is met even when the fence value is not incremented while executing 
        // the command list (as is the case with Unity command queue).
        DiscardStaleD3D12Objects(CmdListNumber, FenceValue);
    }

    // DiscardAllocator() is thread-safe
	m_CmdListManager.DiscardAllocator(FenceValue, pAllocator);
    
    pCtx->DiscardDynamicDescriptors(FenceValue);

    {
	    std::lock_guard<std::mutex> LockGuard(m_ContextAllocationMutex);
    	m_AvailableContexts.push_back(pCtx);
    }
}
Exemplo n.º 21
0
void CommandAllocatorPool::DiscardAllocator( uint64_t FenceValue, ID3D12CommandAllocator* Allocator )
{
	CriticalSectionScope LockGuard( &m_AllocatorCS );
	m_ReadyAllocators.push( std::make_pair( FenceValue, Allocator ) );
}
Uint64 RenderDeviceD3D12Impl::IncrementFence(void)
{
	std::lock_guard<std::mutex> LockGuard(m_FenceMutex);
    m_pd3d12CmdQueue->Signal(m_pFence, m_NextFenceValue);
	return m_NextFenceValue++;
}
void RenderDeviceD3D12Impl::SafeReleaseD3D12Object(ID3D12Object* pObj)
{
    std::lock_guard<std::mutex> LockGuard(m_ReleasedObjectsMutex);
    m_D3D12ObjReleaseQueue.emplace_back( m_CurrentFrame, CComPtr<ID3D12Object>(pObj) );
}
Exemplo n.º 24
0
void RenderDeviceD3D12Impl::FinishFrame(bool ReleaseAllResources)
{
    {
        if (auto pImmediateCtx = m_wpImmediateContext.Lock())
        {
            auto pImmediateCtxD3D12 = ValidatedCast<DeviceContextD3D12Impl>(pImmediateCtx.RawPtr());
            if(pImmediateCtxD3D12->GetNumCommandsInCtx() != 0)
                LOG_ERROR_MESSAGE("There are outstanding commands in the immediate device context when finishing the frame. This is an error and may cause unpredicted behaviour. Call Flush() to submit all commands for execution before finishing the frame");
        }

        for (auto wpDeferredCtx : m_wpDeferredContexts)
        {
            if (auto pDeferredCtx = wpDeferredCtx.Lock())
            {
                auto pDeferredCtxD3D12 = ValidatedCast<DeviceContextD3D12Impl>(pDeferredCtx.RawPtr());
                if(pDeferredCtxD3D12->GetNumCommandsInCtx() != 0)
                    LOG_ERROR_MESSAGE("There are outstanding commands in the deferred device context when finishing the frame. This is an error and may cause unpredicted behaviour. Close all deferred contexts and execute them before finishing the frame");
            }
        }
    }
    
    auto CompletedFenceValue = ReleaseAllResources ? std::numeric_limits<Uint64>::max() : GetCompletedFenceValue();

    // We must use NextFenceValue here, NOT current value, because the 
    // fence value may or may not have been incremented when the last 
    // command list was submitted for execution (Unity only
    // increments fence value once per frame)
    Uint64 NextFenceValue = 0;
    Uint64 CmdListNumber = 0;
    {
        // Lock the command queue to avoid other threads interfering with the GPU
        std::lock_guard<std::mutex> LockGuard(m_CmdQueueMutex);
        NextFenceValue = m_pCommandQueue->GetNextFenceValue();
        // Increment cmd list number while keeping queue locked. 
        // This guarantees that any D3D12 object released after the lock
        // is released, will be associated with the incremented cmd list number
        CmdListNumber = m_NextCmdListNumber;
        Atomics::AtomicIncrement(m_NextCmdListNumber);
    }

    {
        // There is no need to lock as new heaps are only created during initialization
        // time for every context
        //std::lock_guard<std::mutex> LockGuard(m_UploadHeapMutex);
        
        // Upload heaps are used to update resource contents as well as to allocate
        // space for dynamic resources.
        // Initial resource data is uploaded using temporary one-time upload buffers, 
        // so can be performed in parallel across frame boundaries
        for (auto &UploadHeap : m_UploadHeaps)
        {
            // Currently upload heaps are free-threaded, so other threads must not allocate
            // resources at the same time. This means that all dynamic buffers must be unmaped 
            // in the same frame and all resources must be updated within boundaries of a single frame.
            //
            //    worker thread 3    | pDevice->CrateTexture(InitData) |    | pDevice->CrateBuffer(InitData) |    | pDevice->CrateTexture(InitData) |
            //                                                                                               
            //    worker thread 2     | pDfrdCtx2->UpdateResource()  |                                              ||
            //                                                                                                      ||
            //    worker thread 1       |  pDfrdCtx1->Map(WRITE_DISCARD) |    | pDfrdCtx1->UpdateResource()  |      ||
            //                                                                                                      ||
            //    main thread        |  pCtx->Map(WRITE_DISCARD )|  | pCtx->UpdateResource()  |                     ||   | Present() |
            //
            //
            
            UploadHeap->FinishFrame(NextFenceValue, CompletedFenceValue);
        }
    }

    for(Uint32 CPUHeap=0; CPUHeap < _countof(m_CPUDescriptorHeaps); ++CPUHeap)
    {
        // This is OK if other thread disposes descriptor heap allocation at this time
        // The allocation will be registered as part of the current frame
        m_CPUDescriptorHeaps[CPUHeap].ReleaseStaleAllocations(CompletedFenceValue);
    }
        
    for(Uint32 GPUHeap=0; GPUHeap < _countof(m_GPUDescriptorHeaps); ++GPUHeap)
    {
        m_GPUDescriptorHeaps[GPUHeap].ReleaseStaleAllocations(CompletedFenceValue);
    }

    // Discard all remaining objects. This is important to do if there were 
    // no command lists submitted during the frame
    DiscardStaleD3D12Objects(CmdListNumber, NextFenceValue);
    ProcessReleaseQueue(CompletedFenceValue);

    Atomics::AtomicIncrement(m_FrameNumber);
}
Exemplo n.º 25
0
llvm::Error AllTUsToolExecutor::execute(
    llvm::ArrayRef<
        std::pair<std::unique_ptr<FrontendActionFactory>, ArgumentsAdjuster>>
        Actions) {
  if (Actions.empty())
    return make_string_error("No action to execute.");

  if (Actions.size() != 1)
    return make_string_error(
        "Only support executing exactly 1 action at this point.");

  std::string ErrorMsg;
  std::mutex TUMutex;
  auto AppendError = [&](llvm::Twine Err) {
    std::unique_lock<std::mutex> LockGuard(TUMutex);
    ErrorMsg += Err.str();
  };

  auto Log = [&](llvm::Twine Msg) {
    std::unique_lock<std::mutex> LockGuard(TUMutex);
    llvm::errs() << Msg.str() << "\n";
  };

  std::vector<std::string> Files;
  llvm::Regex RegexFilter(Filter);
  for (const auto& File : Compilations.getAllFiles()) {
    if (RegexFilter.match(File))
      Files.push_back(File);
  }
  // Add a counter to track the progress.
  const std::string TotalNumStr = std::to_string(Files.size());
  unsigned Counter = 0;
  auto Count = [&]() {
    std::unique_lock<std::mutex> LockGuard(TUMutex);
    return ++Counter;
  };

  auto &Action = Actions.front();

  {
    llvm::ThreadPool Pool(ThreadCount == 0 ? llvm::hardware_concurrency()
                                           : ThreadCount);
    llvm::SmallString<128> InitialWorkingDir;
    if (auto EC = llvm::sys::fs::current_path(InitialWorkingDir)) {
      InitialWorkingDir = "";
      llvm::errs() << "Error while getting current working directory: "
                   << EC.message() << "\n";
    }
    for (std::string File : Files) {
      Pool.async(
          [&](std::string Path) {
            Log("[" + std::to_string(Count()) + "/" + TotalNumStr +
                "] Processing file " + Path);
            ClangTool Tool(Compilations, {Path});
            Tool.appendArgumentsAdjuster(Action.second);
            Tool.appendArgumentsAdjuster(getDefaultArgumentsAdjusters());
            for (const auto &FileAndContent : OverlayFiles)
              Tool.mapVirtualFile(FileAndContent.first(),
                                  FileAndContent.second);
            // Do not restore working dir from multiple threads to avoid races.
            Tool.setRestoreWorkingDir(false);
            if (Tool.run(Action.first.get()))
              AppendError(llvm::Twine("Failed to run action on ") + Path +
                          "\n");
          },
          File);
    }
    // Make sure all tasks have finished before resetting the working directory.
    Pool.wait();
    if (!InitialWorkingDir.empty()) {
      if (auto EC = llvm::sys::fs::set_current_path(InitialWorkingDir))
        llvm::errs() << "Error while restoring working directory: "
                     << EC.message() << "\n";
    }
  }

  if (!ErrorMsg.empty())
    return make_string_error(ErrorMsg);

  return llvm::Error::success();
}