Darwin中OSRef和OSHashTable类的使用

 

//哈希表被设计成模版类的形式

template<class T, class K>

class OSHashTable {

public:

   OSHashTable( UInt32 size ) //构造函数

    {

       fHashTable = new ( T*[size] );//初始化大小

       Assert( fHashTable );

       memset( fHashTable, 0, sizeof(T*) * size );//设置初始值

       fSize = size;

        /*下面的代码决定用哪种方式为哈希表的键值计算索引;

如果哈希表的大小不是2的幂,只好采用对fSize求余的方法;

否则可以直接用掩码的方式,这种方式相对速度更快*/      

 fMask = fSize - 1;

        if((fMask & fSize) != 0)//判断是不是2的幂,确定使用何种哈希函数(ComputeIndex

           fMask = 0;

       fNumEntries = 0;

    }

   ~OSHashTable() //析构

    {

       delete [] fHashTable;

    }

    voidAdd( T* entry ) { //加入元素,有标记代码可以看出,此处解决冲突的方式采用了链地址法

       Assert( entry->fNextHashEntry == NULL );

        Kkey( entry );

       UInt32 theIndex = ComputeIndex( key.GetHashKey() );

        entry->fNextHashEntry = fHashTable[theIndex ];

       fHashTable[ theIndex ] = entry;

       fNumEntries++;

    }

    voidRemove( T* entry )//移除元素

    {

        Kkey( entry );

       UInt32 theIndex = ComputeIndex( key.GetHashKey() );

        T*elem = fHashTable[ theIndex ];

        T*last = NULL;

       while (elem && elem != entry) {

           last = elem;

           elem = elem->fNextHashEntry;

        }

   

        if( elem ) // sometimes remove is called 2x ( swap, then un register )

        {

           Assert(elem);

           if (last)

               last->fNextHashEntry = elem->fNextHashEntry;

           else

                fHashTable[ theIndex ] =elem->fNextHashEntry;

           elem->fNextHashEntry = NULL;

           fNumEntries--;

        }

    }

    T* Map(K* key ) //查找对象

    {

       UInt32 theIndex = ComputeIndex( key->GetHashKey() );

        T*elem = fHashTable[ theIndex ];

       while (elem) {

           K elemKey( elem );

           if (elemKey == *key)

               break;

           elem = elem->fNextHashEntry;

        }

       return elem;

    }

    UInt64GetNumEntries() { return fNumEntries; }

   

    UInt32GetTableSize() { return fSize; }

    T*GetTableEntry( int i ) { return fHashTable[i]; }

 

private:

    T**fHashTable;

    UInt32fSize;

    UInt32fMask;

    UInt64fNumEntries;

    UInt32 ComputeIndex(UInt32 hashKey )

   {

       if (fMask)

            return( hashKey & fMask );//掩码方式

       else

            return( hashKey % fSize );// 除留取余法

   }

};

//实现了一个hash表迭代器的功能

template<class T, class K>

class OSHashTableIter {

public:

   OSHashTableIter( OSHashTable<T,K>* table )

    {

       fHashTable = table;

       First();

    }

    voidFirst()

    {

        for(fIndex = 0; fIndex < fHashTable->GetTableSize(); fIndex++) {

           fCurrent = fHashTable->GetTableEntry( fIndex );

           if (fCurrent)

               break;

        }

    }

    voidNext()

    {

       fCurrent = fCurrent->fNextHashEntry;

        if(!fCurrent) {

           for (fIndex = fIndex + 1; fIndex < fHashTable->GetTableSize();fIndex++) {

                fCurrent =fHashTable->GetTableEntry( fIndex );

               if (fCurrent)

                    break;

           }

        }

    }

    Bool16IsDone()

    {

       return( fCurrent == NULL );

    }

    T*GetCurrent() { return fCurrent; }

   

private:

   OSHashTable<T,K>* fHashTable;

    T*fCurrent;

