2014-09-02 21 views
5

Tôi đang cố gắng tìm hiểu cách khắc phục những rò rỉ bộ nhớ mà tôi nhận được khi chạy chương trình này với Valgrind. Các rò rỉ xảy ra với hai phân bổ trong nShell_client_main. Nhưng tôi không phải là chắc chắn cách giải phóng chúng một cách chính xác.Đóng chốt libUV Xử lý chính xác

Tôi đã thử giải phóng chúng tại nShell_Connect, nhưng nó khiến libUV hủy bỏ chương trình. Tôi đã thử giải phóng chúng vào cuối nShell_client_main, nhưng sau đó tôi nhận được lỗi đọc/ghi khi đóng vòng lặp. Có ai biết làm thế nào tôi phải đóng những tay cầm? Tôi đã đọc this, điều này khiến tôi bắt đầu. Tuy nhiên, nó đường nối out-date bởi vì uv_ip4_addr có một nguyên mẫu khác nhau trong phiên bản mới nhất.

(nShell_main là "entry" điểm)

#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; 
} 

Khẳng định đang xảy ra ở phần cuối của câu lệnh switch trong trích đoạn mã này (lấy từ trang libUV Joyent trên Github):

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); 
} 

Tôi có thể gọi uv__tcp_close theo cách thủ công nhưng không nằm trong tiêu đề công khai (và có thể không phải là giải pháp phù hợp).

+0

Nhắc đến tránh mã xem xét mã của bạn; cách bố trí các tham số chức năng của bạn không chính thống và thực sự kỳ lạ (và do đó khó đọc) - và cũng không hoàn toàn nhất quán. –

+0

@JonathanLeffler yeah, tôi bắt đầu toàn bộ dự án viết các hàm dài được chia nhỏ như thế. Bây giờ tôi hơi hối tiếc, nhưng chưa có cơ hội viết lại tất cả. – tay10r

Trả lời

16

libuv không được thực hiện bằng tay cầm cho đến khi gọi lại gần được gọi. Đó là thời điểm chính xác khi bạn có thể giải phóng tay cầm.

Tôi thấy bạn gọi số uv_loop_close, nhưng bạn không kiểm tra giá trị trả lại. Nếu vẫn còn xử lý đang chờ xử lý, nó sẽ trả về UV_EBUSY, vì vậy bạn nên kiểm tra điều đó.

Nếu bạn muốn đóng một vòng lặp và đóng tất cả các xử lý, bạn cần phải làm như sau:

  • Sử dụng uv_stop để ngăn chặn vòng lặp
  • Sử dụng uv_walk và gọi uv_close trên tất cả các xử lý mà không được đóng cửa
  • Chạy lại vòng lặp với uv_run để tất cả các cuộc gọi lại gần được gọi và bạn có thể giải phóng bộ nhớ trong các cuộc gọi lại
  • Gọi uv_loop_close, nó sẽ trả về 0 ngay bây giờ
+0

Chúng ta nên sử dụng runmode nào khi gọi 'uv_run'? – ruipacheco

+1

Sử dụng UV_RUN_DEFAULT, vì tất cả các chốt được đóng và có thể mất nhiều vòng lặp lặp lại cho tất cả chúng để đóng và kích hoạt các cuộc gọi lại gần. – saghul

2

Cuối cùng tôi đã tìm ra cách dừng vòng lặp và dọn sạch tất cả các tay cầm. Tôi tạo ra một loạt các xử lý và SIGINT tín hiệu xử lý:

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

Khi SIGINT nhận được (Ctrl + C trong giao diện điều khiển được nhấn) gọi lại on_sigint_received được gọi. Các on_sigint_received trông giống như:

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); 
    } 
} 

Nó gây nên một cuộc gọi trở lại chức năng on_uv_walk:

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

Nó cố gắng để đóng từng mở xử lý libuv. Lưu ý: mà tôi không gọi uv_stop trước uv_walk, dưới dạng mentioned saghul. Sau khi on_sigint_received chức năng được gọi là vòng lặp libuv liên tục thực hiện và trên các cuộc gọi lặp lại tiếp theo on_uv_close cho mỗi xử lý mở. Nếu bạn gọi hàm uv_stop thì cuộc gọi lại on_uv_close sẽ không được gọi.

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

Sau libuv không đã mở xử lý và kết thúc vòng lặp (lối thoát hiểm từ 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"; 
} 
Các vấn đề liên quan