void DHContext::PrintDeadlock(DHDetails *pDetails, SIZE_T cookie) { ICLRSyncManager *pClrSyncManager = m_pSyncManager->GetCLRSyncManager(); fprintf(stderr, "A deadlock has occurred; terminating the program. Details below.\r\n"); // Print details about the attempted acquisition: IHostTask *pTask; m_pTaskManager->GetCurrentTask((IHostTask**)&pTask); fprintf(stderr, " %x was attempting to acquire %x, which created the cycle\r\n", dynamic_cast<DHTask*>(pTask)->GetThreadHandle(), cookie); pTask->Release(); // Now walk through the wait-graph and print details: for (DHDetails::iterator walker = pDetails->begin(); walker != pDetails->end(); walker++) { IHostTask *pOwner; pClrSyncManager->GetMonitorOwner(walker->second, (IHostTask**)&pOwner); fprintf(stderr, " %x waits on lock %x (owned by %x)\r\n", walker->first->GetThreadHandle(), walker->second, dynamic_cast<DHTask*>(pOwner)->GetThreadHandle()); pOwner->Release(); walker->first->Release(); } }
STDMETHODIMP DDTaskManager::CreateTask(/* in */ DWORD dwStackSize, /* in */ LPTHREAD_START_ROUTINE pStartAddress, /* in */ PVOID pParameter, /* out */ IHostTask **ppTask) { DWORD dwThreadId; HANDLE hThread = CreateThread( NULL, dwStackSize, pStartAddress, pParameter, CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION, &dwThreadId); IHostTask* task = new DDTask(hThread); if (!task) { _ASSERTE(!"Failed to allocate task"); *ppTask = NULL; return E_OUTOFMEMORY; } m_pThreadMapCrst->Enter(); m_pThreadMap->insert(map<DWORD, IHostTask*>::value_type(dwThreadId, task)); ThreadInfo info; info.threadHandle = hThread; info.threadId = dwThreadId; m_pContext->GetTasksList()->insert(m_pContext->GetTasksList()->begin(), list<ThreadInfo>::value_type(info)); m_pThreadMapCrst->Exit(); task->AddRef(); *ppTask = task; return S_OK; }
STDMETHODIMP DHTaskManager::CreateTask(/* in */ DWORD dwStackSize, /* in */ LPTHREAD_START_ROUTINE pStartAddress, /* in */ PVOID pParameter, /* out */ IHostTask **ppTask) { DWORD dwThreadId; HANDLE hThread = CreateThread( NULL, dwStackSize, pStartAddress, pParameter, CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION, &dwThreadId); IHostTask* task = new DHTask(this, hThread); if (!task) { _ASSERTE(!"Failed to allocate task"); *ppTask = NULL; return E_OUTOFMEMORY; } CrstLock crst(m_pThreadMapCrst); m_pThreadMap->insert(map<DWORD, IHostTask*>::value_type(dwThreadId, task)); crst.Exit(); task->AddRef(); *ppTask = task; return S_OK; }
STDMETHODIMP_(VOID) DHContext::EndEnter(SIZE_T cookie) { IHostTask* pCurrentTask; m_pTaskManager->GetCurrentTask((IHostTask**)&pCurrentTask); // This call is made after the thread has acquired the lock. We simply remove // our wait record to indicate that we're no longer waiting for the target lock. CrstLock lock(m_pCrst); m_pLockWaits->erase(pCurrentTask); lock.Exit(); #if LOCK_TRACE // If we're tracing, enter a record into the list of currently held locks. vector<SIZE_T> *pCurrentLocks = reinterpret_cast<vector<SIZE_T>*>(TlsGetValue(m_dwTlsCurrentLocks)); if (!pCurrentLocks) { pCurrentLocks = new vector<SIZE_T>; TlsSetValue(m_dwTlsCurrentLocks, pCurrentLocks); } pCurrentLocks->push_back(cookie); // And record the uniquely held locks in the active context. CrstLock traceLock(m_pLockTraceCrst); for (vector<SIZE_T>::iterator lockWalker = pCurrentLocks->begin(); lockWalker != pCurrentLocks->end(); lockWalker++) { // Obtain the list of locks held for the current lock. map<SIZE_T, vector<SIZE_T>*>::iterator traceWalker = m_pLockTrace->find(*lockWalker); vector<SIZE_T> *pTrace; if (traceWalker == m_pLockTrace->end()) { pTrace = new vector<SIZE_T>; m_pLockTrace->insert(map<SIZE_T, vector<SIZE_T>*>::value_type(*lockWalker, pTrace)); } else { pTrace = traceWalker->second; } // And ensure all locks held from here on are added. for (vector<SIZE_T>::iterator heldLocks(lockWalker); heldLocks != pCurrentLocks->end(); heldLocks++) { pTrace->push_back(*heldLocks); } } traceLock.Exit(); #endif // This may look strange. We Release the underlying IHostTask twice. But this is an // operation paired with TryEnter above, which required a call to GetCurrentTask, the // result for which we stashed in our map. Thus, we need to Release once for TryEnter // and once for the call just above in EndEnter. pCurrentTask->Release(); pCurrentTask->Release(); }