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;
}
示例#4
0
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;
}
示例#7
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;
}
示例#9
0
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;
}