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看看我的搜索条件:

image

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中,如下:

posted @ 2012-03-25 00:26  Dance With Automation  Views(695)  Comments(0Edit  收藏  举报