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_(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(); }