디스패처는 멀티플래싱하는 부분과 디멀티플래싱하는 부분으로 구분됩니다. Select Reactor나 TP Reactor는 select 라는 디멀티플래싱 함수를 사용했고, 멀티플래싱하는 과정은 FD_SET를 사용했습니다. 그에 비해 WFMO Reactor는 다음의 멀티 플래싱 함수를 사용합니다.
DWORD WINAPI WaitForMultipleObjects(
_In_ DWORD nCount,
_In_ const HANDLE *lpHandles,
_In_ BOOL bWaitAll,
_In_ DWORD dwMilliseconds
);
두번째 인자는 HANDLE에 대한 포인터로 최대 64개의 HANDLE에 대한 멀티 플래싱을 매개 변수로 전달할 수 있습니다. 디멀티플래싱 결과는 DWORD로 반환합니다.
select 함수와 다른 점은 여러개의 쓰레드에서 해당 함수를 호출하더라도 쓰레드에 안정적으로 시그널당 처리를 할 수 있습니다. HANDLE에 등록할 수 있는 유형은 다음과 같습니다.
- Change notification
- Console input
- Event
- Memory resource notification
- Mutex
- Process
- Semaphore
- Thread
- Waitable timer
소켓 IO는 Event 형식으로 등록해줄 수 있습니다.
int WSAEventSelect(
_In_ SOCKET s,
_In_ WSAEVENT hEventObject,
_In_ long lNetworkEvents
);
상단 함수는 소켓과 연관된 Event 핸들러를 생성할 때 사용합니다. 3번재 인자는 마스크 데이터 옵셥입니다.
int WSAEnumNetworkEvents(
_In_ SOCKET s,
_In_ WSAEVENT hEventObject,
_Out_ LPWSANETWORKEVENTS lpNetworkEvents
);
상단 함수는 시그널이 발생했을때 어떤 마스크 옵션에 대한 이벤트인지 추적할 때 사용합니다.
WFMO Reactor는 최대 64개의 핸들을 등록할 수 있습니다. 2개는 Reactor가 자체적으로 사용합니다.(알람 기능과 전체 쓰레드 깨움 관련으로 HANDLE를 사용합니다.) 따라서 실제 등록 가능한 핸들은 62개입니다.
WRITE_MASK에 대한 처리가 select 또는 TP Reactor와 다릅니다. handle_output 함수가 select 함수 기반 디스펴처에서는 송신 가능한 상태가 되면 지속적으로 호출했지만, WFMO Reactor는 초기 접속이 되었을때와 흐름 제어에서 벗어날때 한번 호출됩니다. 따라서 handle_output 함수에는 do while(true) 구문으로 모든 데이터를 보내도록 노력해야 합니다. 만약 send 함수에서 반환값이 -1이고 EWOULDBLOCK==ACE_OS::last_error() 이면 반복문에서 벗어나야 합니다.
intEchoService::handle_output (ACE_HANDLE fd/* = ACE_INVALID_HANDLE*/){
ACE_Message_Block* mb(NULL);
ACE_Time_Value rt(0,0);
do{
this->send_datas_.dequeue_head(mb,&rt);
ssize_t len=this->peer_.send(mb->rd_ptr(), mb->length(),&rt);
if( -1==len && EWOULDBLOCK==ACE_OS::last_error()){//버퍼가 찬 상태
this->send_datas_.enqueue_head( mb );
break;
}
if( len <0){//통신 에러
mb->release();
return -1;
}
mb->rd_ptr(len);
if( mb->length()>0){
this->send_datas_.enqueue_head( mb );
continue;
}
mb->release();
if( this->send_datas_.is_empty()){
ACE_Reactor_Mask m = ACE_Event_Handler::WRITE_MASK | ACE_Event_Handler::DONT_CALL;
this->reactor()->remove_handler(this, m);
break;
}
}while(true);
return 0;
}
handle_close 함수는 모든 쓰레드에서 해당 이벤트 핸들러를 처리한 이후에 지연 호출됩니다.