2014-09-02 125 views
5

我试图找出如何解决这些内存泄漏问题,这些问题是在Valgrind运行此程序时得到的。泄漏发生在nShell_client_main的两个分配中。但我不是 确定如何正确地释放它们。正确关闭libUV句柄

我试过在nShell_Connect上释放它们,但它导致libUV中止程序。我试图在nShell_client_main结束时释放它们,但是当关闭循环时出现读/写错误。有谁知道我应该如何关闭这些手柄?我读过this,这让我开始了。但是,由于uv_ip4_addr在最新版本中具有不同的原型,因此它接缝过时。

nShell_main是 “进入” 点)

#include "nPort.h" 
#include "nShell-main.h" 

void nShell_Close(
    uv_handle_t * term_handle 
){ 
} 

void nShell_Connect(uv_connect_t * term_handle, int status){ 
    uv_close((uv_handle_t *) term_handle, 0); 
} 

nError * nShell_client_main(nShell * n_shell, uv_loop_t * n_shell_loop){ 

    int uv_error = 0; 

    nError * n_error = 0; 

    uv_tcp_t * n_shell_socket = 0; 
    uv_connect_t * n_shell_connect = 0; 

    struct sockaddr_in dest_addr; 

    n_shell_socket = malloc(sizeof(uv_tcp_t)); 

    if (!n_shell_socket){ 
     // handle error 
    } 

    uv_error = uv_tcp_init(n_shell_loop, n_shell_socket); 

    if (uv_error){ 
     // handle error 
    } 

    uv_error = uv_ip4_addr("127.0.0.1", NPORT, &dest_addr); 

    if (uv_error){ 
     // handle error 
    } 

    n_shell_connect = malloc(sizeof(uv_connect_t)); 

    if (!n_shell_connect){ 
     // handle error 
    } 

    uv_error = uv_tcp_connect(n_shell_connect, n_shell_socket, (struct sockaddr *) &dest_addr, nShell_Connect); 

    if (uv_error){ 
     // handle error 
    } 

    uv_error = uv_run(n_shell_loop, UV_RUN_DEFAULT); 

    if (uv_error){ 
     // handle error 
    } 

    return 0; 
} 

nError * nShell_loop_main(nShell * n_shell){ 

    int uv_error = 0; 

    nError * n_error = 0; 

    uv_loop_t * n_shell_loop = 0; 

    n_shell_loop = malloc(sizeof(uv_loop_t)); 

    if (!n_shell_loop){ 
     // handle error 
    } 

    uv_error = uv_loop_init(n_shell_loop); 

    if (uv_error){ 
     // handle error 
    } 

    n_error = nShell_client_main(n_shell, n_shell_loop); 

    if (n_error){ 
     // handle error 
    } 

    uv_loop_close(n_shell_loop); 
    free(n_shell_loop); 

    return 0; 
} 

断言在此摘录的代码switch语句的结束发生(从Github上Joyent公司的libUV页面获取):

void uv_close(uv_handle_t* handle, uv_close_cb close_cb) { 
    assert(!(handle->flags & (UV_CLOSING | UV_CLOSED))); 

    handle->flags |= UV_CLOSING; 
    handle->close_cb = close_cb; 

    switch (handle->type) { 
    case UV_NAMED_PIPE: 
    uv__pipe_close((uv_pipe_t*)handle); 
    break; 

    case UV_TTY: 
    uv__stream_close((uv_stream_t*)handle); 
    break; 

    case UV_TCP: 
    uv__tcp_close((uv_tcp_t*)handle); 
    break; 

    case UV_UDP: 
    uv__udp_close((uv_udp_t*)handle); 
    break; 

    case UV_PREPARE: 
    uv__prepare_close((uv_prepare_t*)handle); 
    break; 

    case UV_CHECK: 
    uv__check_close((uv_check_t*)handle); 
    break; 

    case UV_IDLE: 
    uv__idle_close((uv_idle_t*)handle); 
    break; 

    case UV_ASYNC: 
    uv__async_close((uv_async_t*)handle); 
    break; 

    case UV_TIMER: 
    uv__timer_close((uv_timer_t*)handle); 
    break; 

    case UV_PROCESS: 
    uv__process_close((uv_process_t*)handle); 
    break; 

    case UV_FS_EVENT: 
    uv__fs_event_close((uv_fs_event_t*)handle); 
    break; 

    case UV_POLL: 
    uv__poll_close((uv_poll_t*)handle); 
    break; 

    case UV_FS_POLL: 
    uv__fs_poll_close((uv_fs_poll_t*)handle); 
    break; 

    case UV_SIGNAL: 
    uv__signal_close((uv_signal_t*) handle); 
    /* Signal handles may not be closed immediately. The signal code will */ 
    /* itself close uv__make_close_pending whenever appropriate. */ 
    return; 

    default: 
    assert(0); // assertion is happening here 
    } 

    uv__make_close_pending(handle); 
} 