    UInt32fIndex;

class OSRefKey;
class OSRefTableUtils
{
   private:
       static UInt32  HashString(StrPtrLen* inString);   
       friend class OSRef;
       friend class OSRefKey;
};
class OSRef
{
    public:
       OSRef() :   fObjectP(NULL),fRefCount(0), fNextHashEntry(NULL)
           {    
           }
       OSRef(const StrPtrLen &inString, void* inObjectP)
                                : fRefCount(0),fNextHashEntry(NULL)
                                    {   Set(inString, inObjectP); }
       ~OSRef() {}
        void Set(const StrPtrLen& inString,void* inObjectP)
           {     
               fString = inString; fObjectP = inObjectP;
               fHashValue = OSRefTableUtils::HashString(&fString);
           }
       void**  GetObjectPtr()  { return &fObjectP; }
       void*   GetObject()     { return fObjectP; }
       UInt32  GetRefCount()   { return fRefCount; }
       StrPtrLen *GetString()  { return&fString; }
   private:
       //value
       void*   fObjectP;
       //key
       StrPtrLen   fString;
       //refcounting
        UInt32  fRefCount;
#if DEBUG
       Bool16  fInATable;
       Bool16  fSwapCalled;
#endif
       OSCond  fCond;//to block threadswaiting for this ref.
       UInt32              fHashValue;
       OSRef*             fNextHashEntry;
        friend class OSRefKey;
       friend class OSHashTable<OSRef, OSRefKey>;
       friend class OSHashTableIter<OSRef, OSRefKey>;
       friend class OSRefTable;
};
class OSRefKey
{
public:
   //CONSTRUCTOR / DESTRUCTOR:
   OSRefKey(StrPtrLen* inStringP)
       :   fStringP(inStringP)
         {fHashValue = OSRefTableUtils::HashString(inStringP); }
   ~OSRefKey() {}
   //ACCESSORS:
   StrPtrLen*  GetString()         { return fStringP; }
private:
   //PRIVATE ACCESSORS:   
   SInt32      GetHashKey()        { return fHashValue; }
    //thesefunctions are only used by the hash table itself. This constructor
    //willbreak the "Set" functions.
   OSRefKey(OSRef *elem) : fStringP(&elem->fString),
                           fHashValue(elem->fHashValue) {}                  
    friendint operator ==(const OSRefKey &key1, const OSRefKey &key2)
    {
        if(key1.fStringP->Equal(*key2.fStringP))
           return true;
       return false;
    }
    //data:
   StrPtrLen *fStringP;
   UInt32  fHashValue;
    friendclass OSHashTable<OSRef, OSRefKey>;
};
typedef OSHashTable<OSRef, OSRefKey>OSRefHashTable;
typedef OSHashTableIter<OSRef, OSRefKey>OSRefHashTableIter;
class OSRefTable
{
    public:
       enum
        {
           kDefaultTableSize = 1193 //UInt32
        };
       //tableSize doesn't indicate the max number of Refs that can be added
       //(it's unlimited), but is rather just how big to make the hash table
       OSRefTable(UInt32 tableSize = kDefaultTableSize) : fTable(tableSize),fMutex() {}
       ~OSRefTable() {}
       //Allows access to the mutex in case you need to lock the table down
       //between operations
       OSMutex*    GetMutex()      { return &fMutex; }
       OSRefHashTable* GetHashTable() { return &fTable; 
       //Registers a Ref in the table. Once the Ref is in, clients may resolve
       //the ref by using its string ID. You must setup the Ref before passingit
       //in here, ie., setup the string and object pointers
       //This function will succeed unless the string identifier is not unique,
       //in which case it will return QTSS_DupName
        //This function is atomic wrt this reftable.
       OS_Error        Register(OSRef*ref);
        //RegisterOrResolve
        //If the ID of the input ref is unique, this function is equivalent to
        //Register, and returns NULL.
        // If there is a duplicate ID already inthe map, this funcion
        //leave it, resolves it, and returns it.
       OSRef*             RegisterOrResolve(OSRef* inRef);
       //This function may block. You can only remove a Ref from the table
       //when the refCount drops to the level specified. If several threadshave
       //the ref currently, the calling thread will wait until the otherthreads
       //stop using the ref (by calling Release, below)
       //This function is atomic wrt this ref table.
       void        UnRegister(OSRef* ref,UInt32 refCount = 0);
        //Same as UnRegister, but guarenteed not to block. Will return
        //true if ref was sucessfully unregistered, false otherwise
       Bool16      TryUnRegister(OSRef*ref, UInt32 refCount = 0);
       //Resolve. This function uses the provided key string to identify andgrab
       //the Ref keyed by that string. Once the Ref is resolved, it is safe touse
       //(it cannot be removed from the Ref table) until you call Release.Because
       //of that, you MUST call release in a timely manner, and be aware ofpotential
       //deadlocks because you now own a resource being contended over.
       //This function is atomic wrt this ref table.
       OSRef*     Resolve(StrPtrLen*  inString);
       //Release. Release a Ref, and drops its refCount. After calling this,the
       //Ref is no longer safe to use, as it may be removed from the ref table.
        void       Release(OSRef*  inRef);
        //Swap. This atomically removes any existing Ref in the table with the new
        //ref's ID, and replaces it with this new Ref. If there is no matching Ref
        //already in the table, this function does nothing.
        //
        //Be aware that this creates a situation where clients may have a Ref resolved
        //that is no longer in the table. The old Ref must STILL be UnRegisterednormally.
        //Once Swap completes sucessfully, clients that call resolve on the ID will get
        //the new OSRef object.
       void        Swap(OSRef* newRef);
       UInt32      GetNumRefsInTable() {UInt64 result =  fTable.GetNumEntries();Assert(result < kUInt32_Max); return (UInt32) result; }
   private:
       //all this object needs to do its job is an atomic hashtable
       OSRefHashTable  fTable;
       OSMutex         fMutex;
};
class OSRefReleaser
{
    public:
       OSRefReleaser(OSRefTable* inTable, OSRef* inRef) : fOSRefTable(inTable),fOSRef(inRef) {}
       ~OSRefReleaser() { fOSRefTable->Release(fOSRef); }
       OSRef*          GetRef() { returnfOSRef; }
   private:
       OSRefTable*     fOSRefTable;
       OSRef*          fOSRef;
};
 

};

 

引用表头文件定义,详细的代码请参考源码,此处只结合实例讲解几个主要的函数


//结合实例说明常用的方法

服务器网络模型中有个很重要的类EventContext, EventContext.h中包含EventContext类和EventThread类的定义

每一个EventContext类中都有一个引用对象,如下图

在每次执行RequestEvent函数时,就会执行以下代码(EventContext.cpp182行)

if (!compare_and_store(8192, WM_USER,&sUniqueID))

