I'm trying to find out how to fix these memory leaks I'm getting while running this program with Valgrind. The leaks occur with the two allocations in nShell_client_main
. But I'm not
sure how to properly free them.
I've tried freeing them at nShell_Connect, but it's causing libUV to abort the program. I've tried freeing them at the end of nShell_client_main
, but then I get read/write errors when closing the loop. Does anyone know how I'm supposed to close these handles? I've read this, which got me started. But, it seams out-dated 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 assertion is happening at the end of the switch statement in this excerpt of code (taken from Joyent's 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 it's not in the public headers (and probably not the right solution anyway).
libuv is not done with a handle until it's close callback is called. That is the exact moment when you can free the handle.
I see you call uv_loop_close
, but you don't check for the return value. If there are still pending handles, it will return UV_EBUSY, so you should check for that.
If you want to close a loop and close all handles, you need to do the following:
uv_stop
to stop the loopuv_walk
and call uv_close
on all handles which are not closinguv_run
so all close callbacks are called and you can free the memory in the callbacksuv_loop_close
, it should return 0 nowI finally figured out how to stop a loop and clean up all handles. I created a bunch of handles and SIGINT signal handle:
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 (Ctrl+C in console is pressed) the on_sigint_received
callback is called.
The on_sigint_received
looks like:
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 triggers a call back function on_uv_walk
:
void on_uv_walk(uv_handle_t* handle, void* arg)
{
uv_close(handle, on_uv_close);
}
It tries to close each opened libuv handle.
Note: that I do not call uv_stop
before uv_walk
, as mentioned saghul.
After on_sigint_received
function is called libuv loop continuous the execution and on the next iteration calls on_uv_close
for each opened handle. If you call the uv_stop
function, then the on_uv_close
callback will not be called.
void on_uv_close(uv_handle_t* handle)
{
if (handle != NULL)
{
delete handle;
}
}
After that libuv do not have opened handles and finishes the loop (exits from 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";
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With