关于C#:正确关闭libUV处理

Closing libUV Handles Correctly

我试图找出如何解决在Valgrind中运行该程序时遇到的内存泄漏问题。 nShell_client_main中的两个分配都会发生泄漏。 但我不是
确定如何正确释放它们。

我尝试在nShell_Connect上释放它们,但是这导致libUV中止该程序。 我尝试在nShell_client_main的末尾释放它们,但随后在关闭循环时出现读取/写入错误。 有谁知道我应该如何关闭这些手柄? 我读过这篇,这让我开始。 但是,它的接缝已经过时,因为uv_ip4_addr在最新版本中具有不同的原型。

(nShell_main是"入口"点)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#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页面):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
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,但是它不在公共头文件中(无论如何也可能不是正确的解决方案)。


直到它的关闭回调被调用时,libuv才使用句柄完成。那是释放手柄的确切时间。

我看到您呼叫uv_loop_close,但是您不检查返回值。如果仍有待处理的句柄,它将返回UV_EBUSY,因此您应该检查一下。

如果要关闭循环并关闭所有句柄,则需要执行以下操作:

  • 使用uv_stop停止循环
  • 在所有未关闭的句柄上使用uv_walk并调用uv_close
  • 使用uv_run再次运行循环,以便调用所有关闭的回调,然后可以释放这些回调中的内存
  • 调用uv_loop_close,现在应该返回0


我终于想出了如何停止循环并清理所有句柄的方法。
我创建了一堆句柄和SIGINT信号句柄:

1
2
3
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看起来像:

1
2
3
4
5
6
7
8
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

1
2
3
4
void on_uv_walk(uv_handle_t* handle, void* arg)
{
    uv_close(handle, on_uv_close);
}

它尝试关闭每个打开的libuv句柄。
注意:正如saghul所述,我不会在uv_walk之前调用uv_stop
on_sigint_received函数被称为libuv循环之后,连续执行,并在下一次迭代中为每个打开的句柄调用on_uv_close。如果调用uv_stop函数,则不会调用on_uv_close回调。

1
2
3
4
5
6
7
void on_uv_close(uv_handle_t* handle)
{
    if (handle != NULL)
    {
        delete handle;
    }
}

之后,libuv没有打开句柄并完成循环(从uv_run退出):

1
2
3
4
5
6
7
8
9
10
11
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!
"
;
}