Darwin中OSRef和OSHashTable类的使用
2015-12-30 17:12
190 查看
//哈希表被设计成模版类的形式
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;
};
引用表头文件定义,详细的代码请参考源码,此处只结合实例讲解几个主要的函数
//结合实例说明常用的方法
服务器网络模型中有个很重要的类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)
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)
相关文章推荐
- 为什么Enable BitCode(Xcode7)真机测试要修改为 NO?
- 【蒟蒻の进阶PLAN】 置顶+持续连载
- 新发现:云盘转移multcloud
- 写在年尾
- 1. Two Sum
- 运维安全系列基础服务之 FTP 服务(系列一)
- 数据挖掘十大经典算法
- java Thread ,Thread.currentThread().getName() 的含义 & 普通成员变量是何时被赋值的
- android http协议详细
- How to play videos in android from assets folder or raw folder?
- jQuery.page.js用法
- EasyUI combogrid 赋多个值
- 解决.NET 32位程序运行在64位操作系统下的兼容性问题
- 深拷贝和浅拷贝
- 【tyvj1038】忠诚
- nGrinder学习笔记 — 在IDEA搭建nGrinder开发环境
- 我的Python成长之路---第一天---Python基础(作业2:三级菜单)---2015年12月26日(雾霾)
- 对于成长社区的发展的个人思考
- Android MediaPlayer 播放prepareAsync called in state 8解决办法
- SQLite中DML DDL DML命令的区别[转]