start
1[MethodImpl(MethodImplOptions.InternalCall), SecuritySafeCritical, __DynamicallyInvokable] 2public static extern void Enter(object obj);
src/vm/ecall.cpp
1FCFuncStart(gMonitorFuncs) 2 FCFuncElement("Enter", JIT_MonEnter) 3 FCFuncElement("Exit", JIT_MonExit) 4 FCFuncElement("TryEnterTimeout", JIT_MonTryEnter) 5 FCFuncElement("ObjWait", ObjectNative::WaitTimeout) 6 FCFuncElement("ObjPulse", ObjectNative::Pulse) 7 FCFuncElement("ObjPulseAll", ObjectNative::PulseAll) 8 FCFuncElement("ReliableEnter", JIT_MonReliableEnter) 9FCFuncEnd()
next -> JIT_MonEnter
clr/src/vm/jithelpers.cpp
1HCIMPL_MONHELPER (JIT_MonEnterWorker_Portable, Object* obj) 2{ 3 ....... 4}
相关参数 :
1SyncBlock* syncBlock = NULL;//😢同步索引块 2ObjHeader* objHeader = NULL;//对象头 3int spincount = 50;//spin - 轮询 😢采用了轮询机制 4const int MaxSpinCount = 20000 * g_SystemInfo.dwNumberOfProcessors; 5LONG oldvalue, state; 6DWORD tid; 7 8objHeader = obj->GetHeader();//获取请求头 9 10OBJECTREF objRef = ObjectToOBJECTREF(obj);//获取请求头的引用 11 12objRef->EnterObjMonitor();//调用EnterObjMonitor
源码追踪
1.GetHeader()方法获取对象头ObjHeader,在ObjHeader里有对EnterObjMonitor()方法的定义:
clr/src/vm/object.cpp
1// 获取请求头 2// 访问对象上负偏移量的ObjHeader(因为高速缓存线路) 3// Access the ObjHeader which is at a negative offset on the object (because of 4// cache lines) 5ObjHeader *GetHeader() 6{ 7 LEAF_CONTRACT; 8 return PTR_ObjHeader(PTR_HOST_TO_TADDR(this) - sizeof(ObjHeader)); 9}
next->查看ObjHeader的EnterObjMonitor的定义
clr/src/vm/syncblk.cpp
1void ObjHeader::EnterObjMonitor() 2{ 3 WRAPPER_CONTRACT; 4 GetSyncBlock()->EnterMonitor(); 5}
又调用了GetSyncBlock的EnterMonitor 追下去。
clr/src/vm/syncblk.h
1void EnterMonitor() 2{ 3 WRAPPER_CONTRACT; 4 m_Monitor.Enter(); 5}
调用了m_Monitor的Enter
查看m_Monitor的定义:
1protected: 2AwareLock m_Monitor; // the actual monitor
继续 查看AwareLock的Enter方法定义,感觉已经越来越近了😢
1void AwareLock::Enter() 2{ 3 CONTRACTL 4 { 5 INSTANCE_CHECK; 6 THROWS; 7 GC_TRIGGERS; 8 MODE_ANY; 9 INJECT_FAULT(COMPlusThrowOM();); 10 } 11 CONTRACTL_END; 12 13 Thread *pCurThread = GetThread(); 14 15 for (;;) 16 { 17 // Read existing lock state. 18 volatile LONG state = m_MonitorHeld; 19 20 if (state == 0) 21 { 22 // Common case: lock not held, no waiters. Attempt to acquire lock by 23 //常见情况:锁没锁,没有服务员。试图获得锁定 24 // switching lock bit. 25 //开关锁。 26 if (FastInterlockCompareExchange((LONG*)&m_MonitorHeld, 1, 0) == 0)//cas 修改值。 27 { 28 break; 29 } 30 } 31 else 32 { 33 // It's possible to get here with waiters but no lock held, but in this 34 // case a signal is about to be fired which will wake up a waiter. So 35 // for fairness sake we should wait too. 36 // Check first for recursive lock attempts on the same thread. 37 if (m_HoldingThread == pCurThread)//如果为当前线程 38 { 39 goto Recursion; 40 } 41 42 // Attempt to increment this count of waiters then goto contention 43 // handling code. 44 if (FastInterlockCompareExchange((LONG*)&m_MonitorHeld, (state + 2), state) == state) 45 { 46 goto MustWait; 47 } 48 } 49 50 } 51 52 // We get here if we successfully acquired the mutex. 53 m_HoldingThread = pCurThread; 54 m_Recursion = 1; 55 pCurThread->IncLockCount(); 56 57#if defined(_DEBUG) && defined(TRACK_SYNC) 58 { 59 // The best place to grab this is from the ECall frame 60 Frame *pFrame = pCurThread->GetFrame(); 61 int caller = (pFrame && pFrame != FRAME_TOP 62 ? (int) pFrame->GetReturnAddress() 63 : -1); 64 pCurThread->m_pTrackSync->EnterSync(caller, this); 65 } 66#endif 67 68 return; 69 70MustWait: 71 // Didn't manage to get the mutex, must wait. //继续等待 72 EnterEpilog(pCurThread); 73 return; 74 75Recursion: 76 // Got the mutex via recursive locking on the same thread. 77 _ASSERTE(m_Recursion >= 1); 78 m_Recursion++;//递归次数加1 79#if defined(_DEBUG) && defined(TRACK_SYNC) 80 // The best place to grab this is from the ECall frame 81 Frame *pFrame = pCurThread->GetFrame(); 82 int caller = (pFrame && pFrame != FRAME_TOP ? (int) pFrame->GetReturnAddress() : -1); 83 pCurThread->m_pTrackSync->EnterSync(caller, this); 84#endif 85}
从上面的代码我们可以看到,先使用GetThread()获取当前的线程,然后取出m_MonitorHeld字段,如果现在没有线程进入临界区,则设置该字段的状态,然后将m_HoldingThread设置为当前线程,从这一点上来这与Win32的过程应该是一样的。
如果从m_MonitorHeld字段看,有线程已经进入临界区则分两种情况:第一,是否已进入的线程如当前线程是同一个线程,如果是,则把m_Recursion递加,如果不是,则通过EnterEpilog( pCurThread)方法,当前线程进入线程等待队列。
通过上面的文字描述和代码的跟踪,在我们的大脑中应该有这样一张图了:

confirm
clr/src/vm/syncblk.h
ObjHeader has an index to a SyncBlock. This index is 0 for the bulk of all
ObjHeader有一个指向同步块的索引。大多数情况下,这个指数是0
相关链接
https://github.com/SSCLI/sscli20_20060311
http://www.aspphp.online/bianchen/dnet/gydnet/201701/14624.html
此次只是简单的追踪了一番,部分内容可能不详细。