2014-09-02 16 views
5

Sto cercando di scoprire come risolvere queste perdite di memoria che sto ottenendo durante l'esecuzione di questo programma con Valgrind. Le perdite si verificano con le due allocazioni in nShell_client_main. Ma io non sono il sicuro come liberarli correttamente.Chiudere libUV gestisce correttamente

Ho provato a liberarli su nShell_Connect, ma sta facendo sì che libUV interrompa il programma. Ho provato a liberarli alla fine di nShell_client_main, ma poi ottengo errori di lettura/scrittura durante la chiusura del ciclo. Qualcuno sa come dovrei chiudere queste maniglie? Ho letto this, che mi ha fatto iniziare. Ma è obsoleto perché lo uv_ip4_addr ha un prototipo diverso nell'ultima versione.

(nShell_main è il punto di "ingresso")

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

L'affermazione sta accadendo alla fine dell'istruzione switch in questo estratto di codice (preso dalla pagina libUV di Joyent su 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); 
} 

Potrei chiamare manualmente uv__tcp_close, ma non è nelle intestazioni pubbliche (e probabilmente non è la soluzione giusta comunque).

+0

Remind per evitare il codice rivedere il codice; il layout dei parametri della tua funzione è poco ortodosso e davvero strano (e quindi difficile da leggere) - e anche non del tutto coerente. –

+0

@JonathanLeffler si, ho iniziato l'intero progetto scrivendo lunghe funzioni spezzate in questo modo. Mi dispiace ora, ma non ho avuto la possibilità di riscriverli tutti. – tay10r

risposta

16

libuv non viene eseguito con un handle finché non viene chiamato il callback chiuso. Questo è il momento esatto in cui puoi liberare la maniglia.

Vedo che chiamate uv_loop_close, ma non controllate il valore restituito. Se ci sono ancora maniglie in attesa, restituirà UV_EBUSY, quindi dovresti verificarlo.

Se si desidera chiudere un ciclo e chiudere tutti gli handle, è necessario effettuare le seguenti operazioni:

  • Usa uv_stop per fermare il ciclo
  • Usa uv_walk e chiamare uv_close su tutte le maniglie che non stanno chiudendo
  • Eseguire il ciclo di nuovo con uv_run così tutte le richiamate vicini si chiamano e si può liberare la memoria nel callback
  • chiamata uv_loop_close, deve restituire 0 ora
+0

Quale runmode dovremmo usare quando chiamiamo 'uv_run'? – ruipacheco

+1

Usa UV_RUN_DEFAULT, poiché tutti gli handle sono chiusi e potrebbero essere necessari più di un ciclo iterativo per tutti loro per chiudere e attivare i callback vicini. – saghul

2

Ho finalmente capito come fermare un ciclo e pulire tutte le maniglie. ho creato un po 'di maniglie e SIGINT maniglia del segnale:

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

Quando viene ricevuto SIGINT (Ctrl + C nella consolle viene premuto) il on_sigint_received callback viene chiamata. Il on_sigint_received assomiglia:

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

Si innesca funzione on_uv_walk di essere richiamati:

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

Si cerca di chiudere ogni aperto maniglia libuv. Nota: che non chiamo uv_stop prima del uv_walk, come mentioned saghul. Dopo la funzione on_sigint_received si chiama ciclo libuv continua l'esecuzione e alla successiva iterazione chiama on_uv_close per ogni handle aperto. Se si chiama la funzione uv_stop, la chiamata on_uv_close non verrà richiamata.

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

Dopo che libuv non hanno aperto le maniglie e termina il ciclo (uscite dalla 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"; 
}