Closing libUV Descriptors

I am trying to figure out how to fix these memory leaks that I get when I run this program with Valgrind. Leaks occur with two distributions in nShell_client_main . But it's not me how to free them.

I tried to free them on nShell_Connect, but this led to the fact that libUV interrupted the execution of the program. I tried to free them at the end of nShell_client_main , but then I get read / write errors when closing the loop. Does anyone know how I should close these pens? I read this one that made me start. But these shorts are outdated because uv_ip4_addr has a different prototype in the latest version.

( nShell_main is the entry point)

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

The statement occurs at the end of the switch statement in this piece of code (taken from the Joyent libUV page on 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); } 

I could call uv__tcp_close manually, but this is not in the public headers (and probably this is not the right solution).

+5
source share
2 answers

libuv is not executed using a handle until a callback is called. This is the exact moment when you can free the handle.

I see that you are calling uv_loop_close , but you are not checking the return value. If there are still pending processing, it will return UV_EBUSY, so you should check this.

If you want to close the loop and close all descriptors, you need to do the following:

  • Use uv_stop to stop the loop
  • Use uv_walk and call uv_close on all descriptors that don't close
  • Run the loop again with uv_run to call all callbacks, and you can free up memory in callbacks
  • Call uv_loop_close , it should return 0 now
+16
source

I finally figured out how to stop the cycle and clear all the handles. I created a bunch of descriptors and SIGINT :

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

When SIGINT is received (the keyboard shortcut Ctrl + C is pressed in the console), the on_sigint_received callback is called. on_sigint_received looks like this:

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

It calls the on_uv_walk callback function:

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

It tries to close every open libuv descriptor. Note: which I do not call uv_stop until uv_walk , as the mentioned sagul . After the on_sigint_received function is called the libuv loop, continuous execution at the next iteration calls on_uv_close for each open descriptor. If you call the uv_stop function, then the on_uv_close will not be called.

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

After that, libuv has no open descriptors and ends the loop (exits 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"; } 
+2
source

Source: https://habr.com/ru/post/1201606/


All Articles