그런데, 이 함수에 넘겨지는 block 인자의 메모리 소멸 시점이 애매합니다.
기본적으로 SendRequest() 함수가 false 를 리턴하면 block 인자로 넘겨진 메모리를 삭제했는데,
내부 소스를 들춰보니 false 를 리턴하더라도 ACE 내부적으로 메모리가 이미 삭제되었더군요.
Proactor 기준으로 설명드리면, 최종적으로 다음 함수입니다.
bool ServiceImpl::PushQueue(ACE_Message_Block* block, uint32 tick)
{
block->msg_priority(tick);
ACE_Time_Value noWait(0, 0); // I don't want to wait here.
return (-1 != _block_queue->enqueue_prio(block, &noWait));
}
여기서 enqueue_prio() 함수가 -1 을 리턴하여 에러가 발생하더라도, enqueue_prio() 함수 내부에서
block->release() 를 호출해주고 있습니다.
어쨋든, ACE 를 기준으로 block 의 메모리 소멸을 생각해보면, "block 을 다루는 함수에서 에러를 리턴하더라도 이미
내부적으로 메모리를 자동으로 해제한다." 라고 알게 되었습니다.
그런데, NetworkFacade::SendRequest() 함수를 보면, true 를 리턴하는 상황인데도, 메모리가 해제되지
않습니다.
bool NetworkFacade::SendRequest(uint32 streamId, ACE_Message_Block*
block, bool copy_block)
{
if (TRUE == _suspend.value())
{
//OutputDebugString(_T("SendRequest #1\n"));
// copy_block 이 아니면, 메모리 해제가 필요함!!!
return true;
}
ACE_Message_Block* send_block = NULL;
if (copy_block)
{
send_block = AllocateBlock(block->length());
send_block->copy(block->rd_ptr(), block->length());
}
else
{
send_block = block;
}
return _serviceAccessor->SendRequest(streamId, send_block);
}
bool IntervalProactorSend::Send(uint32 streamID, ACE_Message_Block*
block)
{
ProactorService* service = ProactorServiceMap->Get(streamID);
if (NULL == service)
return false; // 여기서도 해제하지 않음.
return service->IntervalSend(block);
}
bool DirectProactorSend::Send(uint32 streamID, ACE_Message_Block*
block)
{
ProactorService* service = ProactorServiceMap->Get(streamID);
if (NULL == service)
{
//OutputDebugString(_T("DirectProactorSend::Send #1\n"));
// 여기서도 해제하지 않음.
return false;
}
return service->DirectSend(block);
}
위와 같은 부분들이 많이 보입니다. ACE 내부함수까지 내려가면 그 안에서는 대부분 자동으로 메모리를 해제해주는데,
HalfNetwork 에서는 많은 예외 상황에서 누수가 보입니다.
ACE 처럼 인자로 넘겨지는 block 에 대해서 ACE_Message_Block* new_block 이런식으로 명시적으로 지정
해주고선 에러가 발생할 경우, new_block->release() 를 호출해주는 것이 어떨까 생각이 듭니다.
위와 같은 부분을 더 찾아보면, 여러 군데 있을 법한데...
대표적으로 이런 곳입니다.
bool MessageQueueRepository::Push( uint8 queId, EMessageHeader
command, uint32 serial, ACE_Message_Block* block)
{
if (TRUE == m_suspendPush.value())
return false; // 여기서도 누수
MessageQueue* que = GetQueue(queId);
if (NULL == que)
return false; // 여기서도 누수
return que->Push(queId, command, serial, block);
}
HalfNetwork 제작님의 의견은 어떠하신지 궁금합니다.
오늘도 테스트 중에 다른 버그를 발견한 것 같습니다.
ACE 내부에서 에러가 발생하여 block 을 해제했는데, HalfNetwork 에서 또 해제하는 경우입니다.
(아래 코드에 commet 처리하여 버그를 수정했습니다.)
bool ProactorService::_SmartSend(ACE_Message_Block* block)
{
if (false == _serviceImpl->AcquireSendLock())
{
return _PushQueue(block, 0);
}
//ACE_DEBUG (( LM_INFO, ACE_TEXT("_%d_ SmartSend : Length(%d)"),
_serial, block->length()));
if (false == _SmartSendImpl(block))
{
//block->release(); 에러가 발생하더라도, 메모리는 해제되었음
ReserveClose();
return false;
}
return true;
}
이러한 경우에도 검토 부탁드립니다.
On 10월13일, 오후2시42분, YoungGi Lim <javawor...@gmail.com> wrote:
> 지적하신 부분이 맞습니다.
>
> false를 리턴하는 상황이 일반적으로는 발생하지 않는 상황이라 안일하게 생각했던것 같습니다.
>
> HalfNetwork에서 모든 예외상황에 대한 처리도 ACE를 따라가는게 맞다고 보기때문에,
>
> 함수 안쪽에서 블럭을 해제시켜주는 방식으로 처리하는것이 맞다고 봅니다.
>
> 다른 코드도 이런 부분이 있는지 체크해서 일괄 반영하겠습니다.
>
> 꼼꼼한 리포트에 감사드립니다.
>
> 2011/10/13 ened <emocrea...@gmail.com>
> > HalfNetwork 제작님의 의견은 어떠하신지 궁금합니다.- 원본 텍스트 숨기기 -
>
> - 원본 텍스트 보기 -