【问题标题】:How to know if a handle has been already initialized如何知道句柄是否已经初始化
【发布时间】:2016-07-01 15:55:49
【问题描述】:

libuv中的句柄必须在使用前进行初始化。
它们都有一个关联的uv_<handle>_init 函数。例如,uv_timer_t 有一个关联函数 uv_timer_init 来初始化它。

也就是说,如果我多次调用给定句柄的 init 函数,我注意到 libuv 具有未定义的行为。
一旦我关闭循环并执行一堆无效的读/写操作,它就会显示问题。
有没有办法知道句柄是否已经初始化?

例如,要知道句柄是关闭还是关闭,存在函数uv_is_closing
是否有类似的函数可以知道句柄是否已经初始化?

【问题讨论】:

  • 您可以使用数据结构来跟踪已初始化的变量,如果它们的类型相同,您可以定义自己的哈希函数并执行查找。或者,您可以将变量名称存储在这样的结构中,但我不建议这样做,因为重命名变量变得很麻烦。
  • 在多次调用 init 函数时遇到未定义行为这一事实肯定暗示了答案是“你不能”的假设 - 否则库将能够自己检测到它。 .
  • @tofro 我不这么认为。一个例子是uv_close 函数。你不应该调用它两次,你有 uv_is_closing 函数来检查它是否已经关闭。
  • 手柄就像一张支票。只是一张上面有数字的纸,当你把它带到银行兑现时,很多机器在后台呼呼作响。把它带到银行两次或根本没有,好吧,你可以想象。编程就是要做出明智的决定。
  • @HansPassant 那么,为什么存在uv_is_active 并且它可以与uv_timer 一起使用?想要启动汽车引擎两次而不停止它听起来不是一个好的决定。它的目的是了解它是否已经开始。我在问是否有一个等效的函数可以用来知道它是否已经初始化。很简单的一个问题,不是吗?

标签: c libuv


【解决方案1】:

uv_is_closing 的比较并不合适。 Closing 可以在调用时设置一点,您可以稍后检查。但是当还没有任何函数接触到句柄时,你期望检查什么?

尽管如此,还是有一些解决方法:

标记未初始化的句柄

将句柄显式归零:memset(&handle, 0x00, sizeof handle)。要确定句柄是否未初始化,请检查它是否仍然是全字节零:

int is_all_zeroes(void *buf, size_t len) {
   for (unsigned char *p = buf; p < buf + len; p++) {
        if (*p != 0x00)
           return 0;
   }
   return 1;
}

假设全零句柄不能是有效对象,这是一个安全的赌注,因为任何初始化的 libuv 句柄都将包含非空指针,而且这在未来不太可能改变。

标记初始化句柄

如果我们不标记未初始化的对象,我们将不得不标记已初始化的对象。

保留已初始化句柄的列表。在初始化/关闭条目时添加/删除条目。


在内部,libuv 已经标记了初始化的句柄。当一个句柄被初始化时,它被添加到一个uv_loop_t 特定的QUEUE

API 不打算公开使用,但是:

#define uv__handle_init(loop_, h, type_)                                      \
  do {                                                                        \
    (h)->loop = (loop_);                                                      \
    (h)->type = (type_);                                                      \
    (h)->flags = UV__HANDLE_REF;  /* Ref the loop when active. */             \
    QUEUE_INSERT_TAIL(&(loop_)->handle_queue, &(h)->handle_queue);            \
    uv__handle_platform_init(h);                                              \
  }                                                                           \

所以你最好自己跟踪它。

【讨论】:

  • 该队列可能是我观察到的行为的来源。两次初始化句柄意味着可能将它两次放入队列中。一旦我关闭循环,我应该查看 libuv 的代码以了解那里发生了什么。无论如何,谢谢你的建议。
【解决方案2】:

建议here(实际上是libev的文档,但建议也适用于libuv):

如果您需要更多数据并且不想单独分配内存并将指向它的指针存储在该数据成员中,您还可以“子类化”观察者类型并提供您自己的数据:

struct my_io {
    ev_io io;
    int otherfd;
    void *somedata;
    struct whatever *mostinteresting;
};

// ...

struct my_io w;
ev_io_init (&w.io, my_cb, fd, EV_READ); 

而且由于您的回调将使用指向观察者的指针调用,您可以将其转换回您自己的类型:

static void my_cb (struct ev_loop *loop, ev_io *w_, int revents) {
    struct my_io *w = (struct my_io *)w_;
    // ...
}

使用类似的方法,我们可以定义一个具有布尔参数的新结构,该参数指示它是否已被初始化。
在创建过程中将其设置为 false(工厂方法可以在此处提供帮助)并在初始化后将其切换为 true。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多