// Return only the locked state and waiter count in the previous (m_MonitorHeld) layout for the debugger: // bit 0: 1 if locked, 0 otherwise // bits 1-31: waiter count UINT32 state = m_state; return (state & IsLockedMask) + (state >> WaiterCountShift << 1); } //..
Thread *pCurThread = GetThread(); LockState state = m_lockState.VolatileLoadWithoutBarrier(); if (!state.IsLocked() || m_HoldingThread != pCurThread) { if (m_lockState.InterlockedTryLock_Or_RegisterWaiter(this, state)) { // We get here if we successfully acquired the mutex. m_HoldingThread = pCurThread; m_Recursion = 1; pCurThread->IncLockCount(); return; }
// Lock was not acquired and the waiter was registered
// Didn't manage to get the mutex, must wait. // The precondition for EnterEpilog is that the count of waiters be bumped // to account for this thread, which was done above. EnterEpilog(pCurThread); return; }
// Got the mutex via recursive locking on the same thread. _ASSERTE(m_Recursion >= 1); m_Recursion++; }
switch(action) { case AwareLock::LeaveHelperAction_None: // We are done return TRUE; case AwareLock::LeaveHelperAction_Signal: // Signal the event Signal(); return TRUE; default: // Must be an error otherwise _ASSERTE(action == AwareLock::LeaveHelperAction_Error); return FALSE; } }
if (m_HoldingThread != pCurThread) return AwareLock::LeaveHelperAction_Error;
//省略一些程式碼 if (--m_Recursion == 0) { m_HoldingThread = NULL;
// Clear lock bit and determine whether we must signal a waiter to wake if (!m_lockState.InterlockedUnlock()) { return AwareLock::LeaveHelperAction_None; }
// There is a waiter and we must signal a waiter to wake return AwareLock::LeaveHelperAction_Signal; }
LockState state = InterlockedDecrementRelease((LONG *)&m_state); while (true) { // Keep track of whether a thread has been signaled to wake but has not yet woken from the wait. // IsWaiterSignaledToWakeMask is cleared when a signaled thread wakes up by observing a signal. Since threads can // preempt waiting threads and acquire the lock (see InterlockedTryLock()), it allows for example, one thread to acquire // and release the lock multiple times while there are multiple waiting threads. In such a case, we don't want that // thread to signal a waiter every time it releases the lock, as that will cause unnecessary context switches with more // and more signaled threads waking up, finding that the lock is still locked, and going right back into a wait state. // So, signal only one waiting thread at a time. if (!state.NeedToSignalWaiter()) { returnfalse; }
// We can't afford to use an SList<> here because we only want to burn // space for the minimum, which is the pointer within an SLink. SLink m_Link;
WaitEventLink程式碼
1 2 3 4 5 6 7 8 9
// Used inside Thread class to chain all events that a thread is waiting for by Object::Wait structWaitEventLink { SyncBlock *m_WaitSB; CLREvent *m_EventWait; PTR_Thread m_Thread; // Owner of this WaitEventLink. PTR_WaitEventLink m_Next; // Chain to the next waited SyncBlock. SLink m_LinkSB; // Chain to the next thread waiting on the same SyncBlock. DWORD m_RefCount; // How many times Object::Wait is called on the same SyncBlock. };
// Unlink the head of the Q. We are always in the SyncBlock's critical // section. /* static */ inline WaitEventLink *ThreadQueue::DequeueThread(SyncBlock *psb) { CONTRACTL { NOTHROW; GC_NOTRIGGER; MODE_ANY; CAN_TAKE_LOCK; } CONTRACTL_END;
// Be careful, the debugger inspects the queue from out of process and just looks at the memory... // it must be valid even if the lock is held. Be careful if you change the way the queue is updated. SyncBlockCache::LockHolder lh(SyncBlockCache::GetSyncBlockCache());
// Enqueue is the slow one. We have to find the end of the Q since we don't // want to burn storage for this in the SyncBlock. /* static */ inlinevoidThreadQueue::EnqueueThread(WaitEventLink *pWaitEventLink, SyncBlock *psb) { CONTRACTL { NOTHROW; GC_NOTRIGGER; MODE_ANY; CAN_TAKE_LOCK; } CONTRACTL_END;
// Be careful, the debugger inspects the queue from out of process and just looks at the memory... // it must be valid even if the lock is held. Be careful if you change the way the queue is updated. SyncBlockCache::LockHolder lh(SyncBlockCache::GetSyncBlockCache());
SLink *pPrior = &psb->m_Link;
while (pPrior->m_pNext) { // We shouldn't already be in the waiting list! _ASSERTE(pPrior->m_pNext != &pWaitEventLink->m_LinkSB);