前面通过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中,如下: