@@ -786,7 +786,7 @@ void SyncBlockCache::DeleteSyncBlock(SyncBlock *psb)
786786#endif // FEATURE_METADATA_UPDATER
787787
788788 // Cleanup lock info
789- psb->m_thinLock = 0 ;
789+ psb->m_thinLock . StoreWithoutBarrier ( 0 ) ;
790790 if (psb->m_Lock )
791791 {
792792 DestroyHandle (psb->m_Lock );
@@ -1307,117 +1307,113 @@ void DumpSyncBlockCache()
13071307
13081308// ***************************************************************************
13091309//
1310- // ObjHeader class implementation
1310+ // SpinLock implementation
13111311//
13121312// ***************************************************************************
13131313
1314- #ifdef MP_LOCKS
1315- DEBUG_NOINLINE void ObjHeader::EnterSpinLock ()
1314+ namespace
13161315{
1317- // NOTE: This function cannot have a dynamic contract. If it does, the contract's
1318- // destructor will reset the CLR debug state to what it was before entering the
1319- // function, which will undo the BeginNoTriggerGC() call below.
1320- STATIC_CONTRACT_GC_NOTRIGGER;
1321-
1322- #ifdef _DEBUG
1323- int i = 0 ;
1324- #endif
1325-
1326- DWORD dwSwitchCount = 0 ;
1327-
1328- while (TRUE )
1316+ void EnterSpinLock (Volatile<DWORD>* pLock)
13291317 {
1330- #ifdef _DEBUG
1331- #ifdef HOST_64BIT
1332- // Give 64bit more time because there isn't a remoting fast path now, and we've hit this assert
1333- // needlessly in CLRSTRESS.
1334- if (i++ > 30000 )
1335- #else
1336- if (i++ > 10000 )
1337- #endif // HOST_64BIT
1338- _ASSERTE (!" ObjHeader::EnterLock timed out" );
1339- #endif
1340- // get the value so that it doesn't get changed under us.
1341- LONG curValue = m_SyncBlockValue.LoadWithoutBarrier ();
1318+ STATIC_CONTRACT_GC_NOTRIGGER;
13421319
1343- // check if lock taken
1344- if (! (curValue & BIT_SBLK_SPIN_LOCK))
1345- {
1346- // try to take the lock
1347- LONG newValue = curValue | BIT_SBLK_SPIN_LOCK;
1348- LONG result = InterlockedCompareExchange ((LONG*)&m_SyncBlockValue, newValue, curValue);
1349- if (result == curValue)
1350- break ;
1351- }
1352- if (g_SystemInfo.dwNumberOfProcessors > 1 )
1320+ DWORD dwSwitchCount = 0 ;
1321+
1322+ while (TRUE )
13531323 {
1354- for (int spinCount = 0 ; spinCount < BIT_SBLK_SPIN_COUNT; spinCount++)
1324+ // get the value so that it doesn't get changed under us.
1325+ LONG curValue = pLock->LoadWithoutBarrier ();
1326+
1327+ // check if lock taken
1328+ if (! (curValue & BIT_SBLK_SPIN_LOCK))
13551329 {
1356- if (! (m_SyncBlockValue & BIT_SBLK_SPIN_LOCK))
1330+ // try to take the lock
1331+ LONG newValue = curValue | BIT_SBLK_SPIN_LOCK;
1332+ LONG result = InterlockedCompareExchange ((LONG*)pLock, newValue, curValue);
1333+ if (result == curValue)
13571334 break ;
1358- YieldProcessorNormalized (); // indicate to the processor that we are spinning
13591335 }
1360- if (m_SyncBlockValue & BIT_SBLK_SPIN_LOCK)
1336+ if (g_SystemInfo.dwNumberOfProcessors > 1 )
1337+ {
1338+ for (int spinCount = 0 ; spinCount < BIT_SBLK_SPIN_COUNT; spinCount++)
1339+ {
1340+ if (! (*pLock & BIT_SBLK_SPIN_LOCK))
1341+ break ;
1342+ YieldProcessorNormalized (); // indicate to the processor that we are spinning
1343+ }
1344+ if (*pLock & BIT_SBLK_SPIN_LOCK)
1345+ __SwitchToThread (0 , ++dwSwitchCount);
1346+ }
1347+ else
13611348 __SwitchToThread (0 , ++dwSwitchCount);
13621349 }
1363- else
1364- __SwitchToThread (0 , ++dwSwitchCount);
13651350 }
13661351
1367- INCONTRACT (Thread* pThread = GetThreadNULLOk ());
1368- INCONTRACT (if (pThread != NULL ) pThread->BeginNoTriggerGC (__FILE__, __LINE__));
1369- }
1370- #else
1371- DEBUG_NOINLINE void ObjHeader::EnterSpinLock ()
1372- {
1373- STATIC_CONTRACT_GC_NOTRIGGER;
1374-
1375- #ifdef _DEBUG
1376- int i = 0 ;
1377- #endif
1352+ void ReleaseSpinLock (Volatile<DWORD>* pLock)
1353+ {
1354+ LIMITED_METHOD_CONTRACT;
13781355
1379- DWORD dwSwitchCount = 0 ;
1356+ InterlockedAnd ((LONG*)pLock, ~BIT_SBLK_SPIN_LOCK);
1357+ }
13801358
1381- while ( TRUE )
1359+ struct HeaderSpinLockHolder
13821360 {
1383- #ifdef _DEBUG
1384- if (i++ > 10000 )
1385- _ASSERTE (!" ObjHeader::EnterLock timed out" );
1386- #endif
1387- // get the value so that it doesn't get changed under us.
1388- LONG curValue = m_SyncBlockValue.LoadWithoutBarrier ();
1361+ Volatile<DWORD>* m_pLock;
13891362
1390- // check if lock taken
1391- if (! (curValue & BIT_SBLK_SPIN_LOCK) )
1363+ HeaderSpinLockHolder (Volatile<DWORD>* pLock)
1364+ : m_pLock(pLock )
13921365 {
1393- // try to take the lock
1394- LONG newValue = curValue | BIT_SBLK_SPIN_LOCK;
1395- LONG result = InterlockedCompareExchange ((LONG*)&m_SyncBlockValue, newValue, curValue);
1396- if (result == curValue)
1397- break ;
1366+ // Acquire the spin-lock in preemptive mode with GC_NOTRIGGER
1367+ // to avoid deadlocks with the GC.
1368+ CONTRACTL
1369+ {
1370+ GC_NOTRIGGER;
1371+ NOTHROW;
1372+ MODE_PREEMPTIVE;
1373+ }
1374+ CONTRACTL_END;
1375+ EnterSpinLock (m_pLock);
13981376 }
1399- __SwitchToThread (0 , ++dwSwitchCount);
1400- }
1377+
1378+ ~HeaderSpinLockHolder ()
1379+ {
1380+ LIMITED_METHOD_CONTRACT;
1381+ ReleaseSpinLock (m_pLock);
1382+ }
1383+ };
1384+ }
1385+ #endif // !DACCESS_COMPILE
1386+
1387+
1388+ // ***************************************************************************
1389+ //
1390+ // ObjHeader class implementation
1391+ //
1392+ // ***************************************************************************
1393+
1394+ #ifndef DACCESS_COMPILE
1395+
1396+
1397+ DEBUG_NOINLINE void ObjHeader::EnterSpinLock ()
1398+ {
1399+ // NOTE: This function cannot have a dynamic contract. If it does, the contract's
1400+ // destructor will reset the CLR debug state to what it was before entering the
1401+ // function, which will undo the BeginNoTriggerGC() call below.
1402+ STATIC_CONTRACT_GC_NOTRIGGER;
1403+
1404+ ::EnterSpinLock (std::addressof(m_SyncBlockValue));
14011405
14021406 INCONTRACT (Thread* pThread = GetThreadNULLOk ());
14031407 INCONTRACT (if (pThread != NULL ) pThread->BeginNoTriggerGC (__FILE__, __LINE__));
14041408}
1405- #endif // MP_LOCKS
1406-
14071409DEBUG_NOINLINE void ObjHeader::ReleaseSpinLock ()
14081410{
1409- LIMITED_METHOD_CONTRACT;
1410-
14111411 INCONTRACT (Thread* pThread = GetThreadNULLOk ());
14121412 INCONTRACT (if (pThread != NULL ) pThread->EndNoTriggerGC ());
14131413
1414- InterlockedAnd ((LONG*)& m_SyncBlockValue, ~BIT_SBLK_SPIN_LOCK );
1414+ ::ReleaseSpinLock (std::addressof( m_SyncBlockValue) );
14151415}
14161416
1417- #endif // !DACCESS_COMPILE
1418-
1419- #ifndef DACCESS_COMPILE
1420-
14211417DWORD ObjHeader::GetSyncBlockIndex ()
14221418{
14231419 CONTRACTL
@@ -1442,7 +1438,7 @@ DWORD ObjHeader::GetSyncBlockIndex()
14421438 // Try one more time
14431439 if (GetHeaderSyncBlockIndex () == 0 )
14441440 {
1445- ENTER_SPIN_LOCK ( this );
1441+ EnterSpinLock ( );
14461442 // Now the header will be stable - check whether hashcode, appdomain index or lock information is stored in it.
14471443 DWORD bits = GetBits ();
14481444 if (((bits & (BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX | BIT_SBLK_IS_HASHCODE)) == (BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX | BIT_SBLK_IS_HASHCODE)) ||
@@ -1455,7 +1451,7 @@ DWORD ObjHeader::GetSyncBlockIndex()
14551451 {
14561452 SetIndex (BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX | SyncBlockCache::GetSyncBlockCache ()->NewSyncBlockSlot (GetBaseObject ()));
14571453 }
1458- LEAVE_SPIN_LOCK ( this );
1454+ ReleaseSpinLock ( );
14591455 }
14601456 // SyncBlockCache::LockHolder goes out of scope here
14611457 }
@@ -1621,7 +1617,7 @@ SyncBlock *ObjHeader::GetSyncBlock()
16211617
16221618 {
16231619 // after this point, nobody can update the index in the header
1624- ENTER_SPIN_LOCK ( this );
1620+ EnterSpinLock ( );
16251621
16261622 {
16271623 // If the thin lock in the header is in use, transfer the information to the syncblock
@@ -1661,7 +1657,7 @@ SyncBlock *ObjHeader::GetSyncBlock()
16611657 if (indexHeld)
16621658 syncBlock->SetPrecious ();
16631659
1664- LEAVE_SPIN_LOCK ( this );
1660+ ReleaseSpinLock ( );
16651661 }
16661662 }
16671663 // SyncBlockCache::LockHolder goes out of scope here
@@ -1716,7 +1712,7 @@ void SyncBlock::InitializeThinLock(DWORD recursionLevel, DWORD threadId)
17161712
17171713 _ASSERTE (m_Lock == (OBJECTHANDLE)NULL );
17181714 _ASSERTE (m_thinLock == 0u );
1719- m_thinLock = ( threadId & SBLK_MASK_LOCK_THREADID) | (recursionLevel << SBLK_RECLEVEL_SHIFT);
1715+ m_thinLock. StoreWithoutBarrier (( threadId & SBLK_MASK_LOCK_THREADID) | (recursionLevel << SBLK_RECLEVEL_SHIFT) );
17201716}
17211717
17221718OBJECTHANDLE SyncBlock::GetOrCreateLock (OBJECTREF lockObj)
@@ -1729,48 +1725,66 @@ OBJECTHANDLE SyncBlock::GetOrCreateLock(OBJECTREF lockObj)
17291725 }
17301726 CONTRACTL_END;
17311727
1732- if (m_Lock != (OBJECTHANDLE)NULL )
1728+ OBJECTHANDLE existingLock = VolatileLoad (&m_Lock);
1729+ if (existingLock != (OBJECTHANDLE)NULL )
17331730 {
1734- return m_Lock ;
1731+ return existingLock ;
17351732 }
17361733
17371734 SetPrecious ();
17381735
1739- // We need to create a new lock
1740- DWORD thinLock = m_thinLock;
1736+ // We'll likely need to put this lock object into the sync block.
1737+ // Create the handle here.
17411738 OBJECTHANDLEHolder lockHandle = GetAppDomain ()->CreateHandle (lockObj);
17421739
1740+ // Switch to preemptive so we can grab the spin-lock.
1741+ // Use the NO_DTOR version so we don't do a coop->preemptive->coop transition on return.
1742+ GCX_PREEMP_NO_DTOR ();
1743+
1744+ HeaderSpinLockHolder lock (std::addressof (m_thinLock));
1745+
1746+ // We don't need to be in preemptive any more here.
1747+ GCX_PREEMP_NO_DTOR_END ();
1748+
1749+ // Check again now that we hold the spin-lock
1750+ existingLock = m_Lock;
1751+ if (existingLock != (OBJECTHANDLE)NULL )
1752+ {
1753+ return existingLock;
1754+ }
1755+
1756+ // We need to create a new lock
1757+ // Grab the bits that are interesting for thin-lock info.
1758+ // This way we only call back into managed code
1759+ // to initialize the lock when necessary.
1760+ DWORD thinLock = (m_thinLock.LoadWithoutBarrier () & ((SBLK_MASK_LOCK_THREADID) | (SBLK_MASK_LOCK_RECLEVEL)));
1761+
17431762 if (thinLock != 0 )
17441763 {
1745- GCPROTECT_BEGIN (lockObj);
17461764
17471765 // We have thin-lock info that needs to be transferred to the lock object.
17481766 DWORD lockThreadId = thinLock & SBLK_MASK_LOCK_THREADID;
17491767 DWORD recursionLevel = (thinLock & SBLK_MASK_LOCK_RECLEVEL) >> SBLK_RECLEVEL_SHIFT;
17501768 _ASSERTE (lockThreadId != 0 );
17511769 PREPARE_NONVIRTUAL_CALLSITE (METHOD__LOCK__INITIALIZE_FOR_MONITOR);
17521770 DECLARE_ARGHOLDER_ARRAY (args, 3 );
1753- args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER (lockObj );
1771+ args[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER (ObjectFromHandle (lockHandle) );
17541772 args[ARGNUM_1] = DWORD_TO_ARGHOLDER (lockThreadId);
17551773 args[ARGNUM_2] = DWORD_TO_ARGHOLDER (recursionLevel);
17561774 CALL_MANAGED_METHOD_NORET (args);
1757-
1758- GCPROTECT_END ();
17591775 }
17601776
1761- OBJECTHANDLE existingHandle = InterlockedCompareExchangeT (&m_Lock, lockHandle.GetValue (), NULL );
1762-
1763- if (existingHandle != NULL )
1764- {
1765- return existingHandle;
1766- }
1777+ VolatileStore (&m_Lock, lockHandle.GetValue ());
17671778
17681779 // Our lock instance is in the sync block now.
17691780 // Don't release it.
17701781 lockHandle.SuppressRelease ();
1782+
17711783 // Also, clear the thin lock info.
17721784 // It won't be used any more, but it will look out of date.
1773- m_thinLock = 0u ;
1785+ // Only clear the relevant bits, as the spin-lock bit is used to lock this method.
1786+ // That bit will be reset upon return.
1787+ m_thinLock.StoreWithoutBarrier (m_thinLock.LoadWithoutBarrier () & ~((SBLK_MASK_LOCK_THREADID) | (SBLK_MASK_LOCK_RECLEVEL)));
17741788
17751789 return lockHandle;
17761790}
0 commit comments