void* Queue::Pop() { if ( 0 >= m_nReadAbleCount ) return NULL;//空列队 if ( 0 >= AtomDec(&m_nReadAbleCount,1)) //已经有足够多的pop操作读取列队不同位置的数据 { AtomAdd(&m_nReadAbleCount, 1); return NULL; } uint32 popPos = AtomAdd(&m_pop, 1); popPos = popPos % m_nSize; /* 只有在NPush并发情况下,因Push无序完成,第一个位置的Push未完成, 后面的Push就先完成提示有数据,导致这里检查第一个位置时,发现没有数据可读 因为该类只允许1对N,必然是单线程Pop(),所以条件内m_pop不需要并发控制 */ if ( m_queue[popPos].IsEmpty ) { m_pop--; AtomAdd(&m_nReadAbleCount,1); return NULL; } void *pObject = m_queue[popPos].pObject; m_queue[popPos].pObject = NULL; m_queue[popPos].IsEmpty = true; AtomAdd(&m_nWriteAbleCount,1); return pObject; }
bool Queue::Push( void *pObject ) { if ( NULL == pObject ) return false; if ( 0 >= m_nWriteAbleCount ) return false;//列队已满 if ( 0 >= AtomDec(&m_nWriteAbleCount,1) ) //已经有足够多的push操作向列队不同位置写入数据 { AtomAdd(&m_nWriteAbleCount, 1); return false; } uint32 pushPos = AtomAdd(&m_push, 1); pushPos = pushPos % m_nSize; /* 只有在NPop并发情况下,因Pop无序完成,第一个位置的Pop未完成, 后面的Pop就先完成提示有空位,导致这里检查第一个位置时,发现数据未被取走 因为该类只允许1对N,所以必然是单线程Push( void *pObject ),所以条件内m_push不需要并发控制 */ if ( !m_queue[pushPos].IsEmpty ) { m_push--; AtomAdd(&m_nWriteAbleCount,1); return false; } m_queue[pushPos].pObject = pObject; m_queue[pushPos].IsEmpty = false; AtomAdd(&m_nReadAbleCount,1); return true; }
void NetConnect::SetData( HostData *pData, bool autoFree ) { m_autoFreeData = autoFree; //释放已设置的数据或引用计数 if ( NULL != m_pHostData ) { if ( m_pHostData->m_autoFree ) return;//自由模式,不能解除关联 NetHost unconnectHost; m_pHostData->SetHost(&unconnectHost);//关联到未连接的host mdk::AutoLock lock(&m_mutexData); m_pHostData->Release();//释放数据,并发GetData()分析,参考HostData::Release()内部注释 /* 与GetData并发 可能返回NULL,也可能返回新HostData,也可能返回无效的HostData 就算提前到罪愆执行,结果也一样 就算对整个SetData加lock控制,也不会有多大改善 */ m_pHostData = NULL;//解除host与data的绑定 } if ( NULL == pData ) return; if ( -1 == AtomAdd(&pData->m_refCount, 1) ) return; //有线程完成了Release()的释放检查,对象即将被释放,放弃关联 pData->m_autoFree = autoFree; /* autoFree = true HostData的生命周期由框架管理 框架保证了,地址的有效性, 直接复制NetHost指针,到HostData中,不增加引用计数. autoFree = false HostData的生命周期由用户自行管理,框架不管 则复制NetHost对象到HostData中,引用计数增加,表示有用户在访问NetHost对象, 确保在用户释放HostData之前,NetHost永远不会被框架释放。 */ if ( autoFree ) { /* 代理模式,不需要增加引用计数,减回来 前面+1检查能通过,旧值最小应该=1,否则说明+1之后,外部又发生了重复Release(),是不合理的,触发断言 */ if ( 1 > AtomDec(&pData->m_refCount, 1) ) { mdk::mdk_assert(false); return; } pData->m_pHost = &m_host;//代理模式,copy规则限制了,pData不会超出host生命周期,只要pData存在,m_pHost必然存在,不必copy } else //自主模式 { pData->m_hostRef = m_host;//确保m_pHost指向安全内存,将host做1份copy } m_pHostData = pData;//必须最后,确保GetData()并发时,返回的时是完整数据 return; }
void* STNetEngine::MsgWorker( STNetConnect *pConnect ) { for ( ; !m_stop; ) { m_pNetServer->OnMsg( pConnect->m_host );//无返回值,避免框架逻辑依赖于客户实现 if ( !pConnect->m_bConnect ) break; if ( !pConnect->IsReadAble() ) break; } AtomDec(&pConnect->m_nReadCount,1); //确保NetServer::OnClose()一定在所有NetServer::OnMsg()完成之后 if ( !pConnect->m_bConnect ) NotifyOnClose(pConnect); return 0; }
void NetConnect::Release() { if ( 1 == AtomDec(&m_useCount, 1) ) { m_host.m_pConnect = NULL; if ( NULL == m_pMemoryPool ) { delete this; return; } this->~NetConnect(); m_pMemoryPool->Free(this); } }
void* NetEngine::MsgWorker( NetConnect *pConnect ) { for ( ; !m_stop; ) { pConnect->m_nReadCount = 1; m_pNetServer->OnMsg( pConnect->m_host );//无返回值,避免框架逻辑依赖于客户实现 if ( !pConnect->m_bConnect ) break; if ( pConnect->IsReadAble() ) continue; if ( 1 == AtomDec(&pConnect->m_nReadCount,1) ) break;//避免漏接收 } //触发OnClose(),确保NetServer::OnClose()一定在所有NetServer::OnMsg()完成之后 if ( !pConnect->m_bConnect ) NotifyOnClose(pConnect); pConnect->Release();//使用完毕释放共享对象 return 0; }
//关闭一个连接 void STNetEngine::CloseConnect( ConnectList::iterator it ) { /* 必须先删除再关闭,顺序不能换, 避免关闭后,erase前,正好有client连接进来, 系统立刻就把该连接分配给新client使用,造成新client在插入m_connectList时失败 */ STNetConnect *pConnect = it->second; m_connectList.erase( it );//之后不可能有MsgWorker()发生,因为OnData里面已经找不到连接了 AtomDec(&pConnect->m_useCount, 1);//m_connectList访问完成 pConnect->GetSocket()->Close(); pConnect->m_bConnect = false; if ( 0 == AtomAdd(&pConnect->m_nReadCount, 1) ) NotifyOnClose(pConnect); pConnect->Release();//连接断开释放共享对象 return; }
/* * 从缓冲区读取一定长度的数据 * 数据长度足够则成功,返回true * 否则失败,返回false */ bool IOBuffer::ReadData( unsigned char *data, int uLength, bool bDel ) { //这里检查m_uDataSize不需要原子操作 //cpu与内存1次交换最小长度就是4byte,对于小于等于4byte的类型的取值/赋值操作, //本身就是原子操作 if ( 0 > uLength || m_uDataSize < uLength ) return false;//读取长度小于0,或数据不够,不执行读取 //读取数据 IOBufferBlock *pRecvBlock = NULL; AutoLock lock( &m_mutex ); vector<IOBufferBlock*>::iterator it = m_recvBufferList.begin(); unsigned short uRecvSize = 0; unsigned short uStartPos = 0; for ( ; it != m_recvBufferList.end(); ) { pRecvBlock = *it; uRecvSize = pRecvBlock->ReadData( &data[uStartPos], uLength, bDel ); if ( bDel ) AtomDec(&m_uDataSize, uRecvSize); if ( uLength == uRecvSize ) return true;//读取完成 //缓冲内容不足够读取,从下一块缓冲读取 uStartPos += uRecvSize; uLength -= uRecvSize; if ( bDel )//删除缓冲块 { //前面已经保证数据足够,不可能从正在等待的缓冲块中还接收不完整数据 //不用做以下检查 //if ( m_pRecvBufferBlock == pRecvBlock ) m_pRecvBufferBlock = NULL; //写操作只会在m_pRecvBufferBlock当前指向的内存上进行 //删除操作绝对不在m_pRecvBufferBlock当前指向的内存上进行 //读操作要么在m_pRecvBufferBlock之前的内存上进行 //要么在m_pRecvBufferBlock当前指向的内存块前端,已经写入完成的byte上进行 //结论,并发读写永远不会发生冲突,无锁 delete pRecvBlock;//释放缓冲块 m_recvBufferList.erase( it ); it = m_recvBufferList.begin();//准备从下一个缓冲块中读取 } else it++; } return true; }
bool Signal::Wait( unsigned long lMillSecond ) { bool bHasSingle = true; #ifdef WIN32 int nObjIndex = WaitForSingleObject( m_signal, lMillSecond ); if ( WAIT_TIMEOUT == nObjIndex || (WAIT_ABANDONED_0 <= nObjIndex && nObjIndex <= WAIT_ABANDONED_0 + 1) ) bHasSingle = false; #else AtomAdd(&m_waitCount, 1); unsigned long notimeout = -1; if ( notimeout == lMillSecond ) { sem_wait( &m_signal );//等待任务 } else { int nSecond = lMillSecond / 1000; int nNSecond = (lMillSecond - nSecond * 1000) * 1000; timespec timeout; timeout.tv_sec=time(NULL) + nSecond; timeout.tv_nsec=nNSecond; if ( 0 != sem_timedwait(&m_signal, &timeout) ) bHasSingle = false; } /* windows行为,在没有wait时,notify n次 之后再有多个wait,只能通过一个 linux行为,在没有wait时,notify n次 之后再有多个wait,会通过n个,第n+1个开始阻塞 简单说就是windows上第2~n个notify的信号丢失了 为了和windows行为一致,当等待线程为0时,将多余的信号丢弃 */ if ( 1 == AtomDec(&m_waitCount, 1) ) { sem_init( &m_signal, 1, 0 ); } #endif return bHasSingle; }