           fUniqueID = (PointerSizedInt)atomic_add(&sUniqueID, 1);      //获取一个唯一标识  

fRef.Set(fUniqueIDStr, this);//对引用对象赋值

void Set(const StrPtrLen&inString, void* inObjectP)

            {

                fString = inString; fObjectP =inObjectP;

                fHashValue =OSRefTableUtils::HashString(&fString);

            }

fString作为索引,fObjectP保存对象,fHashValue根据索引计算出一个hash

fEventThread->fRefTable.Register(&fRef);//把这个引用对象加入到EventThread中的引用表中(其实就是hash表),fRefTable是OSRefTable类的实例,而类中操作的表是OSRefHashTable类型(typedef OSHashTable<OSRef, OSRefKey>OSRefHashTable;)

OS_ErrorOSRefTable::Register(OSRef* inRef)

{

       if (inRef == NULL)

        return EPERM;

   OSMutexLocker locker(&fMutex);

   if (inRef->fString.Ptr == NULL || inRef->fString.Len == 0)

   {         return EPERM;

   }

   // Check for a duplicate. In this function, if there is a duplicate,

   // return an error, don't resolve the duplicate

   OSRefKey key(&inRef->fString);

   OSRef* duplicateRef = fTable.Map(&key);//查找有没有重复的,没有则加入到hash表中

   if (duplicateRef != NULL)

        return EPERM;

       

   // There is no duplicate, so add this ref into the table

   fTable.Add(inRef);

   return OS_NoErr;

}

::memset( &fEventReq, '\0',sizeof(fEventReq));//下面的代码其实就是把socket加入到select监视中,由于本文主要讲解下引用表相关类的使用,所以此处不再详细描述

 fEventReq.er_type = EV_FD;

 fEventReq.er_handle = fFileDesc;

 fEventReq.er_eventbits = theMask;

 fEventReq.er_data = (void*)fUniqueID;

if (select_watchevent(&fEventReq, theMask) !=0)

 

========以上代码描述了构造一个ref,然后加入reftable中的操作

 

在EventThread的线程执行函数Entry中,使用了reftable查找EventContext对象

当select返回一个可操作的socket时,执行了以下代码,

if (theCurrentEvent.er_data != NULL)// theCurrentEvent就是select返回的数据

        {

        

           StrPtrLen idStr((char*)&theCurrentEvent.er_data,sizeof(theCurrentEvent.er_data));

//返回的数据用于构造一个id,这个id其实就是在上一步中得到的唯一标识,如下图

           OSRef* ref = fRefTable.Resolve(&idStr);//根据这个唯一标识获取到引用对象,其实就是通过hash类中map函数去查找对象,然后把引用对象的引用计数+1

           if (ref != NULL)

           {

               EventContext* theContext = (EventContext*)ref->GetObject();

               theContext->ProcessEvent(theCurrentEvent.er_eventbits);

               fRefTable.Release(ref);//把引用对象的引用计数-1,然后设置事件为有信号,确保唤醒等待该资源被释放的对象

           }

        }

以上说明是通过darwin中一个使用实例,为了方面理解引用表和哈希表的使用(OSRef和OSHashTable)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值