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

  • 相关阅读:
    JAVA--导数到Mongodb
    关于jquery的事件委托-bind,live,delegate,on的区别发展
    cookies localStorage和sessionStorage的区别
    px em 和rem之间的区别
    js中string常用方法
    js中number常用方法
    json格式常用操作
    Node.js到底是做什么的?这是我看到最好的解释了
    数组常用操作方法
    JQuery.Ajax()的data参数类型
  • 原文地址:https://www.cnblogs.com/dancewithautomation/p/2416260.html
Copyright © 2020-2023  润新知