我可以手动调用uv__tcp_close,但它不在公共头文件中(也可能不是正确的解决方案)。

+0

提醒,以避免代码审查你的代码;你的函数参数的布局是非正统和非常奇怪的(因此难以阅读) - 也不完全一致。 – 2014-09-02 04:14:55

+0

@JonathanLeffler是的,我开始编写整个项目,编写像这样打破的长功能。我现在有点后悔,但还没有机会重写这些。 – tay10r 2014-09-02 04:37:13

回答

16

libuv直到关闭回调被调用时才会执行句柄。这是您可以释放手柄的确切时刻。

我看到你打电话给uv_loop_close,但你不检查返回值。如果还有待处理的句柄,它将返回UV_EBUSY,所以你应该检查。

如果你想关闭一个循环,并关闭所有的句柄,你需要做到以下几点:

  • 使用uv_stop停止循环
  • 使用uv_walk并呼吁未关闭所有句柄uv_close
  • uv_run因此,所有密切的回调被称为再次运行循环,你可以在回调释放内存
  • 呼叫uv_loop_close,它现在应该返回0
+0

调用'uv_run'时我们应该使用哪一个runmode? – ruipacheco 2015-01-28 22:57:49

+1

使用UV_RUN_DEFAULT,因为所有句柄都是关闭的,并且可能需要多个循环迭代才能关闭并触发关闭回调。 – saghul 2015-01-28 23:14:51

2

我终于想出了如何停止循环并清理所有句柄。 我创建一堆手柄和SIGINT信号手柄:

uv_signal_t *sigint = new uv_signal_t; 
uv_signal_init(uv_default_loop(), sigint); 
uv_signal_start(sigint, on_sigint_received, SIGINT); 

当接收SIGINT(CTRL + C在控制台被按压)的on_sigint_received回调被调用。 的on_sigint_received样子:

void on_sigint_received(uv_signal_t *handle, int signum) 
{ 
    int result = uv_loop_close(handle->loop); 
    if (result == UV_EBUSY) 
    { 
     uv_walk(handle->loop, on_uv_walk, NULL); 
    } 
} 

它触发一个回调函数on_uv_walk

void on_uv_walk(uv_handle_t* handle, void* arg) 
{ 
    uv_close(handle, on_uv_close); 
} 

它试图关闭每个打开的libuv手柄。 注意:,我不叫uv_stop之前uv_walk,作为mentioned saghulon_sigint_received函数被称为libuv循环连续执行,并且在下一次迭代中为每个打开的句柄调用on_uv_close。如果您拨打uv_stop功能,则不会调用on_uv_close回拨。

void on_uv_close(uv_handle_t* handle) 
{ 
    if (handle != NULL) 
    { 
     delete handle; 
    } 
} 

之后libuv不具有打开的句柄并完成环路(从uv_run退出):

uv_run(uv_default_loop(), UV_RUN_DEFAULT); 
int result = uv_loop_close(uv_default_loop()); 
if (result) 
{ 
    cerr << "failed to close libuv loop: " << uv_err_name(result) << endl; 
} 
else 
{ 
    cout << "libuv loop is closed successfully!\n"; 
}