Monitor.Wait初探(4)
前面通过Windbg调试步步追踪最后终于发现Wait函数涉及WaitForMultipleObjectsEx的系统调用。
不过这里我还想换一个角度再来追踪一把,这次是通过.Net源码分析进行追踪。
当然,需要首先下载Rotor并配置好,这里假设全部源码文件放在目录d:\sscli20,这里略去不讲。
我们已经知道Wait在托管层最后调用的实际是ObjWait这个函数:
[MethodImpl(MethodImplOptions.InternalCall), SecurityCritical] private static extern bool ObjWait(bool exitContext, int millisecondsTimeout, object obj);
这个函数的实现是非托管的C++代码实现,而文件sscli20\clr\src\vm\ecall.cpp记录的就是InternalCall类型的托管层函数对应的native方法的一个映射。
注意vm子目录下方的全是此类.net managed code的native实现。
在ecall.cpp搜索ObjWait找到如下内容,原来是映射到了ObjectNative类的WaitTimeout方法,
FCFuncStart(gMonitorFuncs)
  
    FCFuncElement("Enter", JIT_MonEnter)
  
    FCFuncElement("Exit", JIT_MonExit)
  
    FCFuncElement("TryEnterTimeout", JIT_MonTryEnter)
      FCFuncElement("ObjWait", ObjectNative::WaitTimeout)
        FCFuncElement("ObjPulse", ObjectNative::Pulse)
  
    FCFuncElement("ObjPulseAll", ObjectNative::PulseAll)
  
    FCFuncElement("ReliableEnter", JIT_MonReliableEnter)
  
FCFuncEnd()
在vm目录搜素包含class ObjectNative的头文件,地球人都知道类的原型一般都定义在头文件.h,so看看我的搜索条件:
ok,原来定义在comobject.h中,相应的WaitTimeOut函数的实现应该在comobject.cpp中,打开之,看到:
FCIMPL3(FC_BOOL_RET, ObjectNative::WaitTimeout, CLR_BOOL exitContext, INT32 Timeout, Object* pThisUNSAFE)
  
{
  
    CONTRACTL
  
    {
  
        MODE_COOPERATIVE;
  
        DISABLED(GC_TRIGGERS);  // can't use this in an FCALL because we're in forbid gc mode until we setup a H_M_F.
  
        SO_TOLERANT;
  
        THROWS;
  
    }
  
    CONTRACTL_END; 
    BOOL retVal = FALSE;
  
    OBJECTREF pThis = (OBJECTREF) pThisUNSAFE;
  
    HELPER_METHOD_FRAME_BEGIN_RET_1(pThis);
  
    //-[autocvtpro]------------------------------------------------------- 
    if (pThis == NULL)
  
        COMPlusThrow(kNullReferenceException, L"NullReference_This"); 
    if ((Timeout < 0) && (Timeout != INFINITE_TIMEOUT))
  
        COMPlusThrowArgumentOutOfRange(L"millisecondsTimeout", L"ArgumentOutOfRange_NeedNonNegNum"); 
    retVal = pThis->Wait(Timeout, exitContext); 
    //-[autocvtepi]-------------------------------------------------------
  
    HELPER_METHOD_FRAME_END();
  
    FC_RETURN_BOOL(retVal);
  
}
  
FCIMPLEND
现在我们看到函数体中最终调用的是pThis->Wait,pThis是个啥玩意呢,通过分析代码,发现它就是WaitTimeOut函数的最后一个参数Object* pThisUNSAFE的一个引用,原来是一个Object类型,那这里的Object和c#的object或者.Net的Object有啥关系,大胆猜想,这其实就是托管Object对应的native Object。而事实也应如此。
那麽废话不多说,我们要来看看此Object的Wait实现,依然避免不了搜索一番,首先我们在object.h中找到了Object类的定义,摘取其说明如下,也印证了刚才的猜想:
/*
  
 * Object
  
 *
  
 * This is the underlying base on which objects are built.   The MethodTable
  
 * pointer and the sync block index live here.  The sync block index is actually
  
 * at a negative offset to the instance.  See syncblk.h for details.
  
 *
  
 */
注意这句话:The MethodTable * pointer and the sync block index live here. 每个对象都要维护自己的方法表,我们稍微瞥一眼该类的定义就发现如下定义:
class Object
  
{
  
  protected:
  
    MethodTable*    m_pMethTab;
。。。。。
那麽什麽是sync block index呢?其实就是翻译过来的同步索引块,还记得这个玩意麽?在.Net CLR via C#一书中我们知道一个引用类型的对象都要额外负担两个东西的维护:类型指针和同步索引块,原文是这麽说的:
all objects on the heap contain two overhead members: the type object pointer and the sync block index
回归正题,Object::Wait的实现仍然在头文件中,如下:
BOOL Wait(INT32 timeOut, BOOL exitContext)
  
{
  
    WRAPPER_CONTRACT;
  
    return GetHeader()->Wait(timeOut, exitContext);
  
}
哦,原来是先调用了GetHeader方法获取对象头,然后调用对象头的Wait方法,追下去,GetHeader方法的实现:
// Sync Block & Synchronization services
// Access the ObjHeader which is at a negative offset on the object (because of
  
// cache lines)
  
ObjHeader   *GetHeader()
  
{
  
    LEAF_CONTRACT;
  
    return PTR_ObjHeader(PTR_HOST_TO_TADDR(this) - sizeof(ObjHeader));
  
}
看来要想往下追,还必须看对象头ObjHeader类的Wait方法实现:在syncblk.h中找到了其定义,在对应的cpp文件中找到了其相应的实现如下:
BOOL ObjHeader::Wait(INT32 timeOut, BOOL exitContext)
  
{
  
    CONTRACTL
  
    {
  
        INSTANCE_CHECK;
  
        THROWS;
  
        GC_TRIGGERS;
  
        MODE_ANY;
  
        INJECT_FAULT(COMPlusThrowOM(););
  
    }
  
    CONTRACTL_END; 
    //  The following code may cause GC, so we must fetch the sync block from
  
    //  the object now in case it moves.
      SyncBlock *pSB = GetBaseObject()->GetSyncBlock(); 
    // GetSyncBlock throws on failure
  
    _ASSERTE(pSB != NULL); 
    // make sure we own the crst
  
    if (!pSB->DoesCurrentThreadOwnMonitor())
  
        COMPlusThrow(kSynchronizationLockException); 
#ifdef _DEBUG
  
    Thread *pThread = GetThread();
  
    DWORD curLockCount = pThread->m_dwLockCount;
  
#endif 
    BOOL result = pSB->Wait(timeOut,exitContext); 
_ASSERTE (curLockCount == pThread->m_dwLockCount);
    return result;
  
}
看到了嘛!!!!该Wait实现最重要的两行代码终于浮现出来了,它们就是加横线的两行。
第一行 SyncBlock *pSB = GetBaseObject()->GetSyncBlock(); 用来获取对象的索引块;
第二行 BOOL result = pSB->Wait(timeOut,exitContext); 嗯,越来越接近真相,原来又调用了索引块对象的Wait方法。
那继续吧,看看SyncBlock 类型的Wait方法实现,依旧在syncblk.cpp中,如下:
 
                    
                     
                    
                 
                    
                
 

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号