2014-09-02 192 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"; 
}