From 0d6525acae3b9cf8e52bda01a2662b82ca63f6dc Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Thu, 30 Nov 2017 00:29:47 +0100 Subject: [PATCH] core: add getter/setter functions for easier ABI compat Add getter/setter functions for the fields of public structs that might be relevant to e.g. Node.js addons. Through these methods, ABI compatibility for a subset of the ABI is easier to achieve, since using them makes code independent of the exact offsets of these fields. The intended use case that prompted this are N-API addons for Node.js, which look for more long-term ABI compatibility guarantees than typical Node code. With these helper functions, using libuv directly should no longer be an obstacle for such addons. PR-URL: https://github.com/libuv/libuv/pull/1657 Refs: https://github.com/nodejs/node/issues/13512 Reviewed-By: Ben Noordhuis Reviewed-By: Colin Ihrig Reviewed-By: Refael Ackermann Reviewed-By: Santiago Gimeno --- Makefile.am | 2 + checksparse.sh | 2 + docs/src/fs.rst | 30 +++++++++++ docs/src/handle.rst | 32 ++++++++++++ docs/src/loop.rst | 12 +++++ docs/src/process.rst | 6 +++ docs/src/request.rst | 27 ++++++++++ docs/src/stream.rst | 6 +++ docs/src/udp.rst | 12 +++++ include/uv.h | 23 +++++++++ src/uv-data-getter-setters.c | 96 ++++++++++++++++++++++++++++++++++++ test/test-getters-setters.c | 88 +++++++++++++++++++++++++++++++++ test/test-list.h | 8 +++ test/test-spawn.c | 2 + uv.gyp | 2 + 15 files changed, 348 insertions(+) create mode 100644 src/uv-data-getter-setters.c create mode 100644 test/test-getters-setters.c diff --git a/Makefile.am b/Makefile.am index e01cf416..b104edec 100644 --- a/Makefile.am +++ b/Makefile.am @@ -29,6 +29,7 @@ libuv_la_SOURCES = src/fs-poll.c \ src/inet.c \ src/queue.h \ src/threadpool.c \ + src/uv-data-getter-setters.c \ src/uv-common.c \ src/uv-common.h \ src/version.c @@ -174,6 +175,7 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-fs-poll.c \ test/test-fs.c \ test/test-fork.c \ + test/test-getters-setters.c \ test/test-get-currentexe.c \ test/test-get-loadavg.c \ test/test-get-memory.c \ diff --git a/checksparse.sh b/checksparse.sh index d4a983d0..02a5d7f2 100755 --- a/checksparse.sh +++ b/checksparse.sh @@ -53,6 +53,7 @@ src/unix/tty.c src/unix/udp.c src/uv-common.c src/uv-common.h +src/uv-data-getter-setters.c " TESTS=" @@ -100,6 +101,7 @@ test/test-fs-copyfile.c test/test-fs-event.c test/test-fs-poll.c test/test-fs.c +test/test-getters-setters.c test/test-get-currentexe.c test/test-get-loadavg.c test/test-get-memory.c diff --git a/docs/src/fs.rst b/docs/src/fs.rst index 16d5e05c..87af828a 100644 --- a/docs/src/fs.rst +++ b/docs/src/fs.rst @@ -340,6 +340,36 @@ API .. note:: These functions are not implemented on Windows. +.. c:function:: uv_fs_type uv_fs_get_type(const uv_fs_t* req) + + Returns `req->fs_type`. + + .. versionadded:: 1.19.0 + +.. c:function:: ssize_t uv_fs_get_result(const uv_fs_t* req) + + Returns `req->result`. + + .. versionadded:: 1.19.0 + +.. c:function:: void* uv_fs_get_ptr(const uv_fs_t* req) + + Returns `req->ptr`. + + .. versionadded:: 1.19.0 + +.. c:function:: const char* uv_fs_get_path(const uv_fs_t* req) + + Returns `req->path`. + + .. versionadded:: 1.19.0 + +.. c:function:: uv_stat_t* uv_fs_get_statbuf(uv_fs_t* req) + + Returns `&req->statbuf`. + + .. versionadded:: 1.19.0 + .. seealso:: The :c:type:`uv_req_t` API functions also apply. Helper functions diff --git a/docs/src/handle.rst b/docs/src/handle.rst index a0f3d05f..e4cb90b5 100644 --- a/docs/src/handle.rst +++ b/docs/src/handle.rst @@ -211,6 +211,38 @@ just for some handle types. Be very careful when using this function. libuv assumes it's in control of the file descriptor so any change to it may lead to malfunction. +.. c:function:: uv_loop_t* uv_handle_get_loop(const uv_handle_t* handle) + + Returns `handle->loop`. + + .. versionadded:: 1.19.0 + +.. c:function:: void* uv_handle_get_data(const uv_handle_t* handle) + + Returns `handle->data`. + + .. versionadded:: 1.19.0 + +.. c:function:: void* uv_handle_set_data(uv_handle_t* handle, void* data) + + Sets `handle->data` to `data`. + + .. versionadded:: 1.19.0 + +.. c:function:: uv_handle_type uv_handle_get_type(const uv_handle_t* handle) + + Returns `handle->type`. + + .. versionadded:: 1.19.0 + +.. c:function:: const char* uv_handle_type_name(uv_handle_type type) + + Returns the name for the equivalent struct for a given handle type, + e.g. `"pipe"` (as in :c:type:`uv_pipe_t`) for `UV_NAMED_PIPE`. + + If no such handle type exists, this returns `NULL`. + + .. versionadded:: 1.19.0 .. _refcount: diff --git a/docs/src/loop.rst b/docs/src/loop.rst index 18dd135c..dcde5049 100644 --- a/docs/src/loop.rst +++ b/docs/src/loop.rst @@ -222,3 +222,15 @@ API Any previous value returned from :c:func`uv_backend_fd` is now invalid. That function must be called again to determine the correct backend file descriptor. + +.. c:function:: void* uv_loop_get_data(const uv_loop_t* loop) + + Returns `loop->data`. + + .. versionadded:: 1.19.0 + +.. c:function:: void* uv_loop_set_data(uv_loop_t* loop, void* data) + + Sets `loop->data` to `data`. + + .. versionadded:: 1.19.0 diff --git a/docs/src/process.rst b/docs/src/process.rst index b0380ddf..ecc3cbf3 100644 --- a/docs/src/process.rst +++ b/docs/src/process.rst @@ -222,4 +222,10 @@ API Sends the specified signal to the given PID. Check the documentation on :c:ref:`signal` for signal support, specially on Windows. +.. c:function:: uv_pid_t uv_process_get_pid(const uv_process_t* handle) + + Returns `handle->pid`. + + .. versionadded:: 1.19.0 + .. seealso:: The :c:type:`uv_handle_t` API functions also apply. diff --git a/docs/src/request.rst b/docs/src/request.rst index 660b80ae..54d9a2f3 100644 --- a/docs/src/request.rst +++ b/docs/src/request.rst @@ -80,3 +80,30 @@ API Returns the size of the given request type. Useful for FFI binding writers who don't want to know the structure layout. + +.. c:function:: void* uv_req_get_data(const uv_req_t* req) + + Returns `req->data`. + + .. versionadded:: 1.19.0 + +.. c:function:: void* uv_req_set_data(uv_req_t* req, void* data) + + Sets `req->data` to `data`. + + .. versionadded:: 1.19.0 + +.. c:function:: uv_req_type uv_req_get_type(const uv_req_t* req) + + Returns `req->type`. + + .. versionadded:: 1.19.0 + +.. c:function:: const char* uv_req_type_name(uv_req_type type) + + Returns the name for the equivalent struct for a given request type, + e.g. `"connect"` (as in :c:type:`uv_connect_t`) for `UV_CONNECT`. + + If no such request type exists, this returns `NULL`. + + .. versionadded:: 1.19.0 diff --git a/docs/src/stream.rst b/docs/src/stream.rst index 1f4e87e6..9ec23622 100644 --- a/docs/src/stream.rst +++ b/docs/src/stream.rst @@ -228,4 +228,10 @@ API .. versionchanged:: 1.4.0 UNIX implementation added. +.. c:function:: size_t uv_stream_get_write_queue_size(const uv_stream_t* stream) + + Returns `stream->write_queue_size`. + + .. versionadded:: 1.19.0 + .. seealso:: The :c:type:`uv_handle_t` API functions also apply. diff --git a/docs/src/udp.rst b/docs/src/udp.rst index dd466033..8704a099 100644 --- a/docs/src/udp.rst +++ b/docs/src/udp.rst @@ -292,4 +292,16 @@ API :returns: 0 on success, or an error code < 0 on failure. +.. c:function:: size_t uv_udp_get_send_queue_size(const uv_udp_t* handle) + + Returns `handle->send_queue_size`. + + .. versionadded:: 1.19.0 + +.. c:function:: size_t uv_udp_get_send_queue_count(const uv_udp_t* handle) + + Returns `handle->send_queue_count`. + + .. versionadded:: 1.19.0 + .. seealso:: The :c:type:`uv_handle_t` API functions also apply. diff --git a/include/uv.h b/include/uv.h index b11666e2..3a061132 100644 --- a/include/uv.h +++ b/include/uv.h @@ -425,7 +425,17 @@ struct uv_handle_s { }; UV_EXTERN size_t uv_handle_size(uv_handle_type type); +UV_EXTERN uv_handle_type uv_handle_get_type(const uv_handle_t* handle); +UV_EXTERN const char* uv_handle_type_name(uv_handle_type type); +UV_EXTERN void* uv_handle_get_data(const uv_handle_t* handle); +UV_EXTERN uv_loop_t* uv_handle_get_loop(const uv_handle_t* handle); +UV_EXTERN void uv_handle_set_data(uv_handle_t* handle, void* data); + UV_EXTERN size_t uv_req_size(uv_req_type type); +UV_EXTERN void* uv_req_get_data(const uv_req_t* req); +UV_EXTERN void uv_req_set_data(uv_req_t* req, void* data); +UV_EXTERN uv_req_type uv_req_get_type(const uv_req_t* req); +UV_EXTERN const char* uv_req_type_name(uv_req_type type); UV_EXTERN int uv_is_active(const uv_handle_t* handle); @@ -465,6 +475,8 @@ struct uv_stream_s { UV_STREAM_FIELDS }; +UV_EXTERN size_t uv_stream_get_write_queue_size(const uv_stream_t* stream); + UV_EXTERN int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb); UV_EXTERN int uv_accept(uv_stream_t* server, uv_stream_t* client); @@ -642,6 +654,8 @@ UV_EXTERN int uv_udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, uv_udp_recv_cb recv_cb); UV_EXTERN int uv_udp_recv_stop(uv_udp_t* handle); +UV_EXTERN size_t uv_udp_get_send_queue_size(const uv_udp_t* handle); +UV_EXTERN size_t uv_udp_get_send_queue_count(const uv_udp_t* handle); /* @@ -962,6 +976,7 @@ UV_EXTERN int uv_spawn(uv_loop_t* loop, const uv_process_options_t* options); UV_EXTERN int uv_process_kill(uv_process_t*, int signum); UV_EXTERN int uv_kill(int pid, int signum); +UV_EXTERN uv_pid_t uv_process_get_pid(const uv_process_t*); /* @@ -1135,6 +1150,12 @@ struct uv_fs_s { UV_FS_PRIVATE_FIELDS }; +UV_EXTERN uv_fs_type uv_fs_get_type(const uv_fs_t*); +UV_EXTERN ssize_t uv_fs_get_result(const uv_fs_t*); +UV_EXTERN void* uv_fs_get_ptr(const uv_fs_t*); +UV_EXTERN const char* uv_fs_get_path(const uv_fs_t*); +UV_EXTERN uv_stat_t* uv_fs_get_statbuf(uv_fs_t*); + UV_EXTERN void uv_fs_req_cleanup(uv_fs_t* req); UV_EXTERN int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, @@ -1516,6 +1537,8 @@ struct uv_loop_s { UV_LOOP_PRIVATE_FIELDS }; +UV_EXTERN void* uv_loop_get_data(const uv_loop_t*); +UV_EXTERN void uv_loop_set_data(uv_loop_t*, void* data); /* Don't export the private CPP symbols. */ #undef UV_HANDLE_TYPE_PRIVATE diff --git a/src/uv-data-getter-setters.c b/src/uv-data-getter-setters.c new file mode 100644 index 00000000..533e4a2f --- /dev/null +++ b/src/uv-data-getter-setters.c @@ -0,0 +1,96 @@ +#include "uv.h" + +const char* uv_handle_type_name(uv_handle_type type) { + switch (type) { +#define XX(uc,lc) case UV_##uc: return #lc; + UV_HANDLE_TYPE_MAP(XX) +#undef XX + case UV_FILE: return "file"; + case UV_HANDLE_TYPE_MAX: + case UV_UNKNOWN_HANDLE: return NULL; + } + return NULL; +} + +uv_handle_type uv_handle_get_type(const uv_handle_t* handle) { + return handle->type; +} + +void* uv_handle_get_data(const uv_handle_t* handle) { + return handle->data; +} + +uv_loop_t* uv_handle_get_loop(const uv_handle_t* handle) { + return handle->loop; +} + +void uv_handle_set_data(uv_handle_t* handle, void* data) { + handle->data = data; +} + +const char* uv_req_type_name(uv_req_type type) { + switch (type) { +#define XX(uc,lc) case UV_##uc: return #lc; + UV_REQ_TYPE_MAP(XX) +#undef XX + case UV_REQ_TYPE_MAX: + case UV_UNKNOWN_REQ: return NULL; + } + return NULL; +} + +uv_req_type uv_req_get_type(const uv_req_t* req) { + return req->type; +} + +void* uv_req_get_data(const uv_req_t* req) { + return req->data; +} + +void uv_req_set_data(uv_req_t* req, void* data) { + req->data = data; +} + +size_t uv_stream_get_write_queue_size(const uv_stream_t* stream) { + return stream->write_queue_size; +} + +size_t uv_udp_get_send_queue_size(const uv_udp_t* handle) { + return handle->send_queue_size; +} + +size_t uv_udp_get_send_queue_count(const uv_udp_t* handle) { + return handle->send_queue_count; +} + +uv_pid_t uv_process_get_pid(const uv_process_t* proc) { + return proc->pid; +} + +uv_fs_type uv_fs_get_type(const uv_fs_t* req) { + return req->fs_type; +} + +ssize_t uv_fs_get_result(const uv_fs_t* req) { + return req->result; +} + +void* uv_fs_get_ptr(const uv_fs_t* req) { + return req->ptr; +} + +const char* uv_fs_get_path(const uv_fs_t* req) { + return req->path; +} + +uv_stat_t* uv_fs_get_statbuf(uv_fs_t* req) { + return &req->statbuf; +} + +void* uv_loop_get_data(const uv_loop_t* loop) { + return loop->data; +} + +void uv_loop_set_data(uv_loop_t* loop, void* data) { + loop->data = data; +} diff --git a/test/test-getters-setters.c b/test/test-getters-setters.c new file mode 100644 index 00000000..60a1b926 --- /dev/null +++ b/test/test-getters-setters.c @@ -0,0 +1,88 @@ +#include "uv.h" +#include "task.h" +#include +#include + +int cookie1; +int cookie2; +int cookie3; + + +TEST_IMPL(handle_type_name) { + ASSERT(strcmp(uv_handle_type_name(UV_NAMED_PIPE), "pipe") == 0); + ASSERT(strcmp(uv_handle_type_name(UV_UDP), "udp") == 0); + ASSERT(strcmp(uv_handle_type_name(UV_FILE), "file") == 0); + ASSERT(uv_handle_type_name(UV_HANDLE_TYPE_MAX) == NULL); + ASSERT(uv_handle_type_name(UV_HANDLE_TYPE_MAX + 1) == NULL); + ASSERT(uv_handle_type_name(UV_UNKNOWN_HANDLE) == NULL); + return 0; +} + + +TEST_IMPL(req_type_name) { + ASSERT(strcmp(uv_req_type_name(UV_REQ), "req") == 0); + ASSERT(strcmp(uv_req_type_name(UV_UDP_SEND), "udp_send") == 0); + ASSERT(strcmp(uv_req_type_name(UV_WORK), "work") == 0); + ASSERT(uv_req_type_name(UV_REQ_TYPE_MAX) == NULL); + ASSERT(uv_req_type_name(UV_REQ_TYPE_MAX + 1) == NULL); + ASSERT(uv_req_type_name(UV_UNKNOWN_REQ) == NULL); + return 0; +} + + +TEST_IMPL(getters_setters) { + uv_loop_t* loop; + uv_pipe_t* pipe; + uv_fs_t* fs; + int r; + + loop = malloc(uv_loop_size()); + ASSERT(loop != NULL); + r = uv_loop_init(loop); + ASSERT(r == 0); + + uv_loop_set_data(loop, &cookie1); + ASSERT(loop->data == &cookie1); + ASSERT(uv_loop_get_data(loop) == &cookie1); + + pipe = malloc(uv_handle_size(UV_NAMED_PIPE)); + r = uv_pipe_init(loop, pipe, 0); + ASSERT(uv_handle_get_type((uv_handle_t*)pipe) == UV_NAMED_PIPE); + + ASSERT(uv_handle_get_loop((uv_handle_t*)pipe) == loop); + pipe->data = &cookie2; + ASSERT(uv_handle_get_data((uv_handle_t*)pipe) == &cookie2); + uv_handle_set_data((uv_handle_t*)pipe, &cookie1); + ASSERT(uv_handle_get_data((uv_handle_t*)pipe) == &cookie1); + ASSERT(pipe->data == &cookie1); + + ASSERT(uv_stream_get_write_queue_size((uv_stream_t*)pipe) == 0); + pipe->write_queue_size++; + ASSERT(uv_stream_get_write_queue_size((uv_stream_t*)pipe) == 1); + pipe->write_queue_size--; + uv_close((uv_handle_t*)pipe, NULL); + + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT(r == 0); + + fs = malloc(uv_req_size(UV_FS)); + uv_fs_stat(loop, fs, ".", NULL); + + r = uv_run(loop, UV_RUN_DEFAULT); + ASSERT(r == 0); + + ASSERT(uv_fs_get_type(fs) == UV_FS_STAT); + ASSERT(uv_fs_get_result(fs) == 0); + ASSERT(uv_fs_get_ptr(fs) == uv_fs_get_statbuf(fs)); + ASSERT(uv_fs_get_statbuf(fs)->st_mode & S_IFDIR); + ASSERT(strcmp(uv_fs_get_path(fs), ".") == 0); + uv_fs_req_cleanup(fs); + + r = uv_loop_close(loop); + ASSERT(r == 0); + + free(pipe); + free(fs); + free(loop); + return 0; +} diff --git a/test/test-list.h b/test/test-list.h index 2adbe6a0..662fe78b 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -397,6 +397,10 @@ HELPER_DECLARE (pipe_echo_server) TEST_DECLARE (queue_foreach_delete) +TEST_DECLARE (handle_type_name) +TEST_DECLARE (req_type_name) +TEST_DECLARE (getters_setters) + #ifndef _WIN32 TEST_DECLARE (fork_timer) TEST_DECLARE (fork_socketpair) @@ -870,6 +874,10 @@ TASK_LIST_START TEST_ENTRY (queue_foreach_delete) + TEST_ENTRY (handle_type_name) + TEST_ENTRY (req_type_name) + TEST_ENTRY (getters_setters) + #ifndef _WIN32 TEST_ENTRY (fork_timer) TEST_ENTRY (fork_socketpair) diff --git a/test/test-spawn.c b/test/test-spawn.c index 4b138265..9a30f6cb 100644 --- a/test/test-spawn.c +++ b/test/test-spawn.c @@ -805,6 +805,8 @@ TEST_IMPL(spawn_detached) { ASSERT(exit_cb_called == 0); + ASSERT(process.pid == uv_process_get_pid(&process)); + r = uv_kill(process.pid, 0); ASSERT(r == 0); diff --git a/uv.gyp b/uv.gyp index 96fb801a..10f32279 100644 --- a/uv.gyp +++ b/uv.gyp @@ -78,6 +78,7 @@ 'src/inet.c', 'src/queue.h', 'src/threadpool.c', + 'src/uv-data-getter-setters.c', 'src/uv-common.c', 'src/uv-common.h', 'src/version.c' @@ -380,6 +381,7 @@ 'test/test-fs.c', 'test/test-fs-copyfile.c', 'test/test-fs-event.c', + 'test/test-getters-setters.c', 'test/test-get-currentexe.c', 'test/test-get-memory.c', 'test/test-get-passwd.c',