ACE_Message_Block 누수 현상에 대해서

154 views
Skip to first unread message

ened

unread,
Oct 12, 2011, 9:14:51 PM10/12/11
to HalfNetwork
패킷을 전송하는데 주로 사용하는 함수가 NetworkFacade::SendRequest() 함수입니다.

그런데, 이 함수에 넘겨지는 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 제작님의 의견은 어떠하신지 궁금합니다.

YoungGi Lim

unread,
Oct 13, 2011, 1:42:36 AM10/13/11
to halfn...@googlegroups.com
지적하신 부분이 맞습니다.

false를 리턴하는 상황이 일반적으로는 발생하지 않는 상황이라 안일하게 생각했던것 같습니다.

HalfNetwork에서 모든 예외상황에 대한 처리도 ACE를 따라가는게 맞다고 보기때문에,

함수 안쪽에서 블럭을 해제시켜주는 방식으로 처리하는것이 맞다고 봅니다.

다른 코드도 이런 부분이 있는지 체크해서 일괄 반영하겠습니다.

꼼꼼한 리포트에 감사드립니다.

2011/10/13 ened <emocr...@gmail.com>

ened

unread,
Oct 13, 2011, 10:59:37 PM10/13/11
to 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 제작님의 의견은 어떠하신지 궁금합니다.- 원본 텍스트 숨기기 -
>
> - 원본 텍스트 보기 -

Reply all
Reply to author
Forward
0 new messages