From dbc2971ac485c8b435614438def0e507c7e024d2 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Mon, 16 Nov 2020 19:08:04 +0300 Subject: [PATCH 001/117] timer --- libs/uv/uv.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 1273bb53b..3648f0cd4 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -21,6 +21,8 @@ typedef struct sockaddr uv_sockaddr; #define EVT_WRITE 0 // write_t #define EVT_CONNECT 0 // connect_t +#define EVT_TIMER_TICK 0 // timer + #define EVT_MAX 2 typedef struct { @@ -153,6 +155,66 @@ DEFINE_PRIM(_BOOL, stream_read_start, _HANDLE _FUN(_VOID,_BYTES _I32)); DEFINE_PRIM(_VOID, stream_read_stop, _HANDLE); DEFINE_PRIM(_BOOL, stream_listen, _HANDLE _I32 _CALLB); +// Timer +HL_PRIM uv_timer_t *HL_NAME(timer_init_wrap)( uv_loop_t *loop ) { + uv_timer_t *t = UV_ALLOC(uv_timer_t); + if( uv_timer_init(loop,t) < 0) { + free(t); + //TODO: throw error + return NULL; + } + init_hl_data((uv_handle_t*)t); + return t; +} +DEFINE_PRIM(_HANDLE, timer_init_wrap, _LOOP); + +static void on_timer_tick( uv_timer_t *t ) { + trigger_callb((uv_handle_t*)t, EVT_TIMER_TICK, NULL, 0, true); +} + +// TODO: change `timeout` and `repeat` to uint64 +HL_PRIM void HL_NAME(timer_start_wrap)(uv_timer_t *t, vclosure *c, int timeout, int repeat) { + register_callb((uv_handle_t*)t,c,EVT_TIMER_TICK); + if(uv_timer_start(t,on_timer_tick, (uint64_t)timeout, (uint64_t)repeat) < 0) { + clear_callb((uv_handle_t*)t, EVT_TIMER_TICK); + //TODO: throw error + } +} +DEFINE_PRIM(_VOID, timer_start_wrap, _HANDLE _FUN(_VOID,_NO_ARG) _I32 _I32); + +HL_PRIM void HL_NAME(timer_stop_wrap)(uv_timer_t *t) { + clear_callb((uv_handle_t*)t, EVT_TIMER_TICK); + if(uv_timer_stop(t) < 0) { + //TODO: throw error + } +} +DEFINE_PRIM(_VOID, timer_stop_wrap, _HANDLE); + +HL_PRIM void HL_NAME(timer_again_wrap)(uv_timer_t *t) { + if(uv_timer_again(t) < 0) { + //TODO: throw error + } +} +DEFINE_PRIM(_VOID, timer_again_wrap, _HANDLE); + +// TODO: Requires libuv 1.40 +// HL_PRIM int HL_NAME(timer_get_due_in_wrap)(uv_timer_t *t) { +// return (int)uv_timer_get_due_in(t); //TODO: change to uint64_t +// } +// DEFINE_PRIM(_I32, timer_get_due_in_wrap, _HANDLE); + +HL_PRIM int HL_NAME(timer_get_repeat_wrap)(uv_timer_t *t) { + return (int)uv_timer_get_repeat(t); //TODO: change to uint64_t +} +DEFINE_PRIM(_I32, timer_get_repeat_wrap, _HANDLE); + +//TODO: change `value` to uint64_t +HL_PRIM int HL_NAME(timer_set_repeat_wrap)(uv_timer_t *t, int value) { + uv_timer_set_repeat(t, (uint64_t)value); + return value; +} +DEFINE_PRIM(_I32, timer_set_repeat_wrap, _HANDLE _I32); + // TCP #define _TCP _HANDLE @@ -229,6 +291,17 @@ DEFINE_PRIM(_VOID, tcp_nodelay_wrap, _TCP _BOOL); // loop +HL_PRIM uv_loop_t *HL_NAME(loop_init_wrap)( ) { + uv_loop_t *loop = UV_ALLOC(uv_loop_t); + if( uv_loop_init(loop) < 0) { + free(loop); + //TODO: throw error + return NULL; + } + return loop; +} +DEFINE_PRIM(_LOOP, loop_init_wrap, _NO_ARG); + DEFINE_PRIM(_LOOP, default_loop, _NO_ARG); DEFINE_PRIM(_I32, loop_close, _LOOP); DEFINE_PRIM(_I32, run, _LOOP _I32); From c5a9c5c90cd621b3d9779e1b1e6789d4d5e788e4 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Tue, 17 Nov 2020 14:38:00 +0300 Subject: [PATCH 002/117] uv error codes to hx --- libs/uv/uv.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 3648f0cd4..c055e367a 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -37,6 +37,93 @@ typedef struct { #define _CALLB _FUN(_VOID,_NO_ARG) #define UV_ALLOC(t) ((t*)malloc(sizeof(t))) +// Errors + +// static int code_uv2hx( int code ) { +// switch(code) { +// case 0: return 0; break; +// case UV_E2BIG: return 1; break; +// case UV_EACCES: return 2; break; +// case UV_EADDRINUSE: return 3; break; +// case UV_EADDRNOTAVAIL: return 4; break; +// case UV_EAFNOSUPPORT: return 5; break; +// case UV_EAGAIN: return 6; break; +// case UV_EAI_ADDRFAMILY: return 7; break; +// case UV_EAI_AGAIN: return 8; break; +// case UV_EAI_BADFLAGS: return 9; break; +// case UV_EAI_BADHINTS: return 10; break; +// case UV_EAI_CANCELED: return 11; break; +// case UV_EAI_FAIL: return 11; break; +// case UV_EAI_FAMILY: return 12; break; +// case UV_EAI_MEMORY: return 13; break; +// case UV_EAI_NODATA: return 14; break; +// case UV_EAI_NONAME: return 15; break; +// case UV_EAI_OVERFLOW: return 16; break; +// case UV_EAI_PROTOCOL: return 17; break; +// case UV_EAI_SERVICE: return 18; break; +// case UV_EAI_SOCKTYPE: return 19; break; +// case UV_EALREADY: return 20; break; +// case UV_EBADF: return 21; break; +// case UV_EBUSY: return 22; break; +// case UV_ECANCELED: return 23; break; +// case UV_ECHARSET: return 24; break; +// case UV_ECONNABORTED: return 25; break; +// case UV_ECONNREFUSED: return 26; break; +// case UV_ECONNRESET: return 27; break; +// case UV_EDESTADDRREQ: return 28; break; +// case UV_EEXIST: return 29; break; +// case UV_EFAULT: return 30; break; +// case UV_EFBIG: return 31; break; +// case UV_EHOSTUNREACH: return 32; break; +// case UV_EINTR: return 33; break; +// case UV_EINVAL: return 34; break; +// case UV_EIO: return 35; break; +// case UV_EISCONN: return 36; break; +// case UV_EISDIR: return 37; break; +// case UV_ELOOP: return 38; break; +// case UV_EMFILE: return 39; break; +// case UV_EMSGSIZE: return 40; break; +// case UV_ENAMETOOLONG: return 41; break; +// case UV_ENETDOWN: return 42; break; +// case UV_ENETUNREACH: return 43; break; +// case UV_ENFILE: return 44; break; +// case UV_ENOBUFS: return 45; break; +// case UV_ENODEV: return 46; break; +// case UV_ENOENT: return 47; break; +// case UV_ENOMEM: return 48; break; +// case UV_ENONET: return 49; break; +// case UV_ENOPROTOOPT: return 50; break; +// case UV_ENOSPC: return 51; break; +// case UV_ENOSYS: return 52; break; +// case UV_ENOTCONN: return 53; break; +// case UV_ENOTDIR: return 54; break; +// case UV_ENOTEMPTY: return 55; break; +// case UV_ENOTSOCK: return 56; break; +// case UV_ENOTSUP: return 57; break; +// case UV_EPERM: return 58; break; +// case UV_EPIPE: return 59; break; +// case UV_EPROTO: return 60; break; +// case UV_EPROTONOSUPPORT: return 61; break; +// case UV_EPROTOTYPE: return 62; break; +// case UV_ERANGE: return 63; break; +// case UV_EROFS: return 64; break; +// case UV_ESHUTDOWN: return 65; break; +// case UV_ESPIPE: return 66; break; +// case UV_ESRCH: return 67; break; +// case UV_ETIMEDOUT: return 68; break; +// case UV_ETXTBSY: return 69; break; +// case UV_EXDEV: return 70; break; +// case UV_UNKNOWN: return 71; break; +// case UV_EOF: return 72; break; +// case UV_ENXIO: return 73; break; +// case UV_EMLINK: return 74; break; +// case UV_EHOSTDOWN: return 75; break; +// case UV_EREMOTEIO: return 76; break; +// case UV_ENOTTY: return 77; break; +// default: return UV_UNKNOWN; +// } +// } + // HANDLE static events_data *init_hl_data( uv_handle_t *h ) { @@ -300,7 +387,7 @@ HL_PRIM uv_loop_t *HL_NAME(loop_init_wrap)( ) { } return loop; } -DEFINE_PRIM(_LOOP, loop_init_wrap, _NO_ARG); +DEFINE_PRIM(_DYN, loop_init_wrap, _NO_ARG); DEFINE_PRIM(_LOOP, default_loop, _NO_ARG); DEFINE_PRIM(_I32, loop_close, _LOOP); From 89dbaca2a57c5ddf01119096b59f52f7d42e68f5 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Tue, 17 Nov 2020 17:36:46 +0300 Subject: [PATCH 003/117] fix loop_init_wrap --- libs/uv/uv.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index c055e367a..3a19dac84 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -285,10 +285,10 @@ HL_PRIM void HL_NAME(timer_again_wrap)(uv_timer_t *t) { DEFINE_PRIM(_VOID, timer_again_wrap, _HANDLE); // TODO: Requires libuv 1.40 -// HL_PRIM int HL_NAME(timer_get_due_in_wrap)(uv_timer_t *t) { -// return (int)uv_timer_get_due_in(t); //TODO: change to uint64_t -// } -// DEFINE_PRIM(_I32, timer_get_due_in_wrap, _HANDLE); +HL_PRIM int HL_NAME(timer_get_due_in_wrap)(uv_timer_t *t) { + return (int)uv_timer_get_due_in(t); //TODO: change to uint64_t +} +DEFINE_PRIM(_I32, timer_get_due_in_wrap, _HANDLE); HL_PRIM int HL_NAME(timer_get_repeat_wrap)(uv_timer_t *t) { return (int)uv_timer_get_repeat(t); //TODO: change to uint64_t @@ -387,7 +387,7 @@ HL_PRIM uv_loop_t *HL_NAME(loop_init_wrap)( ) { } return loop; } -DEFINE_PRIM(_DYN, loop_init_wrap, _NO_ARG); +DEFINE_PRIM(_LOOP, loop_init_wrap, _NO_ARG); DEFINE_PRIM(_LOOP, default_loop, _NO_ARG); DEFINE_PRIM(_I32, loop_close, _LOOP); From 22b7d944bfb6d1e2198b08af1f427e35302ab1f7 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Tue, 17 Nov 2020 17:37:26 +0300 Subject: [PATCH 004/117] libuv 1.40.0 --- include/libuv/AUTHORS | 191 ++ include/libuv/ChangeLog | 2377 +++++++++++++++++- include/libuv/LICENSE | 32 +- include/libuv/include/uv.h | 373 ++- include/libuv/include/uv/aix.h | 32 + include/libuv/include/uv/android-ifaddrs.h | 54 + include/libuv/include/uv/bsd.h | 34 + include/libuv/include/uv/darwin.h | 61 + include/libuv/include/uv/errno.h | 448 ++++ include/libuv/include/uv/linux.h | 34 + include/libuv/include/uv/os390.h | 33 + include/libuv/include/uv/posix.h | 31 + include/libuv/include/uv/stdint-msvc2008.h | 247 ++ include/libuv/include/uv/sunos.h | 44 + include/libuv/include/uv/threadpool.h | 37 + include/libuv/include/uv/tree.h | 768 ++++++ include/libuv/include/uv/unix.h | 507 ++++ include/libuv/include/uv/version.h | 43 + include/libuv/include/uv/win.h | 691 +++++ include/libuv/src/fs-poll.c | 67 +- include/libuv/src/idna.c | 291 +++ include/libuv/src/idna.h | 31 + include/libuv/src/inet.c | 13 +- include/libuv/src/random.c | 123 + include/libuv/src/strscpy.c | 38 + include/libuv/src/strscpy.h | 39 + include/libuv/src/threadpool.c | 159 +- include/libuv/src/timer.c | 184 ++ include/libuv/src/unix/aix-common.c | 89 + include/libuv/src/unix/aix.c | 1304 ++++++++++ include/libuv/src/unix/android-ifaddrs.c | 713 ++++++ include/libuv/src/unix/async.c | 253 ++ include/libuv/src/unix/atomic-ops.h | 61 + include/libuv/src/unix/bsd-ifaddrs.c | 163 ++ include/libuv/src/unix/bsd-proctitle.c | 100 + include/libuv/src/unix/core.c | 1613 ++++++++++++ include/libuv/src/unix/cygwin.c | 53 + include/libuv/src/unix/darwin-proctitle.c | 192 ++ include/libuv/src/unix/darwin-stub.h | 113 + include/libuv/src/unix/darwin.c | 371 +++ include/libuv/src/unix/dl.c | 80 + include/libuv/src/unix/freebsd.c | 282 +++ include/libuv/src/unix/fs.c | 2134 ++++++++++++++++ include/libuv/src/unix/fsevents.c | 923 +++++++ include/libuv/src/unix/getaddrinfo.c | 255 ++ include/libuv/src/unix/getnameinfo.c | 121 + include/libuv/src/unix/haiku.c | 167 ++ include/libuv/src/unix/ibmi.c | 501 ++++ include/libuv/src/unix/internal.h | 344 +++ include/libuv/src/unix/kqueue.c | 585 +++++ include/libuv/src/unix/linux-core.c | 1146 +++++++++ include/libuv/src/unix/linux-inotify.c | 327 +++ include/libuv/src/unix/linux-syscalls.c | 267 ++ include/libuv/src/unix/linux-syscalls.h | 81 + include/libuv/src/unix/loop-watcher.c | 68 + include/libuv/src/unix/loop.c | 228 ++ include/libuv/src/unix/netbsd.c | 259 ++ include/libuv/src/unix/no-fsevents.c | 42 + include/libuv/src/unix/no-proctitle.c | 45 + include/libuv/src/unix/openbsd.c | 240 ++ include/libuv/src/unix/os390-syscalls.c | 584 +++++ include/libuv/src/unix/os390-syscalls.h | 74 + include/libuv/src/unix/os390.c | 975 +++++++ include/libuv/src/unix/pipe.c | 381 +++ include/libuv/src/unix/poll.c | 150 ++ include/libuv/src/unix/posix-hrtime.c | 35 + include/libuv/src/unix/posix-poll.c | 374 +++ include/libuv/src/unix/process.c | 595 +++++ include/libuv/src/unix/procfs-exepath.c | 45 + include/libuv/src/unix/proctitle.c | 159 ++ include/libuv/src/unix/pthread-fixes.c | 58 + include/libuv/src/unix/qnx.c | 137 + include/libuv/src/unix/random-devurandom.c | 93 + include/libuv/src/unix/random-getentropy.c | 57 + include/libuv/src/unix/random-getrandom.c | 88 + include/libuv/src/unix/random-sysctl-linux.c | 99 + include/libuv/src/unix/signal.c | 558 ++++ include/libuv/src/unix/spinlock.h | 53 + include/libuv/src/unix/stream.c | 1693 +++++++++++++ include/libuv/src/unix/sunos.c | 867 +++++++ include/libuv/src/unix/sysinfo-loadavg.c | 36 + include/libuv/src/unix/sysinfo-memory.c | 42 + include/libuv/src/unix/tcp.c | 461 ++++ include/libuv/src/unix/thread.c | 842 +++++++ include/libuv/src/unix/tty.c | 402 +++ include/libuv/src/unix/udp.c | 1332 ++++++++++ include/libuv/src/uv-common.c | 349 ++- include/libuv/src/uv-common.h | 213 +- include/libuv/src/uv-data-getter-setters.c | 119 + include/libuv/src/win/async.c | 13 +- include/libuv/src/win/atomicops-inl.h | 11 +- include/libuv/src/win/core.c | 305 ++- include/libuv/src/win/detect-wakeup.c | 56 + include/libuv/src/win/dl.c | 64 +- include/libuv/src/win/error.c | 9 +- include/libuv/src/win/fs-event.c | 123 +- include/libuv/src/win/fs-fd-hash-inl.h | 200 ++ include/libuv/src/win/fs.c | 1911 ++++++++++---- include/libuv/src/win/getaddrinfo.c | 108 +- include/libuv/src/win/getnameinfo.c | 53 +- include/libuv/src/win/handle-inl.h | 25 +- include/libuv/src/win/handle.c | 18 +- include/libuv/src/win/internal.h | 162 +- include/libuv/src/win/loop-watcher.c | 2 +- include/libuv/src/win/pipe.c | 1360 ++++++---- include/libuv/src/win/poll.c | 238 +- include/libuv/src/win/process-stdio.c | 60 +- include/libuv/src/win/process.c | 120 +- include/libuv/src/win/req-inl.h | 9 +- include/libuv/src/win/signal.c | 206 +- include/libuv/src/win/stream-inl.h | 14 +- include/libuv/src/win/stream.c | 46 +- include/libuv/src/win/tcp.c | 418 +-- include/libuv/src/win/thread.c | 279 +- include/libuv/src/win/tty.c | 847 ++++--- include/libuv/src/win/udp.c | 433 +++- include/libuv/src/win/util.c | 975 +++++-- include/libuv/src/win/winapi.c | 75 +- include/libuv/src/win/winapi.h | 146 +- include/libuv/src/win/winsock.c | 102 +- include/libuv/src/win/winsock.h | 11 + 121 files changed, 36447 insertions(+), 2925 deletions(-) create mode 100644 include/libuv/include/uv/aix.h create mode 100644 include/libuv/include/uv/android-ifaddrs.h create mode 100644 include/libuv/include/uv/bsd.h create mode 100644 include/libuv/include/uv/darwin.h create mode 100644 include/libuv/include/uv/errno.h create mode 100644 include/libuv/include/uv/linux.h create mode 100644 include/libuv/include/uv/os390.h create mode 100644 include/libuv/include/uv/posix.h create mode 100644 include/libuv/include/uv/stdint-msvc2008.h create mode 100644 include/libuv/include/uv/sunos.h create mode 100644 include/libuv/include/uv/threadpool.h create mode 100644 include/libuv/include/uv/tree.h create mode 100644 include/libuv/include/uv/unix.h create mode 100644 include/libuv/include/uv/version.h create mode 100644 include/libuv/include/uv/win.h create mode 100644 include/libuv/src/idna.c create mode 100644 include/libuv/src/idna.h create mode 100644 include/libuv/src/random.c create mode 100644 include/libuv/src/strscpy.c create mode 100644 include/libuv/src/strscpy.h create mode 100644 include/libuv/src/timer.c create mode 100644 include/libuv/src/unix/aix-common.c create mode 100644 include/libuv/src/unix/aix.c create mode 100644 include/libuv/src/unix/android-ifaddrs.c create mode 100644 include/libuv/src/unix/async.c create mode 100644 include/libuv/src/unix/atomic-ops.h create mode 100644 include/libuv/src/unix/bsd-ifaddrs.c create mode 100644 include/libuv/src/unix/bsd-proctitle.c create mode 100644 include/libuv/src/unix/core.c create mode 100644 include/libuv/src/unix/cygwin.c create mode 100644 include/libuv/src/unix/darwin-proctitle.c create mode 100644 include/libuv/src/unix/darwin-stub.h create mode 100644 include/libuv/src/unix/darwin.c create mode 100644 include/libuv/src/unix/dl.c create mode 100644 include/libuv/src/unix/freebsd.c create mode 100644 include/libuv/src/unix/fs.c create mode 100644 include/libuv/src/unix/fsevents.c create mode 100644 include/libuv/src/unix/getaddrinfo.c create mode 100644 include/libuv/src/unix/getnameinfo.c create mode 100644 include/libuv/src/unix/haiku.c create mode 100644 include/libuv/src/unix/ibmi.c create mode 100644 include/libuv/src/unix/internal.h create mode 100644 include/libuv/src/unix/kqueue.c create mode 100644 include/libuv/src/unix/linux-core.c create mode 100644 include/libuv/src/unix/linux-inotify.c create mode 100644 include/libuv/src/unix/linux-syscalls.c create mode 100644 include/libuv/src/unix/linux-syscalls.h create mode 100644 include/libuv/src/unix/loop-watcher.c create mode 100644 include/libuv/src/unix/loop.c create mode 100644 include/libuv/src/unix/netbsd.c create mode 100644 include/libuv/src/unix/no-fsevents.c create mode 100644 include/libuv/src/unix/no-proctitle.c create mode 100644 include/libuv/src/unix/openbsd.c create mode 100644 include/libuv/src/unix/os390-syscalls.c create mode 100644 include/libuv/src/unix/os390-syscalls.h create mode 100644 include/libuv/src/unix/os390.c create mode 100644 include/libuv/src/unix/pipe.c create mode 100644 include/libuv/src/unix/poll.c create mode 100644 include/libuv/src/unix/posix-hrtime.c create mode 100644 include/libuv/src/unix/posix-poll.c create mode 100644 include/libuv/src/unix/process.c create mode 100644 include/libuv/src/unix/procfs-exepath.c create mode 100644 include/libuv/src/unix/proctitle.c create mode 100644 include/libuv/src/unix/pthread-fixes.c create mode 100644 include/libuv/src/unix/qnx.c create mode 100644 include/libuv/src/unix/random-devurandom.c create mode 100644 include/libuv/src/unix/random-getentropy.c create mode 100644 include/libuv/src/unix/random-getrandom.c create mode 100644 include/libuv/src/unix/random-sysctl-linux.c create mode 100644 include/libuv/src/unix/signal.c create mode 100644 include/libuv/src/unix/spinlock.h create mode 100644 include/libuv/src/unix/stream.c create mode 100644 include/libuv/src/unix/sunos.c create mode 100644 include/libuv/src/unix/sysinfo-loadavg.c create mode 100644 include/libuv/src/unix/sysinfo-memory.c create mode 100644 include/libuv/src/unix/tcp.c create mode 100644 include/libuv/src/unix/thread.c create mode 100644 include/libuv/src/unix/tty.c create mode 100644 include/libuv/src/unix/udp.c create mode 100644 include/libuv/src/uv-data-getter-setters.c create mode 100644 include/libuv/src/win/detect-wakeup.c create mode 100644 include/libuv/src/win/fs-fd-hash-inl.h diff --git a/include/libuv/AUTHORS b/include/libuv/AUTHORS index 7acee7c53..e7c789cfd 100644 --- a/include/libuv/AUTHORS +++ b/include/libuv/AUTHORS @@ -257,3 +257,194 @@ Michael Fero Robert Jefe Lindstaedt Myles Borins Tony Theodore +Jason Ginchereau +Nicolas Cavallari +Pierre-Marie de Rodat +Brian Maher +neevek +John Barboza +liuxiaobo +Michele Caini +Bartosz Sosnowski +Matej Knopp +sunjin.lee +Matt Clarkson +Jeffrey Clark +Bart Robinson +Vit Gottwald +Vladimír Čunát +Alex Hultman +Brad King +Philippe Laferriere +Will Speak +Hitesh Kanwathirtha +Eric Sciple +jBarz +muflub +Daniel Bevenius +Howard Hellyer +Chris Araman +Vladimir Matveev +Jason Madden +Jamie Davis +Daniel Kahn Gillmor +Keane +James McCoy +Bernardo Ramos +Juan Cruz Viotti +Gemini Wen +Sebastian Wiedenroth +Sai Ke WANG +Barnabas Gema +Romain Caire +Robert Ayrapetyan +Refael Ackermann +André Klitzing +Matthew Taylor +CurlyMoo +XadillaX +Anticrisis +Jacob Segal +Maciej Szeptuch (Neverous) +Joel Winarske +Gergely Nagy +Kamil Rytarowski +tux.uudiin <77389867@qq.com> +Nick Logan +darobs +Zheng, Lei +Carlo Marcelo Arenas Belón +Scott Parker +Wade Brainerd +rayrase +Pekka Nikander +Ed Schouten +Xu Meng +Matt Harrison +Anna Henningsen +Jérémy Lal +Ben Wijen +elephantp +Felix Yan +Mason X +Jesse Gorzinski +Ryuichi KAWAMATA +Joyee Cheung +Michael Kilburn +Ruslan Bekenev +Bob Burger +Thomas Versteeg +zzzjim +Alex Arslan +Kyle Farnung +ssrlive <30760636+ssrlive@users.noreply.github.com> +Tobias Nießen +Björn Linse +zyxwvu Shi +Peter Johnson +Paolo Greppi +Shelley Vohr +Ujjwal Sharma +Michał Kozakiewicz +Emil Bay +Jeremiah Senkpiel +Andy Zhang +dmabupt +Ryan Liptak +Ali Ijaz Sheikh +hitesh +Svante Signell +Samuel Thibault +Jeremy Studer +damon-kwok <563066990@qq.com> +Damon Kwok +Ashe Connor +Rick +Ivan Krylov +Michael Meier +ptlomholt +Victor Costan +sid +Kevin Adler +Stephen Belanger +yeyuanfeng +erw7 +Thomas Karl Pietrowski +evgley +Andreas Rohner +Rich Trott +Milad Farazmand +zlargon +Yury Selivanov +Oscar Waddell +FX Coudert +George Zhao +Kyle Edwards +ken-cunningham-webuse +Kelvin Jin +Leorize +Vlad A +Niels Lohmann +Jenil Christo +Evgeny Ermakov +gengjiawen +Leo Chung +Javier Blazquez +Mustafa M +Zach Bjornson +Nan Xiao +Ben Davies +Nhan Khong +Crunkle +Tomas Krizek +Konstantin Podsvirov +seny +Vladimir Karnushin +MaYuming +Eneas U de Queiroz +Daniel Hahler +Yang Yu +David Carlier +Calvin Hill +Isabella Muerte <63051+slurps-mad-rips@users.noreply.github.com> +Ouyang Yadong +ZYSzys +Carl Lei +Stefan Bender +nia +virtualyw +Witold Kręcicki +Dominique Dumont +Manuel BACHMANN +Marek Vavrusa +TK-one +Irek Fakhrutdinov +Lin Zhang +毛毛 +Sk Sajidul Kadir +twosee +Rikard Falkeborn +Yash Ladha +James Ross +Colin Finck +Shohei YOSHIDA +Philip Chimento +Michal Artazov +Jeroen Roovers +MasterDuke17 +Alexander Tokmakov +Arenoros +lander0s +Turbinya +OleksandrKvl +Carter Li +Juan Sebastian velez Posada +escherstair +Evan Lucas +tjarlama <59913901+tjarlama@users.noreply.github.com> +司徒玟琅 +YuMeiJie +Aleksej Lebedev +Nikolay Mitev +Ulrik Strid +Elad Lahav diff --git a/include/libuv/ChangeLog b/include/libuv/ChangeLog index 30cdf7a67..8788d946b 100644 --- a/include/libuv/ChangeLog +++ b/include/libuv/ChangeLog @@ -1,4 +1,2379 @@ -2016.05.17, Version 1.9.1 (Stable) +2020.09.26, Version 1.40.0 (Stable) + +Changes since version 1.39.0: + +* udp: add UV_UDP_MMSG_FREE recv_cb flag (Ryan Liptak) + +* include: re-map UV__EPROTO from 4046 to -4046 (YuMeiJie) + +* doc: correct UV_UDP_MMSG_FREE version added (cjihrig) + +* doc: add uv_metrics_idle_time() version metadata (Ryan Liptak) + +* win,tty: pass through utf-16 surrogate pairs (Mustafa M) + +* unix: fix DragonFly BSD build (Aleksej Lebedev) + +* win,udp: fix error code returned by connect() (Santiago Gimeno) + +* src: suppress user_timeout maybe-uninitialized (Daniel Bevenius) + +* test: fix compiler warning (Vladimír Čunát) + +* build: fix the Haiku cmake build (David Carlier) + +* linux: fix i386 sendmmsg/recvmmsg support (Ben Noordhuis) + +* build: add libuv-static pkg-config file (Nikolay Mitev) + +* unix,win: add uv_timer_get_due_in() (Ulrik Strid) + +* build,unix: add QNX support (Elad Lahav) + +* include: remove incorrect UV__ERR() for EPROTO (cjihrig) + + +2020.08.26, Version 1.39.0 (Stable), 25f4b8b8a3c0f934158cd37a37b0525d75ca488e + +Changes since version 1.38.1: + +* unix: use relaxed loads/stores for clock id (Ben Noordhuis) + +* build,win: link to user32.lib and advapi32.lib (George Zhao) + +* unix: squelch harmless valgrind warning (ssrlive) + +* include: fx c++ style comments warnings (Turbinya) + +* build,cmake: Change installation location on MinGW (erw7) + +* linux: use copy_file_range for uv_fs_copyfile when possible (Carter Li) + +* win,tcp: avoid reinserting a pending request ( + +* docs: improve the descriptions for get memory info (Juan Sebastian velez + Posada) + +* test: add udp-mmsg test (Ryan Liptak) + +* udp: add uv_udp_using_recvmmsg query (Ryan Liptak) + +* doc: add more error constants (TK-one) + +* zos: fix potential event loop stall (Trevor Norris) + +* include: add internal fields struct to uv_loop_t (Trevor Norris) + +* core: add API to measure event loop idle time (Trevor Norris) + +* win,fs: use CreateDirectoryW instead of _wmkdir (Mustafa M) + +* win,nfc: fix integer comparison signedness (escherstair) + +* win,nfc: use + +* win,nfc: removed some unused variables (escherstair) + +* win,nfc: add missing return statement (escherstair) + +* win,nfc: disable clang-format for + +* darwin: use IOKit for uv_cpu_info (Evan Lucas) + +* test: fix thread race in process_title_threadsafe (Ben Noordhuis) + +* win,fs: avoid implicit access to _doserrno (Jameson Nash) + +* test: give hrtime test a custom 20s timeout (Jameson Nash) + +* build: add more failed test, for qemu version bump (gengjiawen) + +* unix: handle src, dest same in uv_fs_copyfile() (cjihrig) + +* unix: error when uv_setup_args() is not called (Ryan Liptak) + +* aix: protect uv_exepath() from uv_set_process_title() (Richard Lau) + +* fs: clobber req->path on uv_fs_mkstemp() error (tjarlama) + +* cmake: fix compile error C2001 on Chinese Windows (司徒玟琅) + +* test: avoid double evaluation in ASSERT_BASE macro (tjarlama) + +* tcp: fail instantly if local port is unbound (Bartosz Sosnowski) + +* doc: fix most sphinx warnings (Jameson Nash) + +* nfci: address some style nits (Jameson Nash) + +* unix: don't use _POSIX_PATH_MAX (Ben Noordhuis) + + +2020.07.04, Version 1.38.1 (Stable), e8b989ea1f7f9d4083511a2caec7791e9abd1871 + +Changes since version 1.38.0: + +* test: use last matching qemu version (cjihrig) + +* win, util: rearrange uv_hrtime (Bartosz Sosnowski) + +* test: skip signal_multiple_loops test on QEMU (gengjiawen) + +* build: add android build to CI (gengjiawen) + +* test: extend fs_event_error_reporting timeout (cjihrig) + +* build: link libkvm on netbsd only (Alexander Tokmakov) + +* linux: refactor /proc file reader logic (Ben Noordhuis) + +* linux: read load average from /proc/loadavg (Ben Noordhuis) + +* android: remove patch code for below 21 (gengjiawen) + +* win: fix visual studio 2008 build (Arenoros) + +* win,tty: fix deadlock caused by inconsistent state (lander0s) + +* unix: use relaxed loads/stores for feature checks (Ben Noordhuis) + +* build: don't .gitignore m4/ax_pthread.m4 (Ben Noordhuis) + +* unix: fix gcc atomics feature check (Ben Noordhuis) + +* darwin: work around clock jumping back in time (Ben Noordhuis) + +* udp: fix write_queue cleanup on sendmmsg error (Santiago Gimeno) + +* src: build fix for Android (David Carlier) + + +2020.05.18, Version 1.38.0 (Stable), 1ab9ea3790378f9f25c4e78e9e2b511c75f9c9ed + +Changes since version 1.37.0: + +* test: skip poll_duplex and poll_unidirectional on PASE (Xu Meng) + +* linux: make cpu_times consistently be milliseconds (James Ross) + +* win: DRY uv_poll_start() and uv_poll_stop() (Ben Noordhuis) + +* win: DRY uv_poll_close() (Ben Noordhuis) + +* unix,win: add uv_library_shutdown() (Ben Noordhuis) + +* unix: yield cpu when spinlocking on async handle (Ben Noordhuis) + +* win: remove dep on GetQueuedCompletionStatusEx (Colin Finck) + +* doc: correct source lines (Shohei YOSHIDA) + +* build,android: fix typo (twosee) + +* doc: uv_cancel() handles uv_random_t requests (Philip Chimento) + +* doc: fix unescaped character (Philip Chimento) + +* build,cmake: fix compilation on old MinGW (erw7) + +* build: remove unnessesary MSVC warnings (Bartosz Sosnowski) + +* win: make uv_udp_init_ex() accept UV_UDP_RECVMMSG (Ben Noordhuis) + +* unix: simplify uv__udp_init_ex() (Ben Noordhuis) + +* win: remove MAX_PATH limitations (Bartosz Sosnowski) + +* build, win: add long path aware manifest (Bartosz Sosnowski) + +* doc: check/idle/prepare functions always succeed (Ben Noordhuis) + +* darwin: fix build with non-apple compilers (Ben Noordhuis) + +* win: support environment variables > 32767 chars (Ben Noordhuis) + +* unix: fully initialize struct msghdr (Ben Noordhuis) + +* doc: add uv_replace_allocator thread safety warning (twosee) + +* unix: fix int overflow when copying large files (Michal Artazov) + +* fs: report original error (Bartosz Sosnowski) + +* win, fs: add IO_REPARSE_TAG_APPEXECLINK support (Bartosz Sosnowski) + +* doc: fix formatting (Ben Noordhuis) + +* unix: fix memory leak when uv_loop_init() fails (Anna Henningsen) + +* unix: shrink uv_udp_set_source_membership() stack (Ben Noordhuis) + +* unix,win: fix wrong sizeof argument to memcpy() (Ben Noordhuis) + +* build: check for libraries not provided by libc (Jeroen Roovers) + +* doc: fix the order of arguments to calloc() (MasterDuke17) + +* unix: don't abort when getrlimit() fails (Ben Noordhuis) + +* test: support common user profile on IBMi (Xu Meng) + +* build: test on more platforms via QEMU in CI (gengjiawen) + + +2020.04.20, Version 1.37.0 (Stable), 02a9e1be252b623ee032a3137c0b0c94afbe6809 + +Changes since version 1.36.0: + +* timer: remove redundant check in heap compare (Yash Ladha) + +* udp: add flag to enable recvmmsg(2) explicitly (Saúl Ibarra Corretgé) + + +2020.04.16, Version 1.36.0 (Stable), 533b738838ad8407032e14b6772b29ef9af63cfa + +Changes since version 1.35.0: + +* build: add aix-common.c for AIX cmake build (Jesse Gorzinski) + +* zos: explicitly mark message queue events (Irek Fakhrutdinov) + +* zos: move mq check out of loop to save cpu cycles (Irek Fakhrutdinov) + +* zos: add checks to ensure behavior of epoll_wait (Irek Fakhrutdinov) + +* src: add uv__reallocf() (Ben Noordhuis) + +* build: ibmi support for cmake (Jesse Gorzinski) + +* build: fix gyp build for Android API >= 28 (Lin Zhang) + +* udp: return recvmmsg-ed datagrams in order (Saúl Ibarra Corretgé) + +* zos,test: fix spawn_empty_env for shared library build (Richard Lau) + +* zos: fix non-Release builds (Richard Lau) + +* zos: fix return value on expired nanosleep() call (Richard Lau) + +* build: fix z/OS cmake build (Richard Lau) + +* test: add a bunch of ASSERT macros (Santiago Gimeno) + +* test: remove unused extern declaration (Ben Noordhuis) + +* test: canonicalize argv[0] in exepath test (Ben Noordhuis) + +* test: simplify platform_init() (Ben Noordhuis) + +* ibmi: Fix isatty EBADF handling and refactor (Kevin Adler) + +* test: Test EBADF tty handling (Kevin Adler) + +* build: make cmake build benchmarks (Ben Noordhuis) + +* win: use RtlGenRandom from advapi32.dll directly (Ben Noordhuis) + +* android: fix OOB write in uv_interface_addresses() (Lin Zhang) + +* test: pass test when hostname is single character (毛毛) + +* ibmi: set the highest process priority to -10 (Xu Meng) + +* build: remove support for gyp (Ben Noordhuis) + +* doc: add note to README on cross-compiling (Ben Noordhuis) + +* fs: add uv_fs_lutime() (Sk Sajidul Kadir) + +* unix: implement cpu_relax() for arm (David Carlier) + +* linux: fix uv__accept4() (twosee) + +* win: handle file paths in uv_fs_statfs() (erw7) + +* unix: fix uv_os_environ() null pointer check (Rikard Falkeborn) + +* win: fix uv_os_environ() null pointer check (Rikard Falkeborn) + +* unix: fix compilation on macOS 32-bit architectures (Brad King) + +* win: replace alloca() with stack-based array (Ben Noordhuis) + + +2020.03.12, Version 1.35.0 (Stable), e45f1ec38db882f8dc17b51f51a6684027034609 + +Changes since version 1.34.2: + +* src: android build fix (David Carlier) + +* build: make code compilable for iOS on Xcode (ssrlive) + +* ibmi: skip unsupported fs test cases (Xu Meng) + +* ibmi: ensure that pipe backlog is not zero (Xu Meng) + +* test,udp6: fix udp_ipv6 test flakiness (Jameson Nash) + +* test: fix fs_event_watch_dir_recursive flakiness (Santiago Gimeno) + +* pipe: disallow listening on an IPC pipe (Witold Kręcicki) + +* build,cmake: improve buil experience (Isabella Muerte) + +* unix: remove support for FreeBSD < 10 (Saúl Ibarra Corretgé) + +* linux: simplify uv__accept() (Ben Noordhuis) + +* linux: assume presence of SOCK_CLOEXEC flag (Ben Noordhuis) + +* linux: simplify uv__dup2_cloexec() (Ben Noordhuis) + +* freebsd,linux: simplify uv__make_socketpair() (Ben Noordhuis) + +* unix: fix error handling in uv__make_socketpair() (Ben Noordhuis) + +* freebsd,linux: simplify uv__make_pipe() (Ben Noordhuis) + +* unix: fix error handling in uv__make_pipe() (Ben Noordhuis) + +* linux: simplify uv__async_eventfd() (Ben Noordhuis) + +* linux: assume the presence of inotify system calls (Ben Noordhuis) + +* doc: strip ICC profile from 2 jpg files (Dominique Dumont) + +* unix: make uv_tcp_keepalive predictable (Manuel BACHMANN) + +* docs: uv_setup_args() may take ownership of argv (Ben Noordhuis) + +* unix: fix error path in uv_setup_args() (Ben Noordhuis) + +* unix: fix size check in uv_get_process_title() (Ben Noordhuis) + +* doc: add erw7 to maintainers (erw7) + +* test: fixed udp4_echo_server implementation (Marek Vavrusa) + +* test: added udp ping benchmark (1,10,100 pingers) (Marek Vavrusa) + +* freebsd,linux: add recvmmsg() + sendmmsg() udp implementation (Marek Vavrusa) + +* win,pipe: DRY/simplify some code paths (Jameson Nash) + +* win: address some style nits (Jameson Nash) + +* win,pipe: ensure `req->event_handle` is defined (Elliot Saba) + +* win,pipe: consolidate overlapped initialization (Elliot Saba) + +* win,pipe: erase event_handle after deleting pointer (Jameson Nash) + +* build: fix android cmake build, build missing file (Ben Noordhuis) + +* test: skip some UDP tests on IBMi (Xu Meng) + +* test: skip some spawn test cases on IBMi (Xu Meng) + +* src: fix wrong method name in comment (TK-one) + +* test: add UV_TIMEOUT_MULTIPLIER environment var (Ben Noordhuis) + +* unix: fix uv_cpu_info always returning UV_ENOTDIR on OpenBSD (Ben Davies) + +* test: skip the pwd_shell test on IBMi (Xu Meng) + +* win,tty: Change to restore cursor shape with uv_tty_reset() (erw7) + +* win,tty: Added set cursor style to CSI sequences (erw7) + +* test: handle EINTR, fix EOF check in poll test (Ben Noordhuis) + +* unix: use socklen_t instead of size_t (Ben Noordhuis) + +* doc: fix header file location (TK-one) + +* unix: fix signal handle closing deferral (Ben Noordhuis) + +* ibmi: set the amount of memory in use to zero (Xu Meng) + +* zos: return on realloc failure in scandir() (Milad Farazmand) + +* zos: fix scandir() error path NULL pointer deref (Ben Noordhuis) + + +2020.01.24, Version 1.34.2 (Stable), f868c9ab0c307525a16fff99fd21e32a6ebc3837 + +Changes since version 1.34.1: + +* misc: adjust stalebot deadlines (Jameson Nash) + +* test: fix env-vars flakiness (cjihrig) + +* test: avoid truncating output lines (Jameson Nash) + +* darwin: stop calling SetApplicationIsDaemon() (Ben Noordhuis) + +* ibmi: implement uv_interface_addresses() (Xu Meng) + +* osx,fsevent: fix race during uv_loop_close (Jameson Nash) + +* osx,fsevent: clear pointer when deleting it [NFCI] (Jameson Nash) + +* Revert "aix: replace ECONNRESET with EOF if already closed" (Jameson Nash) + +* unix: handle uv__open_cloexec return value correctly (Anna Henningsen) + + +2020.01.13, Version 1.34.1 (Stable), 8aa5636ec72990bb2856f81e14c95813024a5c2b + +Changes since version 1.34.0: + +* unix: fix -Wstrict-aliasing compiler warning (Ben Noordhuis) + +* unix: cache address of dlsym("mkostemp") (Ben Noordhuis) + +* build: remove -pedantic from compiler flags (Ben Noordhuis) + +* Revert "darwin: assume pthread_setname_np() is available" (Ben Noordhuis) + +* Revert "darwin: speed up uv_set_process_title()" (Ben Noordhuis) + +* darwin: assume pthread_setname_np() is available (Ben Noordhuis) + +* ibmi: fix the false isatty() issue on IBMi (Xu Meng) + +* test: fix test failure under NetBSD and OpenBSD (David Carlier) + +* test: skip some test cases on IBMi (Xu Meng) + +* test: skip uv_(get|set)_process_title on IBMi (Xu Meng) + +* doc: remove binaries for Windows from README (Richard Lau) + +* unix: fix -Wunused-but-set-variable warning (George Zhao) + +* unix: pass sysctl size arg using ARRAY_SIZE macro (David Carlier) + +* test: disallow running the test suite as root (cjihrig) + +* unix: suppress -Waddress-of-packed-member warning (Ben Noordhuis) + +* misc: make more tags "not-stale" (Jameson Nash) + +* test: fix pthread memory leak (Trevor Norris) + +* docs: delete socks5-proxy sample (Jameson Nash) + +* ibmi: fix the CMSG length issue (Xu Meng) + +* docs: fix formatting (Jameson Nash) + +* unix: squelch fchmod() EPERM on CIFS share (Ben Noordhuis) + +* docs: fix linkcheck (Jameson Nash) + +* docs: switch from linux.die.net to man7.org (Jameson Nash) + +* win: remove abort when non-IFS LSP detection fails (virtualyw) + +* docs: clarify that uv_pipe_t is a pipe (Jameson Nash) + +* win,tty: avoid regressions in utf-8 handling (Jameson Nash) + +* win: remove bad assert in uv_loop_close (Jameson Nash) + +* test: fix -fno-common build errors (Ben Noordhuis) + +* build: turn on -fno-common to catch regressions (Ben Noordhuis) + +* test: fix fs birth time test failure (Ben Noordhuis) + +* tty,unix: avoid affecting controlling TTY (Jameson Nash) + + +2019.12.05, Version 1.34.0 (Stable), 15ae750151ac9341e5945eb38f8982d59fb99201 + +Changes since version 1.33.1: + +* unix: move random-sysctl to random-sysctl-linux (nia) + +* netbsd: use KERN_ARND sysctl to get entropy (nia) + +* unix: refactor uv__fs_copyfile() logic (cjihrig) + +* build: fix android build, add missing sources (Ben Noordhuis) + +* build: fix android build, fix symbol redefinition (Ben Noordhuis) + +* build: fix android autotools build (Ben Noordhuis) + +* fs: handle non-functional statx system call (Milad Farazmand) + +* unix,win: add uv_sleep() (cjihrig) + +* doc: add richardlau to maintainers (Richard Lau) + +* aix: fix netmask for IPv6 (Richard Lau) + +* aix: clean up after errors in uv_interface_addresses() (Richard Lau) + +* aix: fix setting of physical addresses (Richard Lau) + +* fs: add uv_fs_mkstemp (Saúl Ibarra Corretgé) + +* unix: switch uv_sleep() to nanosleep() (Ben Noordhuis) + +* unix: retry on EINTR in uv_sleep() (Ben Noordhuis) + +* zos: fix nanosleep() emulation (Ben Noordhuis) + + +2019.10.20, Version 1.33.1 (Stable), 07ad32138f4d2285ba2226b5e20462b27b091a59 + +Changes since version 1.33.0: + +* linux: fix arm64 SYS__sysctl build breakage (Ben Noordhuis) + + +2019.10.17, Version 1.33.0 (Stable), e56e42e9310e4437e1886dbd6771792c14c0a5f3 + +Changes since version 1.32.0: + +* Revert "linux: drop code path for epoll_pwait-less kernels" (Yang Yu) + +* build: fix build error with __ANDROID_API__ < 21 (Yang Yu) + +* win: fix reading hidden env vars (Anna Henningsen) + +* unix,win: add uv_random() (Ben Noordhuis) + +* win: simplify mkdtemp (Saúl Ibarra Corretgé) + +* docs: fix literal-includes in User Guide (Nhan Khong) + +* win, tty: fix problem of receiving unexpected SIGWINCH (erw7) + +* unix: fix {Net,Open}BSD build (David Carlier) + +* win,mingw: Fix undefined MCAST_* constants (Crunkle) + +* build: Add link for test/fixtures/lorem_ipsum.txt (Andrew Paprocki) + +* fs: use statvfs in uv__fs_statfs() for Haiku (Calvin Hill) + +* fsevents: stop using fsevents to watch files (Jameson Nash) + +* fsevents: regression in watching / (Jameson Nash) + +* build,cmake: don't try to detect a C++ compiler (Isabella Muerte) + +* build: fix build warning on cygwin (MaYuming) + +* unix: set sin_len and sin6_len (Ouyang Yadong) + +* test: fix order of operations in test (cjihrig) + +* doc: improve uv_fs_readdir() cleanup docs (cjihrig) + +* build: remove duplicated test in build files (ZYSzys) + +* android: enable getentropy on Android >= 28 (David Carlier) + +* android: fix build (David Carlier) + +* darwin: speed up uv_set_process_title() (Ben Noordhuis) + +* darwin: assume pthread_setname_np() is available (Ben Noordhuis) + +* unix,udp: ensure addr is non-null (Jameson Nash) + +* win,tty: add uv_tty_{get,set}_vterm_state (erw7) + +* win: fix uv_statfs_t leak in uv_fs_statfs() (Ryan Liptak) + +* build: install files on windows via cmake (Carl Lei) + +* darwin,test: include AvailabilityMacros.h (Saúl Ibarra Corretgé) + +* darwin,test: update loop time after sleeping (Saúl Ibarra Corretgé) + +* doc: remove old FreeBSD 9 related note (Saúl Ibarra Corretgé) + +* doc: improve uv_{send,recv}_buffer_size() docs (Ryan Liptak) + +* build: move -Wno-long-long check to configure time (Ben Noordhuis) + +* unix: update uv_fs_copyfile() fallback logic (Stefan Bender) + +* win: cast setsockopt struct to const char* (Shelley Vohr) + + +2019.09.10, Version 1.32.0 (Stable), 697bea87b3a0b0e9b4e5ff86b39d1dedb70ee46d + +Changes since version 1.31.0: + +* misc: enable stalebot (Saúl Ibarra Corretgé) + +* win: map ERROR_ENVVAR_NOT_FOUND to UV_ENOENT (cjihrig) + +* win: use L'\0' as UTF-16 null terminator (cjihrig) + +* win: support retrieving empty env variables (cjihrig) + +* unix,stream: fix returned error codes (Santiago Gimeno) + +* test: fix typo in DYLD_LIBRARY_PATH (Ben Noordhuis) + +* unix,signal: keep handle active if pending signal (Santiago Gimeno) + +* openbsd: fix uv_cpu_info (Santiago Gimeno) + +* src: move uv_free_cpu_info to uv-common.c (Santiago Gimeno) + +* tcp: add uv_tcp_close_reset method (Santiago Gimeno) + +* test: fix udp-multicast-join tests (Santiago Gimeno) + +* test: remove assertion in fs_statfs test (cjihrig) + +* doc: clarify uv_buf_t usage in uv_alloc_cb (Tomas Krizek) + +* win: fix typo in preprocessor expression (Konstantin Podsvirov) + +* timer: fix uv_timer_start on closing timer (seny) + +* udp: add source-specific multicast support (Vladimir Karnushin) + +* udp: fix error return values (Santiago Gimeno) + +* udp: drop IPV6_SSM_SUPPORT macro (Santiago Gimeno) + +* udp: fix uv__udp_set_source_membership6 (Santiago Gimeno) + +* udp: use sockaddr_storage instead of union (Santiago Gimeno) + +* build,zos: add _OPEN_SYS_SOCK_EXT3 flag (Santiago Gimeno) + +* test: add specific source multicast tests (Santiago Gimeno) + +* include: map EILSEQ error code (cjihrig) + +* win, tty: improve SIGWINCH performance (Bartosz Sosnowski) + +* build: fix ios build error (MaYuming) + +* aix: replace ECONNRESET with EOF if already closed (Milad Farazmand) + +* build: add cmake library VERSION, SOVERSION (Eneas U de Queiroz) + +* build: make include/ public in CMakeLists.txt (Ben Noordhuis) + +* build: export USING_UV_SHARED=1 to cmake deps (Ben Noordhuis) + +* build: cmake_minimum_required(VERSION 2.8.12) (Daniel Hahler) + +* aix: Fix broken cmpxchgi() XL C++ specialization. (Andrew Paprocki) + +* test: fix -Wsign-compare warning (Ben Noordhuis) + +* unix: simplify open(O_CLOEXEC) feature detection (Ben Noordhuis) + +* unix: fix UV_FS_O_DIRECT definition on Linux (Joran Dirk Greef) + +* doc: uv_handle_t documentation suggestion (Daniel Bevenius) + + +2019.08.10, Version 1.31.0 (Stable), 0a6771cee4c15184c924bfe9d397bdd0c3b206ba + +Changes since version 1.30.1: + +* win,fs: don't modify global file translation mode (Javier Blazquez) + +* win: fix uv_os_tmpdir when env var is 260 chars (Mustafa M) + +* win: prevent tty event explosion machine hang (Javier Blazquez) + +* win: add UV_FS_O_FILEMAP (João Reis) + +* win, fs: mkdir return UV_EINVAL for invalid names (Bartosz Sosnowski) + +* github: add root warning to template (cjihrig) + +* win: misc fs cleanup (cjihrig) + +* unix,win: add uv_fs_statfs() (cjihrig) + +* test: avoid AF_LOCAL (Carlo Marcelo Arenas Belón) + +* unix,win: add ability to retrieve all env variables (Saúl Ibarra Corretgé) + +* Revert "darwin: speed up uv_set_process_title()" (Ben Noordhuis) + +* doc: add %p to valgrind log-file arg (Zach Bjornson) + +* doc: fix typo in basics.rst (Nan Xiao) + +* ibmi: support Makefile build for IBM i (Xu Meng) + +* OpenBSD: only get active CPU core count (Ben Davies) + +* test: fix gcc 8 warnings for tests (Nhan Khong) + +* ibmi: use correct header files (Xu Meng) + +* unix: clear UV_HANDLE_READING flag before callback (zyxwvu Shi) + +* unix: fix unused-function warning on BSD (Nhan Khong) + +* test: fix test runner on MinGW (Crunkle) + +* win: remove try-except outside MSVC (Crunkle) + +* win: fix uv_spawn() ENOMEM on empty env (Ben Noordhuis) + + +2019.07.03, Version 1.30.1 (Stable), 1551969c84c2f546a429dac169c7fdac3e38115e + +Changes since version 1.30.0: + +* doc: fix incorrect versionchanged (cjihrig) + +* test: allow UV_ECONNRESET in tcp_try_write_error (cjihrig) + +* unix: add uv_get_constrained_memory() cygwin stub (cjihrig) + +* build: fix android cmake build (Ben Noordhuis) + +* unix: squelch -Wcast-function-type warning (Ben Noordhuis) + +* build: fix compile error with uClibc (zlargon) + + +2019.06.28, Version 1.30.0 (Stable), 365b6f2a0eacda1ff52be8e57ab9381cfddc5dbb + +Changes since version 1.29.1: + +* darwin: fall back to F_BARRIERFSYNC (Ben Noordhuis) + +* darwin: add 32 bit close$NOCANCEL implementation (ken-cunningham-webuse) + +* build, core, unix: add support for Haiku (Leorize) + +* darwin,linux: more conservative minimum stack size (Ben Noordhuis) + +* threadpool: increase UV_THREADPOOL_SIZE limit (Vlad A) + +* unix: return actual error from `uv_try_write()` (Anna Henningsen) + +* darwin: fix build error with macos 10.10 (Ben Noordhuis) + +* unix: make uv_cwd() report UV_ENOBUFS (Ben Noordhuis) + +* unix: make uv_fs_read() fill all buffers (Ben Noordhuis) + +* test: give hrtime test a custom 10s timeout (Ben Noordhuis) + +* fs: fix uv_fs_copyfile if same src and dst (Santiago Gimeno) + +* build: add cmake option to skip building tests (Niels Lohmann) + +* doc: add link to nodejs.org (Jenil Christo) + +* unix: fix a comment typo in signal.c (Evgeny Ermakov) + +* unix: remove redundant cast in process.c (gengjiawen) + +* doc: fix wrong mutex function prototypes (Leo Chung) + + +2019.05.22, Version 1.29.1 (Stable), d16e6094e1eb3b0b5981ef1dd7e03ec4d466944d + +Changes since version 1.29.0: + +* unix: simplify uv/posix.h include logic (cjihrig) + +* test: increase test timeout (cjihrig) + +* linux: fix sscanf() overflows reading from /proc (Ben Noordhuis) + + +2019.05.16, Version 1.29.0 (Stable), 43957efd92c167b352b4c948b617ca7afbee0ed1 + +Changes since version 1.28.0: + +* ibmi: read memory and CPU usage info (Xu Meng) + +* doc: update the cmake testing instruction (zlargon) + +* unix: fix race condition in uv_async_send() (Ben Noordhuis) + +* linux: use O_CLOEXEC instead of EPOLL_CLOEXEC (Ben Noordhuis) + +* doc: mark uv_async_send() as async-signal-safe (Ben Noordhuis) + +* linux: init st_flags and st_gen when using statx (Oscar Waddell) + +* linux: read free/total memory from /proc/meminfo (Ben Noordhuis) + +* test: test zero-sized uv_fs_sendfile() writes (Ben Noordhuis) + +* unix: don't assert on UV_PROCESS_WINDOWS_* flags (Ben Noordhuis) + +* linux: set correct mac address for IP-aliases (Santiago Gimeno) + +* win,util: fix null pointer dereferencing (Tobias Nießen) + +* unix,win: fix `uv_fs_poll_stop()` when active (Anna Henningsen) + +* doc: add missing uv_fs_type entries (Michele Caini) + +* doc: fix build with sphinx 2.x (FX Coudert) + +* unix: don't make statx system call on Android (George Zhao) + +* unix: fix clang scan-build warning (Kyle Edwards) + +* unix: fall back to kqueue on older macOS systems (ken-cunningham-webuse) + +* unix,win: add uv_get_constrained_memory() (Kelvin Jin) + +* darwin: fix thread cancellation fd leak (Ben Noordhuis) + +* linux: fix thread cancellation fd leak (Ben Noordhuis) + + +2019.04.16, Version 1.28.0 (Stable), 7bf8fabfa934660ee0fe889f78e151198a1165fc + +Changes since version 1.27.0: + +* unix,win: add uv_gettimeofday() (cjihrig) + +* unix,win: add uv_fs_{open,read,close}dir() (cjihrig) + +* unix: fix uv_interface_addresses() (Andreas Rohner) + +* fs: remove macOS-specific copyfile(3) (Rich Trott) + +* fs: add test for copyfile() respecting permissions (Rich Trott) + +* build: partially revert 5234b1c43a (Ben Noordhuis) + +* zos: fix setsockopt error when using AF_UNIX (Milad Farazmand) + +* unix: suppress EINTR/EINPROGRESS in uv_fs_close() (Ben Noordhuis) + +* build: use cmake APPLE variable to detect platform (zlargon) + +* distcheck: remove duplicate test/ entry (Jameson Nash) + +* unix: remove unused cmpxchgl() function (Ben Noordhuis) + +* unix: support sockaddr_un in uv_udp_send() (Yury Selivanov) + +* unix: guard use of PTHREAD_STACK_MIN (Kamil Rytarowski) + +* unix,win: introduce uv_timeval64_t (cjihrig) + +* doc: document uv_timeval_t and uv_timeval64_t (cjihrig) + + +2019.03.17, Version 1.27.0 (Stable), a4fc9a66cc35256dbc4dcd67c910174f05b6daa6 + +Changes since version 1.26.0: + +* doc: describe unix signal handling better (Vladimír Čunát) + +* linux: use statx() to obtain file birth time (Ben Noordhuis) + +* src: fill sockaddr_in6.sin6_len when it's defined (Santiago Gimeno) + +* test: relax uv_hrtime() test assumptions (Ben Noordhuis) + +* build: make cmake install LICENSE only once (Thomas Karl Pietrowski) + +* bsd: plug uv_fs_event_start() error path fd leak (Ben Noordhuis) + +* unix: fix __FreeBSD_kernel__ typo (cjihrig) + +* doc: add note about uv_run() not being reentrant (Ben Noordhuis) + +* unix, win: make fs-poll close wait for resource cleanup (Anna Henningsen) + +* doc: fix typo in uv_thread_options_t definition (Ryan Liptak) + +* win: skip winsock initialization in safe mode (evgley) + +* unix: refactor getsockname/getpeername methods (Santiago Gimeno) + +* win,udp: allow to use uv_udp_open on bound sockets (Santiago Gimeno) + +* udp: add support for UDP connected sockets (Santiago Gimeno) + +* build: fix uv_test shared uv Windows cmake build (ptlomholt) + +* build: add android-configure scripts to EXTRA_DIST (Ben Noordhuis) + +* build: add missing header (cjihrig) + +* sunos: add perror() output prior to abort() (Andrew Paprocki) + +* test,sunos: disable UV_DISCONNECT handling (Andrew Paprocki) + +* sunos: disable __attribute__((unused)) (Andrew Paprocki) + +* test,sunos: use unistd.h code branch (Andrew Paprocki) + +* build,sunos: better handling of non-GCC compiler (Andrew Paprocki) + +* test,sunos: fix statement not reached warnings (Andrew Paprocki) + +* sunos: fix argument/prototype mismatch in atomics (Andrew Paprocki) + +* test,sunos: test-ipc.c lacks newline at EOF (Andrew Paprocki) + +* test: change spawn_stdin_stdout return to void (Andrew Paprocki) + +* test: remove call to floor() in test driver (Andrew Paprocki) + + +2019.02.11, Version 1.26.0 (Stable), 8669d8d3e93cddb62611b267ef62a3ddb5ba3ca0 + +Changes since version 1.25.0: + +* doc: fix uv_get_free_memory doc (Stephen Belanger) + +* unix: fix epoll cpu 100% issue (yeyuanfeng) + +* openbsd,tcp: special handling of EINVAL on connect (ptlomholt) + +* win: simplify registry closing in uv_cpu_info() (cjihrig) + +* src,include: define UV_MAXHOSTNAMESIZE (cjihrig) + +* win: return product name in uv_os_uname() version (cjihrig) + +* thread: allow specifying stack size for new thread (Anna Henningsen) + +* win: fix duplicate tty vt100 fn key (erw7) + +* unix: don't attempt to invalidate invalid fd (Ben Noordhuis) + + +2019.01.19, Version 1.25.0 (Stable), 4a10a9d425863330af199e4b74bd688e62d945f1 + +Changes since version 1.24.1: + +* Revert "win,fs: retry if uv_fs_rename fails" (Ben Noordhuis) + +* aix: manually trigger fs event monitoring (Gireesh Punathil) + +* unix: rename WRITE_RETRY_ON_ERROR macro (Ben Noordhuis) + +* darwin: DRY platform-specific error check (Ben Noordhuis) + +* unix: refactor uv__write() (Ben Noordhuis) + +* unix: don't send handle twice on partial write (Ben Noordhuis) + +* tty,win: fix Alt+key under WSL (Bartosz Sosnowski) + +* build: support running tests in out-of-tree builds (Jameson Nash) + +* fsevents: really watch files with fsevents on macos 10.7+ (Jameson Nash) + +* thread,mingw64: need intrin.h header for SSE2 MemoryBarrier (Jameson Nash) + +* win: fix sizeof-pointer-div warning (cjihrig) + +* unix,win: add uv_os_uname() (cjihrig) + +* win, tty: fix CreateFileW() return value check (Bartosz Sosnowski) + +* unix: enable IPv6 tests on OpenBSD (ptlomholt) + +* test: fix test-ipc spawn_helper exit_cb (Santiago Gimeno) + +* test: fix test-ipc tests (Santiago Gimeno) + +* unix: better handling of unsupported F_FULLFSYNC (Victor Costan) + +* win,test: de-flake fs_event_watch_dir_short_path (Refael Ackermann) + +* win: fix msvc warning (sid) + +* openbsd: switch to libuv's barrier implementation (ptlomholt) + +* unix,stream: fix zero byte writes (Santiago Gimeno) + +* ibmi: return EISDIR on read from directory fd (Kevin Adler) + +* build: wrap long lines in Makefile.am (cjihrig) + + +2018.12.17, Version 1.24.1 (Stable), 274f2bd3b70847cadd9a3965577a87e666ab9ac3 + +Changes since version 1.24.0: + +* test: fix platform_output test on cygwin (damon-kwok) + +* gitignore: ignore build/ directory (Damon Kwok) + +* unix: zero epoll_event before use (Ashe Connor) + +* darwin: use runtime check for file cloning (Ben Noordhuis) + +* doc: replace deprecated build command on macOS (Rick) + +* warnings: fix code that emits compiler warnings (Jameson Nash) + +* doc: clarify expected memory management strategy (Ivan Krylov) + +* test: add uv_inet_ntop(AF_INET) coverage (Ben Noordhuis) + +* unix: harden string copying, introduce strscpy() (Ben Noordhuis) + +* linux: get rid of strncpy() call (Ben Noordhuis) + +* aix: get rid of strcat() calls (Ben Noordhuis) + +* aix: fix data race in uv_fs_event_start() (Ben Noordhuis) + +* win: fs: fix `FILE_FLAG_NO_BUFFERING` for writes (Joran Dirk Greef) + +* build: don't link against -lpthread on Android (Michael Meier) + + +2018.11.14, Version 1.24.0 (Stable), 2d427ee0083d1baf995df4ebf79a3f8890e9a3e1 + +Changes since version 1.23.2: + +* unix: do not require PATH_MAX to be defined (Brad King) + +* win,doc: path encoding in uv_fs_XX is UTF-8 (hitesh) + +* unix: add missing link dependency on kFreeBSD (Svante Signell) + +* unix: add support for GNU/Hurd (Samuel Thibault) + +* test: avoid memory leak for test_output (Carlo Marcelo Arenas Belón) + +* zos: avoid UB with NULL pointer arithmetic (Carlo Marcelo Arenas Belón) + +* doc: add vtjnash to maintainers (Jameson Nash) + +* unix: restore skipping of phys_addr copy (cjihrig) + +* unix,win: make uv_interface_addresses() consistent (cjihrig) + +* unix: remove unnecessary linebreaks (cjihrig) + +* unix,win: handle zero-sized allocations uniformly (Ben Noordhuis) + +* unix: remove unused uv__dup() function (Ben Noordhuis) + +* core,bsd: refactor process_title functions (Santiago Gimeno) + +* win: Redefine NSIG to consider SIGWINCH (Jeremy Studer) + +* test: make sure that reading a directory fails (Sakthipriyan Vairamani) + +* win, tty: remove zero-size read callbacks (Bartosz Sosnowski) + +* test: fix test runner getenv async-signal-safety (Ben Noordhuis) + +* test: fix test runner execvp async-signal-safety (Ben Noordhuis) + +* test,unix: fix race in test runner (Ben Noordhuis) + +* unix,win: support IDNA 2008 in uv_getaddrinfo() (Ben Noordhuis) + +* win, tcp: avoid starving the loop (Bartosz Sosnowski) + +* win, dl: proper error messages on some systems (Bartosz Sosnowski) + +* win,fs: retry if uv_fs_rename fails (Bartosz Sosnowski) + +* darwin: speed up uv_set_process_title() (Ben Noordhuis) + +* aix: fix race in uv_get_process_title() (Gireesh Punathil) + +* win: support more fine-grained windows hiding (Bartosz Sosnowski) + + +2018.10.09, Version 1.23.2 (Stable), 34c12788d2e7308f3ac506c0abcbf74c0d6abd20 + +Changes since version 1.23.1: + +* unix: return 0 retrieving rss on cygwin (cjihrig) + +* unix: initialize uv_interface_address_t.phys_addr (cjihrig) + +* test: handle uv_os_setpriority() windows edge case (cjihrig) + +* tty, win: fix read stop for raw mode (Bartosz Sosnowski) + +* Revert "Revert "unix,fs: fix for potential partial reads/writes"" (Jameson + Nash) + +* unix,readv: always permit partial reads to return (Jameson Nash) + +* win,tty: fix uv_tty_close() (Bartosz Sosnowski) + +* doc: remove extraneous "on" (Ben Noordhuis) + +* unix,win: fix threadpool race condition (Anna Henningsen) + +* unix: rework thread barrier implementation (Ben Noordhuis) + +* aix: switch to libuv's own thread barrier impl (Ben Noordhuis) + +* unix: signal done to last thread barrier waiter (Ben Noordhuis) + +* test: add uv_barrier_wait serial thread test (Ali Ijaz Sheikh) + +* unix: optimize uv_fs_readlink() memory allocation (Ben Noordhuis) + +* win: remove req.c and other cleanup (Carlo Marcelo Arenas Belón) + +* aix: don't EISDIR on read from directory fd (Ben Noordhuis) + + +2018.09.22, Version 1.23.1 (Stable), d2282b3d67821dc53c907c2155fa8c5c6ce25180 + +Changes since version 1.23.0: + +* unix,win: limit concurrent DNS calls to nthreads/2 (Anna Henningsen) + +* doc: add addaleax to maintainers (Anna Henningsen) + +* doc: add missing slash in stream.rst (Emil Bay) + +* unix,fs: use utimes & friends for uv_fs_utime (Jeremiah Senkpiel) + +* unix,fs: remove linux fallback from utimesat() (Jeremiah Senkpiel) + +* unix,fs: remove uv__utimesat() syscall fallback (Jeremiah Senkpiel) + +* doc: fix argument name in tcp.rts (Emil Bay) + +* doc: notes on running tests, benchmarks, tools (Jamie Davis) + +* linux: remove epoll syscall wrappers (Ben Noordhuis) + +* linux: drop code path for epoll_pwait-less kernels (Ben Noordhuis) + +* Partially revert "win,code: remove GetQueuedCompletionStatus-based poller" + (Jameson Nash) + +* build: add compile for android arm64/x86/x86-64 (Andy Zhang) + +* doc: clarify that some remarks apply to windows (Bert Belder) + +* test: fix compiler warnings (Jamie Davis) + +* ibmi: return 0 from uv_resident_set_memory() (dmabupt) + +* win: fix uv_udp_recv_start() error translation (Ryan Liptak) + +* win,doc: improve uv_os_setpriority() documentation (Bartosz Sosnowski) + +* test: increase upper bound in condvar_5 (Jamie Davis) + +* win,tty: remove deadcode (Jameson Nash) + +* stream: autodetect direction (Jameson Nash) + + +2018.08.18, Version 1.23.0 (Stable), 7ebb26225f2eaae6db22f4ef34ce76fa16ff89ec + +Changes since version 1.22.0: + +* win,pipe: restore compatibility with the old IPC framing protocol (Bert + Belder) + +* fs: add uv_open_osfhandle (Bartosz Sosnowski) + +* doc: update Visual C++ Build Tools URL (Michał Kozakiewicz) + +* unix: loop starvation on successful write complete (jBarz) + +* win: add uv__getnameinfo_work() error handling (A. Hauptmann) + +* win: return UV_ENOMEM from uv_loop_init() (cjihrig) + +* unix,win: add uv_os_{get,set}priority() (cjihrig) + +* test: fix warning in test-tcp-open (Santiago Gimeno) + + +2018.07.11, Version 1.22.0 (Stable), 8568f78a777d79d35eb7d6994617267b9fb33967 + +Changes since version 1.21.0: + +* unix: remove checksparse.sh (Ben Noordhuis) + +* win: fix mingw build error (Ben Noordhuis) + +* win: fix -Wunused-function warnings in thread.c (Ben Noordhuis) + +* unix,win: merge timers implementation (Ben Noordhuis) + +* win: fix pointer type in pipe.c (Ben Noordhuis) + +* win: fixing build for older MSVC compilers (Michael Fero) + +* zos: clear poll events on every iteration (jBarz) + +* zos: write-protect message queue (jBarz) + +* zos: use correct pointer type in strnlen (jBarz) + +* unix,win: merge handle flags (Ben Noordhuis) + +* doc: update Imran Iqbal's GitHub handle (cjihrig) + +* src: add new error apis to prevent memory leaks (Shelley Vohr) + +* test: make test-condvar call uv_cond_wait (Jamie Davis) + +* fs: change position of uv_fs_lchown (Ujjwal Sharma) + + +2018.06.23, Version 1.21.0 (Stable), e4983a9b0c152932f7553ff4a9ff189d2314cdcb + +Changes since version 1.20.3: + +* unix,windows: map EFTYPE errno (cjihrig) + +* win: perform case insensitive PATH= comparison (cjihrig) + +* win, fs: uv_fs_fchmod support for -A files (Bartosz Sosnowski) + +* src,lib: fix comments (Tobias Nießen) + +* win,process: allow child pipe handles to be opened in overlapped mode (Björn + Linse) + +* src,test: fix idiosyncratic comment style (Bert Belder) + +* test: fs_fchmod_archive_readonly must return a value (Bert Belder) + +* win,pipe: fix incorrect error code returned from uv_pipe_write_impl() (Bert + Belder) + +* win,pipe: properly set uv_write_t.send_handle in uv_write2() (Bert Belder) + +* test: add vectored uv_write() ping-pong tests (Bert Belder) + +* win,pipe: support vectored uv_write() calls (Bert Belder) + +* win,pipe: refactor pipe read cancellation logic (Bert Belder) + +* test: improve output from IPC test helpers (Bert Belder) + +* test: add test for IPC deadlock on Windows ( + +* win,pipe: fix IPC pipe deadlock (Bert Belder) + +* unix: catch some cases of watching fd twice (Ben Noordhuis) + +* test: use custom timeout for getaddrinfo_fail_sync (Ben Noordhuis) + +* Revert "win: add Windows XP support to uv_if_indextoname()" (Bert Belder) + +* win,thread: remove fallback uv_cond implementation (Bert Belder) + +* src,test: s/olny/only (cjihrig) + +* unix: close signal pipe fds on unload (Ben Noordhuis) + +* win: allow setting udp socket options before bind (cjihrig) + +* unix: return UV_ENOTSUP on FICLONE_FORCE failure (cjihrig) + +* win,pipe: remove unreferenced local variable (Bert Belder) + +* win,code: remove GetQueuedCompletionStatus-based poller (Bert Belder) + +* win: remove the remaining dynamic kernel32 imports (Bert Belder) + +* test: speedup process-title-threadsafe on macOS (cjihrig) + +* core: move all include files except uv.h to uv/ (Saúl Ibarra Corretgé) + +* win: move stdint-msvc2008.h to include/uv/ (Ben Noordhuis) + +* build: fix cygwin install (Ben Noordhuis) + +* build,win: remove MinGW Makefile (Saúl Ibarra Corretgé) + +* build: add a cmake build file (Ben Noordhuis) + +* build: add test suite option to cmake build (Ben Noordhuis) + +* unix: set errno in uv_fs_copyfile() (cjihrig) + +* samples: fix inconsistency in parse_opts vs usage (zyxwvu Shi) + +* linux: handle exclusive POLLHUP with UV_DISCONNECT (Brad King) + +* include: declare uv_cpu_times_s in higher scope (Peter Johnson) + +* doc: add uv_fs_fsync() AIX limitations (jBarz) + +* unix,win: add uv_fs_lchown() (Paolo Greppi) + +* unix: disable clang variable length array warning (Peter Johnson) + +* doc: document uv_pipe_t::ipc (Ed Schouten) + +* doc: undocument uv_req_type's UV_REQ_TYPE_PRIVATE (Ed Schouten) + +* doc: document UV_*_MAP() macros (Ed Schouten) + +* win: remove use of min() macro in pipe.c (Peter Johnson) + +* doc: add jbarz as maintainer ( + + +2018.05.08, Version 1.20.3 (Stable), 8cfd67e59195251dff793ee47c185c9d6a8f3818 + +Changes since version 1.20.2: + +* win: add Windows XP support to uv_if_indextoname() (ssrlive) + +* win: fix `'floor' undefined` compiler warning (ssrlive) + +* win, pipe: stop read for overlapped pipe (Bartosz Sosnowski) + +* build: fix utf-8 name of copyright holder (Jérémy Lal) + +* zos: initialize pollfd revents (jBarz) + +* zos,doc: add system V message queue note (jBarz) + +* linux: don't use uv__nonblock_ioctl() on sparc (Ben Noordhuis) + + +2018.04.23, Version 1.20.2 (Stable), c51fd3f66bbb386a1efdeba6812789f35a372d1e + +Changes since version 1.20.1: + +* zos: use custom semaphore (jBarz) + +* win: fix registry API error handling (Kyle Farnung) + +* build: add support for 64-bit AIX (Richard Lau) + +* aix: guard STATIC_ASSERT for glibc work around (Richard Lau) + + +2018.04.19, Version 1.20.1 (Stable), 36ac2fc8edfd5ff3e9be529be1d4a3f0d5364e94 + +Changes since version 1.20.0: + +* doc,fs: improve documentation (Bob Burger) + +* win: return a floored double from uv_uptime() (Refael Ackermann) + +* doc: clarify platform specific pipe naming (Thomas Versteeg) + +* unix: fix uv_pipe_chmod() on macOS (zzzjim) + +* unix: work around glibc semaphore race condition (Anna Henningsen) + +* tcp,openbsd: disable Unix TCP check for IPV6_ONLY (Alex Arslan) + +* test,openbsd: use RETURN_SKIP in UDP IPv6 tests (Alex Arslan) + +* test,openbsd: fix multicast test (Alex Arslan) + +* Revert "win, fs: use FILE_WRITE_ATTRIBUTES when opening files" (cjihrig) + + +2018.04.03, Version 1.20.0 (Stable), 0012178ee2b04d9e4a2c66c27cf8891ad8325ceb + +Changes since version 1.19.2: + +* unix,spawn: respect user stdio flags for new pipe (Jameson Nash) + +* Revert "Revert "unix,tcp: avoid marking server sockets connected"" (Jameson + Nash) + +* req: revisions to uv_req_t handling (Jameson Nash) + +* win: remove unnecessary initialization (cjihrig) + +* win: update uv_os_homedir() to use uv_os_getenv() (cjihrig) + +* test: fix tcp_oob test flakiness (Santiago Gimeno) + +* posix: fix uv__pollfds_del() for invalidated fd's (Jesse Gorzinski) + +* doc: README: add note on installing gyp (Jamie Davis) + +* unix: refactor uv_os_homedir to use uv_os_getenv (Santiago Gimeno) + +* unix: fix several instances of lost errno (Michael Kilburn) + +* win,tty: update several TODO comments (Ruslan Bekenev) + +* unix: add UV_FS_COPYFILE_FICLONE support (cjihrig) + +* test: fix connect_unspecified (Santiago Gimeno) + +* unix,win: add UV_FS_COPYFILE_FICLONE_FORCE support (cjihrig) + +* win: use long directory name for handle->dirw (Nicholas Vavilov) + +* build: build with -D_FILE_OFFSET_BITS=64 again (Ben Noordhuis) + +* win, fs: fix uv_fs_unlink for +R -A files (Bartosz Sosnowski) + +* win, fs: use FILE_WRITE_ATTRIBUTES when opening files (Bartosz Sosnowski) + +* unix: use __PASE__ on IBM i platforms (Jesse Gorzinski) + +* test,freebsd: fix flaky poll tests (Santiago Gimeno) + +* test: increase connection timeout to 1 second (jBarz) + +* win,tcp: handle canceled connect with ECANCELED (Jameson Nash) + + +2018.02.22, Version 1.19.2 (Stable), c5afc37e2a8a70d8ab0da8dac10b77ba78c0488c + +Changes since version 1.19.1: + +* test: fix incorrect asserts (cjihrig) + +* test: fix a typo in test-fork.c (Felix Yan) + +* build: remove long-obsolete gyp workarounds (Ben Noordhuis) + +* build: split off tests into separate gyp file (Ben Noordhuis) + +* test: check uv_cond_timedwait more carefully (Jamie Davis) + +* include,src: introduce UV__ERR() macro (Mason X) + +* build: add url field to libuv.pc (Ben Noordhuis) + +* doc: mark IBM i as Tier 3 support (Jesse Gorzinski) + +* win,build: correct C2059 errors (Michael Fero) + +* zos: fix timeout for condition variable (jBarz) + +* win: CREATE_NO_WINDOW when stdio is not inherited (Nick Logan) + +* build: fix commmon.gypi comment (Ryuichi KAWAMATA) + +* doc: document uv_timer_start() on an active timer (Vladimír Čunát) + +* doc: add note about handle movability (Bartosz Sosnowski) + +* doc: fix syntax error in loop documentation (Bartosz Sosnowski) + +* osx,stream: retry sending handle on EMSGSIZE error (Santiago Gimeno) + +* unix: delay fs req register until after validation (cjihrig) + +* test: add tests for bad inputs (Joyee Cheung) + +* unix,win: ensure req->bufs is freed (cjihrig) + +* test: add additional fs memory management checks (cjihrig) + + +2018.01.20, Version 1.19.1 (Stable), 8202d1751196c2374ad370f7f3779daef89befae + +Changes since version 1.19.0: + +* Revert "unix,tcp: avoid marking server sockets connected" (Ben Noordhuis) + +* Revert "unix,fs: fix for potential partial reads/writes" (Ben Noordhuis) + +* Revert "win: use RemoveDirectoryW() instead of _wmrmdir()" (Ben Noordhuis) + +* cygwin: fix compilation of ifaddrs impl (Brad King) + + +2018.01.18, Version 1.19.0 (Stable), effbb7c9d29090b2e085a40867f8cdfa916a66df + +Changes since version 1.18.0: + +* core: add getter/setter functions for easier ABI compat (Anna Henningsen) + +* unix: make get(set)_process_title MT-safe (Matt Harrison) + +* unix,win: wait for threads to start (Ben Noordhuis) + +* test: add threadpool init/teardown test (Bartosz Sosnowski) + +* win, process: uv_kill improvements (Bartosz Sosnowski) + +* win: set _WIN32_WINNT to 0x0600 (cjihrig) + +* zos: implement uv_fs_event* functions (jBarz) + +* unix,tcp: avoid marking server sockets connected (Jameson Nash) + +* doc: mark Windows 7 as Tier 1 support (Bartosz Sosnowski) + +* win: map 0.0.0.0 and :: addresses to localhost (Bartosz Sosnowski) + +* build: install libuv.pc unconditionally (Ben Noordhuis) + +* test: remove custom timeout for thread test on ppc (Ben Noordhuis) + +* test: allow multicast not permitted status (Jérémy Lal) + +* test: allow net unreachable status in udp test (Ben Noordhuis) + +* unix: use SA_RESTART when setting our sighandler (Brad King) + +* unix,fs: fix for potential partial reads/writes (Ben Wijen) + +* win,build: do not build executable installer for dll (Bert Belder) + +* win: allow directory symlinks to be created in a non-elevated context (Bert + Belder) + +* zos,test: accept SIGKILL for flaky test (jBarz) + +* win: use RemoveDirectoryW() instead of _wmrmdir() (Ben Noordhuis) + +* unix: fix uv_cpu_info() error on FreeBSD (elephantp) + +* zos,test: decrease pings to avoid timeout (jBarz) + + +2017.12.02, Version 1.18.0 (Stable), 1489c98b7fc17f1702821a269eb0c5e730c5c813 + +Changes since version 1.17.0: + +* aix: fix -Wmaybe-uninitialized warning (cjihrig) + +* doc: remove note about SIGWINCH on Windows (Bartosz Sosnowski) + +* Revert "unix,win: wait for threads to start" (Ben Noordhuis) + +* unix,win: add uv_os_getpid() (Bartosz Sosnowski) + +* unix: remove incorrect assertion in uv_shutdown() (Jameson Nash) + +* doc: fix IRC URL in CONTRIBUTING.md (Matt Harrison) + + +2017.11.25, Version 1.17.0 (Stable), 1344d2bb82e195d0eafc0b40ba103f18dfd04cc5 + +Changes since version 1.16.1: + +* unix: avoid malloc() call in uv_spawn() (Ben Noordhuis) + +* doc: clarify the description of uv_loop_alive() (Ed Schouten) + +* win: map UV_FS_O_EXLOCK to a share mode of 0 (Joran Dirk Greef) + +* win: fix build on case-sensitive file systems (Ben Noordhuis) + +* win: fix test runner build with mingw64 (Ben Noordhuis) + +* win: remove unused variable in test/test-fs.c (Ben Noordhuis) + +* zos: add strnlen() implementation (jBarz) + +* unix: keep track of bound sockets sent via spawn (jBarz) + +* unix,win: wait for threads to start (Ben Noordhuis) + +* test: add threadpool init/teardown test (Bartosz Sosnowski) + +* test: avoid malloc() in threadpool test (Ben Noordhuis) + +* test: lower number of tasks in threadpool test (Ben Noordhuis) + +* win: issue memory barrier in uv_thread_join() (Ben Noordhuis) + +* ibmi: add support for new platform (Xu Meng) + +* test: fix test-spawn compilation (Bartosz Sosnowski) + + +2017.11.11, Version 1.16.1 (Stable), 4056fbe46493ef87237e307e0025e551db875e13 + +Changes since version 1.16.0: + +* unix: move net/if.h include (cjihrig) + +* win: fix undeclared NDIS_IF_MAX_STRING_SIZE (Nick Logan) + + +2017.11.07, Version 1.16.0 (Stable), d68779f0ea742918f653b9c20237460271c39aeb + +Changes since version 1.15.0: + +* win: change st_blksize from `2048` to `4096` (Joran Dirk Greef) + +* unix,win: add fs open flags, map O_DIRECT|O_DSYNC (Joran Dirk Greef) + +* win, fs: fix non-symlink reparse points (Wade Brainerd) + +* test: fix -Wstrict-prototypes warnings (Ben Noordhuis) + +* unix, windows: map ENOTTY errno (Ben Noordhuis) + +* unix: fall back to fsync() if F_FULLFSYNC fails (Joran Dirk Greef) + +* unix: do not close invalid kqueue fd after fork (jBarz) + +* zos: reset epoll data after fork (jBarz) + +* zos: skip fork_threadpool_queue_work_simple (jBarz) + +* test: keep platform_output as first test (Bartosz Sosnowski) + +* win: fix non-English dlopen error message (Bartosz Sosnowski) + +* unix,win: add uv_os_getppid() (cjihrig) + +* test: fix const qualification compiler warning (Ben Noordhuis) + +* doc: mark uv_default_loop() as not thread safe (rayrase) + +* win, pipe: null-initialize stream->shutdown_req (Jameson Nash) + +* tty, win: get SetWinEventHook pointer at startup (Bartosz Sosnowski) + +* test: no extra new line in skipped test output (Bartosz Sosnowski) + +* pipe: allow access from other users (Bartosz Sosnowski) + +* unix,win: add uv_if_{indextoname,indextoiid} (Pekka Nikander) + + +2017.10.03, Version 1.15.0 (Stable), 8b69ce1419d2958011d415a636810705c36c2cc2 + +Changes since version 1.14.1: + +* unix: limit uv__has_forked_with_cfrunloop to macOS (Kamil Rytarowski) + +* win: fix buffer size in uv__getpwuid_r() (tux.uudiin) + +* win,tty: improve SIGWINCH support (Bartosz Sosnowski) + +* unix: use fchmod() in uv_fs_copyfile() (cjihrig) + +* unix: support copying empty files (cjihrig) + +* unix: truncate destination in uv_fs_copyfile() (Nick Logan) + +* win,build: keep cwd when setting build environment (darobs) + +* test: add NetBSD support to test-udp-ipv6.c (Kamil Rytarowski) + +* unix: add NetBSD support in core.c (Kamil Rytarowski) + +* linux: increase thread stack size with musl libc (Ben Noordhuis) + +* netbsd: correct uv_exepath() on NetBSD (Kamil Rytarowski) + +* test: clean up semaphore after use (jBarz) + +* win,build: bump vswhere_usability_wrapper to 2.0.0 (Refael Ackermann) + +* win: let UV_PROCESS_WINDOWS_HIDE hide consoles (cjihrig) + +* zos: lock protect global epoll list in epoll_ctl (jBarz) + +* zos: change platform name to match python (jBarz) + +* android: fix getifaddrs() (Zheng, Lei) + +* netbsd: implement uv__tty_is_slave() (Kamil Rytarowski) + +* zos: fix readlink for mounts with system variables (jBarz) + +* test: sort the tests alphabetically (Sakthipriyan Vairamani) + +* windows: fix compilation warnings (Carlo Marcelo Arenas Belón) + +* build: avoid -fstrict-aliasing compile option (jBarz) + +* win: remove unused variables (Carlo Marcelo Arenas Belón) + +* unix: remove unused variables (Sakthipriyan Vairamani) + +* netbsd: disable poll_bad_fdtype on NetBSD (Kamil Rytarowski) + +* netbsd: use uv__cloexec and uv__nonblock (Kamil Rytarowski) + +* test: fix udp_multicast_join6 on NetBSD (Kamil Rytarowski) + +* unix,win: add uv_mutex_init_recursive() (Scott Parker) + +* netbsd: do not exclude IPv6 functionality (Kamil Rytarowski) + +* fsevents: watch files with fsevents on macos 10.7+ (Ben Noordhuis) + +* unix: retry on ENOBUFS in sendmsg(2) (Kamil Rytarowski) + + +2017.09.07, Version 1.14.1 (Stable), b0f9fb2a07a5e638b1580fe9a42a356c3ab35f37 + +Changes since version 1.14.0: + +* fs, win: add support for user symlinks (Bartosz Sosnowski) + +* cygwin: include uv-posix.h header (Joel Winarske) + +* zos: fix semaphore initialization (jBarz) + +* zos: improve loop_count benchmark performance (jBarz) + +* zos, test: flush out the oob data in callback (jBarz) + +* unix,win: check for bad flags in uv_fs_copyfile() (cjihrig) + +* unix: modify argv[0] when process title is set (Matthew Taylor) + +* unix: don't use req->loop in uv__fs_copyfile() (cjihrig) + +* doc: fix a trivial typo (Vladimír Čunát) + +* android: fix uv_cond_timedwait on API level < 21 (Gergely Nagy) + +* win: add uv__once_init() calls (Bartosz Sosnowski) + +* unix,windows: init all requests in fs calls (cjihrig) + +* unix,windows: return UV_EINVAL on NULL fs reqs (cjihrig) + +* windows: add POST macro to fs functions (cjihrig) + +* unix: handle partial sends in uv_fs_copyfile() (A. Hauptmann) + +* Revert "win, test: fix double close in test runner" (Bartosz Sosnowski) + +* win, test: remove surplus CloseHandle (Bartosz Sosnowski) + + +2017.08.17, Version 1.14.0 (Stable), e0d31e9e21870f88277746b6d59cf07b977cdfea + +Changes since version 1.13.1: + +* unix: check for NULL in uv_os_unsetenv for parameter name (André Klitzing) + +* doc: add thread safety warning for process title (Matthew Taylor) + +* unix: always copy process title into local buffer (Matthew Taylor) + +* poll: add support for OOB TCP and GPIO interrupts (CurlyMoo) + +* win,build: fix appveyor properly (Refael Ackermann) + +* win: include filename in dlopen error message (Ben Noordhuis) + +* aix: add netmask, mac address into net interfaces (Gireesh Punathil) + +* unix, windows: map EREMOTEIO errno (Ben Noordhuis) + +* unix: fix wrong MAC of uv_interface_address (XadillaX) + +* win,build: fix building from Windows SDK or VS console (Saúl Ibarra Corretgé) + +* github: fix link to help repo in issue template (Ben Noordhuis) + +* zos: remove nonexistent include from autotools build (Saúl Ibarra Corretgé) + +* misc: remove reference to pthread-fixes.h from LICENSE (Saúl Ibarra Corretgé) + +* docs: fix guide source code example paths (Anticrisis) + +* android: fix compilation with new NDK versions (Saúl Ibarra Corretgé) + +* misc: add android-toolchain to .gitignore (Saúl Ibarra Corretgé) + +* win, fs: support unusual reparse points (Bartosz Sosnowski) + +* android: fix detection of pthread_condattr_setclock (Saúl Ibarra Corretgé) + +* android: remove no longer needed check (Saúl Ibarra Corretgé) + +* doc: update instructions for building on Android (Saúl Ibarra Corretgé) + +* win, process: support semicolons in PATH variable (Bartosz Sosnowski) + +* doc: document uv_async_(init|send) return values (Ben Noordhuis) + +* doc: add Android as a tier 3 supported platform (Saúl Ibarra Corretgé) + +* unix: add missing semicolon (jBarz) + +* win, test: fix double close in test runner (Bartosz Sosnowski) + +* doc: update supported windows version baseline (Ben Noordhuis) + +* test,zos: skip chown root test (jBarz) + +* test,zos: use gid=-1 to test spawn_setgid_fails (jBarz) + +* zos: fix hr timer resolution (jBarz) + +* android: fix blocking recvmsg due to netlink bug (Jacob Segal) + +* zos: read more accurate rss info from RSM (jBarz) + +* win: allow bound/connected socket in uv_tcp_open() (Maciej Szeptuch + (Neverous)) + +* doc: differentiate SmartOS and SunOS support (cjihrig) + +* unix: make uv_poll_stop() remove fd from pollset (Ben Noordhuis) + +* unix, windows: add basic uv_fs_copyfile() (cjihrig) + + +2017.07.07, Version 1.13.1 (Stable), 2bb4b68758f07cd8617838e68c44c125bc567ba6 + +Changes since version 1.13.0: + +* Now working on version 1.13.1 (cjihrig) + +* build: workaround AppVeyor quirk (Refael Ackermann) + + +2017.07.06, Version 1.13.0 (Stable), 8342fcaab815f33b988c1910ea988f28dfe27edb + +Changes since version 1.12.0: + +* Now working on version 1.12.1 (cjihrig) + +* unix: avoid segfault in uv_get_process_title (Michele Caini) + +* build: add a comma to uv.gyp (Gemini Wen) + +* win: restore file pos after positional read/write (Bartosz Sosnowski) + +* unix,stream: return error on closed handle passing (Santiago Gimeno) + +* unix,benchmark: use fd instead of FILE* after fork (jBarz) + +* zos: avoid compiler warnings (jBarz) + +* win,pipe: race condition canceling readfile thread (Jameson Nash) + +* sunos: filter out non-IPv4/IPv6 interfaces (Sebastian Wiedenroth) + +* sunos: fix cmpxchgi and cmpxchgl type error (Sai Ke WANG) + +* unix: reset signal disposition before execve() (Ben Noordhuis) + +* unix: reset signal mask before execve() (Ben Noordhuis) + +* unix: fix POLLIN assertion on server read (jBarz) + +* zos: use stckf builtin for high-res timer (jBarz) + +* win,udp: implements uv_udp_try_send (Barnabas Gema) + +* win,udp: return UV_EINVAL instead of aborting (Romain Caire) + +* freebsd: replace kvm with sysctl (Robert Ayrapetyan) + +* aix: fix un-initialized pointer field in fs handle (Gireesh Punathil) + +* win,build: support building with VS2017 (Refael Ackermann) + +* doc: add instructions for building on Windows (Refael Ackermann) + +* doc: format README (Refael Ackermann) + + +2017.05.31, Version 1.12.0 (Stable), d6ac141ac674657049598c36604f26e031fae917 + +Changes since version 1.11.0: + +* Now working on version 1.11.1 (cjihrig) + +* test: fix tests on OpenBSD (Santiago Gimeno) + +* test: fix -Wformat warning (Santiago Gimeno) + +* win,fs: avoid double freeing uv_fs_event_t.dirw (Vladimir Matveev) + +* unix: remove unused code in `uv__io_start` (Fedor Indutny) + +* signal: add uv_signal_start_oneshot method (Santiago Gimeno) + +* unix: factor out reusable POSIX hrtime impl (Brad King) + +* unix,win: add uv_os_{get,set,unset}env() (cjihrig) + +* win: add uv__convert_utf8_to_utf16() (cjihrig) + +* docs: improve UV_ENOBUFS scenario documentation (cjihrig) + +* unix: return UV_EINVAL for NULL env name (jBarz) + +* unix: filter getifaddrs results consistently (Brad King) + +* unix: factor out getifaddrs result filter (Brad King) + +* unix: factor out reusable BSD ifaddrs impl (Brad King) + +* unix: use union to follow strict aliasing rules (jBarz) + +* unix: simplify async watcher dispatch logic (Ben Noordhuis) + +* samples: update timer callback prototype (Ben Noordhuis) + +* unix: make loops and watchers usable after fork() (Jason Madden) + +* win: free uv__loops once empty (cjihrig) + +* tools: add make_dist_html.py script (Ben Noordhuis) + +* win,sunos: stop handle on uv_fs_event_start() err (cjihrig) + +* unix,windows: refactor request init logic (Ben Noordhuis) + +* win: fix memory leak inside uv__pipe_getname (A. Hauptmann) + +* fsevent: support for files without short name (Bartosz Sosnowski) + +* doc: fix multiple doc typos (Jamie Davis) + +* test,osx: fix flaky kill test (Santiago Gimeno) + +* unix: inline uv_pipe_bind() err_bind goto target (cjihrig) + +* unix,test: deadstore fixes (Rasmus Christian Pedersen) + +* win: fix memory leak inside uv_fs_access() (A. Hauptmann) + +* doc: fix docs/src/fs.rst build warning (Daniel Bevenius) + +* doc: minor grammar fix in Installation section (Daniel Bevenius) + +* doc: suggestions for design page (Daniel Bevenius) + +* doc: libuv does not touch uv_loop_t.data (Ben Noordhuis) + +* github: add ISSUE_TEMPLATE.md (Ben Noordhuis) + +* doc: add link to libuv/help to README (Ben Noordhuis) + +* udp: fix fast path in uv_udp_send() on unix (Fedor Indutny) + +* test: add test for uv_udp_send() fix (Trevor Norris) + +* doc: fix documentation for uv_handle_t.type (Daniel Kahn Gillmor) + +* zos: use proper prototype for epoll_init() (Ben Noordhuis) + +* doc: rename docs to "libuv documentation" (Saúl Ibarra Corretgé) + +* doc: update copyright years (Saúl Ibarra Corretgé) + +* doc: move TOC to a dedicated document (Saúl Ibarra Corretgé) + +* doc: move documentation section up (Saúl Ibarra Corretgé) + +* doc: move "upgrading" to a standalone document (Saúl Ibarra Corretgé) + +* doc: add initial version of the User Guide (Saúl Ibarra Corretgé) + +* doc: removed unused file (Saúl Ibarra Corretgé) + +* doc: update guide/about and mention new maintainership (Saúl Ibarra Corretgé) + +* doc: remove licensing note from guide/about (Saúl Ibarra Corretgé) + +* doc: add warning note to user guide (Saúl Ibarra Corretgé) + +* doc: change license to CC BY 4.0 (Saúl Ibarra Corretgé) + +* doc: remove ubvook reference from README (Saúl Ibarra Corretgé) + +* doc: add code samples from uvbook (unadapted) (Saúl Ibarra Corretgé) + +* doc: update supported linux/glibc baseline (Ben Noordhuis) + +* win: avoid leaking pipe handles to child processes (Jameson Nash) + +* win,test: support stdout output larger than 1kb (Bartosz Sosnowski) + +* win: remove __declspec(inline) from atomic op (Keane) + +* test: fix VC++ compilation warning (Rasmus Christian Pedersen) + +* build: add -Wstrict-prototypes (Jameson Nash) + +* zos: implement uv__io_fork, skip fs event tests (jBarz) + +* unix: do not close udp sockets on bind error (Marc Schlaich) + +* unix: remove FSEventStreamFlushSync() call (cjihrig) + +* build,openbsd: remove kvm-related code (James McCoy) + +* test: fix flaky tcp-write-queue-order (Santiago Gimeno) + +* unix,win: add uv_os_gethostname() (cjihrig) + +* zos: increase timeout for tcp_writealot (jBarz) + +* zos: do not inline OOB data by default (jBarz) + +* test: fix -Wstrict-prototypes compiler warnings (Ben Noordhuis) + +* unix: factor out reusable no-proctitle impl (Brad King) + +* test: factor out fsevents skip explanation (Brad King) + +* test: skip fork fsevent cases when lacking support (Brad King) + +* unix: factor out reusable no-fsevents impl (Brad King) + +* unix: factor out reusable sysinfo memory lookup (Brad King) + +* unix: factor out reusable sysinfo loadavg impl (Brad King) + +* unix: factor out reusable procfs exepath impl (Brad King) + +* unix: add a uv__io_poll impl using POSIX poll(2) (Brad King) + +* cygwin: implement support for cygwin and msys2 (Brad King) + +* cygwin: recognize EOF on named pipe closure (Brad King) + +* cygwin: fix uv_pipe_connect report of ENOTSOCK (Brad King) + +* cygwin: disable non-functional ipc handle send (Brad King) + +* test: skip self-connecting tests on cygwin (Brad King) + +* doc: mark uv_loop_fork() as experimental (cjihrig) + +* doc: add bzoz to maintainers (Bartosz Sosnowski) + +* doc: fix memory leak in tcp-echo-server example (Bernardo Ramos) + +* win: make uv__get_osfhandle() public (Juan Cruz Viotti) + +* doc: use valid pipe name in pipe-echo-server (Bernardo Ramos) + + +2017.02.02, Version 1.11.0 (Stable), 7452ef4e06a4f99ee26b694c65476401534f2725 + +Changes since version 1.10.2: + +* Now working on version 1.10.3 (cjihrig) + +* win: added fcntl.h to uv-win.h (Michele Caini) + +* unix: move function call out of assert (jBarz) + +* fs: cleanup uv__fs_scandir (Santiago Gimeno) + +* fs: fix crash in uv_fs_scandir_next (muflub) + +* win,signal: fix potential deadlock (Bartosz Sosnowski) + +* unix: use async-signal safe functions between fork and exec (jBarz) + +* sunos: fix SUNOS_NO_IFADDRS build (Ben Noordhuis) + +* zos: make platform functional (John Barboza) + +* doc: add repitition qualifier to version regexs (Daniel Bevenius) + +* zos: use gyp OS label "os390" on z/OS (John Barboza) + +* aix: enable uv_get/set_process_title (Howard Hellyer) + +* zos: use built-in proctitle implementation (John Barboza) + +* Revert "darwin: use clock_gettime in macOS 10.12" (Chris Araman) + +* win,test: don't write uninitialized buffer to tty (Bert Belder) + +* win: define ERROR_ELEVATION_REQUIRED for MinGW (Richard Lau) + +* aix: re-enable fs watch facility (Gireesh Punathil) + + +2017.01.10, Version 1.10.2 (Stable), cb9f579a454b8db592030ffa274ae58df78dbe20 + +Changes since version 1.10.1: + +* Now working on version 1.10.2 (cjihrig) + +* darwin: fix fsync and fdatasync (Joran Dirk Greef) + +* Revert "Revert "win,tty: add support for ANSI codes in win10 v1511"" + (Santiago Gimeno) + +* win,tty: fix MultiByteToWideChar output buffer (Santiago Gimeno) + +* win: remove dead code related to BACKUP_SEMANTICS (Sam Roberts) + +* win: fix comment in quote_cmd_arg (Eric Sciple) + +* darwin: use clock_gettime in macOS 10.12 (Saúl Ibarra Corretgé) + +* win, tty: fix crash on restarting with pending data (Nicholas Vavilov) + +* fs: fix uv__to_stat on BSD platforms (Santiago Gimeno) + +* win: map ERROR_ELEVATION_REQUIRED to UV_EACCES (Richard Lau) + +* win: fix free() on bad input in uv_getaddrinfo() (Ben Noordhuis) + + +2016.11.17, Version 1.10.1 (Stable), 2e49e332bdede6db7cf17fa784a902e8386d5d86 + +Changes since version 1.10.0: + +* Now working on version 1.10.1 (cjihrig) + +* win: fix anonymous union syntax (Brad King) + +* unix: use uv__is_closing everywhere (Santiago Gimeno) + +* win: add missing break statement (cjihrig) + +* doc: fix wrong man page link for uv_fs_lstat() (Michele Caini) + +* win, tty: handle empty buffer in uv_tty_write_bufs (Hitesh Kanwathirtha) + +* doc: add cjihrig alternative GPG ID (cjihrig) + +* Revert "win,tty: add support for ANSI codes in win10 v1511" (Ben Noordhuis) + + +2016.10.25, Version 1.10.0 (Stable), c8a373c729b4c9392e0e14fc53cd6b67b3051ab9 + +Changes since version 1.9.1: + +* Now working on version 1.9.2 (Saúl Ibarra Corretgé) + +* doc: add cjihrig GPG ID (cjihrig) + +* win,build: fix compilation on old Windows / MSVC (Saúl Ibarra Corretgé) + +* darwin: fix setting fd to non-blocking in select(() trick (Saúl Ibarra + Corretgé) + +* unix: allow nesting of kqueue fds in uv_poll_start (Ben Noordhuis) + +* doc: fix generation the first time livehtml runs (Saúl Ibarra Corretgé) + +* test: fix test_close_accept flakiness on Centos5 (Santiago Gimeno) + +* license: libuv is no longer a Node project (Saúl Ibarra Corretgé) + +* license: add license text we've been using for a while (Saúl Ibarra Corretgé) + +* doc: add licensing information to README (Saúl Ibarra Corretgé) + +* win,pipe: fixed formatting, DWORD is long unsigned (Miodrag Milanovic) + +* win: support sub-second precision in uv_fs_futimes() (Jason Ginchereau) + +* unix: ignore EINPROGRESS in uv__close (Saúl Ibarra Corretgé) + +* doc: add Imran Iqbal (iWuzHere) to maintainers (Imran Iqbal) + +* doc: update docs with AIX related information (Imran Iqbal) + +* test: silence build warnings (Kári Tristan Helgason) + +* doc: add iWuzHere GPG ID (Imran Iqbal) + +* linux-core: fix uv_get_total/free_memory on uclibc (Nicolas Cavallari) + +* build: fix build on DragonFly (Michael Neumann) + +* unix: correctly detect named pipes on DragonFly (Michael Neumann) + +* test: make tap output the default (Ben Noordhuis) + +* test: don't dump output for skipped tests (Ben Noordhuis) + +* test: improve formatting of diagnostic messages (Ben Noordhuis) + +* test: remove unused RETURN_TODO macro (Ben Noordhuis) + +* doc: fix stream typos (Pierre-Marie de Rodat) + +* doc: update coding style link (Imran Iqbal) + +* unix,fs: use uint64_t instead of unsigned long (Imran Iqbal) + +* build: check for warnings for -fvisibility=hidden (Imran Iqbal) + +* unix: remove unneeded TODO note (Saúl Ibarra Corretgé) + +* test: skip tty_pty test if pty is not available (Luca Bruno) + +* sunos: set phys_addr of interface_address using ARP (Brian Maher) + +* doc: clarify callbacks won't be called in error case (Saúl Ibarra Corretgé) + +* unix: don't convert stat buffer when syscall fails (Ben Noordhuis) + +* win: compare entire filename in watch events (cjihrig) + +* doc: add a note on safe reuse of uv_write_t (neevek) + +* linux: fix potential event loop stall (Ben Noordhuis) + +* unix,win: make uv_get_process_title() stricter (cjihrig) + +* test: close server before initiating new connection (John Barboza) + +* test: account for multiple handles in one ipc read (John Barboza) + +* unix: fix errno and retval conflict (liuxiaobo) + +* doc: add missing entry in uv_fs_type enum (Michele Caini) + +* unix: preserve loop->data across loop init/done (Ben Noordhuis) + +* win: return UV_EINVAL on bad uv_tty_mode mode arg (Ben Noordhuis) + +* win: simplify memory copy logic in fs.c (Ben Noordhuis) + +* win: fix compilation on mingw (Bartosz Sosnowski) + +* win: ensure 32-bit printf precision (Matej Knopp) + +* darwin: handle EINTR in /dev/tty workaround (Ben Noordhuis) + +* test: fix OOB buffer access (Saúl Ibarra Corretgé) + +* test: don't close CRT fd handed off to uv_pipe_t (Saúl Ibarra Corretgé) + +* test: fix android build error. (sunjin.lee) + +* win: evaluate timers when system wakes up (Bartosz Sosnowski) + +* doc: add supported platforms description (Saúl Ibarra Corretgé) + +* win: fix lstat reparse point without link data (Jason Ginchereau) + +* unix,win: make on_alloc_cb failures more resilient (Saúl Ibarra Corretgé) + +* zos: add support for new platform (John Barboza) + +* test: make tcp_close_while_connecting more resilient (Saúl Ibarra Corretgé) + +* build: use '${prefix}' for pkg-config 'exec_prefix' (Matt Clarkson) + +* build: GNU/kFreeBSD support (Jeffrey Clark) + +* zos: use PLO instruction for atomic operations (John Barboza) + +* zos: use pthread helper functions (John Barboza) + +* zos: implement uv__fs_futime (John Barboza) + +* unix: expand range of values for usleep (John Barboza) + +* zos: track unbound handles and bind before listen (John Barboza) + +* test: improve tap output on test failures (Santiago Gimeno) + +* test: refactor fs_event_close_in_callback (Julien Gilli) + +* zos: implement uv__io_check_fd (John Barboza) + +* unix: unneccessary use const qualifier in container_of (John Barboza) + +* win,tty: add support for ANSI codes in win10 v1511 (Imran Iqbal) + +* doc: add santigimeno to maintainers (Santiago Gimeno) + +* win: fix typo in type name (Saúl Ibarra Corretgé) + +* unix: always define pthread barrier fallback pad (Saúl Ibarra Corretgé) + +* test: use RETURN_SKIP in spawn_setuid_setgid test (Santiago Gimeno) + +* win: add disk read/write count to uv_getrusage (Imran Iqbal) + +* doc: document uv_fs_realpath caveats (Saúl Ibarra Corretgé) + +* test: improve spawn_setuid_setgid test (Santiago Gimeno) + +* test: fix building pty test on Android (Saúl Ibarra Corretgé) + +* doc: uv_buf_t members are not readonly (Saúl Ibarra Corretgé) + +* doc: improve documentation on uv_alloc_cb (Saúl Ibarra Corretgé) + +* fs: fix uv_fs_fstat on platforms using musl libc (Santiago Gimeno) + +* doc: update supported fields for uv_rusage_t (Imran Iqbal) + +* test: fix test-tcp-writealot flakiness on arm (Santiago Gimeno) + +* test: fix fs_event_watch_dir flakiness on arm (Santiago Gimeno) + +* unix: don't use alphasort in uv_fs_scandir() (Ben Noordhuis) + +* doc: fix confusing doc of uv_tcp_nodelay (Bart Robinson) + +* build,osx: fix warnings on tests compilation with gyp (Santiago Gimeno) + +* doc: add ABI tracker link to README (Saúl Ibarra Corretgé) + +* win,tty: fix uv_tty_set_mode race conditions (Bartosz Sosnowski) + +* test: fix fs_fstat on Android (Vit Gottwald) + +* win, test: fix fs_event_watch_dir_recursive (Bartosz Sosnowski) + +* doc: add description of uv_handle_type (Vit Gottwald) + +* build: use -pthreads for tests with autotools (Julien Gilli) + +* win: fix leaky fs request buffer (Jason Ginchereau) + +* doc: note buffer lifetime requirements in uv_write (Vladimír Čunát) + +* doc: add reference to uv_update_time on uv_timer_start (Alex Hultman) + +* win: fix winapi function pointer typedef syntax (Brad King) + +* test: fix tcp_close_while_connecting CI failures (Ben Noordhuis) + +* test: make threadpool_cancel_single deterministic (Ben Noordhuis) + +* test: make threadpool saturation reliable (Ben Noordhuis) + +* unix: don't malloc in uv_thread_create() (Ben Noordhuis) + +* unix: don't include CoreServices globally on macOS (Brad King) + +* unix,win: add uv_translate_sys_error() public API (Philippe Laferriere) + +* win: remove unused static variables (Ben Noordhuis) + +* win: silence -Wmaybe-uninitialized warning (Ben Noordhuis) + +* signal: replace pthread_once with uv_once (Santiago Gimeno) + +* test: fix sign-compare warning (Will Speak) + +* common: fix unused variable warning (Brad King) + + +2016.05.17, Version 1.9.1 (Stable), d989902ac658b4323a4f4020446e6f4dc449e25c Changes since version 1.9.0: diff --git a/include/libuv/LICENSE b/include/libuv/LICENSE index 4d411670e..28f17339e 100644 --- a/include/libuv/LICENSE +++ b/include/libuv/LICENSE @@ -1,5 +1,29 @@ -libuv is part of the Node project: http://nodejs.org/ -libuv may be distributed alone under Node's license: +libuv is licensed for use as follows: + +==== +Copyright (c) 2015-present libuv project contributors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. +==== + +This license applies to parts of libuv originating from the +https://github.com/joyent/libuv repository: ==== @@ -38,8 +62,8 @@ The externally maintained libraries used by libuv are: - stdint-msvc2008.h (from msinttypes), copyright Alexander Chemeris. Three clause BSD license. - - pthread-fixes.h, pthread-fixes.c, copyright Google Inc. and Sony Mobile - Communications AB. Three clause BSD license. + - pthread-fixes.c, copyright Google Inc. and Sony Mobile Communications AB. + Three clause BSD license. - android-ifaddrs.h, android-ifaddrs.c, copyright Berkeley Software Design Inc, Kenneth MacKay and Emergya (Cloud4all, FP7/2007-2013, grant agreement diff --git a/include/libuv/include/uv.h b/include/libuv/include/uv.h index baa0b2812..2557961ee 100644 --- a/include/libuv/include/uv.h +++ b/include/libuv/include/uv.h @@ -27,6 +27,10 @@ extern "C" { #endif +#if defined(BUILDING_UV_SHARED) && defined(USING_UV_SHARED) +#error "Define either BUILDING_UV_SHARED or USING_UV_SHARED, not both." +#endif + #ifdef _WIN32 /* Windows - set up dll import/export decorators. */ # if defined(BUILDING_UV_SHARED) @@ -45,21 +49,21 @@ extern "C" { # define UV_EXTERN /* nothing */ #endif -#include "uv-errno.h" -#include "uv-version.h" +#include "uv/errno.h" +#include "uv/version.h" #include #include #if defined(_MSC_VER) && _MSC_VER < 1600 -# include "stdint-msvc2008.h" +# include "uv/stdint-msvc2008.h" #else # include #endif #if defined(_WIN32) -# include "uv-win.h" +# include "uv/win.h" #else -# include "uv-unix.h" +# include "uv/unix.h" #endif /* Expand this list if necessary. */ @@ -140,6 +144,10 @@ extern "C" { XX(ENXIO, "no such device or address") \ XX(EMLINK, "too many links") \ XX(EHOSTDOWN, "host is down") \ + XX(EREMOTEIO, "remote I/O error") \ + XX(ENOTTY, "inappropriate ioctl for device") \ + XX(EFTYPE, "inappropriate file type or format") \ + XX(EILSEQ, "illegal byte sequence") \ #define UV_HANDLE_TYPE_MAP(XX) \ XX(ASYNC, async) \ @@ -169,6 +177,7 @@ extern "C" { XX(WORK, work) \ XX(GETADDRINFO, getaddrinfo) \ XX(GETNAMEINFO, getnameinfo) \ + XX(RANDOM, random) \ typedef enum { #define XX(code, _) UV_ ## code = UV__ ## code, @@ -199,6 +208,7 @@ typedef enum { /* Handle types. */ typedef struct uv_loop_s uv_loop_t; typedef struct uv_handle_s uv_handle_t; +typedef struct uv_dir_s uv_dir_t; typedef struct uv_stream_s uv_stream_t; typedef struct uv_tcp_s uv_tcp_t; typedef struct uv_udp_s uv_udp_t; @@ -225,15 +235,20 @@ typedef struct uv_connect_s uv_connect_t; typedef struct uv_udp_send_s uv_udp_send_t; typedef struct uv_fs_s uv_fs_t; typedef struct uv_work_s uv_work_t; +typedef struct uv_random_s uv_random_t; /* None of the above. */ +typedef struct uv_env_item_s uv_env_item_t; typedef struct uv_cpu_info_s uv_cpu_info_t; typedef struct uv_interface_address_s uv_interface_address_t; typedef struct uv_dirent_s uv_dirent_t; typedef struct uv_passwd_s uv_passwd_t; +typedef struct uv_utsname_s uv_utsname_t; +typedef struct uv_statfs_s uv_statfs_t; typedef enum { - UV_LOOP_BLOCK_SIGNAL + UV_LOOP_BLOCK_SIGNAL = 0, + UV_METRICS_IDLE_TIME } uv_loop_option; typedef enum { @@ -251,6 +266,8 @@ typedef void* (*uv_realloc_func)(void* ptr, size_t size); typedef void* (*uv_calloc_func)(size_t count, size_t size); typedef void (*uv_free_func)(void* ptr); +UV_EXTERN void uv_library_shutdown(void); + UV_EXTERN int uv_replace_allocator(uv_malloc_func malloc_func, uv_realloc_func realloc_func, uv_calloc_func calloc_func, @@ -274,6 +291,7 @@ UV_EXTERN void uv_loop_delete(uv_loop_t*); UV_EXTERN size_t uv_loop_size(void); UV_EXTERN int uv_loop_alive(const uv_loop_t* loop); UV_EXTERN int uv_loop_configure(uv_loop_t* loop, uv_loop_option option, ...); +UV_EXTERN int uv_loop_fork(uv_loop_t* loop); UV_EXTERN int uv_run(uv_loop_t*, uv_run_mode mode); UV_EXTERN void uv_stop(uv_loop_t*); @@ -317,6 +335,10 @@ typedef void (*uv_getnameinfo_cb)(uv_getnameinfo_t* req, int status, const char* hostname, const char* service); +typedef void (*uv_random_cb)(uv_random_t* req, + int status, + void* buf, + size_t buflen); typedef struct { long tv_sec; @@ -363,8 +385,13 @@ typedef enum { } uv_membership; +UV_EXTERN int uv_translate_sys_error(int sys_errno); + UV_EXTERN const char* uv_strerror(int err); +UV_EXTERN char* uv_strerror_r(int err, char* buf, size_t buflen); + UV_EXTERN const char* uv_err_name(int err); +UV_EXTERN char* uv_err_name_r(int err, char* buf, size_t buflen); #define UV_REQ_FIELDS \ @@ -373,8 +400,7 @@ UV_EXTERN const char* uv_err_name(int err); /* read-only */ \ uv_req_type type; \ /* private */ \ - void* active_queue[2]; \ - void* reserved[4]; \ + void* reserved[6]; \ UV_REQ_PRIVATE_FIELDS \ /* Abstract base class of all requests. */ @@ -420,7 +446,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); @@ -460,6 +496,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); @@ -487,7 +525,7 @@ UV_EXTERN int uv_try_write(uv_stream_t* handle, struct uv_write_s { UV_REQ_FIELDS uv_write_cb cb; - uv_stream_t* send_handle; + uv_stream_t* send_handle; /* TODO: make private and unix-only in v2.x. */ uv_stream_t* handle; UV_WRITE_PRIVATE_FIELDS }; @@ -535,6 +573,7 @@ UV_EXTERN int uv_tcp_getsockname(const uv_tcp_t* handle, UV_EXTERN int uv_tcp_getpeername(const uv_tcp_t* handle, struct sockaddr* name, int* namelen); +UV_EXTERN int uv_tcp_close_reset(uv_tcp_t* handle, uv_close_cb close_cb); UV_EXTERN int uv_tcp_connect(uv_connect_t* req, uv_tcp_t* handle, const struct sockaddr* addr, @@ -569,7 +608,23 @@ enum uv_udp_flags { * (provided they all set the flag) but only the last one to bind will receive * any traffic, in effect "stealing" the port from the previous listener. */ - UV_UDP_REUSEADDR = 4 + UV_UDP_REUSEADDR = 4, + /* + * Indicates that the message was received by recvmmsg, so the buffer provided + * must not be freed by the recv_cb callback. + */ + UV_UDP_MMSG_CHUNK = 8, + /* + * Indicates that the buffer provided has been fully utilized by recvmmsg and + * that it should now be freed by the recv_cb callback. When this flag is set + * in uv_udp_recv_cb, nread will always be 0 and addr will always be NULL. + */ + UV_UDP_MMSG_FREE = 16, + + /* + * Indicates that recvmmsg should be used, if available. + */ + UV_UDP_RECVMMSG = 256 }; typedef void (*uv_udp_send_cb)(uv_udp_send_t* req, int status); @@ -609,7 +664,11 @@ UV_EXTERN int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock); UV_EXTERN int uv_udp_bind(uv_udp_t* handle, const struct sockaddr* addr, unsigned int flags); +UV_EXTERN int uv_udp_connect(uv_udp_t* handle, const struct sockaddr* addr); +UV_EXTERN int uv_udp_getpeername(const uv_udp_t* handle, + struct sockaddr* name, + int* namelen); UV_EXTERN int uv_udp_getsockname(const uv_udp_t* handle, struct sockaddr* name, int* namelen); @@ -617,6 +676,11 @@ UV_EXTERN int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr, const char* interface_addr, uv_membership membership); +UV_EXTERN int uv_udp_set_source_membership(uv_udp_t* handle, + const char* multicast_addr, + const char* interface_addr, + const char* source_addr, + uv_membership membership); UV_EXTERN int uv_udp_set_multicast_loop(uv_udp_t* handle, int on); UV_EXTERN int uv_udp_set_multicast_ttl(uv_udp_t* handle, int ttl); UV_EXTERN int uv_udp_set_multicast_interface(uv_udp_t* handle, @@ -636,7 +700,10 @@ UV_EXTERN int uv_udp_try_send(uv_udp_t* handle, 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_using_recvmmsg(const uv_udp_t* handle); 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); /* @@ -659,10 +726,25 @@ typedef enum { UV_TTY_MODE_IO } uv_tty_mode_t; +typedef enum { + /* + * The console supports handling of virtual terminal sequences + * (Windows10 new console, ConEmu) + */ + UV_TTY_SUPPORTED, + /* The console cannot process the virtual terminal sequence. (Legacy + * console) + */ + UV_TTY_UNSUPPORTED +} uv_tty_vtermstate_t; + + UV_EXTERN int uv_tty_init(uv_loop_t*, uv_tty_t*, uv_file fd, int readable); UV_EXTERN int uv_tty_set_mode(uv_tty_t*, uv_tty_mode_t mode); UV_EXTERN int uv_tty_reset_mode(void); UV_EXTERN int uv_tty_get_winsize(uv_tty_t*, int* width, int* height); +UV_EXTERN void uv_tty_set_vterm_state(uv_tty_vtermstate_t state); +UV_EXTERN int uv_tty_get_vterm_state(uv_tty_vtermstate_t* state); #ifdef __cplusplus extern "C++" { @@ -705,6 +787,7 @@ UV_EXTERN int uv_pipe_getpeername(const uv_pipe_t* handle, UV_EXTERN void uv_pipe_pending_instances(uv_pipe_t* handle, int count); UV_EXTERN int uv_pipe_pending_count(uv_pipe_t* handle); UV_EXTERN uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle); +UV_EXTERN int uv_pipe_chmod(uv_pipe_t* handle, int flags); struct uv_poll_s { @@ -716,7 +799,8 @@ struct uv_poll_s { enum uv_poll_event { UV_READABLE = 1, UV_WRITABLE = 2, - UV_DISCONNECT = 4 + UV_DISCONNECT = 4, + UV_PRIORITIZED = 8 }; UV_EXTERN int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd); @@ -787,6 +871,7 @@ UV_EXTERN int uv_timer_stop(uv_timer_t* handle); UV_EXTERN int uv_timer_again(uv_timer_t* handle); UV_EXTERN void uv_timer_set_repeat(uv_timer_t* handle, uint64_t repeat); UV_EXTERN uint64_t uv_timer_get_repeat(const uv_timer_t* handle); +UV_EXTERN uint64_t uv_timer_get_due_in(const uv_timer_t* handle); /* @@ -845,7 +930,13 @@ typedef enum { * flags may be specified to create a duplex data stream. */ UV_READABLE_PIPE = 0x10, - UV_WRITABLE_PIPE = 0x20 + UV_WRITABLE_PIPE = 0x20, + + /* + * Open the child pipe handle in overlapped mode on Windows. + * On Unix it is silently ignored. + */ + UV_OVERLAPPED_PIPE = 0x40 } uv_stdio_flags; typedef struct uv_stdio_container_s { @@ -932,12 +1023,23 @@ enum uv_process_flags { * the child's process handle. */ UV_PROCESS_DETACHED = (1 << 3), + /* + * Hide the subprocess window that would normally be created. This option is + * only meaningful on Windows systems. On Unix it is silently ignored. + */ + UV_PROCESS_WINDOWS_HIDE = (1 << 4), /* * Hide the subprocess console window that would normally be created. This * option is only meaningful on Windows systems. On Unix it is silently * ignored. */ - UV_PROCESS_WINDOWS_HIDE = (1 << 4) + UV_PROCESS_WINDOWS_HIDE_CONSOLE = (1 << 5), + /* + * Hide the subprocess GUI window that would normally be created. This + * option is only meaningful on Windows systems. On Unix it is silently + * ignored. + */ + UV_PROCESS_WINDOWS_HIDE_GUI = (1 << 6) }; /* @@ -955,6 +1057,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*); /* @@ -976,16 +1079,18 @@ UV_EXTERN int uv_queue_work(uv_loop_t* loop, UV_EXTERN int uv_cancel(uv_req_t* req); +struct uv_cpu_times_s { + uint64_t user; /* milliseconds */ + uint64_t nice; /* milliseconds */ + uint64_t sys; /* milliseconds */ + uint64_t idle; /* milliseconds */ + uint64_t irq; /* milliseconds */ +}; + struct uv_cpu_info_s { char* model; int speed; - struct uv_cpu_times_s { - uint64_t user; - uint64_t nice; - uint64_t sys; - uint64_t idle; - uint64_t irq; - } cpu_times; + struct uv_cpu_times_s cpu_times; }; struct uv_interface_address_s { @@ -1010,6 +1115,27 @@ struct uv_passwd_s { char* homedir; }; +struct uv_utsname_s { + char sysname[256]; + char release[256]; + char version[256]; + char machine[256]; + /* This struct does not contain the nodename and domainname fields present in + the utsname type. domainname is a GNU extension. Both fields are referred + to as meaningless in the docs. */ +}; + +struct uv_statfs_s { + uint64_t f_type; + uint64_t f_bsize; + uint64_t f_blocks; + uint64_t f_bfree; + uint64_t f_bavail; + uint64_t f_files; + uint64_t f_ffree; + uint64_t f_spare[4]; +}; + typedef enum { UV_DIRENT_UNKNOWN, UV_DIRENT_FILE, @@ -1031,12 +1157,19 @@ UV_EXTERN int uv_get_process_title(char* buffer, size_t size); UV_EXTERN int uv_set_process_title(const char* title); UV_EXTERN int uv_resident_set_memory(size_t* rss); UV_EXTERN int uv_uptime(double* uptime); +UV_EXTERN uv_os_fd_t uv_get_osfhandle(int fd); +UV_EXTERN int uv_open_osfhandle(uv_os_fd_t os_fd); typedef struct { long tv_sec; long tv_usec; } uv_timeval_t; +typedef struct { + int64_t tv_sec; + int32_t tv_usec; +} uv_timeval64_t; + typedef struct { uv_timeval_t ru_utime; /* user CPU time used */ uv_timeval_t ru_stime; /* system CPU time used */ @@ -1062,6 +1195,28 @@ UV_EXTERN int uv_os_homedir(char* buffer, size_t* size); UV_EXTERN int uv_os_tmpdir(char* buffer, size_t* size); UV_EXTERN int uv_os_get_passwd(uv_passwd_t* pwd); UV_EXTERN void uv_os_free_passwd(uv_passwd_t* pwd); +UV_EXTERN uv_pid_t uv_os_getpid(void); +UV_EXTERN uv_pid_t uv_os_getppid(void); + +#if defined(__PASE__) +/* On IBM i PASE, the highest process priority is -10 */ +# define UV_PRIORITY_LOW 39 /* RUNPTY(99) */ +# define UV_PRIORITY_BELOW_NORMAL 15 /* RUNPTY(50) */ +# define UV_PRIORITY_NORMAL 0 /* RUNPTY(20) */ +# define UV_PRIORITY_ABOVE_NORMAL -4 /* RUNTY(12) */ +# define UV_PRIORITY_HIGH -7 /* RUNPTY(6) */ +# define UV_PRIORITY_HIGHEST -10 /* RUNPTY(1) */ +#else +# define UV_PRIORITY_LOW 19 +# define UV_PRIORITY_BELOW_NORMAL 10 +# define UV_PRIORITY_NORMAL 0 +# define UV_PRIORITY_ABOVE_NORMAL -7 +# define UV_PRIORITY_HIGH -14 +# define UV_PRIORITY_HIGHEST -20 +#endif + +UV_EXTERN int uv_os_getpriority(uv_pid_t pid, int* priority); +UV_EXTERN int uv_os_setpriority(uv_pid_t pid, int priority); UV_EXTERN int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count); UV_EXTERN void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count); @@ -1071,6 +1226,33 @@ UV_EXTERN int uv_interface_addresses(uv_interface_address_t** addresses, UV_EXTERN void uv_free_interface_addresses(uv_interface_address_t* addresses, int count); +struct uv_env_item_s { + char* name; + char* value; +}; + +UV_EXTERN int uv_os_environ(uv_env_item_t** envitems, int* count); +UV_EXTERN void uv_os_free_environ(uv_env_item_t* envitems, int count); +UV_EXTERN int uv_os_getenv(const char* name, char* buffer, size_t* size); +UV_EXTERN int uv_os_setenv(const char* name, const char* value); +UV_EXTERN int uv_os_unsetenv(const char* name); + +#ifdef MAXHOSTNAMELEN +# define UV_MAXHOSTNAMESIZE (MAXHOSTNAMELEN + 1) +#else + /* + Fallback for the maximum hostname size, including the null terminator. The + Windows gethostname() documentation states that 256 bytes will always be + large enough to hold the null-terminated hostname. + */ +# define UV_MAXHOSTNAMESIZE 256 +#endif + +UV_EXTERN int uv_os_gethostname(char* buffer, size_t* size); + +UV_EXTERN int uv_os_uname(uv_utsname_t* buffer); + +UV_EXTERN uint64_t uv_metrics_idle_time(uv_loop_t* loop); typedef enum { UV_FS_UNKNOWN = -1, @@ -1102,9 +1284,24 @@ typedef enum { UV_FS_READLINK, UV_FS_CHOWN, UV_FS_FCHOWN, - UV_FS_REALPATH + UV_FS_REALPATH, + UV_FS_COPYFILE, + UV_FS_LCHOWN, + UV_FS_OPENDIR, + UV_FS_READDIR, + UV_FS_CLOSEDIR, + UV_FS_STATFS, + UV_FS_MKSTEMP, + UV_FS_LUTIME } uv_fs_type; +struct uv_dir_s { + uv_dirent_t* dirents; + size_t nentries; + void* reserved[4]; + UV_DIR_PRIVATE_FIELDS +}; + /* uv_fs_t is a subclass of uv_req_t. */ struct uv_fs_s { UV_REQ_FIELDS @@ -1118,6 +1315,13 @@ 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 int uv_fs_get_system_error(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, @@ -1147,6 +1351,30 @@ UV_EXTERN int uv_fs_write(uv_loop_t* loop, unsigned int nbufs, int64_t offset, uv_fs_cb cb); +/* + * This flag can be used with uv_fs_copyfile() to return an error if the + * destination already exists. + */ +#define UV_FS_COPYFILE_EXCL 0x0001 + +/* + * This flag can be used with uv_fs_copyfile() to attempt to create a reflink. + * If copy-on-write is not supported, a fallback copy mechanism is used. + */ +#define UV_FS_COPYFILE_FICLONE 0x0002 + +/* + * This flag can be used with uv_fs_copyfile() to attempt to create a reflink. + * If copy-on-write is not supported, an error is returned. + */ +#define UV_FS_COPYFILE_FICLONE_FORCE 0x0004 + +UV_EXTERN int uv_fs_copyfile(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + const char* new_path, + int flags, + uv_fs_cb cb); UV_EXTERN int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, @@ -1156,6 +1384,10 @@ UV_EXTERN int uv_fs_mkdtemp(uv_loop_t* loop, uv_fs_t* req, const char* tpl, uv_fs_cb cb); +UV_EXTERN int uv_fs_mkstemp(uv_loop_t* loop, + uv_fs_t* req, + const char* tpl, + uv_fs_cb cb); UV_EXTERN int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, @@ -1167,6 +1399,18 @@ UV_EXTERN int uv_fs_scandir(uv_loop_t* loop, uv_fs_cb cb); UV_EXTERN int uv_fs_scandir_next(uv_fs_t* req, uv_dirent_t* ent); +UV_EXTERN int uv_fs_opendir(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + uv_fs_cb cb); +UV_EXTERN int uv_fs_readdir(uv_loop_t* loop, + uv_fs_t* req, + uv_dir_t* dir, + uv_fs_cb cb); +UV_EXTERN int uv_fs_closedir(uv_loop_t* loop, + uv_fs_t* req, + uv_dir_t* dir, + uv_fs_cb cb); UV_EXTERN int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, @@ -1222,6 +1466,12 @@ UV_EXTERN int uv_fs_futime(uv_loop_t* loop, double atime, double mtime, uv_fs_cb cb); +UV_EXTERN int uv_fs_lutime(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + double atime, + double mtime, + uv_fs_cb cb); UV_EXTERN int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, @@ -1275,6 +1525,16 @@ UV_EXTERN int uv_fs_fchown(uv_loop_t* loop, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb); +UV_EXTERN int uv_fs_lchown(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + uv_uid_t uid, + uv_gid_t gid, + uv_fs_cb cb); +UV_EXTERN int uv_fs_statfs(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + uv_fs_cb cb); enum uv_fs_event { @@ -1322,6 +1582,9 @@ UV_EXTERN int uv_signal_init(uv_loop_t* loop, uv_signal_t* handle); UV_EXTERN int uv_signal_start(uv_signal_t* handle, uv_signal_cb signal_cb, int signum); +UV_EXTERN int uv_signal_start_oneshot(uv_signal_t* handle, + uv_signal_cb signal_cb, + int signum); UV_EXTERN int uv_signal_stop(uv_signal_t* handle); UV_EXTERN void uv_loadavg(double avg[3]); @@ -1377,6 +1640,41 @@ UV_EXTERN int uv_ip6_name(const struct sockaddr_in6* src, char* dst, size_t size UV_EXTERN int uv_inet_ntop(int af, const void* src, char* dst, size_t size); UV_EXTERN int uv_inet_pton(int af, const char* src, void* dst); + +struct uv_random_s { + UV_REQ_FIELDS + /* read-only */ + uv_loop_t* loop; + /* private */ + int status; + void* buf; + size_t buflen; + uv_random_cb cb; + struct uv__work work_req; +}; + +UV_EXTERN int uv_random(uv_loop_t* loop, + uv_random_t* req, + void *buf, + size_t buflen, + unsigned flags, /* For future extension; must be 0. */ + uv_random_cb cb); + +#if defined(IF_NAMESIZE) +# define UV_IF_NAMESIZE (IF_NAMESIZE + 1) +#elif defined(IFNAMSIZ) +# define UV_IF_NAMESIZE (IFNAMSIZ + 1) +#else +# define UV_IF_NAMESIZE (16 + 1) +#endif + +UV_EXTERN int uv_if_indextoname(unsigned int ifindex, + char* buffer, + size_t* size); +UV_EXTERN int uv_if_indextoiid(unsigned int ifindex, + char* buffer, + size_t* size); + UV_EXTERN int uv_exepath(char* buffer, size_t* size); UV_EXTERN int uv_cwd(char* buffer, size_t* size); @@ -1385,8 +1683,10 @@ UV_EXTERN int uv_chdir(const char* dir); UV_EXTERN uint64_t uv_get_free_memory(void); UV_EXTERN uint64_t uv_get_total_memory(void); +UV_EXTERN uint64_t uv_get_constrained_memory(void); UV_EXTERN uint64_t uv_hrtime(void); +UV_EXTERN void uv_sleep(unsigned int msec); UV_EXTERN void uv_disable_stdio_inheritance(void); @@ -1396,6 +1696,7 @@ UV_EXTERN int uv_dlsym(uv_lib_t* lib, const char* name, void** ptr); UV_EXTERN const char* uv_dlerror(const uv_lib_t* lib); UV_EXTERN int uv_mutex_init(uv_mutex_t* handle); +UV_EXTERN int uv_mutex_init_recursive(uv_mutex_t* handle); UV_EXTERN void uv_mutex_destroy(uv_mutex_t* handle); UV_EXTERN void uv_mutex_lock(uv_mutex_t* handle); UV_EXTERN int uv_mutex_trylock(uv_mutex_t* handle); @@ -1437,9 +1738,29 @@ UV_EXTERN void uv_key_delete(uv_key_t* key); UV_EXTERN void* uv_key_get(uv_key_t* key); UV_EXTERN void uv_key_set(uv_key_t* key, void* value); +UV_EXTERN int uv_gettimeofday(uv_timeval64_t* tv); + typedef void (*uv_thread_cb)(void* arg); UV_EXTERN int uv_thread_create(uv_thread_t* tid, uv_thread_cb entry, void* arg); + +typedef enum { + UV_THREAD_NO_FLAGS = 0x00, + UV_THREAD_HAS_STACK_SIZE = 0x01 +} uv_thread_create_flags; + +struct uv_thread_options_s { + unsigned int flags; + size_t stack_size; + /* More fields may be added at any time. */ +}; + +typedef struct uv_thread_options_s uv_thread_options_t; + +UV_EXTERN int uv_thread_create_ex(uv_thread_t* tid, + const uv_thread_options_t* params, + uv_thread_cb entry, + void* arg); UV_EXTERN uv_thread_t uv_thread_self(void); UV_EXTERN int uv_thread_join(uv_thread_t *tid); UV_EXTERN int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2); @@ -1462,12 +1783,19 @@ struct uv_loop_s { /* Loop reference counting. */ unsigned int active_handles; void* handle_queue[2]; - void* active_reqs[2]; + union { + void* unused; + unsigned int count; + } active_reqs; + /* Internal storage for future extensions. */ + void* internal_fields; /* Internal flag to signal loop stop. */ unsigned int stop_flag; 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 @@ -1488,6 +1816,7 @@ struct uv_loop_s { #undef UV_SIGNAL_PRIVATE_FIELDS #undef UV_LOOP_PRIVATE_FIELDS #undef UV_LOOP_PRIVATE_PLATFORM_FIELDS +#undef UV__ERR #ifdef __cplusplus } diff --git a/include/libuv/include/uv/aix.h b/include/libuv/include/uv/aix.h new file mode 100644 index 000000000..7dc992fa6 --- /dev/null +++ b/include/libuv/include/uv/aix.h @@ -0,0 +1,32 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef UV_AIX_H +#define UV_AIX_H + +#define UV_PLATFORM_LOOP_FIELDS \ + int fs_fd; \ + +#define UV_PLATFORM_FS_EVENT_FIELDS \ + uv__io_t event_watcher; \ + char *dir_filename; \ + +#endif /* UV_AIX_H */ diff --git a/include/libuv/include/uv/android-ifaddrs.h b/include/libuv/include/uv/android-ifaddrs.h new file mode 100644 index 000000000..9cd19fec1 --- /dev/null +++ b/include/libuv/include/uv/android-ifaddrs.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1995, 1999 + * Berkeley Software Design, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY Berkeley Software Design, Inc. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Berkeley Software Design, Inc. BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * BSDI ifaddrs.h,v 2.5 2000/02/23 14:51:59 dab Exp + */ + +#ifndef _IFADDRS_H_ +#define _IFADDRS_H_ + +struct ifaddrs { + struct ifaddrs *ifa_next; + char *ifa_name; + unsigned int ifa_flags; + struct sockaddr *ifa_addr; + struct sockaddr *ifa_netmask; + struct sockaddr *ifa_dstaddr; + void *ifa_data; +}; + +/* + * This may have been defined in . Note that if is + * to be included it must be included before this header file. + */ +#ifndef ifa_broadaddr +#define ifa_broadaddr ifa_dstaddr /* broadcast address interface */ +#endif + +#include + +__BEGIN_DECLS +extern int getifaddrs(struct ifaddrs **ifap); +extern void freeifaddrs(struct ifaddrs *ifa); +__END_DECLS + +#endif diff --git a/include/libuv/include/uv/bsd.h b/include/libuv/include/uv/bsd.h new file mode 100644 index 000000000..2d72b3d77 --- /dev/null +++ b/include/libuv/include/uv/bsd.h @@ -0,0 +1,34 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef UV_BSD_H +#define UV_BSD_H + +#define UV_PLATFORM_FS_EVENT_FIELDS \ + uv__io_t event_watcher; \ + +#define UV_IO_PRIVATE_PLATFORM_FIELDS \ + int rcount; \ + int wcount; \ + +#define UV_HAVE_KQUEUE 1 + +#endif /* UV_BSD_H */ diff --git a/include/libuv/include/uv/darwin.h b/include/libuv/include/uv/darwin.h new file mode 100644 index 000000000..d22641582 --- /dev/null +++ b/include/libuv/include/uv/darwin.h @@ -0,0 +1,61 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef UV_DARWIN_H +#define UV_DARWIN_H + +#if defined(__APPLE__) && defined(__MACH__) +# include +# include +# include +# include +# define UV_PLATFORM_SEM_T semaphore_t +#endif + +#define UV_IO_PRIVATE_PLATFORM_FIELDS \ + int rcount; \ + int wcount; \ + +#define UV_PLATFORM_LOOP_FIELDS \ + uv_thread_t cf_thread; \ + void* _cf_reserved; \ + void* cf_state; \ + uv_mutex_t cf_mutex; \ + uv_sem_t cf_sem; \ + void* cf_signals[2]; \ + +#define UV_PLATFORM_FS_EVENT_FIELDS \ + uv__io_t event_watcher; \ + char* realpath; \ + int realpath_len; \ + int cf_flags; \ + uv_async_t* cf_cb; \ + void* cf_events[2]; \ + void* cf_member[2]; \ + int cf_error; \ + uv_mutex_t cf_mutex; \ + +#define UV_STREAM_PRIVATE_PLATFORM_FIELDS \ + void* select; \ + +#define UV_HAVE_KQUEUE 1 + +#endif /* UV_DARWIN_H */ diff --git a/include/libuv/include/uv/errno.h b/include/libuv/include/uv/errno.h new file mode 100644 index 000000000..aadce9c14 --- /dev/null +++ b/include/libuv/include/uv/errno.h @@ -0,0 +1,448 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef UV_ERRNO_H_ +#define UV_ERRNO_H_ + +#include +#if EDOM > 0 +# define UV__ERR(x) (-(x)) +#else +# define UV__ERR(x) (x) +#endif + +#define UV__EOF (-4095) +#define UV__UNKNOWN (-4094) + +#define UV__EAI_ADDRFAMILY (-3000) +#define UV__EAI_AGAIN (-3001) +#define UV__EAI_BADFLAGS (-3002) +#define UV__EAI_CANCELED (-3003) +#define UV__EAI_FAIL (-3004) +#define UV__EAI_FAMILY (-3005) +#define UV__EAI_MEMORY (-3006) +#define UV__EAI_NODATA (-3007) +#define UV__EAI_NONAME (-3008) +#define UV__EAI_OVERFLOW (-3009) +#define UV__EAI_SERVICE (-3010) +#define UV__EAI_SOCKTYPE (-3011) +#define UV__EAI_BADHINTS (-3013) +#define UV__EAI_PROTOCOL (-3014) + +/* Only map to the system errno on non-Windows platforms. It's apparently + * a fairly common practice for Windows programmers to redefine errno codes. + */ +#if defined(E2BIG) && !defined(_WIN32) +# define UV__E2BIG UV__ERR(E2BIG) +#else +# define UV__E2BIG (-4093) +#endif + +#if defined(EACCES) && !defined(_WIN32) +# define UV__EACCES UV__ERR(EACCES) +#else +# define UV__EACCES (-4092) +#endif + +#if defined(EADDRINUSE) && !defined(_WIN32) +# define UV__EADDRINUSE UV__ERR(EADDRINUSE) +#else +# define UV__EADDRINUSE (-4091) +#endif + +#if defined(EADDRNOTAVAIL) && !defined(_WIN32) +# define UV__EADDRNOTAVAIL UV__ERR(EADDRNOTAVAIL) +#else +# define UV__EADDRNOTAVAIL (-4090) +#endif + +#if defined(EAFNOSUPPORT) && !defined(_WIN32) +# define UV__EAFNOSUPPORT UV__ERR(EAFNOSUPPORT) +#else +# define UV__EAFNOSUPPORT (-4089) +#endif + +#if defined(EAGAIN) && !defined(_WIN32) +# define UV__EAGAIN UV__ERR(EAGAIN) +#else +# define UV__EAGAIN (-4088) +#endif + +#if defined(EALREADY) && !defined(_WIN32) +# define UV__EALREADY UV__ERR(EALREADY) +#else +# define UV__EALREADY (-4084) +#endif + +#if defined(EBADF) && !defined(_WIN32) +# define UV__EBADF UV__ERR(EBADF) +#else +# define UV__EBADF (-4083) +#endif + +#if defined(EBUSY) && !defined(_WIN32) +# define UV__EBUSY UV__ERR(EBUSY) +#else +# define UV__EBUSY (-4082) +#endif + +#if defined(ECANCELED) && !defined(_WIN32) +# define UV__ECANCELED UV__ERR(ECANCELED) +#else +# define UV__ECANCELED (-4081) +#endif + +#if defined(ECHARSET) && !defined(_WIN32) +# define UV__ECHARSET UV__ERR(ECHARSET) +#else +# define UV__ECHARSET (-4080) +#endif + +#if defined(ECONNABORTED) && !defined(_WIN32) +# define UV__ECONNABORTED UV__ERR(ECONNABORTED) +#else +# define UV__ECONNABORTED (-4079) +#endif + +#if defined(ECONNREFUSED) && !defined(_WIN32) +# define UV__ECONNREFUSED UV__ERR(ECONNREFUSED) +#else +# define UV__ECONNREFUSED (-4078) +#endif + +#if defined(ECONNRESET) && !defined(_WIN32) +# define UV__ECONNRESET UV__ERR(ECONNRESET) +#else +# define UV__ECONNRESET (-4077) +#endif + +#if defined(EDESTADDRREQ) && !defined(_WIN32) +# define UV__EDESTADDRREQ UV__ERR(EDESTADDRREQ) +#else +# define UV__EDESTADDRREQ (-4076) +#endif + +#if defined(EEXIST) && !defined(_WIN32) +# define UV__EEXIST UV__ERR(EEXIST) +#else +# define UV__EEXIST (-4075) +#endif + +#if defined(EFAULT) && !defined(_WIN32) +# define UV__EFAULT UV__ERR(EFAULT) +#else +# define UV__EFAULT (-4074) +#endif + +#if defined(EHOSTUNREACH) && !defined(_WIN32) +# define UV__EHOSTUNREACH UV__ERR(EHOSTUNREACH) +#else +# define UV__EHOSTUNREACH (-4073) +#endif + +#if defined(EINTR) && !defined(_WIN32) +# define UV__EINTR UV__ERR(EINTR) +#else +# define UV__EINTR (-4072) +#endif + +#if defined(EINVAL) && !defined(_WIN32) +# define UV__EINVAL UV__ERR(EINVAL) +#else +# define UV__EINVAL (-4071) +#endif + +#if defined(EIO) && !defined(_WIN32) +# define UV__EIO UV__ERR(EIO) +#else +# define UV__EIO (-4070) +#endif + +#if defined(EISCONN) && !defined(_WIN32) +# define UV__EISCONN UV__ERR(EISCONN) +#else +# define UV__EISCONN (-4069) +#endif + +#if defined(EISDIR) && !defined(_WIN32) +# define UV__EISDIR UV__ERR(EISDIR) +#else +# define UV__EISDIR (-4068) +#endif + +#if defined(ELOOP) && !defined(_WIN32) +# define UV__ELOOP UV__ERR(ELOOP) +#else +# define UV__ELOOP (-4067) +#endif + +#if defined(EMFILE) && !defined(_WIN32) +# define UV__EMFILE UV__ERR(EMFILE) +#else +# define UV__EMFILE (-4066) +#endif + +#if defined(EMSGSIZE) && !defined(_WIN32) +# define UV__EMSGSIZE UV__ERR(EMSGSIZE) +#else +# define UV__EMSGSIZE (-4065) +#endif + +#if defined(ENAMETOOLONG) && !defined(_WIN32) +# define UV__ENAMETOOLONG UV__ERR(ENAMETOOLONG) +#else +# define UV__ENAMETOOLONG (-4064) +#endif + +#if defined(ENETDOWN) && !defined(_WIN32) +# define UV__ENETDOWN UV__ERR(ENETDOWN) +#else +# define UV__ENETDOWN (-4063) +#endif + +#if defined(ENETUNREACH) && !defined(_WIN32) +# define UV__ENETUNREACH UV__ERR(ENETUNREACH) +#else +# define UV__ENETUNREACH (-4062) +#endif + +#if defined(ENFILE) && !defined(_WIN32) +# define UV__ENFILE UV__ERR(ENFILE) +#else +# define UV__ENFILE (-4061) +#endif + +#if defined(ENOBUFS) && !defined(_WIN32) +# define UV__ENOBUFS UV__ERR(ENOBUFS) +#else +# define UV__ENOBUFS (-4060) +#endif + +#if defined(ENODEV) && !defined(_WIN32) +# define UV__ENODEV UV__ERR(ENODEV) +#else +# define UV__ENODEV (-4059) +#endif + +#if defined(ENOENT) && !defined(_WIN32) +# define UV__ENOENT UV__ERR(ENOENT) +#else +# define UV__ENOENT (-4058) +#endif + +#if defined(ENOMEM) && !defined(_WIN32) +# define UV__ENOMEM UV__ERR(ENOMEM) +#else +# define UV__ENOMEM (-4057) +#endif + +#if defined(ENONET) && !defined(_WIN32) +# define UV__ENONET UV__ERR(ENONET) +#else +# define UV__ENONET (-4056) +#endif + +#if defined(ENOSPC) && !defined(_WIN32) +# define UV__ENOSPC UV__ERR(ENOSPC) +#else +# define UV__ENOSPC (-4055) +#endif + +#if defined(ENOSYS) && !defined(_WIN32) +# define UV__ENOSYS UV__ERR(ENOSYS) +#else +# define UV__ENOSYS (-4054) +#endif + +#if defined(ENOTCONN) && !defined(_WIN32) +# define UV__ENOTCONN UV__ERR(ENOTCONN) +#else +# define UV__ENOTCONN (-4053) +#endif + +#if defined(ENOTDIR) && !defined(_WIN32) +# define UV__ENOTDIR UV__ERR(ENOTDIR) +#else +# define UV__ENOTDIR (-4052) +#endif + +#if defined(ENOTEMPTY) && !defined(_WIN32) +# define UV__ENOTEMPTY UV__ERR(ENOTEMPTY) +#else +# define UV__ENOTEMPTY (-4051) +#endif + +#if defined(ENOTSOCK) && !defined(_WIN32) +# define UV__ENOTSOCK UV__ERR(ENOTSOCK) +#else +# define UV__ENOTSOCK (-4050) +#endif + +#if defined(ENOTSUP) && !defined(_WIN32) +# define UV__ENOTSUP UV__ERR(ENOTSUP) +#else +# define UV__ENOTSUP (-4049) +#endif + +#if defined(EPERM) && !defined(_WIN32) +# define UV__EPERM UV__ERR(EPERM) +#else +# define UV__EPERM (-4048) +#endif + +#if defined(EPIPE) && !defined(_WIN32) +# define UV__EPIPE UV__ERR(EPIPE) +#else +# define UV__EPIPE (-4047) +#endif + +#if defined(EPROTO) && !defined(_WIN32) +# define UV__EPROTO UV__ERR(EPROTO) +#else +# define UV__EPROTO (-4046) +#endif + +#if defined(EPROTONOSUPPORT) && !defined(_WIN32) +# define UV__EPROTONOSUPPORT UV__ERR(EPROTONOSUPPORT) +#else +# define UV__EPROTONOSUPPORT (-4045) +#endif + +#if defined(EPROTOTYPE) && !defined(_WIN32) +# define UV__EPROTOTYPE UV__ERR(EPROTOTYPE) +#else +# define UV__EPROTOTYPE (-4044) +#endif + +#if defined(EROFS) && !defined(_WIN32) +# define UV__EROFS UV__ERR(EROFS) +#else +# define UV__EROFS (-4043) +#endif + +#if defined(ESHUTDOWN) && !defined(_WIN32) +# define UV__ESHUTDOWN UV__ERR(ESHUTDOWN) +#else +# define UV__ESHUTDOWN (-4042) +#endif + +#if defined(ESPIPE) && !defined(_WIN32) +# define UV__ESPIPE UV__ERR(ESPIPE) +#else +# define UV__ESPIPE (-4041) +#endif + +#if defined(ESRCH) && !defined(_WIN32) +# define UV__ESRCH UV__ERR(ESRCH) +#else +# define UV__ESRCH (-4040) +#endif + +#if defined(ETIMEDOUT) && !defined(_WIN32) +# define UV__ETIMEDOUT UV__ERR(ETIMEDOUT) +#else +# define UV__ETIMEDOUT (-4039) +#endif + +#if defined(ETXTBSY) && !defined(_WIN32) +# define UV__ETXTBSY UV__ERR(ETXTBSY) +#else +# define UV__ETXTBSY (-4038) +#endif + +#if defined(EXDEV) && !defined(_WIN32) +# define UV__EXDEV UV__ERR(EXDEV) +#else +# define UV__EXDEV (-4037) +#endif + +#if defined(EFBIG) && !defined(_WIN32) +# define UV__EFBIG UV__ERR(EFBIG) +#else +# define UV__EFBIG (-4036) +#endif + +#if defined(ENOPROTOOPT) && !defined(_WIN32) +# define UV__ENOPROTOOPT UV__ERR(ENOPROTOOPT) +#else +# define UV__ENOPROTOOPT (-4035) +#endif + +#if defined(ERANGE) && !defined(_WIN32) +# define UV__ERANGE UV__ERR(ERANGE) +#else +# define UV__ERANGE (-4034) +#endif + +#if defined(ENXIO) && !defined(_WIN32) +# define UV__ENXIO UV__ERR(ENXIO) +#else +# define UV__ENXIO (-4033) +#endif + +#if defined(EMLINK) && !defined(_WIN32) +# define UV__EMLINK UV__ERR(EMLINK) +#else +# define UV__EMLINK (-4032) +#endif + +/* EHOSTDOWN is not visible on BSD-like systems when _POSIX_C_SOURCE is + * defined. Fortunately, its value is always 64 so it's possible albeit + * icky to hard-code it. + */ +#if defined(EHOSTDOWN) && !defined(_WIN32) +# define UV__EHOSTDOWN UV__ERR(EHOSTDOWN) +#elif defined(__APPLE__) || \ + defined(__DragonFly__) || \ + defined(__FreeBSD__) || \ + defined(__FreeBSD_kernel__) || \ + defined(__NetBSD__) || \ + defined(__OpenBSD__) +# define UV__EHOSTDOWN (-64) +#else +# define UV__EHOSTDOWN (-4031) +#endif + +#if defined(EREMOTEIO) && !defined(_WIN32) +# define UV__EREMOTEIO UV__ERR(EREMOTEIO) +#else +# define UV__EREMOTEIO (-4030) +#endif + +#if defined(ENOTTY) && !defined(_WIN32) +# define UV__ENOTTY UV__ERR(ENOTTY) +#else +# define UV__ENOTTY (-4029) +#endif + +#if defined(EFTYPE) && !defined(_WIN32) +# define UV__EFTYPE UV__ERR(EFTYPE) +#else +# define UV__EFTYPE (-4028) +#endif + +#if defined(EILSEQ) && !defined(_WIN32) +# define UV__EILSEQ UV__ERR(EILSEQ) +#else +# define UV__EILSEQ (-4027) +#endif + +#endif /* UV_ERRNO_H_ */ diff --git a/include/libuv/include/uv/linux.h b/include/libuv/include/uv/linux.h new file mode 100644 index 000000000..9b38405a1 --- /dev/null +++ b/include/libuv/include/uv/linux.h @@ -0,0 +1,34 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef UV_LINUX_H +#define UV_LINUX_H + +#define UV_PLATFORM_LOOP_FIELDS \ + uv__io_t inotify_read_watcher; \ + void* inotify_watchers; \ + int inotify_fd; \ + +#define UV_PLATFORM_FS_EVENT_FIELDS \ + void* watchers[2]; \ + int wd; \ + +#endif /* UV_LINUX_H */ diff --git a/include/libuv/include/uv/os390.h b/include/libuv/include/uv/os390.h new file mode 100644 index 000000000..0267d74cb --- /dev/null +++ b/include/libuv/include/uv/os390.h @@ -0,0 +1,33 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef UV_MVS_H +#define UV_MVS_H + +#define UV_PLATFORM_SEM_T long + +#define UV_PLATFORM_LOOP_FIELDS \ + void* ep; \ + +#define UV_PLATFORM_FS_EVENT_FIELDS \ + char rfis_rftok[8]; \ + +#endif /* UV_MVS_H */ diff --git a/include/libuv/include/uv/posix.h b/include/libuv/include/uv/posix.h new file mode 100644 index 000000000..9a96634db --- /dev/null +++ b/include/libuv/include/uv/posix.h @@ -0,0 +1,31 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef UV_POSIX_H +#define UV_POSIX_H + +#define UV_PLATFORM_LOOP_FIELDS \ + struct pollfd* poll_fds; \ + size_t poll_fds_used; \ + size_t poll_fds_size; \ + unsigned char poll_fds_iterating; \ + +#endif /* UV_POSIX_H */ diff --git a/include/libuv/include/uv/stdint-msvc2008.h b/include/libuv/include/uv/stdint-msvc2008.h new file mode 100644 index 000000000..d02608a59 --- /dev/null +++ b/include/libuv/include/uv/stdint-msvc2008.h @@ -0,0 +1,247 @@ +// ISO C9x compliant stdint.h for Microsoft Visual Studio +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// +// Copyright (c) 2006-2008 Alexander Chemeris +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// 3. The name of the author may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef _MSC_VER // [ +#error "Use this header only with Microsoft Visual C++ compilers!" +#endif // _MSC_VER ] + +#ifndef _MSC_STDINT_H_ // [ +#define _MSC_STDINT_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif + +#include + +// For Visual Studio 6 in C++ mode and for many Visual Studio versions when +// compiling for ARM we should wrap include with 'extern "C++" {}' +// or compiler give many errors like this: +// error C2733: second C linkage of overloaded function 'wmemchr' not allowed +#ifdef __cplusplus +extern "C" { +#endif +# include +#ifdef __cplusplus +} +#endif + +// Define _W64 macros to mark types changing their size, like intptr_t. +#ifndef _W64 +# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 +# define _W64 __w64 +# else +# define _W64 +# endif +#endif + + +// 7.18.1 Integer types + +// 7.18.1.1 Exact-width integer types + +// Visual Studio 6 and Embedded Visual C++ 4 doesn't +// realize that, e.g. char has the same size as __int8 +// so we give up on __intX for them. +#if (_MSC_VER < 1300) + typedef signed char int8_t; + typedef signed short int16_t; + typedef signed int int32_t; + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; +#else + typedef signed __int8 int8_t; + typedef signed __int16 int16_t; + typedef signed __int32 int32_t; + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; +#endif +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + + +// 7.18.1.2 Minimum-width integer types +typedef int8_t int_least8_t; +typedef int16_t int_least16_t; +typedef int32_t int_least32_t; +typedef int64_t int_least64_t; +typedef uint8_t uint_least8_t; +typedef uint16_t uint_least16_t; +typedef uint32_t uint_least32_t; +typedef uint64_t uint_least64_t; + +// 7.18.1.3 Fastest minimum-width integer types +typedef int8_t int_fast8_t; +typedef int16_t int_fast16_t; +typedef int32_t int_fast32_t; +typedef int64_t int_fast64_t; +typedef uint8_t uint_fast8_t; +typedef uint16_t uint_fast16_t; +typedef uint32_t uint_fast32_t; +typedef uint64_t uint_fast64_t; + +// 7.18.1.4 Integer types capable of holding object pointers +#ifdef _WIN64 // [ + typedef signed __int64 intptr_t; + typedef unsigned __int64 uintptr_t; +#else // _WIN64 ][ + typedef _W64 signed int intptr_t; + typedef _W64 unsigned int uintptr_t; +#endif // _WIN64 ] + +// 7.18.1.5 Greatest-width integer types +typedef int64_t intmax_t; +typedef uint64_t uintmax_t; + + +// 7.18.2 Limits of specified-width integer types + +#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259 + +// 7.18.2.1 Limits of exact-width integer types +#define INT8_MIN ((int8_t)_I8_MIN) +#define INT8_MAX _I8_MAX +#define INT16_MIN ((int16_t)_I16_MIN) +#define INT16_MAX _I16_MAX +#define INT32_MIN ((int32_t)_I32_MIN) +#define INT32_MAX _I32_MAX +#define INT64_MIN ((int64_t)_I64_MIN) +#define INT64_MAX _I64_MAX +#define UINT8_MAX _UI8_MAX +#define UINT16_MAX _UI16_MAX +#define UINT32_MAX _UI32_MAX +#define UINT64_MAX _UI64_MAX + +// 7.18.2.2 Limits of minimum-width integer types +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST8_MAX UINT8_MAX +#define UINT_LEAST16_MAX UINT16_MAX +#define UINT_LEAST32_MAX UINT32_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +// 7.18.2.3 Limits of fastest minimum-width integer types +#define INT_FAST8_MIN INT8_MIN +#define INT_FAST8_MAX INT8_MAX +#define INT_FAST16_MIN INT16_MIN +#define INT_FAST16_MAX INT16_MAX +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST8_MAX UINT8_MAX +#define UINT_FAST16_MAX UINT16_MAX +#define UINT_FAST32_MAX UINT32_MAX +#define UINT_FAST64_MAX UINT64_MAX + +// 7.18.2.4 Limits of integer types capable of holding object pointers +#ifdef _WIN64 // [ +# define INTPTR_MIN INT64_MIN +# define INTPTR_MAX INT64_MAX +# define UINTPTR_MAX UINT64_MAX +#else // _WIN64 ][ +# define INTPTR_MIN INT32_MIN +# define INTPTR_MAX INT32_MAX +# define UINTPTR_MAX UINT32_MAX +#endif // _WIN64 ] + +// 7.18.2.5 Limits of greatest-width integer types +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +// 7.18.3 Limits of other integer types + +#ifdef _WIN64 // [ +# define PTRDIFF_MIN _I64_MIN +# define PTRDIFF_MAX _I64_MAX +#else // _WIN64 ][ +# define PTRDIFF_MIN _I32_MIN +# define PTRDIFF_MAX _I32_MAX +#endif // _WIN64 ] + +#define SIG_ATOMIC_MIN INT_MIN +#define SIG_ATOMIC_MAX INT_MAX + +#ifndef SIZE_MAX // [ +# ifdef _WIN64 // [ +# define SIZE_MAX _UI64_MAX +# else // _WIN64 ][ +# define SIZE_MAX _UI32_MAX +# endif // _WIN64 ] +#endif // SIZE_MAX ] + +// WCHAR_MIN and WCHAR_MAX are also defined in +#ifndef WCHAR_MIN // [ +# define WCHAR_MIN 0 +#endif // WCHAR_MIN ] +#ifndef WCHAR_MAX // [ +# define WCHAR_MAX _UI16_MAX +#endif // WCHAR_MAX ] + +#define WINT_MIN 0 +#define WINT_MAX _UI16_MAX + +#endif // __STDC_LIMIT_MACROS ] + + +// 7.18.4 Limits of other integer types + +#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260 + +// 7.18.4.1 Macros for minimum-width integer constants + +#define INT8_C(val) val##i8 +#define INT16_C(val) val##i16 +#define INT32_C(val) val##i32 +#define INT64_C(val) val##i64 + +#define UINT8_C(val) val##ui8 +#define UINT16_C(val) val##ui16 +#define UINT32_C(val) val##ui32 +#define UINT64_C(val) val##ui64 + +// 7.18.4.2 Macros for greatest-width integer constants +#define INTMAX_C INT64_C +#define UINTMAX_C UINT64_C + +#endif // __STDC_CONSTANT_MACROS ] + + +#endif // _MSC_STDINT_H_ ] diff --git a/include/libuv/include/uv/sunos.h b/include/libuv/include/uv/sunos.h new file mode 100644 index 000000000..042166424 --- /dev/null +++ b/include/libuv/include/uv/sunos.h @@ -0,0 +1,44 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef UV_SUNOS_H +#define UV_SUNOS_H + +#include +#include + +/* For the sake of convenience and reduced #ifdef-ery in src/unix/sunos.c, + * add the fs_event fields even when this version of SunOS doesn't support + * file watching. + */ +#define UV_PLATFORM_LOOP_FIELDS \ + uv__io_t fs_event_watcher; \ + int fs_fd; \ + +#if defined(PORT_SOURCE_FILE) + +# define UV_PLATFORM_FS_EVENT_FIELDS \ + file_obj_t fo; \ + int fd; \ + +#endif /* defined(PORT_SOURCE_FILE) */ + +#endif /* UV_SUNOS_H */ diff --git a/include/libuv/include/uv/threadpool.h b/include/libuv/include/uv/threadpool.h new file mode 100644 index 000000000..9708ebdd5 --- /dev/null +++ b/include/libuv/include/uv/threadpool.h @@ -0,0 +1,37 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* + * This file is private to libuv. It provides common functionality to both + * Windows and Unix backends. + */ + +#ifndef UV_THREADPOOL_H_ +#define UV_THREADPOOL_H_ + +struct uv__work { + void (*work)(struct uv__work *w); + void (*done)(struct uv__work *w, int status); + struct uv_loop_s* loop; + void* wq[2]; +}; + +#endif /* UV_THREADPOOL_H_ */ diff --git a/include/libuv/include/uv/tree.h b/include/libuv/include/uv/tree.h new file mode 100644 index 000000000..f936416e3 --- /dev/null +++ b/include/libuv/include/uv/tree.h @@ -0,0 +1,768 @@ +/*- + * Copyright 2002 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UV_TREE_H_ +#define UV_TREE_H_ + +#ifndef UV__UNUSED +# if __GNUC__ +# define UV__UNUSED __attribute__((unused)) +# else +# define UV__UNUSED +# endif +#endif + +/* + * This file defines data structures for different types of trees: + * splay trees and red-black trees. + * + * A splay tree is a self-organizing data structure. Every operation + * on the tree causes a splay to happen. The splay moves the requested + * node to the root of the tree and partly rebalances it. + * + * This has the benefit that request locality causes faster lookups as + * the requested nodes move to the top of the tree. On the other hand, + * every lookup causes memory writes. + * + * The Balance Theorem bounds the total access time for m operations + * and n inserts on an initially empty tree as O((m + n)lg n). The + * amortized cost for a sequence of m accesses to a splay tree is O(lg n); + * + * A red-black tree is a binary search tree with the node color as an + * extra attribute. It fulfills a set of conditions: + * - every search path from the root to a leaf consists of the + * same number of black nodes, + * - each red node (except for the root) has a black parent, + * - each leaf node is black. + * + * Every operation on a red-black tree is bounded as O(lg n). + * The maximum height of a red-black tree is 2lg (n+1). + */ + +#define SPLAY_HEAD(name, type) \ +struct name { \ + struct type *sph_root; /* root of the tree */ \ +} + +#define SPLAY_INITIALIZER(root) \ + { NULL } + +#define SPLAY_INIT(root) do { \ + (root)->sph_root = NULL; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ENTRY(type) \ +struct { \ + struct type *spe_left; /* left element */ \ + struct type *spe_right; /* right element */ \ +} + +#define SPLAY_LEFT(elm, field) (elm)->field.spe_left +#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right +#define SPLAY_ROOT(head) (head)->sph_root +#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) + +/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ +#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_LINKLEFT(head, tmp, field) do { \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_LINKRIGHT(head, tmp, field) do { \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ +} while (/*CONSTCOND*/ 0) + +#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ + SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ + SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field); \ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ +} while (/*CONSTCOND*/ 0) + +/* Generates prototypes and inline functions */ + +#define SPLAY_PROTOTYPE(name, type, field, cmp) \ +void name##_SPLAY(struct name *, struct type *); \ +void name##_SPLAY_MINMAX(struct name *, int); \ +struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ +struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ + \ +/* Finds the node with the same key as elm */ \ +static __inline struct type * \ +name##_SPLAY_FIND(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) \ + return(NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) \ + return (head->sph_root); \ + return (NULL); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_NEXT(struct name *head, struct type *elm) \ +{ \ + name##_SPLAY(head, elm); \ + if (SPLAY_RIGHT(elm, field) != NULL) { \ + elm = SPLAY_RIGHT(elm, field); \ + while (SPLAY_LEFT(elm, field) != NULL) { \ + elm = SPLAY_LEFT(elm, field); \ + } \ + } else \ + elm = NULL; \ + return (elm); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_MIN_MAX(struct name *head, int val) \ +{ \ + name##_SPLAY_MINMAX(head, val); \ + return (SPLAY_ROOT(head)); \ +} + +/* Main splay operation. + * Moves node close to the key of elm to top + */ +#define SPLAY_GENERATE(name, type, field, cmp) \ +struct type * \ +name##_SPLAY_INSERT(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) { \ + SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ + } else { \ + int __comp; \ + name##_SPLAY(head, elm); \ + __comp = (cmp)(elm, (head)->sph_root); \ + if(__comp < 0) { \ + SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field); \ + SPLAY_RIGHT(elm, field) = (head)->sph_root; \ + SPLAY_LEFT((head)->sph_root, field) = NULL; \ + } else if (__comp > 0) { \ + SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field); \ + SPLAY_LEFT(elm, field) = (head)->sph_root; \ + SPLAY_RIGHT((head)->sph_root, field) = NULL; \ + } else \ + return ((head)->sph_root); \ + } \ + (head)->sph_root = (elm); \ + return (NULL); \ +} \ + \ +struct type * \ +name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *__tmp; \ + if (SPLAY_EMPTY(head)) \ + return (NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) { \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ + } else { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ + name##_SPLAY(head, elm); \ + SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ + } \ + return (elm); \ + } \ + return (NULL); \ +} \ + \ +void \ +name##_SPLAY(struct name *head, struct type *elm) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ + int __comp; \ + \ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL; \ + __left = __right = &__node; \ + \ + while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL) \ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) > 0){ \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL) \ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} \ + \ +/* Splay with either the minimum or the maximum element \ + * Used to find minimum or maximum element in tree. \ + */ \ +void name##_SPLAY_MINMAX(struct name *head, int __comp) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ + \ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL; \ + __left = __right = &__node; \ + \ + while (1) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL) \ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp > 0) { \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL) \ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} + +#define SPLAY_NEGINF -1 +#define SPLAY_INF 1 + +#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) +#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) +#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) +#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) +#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) +#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) + +#define SPLAY_FOREACH(x, name, head) \ + for ((x) = SPLAY_MIN(name, head); \ + (x) != NULL; \ + (x) = SPLAY_NEXT(name, head, x)) + +/* Macros that define a red-black tree */ +#define RB_HEAD(name, type) \ +struct name { \ + struct type *rbh_root; /* root of the tree */ \ +} + +#define RB_INITIALIZER(root) \ + { NULL } + +#define RB_INIT(root) do { \ + (root)->rbh_root = NULL; \ +} while (/*CONSTCOND*/ 0) + +#define RB_BLACK 0 +#define RB_RED 1 +#define RB_ENTRY(type) \ +struct { \ + struct type *rbe_left; /* left element */ \ + struct type *rbe_right; /* right element */ \ + struct type *rbe_parent; /* parent element */ \ + int rbe_color; /* node color */ \ +} + +#define RB_LEFT(elm, field) (elm)->field.rbe_left +#define RB_RIGHT(elm, field) (elm)->field.rbe_right +#define RB_PARENT(elm, field) (elm)->field.rbe_parent +#define RB_COLOR(elm, field) (elm)->field.rbe_color +#define RB_ROOT(head) (head)->rbh_root +#define RB_EMPTY(head) (RB_ROOT(head) == NULL) + +#define RB_SET(elm, parent, field) do { \ + RB_PARENT(elm, field) = parent; \ + RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ + RB_COLOR(elm, field) = RB_RED; \ +} while (/*CONSTCOND*/ 0) + +#define RB_SET_BLACKRED(black, red, field) do { \ + RB_COLOR(black, field) = RB_BLACK; \ + RB_COLOR(red, field) = RB_RED; \ +} while (/*CONSTCOND*/ 0) + +#ifndef RB_AUGMENT +#define RB_AUGMENT(x) do {} while (0) +#endif + +#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ + (tmp) = RB_RIGHT(elm, field); \ + if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \ + RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_LEFT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (/*CONSTCOND*/ 0) + +#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ + (tmp) = RB_LEFT(elm, field); \ + if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \ + RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_RIGHT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (/*CONSTCOND*/ 0) + +/* Generates prototypes and inline functions */ +#define RB_PROTOTYPE(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) +#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp, UV__UNUSED static) +#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ +attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ +attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ +attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ +attr struct type *name##_RB_INSERT(struct name *, struct type *); \ +attr struct type *name##_RB_FIND(struct name *, struct type *); \ +attr struct type *name##_RB_NFIND(struct name *, struct type *); \ +attr struct type *name##_RB_NEXT(struct type *); \ +attr struct type *name##_RB_PREV(struct type *); \ +attr struct type *name##_RB_MINMAX(struct name *, int); \ + \ + +/* Main rb operation. + * Moves node close to the key of elm to top + */ +#define RB_GENERATE(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp,) +#define RB_GENERATE_STATIC(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp, UV__UNUSED static) +#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ +attr void \ +name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ +{ \ + struct type *parent, *gparent, *tmp; \ + while ((parent = RB_PARENT(elm, field)) != NULL && \ + RB_COLOR(parent, field) == RB_RED) { \ + gparent = RB_PARENT(parent, field); \ + if (parent == RB_LEFT(gparent, field)) { \ + tmp = RB_RIGHT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field); \ + elm = gparent; \ + continue; \ + } \ + if (RB_RIGHT(parent, field) == elm) { \ + RB_ROTATE_LEFT(head, parent, tmp, field); \ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_RIGHT(head, gparent, tmp, field); \ + } else { \ + tmp = RB_LEFT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field); \ + elm = gparent; \ + continue; \ + } \ + if (RB_LEFT(parent, field) == elm) { \ + RB_ROTATE_RIGHT(head, parent, tmp, field); \ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_LEFT(head, gparent, tmp, field); \ + } \ + } \ + RB_COLOR(head->rbh_root, field) = RB_BLACK; \ +} \ + \ +attr void \ +name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, \ + struct type *elm) \ +{ \ + struct type *tmp; \ + while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ + elm != RB_ROOT(head)) { \ + if (RB_LEFT(parent, field) == elm) { \ + tmp = RB_RIGHT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_LEFT(head, parent, tmp, field); \ + tmp = RB_RIGHT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) && \ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) { \ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) { \ + struct type *oleft; \ + if ((oleft = RB_LEFT(tmp, field)) \ + != NULL) \ + RB_COLOR(oleft, field) = RB_BLACK; \ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_RIGHT(head, tmp, oleft, field); \ + tmp = RB_RIGHT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field); \ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_RIGHT(tmp, field)) \ + RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK; \ + RB_ROTATE_LEFT(head, parent, tmp, field); \ + elm = RB_ROOT(head); \ + break; \ + } \ + } else { \ + tmp = RB_LEFT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_RIGHT(head, parent, tmp, field); \ + tmp = RB_LEFT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) && \ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) { \ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) { \ + struct type *oright; \ + if ((oright = RB_RIGHT(tmp, field)) \ + != NULL) \ + RB_COLOR(oright, field) = RB_BLACK; \ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_LEFT(head, tmp, oright, field); \ + tmp = RB_LEFT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field); \ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_LEFT(tmp, field)) \ + RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK; \ + RB_ROTATE_RIGHT(head, parent, tmp, field); \ + elm = RB_ROOT(head); \ + break; \ + } \ + } \ + } \ + if (elm) \ + RB_COLOR(elm, field) = RB_BLACK; \ +} \ + \ +attr struct type * \ +name##_RB_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *child, *parent, *old = elm; \ + int color; \ + if (RB_LEFT(elm, field) == NULL) \ + child = RB_RIGHT(elm, field); \ + else if (RB_RIGHT(elm, field) == NULL) \ + child = RB_LEFT(elm, field); \ + else { \ + struct type *left; \ + elm = RB_RIGHT(elm, field); \ + while ((left = RB_LEFT(elm, field)) != NULL) \ + elm = left; \ + child = RB_RIGHT(elm, field); \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ + if (RB_PARENT(elm, field) == old) \ + parent = elm; \ + (elm)->field = (old)->field; \ + if (RB_PARENT(old, field)) { \ + if (RB_LEFT(RB_PARENT(old, field), field) == old) \ + RB_LEFT(RB_PARENT(old, field), field) = elm; \ + else \ + RB_RIGHT(RB_PARENT(old, field), field) = elm; \ + RB_AUGMENT(RB_PARENT(old, field)); \ + } else \ + RB_ROOT(head) = elm; \ + RB_PARENT(RB_LEFT(old, field), field) = elm; \ + if (RB_RIGHT(old, field)) \ + RB_PARENT(RB_RIGHT(old, field), field) = elm; \ + if (parent) { \ + left = parent; \ + do { \ + RB_AUGMENT(left); \ + } while ((left = RB_PARENT(left, field)) != NULL); \ + } \ + goto color; \ + } \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ +color: \ + if (color == RB_BLACK) \ + name##_RB_REMOVE_COLOR(head, parent, child); \ + return (old); \ +} \ + \ +/* Inserts a node into the RB tree */ \ +attr struct type * \ +name##_RB_INSERT(struct name *head, struct type *elm) \ +{ \ + struct type *tmp; \ + struct type *parent = NULL; \ + int comp = 0; \ + tmp = RB_ROOT(head); \ + while (tmp) { \ + parent = tmp; \ + comp = (cmp)(elm, parent); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + RB_SET(elm, parent, field); \ + if (parent != NULL) { \ + if (comp < 0) \ + RB_LEFT(parent, field) = elm; \ + else \ + RB_RIGHT(parent, field) = elm; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = elm; \ + name##_RB_INSERT_COLOR(head, elm); \ + return (NULL); \ +} \ + \ +/* Finds the node with the same key as elm */ \ +attr struct type * \ +name##_RB_FIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (NULL); \ +} \ + \ +/* Finds the first node greater than or equal to the search key */ \ +attr struct type * \ +name##_RB_NFIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *res = NULL; \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) { \ + res = tmp; \ + tmp = RB_LEFT(tmp, field); \ + } \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (res); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_NEXT(struct type *elm) \ +{ \ + if (RB_RIGHT(elm, field)) { \ + elm = RB_RIGHT(elm, field); \ + while (RB_LEFT(elm, field)) \ + elm = RB_LEFT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_PREV(struct type *elm) \ +{ \ + if (RB_LEFT(elm, field)) { \ + elm = RB_LEFT(elm, field); \ + while (RB_RIGHT(elm, field)) \ + elm = RB_RIGHT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +attr struct type * \ +name##_RB_MINMAX(struct name *head, int val) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *parent = NULL; \ + while (tmp) { \ + parent = tmp; \ + if (val < 0) \ + tmp = RB_LEFT(tmp, field); \ + else \ + tmp = RB_RIGHT(tmp, field); \ + } \ + return (parent); \ +} + +#define RB_NEGINF -1 +#define RB_INF 1 + +#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) +#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) +#define RB_FIND(name, x, y) name##_RB_FIND(x, y) +#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) +#define RB_NEXT(name, x, y) name##_RB_NEXT(y) +#define RB_PREV(name, x, y) name##_RB_PREV(y) +#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) +#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) + +#define RB_FOREACH(x, name, head) \ + for ((x) = RB_MIN(name, head); \ + (x) != NULL; \ + (x) = name##_RB_NEXT(x)) + +#define RB_FOREACH_FROM(x, name, y) \ + for ((x) = (y); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_SAFE(x, name, head, y) \ + for ((x) = RB_MIN(name, head); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE(x, name, head) \ + for ((x) = RB_MAX(name, head); \ + (x) != NULL; \ + (x) = name##_RB_PREV(x)) + +#define RB_FOREACH_REVERSE_FROM(x, name, y) \ + for ((x) = (y); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ + for ((x) = RB_MAX(name, head); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ + (x) = (y)) + +#endif /* UV_TREE_H_ */ diff --git a/include/libuv/include/uv/unix.h b/include/libuv/include/uv/unix.h new file mode 100644 index 000000000..e3cf7bdd4 --- /dev/null +++ b/include/libuv/include/uv/unix.h @@ -0,0 +1,507 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef UV_UNIX_H +#define UV_UNIX_H + +#include +#include +#include +#include + +#include +#include +#include +#include +#include /* MAXHOSTNAMELEN on Solaris */ + +#include +#include + +#if !defined(__MVS__) +#include +#include /* MAXHOSTNAMELEN on Linux and the BSDs */ +#endif +#include +#include + +#include "uv/threadpool.h" + +#if defined(__linux__) +# include "uv/linux.h" +#elif defined (__MVS__) +# include "uv/os390.h" +#elif defined(__PASE__) /* __PASE__ and _AIX are both defined on IBM i */ +# include "uv/posix.h" /* IBM i needs uv/posix.h, not uv/aix.h */ +#elif defined(_AIX) +# include "uv/aix.h" +#elif defined(__sun) +# include "uv/sunos.h" +#elif defined(__APPLE__) +# include "uv/darwin.h" +#elif defined(__DragonFly__) || \ + defined(__FreeBSD__) || \ + defined(__FreeBSD_kernel__) || \ + defined(__OpenBSD__) || \ + defined(__NetBSD__) +# include "uv/bsd.h" +#elif defined(__CYGWIN__) || \ + defined(__MSYS__) || \ + defined(__GNU__) +# include "uv/posix.h" +#elif defined(__HAIKU__) +# include "uv/posix.h" +#elif defined(__QNX__) +# include "uv/posix.h" +#endif + +#ifndef NI_MAXHOST +# define NI_MAXHOST 1025 +#endif + +#ifndef NI_MAXSERV +# define NI_MAXSERV 32 +#endif + +#ifndef UV_IO_PRIVATE_PLATFORM_FIELDS +# define UV_IO_PRIVATE_PLATFORM_FIELDS /* empty */ +#endif + +struct uv__io_s; +struct uv_loop_s; + +typedef void (*uv__io_cb)(struct uv_loop_s* loop, + struct uv__io_s* w, + unsigned int events); +typedef struct uv__io_s uv__io_t; + +struct uv__io_s { + uv__io_cb cb; + void* pending_queue[2]; + void* watcher_queue[2]; + unsigned int pevents; /* Pending event mask i.e. mask at next tick. */ + unsigned int events; /* Current event mask. */ + int fd; + UV_IO_PRIVATE_PLATFORM_FIELDS +}; + +#ifndef UV_PLATFORM_SEM_T +# define UV_PLATFORM_SEM_T sem_t +#endif + +#ifndef UV_PLATFORM_LOOP_FIELDS +# define UV_PLATFORM_LOOP_FIELDS /* empty */ +#endif + +#ifndef UV_PLATFORM_FS_EVENT_FIELDS +# define UV_PLATFORM_FS_EVENT_FIELDS /* empty */ +#endif + +#ifndef UV_STREAM_PRIVATE_PLATFORM_FIELDS +# define UV_STREAM_PRIVATE_PLATFORM_FIELDS /* empty */ +#endif + +/* Note: May be cast to struct iovec. See writev(2). */ +typedef struct uv_buf_t { + char* base; + size_t len; +} uv_buf_t; + +typedef int uv_file; +typedef int uv_os_sock_t; +typedef int uv_os_fd_t; +typedef pid_t uv_pid_t; + +#define UV_ONCE_INIT PTHREAD_ONCE_INIT + +typedef pthread_once_t uv_once_t; +typedef pthread_t uv_thread_t; +typedef pthread_mutex_t uv_mutex_t; +typedef pthread_rwlock_t uv_rwlock_t; +typedef UV_PLATFORM_SEM_T uv_sem_t; +typedef pthread_cond_t uv_cond_t; +typedef pthread_key_t uv_key_t; + +/* Note: guard clauses should match uv_barrier_init's in src/unix/thread.c. */ +#if defined(_AIX) || \ + defined(__OpenBSD__) || \ + !defined(PTHREAD_BARRIER_SERIAL_THREAD) +/* TODO(bnoordhuis) Merge into uv_barrier_t in v2. */ +struct _uv_barrier { + uv_mutex_t mutex; + uv_cond_t cond; + unsigned threshold; + unsigned in; + unsigned out; +}; + +typedef struct { + struct _uv_barrier* b; +# if defined(PTHREAD_BARRIER_SERIAL_THREAD) + /* TODO(bnoordhuis) Remove padding in v2. */ + char pad[sizeof(pthread_barrier_t) - sizeof(struct _uv_barrier*)]; +# endif +} uv_barrier_t; +#else +typedef pthread_barrier_t uv_barrier_t; +#endif + +/* Platform-specific definitions for uv_spawn support. */ +typedef gid_t uv_gid_t; +typedef uid_t uv_uid_t; + +typedef struct dirent uv__dirent_t; + +#define UV_DIR_PRIVATE_FIELDS \ + DIR* dir; + +#if defined(DT_UNKNOWN) +# define HAVE_DIRENT_TYPES +# if defined(DT_REG) +# define UV__DT_FILE DT_REG +# else +# define UV__DT_FILE -1 +# endif +# if defined(DT_DIR) +# define UV__DT_DIR DT_DIR +# else +# define UV__DT_DIR -2 +# endif +# if defined(DT_LNK) +# define UV__DT_LINK DT_LNK +# else +# define UV__DT_LINK -3 +# endif +# if defined(DT_FIFO) +# define UV__DT_FIFO DT_FIFO +# else +# define UV__DT_FIFO -4 +# endif +# if defined(DT_SOCK) +# define UV__DT_SOCKET DT_SOCK +# else +# define UV__DT_SOCKET -5 +# endif +# if defined(DT_CHR) +# define UV__DT_CHAR DT_CHR +# else +# define UV__DT_CHAR -6 +# endif +# if defined(DT_BLK) +# define UV__DT_BLOCK DT_BLK +# else +# define UV__DT_BLOCK -7 +# endif +#endif + +/* Platform-specific definitions for uv_dlopen support. */ +#define UV_DYNAMIC /* empty */ + +typedef struct { + void* handle; + char* errmsg; +} uv_lib_t; + +#define UV_LOOP_PRIVATE_FIELDS \ + unsigned long flags; \ + int backend_fd; \ + void* pending_queue[2]; \ + void* watcher_queue[2]; \ + uv__io_t** watchers; \ + unsigned int nwatchers; \ + unsigned int nfds; \ + void* wq[2]; \ + uv_mutex_t wq_mutex; \ + uv_async_t wq_async; \ + uv_rwlock_t cloexec_lock; \ + uv_handle_t* closing_handles; \ + void* process_handles[2]; \ + void* prepare_handles[2]; \ + void* check_handles[2]; \ + void* idle_handles[2]; \ + void* async_handles[2]; \ + void (*async_unused)(void); /* TODO(bnoordhuis) Remove in libuv v2. */ \ + uv__io_t async_io_watcher; \ + int async_wfd; \ + struct { \ + void* min; \ + unsigned int nelts; \ + } timer_heap; \ + uint64_t timer_counter; \ + uint64_t time; \ + int signal_pipefd[2]; \ + uv__io_t signal_io_watcher; \ + uv_signal_t child_watcher; \ + int emfile_fd; \ + UV_PLATFORM_LOOP_FIELDS \ + +#define UV_REQ_TYPE_PRIVATE /* empty */ + +#define UV_REQ_PRIVATE_FIELDS /* empty */ + +#define UV_PRIVATE_REQ_TYPES /* empty */ + +#define UV_WRITE_PRIVATE_FIELDS \ + void* queue[2]; \ + unsigned int write_index; \ + uv_buf_t* bufs; \ + unsigned int nbufs; \ + int error; \ + uv_buf_t bufsml[4]; \ + +#define UV_CONNECT_PRIVATE_FIELDS \ + void* queue[2]; \ + +#define UV_SHUTDOWN_PRIVATE_FIELDS /* empty */ + +#define UV_UDP_SEND_PRIVATE_FIELDS \ + void* queue[2]; \ + struct sockaddr_storage addr; \ + unsigned int nbufs; \ + uv_buf_t* bufs; \ + ssize_t status; \ + uv_udp_send_cb send_cb; \ + uv_buf_t bufsml[4]; \ + +#define UV_HANDLE_PRIVATE_FIELDS \ + uv_handle_t* next_closing; \ + unsigned int flags; \ + +#define UV_STREAM_PRIVATE_FIELDS \ + uv_connect_t *connect_req; \ + uv_shutdown_t *shutdown_req; \ + uv__io_t io_watcher; \ + void* write_queue[2]; \ + void* write_completed_queue[2]; \ + uv_connection_cb connection_cb; \ + int delayed_error; \ + int accepted_fd; \ + void* queued_fds; \ + UV_STREAM_PRIVATE_PLATFORM_FIELDS \ + +#define UV_TCP_PRIVATE_FIELDS /* empty */ + +#define UV_UDP_PRIVATE_FIELDS \ + uv_alloc_cb alloc_cb; \ + uv_udp_recv_cb recv_cb; \ + uv__io_t io_watcher; \ + void* write_queue[2]; \ + void* write_completed_queue[2]; \ + +#define UV_PIPE_PRIVATE_FIELDS \ + const char* pipe_fname; /* strdup'ed */ + +#define UV_POLL_PRIVATE_FIELDS \ + uv__io_t io_watcher; + +#define UV_PREPARE_PRIVATE_FIELDS \ + uv_prepare_cb prepare_cb; \ + void* queue[2]; \ + +#define UV_CHECK_PRIVATE_FIELDS \ + uv_check_cb check_cb; \ + void* queue[2]; \ + +#define UV_IDLE_PRIVATE_FIELDS \ + uv_idle_cb idle_cb; \ + void* queue[2]; \ + +#define UV_ASYNC_PRIVATE_FIELDS \ + uv_async_cb async_cb; \ + void* queue[2]; \ + int pending; \ + +#define UV_TIMER_PRIVATE_FIELDS \ + uv_timer_cb timer_cb; \ + void* heap_node[3]; \ + uint64_t timeout; \ + uint64_t repeat; \ + uint64_t start_id; + +#define UV_GETADDRINFO_PRIVATE_FIELDS \ + struct uv__work work_req; \ + uv_getaddrinfo_cb cb; \ + struct addrinfo* hints; \ + char* hostname; \ + char* service; \ + struct addrinfo* addrinfo; \ + int retcode; + +#define UV_GETNAMEINFO_PRIVATE_FIELDS \ + struct uv__work work_req; \ + uv_getnameinfo_cb getnameinfo_cb; \ + struct sockaddr_storage storage; \ + int flags; \ + char host[NI_MAXHOST]; \ + char service[NI_MAXSERV]; \ + int retcode; + +#define UV_PROCESS_PRIVATE_FIELDS \ + void* queue[2]; \ + int status; \ + +#define UV_FS_PRIVATE_FIELDS \ + const char *new_path; \ + uv_file file; \ + int flags; \ + mode_t mode; \ + unsigned int nbufs; \ + uv_buf_t* bufs; \ + off_t off; \ + uv_uid_t uid; \ + uv_gid_t gid; \ + double atime; \ + double mtime; \ + struct uv__work work_req; \ + uv_buf_t bufsml[4]; \ + +#define UV_WORK_PRIVATE_FIELDS \ + struct uv__work work_req; + +#define UV_TTY_PRIVATE_FIELDS \ + struct termios orig_termios; \ + int mode; + +#define UV_SIGNAL_PRIVATE_FIELDS \ + /* RB_ENTRY(uv_signal_s) tree_entry; */ \ + struct { \ + struct uv_signal_s* rbe_left; \ + struct uv_signal_s* rbe_right; \ + struct uv_signal_s* rbe_parent; \ + int rbe_color; \ + } tree_entry; \ + /* Use two counters here so we don have to fiddle with atomics. */ \ + unsigned int caught_signals; \ + unsigned int dispatched_signals; + +#define UV_FS_EVENT_PRIVATE_FIELDS \ + uv_fs_event_cb cb; \ + UV_PLATFORM_FS_EVENT_FIELDS \ + +/* fs open() flags supported on this platform: */ +#if defined(O_APPEND) +# define UV_FS_O_APPEND O_APPEND +#else +# define UV_FS_O_APPEND 0 +#endif +#if defined(O_CREAT) +# define UV_FS_O_CREAT O_CREAT +#else +# define UV_FS_O_CREAT 0 +#endif + +#if defined(__linux__) && defined(__arm__) +# define UV_FS_O_DIRECT 0x10000 +#elif defined(__linux__) && defined(__m68k__) +# define UV_FS_O_DIRECT 0x10000 +#elif defined(__linux__) && defined(__mips__) +# define UV_FS_O_DIRECT 0x08000 +#elif defined(__linux__) && defined(__powerpc__) +# define UV_FS_O_DIRECT 0x20000 +#elif defined(__linux__) && defined(__s390x__) +# define UV_FS_O_DIRECT 0x04000 +#elif defined(__linux__) && defined(__x86_64__) +# define UV_FS_O_DIRECT 0x04000 +#elif defined(O_DIRECT) +# define UV_FS_O_DIRECT O_DIRECT +#else +# define UV_FS_O_DIRECT 0 +#endif + +#if defined(O_DIRECTORY) +# define UV_FS_O_DIRECTORY O_DIRECTORY +#else +# define UV_FS_O_DIRECTORY 0 +#endif +#if defined(O_DSYNC) +# define UV_FS_O_DSYNC O_DSYNC +#else +# define UV_FS_O_DSYNC 0 +#endif +#if defined(O_EXCL) +# define UV_FS_O_EXCL O_EXCL +#else +# define UV_FS_O_EXCL 0 +#endif +#if defined(O_EXLOCK) +# define UV_FS_O_EXLOCK O_EXLOCK +#else +# define UV_FS_O_EXLOCK 0 +#endif +#if defined(O_NOATIME) +# define UV_FS_O_NOATIME O_NOATIME +#else +# define UV_FS_O_NOATIME 0 +#endif +#if defined(O_NOCTTY) +# define UV_FS_O_NOCTTY O_NOCTTY +#else +# define UV_FS_O_NOCTTY 0 +#endif +#if defined(O_NOFOLLOW) +# define UV_FS_O_NOFOLLOW O_NOFOLLOW +#else +# define UV_FS_O_NOFOLLOW 0 +#endif +#if defined(O_NONBLOCK) +# define UV_FS_O_NONBLOCK O_NONBLOCK +#else +# define UV_FS_O_NONBLOCK 0 +#endif +#if defined(O_RDONLY) +# define UV_FS_O_RDONLY O_RDONLY +#else +# define UV_FS_O_RDONLY 0 +#endif +#if defined(O_RDWR) +# define UV_FS_O_RDWR O_RDWR +#else +# define UV_FS_O_RDWR 0 +#endif +#if defined(O_SYMLINK) +# define UV_FS_O_SYMLINK O_SYMLINK +#else +# define UV_FS_O_SYMLINK 0 +#endif +#if defined(O_SYNC) +# define UV_FS_O_SYNC O_SYNC +#else +# define UV_FS_O_SYNC 0 +#endif +#if defined(O_TRUNC) +# define UV_FS_O_TRUNC O_TRUNC +#else +# define UV_FS_O_TRUNC 0 +#endif +#if defined(O_WRONLY) +# define UV_FS_O_WRONLY O_WRONLY +#else +# define UV_FS_O_WRONLY 0 +#endif + +/* fs open() flags supported on other platforms: */ +#define UV_FS_O_FILEMAP 0 +#define UV_FS_O_RANDOM 0 +#define UV_FS_O_SHORT_LIVED 0 +#define UV_FS_O_SEQUENTIAL 0 +#define UV_FS_O_TEMPORARY 0 + +#endif /* UV_UNIX_H */ diff --git a/include/libuv/include/uv/version.h b/include/libuv/include/uv/version.h new file mode 100644 index 000000000..5272008a3 --- /dev/null +++ b/include/libuv/include/uv/version.h @@ -0,0 +1,43 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef UV_VERSION_H +#define UV_VERSION_H + + /* + * Versions with the same major number are ABI stable. API is allowed to + * evolve between minor releases, but only in a backwards compatible way. + * Make sure you update the -soname directives in configure.ac + * whenever you bump UV_VERSION_MAJOR or UV_VERSION_MINOR (but + * not UV_VERSION_PATCH.) + */ + +#define UV_VERSION_MAJOR 1 +#define UV_VERSION_MINOR 40 +#define UV_VERSION_PATCH 0 +#define UV_VERSION_IS_RELEASE 1 +#define UV_VERSION_SUFFIX "" + +#define UV_VERSION_HEX ((UV_VERSION_MAJOR << 16) | \ + (UV_VERSION_MINOR << 8) | \ + (UV_VERSION_PATCH)) + +#endif /* UV_VERSION_H */ diff --git a/include/libuv/include/uv/win.h b/include/libuv/include/uv/win.h new file mode 100644 index 000000000..f5f1d3a3c --- /dev/null +++ b/include/libuv/include/uv/win.h @@ -0,0 +1,691 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef _WIN32_WINNT +# define _WIN32_WINNT 0x0600 +#endif + +#if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED) +typedef intptr_t ssize_t; +# define SSIZE_MAX INTPTR_MAX +# define _SSIZE_T_ +# define _SSIZE_T_DEFINED +#endif + +#include + +#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) +typedef struct pollfd { + SOCKET fd; + short events; + short revents; +} WSAPOLLFD, *PWSAPOLLFD, *LPWSAPOLLFD; +#endif + +#ifndef LOCALE_INVARIANT +# define LOCALE_INVARIANT 0x007f +#endif + +#include +#include +#include + +#include +#include +#include +#include + +#if defined(_MSC_VER) && _MSC_VER < 1600 +# include "uv/stdint-msvc2008.h" +#else +# include +#endif + +#include "uv/tree.h" +#include "uv/threadpool.h" + +#define MAX_PIPENAME_LEN 256 + +#ifndef S_IFLNK +# define S_IFLNK 0xA000 +#endif + +/* Additional signals supported by uv_signal and or uv_kill. The CRT defines + * the following signals already: + * + * #define SIGINT 2 + * #define SIGILL 4 + * #define SIGABRT_COMPAT 6 + * #define SIGFPE 8 + * #define SIGSEGV 11 + * #define SIGTERM 15 + * #define SIGBREAK 21 + * #define SIGABRT 22 + * + * The additional signals have values that are common on other Unix + * variants (Linux and Darwin) + */ +#define SIGHUP 1 +#define SIGKILL 9 +#define SIGWINCH 28 + +/* Redefine NSIG to take SIGWINCH into consideration */ +#if defined(NSIG) && NSIG <= SIGWINCH +# undef NSIG +#endif +#ifndef NSIG +# define NSIG SIGWINCH + 1 +#endif + +/* The CRT defines SIGABRT_COMPAT as 6, which equals SIGABRT on many unix-like + * platforms. However MinGW doesn't define it, so we do. */ +#ifndef SIGABRT_COMPAT +# define SIGABRT_COMPAT 6 +#endif + +/* + * Guids and typedefs for winsock extension functions + * Mingw32 doesn't have these :-( + */ +#ifndef WSAID_ACCEPTEX +# define WSAID_ACCEPTEX \ + {0xb5367df1, 0xcbac, 0x11cf, \ + {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}} + +# define WSAID_CONNECTEX \ + {0x25a207b9, 0xddf3, 0x4660, \ + {0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e}} + +# define WSAID_GETACCEPTEXSOCKADDRS \ + {0xb5367df2, 0xcbac, 0x11cf, \ + {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}} + +# define WSAID_DISCONNECTEX \ + {0x7fda2e11, 0x8630, 0x436f, \ + {0xa0, 0x31, 0xf5, 0x36, 0xa6, 0xee, 0xc1, 0x57}} + +# define WSAID_TRANSMITFILE \ + {0xb5367df0, 0xcbac, 0x11cf, \ + {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}} + + typedef BOOL (PASCAL *LPFN_ACCEPTEX) + (SOCKET sListenSocket, + SOCKET sAcceptSocket, + PVOID lpOutputBuffer, + DWORD dwReceiveDataLength, + DWORD dwLocalAddressLength, + DWORD dwRemoteAddressLength, + LPDWORD lpdwBytesReceived, + LPOVERLAPPED lpOverlapped); + + typedef BOOL (PASCAL *LPFN_CONNECTEX) + (SOCKET s, + const struct sockaddr* name, + int namelen, + PVOID lpSendBuffer, + DWORD dwSendDataLength, + LPDWORD lpdwBytesSent, + LPOVERLAPPED lpOverlapped); + + typedef void (PASCAL *LPFN_GETACCEPTEXSOCKADDRS) + (PVOID lpOutputBuffer, + DWORD dwReceiveDataLength, + DWORD dwLocalAddressLength, + DWORD dwRemoteAddressLength, + LPSOCKADDR* LocalSockaddr, + LPINT LocalSockaddrLength, + LPSOCKADDR* RemoteSockaddr, + LPINT RemoteSockaddrLength); + + typedef BOOL (PASCAL *LPFN_DISCONNECTEX) + (SOCKET hSocket, + LPOVERLAPPED lpOverlapped, + DWORD dwFlags, + DWORD reserved); + + typedef BOOL (PASCAL *LPFN_TRANSMITFILE) + (SOCKET hSocket, + HANDLE hFile, + DWORD nNumberOfBytesToWrite, + DWORD nNumberOfBytesPerSend, + LPOVERLAPPED lpOverlapped, + LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers, + DWORD dwFlags); + + typedef PVOID RTL_SRWLOCK; + typedef RTL_SRWLOCK SRWLOCK, *PSRWLOCK; +#endif + +typedef int (WSAAPI* LPFN_WSARECV) + (SOCKET socket, + LPWSABUF buffers, + DWORD buffer_count, + LPDWORD bytes, + LPDWORD flags, + LPWSAOVERLAPPED overlapped, + LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine); + +typedef int (WSAAPI* LPFN_WSARECVFROM) + (SOCKET socket, + LPWSABUF buffers, + DWORD buffer_count, + LPDWORD bytes, + LPDWORD flags, + struct sockaddr* addr, + LPINT addr_len, + LPWSAOVERLAPPED overlapped, + LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine); + +#ifndef _NTDEF_ + typedef LONG NTSTATUS; + typedef NTSTATUS *PNTSTATUS; +#endif + +#ifndef RTL_CONDITION_VARIABLE_INIT + typedef PVOID CONDITION_VARIABLE, *PCONDITION_VARIABLE; +#endif + +typedef struct _AFD_POLL_HANDLE_INFO { + HANDLE Handle; + ULONG Events; + NTSTATUS Status; +} AFD_POLL_HANDLE_INFO, *PAFD_POLL_HANDLE_INFO; + +typedef struct _AFD_POLL_INFO { + LARGE_INTEGER Timeout; + ULONG NumberOfHandles; + ULONG Exclusive; + AFD_POLL_HANDLE_INFO Handles[1]; +} AFD_POLL_INFO, *PAFD_POLL_INFO; + +#define UV_MSAFD_PROVIDER_COUNT 3 + + +/** + * It should be possible to cast uv_buf_t[] to WSABUF[] + * see http://msdn.microsoft.com/en-us/library/ms741542(v=vs.85).aspx + */ +typedef struct uv_buf_t { + ULONG len; + char* base; +} uv_buf_t; + +typedef int uv_file; +typedef SOCKET uv_os_sock_t; +typedef HANDLE uv_os_fd_t; +typedef int uv_pid_t; + +typedef HANDLE uv_thread_t; + +typedef HANDLE uv_sem_t; + +typedef CRITICAL_SECTION uv_mutex_t; + +/* This condition variable implementation is based on the SetEvent solution + * (section 3.2) at http://www.cs.wustl.edu/~schmidt/win32-cv-1.html + * We could not use the SignalObjectAndWait solution (section 3.4) because + * it want the 2nd argument (type uv_mutex_t) of uv_cond_wait() and + * uv_cond_timedwait() to be HANDLEs, but we use CRITICAL_SECTIONs. + */ + +typedef union { + CONDITION_VARIABLE cond_var; + struct { + unsigned int waiters_count; + CRITICAL_SECTION waiters_count_lock; + HANDLE signal_event; + HANDLE broadcast_event; + } unused_; /* TODO: retained for ABI compatibility; remove me in v2.x. */ +} uv_cond_t; + +typedef union { + struct { + unsigned int num_readers_; + CRITICAL_SECTION num_readers_lock_; + HANDLE write_semaphore_; + } state_; + /* TODO: remove me in v2.x. */ + struct { + SRWLOCK unused_; + } unused1_; + /* TODO: remove me in v2.x. */ + struct { + uv_mutex_t unused1_; + uv_mutex_t unused2_; + } unused2_; +} uv_rwlock_t; + +typedef struct { + unsigned int n; + unsigned int count; + uv_mutex_t mutex; + uv_sem_t turnstile1; + uv_sem_t turnstile2; +} uv_barrier_t; + +typedef struct { + DWORD tls_index; +} uv_key_t; + +#define UV_ONCE_INIT { 0, NULL } + +typedef struct uv_once_s { + unsigned char ran; + HANDLE event; +} uv_once_t; + +/* Platform-specific definitions for uv_spawn support. */ +typedef unsigned char uv_uid_t; +typedef unsigned char uv_gid_t; + +typedef struct uv__dirent_s { + int d_type; + char d_name[1]; +} uv__dirent_t; + +#define UV_DIR_PRIVATE_FIELDS \ + HANDLE dir_handle; \ + WIN32_FIND_DATAW find_data; \ + BOOL need_find_call; + +#define HAVE_DIRENT_TYPES +#define UV__DT_DIR UV_DIRENT_DIR +#define UV__DT_FILE UV_DIRENT_FILE +#define UV__DT_LINK UV_DIRENT_LINK +#define UV__DT_FIFO UV_DIRENT_FIFO +#define UV__DT_SOCKET UV_DIRENT_SOCKET +#define UV__DT_CHAR UV_DIRENT_CHAR +#define UV__DT_BLOCK UV_DIRENT_BLOCK + +/* Platform-specific definitions for uv_dlopen support. */ +#define UV_DYNAMIC FAR WINAPI +typedef struct { + HMODULE handle; + char* errmsg; +} uv_lib_t; + +#define UV_LOOP_PRIVATE_FIELDS \ + /* The loop's I/O completion port */ \ + HANDLE iocp; \ + /* The current time according to the event loop. in msecs. */ \ + uint64_t time; \ + /* Tail of a single-linked circular queue of pending reqs. If the queue */ \ + /* is empty, tail_ is NULL. If there is only one item, */ \ + /* tail_->next_req == tail_ */ \ + uv_req_t* pending_reqs_tail; \ + /* Head of a single-linked list of closed handles */ \ + uv_handle_t* endgame_handles; \ + /* TODO(bnoordhuis) Stop heap-allocating |timer_heap| in libuv v2.x. */ \ + void* timer_heap; \ + /* Lists of active loop (prepare / check / idle) watchers */ \ + uv_prepare_t* prepare_handles; \ + uv_check_t* check_handles; \ + uv_idle_t* idle_handles; \ + /* This pointer will refer to the prepare/check/idle handle whose */ \ + /* callback is scheduled to be called next. This is needed to allow */ \ + /* safe removal from one of the lists above while that list being */ \ + /* iterated over. */ \ + uv_prepare_t* next_prepare_handle; \ + uv_check_t* next_check_handle; \ + uv_idle_t* next_idle_handle; \ + /* This handle holds the peer sockets for the fast variant of uv_poll_t */ \ + SOCKET poll_peer_sockets[UV_MSAFD_PROVIDER_COUNT]; \ + /* Counter to keep track of active tcp streams */ \ + unsigned int active_tcp_streams; \ + /* Counter to keep track of active udp streams */ \ + unsigned int active_udp_streams; \ + /* Counter to started timer */ \ + uint64_t timer_counter; \ + /* Threadpool */ \ + void* wq[2]; \ + uv_mutex_t wq_mutex; \ + uv_async_t wq_async; + +#define UV_REQ_TYPE_PRIVATE \ + /* TODO: remove the req suffix */ \ + UV_ACCEPT, \ + UV_FS_EVENT_REQ, \ + UV_POLL_REQ, \ + UV_PROCESS_EXIT, \ + UV_READ, \ + UV_UDP_RECV, \ + UV_WAKEUP, \ + UV_SIGNAL_REQ, + +#define UV_REQ_PRIVATE_FIELDS \ + union { \ + /* Used by I/O operations */ \ + struct { \ + OVERLAPPED overlapped; \ + size_t queued_bytes; \ + } io; \ + } u; \ + struct uv_req_s* next_req; + +#define UV_WRITE_PRIVATE_FIELDS \ + int coalesced; \ + uv_buf_t write_buffer; \ + HANDLE event_handle; \ + HANDLE wait_handle; + +#define UV_CONNECT_PRIVATE_FIELDS \ + /* empty */ + +#define UV_SHUTDOWN_PRIVATE_FIELDS \ + /* empty */ + +#define UV_UDP_SEND_PRIVATE_FIELDS \ + /* empty */ + +#define UV_PRIVATE_REQ_TYPES \ + typedef struct uv_pipe_accept_s { \ + UV_REQ_FIELDS \ + HANDLE pipeHandle; \ + struct uv_pipe_accept_s* next_pending; \ + } uv_pipe_accept_t; \ + \ + typedef struct uv_tcp_accept_s { \ + UV_REQ_FIELDS \ + SOCKET accept_socket; \ + char accept_buffer[sizeof(struct sockaddr_storage) * 2 + 32]; \ + HANDLE event_handle; \ + HANDLE wait_handle; \ + struct uv_tcp_accept_s* next_pending; \ + } uv_tcp_accept_t; \ + \ + typedef struct uv_read_s { \ + UV_REQ_FIELDS \ + HANDLE event_handle; \ + HANDLE wait_handle; \ + } uv_read_t; + +#define uv_stream_connection_fields \ + unsigned int write_reqs_pending; \ + uv_shutdown_t* shutdown_req; + +#define uv_stream_server_fields \ + uv_connection_cb connection_cb; + +#define UV_STREAM_PRIVATE_FIELDS \ + unsigned int reqs_pending; \ + int activecnt; \ + uv_read_t read_req; \ + union { \ + struct { uv_stream_connection_fields } conn; \ + struct { uv_stream_server_fields } serv; \ + } stream; + +#define uv_tcp_server_fields \ + uv_tcp_accept_t* accept_reqs; \ + unsigned int processed_accepts; \ + uv_tcp_accept_t* pending_accepts; \ + LPFN_ACCEPTEX func_acceptex; + +#define uv_tcp_connection_fields \ + uv_buf_t read_buffer; \ + LPFN_CONNECTEX func_connectex; + +#define UV_TCP_PRIVATE_FIELDS \ + SOCKET socket; \ + int delayed_error; \ + union { \ + struct { uv_tcp_server_fields } serv; \ + struct { uv_tcp_connection_fields } conn; \ + } tcp; + +#define UV_UDP_PRIVATE_FIELDS \ + SOCKET socket; \ + unsigned int reqs_pending; \ + int activecnt; \ + uv_req_t recv_req; \ + uv_buf_t recv_buffer; \ + struct sockaddr_storage recv_from; \ + int recv_from_len; \ + uv_udp_recv_cb recv_cb; \ + uv_alloc_cb alloc_cb; \ + LPFN_WSARECV func_wsarecv; \ + LPFN_WSARECVFROM func_wsarecvfrom; + +#define uv_pipe_server_fields \ + int pending_instances; \ + uv_pipe_accept_t* accept_reqs; \ + uv_pipe_accept_t* pending_accepts; + +#define uv_pipe_connection_fields \ + uv_timer_t* eof_timer; \ + uv_write_t dummy; /* TODO: retained for ABI compat; remove this in v2.x. */ \ + DWORD ipc_remote_pid; \ + union { \ + uint32_t payload_remaining; \ + uint64_t dummy; /* TODO: retained for ABI compat; remove this in v2.x. */ \ + } ipc_data_frame; \ + void* ipc_xfer_queue[2]; \ + int ipc_xfer_queue_length; \ + uv_write_t* non_overlapped_writes_tail; \ + CRITICAL_SECTION readfile_thread_lock; \ + volatile HANDLE readfile_thread_handle; + +#define UV_PIPE_PRIVATE_FIELDS \ + HANDLE handle; \ + WCHAR* name; \ + union { \ + struct { uv_pipe_server_fields } serv; \ + struct { uv_pipe_connection_fields } conn; \ + } pipe; + +/* TODO: put the parser states in an union - TTY handles are always half-duplex + * so read-state can safely overlap write-state. */ +#define UV_TTY_PRIVATE_FIELDS \ + HANDLE handle; \ + union { \ + struct { \ + /* Used for readable TTY handles */ \ + /* TODO: remove me in v2.x. */ \ + HANDLE unused_; \ + uv_buf_t read_line_buffer; \ + HANDLE read_raw_wait; \ + /* Fields used for translating win keystrokes into vt100 characters */ \ + char last_key[8]; \ + unsigned char last_key_offset; \ + unsigned char last_key_len; \ + WCHAR last_utf16_high_surrogate; \ + INPUT_RECORD last_input_record; \ + } rd; \ + struct { \ + /* Used for writable TTY handles */ \ + /* utf8-to-utf16 conversion state */ \ + unsigned int utf8_codepoint; \ + unsigned char utf8_bytes_left; \ + /* eol conversion state */ \ + unsigned char previous_eol; \ + /* ansi parser state */ \ + unsigned short ansi_parser_state; \ + unsigned char ansi_csi_argc; \ + unsigned short ansi_csi_argv[4]; \ + COORD saved_position; \ + WORD saved_attributes; \ + } wr; \ + } tty; + +#define UV_POLL_PRIVATE_FIELDS \ + SOCKET socket; \ + /* Used in fast mode */ \ + SOCKET peer_socket; \ + AFD_POLL_INFO afd_poll_info_1; \ + AFD_POLL_INFO afd_poll_info_2; \ + /* Used in fast and slow mode. */ \ + uv_req_t poll_req_1; \ + uv_req_t poll_req_2; \ + unsigned char submitted_events_1; \ + unsigned char submitted_events_2; \ + unsigned char mask_events_1; \ + unsigned char mask_events_2; \ + unsigned char events; + +#define UV_TIMER_PRIVATE_FIELDS \ + void* heap_node[3]; \ + int unused; \ + uint64_t timeout; \ + uint64_t repeat; \ + uint64_t start_id; \ + uv_timer_cb timer_cb; + +#define UV_ASYNC_PRIVATE_FIELDS \ + struct uv_req_s async_req; \ + uv_async_cb async_cb; \ + /* char to avoid alignment issues */ \ + char volatile async_sent; + +#define UV_PREPARE_PRIVATE_FIELDS \ + uv_prepare_t* prepare_prev; \ + uv_prepare_t* prepare_next; \ + uv_prepare_cb prepare_cb; + +#define UV_CHECK_PRIVATE_FIELDS \ + uv_check_t* check_prev; \ + uv_check_t* check_next; \ + uv_check_cb check_cb; + +#define UV_IDLE_PRIVATE_FIELDS \ + uv_idle_t* idle_prev; \ + uv_idle_t* idle_next; \ + uv_idle_cb idle_cb; + +#define UV_HANDLE_PRIVATE_FIELDS \ + uv_handle_t* endgame_next; \ + unsigned int flags; + +#define UV_GETADDRINFO_PRIVATE_FIELDS \ + struct uv__work work_req; \ + uv_getaddrinfo_cb getaddrinfo_cb; \ + void* alloc; \ + WCHAR* node; \ + WCHAR* service; \ + /* The addrinfoW field is used to store a pointer to the hints, and */ \ + /* later on to store the result of GetAddrInfoW. The final result will */ \ + /* be converted to struct addrinfo* and stored in the addrinfo field. */ \ + struct addrinfoW* addrinfow; \ + struct addrinfo* addrinfo; \ + int retcode; + +#define UV_GETNAMEINFO_PRIVATE_FIELDS \ + struct uv__work work_req; \ + uv_getnameinfo_cb getnameinfo_cb; \ + struct sockaddr_storage storage; \ + int flags; \ + char host[NI_MAXHOST]; \ + char service[NI_MAXSERV]; \ + int retcode; + +#define UV_PROCESS_PRIVATE_FIELDS \ + struct uv_process_exit_s { \ + UV_REQ_FIELDS \ + } exit_req; \ + BYTE* child_stdio_buffer; \ + int exit_signal; \ + HANDLE wait_handle; \ + HANDLE process_handle; \ + volatile char exit_cb_pending; + +#define UV_FS_PRIVATE_FIELDS \ + struct uv__work work_req; \ + int flags; \ + DWORD sys_errno_; \ + union { \ + /* TODO: remove me in 0.9. */ \ + WCHAR* pathw; \ + int fd; \ + } file; \ + union { \ + struct { \ + int mode; \ + WCHAR* new_pathw; \ + int file_flags; \ + int fd_out; \ + unsigned int nbufs; \ + uv_buf_t* bufs; \ + int64_t offset; \ + uv_buf_t bufsml[4]; \ + } info; \ + struct { \ + double atime; \ + double mtime; \ + } time; \ + } fs; + +#define UV_WORK_PRIVATE_FIELDS \ + struct uv__work work_req; + +#define UV_FS_EVENT_PRIVATE_FIELDS \ + struct uv_fs_event_req_s { \ + UV_REQ_FIELDS \ + } req; \ + HANDLE dir_handle; \ + int req_pending; \ + uv_fs_event_cb cb; \ + WCHAR* filew; \ + WCHAR* short_filew; \ + WCHAR* dirw; \ + char* buffer; + +#define UV_SIGNAL_PRIVATE_FIELDS \ + RB_ENTRY(uv_signal_s) tree_entry; \ + struct uv_req_s signal_req; \ + unsigned long pending_signum; + +#ifndef F_OK +#define F_OK 0 +#endif +#ifndef R_OK +#define R_OK 4 +#endif +#ifndef W_OK +#define W_OK 2 +#endif +#ifndef X_OK +#define X_OK 1 +#endif + +/* fs open() flags supported on this platform: */ +#define UV_FS_O_APPEND _O_APPEND +#define UV_FS_O_CREAT _O_CREAT +#define UV_FS_O_EXCL _O_EXCL +#define UV_FS_O_FILEMAP 0x20000000 +#define UV_FS_O_RANDOM _O_RANDOM +#define UV_FS_O_RDONLY _O_RDONLY +#define UV_FS_O_RDWR _O_RDWR +#define UV_FS_O_SEQUENTIAL _O_SEQUENTIAL +#define UV_FS_O_SHORT_LIVED _O_SHORT_LIVED +#define UV_FS_O_TEMPORARY _O_TEMPORARY +#define UV_FS_O_TRUNC _O_TRUNC +#define UV_FS_O_WRONLY _O_WRONLY + +/* fs open() flags supported on other platforms (or mapped on this platform): */ +#define UV_FS_O_DIRECT 0x02000000 /* FILE_FLAG_NO_BUFFERING */ +#define UV_FS_O_DIRECTORY 0 +#define UV_FS_O_DSYNC 0x04000000 /* FILE_FLAG_WRITE_THROUGH */ +#define UV_FS_O_EXLOCK 0x10000000 /* EXCLUSIVE SHARING MODE */ +#define UV_FS_O_NOATIME 0 +#define UV_FS_O_NOCTTY 0 +#define UV_FS_O_NOFOLLOW 0 +#define UV_FS_O_NONBLOCK 0 +#define UV_FS_O_SYMLINK 0 +#define UV_FS_O_SYNC 0x08000000 /* FILE_FLAG_WRITE_THROUGH */ diff --git a/include/libuv/src/fs-poll.c b/include/libuv/src/fs-poll.c index ee73d5a2e..89864e23f 100644 --- a/include/libuv/src/fs-poll.c +++ b/include/libuv/src/fs-poll.c @@ -22,12 +22,20 @@ #include "uv.h" #include "uv-common.h" +#ifdef _WIN32 +#include "win/internal.h" +#include "win/handle-inl.h" +#define uv__make_close_pending(h) uv_want_endgame((h)->loop, (h)) +#else +#include "unix/internal.h" +#endif + #include #include #include struct poll_ctx { - uv_fs_poll_t* parent_handle; /* NULL if parent has been stopped or closed */ + uv_fs_poll_t* parent_handle; int busy_polling; unsigned int interval; uint64_t start_time; @@ -36,6 +44,7 @@ struct poll_ctx { uv_timer_t timer_handle; uv_fs_t fs_req; /* TODO(bnoordhuis) mark fs_req internal */ uv_stat_t statbuf; + struct poll_ctx* previous; /* context from previous start()..stop() period */ char path[1]; /* variable length */ }; @@ -49,6 +58,7 @@ static uv_stat_t zero_statbuf; int uv_fs_poll_init(uv_loop_t* loop, uv_fs_poll_t* handle) { uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_POLL); + handle->poll_ctx = NULL; return 0; } @@ -62,7 +72,7 @@ int uv_fs_poll_start(uv_fs_poll_t* handle, size_t len; int err; - if (uv__is_active(handle)) + if (uv_is_active((uv_handle_t*)handle)) return 0; loop = handle->loop; @@ -83,13 +93,15 @@ int uv_fs_poll_start(uv_fs_poll_t* handle, if (err < 0) goto error; - ctx->timer_handle.flags |= UV__HANDLE_INTERNAL; + ctx->timer_handle.flags |= UV_HANDLE_INTERNAL; uv__handle_unref(&ctx->timer_handle); err = uv_fs_stat(loop, &ctx->fs_req, ctx->path, poll_cb); if (err < 0) goto error; + if (handle->poll_ctx != NULL) + ctx->previous = handle->poll_ctx; handle->poll_ctx = ctx; uv__handle_start(handle); @@ -104,19 +116,17 @@ int uv_fs_poll_start(uv_fs_poll_t* handle, int uv_fs_poll_stop(uv_fs_poll_t* handle) { struct poll_ctx* ctx; - if (!uv__is_active(handle)) + if (!uv_is_active((uv_handle_t*)handle)) return 0; ctx = handle->poll_ctx; assert(ctx != NULL); - assert(ctx->parent_handle != NULL); - ctx->parent_handle = NULL; - handle->poll_ctx = NULL; + assert(ctx->parent_handle == handle); /* Close the timer if it's active. If it's inactive, there's a stat request * in progress and poll_cb will take care of the cleanup. */ - if (uv__is_active(&ctx->timer_handle)) + if (uv_is_active((uv_handle_t*)&ctx->timer_handle)) uv_close((uv_handle_t*)&ctx->timer_handle, timer_close_cb); uv__handle_stop(handle); @@ -129,7 +139,7 @@ int uv_fs_poll_getpath(uv_fs_poll_t* handle, char* buffer, size_t* size) { struct poll_ctx* ctx; size_t required_len; - if (!uv__is_active(handle)) { + if (!uv_is_active((uv_handle_t*)handle)) { *size = 0; return UV_EINVAL; } @@ -153,6 +163,9 @@ int uv_fs_poll_getpath(uv_fs_poll_t* handle, char* buffer, size_t* size) { void uv__fs_poll_close(uv_fs_poll_t* handle) { uv_fs_poll_stop(handle); + + if (handle->poll_ctx == NULL) + uv__make_close_pending((uv_handle_t*)handle); } @@ -173,14 +186,13 @@ static void poll_cb(uv_fs_t* req) { uv_stat_t* statbuf; struct poll_ctx* ctx; uint64_t interval; + uv_fs_poll_t* handle; ctx = container_of(req, struct poll_ctx, fs_req); + handle = ctx->parent_handle; - if (ctx->parent_handle == NULL) { /* handle has been stopped or closed */ - uv_close((uv_handle_t*)&ctx->timer_handle, timer_close_cb); - uv_fs_req_cleanup(req); - return; - } + if (!uv_is_active((uv_handle_t*)handle) || uv__is_closing(handle)) + goto out; if (req->result != 0) { if (ctx->busy_polling != req->result) { @@ -205,7 +217,7 @@ static void poll_cb(uv_fs_t* req) { out: uv_fs_req_cleanup(req); - if (ctx->parent_handle == NULL) { /* handle has been stopped by callback */ + if (!uv_is_active((uv_handle_t*)handle) || uv__is_closing(handle)) { uv_close((uv_handle_t*)&ctx->timer_handle, timer_close_cb); return; } @@ -219,8 +231,27 @@ static void poll_cb(uv_fs_t* req) { } -static void timer_close_cb(uv_handle_t* handle) { - uv__free(container_of(handle, struct poll_ctx, timer_handle)); +static void timer_close_cb(uv_handle_t* timer) { + struct poll_ctx* ctx; + struct poll_ctx* it; + struct poll_ctx* last; + uv_fs_poll_t* handle; + + ctx = container_of(timer, struct poll_ctx, timer_handle); + handle = ctx->parent_handle; + if (ctx == handle->poll_ctx) { + handle->poll_ctx = ctx->previous; + if (handle->poll_ctx == NULL && uv__is_closing(handle)) + uv__make_close_pending((uv_handle_t*)handle); + } else { + for (last = handle->poll_ctx, it = last->previous; + it != ctx; + last = it, it = it->previous) { + assert(last->previous != NULL); + } + last->previous = ctx->previous; + } + uv__free(ctx); } @@ -248,7 +279,7 @@ static int statbuf_eq(const uv_stat_t* a, const uv_stat_t* b) { #include "win/handle-inl.h" void uv__fs_poll_endgame(uv_loop_t* loop, uv_fs_poll_t* handle) { - assert(handle->flags & UV__HANDLE_CLOSING); + assert(handle->flags & UV_HANDLE_CLOSING); assert(!(handle->flags & UV_HANDLE_CLOSED)); uv__handle_close(handle); } diff --git a/include/libuv/src/idna.c b/include/libuv/src/idna.c new file mode 100644 index 000000000..13ffac6be --- /dev/null +++ b/include/libuv/src/idna.c @@ -0,0 +1,291 @@ +/* Copyright (c) 2011, 2018 Ben Noordhuis + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* Derived from https://github.com/bnoordhuis/punycode + * but updated to support IDNA 2008. + */ + +#include "uv.h" +#include "idna.h" +#include + +static unsigned uv__utf8_decode1_slow(const char** p, + const char* pe, + unsigned a) { + unsigned b; + unsigned c; + unsigned d; + unsigned min; + + if (a > 0xF7) + return -1; + + switch (*p - pe) { + default: + if (a > 0xEF) { + min = 0x10000; + a = a & 7; + b = (unsigned char) *(*p)++; + c = (unsigned char) *(*p)++; + d = (unsigned char) *(*p)++; + break; + } + /* Fall through. */ + case 2: + if (a > 0xDF) { + min = 0x800; + b = 0x80 | (a & 15); + c = (unsigned char) *(*p)++; + d = (unsigned char) *(*p)++; + a = 0; + break; + } + /* Fall through. */ + case 1: + if (a > 0xBF) { + min = 0x80; + b = 0x80; + c = 0x80 | (a & 31); + d = (unsigned char) *(*p)++; + a = 0; + break; + } + return -1; /* Invalid continuation byte. */ + } + + if (0x80 != (0xC0 & (b ^ c ^ d))) + return -1; /* Invalid sequence. */ + + b &= 63; + c &= 63; + d &= 63; + a = (a << 18) | (b << 12) | (c << 6) | d; + + if (a < min) + return -1; /* Overlong sequence. */ + + if (a > 0x10FFFF) + return -1; /* Four-byte sequence > U+10FFFF. */ + + if (a >= 0xD800 && a <= 0xDFFF) + return -1; /* Surrogate pair. */ + + return a; +} + +unsigned uv__utf8_decode1(const char** p, const char* pe) { + unsigned a; + + a = (unsigned char) *(*p)++; + + if (a < 128) + return a; /* ASCII, common case. */ + + return uv__utf8_decode1_slow(p, pe, a); +} + +#define foreach_codepoint(c, p, pe) \ + for (; (void) (*p <= pe && (c = uv__utf8_decode1(p, pe))), *p <= pe;) + +static int uv__idna_toascii_label(const char* s, const char* se, + char** d, char* de) { + static const char alphabet[] = "abcdefghijklmnopqrstuvwxyz0123456789"; + const char* ss; + unsigned c; + unsigned h; + unsigned k; + unsigned n; + unsigned m; + unsigned q; + unsigned t; + unsigned x; + unsigned y; + unsigned bias; + unsigned delta; + unsigned todo; + int first; + + h = 0; + ss = s; + todo = 0; + + foreach_codepoint(c, &s, se) { + if (c < 128) + h++; + else if (c == (unsigned) -1) + return UV_EINVAL; + else + todo++; + } + + if (todo > 0) { + if (*d < de) *(*d)++ = 'x'; + if (*d < de) *(*d)++ = 'n'; + if (*d < de) *(*d)++ = '-'; + if (*d < de) *(*d)++ = '-'; + } + + x = 0; + s = ss; + foreach_codepoint(c, &s, se) { + if (c > 127) + continue; + + if (*d < de) + *(*d)++ = c; + + if (++x == h) + break; /* Visited all ASCII characters. */ + } + + if (todo == 0) + return h; + + /* Only write separator when we've written ASCII characters first. */ + if (h > 0) + if (*d < de) + *(*d)++ = '-'; + + n = 128; + bias = 72; + delta = 0; + first = 1; + + while (todo > 0) { + m = -1; + s = ss; + foreach_codepoint(c, &s, se) + if (c >= n) + if (c < m) + m = c; + + x = m - n; + y = h + 1; + + if (x > ~delta / y) + return UV_E2BIG; /* Overflow. */ + + delta += x * y; + n = m; + + s = ss; + foreach_codepoint(c, &s, se) { + if (c < n) + if (++delta == 0) + return UV_E2BIG; /* Overflow. */ + + if (c != n) + continue; + + for (k = 36, q = delta; /* empty */; k += 36) { + t = 1; + + if (k > bias) + t = k - bias; + + if (t > 26) + t = 26; + + if (q < t) + break; + + /* TODO(bnoordhuis) Since 1 <= t <= 26 and therefore + * 10 <= y <= 35, we can optimize the long division + * into a table-based reciprocal multiplication. + */ + x = q - t; + y = 36 - t; /* 10 <= y <= 35 since 1 <= t <= 26. */ + q = x / y; + t = t + x % y; /* 1 <= t <= 35 because of y. */ + + if (*d < de) + *(*d)++ = alphabet[t]; + } + + if (*d < de) + *(*d)++ = alphabet[q]; + + delta /= 2; + + if (first) { + delta /= 350; + first = 0; + } + + /* No overflow check is needed because |delta| was just + * divided by 2 and |delta+delta >= delta + delta/h|. + */ + h++; + delta += delta / h; + + for (bias = 0; delta > 35 * 26 / 2; bias += 36) + delta /= 35; + + bias += 36 * delta / (delta + 38); + delta = 0; + todo--; + } + + delta++; + n++; + } + + return 0; +} + +#undef foreach_codepoint + +long uv__idna_toascii(const char* s, const char* se, char* d, char* de) { + const char* si; + const char* st; + unsigned c; + char* ds; + int rc; + + ds = d; + + for (si = s; si < se; /* empty */) { + st = si; + c = uv__utf8_decode1(&si, se); + + if (c != '.') + if (c != 0x3002) /* 。 */ + if (c != 0xFF0E) /* . */ + if (c != 0xFF61) /* 。 */ + continue; + + rc = uv__idna_toascii_label(s, st, &d, de); + + if (rc < 0) + return rc; + + if (d < de) + *d++ = '.'; + + s = si; + } + + if (s < se) { + rc = uv__idna_toascii_label(s, se, &d, de); + + if (rc < 0) + return rc; + } + + if (d < de) + *d++ = '\0'; + + return d - ds; /* Number of bytes written. */ +} diff --git a/include/libuv/src/idna.h b/include/libuv/src/idna.h new file mode 100644 index 000000000..8e0c592fe --- /dev/null +++ b/include/libuv/src/idna.h @@ -0,0 +1,31 @@ +/* Copyright (c) 2011, 2018 Ben Noordhuis + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef UV_SRC_IDNA_H_ +#define UV_SRC_IDNA_H_ + +/* Decode a single codepoint. Returns the codepoint or UINT32_MAX on error. + * |p| is updated on success _and_ error, i.e., bad multi-byte sequences are + * skipped in their entirety, not just the first bad byte. + */ +unsigned uv__utf8_decode1(const char** p, const char* pe); + +/* Convert a UTF-8 domain name to IDNA 2008 / Punycode. A return value >= 0 + * is the number of bytes written to |d|, including the trailing nul byte. + * A return value < 0 is a libuv error code. |s| and |d| can not overlap. + */ +long uv__idna_toascii(const char* s, const char* se, char* d, char* de); + +#endif /* UV_SRC_IDNA_H_ */ diff --git a/include/libuv/src/inet.c b/include/libuv/src/inet.c index da63a688c..698ab232e 100644 --- a/include/libuv/src/inet.c +++ b/include/libuv/src/inet.c @@ -19,7 +19,7 @@ #include #if defined(_MSC_VER) && _MSC_VER < 1600 -# include "stdint-msvc2008.h" +# include "uv/stdint-msvc2008.h" #else # include #endif @@ -59,8 +59,7 @@ static int inet_ntop4(const unsigned char *src, char *dst, size_t size) { if (l <= 0 || (size_t) l >= size) { return UV_ENOSPC; } - strncpy(dst, tmp, size); - dst[size - 1] = '\0'; + uv__strscpy(dst, tmp, size); return 0; } @@ -142,14 +141,8 @@ static int inet_ntop6(const unsigned char *src, char *dst, size_t size) { if (best.base != -1 && (best.base + best.len) == ARRAY_SIZE(words)) *tp++ = ':'; *tp++ = '\0'; - - /* - * Check for overflow, copy, and we're done. - */ - if ((size_t)(tp - tmp) > size) { + if (UV_E2BIG == uv__strscpy(dst, tmp, size)) return UV_ENOSPC; - } - strcpy(dst, tmp); return 0; } diff --git a/include/libuv/src/random.c b/include/libuv/src/random.c new file mode 100644 index 000000000..e75f77deb --- /dev/null +++ b/include/libuv/src/random.c @@ -0,0 +1,123 @@ +/* Copyright libuv contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "uv-common.h" + +#ifdef _WIN32 +# include "win/internal.h" +#else +# include "unix/internal.h" +#endif + +static int uv__random(void* buf, size_t buflen) { + int rc; + +#if defined(__PASE__) + rc = uv__random_readpath("/dev/urandom", buf, buflen); +#elif defined(_AIX) || defined(__QNX__) + rc = uv__random_readpath("/dev/random", buf, buflen); +#elif defined(__APPLE__) || defined(__OpenBSD__) || \ + (defined(__ANDROID_API__) && __ANDROID_API__ >= 28) + rc = uv__random_getentropy(buf, buflen); + if (rc == UV_ENOSYS) + rc = uv__random_devurandom(buf, buflen); +#elif defined(__NetBSD__) + rc = uv__random_sysctl(buf, buflen); +#elif defined(__FreeBSD__) || defined(__linux__) + rc = uv__random_getrandom(buf, buflen); + if (rc == UV_ENOSYS) + rc = uv__random_devurandom(buf, buflen); +# if defined(__linux__) + switch (rc) { + case UV_EACCES: + case UV_EIO: + case UV_ELOOP: + case UV_EMFILE: + case UV_ENFILE: + case UV_ENOENT: + case UV_EPERM: + rc = uv__random_sysctl(buf, buflen); + break; + } +# endif +#elif defined(_WIN32) + uv__once_init(); + rc = uv__random_rtlgenrandom(buf, buflen); +#else + rc = uv__random_devurandom(buf, buflen); +#endif + + return rc; +} + + +static void uv__random_work(struct uv__work* w) { + uv_random_t* req; + + req = container_of(w, uv_random_t, work_req); + req->status = uv__random(req->buf, req->buflen); +} + + +static void uv__random_done(struct uv__work* w, int status) { + uv_random_t* req; + + req = container_of(w, uv_random_t, work_req); + uv__req_unregister(req->loop, req); + + if (status == 0) + status = req->status; + + req->cb(req, status, req->buf, req->buflen); +} + + +int uv_random(uv_loop_t* loop, + uv_random_t* req, + void *buf, + size_t buflen, + unsigned flags, + uv_random_cb cb) { + if (buflen > 0x7FFFFFFFu) + return UV_E2BIG; + + if (flags != 0) + return UV_EINVAL; + + if (cb == NULL) + return uv__random(buf, buflen); + + uv__req_init(loop, req, UV_RANDOM); + req->loop = loop; + req->status = 0; + req->cb = cb; + req->buf = buf; + req->buflen = buflen; + + uv__work_submit(loop, + &req->work_req, + UV__WORK_CPU, + uv__random_work, + uv__random_done); + + return 0; +} diff --git a/include/libuv/src/strscpy.c b/include/libuv/src/strscpy.c new file mode 100644 index 000000000..20df6fcbe --- /dev/null +++ b/include/libuv/src/strscpy.c @@ -0,0 +1,38 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "strscpy.h" +#include /* SSIZE_MAX */ + +ssize_t uv__strscpy(char* d, const char* s, size_t n) { + size_t i; + + for (i = 0; i < n; i++) + if ('\0' == (d[i] = s[i])) + return i > SSIZE_MAX ? UV_E2BIG : (ssize_t) i; + + if (i == 0) + return 0; + + d[--i] = '\0'; + + return UV_E2BIG; +} diff --git a/include/libuv/src/strscpy.h b/include/libuv/src/strscpy.h new file mode 100644 index 000000000..cc78149db --- /dev/null +++ b/include/libuv/src/strscpy.h @@ -0,0 +1,39 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef UV_STRSCPY_H_ +#define UV_STRSCPY_H_ + +/* Include uv.h for its definitions of size_t and ssize_t. + * size_t can be obtained directly from but ssize_t requires + * some hoop jumping on Windows that I didn't want to duplicate here. + */ +#include "uv.h" + +/* Copies up to |n-1| bytes from |d| to |s| and always zero-terminates + * the result, except when |n==0|. Returns the number of bytes copied + * or UV_E2BIG if |d| is too small. + * + * See https://www.kernel.org/doc/htmldocs/kernel-api/API-strscpy.html + */ +ssize_t uv__strscpy(char* d, const char* s, size_t n); + +#endif /* UV_STRSCPY_H_ */ diff --git a/include/libuv/src/threadpool.c b/include/libuv/src/threadpool.c index 2c5152b42..0998938f3 100644 --- a/include/libuv/src/threadpool.c +++ b/include/libuv/src/threadpool.c @@ -23,35 +23,28 @@ #if !defined(_WIN32) # include "unix/internal.h" -#else -# include "win/req-inl.h" -/* TODO(saghul): unify internal req functions */ -static void uv__req_init(uv_loop_t* loop, - uv_req_t* req, - uv_req_type type) { - uv_req_init(loop, req); - req->type = type; - uv__req_register(loop, req); -} -# define uv__req_init(loop, req, type) \ - uv__req_init((loop), (uv_req_t*)(req), (type)) #endif #include -#define MAX_THREADPOOL_SIZE 128 +#define MAX_THREADPOOL_SIZE 1024 static uv_once_t once = UV_ONCE_INIT; static uv_cond_t cond; static uv_mutex_t mutex; static unsigned int idle_threads; +static unsigned int slow_io_work_running; static unsigned int nthreads; static uv_thread_t* threads; static uv_thread_t default_threads[4]; static QUEUE exit_message; static QUEUE wq; -static volatile int initialized; +static QUEUE run_slow_work_message; +static QUEUE slow_io_pending_wq; +static unsigned int slow_work_thread_threshold(void) { + return (nthreads + 1) / 2; +} static void uv__cancelled(struct uv__work* w) { abort(); @@ -64,33 +57,67 @@ static void uv__cancelled(struct uv__work* w) { static void worker(void* arg) { struct uv__work* w; QUEUE* q; + int is_slow_work; - (void) arg; + uv_sem_post((uv_sem_t*) arg); + arg = NULL; + uv_mutex_lock(&mutex); for (;;) { - uv_mutex_lock(&mutex); - - while (QUEUE_EMPTY(&wq)) { + /* `mutex` should always be locked at this point. */ + + /* Keep waiting while either no work is present or only slow I/O + and we're at the threshold for that. */ + while (QUEUE_EMPTY(&wq) || + (QUEUE_HEAD(&wq) == &run_slow_work_message && + QUEUE_NEXT(&run_slow_work_message) == &wq && + slow_io_work_running >= slow_work_thread_threshold())) { idle_threads += 1; uv_cond_wait(&cond, &mutex); idle_threads -= 1; } q = QUEUE_HEAD(&wq); - - if (q == &exit_message) + if (q == &exit_message) { uv_cond_signal(&cond); - else { + uv_mutex_unlock(&mutex); + break; + } + + QUEUE_REMOVE(q); + QUEUE_INIT(q); /* Signal uv_cancel() that the work req is executing. */ + + is_slow_work = 0; + if (q == &run_slow_work_message) { + /* If we're at the slow I/O threshold, re-schedule until after all + other work in the queue is done. */ + if (slow_io_work_running >= slow_work_thread_threshold()) { + QUEUE_INSERT_TAIL(&wq, q); + continue; + } + + /* If we encountered a request to run slow I/O work but there is none + to run, that means it's cancelled => Start over. */ + if (QUEUE_EMPTY(&slow_io_pending_wq)) + continue; + + is_slow_work = 1; + slow_io_work_running++; + + q = QUEUE_HEAD(&slow_io_pending_wq); QUEUE_REMOVE(q); - QUEUE_INIT(q); /* Signal uv_cancel() that the work req is - executing. */ + QUEUE_INIT(q); + + /* If there is more slow I/O work, schedule it to be run as well. */ + if (!QUEUE_EMPTY(&slow_io_pending_wq)) { + QUEUE_INSERT_TAIL(&wq, &run_slow_work_message); + if (idle_threads > 0) + uv_cond_signal(&cond); + } } uv_mutex_unlock(&mutex); - if (q == &exit_message) - break; - w = QUEUE_DATA(q, struct uv__work, wq); w->work(w); @@ -100,12 +127,32 @@ static void worker(void* arg) { QUEUE_INSERT_TAIL(&w->loop->wq, &w->wq); uv_async_send(&w->loop->wq_async); uv_mutex_unlock(&w->loop->wq_mutex); + + /* Lock `mutex` since that is expected at the start of the next + * iteration. */ + uv_mutex_lock(&mutex); + if (is_slow_work) { + /* `slow_io_work_running` is protected by `mutex`. */ + slow_io_work_running--; + } } } -static void post(QUEUE* q) { +static void post(QUEUE* q, enum uv__work_kind kind) { uv_mutex_lock(&mutex); + if (kind == UV__WORK_SLOW_IO) { + /* Insert into a separate queue. */ + QUEUE_INSERT_TAIL(&slow_io_pending_wq, q); + if (!QUEUE_EMPTY(&run_slow_work_message)) { + /* Running slow I/O tasks is already scheduled => Nothing to do here. + The worker that runs said other task will schedule this one as well. */ + uv_mutex_unlock(&mutex); + return; + } + q = &run_slow_work_message; + } + QUEUE_INSERT_TAIL(&wq, q); if (idle_threads > 0) uv_cond_signal(&cond); @@ -113,14 +160,14 @@ static void post(QUEUE* q) { } +void uv__threadpool_cleanup(void) { #ifndef _WIN32 -UV_DESTRUCTOR(static void cleanup(void)) { unsigned int i; - if (initialized == 0) + if (nthreads == 0) return; - post(&exit_message); + post(&exit_message, UV__WORK_CPU); for (i = 0; i < nthreads; i++) if (uv_thread_join(threads + i)) @@ -134,14 +181,14 @@ UV_DESTRUCTOR(static void cleanup(void)) { threads = NULL; nthreads = 0; - initialized = 0; -} #endif +} -static void init_once(void) { +static void init_threads(void) { unsigned int i; const char* val; + uv_sem_t sem; nthreads = ARRAY_SIZE(default_threads); val = getenv("UV_THREADPOOL_SIZE"); @@ -168,24 +215,54 @@ static void init_once(void) { abort(); QUEUE_INIT(&wq); + QUEUE_INIT(&slow_io_pending_wq); + QUEUE_INIT(&run_slow_work_message); + + if (uv_sem_init(&sem, 0)) + abort(); for (i = 0; i < nthreads; i++) - if (uv_thread_create(threads + i, worker, NULL)) + if (uv_thread_create(threads + i, worker, &sem)) abort(); - initialized = 1; + for (i = 0; i < nthreads; i++) + uv_sem_wait(&sem); + + uv_sem_destroy(&sem); +} + + +#ifndef _WIN32 +static void reset_once(void) { + uv_once_t child_once = UV_ONCE_INIT; + memcpy(&once, &child_once, sizeof(child_once)); +} +#endif + + +static void init_once(void) { +#ifndef _WIN32 + /* Re-initialize the threadpool after fork. + * Note that this discards the global mutex and condition as well + * as the work queue. + */ + if (pthread_atfork(NULL, NULL, &reset_once)) + abort(); +#endif + init_threads(); } void uv__work_submit(uv_loop_t* loop, struct uv__work* w, + enum uv__work_kind kind, void (*work)(struct uv__work* w), void (*done)(struct uv__work* w, int status)) { uv_once(&once, init_once); w->loop = loop; w->work = work; w->done = done; - post(&w->wq); + post(&w->wq, kind); } @@ -269,7 +346,11 @@ int uv_queue_work(uv_loop_t* loop, req->loop = loop; req->work_cb = work_cb; req->after_work_cb = after_work_cb; - uv__work_submit(loop, &req->work_req, uv__queue_work, uv__queue_done); + uv__work_submit(loop, + &req->work_req, + UV__WORK_CPU, + uv__queue_work, + uv__queue_done); return 0; } @@ -291,6 +372,10 @@ int uv_cancel(uv_req_t* req) { loop = ((uv_getnameinfo_t*) req)->loop; wreq = &((uv_getnameinfo_t*) req)->work_req; break; + case UV_RANDOM: + loop = ((uv_random_t*) req)->loop; + wreq = &((uv_random_t*) req)->work_req; + break; case UV_WORK: loop = ((uv_work_t*) req)->loop; wreq = &((uv_work_t*) req)->work_req; diff --git a/include/libuv/src/timer.c b/include/libuv/src/timer.c new file mode 100644 index 000000000..1bea2a8bd --- /dev/null +++ b/include/libuv/src/timer.c @@ -0,0 +1,184 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "uv-common.h" +#include "heap-inl.h" + +#include +#include + + +static struct heap *timer_heap(const uv_loop_t* loop) { +#ifdef _WIN32 + return (struct heap*) loop->timer_heap; +#else + return (struct heap*) &loop->timer_heap; +#endif +} + + +static int timer_less_than(const struct heap_node* ha, + const struct heap_node* hb) { + const uv_timer_t* a; + const uv_timer_t* b; + + a = container_of(ha, uv_timer_t, heap_node); + b = container_of(hb, uv_timer_t, heap_node); + + if (a->timeout < b->timeout) + return 1; + if (b->timeout < a->timeout) + return 0; + + /* Compare start_id when both have the same timeout. start_id is + * allocated with loop->timer_counter in uv_timer_start(). + */ + return a->start_id < b->start_id; +} + + +int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle) { + uv__handle_init(loop, (uv_handle_t*)handle, UV_TIMER); + handle->timer_cb = NULL; + handle->repeat = 0; + return 0; +} + + +int uv_timer_start(uv_timer_t* handle, + uv_timer_cb cb, + uint64_t timeout, + uint64_t repeat) { + uint64_t clamped_timeout; + + if (uv__is_closing(handle) || cb == NULL) + return UV_EINVAL; + + if (uv__is_active(handle)) + uv_timer_stop(handle); + + clamped_timeout = handle->loop->time + timeout; + if (clamped_timeout < timeout) + clamped_timeout = (uint64_t) -1; + + handle->timer_cb = cb; + handle->timeout = clamped_timeout; + handle->repeat = repeat; + /* start_id is the second index to be compared in timer_less_than() */ + handle->start_id = handle->loop->timer_counter++; + + heap_insert(timer_heap(handle->loop), + (struct heap_node*) &handle->heap_node, + timer_less_than); + uv__handle_start(handle); + + return 0; +} + + +int uv_timer_stop(uv_timer_t* handle) { + if (!uv__is_active(handle)) + return 0; + + heap_remove(timer_heap(handle->loop), + (struct heap_node*) &handle->heap_node, + timer_less_than); + uv__handle_stop(handle); + + return 0; +} + + +int uv_timer_again(uv_timer_t* handle) { + if (handle->timer_cb == NULL) + return UV_EINVAL; + + if (handle->repeat) { + uv_timer_stop(handle); + uv_timer_start(handle, handle->timer_cb, handle->repeat, handle->repeat); + } + + return 0; +} + + +void uv_timer_set_repeat(uv_timer_t* handle, uint64_t repeat) { + handle->repeat = repeat; +} + + +uint64_t uv_timer_get_repeat(const uv_timer_t* handle) { + return handle->repeat; +} + + +uint64_t uv_timer_get_due_in(const uv_timer_t* handle) { + if (handle->loop->time >= handle->timeout) + return 0; + + return handle->timeout - handle->loop->time; +} + + +int uv__next_timeout(const uv_loop_t* loop) { + const struct heap_node* heap_node; + const uv_timer_t* handle; + uint64_t diff; + + heap_node = heap_min(timer_heap(loop)); + if (heap_node == NULL) + return -1; /* block indefinitely */ + + handle = container_of(heap_node, uv_timer_t, heap_node); + if (handle->timeout <= loop->time) + return 0; + + diff = handle->timeout - loop->time; + if (diff > INT_MAX) + diff = INT_MAX; + + return (int) diff; +} + + +void uv__run_timers(uv_loop_t* loop) { + struct heap_node* heap_node; + uv_timer_t* handle; + + for (;;) { + heap_node = heap_min(timer_heap(loop)); + if (heap_node == NULL) + break; + + handle = container_of(heap_node, uv_timer_t, heap_node); + if (handle->timeout > loop->time) + break; + + uv_timer_stop(handle); + uv_timer_again(handle); + handle->timer_cb(handle); + } +} + + +void uv__timer_close(uv_timer_t* handle) { + uv_timer_stop(handle); +} diff --git a/include/libuv/src/unix/aix-common.c b/include/libuv/src/unix/aix-common.c new file mode 100644 index 000000000..abc4c901a --- /dev/null +++ b/include/libuv/src/unix/aix-common.c @@ -0,0 +1,89 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include + +#include + +#include +#include + +#include + +#include + +extern char* original_exepath; +extern uv_mutex_t process_title_mutex; +extern uv_once_t process_title_mutex_once; +extern void init_process_title_mutex_once(void); + +uint64_t uv__hrtime(uv_clocktype_t type) { + uint64_t G = 1000000000; + timebasestruct_t t; + read_wall_time(&t, TIMEBASE_SZ); + time_base_to_time(&t, TIMEBASE_SZ); + return (uint64_t) t.tb_high * G + t.tb_low; +} + + +/* + * We could use a static buffer for the path manipulations that we need outside + * of the function, but this function could be called by multiple consumers and + * we don't want to potentially create a race condition in the use of snprintf. + * There is no direct way of getting the exe path in AIX - either through /procfs + * or through some libc APIs. The below approach is to parse the argv[0]'s pattern + * and use it in conjunction with PATH environment variable to craft one. + */ +int uv_exepath(char* buffer, size_t* size) { + int res; + char args[UV__PATH_MAX]; + size_t cached_len; + struct procsinfo pi; + + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + + uv_once(&process_title_mutex_once, init_process_title_mutex_once); + uv_mutex_lock(&process_title_mutex); + if (original_exepath != NULL) { + cached_len = strlen(original_exepath); + *size -= 1; + if (*size > cached_len) + *size = cached_len; + memcpy(buffer, original_exepath, *size); + buffer[*size] = '\0'; + uv_mutex_unlock(&process_title_mutex); + return 0; + } + uv_mutex_unlock(&process_title_mutex); + pi.pi_pid = getpid(); + res = getargs(&pi, sizeof(pi), args, sizeof(args)); + + if (res < 0) + return UV_EINVAL; + + return uv__search_path(args, buffer, size); +} diff --git a/include/libuv/src/unix/aix.c b/include/libuv/src/unix/aix.c new file mode 100644 index 000000000..6a013d43e --- /dev/null +++ b/include/libuv/src/unix/aix.c @@ -0,0 +1,1304 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#ifdef HAVE_SYS_AHAFS_EVPRODS_H +#include +#endif + +#include +#include +#include +#include +#include + +#define RDWR_BUF_SIZE 4096 +#define EQ(a,b) (strcmp(a,b) == 0) + +char* original_exepath = NULL; +uv_mutex_t process_title_mutex; +uv_once_t process_title_mutex_once = UV_ONCE_INIT; +static void* args_mem = NULL; +static char** process_argv = NULL; +static int process_argc = 0; +static char* process_title_ptr = NULL; + +void init_process_title_mutex_once(void) { + uv_mutex_init(&process_title_mutex); +} + + +int uv__platform_loop_init(uv_loop_t* loop) { + loop->fs_fd = -1; + + /* Passing maxfd of -1 should mean the limit is determined + * by the user's ulimit or the global limit as per the doc */ + loop->backend_fd = pollset_create(-1); + + if (loop->backend_fd == -1) + return -1; + + return 0; +} + + +void uv__platform_loop_delete(uv_loop_t* loop) { + if (loop->fs_fd != -1) { + uv__close(loop->fs_fd); + loop->fs_fd = -1; + } + + if (loop->backend_fd != -1) { + pollset_destroy(loop->backend_fd); + loop->backend_fd = -1; + } +} + + +int uv__io_fork(uv_loop_t* loop) { + uv__platform_loop_delete(loop); + + return uv__platform_loop_init(loop); +} + + +int uv__io_check_fd(uv_loop_t* loop, int fd) { + struct poll_ctl pc; + + pc.events = POLLIN; + pc.cmd = PS_MOD; /* Equivalent to PS_ADD if the fd is not in the pollset. */ + pc.fd = fd; + + if (pollset_ctl(loop->backend_fd, &pc, 1)) + return UV__ERR(errno); + + pc.cmd = PS_DELETE; + if (pollset_ctl(loop->backend_fd, &pc, 1)) + abort(); + + return 0; +} + + +void uv__io_poll(uv_loop_t* loop, int timeout) { + struct pollfd events[1024]; + struct pollfd pqry; + struct pollfd* pe; + struct poll_ctl pc; + QUEUE* q; + uv__io_t* w; + uint64_t base; + uint64_t diff; + int have_signals; + int nevents; + int count; + int nfds; + int i; + int rc; + int add_failed; + int user_timeout; + int reset_timeout; + + if (loop->nfds == 0) { + assert(QUEUE_EMPTY(&loop->watcher_queue)); + return; + } + + while (!QUEUE_EMPTY(&loop->watcher_queue)) { + q = QUEUE_HEAD(&loop->watcher_queue); + QUEUE_REMOVE(q); + QUEUE_INIT(q); + + w = QUEUE_DATA(q, uv__io_t, watcher_queue); + assert(w->pevents != 0); + assert(w->fd >= 0); + assert(w->fd < (int) loop->nwatchers); + + pc.events = w->pevents; + pc.fd = w->fd; + + add_failed = 0; + if (w->events == 0) { + pc.cmd = PS_ADD; + if (pollset_ctl(loop->backend_fd, &pc, 1)) { + if (errno != EINVAL) { + assert(0 && "Failed to add file descriptor (pc.fd) to pollset"); + abort(); + } + /* Check if the fd is already in the pollset */ + pqry.fd = pc.fd; + rc = pollset_query(loop->backend_fd, &pqry); + switch (rc) { + case -1: + assert(0 && "Failed to query pollset for file descriptor"); + abort(); + case 0: + assert(0 && "Pollset does not contain file descriptor"); + abort(); + } + /* If we got here then the pollset already contained the file descriptor even though + * we didn't think it should. This probably shouldn't happen, but we can continue. */ + add_failed = 1; + } + } + if (w->events != 0 || add_failed) { + /* Modify, potentially removing events -- need to delete then add. + * Could maybe mod if we knew for sure no events are removed, but + * content of w->events is handled above as not reliable (falls back) + * so may require a pollset_query() which would have to be pretty cheap + * compared to a PS_DELETE to be worth optimizing. Alternatively, could + * lazily remove events, squelching them in the mean time. */ + pc.cmd = PS_DELETE; + if (pollset_ctl(loop->backend_fd, &pc, 1)) { + assert(0 && "Failed to delete file descriptor (pc.fd) from pollset"); + abort(); + } + pc.cmd = PS_ADD; + if (pollset_ctl(loop->backend_fd, &pc, 1)) { + assert(0 && "Failed to add file descriptor (pc.fd) to pollset"); + abort(); + } + } + + w->events = w->pevents; + } + + assert(timeout >= -1); + base = loop->time; + count = 48; /* Benchmarks suggest this gives the best throughput. */ + + if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { + reset_timeout = 1; + user_timeout = timeout; + timeout = 0; + } else { + reset_timeout = 0; + } + + for (;;) { + /* Only need to set the provider_entry_time if timeout != 0. The function + * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. + */ + if (timeout != 0) + uv__metrics_set_provider_entry_time(loop); + + nfds = pollset_poll(loop->backend_fd, + events, + ARRAY_SIZE(events), + timeout); + + /* Update loop->time unconditionally. It's tempting to skip the update when + * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the + * operating system didn't reschedule our process while in the syscall. + */ + SAVE_ERRNO(uv__update_time(loop)); + + if (nfds == 0) { + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + if (timeout == -1) + continue; + if (timeout > 0) + goto update_timeout; + } + + assert(timeout != -1); + return; + } + + if (nfds == -1) { + if (errno != EINTR) { + abort(); + } + + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + + if (timeout == -1) + continue; + + if (timeout == 0) + return; + + /* Interrupted by a signal. Update timeout and poll again. */ + goto update_timeout; + } + + have_signals = 0; + nevents = 0; + + assert(loop->watchers != NULL); + loop->watchers[loop->nwatchers] = (void*) events; + loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds; + + for (i = 0; i < nfds; i++) { + pe = events + i; + pc.cmd = PS_DELETE; + pc.fd = pe->fd; + + /* Skip invalidated events, see uv__platform_invalidate_fd */ + if (pc.fd == -1) + continue; + + assert(pc.fd >= 0); + assert((unsigned) pc.fd < loop->nwatchers); + + w = loop->watchers[pc.fd]; + + if (w == NULL) { + /* File descriptor that we've stopped watching, disarm it. + * + * Ignore all errors because we may be racing with another thread + * when the file descriptor is closed. + */ + pollset_ctl(loop->backend_fd, &pc, 1); + continue; + } + + /* Run signal watchers last. This also affects child process watchers + * because those are implemented in terms of signal watchers. + */ + if (w == &loop->signal_io_watcher) { + have_signals = 1; + } else { + uv__metrics_update_idle_time(loop); + w->cb(loop, w, pe->revents); + } + + nevents++; + } + + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + + if (have_signals != 0) { + uv__metrics_update_idle_time(loop); + loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN); + } + + loop->watchers[loop->nwatchers] = NULL; + loop->watchers[loop->nwatchers + 1] = NULL; + + if (have_signals != 0) + return; /* Event loop should cycle now so don't poll again. */ + + if (nevents != 0) { + if (nfds == ARRAY_SIZE(events) && --count != 0) { + /* Poll for more events but don't block this time. */ + timeout = 0; + continue; + } + return; + } + + if (timeout == 0) + return; + + if (timeout == -1) + continue; + +update_timeout: + assert(timeout > 0); + + diff = loop->time - base; + if (diff >= (uint64_t) timeout) + return; + + timeout -= diff; + } +} + + +uint64_t uv_get_free_memory(void) { + perfstat_memory_total_t mem_total; + int result = perfstat_memory_total(NULL, &mem_total, sizeof(mem_total), 1); + if (result == -1) { + return 0; + } + return mem_total.real_free * 4096; +} + + +uint64_t uv_get_total_memory(void) { + perfstat_memory_total_t mem_total; + int result = perfstat_memory_total(NULL, &mem_total, sizeof(mem_total), 1); + if (result == -1) { + return 0; + } + return mem_total.real_total * 4096; +} + + +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + +void uv_loadavg(double avg[3]) { + perfstat_cpu_total_t ps_total; + int result = perfstat_cpu_total(NULL, &ps_total, sizeof(ps_total), 1); + if (result == -1) { + avg[0] = 0.; avg[1] = 0.; avg[2] = 0.; + return; + } + avg[0] = ps_total.loadavg[0] / (double)(1 << SBITS); + avg[1] = ps_total.loadavg[1] / (double)(1 << SBITS); + avg[2] = ps_total.loadavg[2] / (double)(1 << SBITS); +} + + +#ifdef HAVE_SYS_AHAFS_EVPRODS_H +static char* uv__rawname(const char* cp, char (*dst)[FILENAME_MAX+1]) { + char* dp; + + dp = rindex(cp, '/'); + if (dp == 0) + return 0; + + snprintf(*dst, sizeof(*dst), "%.*s/r%s", (int) (dp - cp), cp, dp + 1); + return *dst; +} + + +/* + * Determine whether given pathname is a directory + * Returns 0 if the path is a directory, -1 if not + * + * Note: Opportunity here for more detailed error information but + * that requires changing callers of this function as well + */ +static int uv__path_is_a_directory(char* filename) { + struct stat statbuf; + + if (stat(filename, &statbuf) < 0) + return -1; /* failed: not a directory, assume it is a file */ + + if (statbuf.st_type == VDIR) + return 0; + + return -1; +} + + +/* + * Check whether AHAFS is mounted. + * Returns 0 if AHAFS is mounted, or an error code < 0 on failure + */ +static int uv__is_ahafs_mounted(void){ + char rawbuf[FILENAME_MAX+1]; + int rv, i = 2; + struct vmount *p; + int size_multiplier = 10; + size_t siz = sizeof(struct vmount)*size_multiplier; + struct vmount *vmt; + const char *dev = "/aha"; + char *obj, *stub; + + p = uv__malloc(siz); + if (p == NULL) + return UV__ERR(errno); + + /* Retrieve all mounted filesystems */ + rv = mntctl(MCTL_QUERY, siz, (char*)p); + if (rv < 0) + return UV__ERR(errno); + if (rv == 0) { + /* buffer was not large enough, reallocate to correct size */ + siz = *(int*)p; + uv__free(p); + p = uv__malloc(siz); + if (p == NULL) + return UV__ERR(errno); + rv = mntctl(MCTL_QUERY, siz, (char*)p); + if (rv < 0) + return UV__ERR(errno); + } + + /* Look for dev in filesystems mount info */ + for(vmt = p, i = 0; i < rv; i++) { + obj = vmt2dataptr(vmt, VMT_OBJECT); /* device */ + stub = vmt2dataptr(vmt, VMT_STUB); /* mount point */ + + if (EQ(obj, dev) || EQ(uv__rawname(obj, &rawbuf), dev) || EQ(stub, dev)) { + uv__free(p); /* Found a match */ + return 0; + } + vmt = (struct vmount *) ((char *) vmt + vmt->vmt_length); + } + + /* /aha is required for monitoring filesystem changes */ + return -1; +} + +/* + * Recursive call to mkdir() to create intermediate folders, if any + * Returns code from mkdir call + */ +static int uv__makedir_p(const char *dir) { + char tmp[256]; + char *p = NULL; + size_t len; + int err; + + /* TODO(bnoordhuis) Check uv__strscpy() return value. */ + uv__strscpy(tmp, dir, sizeof(tmp)); + len = strlen(tmp); + if (tmp[len - 1] == '/') + tmp[len - 1] = 0; + for (p = tmp + 1; *p; p++) { + if (*p == '/') { + *p = 0; + err = mkdir(tmp, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + if (err != 0 && errno != EEXIST) + return err; + *p = '/'; + } + } + return mkdir(tmp, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); +} + +/* + * Creates necessary subdirectories in the AIX Event Infrastructure + * file system for monitoring the object specified. + * Returns code from mkdir call + */ +static int uv__make_subdirs_p(const char *filename) { + char cmd[2048]; + char *p; + int rc = 0; + + /* Strip off the monitor file name */ + p = strrchr(filename, '/'); + + if (p == NULL) + return 0; + + if (uv__path_is_a_directory((char*)filename) == 0) { + sprintf(cmd, "/aha/fs/modDir.monFactory"); + } else { + sprintf(cmd, "/aha/fs/modFile.monFactory"); + } + + strncat(cmd, filename, (p - filename)); + rc = uv__makedir_p(cmd); + + if (rc == -1 && errno != EEXIST){ + return UV__ERR(errno); + } + + return rc; +} + + +/* + * Checks if /aha is mounted, then proceeds to set up the monitoring + * objects for the specified file. + * Returns 0 on success, or an error code < 0 on failure + */ +static int uv__setup_ahafs(const char* filename, int *fd) { + int rc = 0; + char mon_file_write_string[RDWR_BUF_SIZE]; + char mon_file[PATH_MAX]; + int file_is_directory = 0; /* -1 == NO, 0 == YES */ + + /* Create monitor file name for object */ + file_is_directory = uv__path_is_a_directory((char*)filename); + + if (file_is_directory == 0) + sprintf(mon_file, "/aha/fs/modDir.monFactory"); + else + sprintf(mon_file, "/aha/fs/modFile.monFactory"); + + if ((strlen(mon_file) + strlen(filename) + 5) > PATH_MAX) + return UV_ENAMETOOLONG; + + /* Make the necessary subdirectories for the monitor file */ + rc = uv__make_subdirs_p(filename); + if (rc == -1 && errno != EEXIST) + return rc; + + strcat(mon_file, filename); + strcat(mon_file, ".mon"); + + *fd = 0; errno = 0; + + /* Open the monitor file, creating it if necessary */ + *fd = open(mon_file, O_CREAT|O_RDWR); + if (*fd < 0) + return UV__ERR(errno); + + /* Write out the monitoring specifications. + * In this case, we are monitoring for a state change event type + * CHANGED=YES + * We will be waiting in select call, rather than a read: + * WAIT_TYPE=WAIT_IN_SELECT + * We only want minimal information for files: + * INFO_LVL=1 + * For directories, we want more information to track what file + * caused the change + * INFO_LVL=2 + */ + + if (file_is_directory == 0) + sprintf(mon_file_write_string, "CHANGED=YES;WAIT_TYPE=WAIT_IN_SELECT;INFO_LVL=2"); + else + sprintf(mon_file_write_string, "CHANGED=YES;WAIT_TYPE=WAIT_IN_SELECT;INFO_LVL=1"); + + rc = write(*fd, mon_file_write_string, strlen(mon_file_write_string)+1); + if (rc < 0 && errno != EBUSY) + return UV__ERR(errno); + + return 0; +} + +/* + * Skips a specified number of lines in the buffer passed in. + * Walks the buffer pointed to by p and attempts to skip n lines. + * Returns the total number of lines skipped + */ +static int uv__skip_lines(char **p, int n) { + int lines = 0; + + while(n > 0) { + *p = strchr(*p, '\n'); + if (!p) + return lines; + + (*p)++; + n--; + lines++; + } + return lines; +} + + +/* + * Parse the event occurrence data to figure out what event just occurred + * and take proper action. + * + * The buf is a pointer to the buffer containing the event occurrence data + * Returns 0 on success, -1 if unrecoverable error in parsing + * + */ +static int uv__parse_data(char *buf, int *events, uv_fs_event_t* handle) { + int evp_rc, i; + char *p; + char filename[PATH_MAX]; /* To be used when handling directories */ + + p = buf; + *events = 0; + + /* Clean the filename buffer*/ + for(i = 0; i < PATH_MAX; i++) { + filename[i] = 0; + } + i = 0; + + /* Check for BUF_WRAP */ + if (strncmp(buf, "BUF_WRAP", strlen("BUF_WRAP")) == 0) { + assert(0 && "Buffer wrap detected, Some event occurrences lost!"); + return 0; + } + + /* Since we are using the default buffer size (4K), and have specified + * INFO_LVL=1, we won't see any EVENT_OVERFLOW conditions. Applications + * should check for this keyword if they are using an INFO_LVL of 2 or + * higher, and have a buffer size of <= 4K + */ + + /* Skip to RC_FROM_EVPROD */ + if (uv__skip_lines(&p, 9) != 9) + return -1; + + if (sscanf(p, "RC_FROM_EVPROD=%d\nEND_EVENT_DATA", &evp_rc) == 1) { + if (uv__path_is_a_directory(handle->path) == 0) { /* Directory */ + if (evp_rc == AHAFS_MODDIR_UNMOUNT || evp_rc == AHAFS_MODDIR_REMOVE_SELF) { + /* The directory is no longer available for monitoring */ + *events = UV_RENAME; + handle->dir_filename = NULL; + } else { + /* A file was added/removed inside the directory */ + *events = UV_CHANGE; + + /* Get the EVPROD_INFO */ + if (uv__skip_lines(&p, 1) != 1) + return -1; + + /* Scan out the name of the file that triggered the event*/ + if (sscanf(p, "BEGIN_EVPROD_INFO\n%sEND_EVPROD_INFO", filename) == 1) { + handle->dir_filename = uv__strdup((const char*)&filename); + } else + return -1; + } + } else { /* Regular File */ + if (evp_rc == AHAFS_MODFILE_RENAME) + *events = UV_RENAME; + else + *events = UV_CHANGE; + } + } + else + return -1; + + return 0; +} + + +/* This is the internal callback */ +static void uv__ahafs_event(uv_loop_t* loop, uv__io_t* event_watch, unsigned int fflags) { + char result_data[RDWR_BUF_SIZE]; + int bytes, rc = 0; + uv_fs_event_t* handle; + int events = 0; + char fname[PATH_MAX]; + char *p; + + handle = container_of(event_watch, uv_fs_event_t, event_watcher); + + /* At this point, we assume that polling has been done on the + * file descriptor, so we can just read the AHAFS event occurrence + * data and parse its results without having to block anything + */ + bytes = pread(event_watch->fd, result_data, RDWR_BUF_SIZE, 0); + + assert((bytes >= 0) && "uv__ahafs_event - Error reading monitor file"); + + /* In file / directory move cases, AIX Event infrastructure + * produces a second event with no data. + * Ignore it and return gracefully. + */ + if(bytes == 0) + return; + + /* Parse the data */ + if(bytes > 0) + rc = uv__parse_data(result_data, &events, handle); + + /* Unrecoverable error */ + if (rc == -1) + return; + + /* For directory changes, the name of the files that triggered the change + * are never absolute pathnames + */ + if (uv__path_is_a_directory(handle->path) == 0) { + p = handle->dir_filename; + } else { + p = strrchr(handle->path, '/'); + if (p == NULL) + p = handle->path; + else + p++; + } + + /* TODO(bnoordhuis) Check uv__strscpy() return value. */ + uv__strscpy(fname, p, sizeof(fname)); + + handle->cb(handle, fname, events, 0); +} +#endif + + +int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { +#ifdef HAVE_SYS_AHAFS_EVPRODS_H + uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT); + return 0; +#else + return UV_ENOSYS; +#endif +} + + +int uv_fs_event_start(uv_fs_event_t* handle, + uv_fs_event_cb cb, + const char* filename, + unsigned int flags) { +#ifdef HAVE_SYS_AHAFS_EVPRODS_H + int fd, rc, str_offset = 0; + char cwd[PATH_MAX]; + char absolute_path[PATH_MAX]; + char readlink_cwd[PATH_MAX]; + struct timeval zt; + fd_set pollfd; + + + /* Figure out whether filename is absolute or not */ + if (filename[0] == '\0') { + /* Missing a pathname */ + return UV_ENOENT; + } + else if (filename[0] == '/') { + /* We have absolute pathname */ + /* TODO(bnoordhuis) Check uv__strscpy() return value. */ + uv__strscpy(absolute_path, filename, sizeof(absolute_path)); + } else { + /* We have a relative pathname, compose the absolute pathname */ + snprintf(cwd, sizeof(cwd), "/proc/%lu/cwd", (unsigned long) getpid()); + rc = readlink(cwd, readlink_cwd, sizeof(readlink_cwd) - 1); + if (rc < 0) + return rc; + /* readlink does not null terminate our string */ + readlink_cwd[rc] = '\0'; + + if (filename[0] == '.' && filename[1] == '/') + str_offset = 2; + + snprintf(absolute_path, sizeof(absolute_path), "%s%s", readlink_cwd, + filename + str_offset); + } + + if (uv__is_ahafs_mounted() < 0) /* /aha checks failed */ + return UV_ENOSYS; + + /* Setup ahafs */ + rc = uv__setup_ahafs((const char *)absolute_path, &fd); + if (rc != 0) + return rc; + + /* Setup/Initialize all the libuv routines */ + uv__handle_start(handle); + uv__io_init(&handle->event_watcher, uv__ahafs_event, fd); + handle->path = uv__strdup(filename); + handle->cb = cb; + handle->dir_filename = NULL; + + uv__io_start(handle->loop, &handle->event_watcher, POLLIN); + + /* AHAFS wants someone to poll for it to start mointoring. + * so kick-start it so that we don't miss an event in the + * eventuality of an event that occurs in the current loop. */ + do { + memset(&zt, 0, sizeof(zt)); + FD_ZERO(&pollfd); + FD_SET(fd, &pollfd); + rc = select(fd + 1, &pollfd, NULL, NULL, &zt); + } while (rc == -1 && errno == EINTR); + return 0; +#else + return UV_ENOSYS; +#endif +} + + +int uv_fs_event_stop(uv_fs_event_t* handle) { +#ifdef HAVE_SYS_AHAFS_EVPRODS_H + if (!uv__is_active(handle)) + return 0; + + uv__io_close(handle->loop, &handle->event_watcher); + uv__handle_stop(handle); + + if (uv__path_is_a_directory(handle->path) == 0) { + uv__free(handle->dir_filename); + handle->dir_filename = NULL; + } + + uv__free(handle->path); + handle->path = NULL; + uv__close(handle->event_watcher.fd); + handle->event_watcher.fd = -1; + + return 0; +#else + return UV_ENOSYS; +#endif +} + + +void uv__fs_event_close(uv_fs_event_t* handle) { +#ifdef HAVE_SYS_AHAFS_EVPRODS_H + uv_fs_event_stop(handle); +#else + UNREACHABLE(); +#endif +} + + +char** uv_setup_args(int argc, char** argv) { + char exepath[UV__PATH_MAX]; + char** new_argv; + size_t size; + char* s; + int i; + + if (argc <= 0) + return argv; + + /* Save the original pointer to argv. + * AIX uses argv to read the process name. + * (Not the memory pointed to by argv[0..n] as on Linux.) + */ + process_argv = argv; + process_argc = argc; + + /* Use argv[0] to determine value for uv_exepath(). */ + size = sizeof(exepath); + if (uv__search_path(argv[0], exepath, &size) == 0) { + uv_once(&process_title_mutex_once, init_process_title_mutex_once); + uv_mutex_lock(&process_title_mutex); + original_exepath = uv__strdup(exepath); + uv_mutex_unlock(&process_title_mutex); + } + + /* Calculate how much memory we need for the argv strings. */ + size = 0; + for (i = 0; i < argc; i++) + size += strlen(argv[i]) + 1; + + /* Add space for the argv pointers. */ + size += (argc + 1) * sizeof(char*); + + new_argv = uv__malloc(size); + if (new_argv == NULL) + return argv; + args_mem = new_argv; + + /* Copy over the strings and set up the pointer table. */ + s = (char*) &new_argv[argc + 1]; + for (i = 0; i < argc; i++) { + size = strlen(argv[i]) + 1; + memcpy(s, argv[i], size); + new_argv[i] = s; + s += size; + } + new_argv[i] = NULL; + + return new_argv; +} + + +int uv_set_process_title(const char* title) { + char* new_title; + + /* If uv_setup_args wasn't called or failed, we can't continue. */ + if (process_argv == NULL || args_mem == NULL) + return UV_ENOBUFS; + + /* We cannot free this pointer when libuv shuts down, + * the process may still be using it. + */ + new_title = uv__strdup(title); + if (new_title == NULL) + return UV_ENOMEM; + + uv_once(&process_title_mutex_once, init_process_title_mutex_once); + uv_mutex_lock(&process_title_mutex); + + /* If this is the first time this is set, + * don't free and set argv[1] to NULL. + */ + if (process_title_ptr != NULL) + uv__free(process_title_ptr); + + process_title_ptr = new_title; + + process_argv[0] = process_title_ptr; + if (process_argc > 1) + process_argv[1] = NULL; + + uv_mutex_unlock(&process_title_mutex); + + return 0; +} + + +int uv_get_process_title(char* buffer, size_t size) { + size_t len; + if (buffer == NULL || size == 0) + return UV_EINVAL; + + /* If uv_setup_args wasn't called, we can't continue. */ + if (process_argv == NULL) + return UV_ENOBUFS; + + uv_once(&process_title_mutex_once, init_process_title_mutex_once); + uv_mutex_lock(&process_title_mutex); + + len = strlen(process_argv[0]); + if (size <= len) { + uv_mutex_unlock(&process_title_mutex); + return UV_ENOBUFS; + } + + memcpy(buffer, process_argv[0], len); + buffer[len] = '\0'; + + uv_mutex_unlock(&process_title_mutex); + + return 0; +} + + +void uv__process_title_cleanup(void) { + uv__free(args_mem); /* Keep valgrind happy. */ + args_mem = NULL; +} + + +int uv_resident_set_memory(size_t* rss) { + char pp[64]; + psinfo_t psinfo; + int err; + int fd; + + snprintf(pp, sizeof(pp), "/proc/%lu/psinfo", (unsigned long) getpid()); + + fd = open(pp, O_RDONLY); + if (fd == -1) + return UV__ERR(errno); + + /* FIXME(bnoordhuis) Handle EINTR. */ + err = UV_EINVAL; + if (read(fd, &psinfo, sizeof(psinfo)) == sizeof(psinfo)) { + *rss = (size_t)psinfo.pr_rssize * 1024; + err = 0; + } + uv__close(fd); + + return err; +} + + +int uv_uptime(double* uptime) { + struct utmp *utmp_buf; + size_t entries = 0; + time_t boot_time; + + boot_time = 0; + utmpname(UTMP_FILE); + + setutent(); + + while ((utmp_buf = getutent()) != NULL) { + if (utmp_buf->ut_user[0] && utmp_buf->ut_type == USER_PROCESS) + ++entries; + if (utmp_buf->ut_type == BOOT_TIME) + boot_time = utmp_buf->ut_time; + } + + endutent(); + + if (boot_time == 0) + return UV_ENOSYS; + + *uptime = time(NULL) - boot_time; + return 0; +} + + +int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + uv_cpu_info_t* cpu_info; + perfstat_cpu_total_t ps_total; + perfstat_cpu_t* ps_cpus; + perfstat_id_t cpu_id; + int result, ncpus, idx = 0; + + result = perfstat_cpu_total(NULL, &ps_total, sizeof(ps_total), 1); + if (result == -1) { + return UV_ENOSYS; + } + + ncpus = result = perfstat_cpu(NULL, NULL, sizeof(perfstat_cpu_t), 0); + if (result == -1) { + return UV_ENOSYS; + } + + ps_cpus = (perfstat_cpu_t*) uv__malloc(ncpus * sizeof(perfstat_cpu_t)); + if (!ps_cpus) { + return UV_ENOMEM; + } + + /* TODO(bnoordhuis) Check uv__strscpy() return value. */ + uv__strscpy(cpu_id.name, FIRST_CPU, sizeof(cpu_id.name)); + result = perfstat_cpu(&cpu_id, ps_cpus, sizeof(perfstat_cpu_t), ncpus); + if (result == -1) { + uv__free(ps_cpus); + return UV_ENOSYS; + } + + *cpu_infos = (uv_cpu_info_t*) uv__malloc(ncpus * sizeof(uv_cpu_info_t)); + if (!*cpu_infos) { + uv__free(ps_cpus); + return UV_ENOMEM; + } + + *count = ncpus; + + cpu_info = *cpu_infos; + while (idx < ncpus) { + cpu_info->speed = (int)(ps_total.processorHZ / 1000000); + cpu_info->model = uv__strdup(ps_total.description); + cpu_info->cpu_times.user = ps_cpus[idx].user; + cpu_info->cpu_times.sys = ps_cpus[idx].sys; + cpu_info->cpu_times.idle = ps_cpus[idx].idle; + cpu_info->cpu_times.irq = ps_cpus[idx].wait; + cpu_info->cpu_times.nice = 0; + cpu_info++; + idx++; + } + + uv__free(ps_cpus); + return 0; +} + + +int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { + uv_interface_address_t* address; + int sockfd, sock6fd, inet6, i, r, size = 1; + struct ifconf ifc; + struct ifreq *ifr, *p, flg; + struct in6_ifreq if6; + struct sockaddr_dl* sa_addr; + + ifc.ifc_req = NULL; + sock6fd = -1; + r = 0; + *count = 0; + *addresses = NULL; + + if (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP))) { + r = UV__ERR(errno); + goto cleanup; + } + + if (0 > (sock6fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_IP))) { + r = UV__ERR(errno); + goto cleanup; + } + + if (ioctl(sockfd, SIOCGSIZIFCONF, &size) == -1) { + r = UV__ERR(errno); + goto cleanup; + } + + ifc.ifc_req = (struct ifreq*)uv__malloc(size); + if (ifc.ifc_req == NULL) { + r = UV_ENOMEM; + goto cleanup; + } + ifc.ifc_len = size; + if (ioctl(sockfd, SIOCGIFCONF, &ifc) == -1) { + r = UV__ERR(errno); + goto cleanup; + } + +#define ADDR_SIZE(p) MAX((p).sa_len, sizeof(p)) + + /* Count all up and running ipv4/ipv6 addresses */ + ifr = ifc.ifc_req; + while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) { + p = ifr; + ifr = (struct ifreq*) + ((char*)ifr + sizeof(ifr->ifr_name) + ADDR_SIZE(ifr->ifr_addr)); + + if (!(p->ifr_addr.sa_family == AF_INET6 || + p->ifr_addr.sa_family == AF_INET)) + continue; + + memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name)); + if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) { + r = UV__ERR(errno); + goto cleanup; + } + + if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING)) + continue; + + (*count)++; + } + + if (*count == 0) + goto cleanup; + + /* Alloc the return interface structs */ + *addresses = uv__calloc(*count, sizeof(**addresses)); + if (!(*addresses)) { + r = UV_ENOMEM; + goto cleanup; + } + address = *addresses; + + ifr = ifc.ifc_req; + while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) { + p = ifr; + ifr = (struct ifreq*) + ((char*)ifr + sizeof(ifr->ifr_name) + ADDR_SIZE(ifr->ifr_addr)); + + if (!(p->ifr_addr.sa_family == AF_INET6 || + p->ifr_addr.sa_family == AF_INET)) + continue; + + inet6 = (p->ifr_addr.sa_family == AF_INET6); + + memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name)); + if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) + goto syserror; + + if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING)) + continue; + + /* All conditions above must match count loop */ + + address->name = uv__strdup(p->ifr_name); + + if (inet6) + address->address.address6 = *((struct sockaddr_in6*) &p->ifr_addr); + else + address->address.address4 = *((struct sockaddr_in*) &p->ifr_addr); + + if (inet6) { + memset(&if6, 0, sizeof(if6)); + r = uv__strscpy(if6.ifr_name, p->ifr_name, sizeof(if6.ifr_name)); + if (r == UV_E2BIG) + goto cleanup; + r = 0; + memcpy(&if6.ifr_Addr, &p->ifr_addr, sizeof(if6.ifr_Addr)); + if (ioctl(sock6fd, SIOCGIFNETMASK6, &if6) == -1) + goto syserror; + address->netmask.netmask6 = *((struct sockaddr_in6*) &if6.ifr_Addr); + /* Explicitly set family as the ioctl call appears to return it as 0. */ + address->netmask.netmask6.sin6_family = AF_INET6; + } else { + if (ioctl(sockfd, SIOCGIFNETMASK, p) == -1) + goto syserror; + address->netmask.netmask4 = *((struct sockaddr_in*) &p->ifr_addr); + /* Explicitly set family as the ioctl call appears to return it as 0. */ + address->netmask.netmask4.sin_family = AF_INET; + } + + address->is_internal = flg.ifr_flags & IFF_LOOPBACK ? 1 : 0; + + address++; + } + + /* Fill in physical addresses. */ + ifr = ifc.ifc_req; + while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) { + p = ifr; + ifr = (struct ifreq*) + ((char*)ifr + sizeof(ifr->ifr_name) + ADDR_SIZE(ifr->ifr_addr)); + + if (p->ifr_addr.sa_family != AF_LINK) + continue; + + address = *addresses; + for (i = 0; i < *count; i++) { + if (strcmp(address->name, p->ifr_name) == 0) { + sa_addr = (struct sockaddr_dl*) &p->ifr_addr; + memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr)); + } + address++; + } + } + +#undef ADDR_SIZE + goto cleanup; + +syserror: + uv_free_interface_addresses(*addresses, *count); + *addresses = NULL; + *count = 0; + r = UV_ENOSYS; + +cleanup: + if (sockfd != -1) + uv__close(sockfd); + if (sock6fd != -1) + uv__close(sock6fd); + uv__free(ifc.ifc_req); + return r; +} + + +void uv_free_interface_addresses(uv_interface_address_t* addresses, + int count) { + int i; + + for (i = 0; i < count; ++i) { + uv__free(addresses[i].name); + } + + uv__free(addresses); +} + + +void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { + struct pollfd* events; + uintptr_t i; + uintptr_t nfds; + struct poll_ctl pc; + + assert(loop->watchers != NULL); + assert(fd >= 0); + + events = (struct pollfd*) loop->watchers[loop->nwatchers]; + nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; + + if (events != NULL) + /* Invalidate events with same file descriptor */ + for (i = 0; i < nfds; i++) + if ((int) events[i].fd == fd) + events[i].fd = -1; + + /* Remove the file descriptor from the poll set */ + pc.events = 0; + pc.cmd = PS_DELETE; + pc.fd = fd; + if(loop->backend_fd >= 0) + pollset_ctl(loop->backend_fd, &pc, 1); +} diff --git a/include/libuv/src/unix/android-ifaddrs.c b/include/libuv/src/unix/android-ifaddrs.c new file mode 100644 index 000000000..4765cc06b --- /dev/null +++ b/include/libuv/src/unix/android-ifaddrs.c @@ -0,0 +1,713 @@ +/* +Copyright (c) 2013, Kenneth MacKay +Copyright (c) 2014, Emergya (Cloud4all, FP7/2007-2013 grant agreement #289016) +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "uv/android-ifaddrs.h" +#include "uv-common.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct NetlinkList +{ + struct NetlinkList *m_next; + struct nlmsghdr *m_data; + unsigned int m_size; +} NetlinkList; + +static int netlink_socket(pid_t *p_pid) +{ + struct sockaddr_nl l_addr; + socklen_t l_len; + + int l_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if(l_socket < 0) + { + return -1; + } + + memset(&l_addr, 0, sizeof(l_addr)); + l_addr.nl_family = AF_NETLINK; + if(bind(l_socket, (struct sockaddr *)&l_addr, sizeof(l_addr)) < 0) + { + close(l_socket); + return -1; + } + + l_len = sizeof(l_addr); + if(getsockname(l_socket, (struct sockaddr *)&l_addr, &l_len) < 0) + { + close(l_socket); + return -1; + } + *p_pid = l_addr.nl_pid; + + return l_socket; +} + +static int netlink_send(int p_socket, int p_request) +{ + char l_buffer[NLMSG_ALIGN(sizeof(struct nlmsghdr)) + NLMSG_ALIGN(sizeof(struct rtgenmsg))]; + + struct nlmsghdr *l_hdr; + struct rtgenmsg *l_msg; + struct sockaddr_nl l_addr; + + memset(l_buffer, 0, sizeof(l_buffer)); + + l_hdr = (struct nlmsghdr *)l_buffer; + l_msg = (struct rtgenmsg *)NLMSG_DATA(l_hdr); + + l_hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*l_msg)); + l_hdr->nlmsg_type = p_request; + l_hdr->nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; + l_hdr->nlmsg_pid = 0; + l_hdr->nlmsg_seq = p_socket; + l_msg->rtgen_family = AF_UNSPEC; + + memset(&l_addr, 0, sizeof(l_addr)); + l_addr.nl_family = AF_NETLINK; + return (sendto(p_socket, l_hdr, l_hdr->nlmsg_len, 0, (struct sockaddr *)&l_addr, sizeof(l_addr))); +} + +static int netlink_recv(int p_socket, void *p_buffer, size_t p_len) +{ + struct sockaddr_nl l_addr; + struct msghdr l_msg; + + struct iovec l_iov; + l_iov.iov_base = p_buffer; + l_iov.iov_len = p_len; + + for(;;) + { + int l_result; + l_msg.msg_name = (void *)&l_addr; + l_msg.msg_namelen = sizeof(l_addr); + l_msg.msg_iov = &l_iov; + l_msg.msg_iovlen = 1; + l_msg.msg_control = NULL; + l_msg.msg_controllen = 0; + l_msg.msg_flags = 0; + l_result = recvmsg(p_socket, &l_msg, 0); + + if(l_result < 0) + { + if(errno == EINTR) + { + continue; + } + return -2; + } + + /* Buffer was too small */ + if(l_msg.msg_flags & MSG_TRUNC) + { + return -1; + } + return l_result; + } +} + +static struct nlmsghdr *getNetlinkResponse(int p_socket, pid_t p_pid, int *p_size, int *p_done) +{ + size_t l_size = 4096; + void *l_buffer = NULL; + + for(;;) + { + int l_read; + + uv__free(l_buffer); + l_buffer = uv__malloc(l_size); + if (l_buffer == NULL) + { + return NULL; + } + + l_read = netlink_recv(p_socket, l_buffer, l_size); + *p_size = l_read; + if(l_read == -2) + { + uv__free(l_buffer); + return NULL; + } + if(l_read >= 0) + { + struct nlmsghdr *l_hdr; + for(l_hdr = (struct nlmsghdr *)l_buffer; NLMSG_OK(l_hdr, (unsigned int)l_read); l_hdr = (struct nlmsghdr *)NLMSG_NEXT(l_hdr, l_read)) + { + if((pid_t)l_hdr->nlmsg_pid != p_pid || (int)l_hdr->nlmsg_seq != p_socket) + { + continue; + } + + if(l_hdr->nlmsg_type == NLMSG_DONE) + { + *p_done = 1; + break; + } + + if(l_hdr->nlmsg_type == NLMSG_ERROR) + { + uv__free(l_buffer); + return NULL; + } + } + return l_buffer; + } + + l_size *= 2; + } +} + +static NetlinkList *newListItem(struct nlmsghdr *p_data, unsigned int p_size) +{ + NetlinkList *l_item = uv__malloc(sizeof(NetlinkList)); + if (l_item == NULL) + { + return NULL; + } + + l_item->m_next = NULL; + l_item->m_data = p_data; + l_item->m_size = p_size; + return l_item; +} + +static void freeResultList(NetlinkList *p_list) +{ + NetlinkList *l_cur; + while(p_list) + { + l_cur = p_list; + p_list = p_list->m_next; + uv__free(l_cur->m_data); + uv__free(l_cur); + } +} + +static NetlinkList *getResultList(int p_socket, int p_request, pid_t p_pid) +{ + int l_size; + int l_done; + NetlinkList *l_list; + NetlinkList *l_end; + + if(netlink_send(p_socket, p_request) < 0) + { + return NULL; + } + + l_list = NULL; + l_end = NULL; + + l_done = 0; + while(!l_done) + { + NetlinkList *l_item; + + struct nlmsghdr *l_hdr = getNetlinkResponse(p_socket, p_pid, &l_size, &l_done); + /* Error */ + if(!l_hdr) + { + freeResultList(l_list); + return NULL; + } + + l_item = newListItem(l_hdr, l_size); + if (!l_item) + { + freeResultList(l_list); + return NULL; + } + if(!l_list) + { + l_list = l_item; + } + else + { + l_end->m_next = l_item; + } + l_end = l_item; + } + return l_list; +} + +static size_t maxSize(size_t a, size_t b) +{ + return (a > b ? a : b); +} + +static size_t calcAddrLen(sa_family_t p_family, int p_dataSize) +{ + switch(p_family) + { + case AF_INET: + return sizeof(struct sockaddr_in); + case AF_INET6: + return sizeof(struct sockaddr_in6); + case AF_PACKET: + return maxSize(sizeof(struct sockaddr_ll), offsetof(struct sockaddr_ll, sll_addr) + p_dataSize); + default: + return maxSize(sizeof(struct sockaddr), offsetof(struct sockaddr, sa_data) + p_dataSize); + } +} + +static void makeSockaddr(sa_family_t p_family, struct sockaddr *p_dest, void *p_data, size_t p_size) +{ + switch(p_family) + { + case AF_INET: + memcpy(&((struct sockaddr_in*)p_dest)->sin_addr, p_data, p_size); + break; + case AF_INET6: + memcpy(&((struct sockaddr_in6*)p_dest)->sin6_addr, p_data, p_size); + break; + case AF_PACKET: + memcpy(((struct sockaddr_ll*)p_dest)->sll_addr, p_data, p_size); + ((struct sockaddr_ll*)p_dest)->sll_halen = p_size; + break; + default: + memcpy(p_dest->sa_data, p_data, p_size); + break; + } + p_dest->sa_family = p_family; +} + +static void addToEnd(struct ifaddrs **p_resultList, struct ifaddrs *p_entry) +{ + if(!*p_resultList) + { + *p_resultList = p_entry; + } + else + { + struct ifaddrs *l_cur = *p_resultList; + while(l_cur->ifa_next) + { + l_cur = l_cur->ifa_next; + } + l_cur->ifa_next = p_entry; + } +} + +static int interpretLink(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList) +{ + struct ifaddrs *l_entry; + + char *l_index; + char *l_name; + char *l_addr; + char *l_data; + + struct ifinfomsg *l_info = (struct ifinfomsg *)NLMSG_DATA(p_hdr); + + size_t l_nameSize = 0; + size_t l_addrSize = 0; + size_t l_dataSize = 0; + + size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg)); + struct rtattr *l_rta; + for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) + { + size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); + switch(l_rta->rta_type) + { + case IFLA_ADDRESS: + case IFLA_BROADCAST: + l_addrSize += NLMSG_ALIGN(calcAddrLen(AF_PACKET, l_rtaDataSize)); + break; + case IFLA_IFNAME: + l_nameSize += NLMSG_ALIGN(l_rtaSize + 1); + break; + case IFLA_STATS: + l_dataSize += NLMSG_ALIGN(l_rtaSize); + break; + default: + break; + } + } + + l_entry = uv__malloc(sizeof(struct ifaddrs) + sizeof(int) + l_nameSize + l_addrSize + l_dataSize); + if (l_entry == NULL) + { + return -1; + } + memset(l_entry, 0, sizeof(struct ifaddrs)); + l_entry->ifa_name = ""; + + l_index = ((char *)l_entry) + sizeof(struct ifaddrs); + l_name = l_index + sizeof(int); + l_addr = l_name + l_nameSize; + l_data = l_addr + l_addrSize; + + /* Save the interface index so we can look it up when handling the + * addresses. + */ + memcpy(l_index, &l_info->ifi_index, sizeof(int)); + + l_entry->ifa_flags = l_info->ifi_flags; + + l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg)); + for(l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) + { + void *l_rtaData = RTA_DATA(l_rta); + size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); + switch(l_rta->rta_type) + { + case IFLA_ADDRESS: + case IFLA_BROADCAST: + { + size_t l_addrLen = calcAddrLen(AF_PACKET, l_rtaDataSize); + makeSockaddr(AF_PACKET, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize); + ((struct sockaddr_ll *)l_addr)->sll_ifindex = l_info->ifi_index; + ((struct sockaddr_ll *)l_addr)->sll_hatype = l_info->ifi_type; + if(l_rta->rta_type == IFLA_ADDRESS) + { + l_entry->ifa_addr = (struct sockaddr *)l_addr; + } + else + { + l_entry->ifa_broadaddr = (struct sockaddr *)l_addr; + } + l_addr += NLMSG_ALIGN(l_addrLen); + break; + } + case IFLA_IFNAME: + strncpy(l_name, l_rtaData, l_rtaDataSize); + l_name[l_rtaDataSize] = '\0'; + l_entry->ifa_name = l_name; + break; + case IFLA_STATS: + memcpy(l_data, l_rtaData, l_rtaDataSize); + l_entry->ifa_data = l_data; + break; + default: + break; + } + } + + addToEnd(p_resultList, l_entry); + return 0; +} + +static struct ifaddrs *findInterface(int p_index, struct ifaddrs **p_links, int p_numLinks) +{ + int l_num = 0; + struct ifaddrs *l_cur = *p_links; + while(l_cur && l_num < p_numLinks) + { + char *l_indexPtr = ((char *)l_cur) + sizeof(struct ifaddrs); + int l_index; + memcpy(&l_index, l_indexPtr, sizeof(int)); + if(l_index == p_index) + { + return l_cur; + } + + l_cur = l_cur->ifa_next; + ++l_num; + } + return NULL; +} + +static int interpretAddr(struct nlmsghdr *p_hdr, struct ifaddrs **p_resultList, int p_numLinks) +{ + struct ifaddrmsg *l_info = (struct ifaddrmsg *)NLMSG_DATA(p_hdr); + struct ifaddrs *l_interface = findInterface(l_info->ifa_index, p_resultList, p_numLinks); + + size_t l_nameSize = 0; + size_t l_addrSize = 0; + + int l_addedNetmask = 0; + + size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg)); + struct rtattr *l_rta; + struct ifaddrs *l_entry; + + char *l_name; + char *l_addr; + + for(l_rta = IFA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) + { + size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); + if(l_info->ifa_family == AF_PACKET) + { + continue; + } + + switch(l_rta->rta_type) + { + case IFA_ADDRESS: + case IFA_LOCAL: + l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize)); + if((l_info->ifa_family == AF_INET || l_info->ifa_family == AF_INET6) && !l_addedNetmask) + { + /* Make room for netmask */ + l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize)); + l_addedNetmask = 1; + } + break; + case IFA_BROADCAST: + l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize)); + break; + case IFA_LABEL: + l_nameSize += NLMSG_ALIGN(l_rtaDataSize + 1); + break; + default: + break; + } + } + + l_entry = uv__malloc(sizeof(struct ifaddrs) + l_nameSize + l_addrSize); + if (l_entry == NULL) + { + return -1; + } + memset(l_entry, 0, sizeof(struct ifaddrs)); + l_entry->ifa_name = (l_interface ? l_interface->ifa_name : ""); + + l_name = ((char *)l_entry) + sizeof(struct ifaddrs); + l_addr = l_name + l_nameSize; + + l_entry->ifa_flags = l_info->ifa_flags; + if(l_interface) + { + l_entry->ifa_flags |= l_interface->ifa_flags; + } + + l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg)); + for(l_rta = IFA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) + { + void *l_rtaData = RTA_DATA(l_rta); + size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); + switch(l_rta->rta_type) + { + case IFA_ADDRESS: + case IFA_BROADCAST: + case IFA_LOCAL: + { + size_t l_addrLen = calcAddrLen(l_info->ifa_family, l_rtaDataSize); + makeSockaddr(l_info->ifa_family, (struct sockaddr *)l_addr, l_rtaData, l_rtaDataSize); + if(l_info->ifa_family == AF_INET6) + { + if(IN6_IS_ADDR_LINKLOCAL((struct in6_addr *)l_rtaData) || IN6_IS_ADDR_MC_LINKLOCAL((struct in6_addr *)l_rtaData)) + { + ((struct sockaddr_in6 *)l_addr)->sin6_scope_id = l_info->ifa_index; + } + } + + /* Apparently in a point-to-point network IFA_ADDRESS contains + * the dest address and IFA_LOCAL contains the local address + */ + if(l_rta->rta_type == IFA_ADDRESS) + { + if(l_entry->ifa_addr) + { + l_entry->ifa_dstaddr = (struct sockaddr *)l_addr; + } + else + { + l_entry->ifa_addr = (struct sockaddr *)l_addr; + } + } + else if(l_rta->rta_type == IFA_LOCAL) + { + if(l_entry->ifa_addr) + { + l_entry->ifa_dstaddr = l_entry->ifa_addr; + } + l_entry->ifa_addr = (struct sockaddr *)l_addr; + } + else + { + l_entry->ifa_broadaddr = (struct sockaddr *)l_addr; + } + l_addr += NLMSG_ALIGN(l_addrLen); + break; + } + case IFA_LABEL: + strncpy(l_name, l_rtaData, l_rtaDataSize); + l_name[l_rtaDataSize] = '\0'; + l_entry->ifa_name = l_name; + break; + default: + break; + } + } + + if(l_entry->ifa_addr && (l_entry->ifa_addr->sa_family == AF_INET || l_entry->ifa_addr->sa_family == AF_INET6)) + { + unsigned l_maxPrefix = (l_entry->ifa_addr->sa_family == AF_INET ? 32 : 128); + unsigned l_prefix = (l_info->ifa_prefixlen > l_maxPrefix ? l_maxPrefix : l_info->ifa_prefixlen); + unsigned char l_mask[16] = {0}; + unsigned i; + for(i=0; i<(l_prefix/8); ++i) + { + l_mask[i] = 0xff; + } + if(l_prefix % 8) + { + l_mask[i] = 0xff << (8 - (l_prefix % 8)); + } + + makeSockaddr(l_entry->ifa_addr->sa_family, (struct sockaddr *)l_addr, l_mask, l_maxPrefix / 8); + l_entry->ifa_netmask = (struct sockaddr *)l_addr; + } + + addToEnd(p_resultList, l_entry); + return 0; +} + +static int interpretLinks(int p_socket, pid_t p_pid, NetlinkList *p_netlinkList, struct ifaddrs **p_resultList) +{ + + int l_numLinks = 0; + for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next) + { + unsigned int l_nlsize = p_netlinkList->m_size; + struct nlmsghdr *l_hdr; + for(l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize)) + { + if((pid_t)l_hdr->nlmsg_pid != p_pid || (int)l_hdr->nlmsg_seq != p_socket) + { + continue; + } + + if(l_hdr->nlmsg_type == NLMSG_DONE) + { + break; + } + + if(l_hdr->nlmsg_type == RTM_NEWLINK) + { + if(interpretLink(l_hdr, p_resultList) == -1) + { + return -1; + } + ++l_numLinks; + } + } + } + return l_numLinks; +} + +static int interpretAddrs(int p_socket, pid_t p_pid, NetlinkList *p_netlinkList, struct ifaddrs **p_resultList, int p_numLinks) +{ + for(; p_netlinkList; p_netlinkList = p_netlinkList->m_next) + { + unsigned int l_nlsize = p_netlinkList->m_size; + struct nlmsghdr *l_hdr; + for(l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); l_hdr = NLMSG_NEXT(l_hdr, l_nlsize)) + { + if((pid_t)l_hdr->nlmsg_pid != p_pid || (int)l_hdr->nlmsg_seq != p_socket) + { + continue; + } + + if(l_hdr->nlmsg_type == NLMSG_DONE) + { + break; + } + + if(l_hdr->nlmsg_type == RTM_NEWADDR) + { + if (interpretAddr(l_hdr, p_resultList, p_numLinks) == -1) + { + return -1; + } + } + } + } + return 0; +} + +int getifaddrs(struct ifaddrs **ifap) +{ + int l_socket; + int l_result; + int l_numLinks; + pid_t l_pid; + NetlinkList *l_linkResults; + NetlinkList *l_addrResults; + + if(!ifap) + { + return -1; + } + *ifap = NULL; + + l_socket = netlink_socket(&l_pid); + if(l_socket < 0) + { + return -1; + } + + l_linkResults = getResultList(l_socket, RTM_GETLINK, l_pid); + if(!l_linkResults) + { + close(l_socket); + return -1; + } + + l_addrResults = getResultList(l_socket, RTM_GETADDR, l_pid); + if(!l_addrResults) + { + close(l_socket); + freeResultList(l_linkResults); + return -1; + } + + l_result = 0; + l_numLinks = interpretLinks(l_socket, l_pid, l_linkResults, ifap); + if(l_numLinks == -1 || interpretAddrs(l_socket, l_pid, l_addrResults, ifap, l_numLinks) == -1) + { + l_result = -1; + } + + freeResultList(l_linkResults); + freeResultList(l_addrResults); + close(l_socket); + return l_result; +} + +void freeifaddrs(struct ifaddrs *ifa) +{ + struct ifaddrs *l_cur; + while(ifa) + { + l_cur = ifa; + ifa = ifa->ifa_next; + uv__free(l_cur); + } +} diff --git a/include/libuv/src/unix/async.c b/include/libuv/src/unix/async.c new file mode 100644 index 000000000..5f58fb88d --- /dev/null +++ b/include/libuv/src/unix/async.c @@ -0,0 +1,253 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* This file contains both the uv__async internal infrastructure and the + * user-facing uv_async_t functions. + */ + +#include "uv.h" +#include "internal.h" +#include "atomic-ops.h" + +#include +#include /* snprintf() */ +#include +#include +#include +#include +#include /* sched_yield() */ + +#ifdef __linux__ +#include +#endif + +static void uv__async_send(uv_loop_t* loop); +static int uv__async_start(uv_loop_t* loop); + + +int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) { + int err; + + err = uv__async_start(loop); + if (err) + return err; + + uv__handle_init(loop, (uv_handle_t*)handle, UV_ASYNC); + handle->async_cb = async_cb; + handle->pending = 0; + + QUEUE_INSERT_TAIL(&loop->async_handles, &handle->queue); + uv__handle_start(handle); + + return 0; +} + + +int uv_async_send(uv_async_t* handle) { + /* Do a cheap read first. */ + if (ACCESS_ONCE(int, handle->pending) != 0) + return 0; + + /* Tell the other thread we're busy with the handle. */ + if (cmpxchgi(&handle->pending, 0, 1) != 0) + return 0; + + /* Wake up the other thread's event loop. */ + uv__async_send(handle->loop); + + /* Tell the other thread we're done. */ + if (cmpxchgi(&handle->pending, 1, 2) != 1) + abort(); + + return 0; +} + + +/* Only call this from the event loop thread. */ +static int uv__async_spin(uv_async_t* handle) { + int i; + int rc; + + for (;;) { + /* 997 is not completely chosen at random. It's a prime number, acyclical + * by nature, and should therefore hopefully dampen sympathetic resonance. + */ + for (i = 0; i < 997; i++) { + /* rc=0 -- handle is not pending. + * rc=1 -- handle is pending, other thread is still working with it. + * rc=2 -- handle is pending, other thread is done. + */ + rc = cmpxchgi(&handle->pending, 2, 0); + + if (rc != 1) + return rc; + + /* Other thread is busy with this handle, spin until it's done. */ + cpu_relax(); + } + + /* Yield the CPU. We may have preempted the other thread while it's + * inside the critical section and if it's running on the same CPU + * as us, we'll just burn CPU cycles until the end of our time slice. + */ + sched_yield(); + } +} + + +void uv__async_close(uv_async_t* handle) { + uv__async_spin(handle); + QUEUE_REMOVE(&handle->queue); + uv__handle_stop(handle); +} + + +static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { + char buf[1024]; + ssize_t r; + QUEUE queue; + QUEUE* q; + uv_async_t* h; + + assert(w == &loop->async_io_watcher); + + for (;;) { + r = read(w->fd, buf, sizeof(buf)); + + if (r == sizeof(buf)) + continue; + + if (r != -1) + break; + + if (errno == EAGAIN || errno == EWOULDBLOCK) + break; + + if (errno == EINTR) + continue; + + abort(); + } + + QUEUE_MOVE(&loop->async_handles, &queue); + while (!QUEUE_EMPTY(&queue)) { + q = QUEUE_HEAD(&queue); + h = QUEUE_DATA(q, uv_async_t, queue); + + QUEUE_REMOVE(q); + QUEUE_INSERT_TAIL(&loop->async_handles, q); + + if (0 == uv__async_spin(h)) + continue; /* Not pending. */ + + if (h->async_cb == NULL) + continue; + + h->async_cb(h); + } +} + + +static void uv__async_send(uv_loop_t* loop) { + const void* buf; + ssize_t len; + int fd; + int r; + + buf = ""; + len = 1; + fd = loop->async_wfd; + +#if defined(__linux__) + if (fd == -1) { + static const uint64_t val = 1; + buf = &val; + len = sizeof(val); + fd = loop->async_io_watcher.fd; /* eventfd */ + } +#endif + + do + r = write(fd, buf, len); + while (r == -1 && errno == EINTR); + + if (r == len) + return; + + if (r == -1) + if (errno == EAGAIN || errno == EWOULDBLOCK) + return; + + abort(); +} + + +static int uv__async_start(uv_loop_t* loop) { + int pipefd[2]; + int err; + + if (loop->async_io_watcher.fd != -1) + return 0; + +#ifdef __linux__ + err = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + if (err < 0) + return UV__ERR(errno); + + pipefd[0] = err; + pipefd[1] = -1; +#else + err = uv__make_pipe(pipefd, UV__F_NONBLOCK); + if (err < 0) + return err; +#endif + + uv__io_init(&loop->async_io_watcher, uv__async_io, pipefd[0]); + uv__io_start(loop, &loop->async_io_watcher, POLLIN); + loop->async_wfd = pipefd[1]; + + return 0; +} + + +int uv__async_fork(uv_loop_t* loop) { + if (loop->async_io_watcher.fd == -1) /* never started */ + return 0; + + uv__async_stop(loop); + + return uv__async_start(loop); +} + + +void uv__async_stop(uv_loop_t* loop) { + if (loop->async_io_watcher.fd == -1) + return; + + if (loop->async_wfd != -1) { + if (loop->async_wfd != loop->async_io_watcher.fd) + uv__close(loop->async_wfd); + loop->async_wfd = -1; + } + + uv__io_stop(loop, &loop->async_io_watcher, POLLIN); + uv__close(loop->async_io_watcher.fd); + loop->async_io_watcher.fd = -1; +} diff --git a/include/libuv/src/unix/atomic-ops.h b/include/libuv/src/unix/atomic-ops.h new file mode 100644 index 000000000..347d19365 --- /dev/null +++ b/include/libuv/src/unix/atomic-ops.h @@ -0,0 +1,61 @@ +/* Copyright (c) 2013, Ben Noordhuis + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef UV_ATOMIC_OPS_H_ +#define UV_ATOMIC_OPS_H_ + +#include "internal.h" /* UV_UNUSED */ + +#if defined(__SUNPRO_C) || defined(__SUNPRO_CC) +#include +#endif + +UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)); +UV_UNUSED(static void cpu_relax(void)); + +/* Prefer hand-rolled assembly over the gcc builtins because the latter also + * issue full memory barriers. + */ +UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)) { +#if defined(__i386__) || defined(__x86_64__) + int out; + __asm__ __volatile__ ("lock; cmpxchg %2, %1;" + : "=a" (out), "+m" (*(volatile int*) ptr) + : "r" (newval), "0" (oldval) + : "memory"); + return out; +#elif defined(__MVS__) + unsigned int op4; + if (__plo_CSST(ptr, (unsigned int*) &oldval, newval, + (unsigned int*) ptr, *ptr, &op4)) + return oldval; + else + return op4; +#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) + return atomic_cas_uint((uint_t *)ptr, (uint_t)oldval, (uint_t)newval); +#else + return __sync_val_compare_and_swap(ptr, oldval, newval); +#endif +} + +UV_UNUSED(static void cpu_relax(void)) { +#if defined(__i386__) || defined(__x86_64__) + __asm__ __volatile__ ("rep; nop"); /* a.k.a. PAUSE */ +#elif (defined(__arm__) && __ARM_ARCH >= 7) || defined(__aarch64__) + __asm__ volatile("yield"); +#endif +} + +#endif /* UV_ATOMIC_OPS_H_ */ diff --git a/include/libuv/src/unix/bsd-ifaddrs.c b/include/libuv/src/unix/bsd-ifaddrs.c new file mode 100644 index 000000000..5223ab487 --- /dev/null +++ b/include/libuv/src/unix/bsd-ifaddrs.c @@ -0,0 +1,163 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include + +#include +#include +#if !defined(__CYGWIN__) && !defined(__MSYS__) +#include +#endif + +#if defined(__HAIKU__) +#define IFF_RUNNING IFF_LINK +#endif + +static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) { + if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING))) + return 1; + if (ent->ifa_addr == NULL) + return 1; +#if !defined(__CYGWIN__) && !defined(__MSYS__) + /* + * If `exclude_type` is `UV__EXCLUDE_IFPHYS`, just see whether `sa_family` + * equals to `AF_LINK` or not. Otherwise, the result depends on the operation + * system with `AF_LINK` or `PF_INET`. + */ + if (exclude_type == UV__EXCLUDE_IFPHYS) + return (ent->ifa_addr->sa_family != AF_LINK); +#endif +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) || \ + defined(__HAIKU__) + /* + * On BSD getifaddrs returns information related to the raw underlying + * devices. We're not interested in this information. + */ + if (ent->ifa_addr->sa_family == AF_LINK) + return 1; +#elif defined(__NetBSD__) || defined(__OpenBSD__) + if (ent->ifa_addr->sa_family != PF_INET && + ent->ifa_addr->sa_family != PF_INET6) + return 1; +#endif + return 0; +} + +int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { + struct ifaddrs* addrs; + struct ifaddrs* ent; + uv_interface_address_t* address; +#if !(defined(__CYGWIN__) || defined(__MSYS__)) + int i; +#endif + + *count = 0; + *addresses = NULL; + + if (getifaddrs(&addrs) != 0) + return UV__ERR(errno); + + /* Count the number of interfaces */ + for (ent = addrs; ent != NULL; ent = ent->ifa_next) { + if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR)) + continue; + (*count)++; + } + + if (*count == 0) { + freeifaddrs(addrs); + return 0; + } + + /* Make sure the memory is initiallized to zero using calloc() */ + *addresses = uv__calloc(*count, sizeof(**addresses)); + + if (*addresses == NULL) { + freeifaddrs(addrs); + return UV_ENOMEM; + } + + address = *addresses; + + for (ent = addrs; ent != NULL; ent = ent->ifa_next) { + if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR)) + continue; + + address->name = uv__strdup(ent->ifa_name); + + if (ent->ifa_addr->sa_family == AF_INET6) { + address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr); + } else { + address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr); + } + + if (ent->ifa_netmask == NULL) { + memset(&address->netmask, 0, sizeof(address->netmask)); + } else if (ent->ifa_netmask->sa_family == AF_INET6) { + address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask); + } else { + address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask); + } + + address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK); + + address++; + } + +#if !(defined(__CYGWIN__) || defined(__MSYS__)) + /* Fill in physical addresses for each interface */ + for (ent = addrs; ent != NULL; ent = ent->ifa_next) { + if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS)) + continue; + + address = *addresses; + + for (i = 0; i < *count; i++) { + if (strcmp(address->name, ent->ifa_name) == 0) { + struct sockaddr_dl* sa_addr; + sa_addr = (struct sockaddr_dl*)(ent->ifa_addr); + memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr)); + } + address++; + } + } +#endif + + freeifaddrs(addrs); + + return 0; +} + + +void uv_free_interface_addresses(uv_interface_address_t* addresses, + int count) { + int i; + + for (i = 0; i < count; i++) { + uv__free(addresses[i].name); + } + + uv__free(addresses); +} diff --git a/include/libuv/src/unix/bsd-proctitle.c b/include/libuv/src/unix/bsd-proctitle.c new file mode 100644 index 000000000..723b81c01 --- /dev/null +++ b/include/libuv/src/unix/bsd-proctitle.c @@ -0,0 +1,100 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include + + +static uv_mutex_t process_title_mutex; +static uv_once_t process_title_mutex_once = UV_ONCE_INIT; +static char* process_title; + + +static void init_process_title_mutex_once(void) { + if (uv_mutex_init(&process_title_mutex)) + abort(); +} + + +void uv__process_title_cleanup(void) { + /* TODO(bnoordhuis) uv_mutex_destroy(&process_title_mutex) + * and reset process_title_mutex_once? + */ +} + + +char** uv_setup_args(int argc, char** argv) { + process_title = argc > 0 ? uv__strdup(argv[0]) : NULL; + return argv; +} + + +int uv_set_process_title(const char* title) { + char* new_title; + + new_title = uv__strdup(title); + if (new_title == NULL) + return UV_ENOMEM; + + uv_once(&process_title_mutex_once, init_process_title_mutex_once); + uv_mutex_lock(&process_title_mutex); + + uv__free(process_title); + process_title = new_title; + setproctitle("%s", title); + + uv_mutex_unlock(&process_title_mutex); + + return 0; +} + + +int uv_get_process_title(char* buffer, size_t size) { + size_t len; + + if (buffer == NULL || size == 0) + return UV_EINVAL; + + uv_once(&process_title_mutex_once, init_process_title_mutex_once); + uv_mutex_lock(&process_title_mutex); + + if (process_title != NULL) { + len = strlen(process_title) + 1; + + if (size < len) { + uv_mutex_unlock(&process_title_mutex); + return UV_ENOBUFS; + } + + memcpy(buffer, process_title, len); + } else { + len = 0; + } + + uv_mutex_unlock(&process_title_mutex); + + buffer[len] = '\0'; + + return 0; +} diff --git a/include/libuv/src/unix/core.c b/include/libuv/src/unix/core.c new file mode 100644 index 000000000..1597828c8 --- /dev/null +++ b/include/libuv/src/unix/core.c @@ -0,0 +1,1613 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include /* NULL */ +#include /* printf */ +#include +#include /* strerror */ +#include +#include +#include +#include +#include +#include /* O_CLOEXEC */ +#include +#include +#include +#include +#include +#include /* INT_MAX, PATH_MAX, IOV_MAX */ +#include /* writev */ +#include /* getrusage */ +#include +#include +#include + +#ifdef __sun +# include +# include +# include +#endif + +#if defined(__APPLE__) +# include +# endif /* defined(__APPLE__) */ + + +#if defined(__APPLE__) && !TARGET_OS_IPHONE +# include +# include /* _NSGetExecutablePath */ +# define environ (*_NSGetEnviron()) +#else /* defined(__APPLE__) && !TARGET_OS_IPHONE */ +extern char** environ; +#endif /* !(defined(__APPLE__) && !TARGET_OS_IPHONE) */ + + +#if defined(__DragonFly__) || \ + defined(__FreeBSD__) || \ + defined(__FreeBSD_kernel__) || \ + defined(__NetBSD__) || \ + defined(__OpenBSD__) +# include +# include +# include +# if defined(__FreeBSD__) +# define uv__accept4 accept4 +# endif +# if defined(__NetBSD__) +# define uv__accept4(a, b, c, d) paccept((a), (b), (c), NULL, (d)) +# endif +#endif + +#if defined(__MVS__) +#include +#endif + +#if defined(__linux__) +# include +# define uv__accept4 accept4 +#endif + +static int uv__run_pending(uv_loop_t* loop); + +/* Verify that uv_buf_t is ABI-compatible with struct iovec. */ +STATIC_ASSERT(sizeof(uv_buf_t) == sizeof(struct iovec)); +STATIC_ASSERT(sizeof(&((uv_buf_t*) 0)->base) == + sizeof(((struct iovec*) 0)->iov_base)); +STATIC_ASSERT(sizeof(&((uv_buf_t*) 0)->len) == + sizeof(((struct iovec*) 0)->iov_len)); +STATIC_ASSERT(offsetof(uv_buf_t, base) == offsetof(struct iovec, iov_base)); +STATIC_ASSERT(offsetof(uv_buf_t, len) == offsetof(struct iovec, iov_len)); + + +uint64_t uv_hrtime(void) { + return uv__hrtime(UV_CLOCK_PRECISE); +} + + +void uv_close(uv_handle_t* handle, uv_close_cb close_cb) { + assert(!uv__is_closing(handle)); + + handle->flags |= UV_HANDLE_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); + /* Poll handles use file system requests, and one of them may still be + * running. The poll code will call uv__make_close_pending() for us. */ + return; + + case UV_SIGNAL: + uv__signal_close((uv_signal_t*) handle); + break; + + default: + assert(0); + } + + uv__make_close_pending(handle); +} + +int uv__socket_sockopt(uv_handle_t* handle, int optname, int* value) { + int r; + int fd; + socklen_t len; + + if (handle == NULL || value == NULL) + return UV_EINVAL; + + if (handle->type == UV_TCP || handle->type == UV_NAMED_PIPE) + fd = uv__stream_fd((uv_stream_t*) handle); + else if (handle->type == UV_UDP) + fd = ((uv_udp_t *) handle)->io_watcher.fd; + else + return UV_ENOTSUP; + + len = sizeof(*value); + + if (*value == 0) + r = getsockopt(fd, SOL_SOCKET, optname, value, &len); + else + r = setsockopt(fd, SOL_SOCKET, optname, (const void*) value, len); + + if (r < 0) + return UV__ERR(errno); + + return 0; +} + +void uv__make_close_pending(uv_handle_t* handle) { + assert(handle->flags & UV_HANDLE_CLOSING); + assert(!(handle->flags & UV_HANDLE_CLOSED)); + handle->next_closing = handle->loop->closing_handles; + handle->loop->closing_handles = handle; +} + +int uv__getiovmax(void) { +#if defined(IOV_MAX) + return IOV_MAX; +#elif defined(_SC_IOV_MAX) + static int iovmax_cached = -1; + int iovmax; + + iovmax = uv__load_relaxed(&iovmax_cached); + if (iovmax != -1) + return iovmax; + + /* On some embedded devices (arm-linux-uclibc based ip camera), + * sysconf(_SC_IOV_MAX) can not get the correct value. The return + * value is -1 and the errno is EINPROGRESS. Degrade the value to 1. + */ + iovmax = sysconf(_SC_IOV_MAX); + if (iovmax == -1) + iovmax = 1; + + uv__store_relaxed(&iovmax_cached, iovmax); + + return iovmax; +#else + return 1024; +#endif +} + + +static void uv__finish_close(uv_handle_t* handle) { + uv_signal_t* sh; + + /* Note: while the handle is in the UV_HANDLE_CLOSING state now, it's still + * possible for it to be active in the sense that uv__is_active() returns + * true. + * + * A good example is when the user calls uv_shutdown(), immediately followed + * by uv_close(). The handle is considered active at this point because the + * completion of the shutdown req is still pending. + */ + assert(handle->flags & UV_HANDLE_CLOSING); + assert(!(handle->flags & UV_HANDLE_CLOSED)); + handle->flags |= UV_HANDLE_CLOSED; + + switch (handle->type) { + case UV_PREPARE: + case UV_CHECK: + case UV_IDLE: + case UV_ASYNC: + case UV_TIMER: + case UV_PROCESS: + case UV_FS_EVENT: + case UV_FS_POLL: + case UV_POLL: + break; + + case UV_SIGNAL: + /* If there are any caught signals "trapped" in the signal pipe, + * we can't call the close callback yet. Reinserting the handle + * into the closing queue makes the event loop spin but that's + * okay because we only need to deliver the pending events. + */ + sh = (uv_signal_t*) handle; + if (sh->caught_signals > sh->dispatched_signals) { + handle->flags ^= UV_HANDLE_CLOSED; + uv__make_close_pending(handle); /* Back into the queue. */ + return; + } + break; + + case UV_NAMED_PIPE: + case UV_TCP: + case UV_TTY: + uv__stream_destroy((uv_stream_t*)handle); + break; + + case UV_UDP: + uv__udp_finish_close((uv_udp_t*)handle); + break; + + default: + assert(0); + break; + } + + uv__handle_unref(handle); + QUEUE_REMOVE(&handle->handle_queue); + + if (handle->close_cb) { + handle->close_cb(handle); + } +} + + +static void uv__run_closing_handles(uv_loop_t* loop) { + uv_handle_t* p; + uv_handle_t* q; + + p = loop->closing_handles; + loop->closing_handles = NULL; + + while (p) { + q = p->next_closing; + uv__finish_close(p); + p = q; + } +} + + +int uv_is_closing(const uv_handle_t* handle) { + return uv__is_closing(handle); +} + + +int uv_backend_fd(const uv_loop_t* loop) { + return loop->backend_fd; +} + + +int uv_backend_timeout(const uv_loop_t* loop) { + if (loop->stop_flag != 0) + return 0; + + if (!uv__has_active_handles(loop) && !uv__has_active_reqs(loop)) + return 0; + + if (!QUEUE_EMPTY(&loop->idle_handles)) + return 0; + + if (!QUEUE_EMPTY(&loop->pending_queue)) + return 0; + + if (loop->closing_handles) + return 0; + + return uv__next_timeout(loop); +} + + +static int uv__loop_alive(const uv_loop_t* loop) { + return uv__has_active_handles(loop) || + uv__has_active_reqs(loop) || + loop->closing_handles != NULL; +} + + +int uv_loop_alive(const uv_loop_t* loop) { + return uv__loop_alive(loop); +} + + +int uv_run(uv_loop_t* loop, uv_run_mode mode) { + int timeout; + int r; + int ran_pending; + + r = uv__loop_alive(loop); + if (!r) + uv__update_time(loop); + + while (r != 0 && loop->stop_flag == 0) { + uv__update_time(loop); + uv__run_timers(loop); + ran_pending = uv__run_pending(loop); + uv__run_idle(loop); + uv__run_prepare(loop); + + timeout = 0; + if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT) + timeout = uv_backend_timeout(loop); + + uv__io_poll(loop, timeout); + + /* Run one final update on the provider_idle_time in case uv__io_poll + * returned because the timeout expired, but no events were received. This + * call will be ignored if the provider_entry_time was either never set (if + * the timeout == 0) or was already updated b/c an event was received. + */ + uv__metrics_update_idle_time(loop); + + uv__run_check(loop); + uv__run_closing_handles(loop); + + if (mode == UV_RUN_ONCE) { + /* UV_RUN_ONCE implies forward progress: at least one callback must have + * been invoked when it returns. uv__io_poll() can return without doing + * I/O (meaning: no callbacks) when its timeout expires - which means we + * have pending timers that satisfy the forward progress constraint. + * + * UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from + * the check. + */ + uv__update_time(loop); + uv__run_timers(loop); + } + + r = uv__loop_alive(loop); + if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT) + break; + } + + /* The if statement lets gcc compile it to a conditional store. Avoids + * dirtying a cache line. + */ + if (loop->stop_flag != 0) + loop->stop_flag = 0; + + return r; +} + + +void uv_update_time(uv_loop_t* loop) { + uv__update_time(loop); +} + + +int uv_is_active(const uv_handle_t* handle) { + return uv__is_active(handle); +} + + +/* Open a socket in non-blocking close-on-exec mode, atomically if possible. */ +int uv__socket(int domain, int type, int protocol) { + int sockfd; + int err; + +#if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC) + sockfd = socket(domain, type | SOCK_NONBLOCK | SOCK_CLOEXEC, protocol); + if (sockfd != -1) + return sockfd; + + if (errno != EINVAL) + return UV__ERR(errno); +#endif + + sockfd = socket(domain, type, protocol); + if (sockfd == -1) + return UV__ERR(errno); + + err = uv__nonblock(sockfd, 1); + if (err == 0) + err = uv__cloexec(sockfd, 1); + + if (err) { + uv__close(sockfd); + return err; + } + +#if defined(SO_NOSIGPIPE) + { + int on = 1; + setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on)); + } +#endif + + return sockfd; +} + +/* get a file pointer to a file in read-only and close-on-exec mode */ +FILE* uv__open_file(const char* path) { + int fd; + FILE* fp; + + fd = uv__open_cloexec(path, O_RDONLY); + if (fd < 0) + return NULL; + + fp = fdopen(fd, "r"); + if (fp == NULL) + uv__close(fd); + + return fp; +} + + +int uv__accept(int sockfd) { + int peerfd; + int err; + + (void) &err; + assert(sockfd >= 0); + + do +#ifdef uv__accept4 + peerfd = uv__accept4(sockfd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); +#else + peerfd = accept(sockfd, NULL, NULL); +#endif + while (peerfd == -1 && errno == EINTR); + + if (peerfd == -1) + return UV__ERR(errno); + +#ifndef uv__accept4 + err = uv__cloexec(peerfd, 1); + if (err == 0) + err = uv__nonblock(peerfd, 1); + + if (err != 0) { + uv__close(peerfd); + return err; + } +#endif + + return peerfd; +} + + +/* close() on macos has the "interesting" quirk that it fails with EINTR + * without closing the file descriptor when a thread is in the cancel state. + * That's why libuv calls close$NOCANCEL() instead. + * + * glibc on linux has a similar issue: close() is a cancellation point and + * will unwind the thread when it's in the cancel state. Work around that + * by making the system call directly. Musl libc is unaffected. + */ +int uv__close_nocancel(int fd) { +#if defined(__APPLE__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdollar-in-identifier-extension" +#if defined(__LP64__) || TARGET_OS_IPHONE + extern int close$NOCANCEL(int); + return close$NOCANCEL(fd); +#else + extern int close$NOCANCEL$UNIX2003(int); + return close$NOCANCEL$UNIX2003(fd); +#endif +#pragma GCC diagnostic pop +#elif defined(__linux__) + return syscall(SYS_close, fd); +#else + return close(fd); +#endif +} + + +int uv__close_nocheckstdio(int fd) { + int saved_errno; + int rc; + + assert(fd > -1); /* Catch uninitialized io_watcher.fd bugs. */ + + saved_errno = errno; + rc = uv__close_nocancel(fd); + if (rc == -1) { + rc = UV__ERR(errno); + if (rc == UV_EINTR || rc == UV__ERR(EINPROGRESS)) + rc = 0; /* The close is in progress, not an error. */ + errno = saved_errno; + } + + return rc; +} + + +int uv__close(int fd) { + assert(fd > STDERR_FILENO); /* Catch stdio close bugs. */ +#if defined(__MVS__) + SAVE_ERRNO(epoll_file_close(fd)); +#endif + return uv__close_nocheckstdio(fd); +} + + +int uv__nonblock_ioctl(int fd, int set) { + int r; + + do + r = ioctl(fd, FIONBIO, &set); + while (r == -1 && errno == EINTR); + + if (r) + return UV__ERR(errno); + + return 0; +} + + +#if !defined(__CYGWIN__) && !defined(__MSYS__) && !defined(__HAIKU__) +int uv__cloexec_ioctl(int fd, int set) { + int r; + + do + r = ioctl(fd, set ? FIOCLEX : FIONCLEX); + while (r == -1 && errno == EINTR); + + if (r) + return UV__ERR(errno); + + return 0; +} +#endif + + +int uv__nonblock_fcntl(int fd, int set) { + int flags; + int r; + + do + r = fcntl(fd, F_GETFL); + while (r == -1 && errno == EINTR); + + if (r == -1) + return UV__ERR(errno); + + /* Bail out now if already set/clear. */ + if (!!(r & O_NONBLOCK) == !!set) + return 0; + + if (set) + flags = r | O_NONBLOCK; + else + flags = r & ~O_NONBLOCK; + + do + r = fcntl(fd, F_SETFL, flags); + while (r == -1 && errno == EINTR); + + if (r) + return UV__ERR(errno); + + return 0; +} + + +int uv__cloexec_fcntl(int fd, int set) { + int flags; + int r; + + do + r = fcntl(fd, F_GETFD); + while (r == -1 && errno == EINTR); + + if (r == -1) + return UV__ERR(errno); + + /* Bail out now if already set/clear. */ + if (!!(r & FD_CLOEXEC) == !!set) + return 0; + + if (set) + flags = r | FD_CLOEXEC; + else + flags = r & ~FD_CLOEXEC; + + do + r = fcntl(fd, F_SETFD, flags); + while (r == -1 && errno == EINTR); + + if (r) + return UV__ERR(errno); + + return 0; +} + + +ssize_t uv__recvmsg(int fd, struct msghdr* msg, int flags) { + struct cmsghdr* cmsg; + ssize_t rc; + int* pfd; + int* end; +#if defined(__linux__) + static int no_msg_cmsg_cloexec; + if (0 == uv__load_relaxed(&no_msg_cmsg_cloexec)) { + rc = recvmsg(fd, msg, flags | 0x40000000); /* MSG_CMSG_CLOEXEC */ + if (rc != -1) + return rc; + if (errno != EINVAL) + return UV__ERR(errno); + rc = recvmsg(fd, msg, flags); + if (rc == -1) + return UV__ERR(errno); + uv__store_relaxed(&no_msg_cmsg_cloexec, 1); + } else { + rc = recvmsg(fd, msg, flags); + } +#else + rc = recvmsg(fd, msg, flags); +#endif + if (rc == -1) + return UV__ERR(errno); + if (msg->msg_controllen == 0) + return rc; + for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) + if (cmsg->cmsg_type == SCM_RIGHTS) + for (pfd = (int*) CMSG_DATA(cmsg), + end = (int*) ((char*) cmsg + cmsg->cmsg_len); + pfd < end; + pfd += 1) + uv__cloexec(*pfd, 1); + return rc; +} + + +int uv_cwd(char* buffer, size_t* size) { + char scratch[1 + UV__PATH_MAX]; + + if (buffer == NULL || size == NULL) + return UV_EINVAL; + + /* Try to read directly into the user's buffer first... */ + if (getcwd(buffer, *size) != NULL) + goto fixup; + + if (errno != ERANGE) + return UV__ERR(errno); + + /* ...or into scratch space if the user's buffer is too small + * so we can report how much space to provide on the next try. + */ + if (getcwd(scratch, sizeof(scratch)) == NULL) + return UV__ERR(errno); + + buffer = scratch; + +fixup: + + *size = strlen(buffer); + + if (*size > 1 && buffer[*size - 1] == '/') { + *size -= 1; + buffer[*size] = '\0'; + } + + if (buffer == scratch) { + *size += 1; + return UV_ENOBUFS; + } + + return 0; +} + + +int uv_chdir(const char* dir) { + if (chdir(dir)) + return UV__ERR(errno); + + return 0; +} + + +void uv_disable_stdio_inheritance(void) { + int fd; + + /* Set the CLOEXEC flag on all open descriptors. Unconditionally try the + * first 16 file descriptors. After that, bail out after the first error. + */ + for (fd = 0; ; fd++) + if (uv__cloexec(fd, 1) && fd > 15) + break; +} + + +int uv_fileno(const uv_handle_t* handle, uv_os_fd_t* fd) { + int fd_out; + + switch (handle->type) { + case UV_TCP: + case UV_NAMED_PIPE: + case UV_TTY: + fd_out = uv__stream_fd((uv_stream_t*) handle); + break; + + case UV_UDP: + fd_out = ((uv_udp_t *) handle)->io_watcher.fd; + break; + + case UV_POLL: + fd_out = ((uv_poll_t *) handle)->io_watcher.fd; + break; + + default: + return UV_EINVAL; + } + + if (uv__is_closing(handle) || fd_out == -1) + return UV_EBADF; + + *fd = fd_out; + return 0; +} + + +static int uv__run_pending(uv_loop_t* loop) { + QUEUE* q; + QUEUE pq; + uv__io_t* w; + + if (QUEUE_EMPTY(&loop->pending_queue)) + return 0; + + QUEUE_MOVE(&loop->pending_queue, &pq); + + while (!QUEUE_EMPTY(&pq)) { + q = QUEUE_HEAD(&pq); + QUEUE_REMOVE(q); + QUEUE_INIT(q); + w = QUEUE_DATA(q, uv__io_t, pending_queue); + w->cb(loop, w, POLLOUT); + } + + return 1; +} + + +static unsigned int next_power_of_two(unsigned int val) { + val -= 1; + val |= val >> 1; + val |= val >> 2; + val |= val >> 4; + val |= val >> 8; + val |= val >> 16; + val += 1; + return val; +} + +static void maybe_resize(uv_loop_t* loop, unsigned int len) { + uv__io_t** watchers; + void* fake_watcher_list; + void* fake_watcher_count; + unsigned int nwatchers; + unsigned int i; + + if (len <= loop->nwatchers) + return; + + /* Preserve fake watcher list and count at the end of the watchers */ + if (loop->watchers != NULL) { + fake_watcher_list = loop->watchers[loop->nwatchers]; + fake_watcher_count = loop->watchers[loop->nwatchers + 1]; + } else { + fake_watcher_list = NULL; + fake_watcher_count = NULL; + } + + nwatchers = next_power_of_two(len + 2) - 2; + watchers = uv__reallocf(loop->watchers, + (nwatchers + 2) * sizeof(loop->watchers[0])); + + if (watchers == NULL) + abort(); + for (i = loop->nwatchers; i < nwatchers; i++) + watchers[i] = NULL; + watchers[nwatchers] = fake_watcher_list; + watchers[nwatchers + 1] = fake_watcher_count; + + loop->watchers = watchers; + loop->nwatchers = nwatchers; +} + + +void uv__io_init(uv__io_t* w, uv__io_cb cb, int fd) { + assert(cb != NULL); + assert(fd >= -1); + QUEUE_INIT(&w->pending_queue); + QUEUE_INIT(&w->watcher_queue); + w->cb = cb; + w->fd = fd; + w->events = 0; + w->pevents = 0; + +#if defined(UV_HAVE_KQUEUE) + w->rcount = 0; + w->wcount = 0; +#endif /* defined(UV_HAVE_KQUEUE) */ +} + + +void uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events) { + assert(0 == (events & ~(POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI))); + assert(0 != events); + assert(w->fd >= 0); + assert(w->fd < INT_MAX); + + w->pevents |= events; + maybe_resize(loop, w->fd + 1); + +#if !defined(__sun) + /* The event ports backend needs to rearm all file descriptors on each and + * every tick of the event loop but the other backends allow us to + * short-circuit here if the event mask is unchanged. + */ + if (w->events == w->pevents) + return; +#endif + + if (QUEUE_EMPTY(&w->watcher_queue)) + QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue); + + if (loop->watchers[w->fd] == NULL) { + loop->watchers[w->fd] = w; + loop->nfds++; + } +} + + +void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events) { + assert(0 == (events & ~(POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI))); + assert(0 != events); + + if (w->fd == -1) + return; + + assert(w->fd >= 0); + + /* Happens when uv__io_stop() is called on a handle that was never started. */ + if ((unsigned) w->fd >= loop->nwatchers) + return; + + w->pevents &= ~events; + + if (w->pevents == 0) { + QUEUE_REMOVE(&w->watcher_queue); + QUEUE_INIT(&w->watcher_queue); + + if (loop->watchers[w->fd] != NULL) { + assert(loop->watchers[w->fd] == w); + assert(loop->nfds > 0); + loop->watchers[w->fd] = NULL; + loop->nfds--; + w->events = 0; + } + } + else if (QUEUE_EMPTY(&w->watcher_queue)) + QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue); +} + + +void uv__io_close(uv_loop_t* loop, uv__io_t* w) { + uv__io_stop(loop, w, POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI); + QUEUE_REMOVE(&w->pending_queue); + + /* Remove stale events for this file descriptor */ + if (w->fd != -1) + uv__platform_invalidate_fd(loop, w->fd); +} + + +void uv__io_feed(uv_loop_t* loop, uv__io_t* w) { + if (QUEUE_EMPTY(&w->pending_queue)) + QUEUE_INSERT_TAIL(&loop->pending_queue, &w->pending_queue); +} + + +int uv__io_active(const uv__io_t* w, unsigned int events) { + assert(0 == (events & ~(POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI))); + assert(0 != events); + return 0 != (w->pevents & events); +} + + +int uv__fd_exists(uv_loop_t* loop, int fd) { + return (unsigned) fd < loop->nwatchers && loop->watchers[fd] != NULL; +} + + +int uv_getrusage(uv_rusage_t* rusage) { + struct rusage usage; + + if (getrusage(RUSAGE_SELF, &usage)) + return UV__ERR(errno); + + rusage->ru_utime.tv_sec = usage.ru_utime.tv_sec; + rusage->ru_utime.tv_usec = usage.ru_utime.tv_usec; + + rusage->ru_stime.tv_sec = usage.ru_stime.tv_sec; + rusage->ru_stime.tv_usec = usage.ru_stime.tv_usec; + +#if !defined(__MVS__) && !defined(__HAIKU__) + rusage->ru_maxrss = usage.ru_maxrss; + rusage->ru_ixrss = usage.ru_ixrss; + rusage->ru_idrss = usage.ru_idrss; + rusage->ru_isrss = usage.ru_isrss; + rusage->ru_minflt = usage.ru_minflt; + rusage->ru_majflt = usage.ru_majflt; + rusage->ru_nswap = usage.ru_nswap; + rusage->ru_inblock = usage.ru_inblock; + rusage->ru_oublock = usage.ru_oublock; + rusage->ru_msgsnd = usage.ru_msgsnd; + rusage->ru_msgrcv = usage.ru_msgrcv; + rusage->ru_nsignals = usage.ru_nsignals; + rusage->ru_nvcsw = usage.ru_nvcsw; + rusage->ru_nivcsw = usage.ru_nivcsw; +#endif + + return 0; +} + + +int uv__open_cloexec(const char* path, int flags) { +#if defined(O_CLOEXEC) + int fd; + + fd = open(path, flags | O_CLOEXEC); + if (fd == -1) + return UV__ERR(errno); + + return fd; +#else /* O_CLOEXEC */ + int err; + int fd; + + fd = open(path, flags); + if (fd == -1) + return UV__ERR(errno); + + err = uv__cloexec(fd, 1); + if (err) { + uv__close(fd); + return err; + } + + return fd; +#endif /* O_CLOEXEC */ +} + + +int uv__dup2_cloexec(int oldfd, int newfd) { +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__linux__) + int r; + + r = dup3(oldfd, newfd, O_CLOEXEC); + if (r == -1) + return UV__ERR(errno); + + return r; +#else + int err; + int r; + + r = dup2(oldfd, newfd); /* Never retry. */ + if (r == -1) + return UV__ERR(errno); + + err = uv__cloexec(newfd, 1); + if (err != 0) { + uv__close(newfd); + return err; + } + + return r; +#endif +} + + +int uv_os_homedir(char* buffer, size_t* size) { + uv_passwd_t pwd; + size_t len; + int r; + + /* Check if the HOME environment variable is set first. The task of + performing input validation on buffer and size is taken care of by + uv_os_getenv(). */ + r = uv_os_getenv("HOME", buffer, size); + + if (r != UV_ENOENT) + return r; + + /* HOME is not set, so call uv__getpwuid_r() */ + r = uv__getpwuid_r(&pwd); + + if (r != 0) { + return r; + } + + len = strlen(pwd.homedir); + + if (len >= *size) { + *size = len + 1; + uv_os_free_passwd(&pwd); + return UV_ENOBUFS; + } + + memcpy(buffer, pwd.homedir, len + 1); + *size = len; + uv_os_free_passwd(&pwd); + + return 0; +} + + +int uv_os_tmpdir(char* buffer, size_t* size) { + const char* buf; + size_t len; + + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + +#define CHECK_ENV_VAR(name) \ + do { \ + buf = getenv(name); \ + if (buf != NULL) \ + goto return_buffer; \ + } \ + while (0) + + /* Check the TMPDIR, TMP, TEMP, and TEMPDIR environment variables in order */ + CHECK_ENV_VAR("TMPDIR"); + CHECK_ENV_VAR("TMP"); + CHECK_ENV_VAR("TEMP"); + CHECK_ENV_VAR("TEMPDIR"); + +#undef CHECK_ENV_VAR + + /* No temp environment variables defined */ + #if defined(__ANDROID__) + buf = "/data/local/tmp"; + #else + buf = "/tmp"; + #endif + +return_buffer: + len = strlen(buf); + + if (len >= *size) { + *size = len + 1; + return UV_ENOBUFS; + } + + /* The returned directory should not have a trailing slash. */ + if (len > 1 && buf[len - 1] == '/') { + len--; + } + + memcpy(buffer, buf, len + 1); + buffer[len] = '\0'; + *size = len; + + return 0; +} + + +int uv__getpwuid_r(uv_passwd_t* pwd) { + struct passwd pw; + struct passwd* result; + char* buf; + uid_t uid; + size_t bufsize; + size_t name_size; + size_t homedir_size; + size_t shell_size; + long initsize; + int r; + + if (pwd == NULL) + return UV_EINVAL; + + initsize = sysconf(_SC_GETPW_R_SIZE_MAX); + + if (initsize <= 0) + bufsize = 4096; + else + bufsize = (size_t) initsize; + + uid = geteuid(); + buf = NULL; + + for (;;) { + uv__free(buf); + buf = uv__malloc(bufsize); + + if (buf == NULL) + return UV_ENOMEM; + + r = getpwuid_r(uid, &pw, buf, bufsize, &result); + + if (r != ERANGE) + break; + + bufsize *= 2; + } + + if (r != 0) { + uv__free(buf); + return -r; + } + + if (result == NULL) { + uv__free(buf); + return UV_ENOENT; + } + + /* Allocate memory for the username, shell, and home directory */ + name_size = strlen(pw.pw_name) + 1; + homedir_size = strlen(pw.pw_dir) + 1; + shell_size = strlen(pw.pw_shell) + 1; + pwd->username = uv__malloc(name_size + homedir_size + shell_size); + + if (pwd->username == NULL) { + uv__free(buf); + return UV_ENOMEM; + } + + /* Copy the username */ + memcpy(pwd->username, pw.pw_name, name_size); + + /* Copy the home directory */ + pwd->homedir = pwd->username + name_size; + memcpy(pwd->homedir, pw.pw_dir, homedir_size); + + /* Copy the shell */ + pwd->shell = pwd->homedir + homedir_size; + memcpy(pwd->shell, pw.pw_shell, shell_size); + + /* Copy the uid and gid */ + pwd->uid = pw.pw_uid; + pwd->gid = pw.pw_gid; + + uv__free(buf); + + return 0; +} + + +void uv_os_free_passwd(uv_passwd_t* pwd) { + if (pwd == NULL) + return; + + /* + The memory for name, shell, and homedir are allocated in a single + uv__malloc() call. The base of the pointer is stored in pwd->username, so + that is the field that needs to be freed. + */ + uv__free(pwd->username); + pwd->username = NULL; + pwd->shell = NULL; + pwd->homedir = NULL; +} + + +int uv_os_get_passwd(uv_passwd_t* pwd) { + return uv__getpwuid_r(pwd); +} + + +int uv_translate_sys_error(int sys_errno) { + /* If < 0 then it's already a libuv error. */ + return sys_errno <= 0 ? sys_errno : -sys_errno; +} + + +int uv_os_environ(uv_env_item_t** envitems, int* count) { + int i, j, cnt; + uv_env_item_t* envitem; + + *envitems = NULL; + *count = 0; + + for (i = 0; environ[i] != NULL; i++); + + *envitems = uv__calloc(i, sizeof(**envitems)); + + if (*envitems == NULL) + return UV_ENOMEM; + + for (j = 0, cnt = 0; j < i; j++) { + char* buf; + char* ptr; + + if (environ[j] == NULL) + break; + + buf = uv__strdup(environ[j]); + if (buf == NULL) + goto fail; + + ptr = strchr(buf, '='); + if (ptr == NULL) { + uv__free(buf); + continue; + } + + *ptr = '\0'; + + envitem = &(*envitems)[cnt]; + envitem->name = buf; + envitem->value = ptr + 1; + + cnt++; + } + + *count = cnt; + return 0; + +fail: + for (i = 0; i < cnt; i++) { + envitem = &(*envitems)[cnt]; + uv__free(envitem->name); + } + uv__free(*envitems); + + *envitems = NULL; + *count = 0; + return UV_ENOMEM; +} + + +int uv_os_getenv(const char* name, char* buffer, size_t* size) { + char* var; + size_t len; + + if (name == NULL || buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + + var = getenv(name); + + if (var == NULL) + return UV_ENOENT; + + len = strlen(var); + + if (len >= *size) { + *size = len + 1; + return UV_ENOBUFS; + } + + memcpy(buffer, var, len + 1); + *size = len; + + return 0; +} + + +int uv_os_setenv(const char* name, const char* value) { + if (name == NULL || value == NULL) + return UV_EINVAL; + + if (setenv(name, value, 1) != 0) + return UV__ERR(errno); + + return 0; +} + + +int uv_os_unsetenv(const char* name) { + if (name == NULL) + return UV_EINVAL; + + if (unsetenv(name) != 0) + return UV__ERR(errno); + + return 0; +} + + +int uv_os_gethostname(char* buffer, size_t* size) { + /* + On some platforms, if the input buffer is not large enough, gethostname() + succeeds, but truncates the result. libuv can detect this and return ENOBUFS + instead by creating a large enough buffer and comparing the hostname length + to the size input. + */ + char buf[UV_MAXHOSTNAMESIZE]; + size_t len; + + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + + if (gethostname(buf, sizeof(buf)) != 0) + return UV__ERR(errno); + + buf[sizeof(buf) - 1] = '\0'; /* Null terminate, just to be safe. */ + len = strlen(buf); + + if (len >= *size) { + *size = len + 1; + return UV_ENOBUFS; + } + + memcpy(buffer, buf, len + 1); + *size = len; + return 0; +} + + +uv_os_fd_t uv_get_osfhandle(int fd) { + return fd; +} + +int uv_open_osfhandle(uv_os_fd_t os_fd) { + return os_fd; +} + +uv_pid_t uv_os_getpid(void) { + return getpid(); +} + + +uv_pid_t uv_os_getppid(void) { + return getppid(); +} + + +int uv_os_getpriority(uv_pid_t pid, int* priority) { + int r; + + if (priority == NULL) + return UV_EINVAL; + + errno = 0; + r = getpriority(PRIO_PROCESS, (int) pid); + + if (r == -1 && errno != 0) + return UV__ERR(errno); + + *priority = r; + return 0; +} + + +int uv_os_setpriority(uv_pid_t pid, int priority) { + if (priority < UV_PRIORITY_HIGHEST || priority > UV_PRIORITY_LOW) + return UV_EINVAL; + + if (setpriority(PRIO_PROCESS, (int) pid, priority) != 0) + return UV__ERR(errno); + + return 0; +} + + +int uv_os_uname(uv_utsname_t* buffer) { + struct utsname buf; + int r; + + if (buffer == NULL) + return UV_EINVAL; + + if (uname(&buf) == -1) { + r = UV__ERR(errno); + goto error; + } + + r = uv__strscpy(buffer->sysname, buf.sysname, sizeof(buffer->sysname)); + if (r == UV_E2BIG) + goto error; + +#ifdef _AIX + r = snprintf(buffer->release, + sizeof(buffer->release), + "%s.%s", + buf.version, + buf.release); + if (r >= sizeof(buffer->release)) { + r = UV_E2BIG; + goto error; + } +#else + r = uv__strscpy(buffer->release, buf.release, sizeof(buffer->release)); + if (r == UV_E2BIG) + goto error; +#endif + + r = uv__strscpy(buffer->version, buf.version, sizeof(buffer->version)); + if (r == UV_E2BIG) + goto error; + +#if defined(_AIX) || defined(__PASE__) + r = uv__strscpy(buffer->machine, "ppc64", sizeof(buffer->machine)); +#else + r = uv__strscpy(buffer->machine, buf.machine, sizeof(buffer->machine)); +#endif + + if (r == UV_E2BIG) + goto error; + + return 0; + +error: + buffer->sysname[0] = '\0'; + buffer->release[0] = '\0'; + buffer->version[0] = '\0'; + buffer->machine[0] = '\0'; + return r; +} + +int uv__getsockpeername(const uv_handle_t* handle, + uv__peersockfunc func, + struct sockaddr* name, + int* namelen) { + socklen_t socklen; + uv_os_fd_t fd; + int r; + + r = uv_fileno(handle, &fd); + if (r < 0) + return r; + + /* sizeof(socklen_t) != sizeof(int) on some systems. */ + socklen = (socklen_t) *namelen; + + if (func(fd, name, &socklen)) + return UV__ERR(errno); + + *namelen = (int) socklen; + return 0; +} + +int uv_gettimeofday(uv_timeval64_t* tv) { + struct timeval time; + + if (tv == NULL) + return UV_EINVAL; + + if (gettimeofday(&time, NULL) != 0) + return UV__ERR(errno); + + tv->tv_sec = (int64_t) time.tv_sec; + tv->tv_usec = (int32_t) time.tv_usec; + return 0; +} + +void uv_sleep(unsigned int msec) { + struct timespec timeout; + int rc; + + timeout.tv_sec = msec / 1000; + timeout.tv_nsec = (msec % 1000) * 1000 * 1000; + + do + rc = nanosleep(&timeout, &timeout); + while (rc == -1 && errno == EINTR); + + assert(rc == 0); +} + +int uv__search_path(const char* prog, char* buf, size_t* buflen) { + char abspath[UV__PATH_MAX]; + size_t abspath_size; + char trypath[UV__PATH_MAX]; + char* cloned_path; + char* path_env; + char* token; + + if (buf == NULL || buflen == NULL || *buflen == 0) + return UV_EINVAL; + + /* + * Possibilities for prog: + * i) an absolute path such as: /home/user/myprojects/nodejs/node + * ii) a relative path such as: ./node or ../myprojects/nodejs/node + * iii) a bare filename such as "node", after exporting PATH variable + * to its location. + */ + + /* Case i) and ii) absolute or relative paths */ + if (strchr(prog, '/') != NULL) { + if (realpath(prog, abspath) != abspath) + return UV__ERR(errno); + + abspath_size = strlen(abspath); + + *buflen -= 1; + if (*buflen > abspath_size) + *buflen = abspath_size; + + memcpy(buf, abspath, *buflen); + buf[*buflen] = '\0'; + + return 0; + } + + /* Case iii). Search PATH environment variable */ + cloned_path = NULL; + token = NULL; + path_env = getenv("PATH"); + + if (path_env == NULL) + return UV_EINVAL; + + cloned_path = uv__strdup(path_env); + if (cloned_path == NULL) + return UV_ENOMEM; + + token = strtok(cloned_path, ":"); + while (token != NULL) { + snprintf(trypath, sizeof(trypath) - 1, "%s/%s", token, prog); + if (realpath(trypath, abspath) == abspath) { + /* Check the match is executable */ + if (access(abspath, X_OK) == 0) { + abspath_size = strlen(abspath); + + *buflen -= 1; + if (*buflen > abspath_size) + *buflen = abspath_size; + + memcpy(buf, abspath, *buflen); + buf[*buflen] = '\0'; + + uv__free(cloned_path); + return 0; + } + } + token = strtok(NULL, ":"); + } + uv__free(cloned_path); + + /* Out of tokens (path entries), and no match found */ + return UV_EINVAL; +} diff --git a/include/libuv/src/unix/cygwin.c b/include/libuv/src/unix/cygwin.c new file mode 100644 index 000000000..169958d55 --- /dev/null +++ b/include/libuv/src/unix/cygwin.c @@ -0,0 +1,53 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include + +int uv_uptime(double* uptime) { + struct sysinfo info; + + if (sysinfo(&info) < 0) + return UV__ERR(errno); + + *uptime = info.uptime; + return 0; +} + +int uv_resident_set_memory(size_t* rss) { + /* FIXME: read /proc/meminfo? */ + *rss = 0; + return 0; +} + +int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + /* FIXME: read /proc/stat? */ + *cpu_infos = NULL; + *count = 0; + return UV_ENOSYS; +} + +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} diff --git a/include/libuv/src/unix/darwin-proctitle.c b/include/libuv/src/unix/darwin-proctitle.c new file mode 100644 index 000000000..5288083ef --- /dev/null +++ b/include/libuv/src/unix/darwin-proctitle.c @@ -0,0 +1,192 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include +#include +#include + +#include + +#if !TARGET_OS_IPHONE +#include "darwin-stub.h" +#endif + + +static int uv__pthread_setname_np(const char* name) { + char namebuf[64]; /* MAXTHREADNAMESIZE */ + int err; + + strncpy(namebuf, name, sizeof(namebuf) - 1); + namebuf[sizeof(namebuf) - 1] = '\0'; + + err = pthread_setname_np(namebuf); + if (err) + return UV__ERR(err); + + return 0; +} + + +int uv__set_process_title(const char* title) { +#if TARGET_OS_IPHONE + return uv__pthread_setname_np(title); +#else + CFStringRef (*pCFStringCreateWithCString)(CFAllocatorRef, + const char*, + CFStringEncoding); + CFBundleRef (*pCFBundleGetBundleWithIdentifier)(CFStringRef); + void *(*pCFBundleGetDataPointerForName)(CFBundleRef, CFStringRef); + void *(*pCFBundleGetFunctionPointerForName)(CFBundleRef, CFStringRef); + CFTypeRef (*pLSGetCurrentApplicationASN)(void); + OSStatus (*pLSSetApplicationInformationItem)(int, + CFTypeRef, + CFStringRef, + CFStringRef, + CFDictionaryRef*); + void* application_services_handle; + void* core_foundation_handle; + CFBundleRef launch_services_bundle; + CFStringRef* display_name_key; + CFDictionaryRef (*pCFBundleGetInfoDictionary)(CFBundleRef); + CFBundleRef (*pCFBundleGetMainBundle)(void); + CFDictionaryRef (*pLSApplicationCheckIn)(int, CFDictionaryRef); + void (*pLSSetApplicationLaunchServicesServerConnectionStatus)(uint64_t, + void*); + CFTypeRef asn; + int err; + + err = UV_ENOENT; + application_services_handle = dlopen("/System/Library/Frameworks/" + "ApplicationServices.framework/" + "Versions/A/ApplicationServices", + RTLD_LAZY | RTLD_LOCAL); + core_foundation_handle = dlopen("/System/Library/Frameworks/" + "CoreFoundation.framework/" + "Versions/A/CoreFoundation", + RTLD_LAZY | RTLD_LOCAL); + + if (application_services_handle == NULL || core_foundation_handle == NULL) + goto out; + + *(void **)(&pCFStringCreateWithCString) = + dlsym(core_foundation_handle, "CFStringCreateWithCString"); + *(void **)(&pCFBundleGetBundleWithIdentifier) = + dlsym(core_foundation_handle, "CFBundleGetBundleWithIdentifier"); + *(void **)(&pCFBundleGetDataPointerForName) = + dlsym(core_foundation_handle, "CFBundleGetDataPointerForName"); + *(void **)(&pCFBundleGetFunctionPointerForName) = + dlsym(core_foundation_handle, "CFBundleGetFunctionPointerForName"); + + if (pCFStringCreateWithCString == NULL || + pCFBundleGetBundleWithIdentifier == NULL || + pCFBundleGetDataPointerForName == NULL || + pCFBundleGetFunctionPointerForName == NULL) { + goto out; + } + +#define S(s) pCFStringCreateWithCString(NULL, (s), kCFStringEncodingUTF8) + + launch_services_bundle = + pCFBundleGetBundleWithIdentifier(S("com.apple.LaunchServices")); + + if (launch_services_bundle == NULL) + goto out; + + *(void **)(&pLSGetCurrentApplicationASN) = + pCFBundleGetFunctionPointerForName(launch_services_bundle, + S("_LSGetCurrentApplicationASN")); + + if (pLSGetCurrentApplicationASN == NULL) + goto out; + + *(void **)(&pLSSetApplicationInformationItem) = + pCFBundleGetFunctionPointerForName(launch_services_bundle, + S("_LSSetApplicationInformationItem")); + + if (pLSSetApplicationInformationItem == NULL) + goto out; + + display_name_key = pCFBundleGetDataPointerForName(launch_services_bundle, + S("_kLSDisplayNameKey")); + + if (display_name_key == NULL || *display_name_key == NULL) + goto out; + + *(void **)(&pCFBundleGetInfoDictionary) = dlsym(core_foundation_handle, + "CFBundleGetInfoDictionary"); + *(void **)(&pCFBundleGetMainBundle) = dlsym(core_foundation_handle, + "CFBundleGetMainBundle"); + if (pCFBundleGetInfoDictionary == NULL || pCFBundleGetMainBundle == NULL) + goto out; + + *(void **)(&pLSApplicationCheckIn) = pCFBundleGetFunctionPointerForName( + launch_services_bundle, + S("_LSApplicationCheckIn")); + + if (pLSApplicationCheckIn == NULL) + goto out; + + *(void **)(&pLSSetApplicationLaunchServicesServerConnectionStatus) = + pCFBundleGetFunctionPointerForName( + launch_services_bundle, + S("_LSSetApplicationLaunchServicesServerConnectionStatus")); + + if (pLSSetApplicationLaunchServicesServerConnectionStatus == NULL) + goto out; + + pLSSetApplicationLaunchServicesServerConnectionStatus(0, NULL); + + /* Check into process manager?! */ + pLSApplicationCheckIn(-2, + pCFBundleGetInfoDictionary(pCFBundleGetMainBundle())); + + asn = pLSGetCurrentApplicationASN(); + + err = UV_EBUSY; + if (asn == NULL) + goto out; + + err = UV_EINVAL; + if (pLSSetApplicationInformationItem(-2, /* Magic value. */ + asn, + *display_name_key, + S(title), + NULL) != noErr) { + goto out; + } + + uv__pthread_setname_np(title); /* Don't care if it fails. */ + err = 0; + +out: + if (core_foundation_handle != NULL) + dlclose(core_foundation_handle); + + if (application_services_handle != NULL) + dlclose(application_services_handle); + + return err; +#endif /* !TARGET_OS_IPHONE */ +} diff --git a/include/libuv/src/unix/darwin-stub.h b/include/libuv/src/unix/darwin-stub.h new file mode 100644 index 000000000..433e3efa7 --- /dev/null +++ b/include/libuv/src/unix/darwin-stub.h @@ -0,0 +1,113 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef UV_DARWIN_STUB_H_ +#define UV_DARWIN_STUB_H_ + +#include + +struct CFArrayCallBacks; +struct CFRunLoopSourceContext; +struct FSEventStreamContext; +struct CFRange; + +typedef double CFAbsoluteTime; +typedef double CFTimeInterval; +typedef int FSEventStreamEventFlags; +typedef int OSStatus; +typedef long CFIndex; +typedef struct CFArrayCallBacks CFArrayCallBacks; +typedef struct CFRunLoopSourceContext CFRunLoopSourceContext; +typedef struct FSEventStreamContext FSEventStreamContext; +typedef uint32_t FSEventStreamCreateFlags; +typedef uint64_t FSEventStreamEventId; +typedef unsigned CFStringEncoding; +typedef void* CFAllocatorRef; +typedef void* CFArrayRef; +typedef void* CFBundleRef; +typedef void* CFDataRef; +typedef void* CFDictionaryRef; +typedef void* CFMutableDictionaryRef; +typedef struct CFRange CFRange; +typedef void* CFRunLoopRef; +typedef void* CFRunLoopSourceRef; +typedef void* CFStringRef; +typedef void* CFTypeRef; +typedef void* FSEventStreamRef; + +typedef uint32_t IOOptionBits; +typedef unsigned int io_iterator_t; +typedef unsigned int io_object_t; +typedef unsigned int io_service_t; +typedef unsigned int io_registry_entry_t; + + +typedef void (*FSEventStreamCallback)(const FSEventStreamRef, + void*, + size_t, + void*, + const FSEventStreamEventFlags*, + const FSEventStreamEventId*); + +struct CFRunLoopSourceContext { + CFIndex version; + void* info; + void* pad[7]; + void (*perform)(void*); +}; + +struct FSEventStreamContext { + CFIndex version; + void* info; + void* pad[3]; +}; + +struct CFRange { + CFIndex location; + CFIndex length; +}; + +static const CFStringEncoding kCFStringEncodingUTF8 = 0x8000100; +static const OSStatus noErr = 0; + +static const FSEventStreamEventId kFSEventStreamEventIdSinceNow = -1; + +static const int kFSEventStreamCreateFlagNoDefer = 2; +static const int kFSEventStreamCreateFlagFileEvents = 16; + +static const int kFSEventStreamEventFlagEventIdsWrapped = 8; +static const int kFSEventStreamEventFlagHistoryDone = 16; +static const int kFSEventStreamEventFlagItemChangeOwner = 0x4000; +static const int kFSEventStreamEventFlagItemCreated = 0x100; +static const int kFSEventStreamEventFlagItemFinderInfoMod = 0x2000; +static const int kFSEventStreamEventFlagItemInodeMetaMod = 0x400; +static const int kFSEventStreamEventFlagItemIsDir = 0x20000; +static const int kFSEventStreamEventFlagItemModified = 0x1000; +static const int kFSEventStreamEventFlagItemRemoved = 0x200; +static const int kFSEventStreamEventFlagItemRenamed = 0x800; +static const int kFSEventStreamEventFlagItemXattrMod = 0x8000; +static const int kFSEventStreamEventFlagKernelDropped = 4; +static const int kFSEventStreamEventFlagMount = 64; +static const int kFSEventStreamEventFlagRootChanged = 32; +static const int kFSEventStreamEventFlagUnmount = 128; +static const int kFSEventStreamEventFlagUserDropped = 2; + +#endif /* UV_DARWIN_STUB_H_ */ diff --git a/include/libuv/src/unix/darwin.c b/include/libuv/src/unix/darwin.c new file mode 100644 index 000000000..d0ecd452d --- /dev/null +++ b/include/libuv/src/unix/darwin.c @@ -0,0 +1,371 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include + +#include +#include +#include +#include /* _NSGetExecutablePath */ +#include +#include +#include /* sysconf */ + +#if !TARGET_OS_IPHONE +#include "darwin-stub.h" +#endif + +static uv_once_t once = UV_ONCE_INIT; +static uint64_t (*time_func)(void); +static mach_timebase_info_data_t timebase; + +typedef unsigned char UInt8; + +int uv__platform_loop_init(uv_loop_t* loop) { + loop->cf_state = NULL; + + if (uv__kqueue_init(loop)) + return UV__ERR(errno); + + return 0; +} + + +void uv__platform_loop_delete(uv_loop_t* loop) { + uv__fsevents_loop_delete(loop); +} + + +static void uv__hrtime_init_once(void) { + if (KERN_SUCCESS != mach_timebase_info(&timebase)) + abort(); + + time_func = (uint64_t (*)(void)) dlsym(RTLD_DEFAULT, "mach_continuous_time"); + if (time_func == NULL) + time_func = mach_absolute_time; +} + + +uint64_t uv__hrtime(uv_clocktype_t type) { + uv_once(&once, uv__hrtime_init_once); + return time_func() * timebase.numer / timebase.denom; +} + + +int uv_exepath(char* buffer, size_t* size) { + /* realpath(exepath) may be > PATH_MAX so double it to be on the safe side. */ + char abspath[PATH_MAX * 2 + 1]; + char exepath[PATH_MAX + 1]; + uint32_t exepath_size; + size_t abspath_size; + + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + + exepath_size = sizeof(exepath); + if (_NSGetExecutablePath(exepath, &exepath_size)) + return UV_EIO; + + if (realpath(exepath, abspath) != abspath) + return UV__ERR(errno); + + abspath_size = strlen(abspath); + if (abspath_size == 0) + return UV_EIO; + + *size -= 1; + if (*size > abspath_size) + *size = abspath_size; + + memcpy(buffer, abspath, *size); + buffer[*size] = '\0'; + + return 0; +} + + +uint64_t uv_get_free_memory(void) { + vm_statistics_data_t info; + mach_msg_type_number_t count = sizeof(info) / sizeof(integer_t); + + if (host_statistics(mach_host_self(), HOST_VM_INFO, + (host_info_t)&info, &count) != KERN_SUCCESS) { + return UV_EINVAL; /* FIXME(bnoordhuis) Translate error. */ + } + + return (uint64_t) info.free_count * sysconf(_SC_PAGESIZE); +} + + +uint64_t uv_get_total_memory(void) { + uint64_t info; + int which[] = {CTL_HW, HW_MEMSIZE}; + size_t size = sizeof(info); + + if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) + return UV__ERR(errno); + + return (uint64_t) info; +} + + +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + +void uv_loadavg(double avg[3]) { + struct loadavg info; + size_t size = sizeof(info); + int which[] = {CTL_VM, VM_LOADAVG}; + + if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0) < 0) return; + + avg[0] = (double) info.ldavg[0] / info.fscale; + avg[1] = (double) info.ldavg[1] / info.fscale; + avg[2] = (double) info.ldavg[2] / info.fscale; +} + + +int uv_resident_set_memory(size_t* rss) { + mach_msg_type_number_t count; + task_basic_info_data_t info; + kern_return_t err; + + count = TASK_BASIC_INFO_COUNT; + err = task_info(mach_task_self(), + TASK_BASIC_INFO, + (task_info_t) &info, + &count); + (void) &err; + /* task_info(TASK_BASIC_INFO) cannot really fail. Anything other than + * KERN_SUCCESS implies a libuv bug. + */ + assert(err == KERN_SUCCESS); + *rss = info.resident_size; + + return 0; +} + + +int uv_uptime(double* uptime) { + time_t now; + struct timeval info; + size_t size = sizeof(info); + static int which[] = {CTL_KERN, KERN_BOOTTIME}; + + if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) + return UV__ERR(errno); + + now = time(NULL); + *uptime = now - info.tv_sec; + + return 0; +} + +static int uv__get_cpu_speed(uint64_t* speed) { + /* IOKit */ + void (*pIOObjectRelease)(io_object_t); + kern_return_t (*pIOMasterPort)(mach_port_t, mach_port_t*); + CFMutableDictionaryRef (*pIOServiceMatching)(const char*); + kern_return_t (*pIOServiceGetMatchingServices)(mach_port_t, + CFMutableDictionaryRef, + io_iterator_t*); + io_service_t (*pIOIteratorNext)(io_iterator_t); + CFTypeRef (*pIORegistryEntryCreateCFProperty)(io_registry_entry_t, + CFStringRef, + CFAllocatorRef, + IOOptionBits); + + /* CoreFoundation */ + CFStringRef (*pCFStringCreateWithCString)(CFAllocatorRef, + const char*, + CFStringEncoding); + CFStringEncoding (*pCFStringGetSystemEncoding)(void); + UInt8 *(*pCFDataGetBytePtr)(CFDataRef); + CFIndex (*pCFDataGetLength)(CFDataRef); + void (*pCFDataGetBytes)(CFDataRef, CFRange, UInt8*); + void (*pCFRelease)(CFTypeRef); + + void* core_foundation_handle; + void* iokit_handle; + int err; + + kern_return_t kr; + mach_port_t mach_port; + io_iterator_t it; + io_object_t service; + + mach_port = 0; + + err = UV_ENOENT; + core_foundation_handle = dlopen("/System/Library/Frameworks/" + "CoreFoundation.framework/" + "Versions/A/CoreFoundation", + RTLD_LAZY | RTLD_LOCAL); + iokit_handle = dlopen("/System/Library/Frameworks/IOKit.framework/" + "Versions/A/IOKit", + RTLD_LAZY | RTLD_LOCAL); + + if (core_foundation_handle == NULL || iokit_handle == NULL) + goto out; + +#define V(handle, symbol) \ + do { \ + *(void **)(&p ## symbol) = dlsym((handle), #symbol); \ + if (p ## symbol == NULL) \ + goto out; \ + } \ + while (0) + V(iokit_handle, IOMasterPort); + V(iokit_handle, IOServiceMatching); + V(iokit_handle, IOServiceGetMatchingServices); + V(iokit_handle, IOIteratorNext); + V(iokit_handle, IOObjectRelease); + V(iokit_handle, IORegistryEntryCreateCFProperty); + V(core_foundation_handle, CFStringCreateWithCString); + V(core_foundation_handle, CFStringGetSystemEncoding); + V(core_foundation_handle, CFDataGetBytePtr); + V(core_foundation_handle, CFDataGetLength); + V(core_foundation_handle, CFDataGetBytes); + V(core_foundation_handle, CFRelease); +#undef V + +#define S(s) pCFStringCreateWithCString(NULL, (s), kCFStringEncodingUTF8) + + kr = pIOMasterPort(MACH_PORT_NULL, &mach_port); + assert(kr == KERN_SUCCESS); + CFMutableDictionaryRef classes_to_match + = pIOServiceMatching("IOPlatformDevice"); + kr = pIOServiceGetMatchingServices(mach_port, classes_to_match, &it); + assert(kr == KERN_SUCCESS); + service = pIOIteratorNext(it); + + CFStringRef device_type_str = S("device_type"); + CFStringRef clock_frequency_str = S("clock-frequency"); + + while (service != 0) { + CFDataRef data; + data = pIORegistryEntryCreateCFProperty(service, + device_type_str, + NULL, + 0); + if (data) { + const UInt8* raw = pCFDataGetBytePtr(data); + if (strncmp((char*)raw, "cpu", 3) == 0 || + strncmp((char*)raw, "processor", 9) == 0) { + CFDataRef freq_ref; + freq_ref = pIORegistryEntryCreateCFProperty(service, + clock_frequency_str, + NULL, + 0); + if (freq_ref) { + uint32_t freq; + CFIndex len = pCFDataGetLength(freq_ref); + CFRange range; + range.location = 0; + range.length = len; + + pCFDataGetBytes(freq_ref, range, (UInt8*)&freq); + *speed = freq; + pCFRelease(freq_ref); + pCFRelease(data); + break; + } + } + pCFRelease(data); + } + + service = pIOIteratorNext(it); + } + + pIOObjectRelease(it); + + err = 0; +out: + if (core_foundation_handle != NULL) + dlclose(core_foundation_handle); + + if (iokit_handle != NULL) + dlclose(iokit_handle); + + mach_port_deallocate(mach_task_self(), mach_port); + + return err; +} + +int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK), + multiplier = ((uint64_t)1000L / ticks); + char model[512]; + size_t size; + unsigned int i; + natural_t numcpus; + mach_msg_type_number_t msg_type; + processor_cpu_load_info_data_t *info; + uv_cpu_info_t* cpu_info; + uint64_t cpuspeed; + int err; + + size = sizeof(model); + if (sysctlbyname("machdep.cpu.brand_string", &model, &size, NULL, 0) && + sysctlbyname("hw.model", &model, &size, NULL, 0)) { + return UV__ERR(errno); + } + + err = uv__get_cpu_speed(&cpuspeed); + if (err < 0) + return err; + + if (host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numcpus, + (processor_info_array_t*)&info, + &msg_type) != KERN_SUCCESS) { + return UV_EINVAL; /* FIXME(bnoordhuis) Translate error. */ + } + + *cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos)); + if (!(*cpu_infos)) { + vm_deallocate(mach_task_self(), (vm_address_t)info, msg_type); + return UV_ENOMEM; + } + + *count = numcpus; + + for (i = 0; i < numcpus; i++) { + cpu_info = &(*cpu_infos)[i]; + + cpu_info->cpu_times.user = (uint64_t)(info[i].cpu_ticks[0]) * multiplier; + cpu_info->cpu_times.nice = (uint64_t)(info[i].cpu_ticks[3]) * multiplier; + cpu_info->cpu_times.sys = (uint64_t)(info[i].cpu_ticks[1]) * multiplier; + cpu_info->cpu_times.idle = (uint64_t)(info[i].cpu_ticks[2]) * multiplier; + cpu_info->cpu_times.irq = 0; + + cpu_info->model = uv__strdup(model); + cpu_info->speed = cpuspeed/1000000; + } + vm_deallocate(mach_task_self(), (vm_address_t)info, msg_type); + + return 0; +} diff --git a/include/libuv/src/unix/dl.c b/include/libuv/src/unix/dl.c new file mode 100644 index 000000000..fc1c052bb --- /dev/null +++ b/include/libuv/src/unix/dl.c @@ -0,0 +1,80 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include +#include + +static int uv__dlerror(uv_lib_t* lib); + + +int uv_dlopen(const char* filename, uv_lib_t* lib) { + dlerror(); /* Reset error status. */ + lib->errmsg = NULL; + lib->handle = dlopen(filename, RTLD_LAZY); + return lib->handle ? 0 : uv__dlerror(lib); +} + + +void uv_dlclose(uv_lib_t* lib) { + uv__free(lib->errmsg); + lib->errmsg = NULL; + + if (lib->handle) { + /* Ignore errors. No good way to signal them without leaking memory. */ + dlclose(lib->handle); + lib->handle = NULL; + } +} + + +int uv_dlsym(uv_lib_t* lib, const char* name, void** ptr) { + dlerror(); /* Reset error status. */ + *ptr = dlsym(lib->handle, name); + return uv__dlerror(lib); +} + + +const char* uv_dlerror(const uv_lib_t* lib) { + return lib->errmsg ? lib->errmsg : "no error"; +} + + +static int uv__dlerror(uv_lib_t* lib) { + const char* errmsg; + + uv__free(lib->errmsg); + + errmsg = dlerror(); + + if (errmsg) { + lib->errmsg = uv__strdup(errmsg); + return -1; + } + else { + lib->errmsg = NULL; + return 0; + } +} diff --git a/include/libuv/src/unix/freebsd.c b/include/libuv/src/unix/freebsd.c new file mode 100644 index 000000000..fe795a0e7 --- /dev/null +++ b/include/libuv/src/unix/freebsd.c @@ -0,0 +1,282 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include /* VM_LOADAVG */ +#include +#include +#include /* sysconf */ +#include + +#ifndef CPUSTATES +# define CPUSTATES 5U +#endif +#ifndef CP_USER +# define CP_USER 0 +# define CP_NICE 1 +# define CP_SYS 2 +# define CP_IDLE 3 +# define CP_INTR 4 +#endif + + +int uv__platform_loop_init(uv_loop_t* loop) { + return uv__kqueue_init(loop); +} + + +void uv__platform_loop_delete(uv_loop_t* loop) { +} + +int uv_exepath(char* buffer, size_t* size) { + char abspath[PATH_MAX * 2 + 1]; + int mib[4]; + size_t abspath_size; + + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PATHNAME; + mib[3] = -1; + + abspath_size = sizeof abspath; + if (sysctl(mib, ARRAY_SIZE(mib), abspath, &abspath_size, NULL, 0)) + return UV__ERR(errno); + + assert(abspath_size > 0); + abspath_size -= 1; + *size -= 1; + + if (*size > abspath_size) + *size = abspath_size; + + memcpy(buffer, abspath, *size); + buffer[*size] = '\0'; + + return 0; +} + +uint64_t uv_get_free_memory(void) { + int freecount; + size_t size = sizeof(freecount); + + if (sysctlbyname("vm.stats.vm.v_free_count", &freecount, &size, NULL, 0)) + return UV__ERR(errno); + + return (uint64_t) freecount * sysconf(_SC_PAGESIZE); + +} + + +uint64_t uv_get_total_memory(void) { + unsigned long info; + int which[] = {CTL_HW, HW_PHYSMEM}; + + size_t size = sizeof(info); + + if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) + return UV__ERR(errno); + + return (uint64_t) info; +} + + +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + +void uv_loadavg(double avg[3]) { + struct loadavg info; + size_t size = sizeof(info); + int which[] = {CTL_VM, VM_LOADAVG}; + + if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0) < 0) return; + + avg[0] = (double) info.ldavg[0] / info.fscale; + avg[1] = (double) info.ldavg[1] / info.fscale; + avg[2] = (double) info.ldavg[2] / info.fscale; +} + + +int uv_resident_set_memory(size_t* rss) { + struct kinfo_proc kinfo; + size_t page_size; + size_t kinfo_size; + int mib[4]; + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + + kinfo_size = sizeof(kinfo); + + if (sysctl(mib, ARRAY_SIZE(mib), &kinfo, &kinfo_size, NULL, 0)) + return UV__ERR(errno); + + page_size = getpagesize(); + +#ifdef __DragonFly__ + *rss = kinfo.kp_vm_rssize * page_size; +#else + *rss = kinfo.ki_rssize * page_size; +#endif + + return 0; +} + + +int uv_uptime(double* uptime) { + int r; + struct timespec sp; + r = clock_gettime(CLOCK_MONOTONIC, &sp); + if (r) + return UV__ERR(errno); + + *uptime = sp.tv_sec; + return 0; +} + + +int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK), + multiplier = ((uint64_t)1000L / ticks), cpuspeed, maxcpus, + cur = 0; + uv_cpu_info_t* cpu_info; + const char* maxcpus_key; + const char* cptimes_key; + const char* model_key; + char model[512]; + long* cp_times; + int numcpus; + size_t size; + int i; + +#if defined(__DragonFly__) + /* This is not quite correct but DragonFlyBSD doesn't seem to have anything + * comparable to kern.smp.maxcpus or kern.cp_times (kern.cp_time is a total, + * not per CPU). At least this stops uv_cpu_info() from failing completely. + */ + maxcpus_key = "hw.ncpu"; + cptimes_key = "kern.cp_time"; +#else + maxcpus_key = "kern.smp.maxcpus"; + cptimes_key = "kern.cp_times"; +#endif + +#if defined(__arm__) || defined(__aarch64__) + /* The key hw.model and hw.clockrate are not available on FreeBSD ARM. */ + model_key = "hw.machine"; + cpuspeed = 0; +#else + model_key = "hw.model"; + + size = sizeof(cpuspeed); + if (sysctlbyname("hw.clockrate", &cpuspeed, &size, NULL, 0)) + return -errno; +#endif + + size = sizeof(model); + if (sysctlbyname(model_key, &model, &size, NULL, 0)) + return UV__ERR(errno); + + size = sizeof(numcpus); + if (sysctlbyname("hw.ncpu", &numcpus, &size, NULL, 0)) + return UV__ERR(errno); + + *cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos)); + if (!(*cpu_infos)) + return UV_ENOMEM; + + *count = numcpus; + + /* kern.cp_times on FreeBSD i386 gives an array up to maxcpus instead of + * ncpu. + */ + size = sizeof(maxcpus); + if (sysctlbyname(maxcpus_key, &maxcpus, &size, NULL, 0)) { + uv__free(*cpu_infos); + return UV__ERR(errno); + } + + size = maxcpus * CPUSTATES * sizeof(long); + + cp_times = uv__malloc(size); + if (cp_times == NULL) { + uv__free(*cpu_infos); + return UV_ENOMEM; + } + + if (sysctlbyname(cptimes_key, cp_times, &size, NULL, 0)) { + uv__free(cp_times); + uv__free(*cpu_infos); + return UV__ERR(errno); + } + + for (i = 0; i < numcpus; i++) { + cpu_info = &(*cpu_infos)[i]; + + cpu_info->cpu_times.user = (uint64_t)(cp_times[CP_USER+cur]) * multiplier; + cpu_info->cpu_times.nice = (uint64_t)(cp_times[CP_NICE+cur]) * multiplier; + cpu_info->cpu_times.sys = (uint64_t)(cp_times[CP_SYS+cur]) * multiplier; + cpu_info->cpu_times.idle = (uint64_t)(cp_times[CP_IDLE+cur]) * multiplier; + cpu_info->cpu_times.irq = (uint64_t)(cp_times[CP_INTR+cur]) * multiplier; + + cpu_info->model = uv__strdup(model); + cpu_info->speed = cpuspeed; + + cur+=CPUSTATES; + } + + uv__free(cp_times); + return 0; +} + + +int uv__sendmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) { +#if __FreeBSD__ >= 11 + return sendmmsg(fd, mmsg, vlen, /* flags */ 0); +#else + return errno = ENOSYS, -1; +#endif +} + + +int uv__recvmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) { +#if __FreeBSD__ >= 11 + return recvmmsg(fd, mmsg, vlen, 0 /* flags */, NULL /* timeout */); +#else + return errno = ENOSYS, -1; +#endif +} diff --git a/include/libuv/src/unix/fs.c b/include/libuv/src/unix/fs.c new file mode 100644 index 000000000..556fd103c --- /dev/null +++ b/include/libuv/src/unix/fs.c @@ -0,0 +1,2134 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* Caveat emptor: this file deviates from the libuv convention of returning + * negated errno codes. Most uv_fs_*() functions map directly to the system + * call of the same name. For more complex wrappers, it's easier to just + * return -1 with errno set. The dispatcher in uv__fs_work() takes care of + * getting the errno to the right place (req->result or as the return value.) + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include +#include +#include +#include /* PATH_MAX */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__DragonFly__) || \ + defined(__FreeBSD__) || \ + defined(__FreeBSD_kernel__) || \ + defined(__OpenBSD__) || \ + defined(__NetBSD__) +# define HAVE_PREADV 1 +#else +# define HAVE_PREADV 0 +#endif + +#if defined(__linux__) || defined(__sun) +# include +#endif + +#if defined(__APPLE__) +# include +#elif defined(__linux__) && !defined(FICLONE) +# include +# define FICLONE _IOW(0x94, 9, int) +#endif + +#if defined(_AIX) && !defined(_AIX71) +# include +#endif + +#if defined(__APPLE__) || \ + defined(__DragonFly__) || \ + defined(__FreeBSD__) || \ + defined(__FreeBSD_kernel__) || \ + defined(__OpenBSD__) || \ + defined(__NetBSD__) +# include +# include +#elif defined(__sun) || \ + defined(__MVS__) || \ + defined(__NetBSD__) || \ + defined(__HAIKU__) || \ + defined(__QNX__) +# include +#else +# include +#endif + +#if defined(_AIX) && _XOPEN_SOURCE <= 600 +extern char *mkdtemp(char *template); /* See issue #740 on AIX < 7 */ +#endif + +#define INIT(subtype) \ + do { \ + if (req == NULL) \ + return UV_EINVAL; \ + UV_REQ_INIT(req, UV_FS); \ + req->fs_type = UV_FS_ ## subtype; \ + req->result = 0; \ + req->ptr = NULL; \ + req->loop = loop; \ + req->path = NULL; \ + req->new_path = NULL; \ + req->bufs = NULL; \ + req->cb = cb; \ + } \ + while (0) + +#define PATH \ + do { \ + assert(path != NULL); \ + if (cb == NULL) { \ + req->path = path; \ + } else { \ + req->path = uv__strdup(path); \ + if (req->path == NULL) \ + return UV_ENOMEM; \ + } \ + } \ + while (0) + +#define PATH2 \ + do { \ + if (cb == NULL) { \ + req->path = path; \ + req->new_path = new_path; \ + } else { \ + size_t path_len; \ + size_t new_path_len; \ + path_len = strlen(path) + 1; \ + new_path_len = strlen(new_path) + 1; \ + req->path = uv__malloc(path_len + new_path_len); \ + if (req->path == NULL) \ + return UV_ENOMEM; \ + req->new_path = req->path + path_len; \ + memcpy((void*) req->path, path, path_len); \ + memcpy((void*) req->new_path, new_path, new_path_len); \ + } \ + } \ + while (0) + +#define POST \ + do { \ + if (cb != NULL) { \ + uv__req_register(loop, req); \ + uv__work_submit(loop, \ + &req->work_req, \ + UV__WORK_FAST_IO, \ + uv__fs_work, \ + uv__fs_done); \ + return 0; \ + } \ + else { \ + uv__fs_work(&req->work_req); \ + return req->result; \ + } \ + } \ + while (0) + + +static int uv__fs_close(int fd) { + int rc; + + rc = uv__close_nocancel(fd); + if (rc == -1) + if (errno == EINTR || errno == EINPROGRESS) + rc = 0; /* The close is in progress, not an error. */ + + return rc; +} + + +static ssize_t uv__fs_fsync(uv_fs_t* req) { +#if defined(__APPLE__) + /* Apple's fdatasync and fsync explicitly do NOT flush the drive write cache + * to the drive platters. This is in contrast to Linux's fdatasync and fsync + * which do, according to recent man pages. F_FULLFSYNC is Apple's equivalent + * for flushing buffered data to permanent storage. If F_FULLFSYNC is not + * supported by the file system we fall back to F_BARRIERFSYNC or fsync(). + * This is the same approach taken by sqlite, except sqlite does not issue + * an F_BARRIERFSYNC call. + */ + int r; + + r = fcntl(req->file, F_FULLFSYNC); + if (r != 0) + r = fcntl(req->file, 85 /* F_BARRIERFSYNC */); /* fsync + barrier */ + if (r != 0) + r = fsync(req->file); + return r; +#else + return fsync(req->file); +#endif +} + + +static ssize_t uv__fs_fdatasync(uv_fs_t* req) { +#if defined(__linux__) || defined(__sun) || defined(__NetBSD__) + return fdatasync(req->file); +#elif defined(__APPLE__) + /* See the comment in uv__fs_fsync. */ + return uv__fs_fsync(req); +#else + return fsync(req->file); +#endif +} + + +UV_UNUSED(static struct timespec uv__fs_to_timespec(double time)) { + struct timespec ts; + ts.tv_sec = time; + ts.tv_nsec = (uint64_t)(time * 1000000) % 1000000 * 1000; + return ts; +} + +UV_UNUSED(static struct timeval uv__fs_to_timeval(double time)) { + struct timeval tv; + tv.tv_sec = time; + tv.tv_usec = (uint64_t)(time * 1000000) % 1000000; + return tv; +} + +static ssize_t uv__fs_futime(uv_fs_t* req) { +#if defined(__linux__) \ + || defined(_AIX71) \ + || defined(__HAIKU__) + /* utimesat() has nanosecond resolution but we stick to microseconds + * for the sake of consistency with other platforms. + */ + struct timespec ts[2]; + ts[0] = uv__fs_to_timespec(req->atime); + ts[1] = uv__fs_to_timespec(req->mtime); + return futimens(req->file, ts); +#elif defined(__APPLE__) \ + || defined(__DragonFly__) \ + || defined(__FreeBSD__) \ + || defined(__FreeBSD_kernel__) \ + || defined(__NetBSD__) \ + || defined(__OpenBSD__) \ + || defined(__sun) + struct timeval tv[2]; + tv[0] = uv__fs_to_timeval(req->atime); + tv[1] = uv__fs_to_timeval(req->mtime); +# if defined(__sun) + return futimesat(req->file, NULL, tv); +# else + return futimes(req->file, tv); +# endif +#elif defined(__MVS__) + attrib_t atr; + memset(&atr, 0, sizeof(atr)); + atr.att_mtimechg = 1; + atr.att_atimechg = 1; + atr.att_mtime = req->mtime; + atr.att_atime = req->atime; + return __fchattr(req->file, &atr, sizeof(atr)); +#else + errno = ENOSYS; + return -1; +#endif +} + + +static ssize_t uv__fs_mkdtemp(uv_fs_t* req) { + return mkdtemp((char*) req->path) ? 0 : -1; +} + + +static int (*uv__mkostemp)(char*, int); + + +static void uv__mkostemp_initonce(void) { + /* z/os doesn't have RTLD_DEFAULT but that's okay + * because it doesn't have mkostemp(O_CLOEXEC) either. + */ +#ifdef RTLD_DEFAULT + uv__mkostemp = (int (*)(char*, int)) dlsym(RTLD_DEFAULT, "mkostemp"); + + /* We don't care about errors, but we do want to clean them up. + * If there has been no error, then dlerror() will just return + * NULL. + */ + dlerror(); +#endif /* RTLD_DEFAULT */ +} + + +static int uv__fs_mkstemp(uv_fs_t* req) { + static uv_once_t once = UV_ONCE_INIT; + int r; +#ifdef O_CLOEXEC + static int no_cloexec_support; +#endif + static const char pattern[] = "XXXXXX"; + static const size_t pattern_size = sizeof(pattern) - 1; + char* path; + size_t path_length; + + path = (char*) req->path; + path_length = strlen(path); + + /* EINVAL can be returned for 2 reasons: + 1. The template's last 6 characters were not XXXXXX + 2. open() didn't support O_CLOEXEC + We want to avoid going to the fallback path in case + of 1, so it's manually checked before. */ + if (path_length < pattern_size || + strcmp(path + path_length - pattern_size, pattern)) { + errno = EINVAL; + r = -1; + goto clobber; + } + + uv_once(&once, uv__mkostemp_initonce); + +#ifdef O_CLOEXEC + if (uv__load_relaxed(&no_cloexec_support) == 0 && uv__mkostemp != NULL) { + r = uv__mkostemp(path, O_CLOEXEC); + + if (r >= 0) + return r; + + /* If mkostemp() returns EINVAL, it means the kernel doesn't + support O_CLOEXEC, so we just fallback to mkstemp() below. */ + if (errno != EINVAL) + goto clobber; + + /* We set the static variable so that next calls don't even + try to use mkostemp. */ + uv__store_relaxed(&no_cloexec_support, 1); + } +#endif /* O_CLOEXEC */ + + if (req->cb != NULL) + uv_rwlock_rdlock(&req->loop->cloexec_lock); + + r = mkstemp(path); + + /* In case of failure `uv__cloexec` will leave error in `errno`, + * so it is enough to just set `r` to `-1`. + */ + if (r >= 0 && uv__cloexec(r, 1) != 0) { + r = uv__close(r); + if (r != 0) + abort(); + r = -1; + } + + if (req->cb != NULL) + uv_rwlock_rdunlock(&req->loop->cloexec_lock); + +clobber: + if (r < 0) + path[0] = '\0'; + return r; +} + + +static ssize_t uv__fs_open(uv_fs_t* req) { +#ifdef O_CLOEXEC + return open(req->path, req->flags | O_CLOEXEC, req->mode); +#else /* O_CLOEXEC */ + int r; + + if (req->cb != NULL) + uv_rwlock_rdlock(&req->loop->cloexec_lock); + + r = open(req->path, req->flags, req->mode); + + /* In case of failure `uv__cloexec` will leave error in `errno`, + * so it is enough to just set `r` to `-1`. + */ + if (r >= 0 && uv__cloexec(r, 1) != 0) { + r = uv__close(r); + if (r != 0) + abort(); + r = -1; + } + + if (req->cb != NULL) + uv_rwlock_rdunlock(&req->loop->cloexec_lock); + + return r; +#endif /* O_CLOEXEC */ +} + + +#if !HAVE_PREADV +static ssize_t uv__fs_preadv(uv_file fd, + uv_buf_t* bufs, + unsigned int nbufs, + off_t off) { + uv_buf_t* buf; + uv_buf_t* end; + ssize_t result; + ssize_t rc; + size_t pos; + + assert(nbufs > 0); + + result = 0; + pos = 0; + buf = bufs + 0; + end = bufs + nbufs; + + for (;;) { + do + rc = pread(fd, buf->base + pos, buf->len - pos, off + result); + while (rc == -1 && errno == EINTR); + + if (rc == 0) + break; + + if (rc == -1 && result == 0) + return UV__ERR(errno); + + if (rc == -1) + break; /* We read some data so return that, ignore the error. */ + + pos += rc; + result += rc; + + if (pos < buf->len) + continue; + + pos = 0; + buf += 1; + + if (buf == end) + break; + } + + return result; +} +#endif + + +static ssize_t uv__fs_read(uv_fs_t* req) { +#if defined(__linux__) + static int no_preadv; +#endif + unsigned int iovmax; + ssize_t result; + + iovmax = uv__getiovmax(); + if (req->nbufs > iovmax) + req->nbufs = iovmax; + + if (req->off < 0) { + if (req->nbufs == 1) + result = read(req->file, req->bufs[0].base, req->bufs[0].len); + else + result = readv(req->file, (struct iovec*) req->bufs, req->nbufs); + } else { + if (req->nbufs == 1) { + result = pread(req->file, req->bufs[0].base, req->bufs[0].len, req->off); + goto done; + } + +#if HAVE_PREADV + result = preadv(req->file, (struct iovec*) req->bufs, req->nbufs, req->off); +#else +# if defined(__linux__) + if (uv__load_relaxed(&no_preadv)) retry: +# endif + { + result = uv__fs_preadv(req->file, req->bufs, req->nbufs, req->off); + } +# if defined(__linux__) + else { + result = uv__preadv(req->file, + (struct iovec*)req->bufs, + req->nbufs, + req->off); + if (result == -1 && errno == ENOSYS) { + uv__store_relaxed(&no_preadv, 1); + goto retry; + } + } +# endif +#endif + } + +done: + /* Early cleanup of bufs allocation, since we're done with it. */ + if (req->bufs != req->bufsml) + uv__free(req->bufs); + + req->bufs = NULL; + req->nbufs = 0; + +#ifdef __PASE__ + /* PASE returns EOPNOTSUPP when reading a directory, convert to EISDIR */ + if (result == -1 && errno == EOPNOTSUPP) { + struct stat buf; + ssize_t rc; + rc = fstat(req->file, &buf); + if (rc == 0 && S_ISDIR(buf.st_mode)) { + errno = EISDIR; + } + } +#endif + + return result; +} + + +#if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_8) +#define UV_CONST_DIRENT uv__dirent_t +#else +#define UV_CONST_DIRENT const uv__dirent_t +#endif + + +static int uv__fs_scandir_filter(UV_CONST_DIRENT* dent) { + return strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0; +} + + +static int uv__fs_scandir_sort(UV_CONST_DIRENT** a, UV_CONST_DIRENT** b) { + return strcmp((*a)->d_name, (*b)->d_name); +} + + +static ssize_t uv__fs_scandir(uv_fs_t* req) { + uv__dirent_t** dents; + int n; + + dents = NULL; + n = scandir(req->path, &dents, uv__fs_scandir_filter, uv__fs_scandir_sort); + + /* NOTE: We will use nbufs as an index field */ + req->nbufs = 0; + + if (n == 0) { + /* OS X still needs to deallocate some memory. + * Memory was allocated using the system allocator, so use free() here. + */ + free(dents); + dents = NULL; + } else if (n == -1) { + return n; + } + + req->ptr = dents; + + return n; +} + +static int uv__fs_opendir(uv_fs_t* req) { + uv_dir_t* dir; + + dir = uv__malloc(sizeof(*dir)); + if (dir == NULL) + goto error; + + dir->dir = opendir(req->path); + if (dir->dir == NULL) + goto error; + + req->ptr = dir; + return 0; + +error: + uv__free(dir); + req->ptr = NULL; + return -1; +} + +static int uv__fs_readdir(uv_fs_t* req) { + uv_dir_t* dir; + uv_dirent_t* dirent; + struct dirent* res; + unsigned int dirent_idx; + unsigned int i; + + dir = req->ptr; + dirent_idx = 0; + + while (dirent_idx < dir->nentries) { + /* readdir() returns NULL on end of directory, as well as on error. errno + is used to differentiate between the two conditions. */ + errno = 0; + res = readdir(dir->dir); + + if (res == NULL) { + if (errno != 0) + goto error; + break; + } + + if (strcmp(res->d_name, ".") == 0 || strcmp(res->d_name, "..") == 0) + continue; + + dirent = &dir->dirents[dirent_idx]; + dirent->name = uv__strdup(res->d_name); + + if (dirent->name == NULL) + goto error; + + dirent->type = uv__fs_get_dirent_type(res); + ++dirent_idx; + } + + return dirent_idx; + +error: + for (i = 0; i < dirent_idx; ++i) { + uv__free((char*) dir->dirents[i].name); + dir->dirents[i].name = NULL; + } + + return -1; +} + +static int uv__fs_closedir(uv_fs_t* req) { + uv_dir_t* dir; + + dir = req->ptr; + + if (dir->dir != NULL) { + closedir(dir->dir); + dir->dir = NULL; + } + + uv__free(req->ptr); + req->ptr = NULL; + return 0; +} + +static int uv__fs_statfs(uv_fs_t* req) { + uv_statfs_t* stat_fs; +#if defined(__sun) || \ + defined(__MVS__) || \ + defined(__NetBSD__) || \ + defined(__HAIKU__) || \ + defined(__QNX__) + struct statvfs buf; + + if (0 != statvfs(req->path, &buf)) +#else + struct statfs buf; + + if (0 != statfs(req->path, &buf)) +#endif /* defined(__sun) */ + return -1; + + stat_fs = uv__malloc(sizeof(*stat_fs)); + if (stat_fs == NULL) { + errno = ENOMEM; + return -1; + } + +#if defined(__sun) || \ + defined(__MVS__) || \ + defined(__OpenBSD__) || \ + defined(__NetBSD__) || \ + defined(__HAIKU__) || \ + defined(__QNX__) + stat_fs->f_type = 0; /* f_type is not supported. */ +#else + stat_fs->f_type = buf.f_type; +#endif + stat_fs->f_bsize = buf.f_bsize; + stat_fs->f_blocks = buf.f_blocks; + stat_fs->f_bfree = buf.f_bfree; + stat_fs->f_bavail = buf.f_bavail; + stat_fs->f_files = buf.f_files; + stat_fs->f_ffree = buf.f_ffree; + req->ptr = stat_fs; + return 0; +} + +static ssize_t uv__fs_pathmax_size(const char* path) { + ssize_t pathmax; + + pathmax = pathconf(path, _PC_PATH_MAX); + + if (pathmax == -1) + pathmax = UV__PATH_MAX; + + return pathmax; +} + +static ssize_t uv__fs_readlink(uv_fs_t* req) { + ssize_t maxlen; + ssize_t len; + char* buf; + +#if defined(_POSIX_PATH_MAX) || defined(PATH_MAX) + maxlen = uv__fs_pathmax_size(req->path); +#else + /* We may not have a real PATH_MAX. Read size of link. */ + struct stat st; + int ret; + ret = lstat(req->path, &st); + if (ret != 0) + return -1; + if (!S_ISLNK(st.st_mode)) { + errno = EINVAL; + return -1; + } + + maxlen = st.st_size; + + /* According to readlink(2) lstat can report st_size == 0 + for some symlinks, such as those in /proc or /sys. */ + if (maxlen == 0) + maxlen = uv__fs_pathmax_size(req->path); +#endif + + buf = uv__malloc(maxlen); + + if (buf == NULL) { + errno = ENOMEM; + return -1; + } + +#if defined(__MVS__) + len = os390_readlink(req->path, buf, maxlen); +#else + len = readlink(req->path, buf, maxlen); +#endif + + if (len == -1) { + uv__free(buf); + return -1; + } + + /* Uncommon case: resize to make room for the trailing nul byte. */ + if (len == maxlen) { + buf = uv__reallocf(buf, len + 1); + + if (buf == NULL) + return -1; + } + + buf[len] = '\0'; + req->ptr = buf; + + return 0; +} + +static ssize_t uv__fs_realpath(uv_fs_t* req) { + char* buf; + +#if defined(_POSIX_VERSION) && _POSIX_VERSION >= 200809L + buf = realpath(req->path, NULL); + if (buf == NULL) + return -1; +#else + ssize_t len; + + len = uv__fs_pathmax_size(req->path); + buf = uv__malloc(len + 1); + + if (buf == NULL) { + errno = ENOMEM; + return -1; + } + + if (realpath(req->path, buf) == NULL) { + uv__free(buf); + return -1; + } +#endif + + req->ptr = buf; + + return 0; +} + +static ssize_t uv__fs_sendfile_emul(uv_fs_t* req) { + struct pollfd pfd; + int use_pread; + off_t offset; + ssize_t nsent; + ssize_t nread; + ssize_t nwritten; + size_t buflen; + size_t len; + ssize_t n; + int in_fd; + int out_fd; + char buf[8192]; + + len = req->bufsml[0].len; + in_fd = req->flags; + out_fd = req->file; + offset = req->off; + use_pread = 1; + + /* Here are the rules regarding errors: + * + * 1. Read errors are reported only if nsent==0, otherwise we return nsent. + * The user needs to know that some data has already been sent, to stop + * them from sending it twice. + * + * 2. Write errors are always reported. Write errors are bad because they + * mean data loss: we've read data but now we can't write it out. + * + * We try to use pread() and fall back to regular read() if the source fd + * doesn't support positional reads, for example when it's a pipe fd. + * + * If we get EAGAIN when writing to the target fd, we poll() on it until + * it becomes writable again. + * + * FIXME: If we get a write error when use_pread==1, it should be safe to + * return the number of sent bytes instead of an error because pread() + * is, in theory, idempotent. However, special files in /dev or /proc + * may support pread() but not necessarily return the same data on + * successive reads. + * + * FIXME: There is no way now to signal that we managed to send *some* data + * before a write error. + */ + for (nsent = 0; (size_t) nsent < len; ) { + buflen = len - nsent; + + if (buflen > sizeof(buf)) + buflen = sizeof(buf); + + do + if (use_pread) + nread = pread(in_fd, buf, buflen, offset); + else + nread = read(in_fd, buf, buflen); + while (nread == -1 && errno == EINTR); + + if (nread == 0) + goto out; + + if (nread == -1) { + if (use_pread && nsent == 0 && (errno == EIO || errno == ESPIPE)) { + use_pread = 0; + continue; + } + + if (nsent == 0) + nsent = -1; + + goto out; + } + + for (nwritten = 0; nwritten < nread; ) { + do + n = write(out_fd, buf + nwritten, nread - nwritten); + while (n == -1 && errno == EINTR); + + if (n != -1) { + nwritten += n; + continue; + } + + if (errno != EAGAIN && errno != EWOULDBLOCK) { + nsent = -1; + goto out; + } + + pfd.fd = out_fd; + pfd.events = POLLOUT; + pfd.revents = 0; + + do + n = poll(&pfd, 1, -1); + while (n == -1 && errno == EINTR); + + if (n == -1 || (pfd.revents & ~POLLOUT) != 0) { + errno = EIO; + nsent = -1; + goto out; + } + } + + offset += nread; + nsent += nread; + } + +out: + if (nsent != -1) + req->off = offset; + + return nsent; +} + + +static ssize_t uv__fs_sendfile(uv_fs_t* req) { + int in_fd; + int out_fd; + + in_fd = req->flags; + out_fd = req->file; + +#if defined(__linux__) || defined(__sun) + { + off_t off; + ssize_t r; + + off = req->off; + +#ifdef __linux__ + { + static int copy_file_range_support = 1; + + if (copy_file_range_support) { + r = uv__fs_copy_file_range(in_fd, NULL, out_fd, &off, req->bufsml[0].len, 0); + + if (r == -1 && errno == ENOSYS) { + errno = 0; + copy_file_range_support = 0; + } else { + goto ok; + } + } + } +#endif + + r = sendfile(out_fd, in_fd, &off, req->bufsml[0].len); + +ok: + /* sendfile() on SunOS returns EINVAL if the target fd is not a socket but + * it still writes out data. Fortunately, we can detect it by checking if + * the offset has been updated. + */ + if (r != -1 || off > req->off) { + r = off - req->off; + req->off = off; + return r; + } + + if (errno == EINVAL || + errno == EIO || + errno == ENOTSOCK || + errno == EXDEV) { + errno = 0; + return uv__fs_sendfile_emul(req); + } + + return -1; + } +#elif defined(__APPLE__) || \ + defined(__DragonFly__) || \ + defined(__FreeBSD__) || \ + defined(__FreeBSD_kernel__) + { + off_t len; + ssize_t r; + + /* sendfile() on FreeBSD and Darwin returns EAGAIN if the target fd is in + * non-blocking mode and not all data could be written. If a non-zero + * number of bytes have been sent, we don't consider it an error. + */ + +#if defined(__FreeBSD__) || defined(__DragonFly__) + len = 0; + r = sendfile(in_fd, out_fd, req->off, req->bufsml[0].len, NULL, &len, 0); +#elif defined(__FreeBSD_kernel__) + len = 0; + r = bsd_sendfile(in_fd, + out_fd, + req->off, + req->bufsml[0].len, + NULL, + &len, + 0); +#else + /* The darwin sendfile takes len as an input for the length to send, + * so make sure to initialize it with the caller's value. */ + len = req->bufsml[0].len; + r = sendfile(in_fd, out_fd, req->off, &len, NULL, 0); +#endif + + /* + * The man page for sendfile(2) on DragonFly states that `len` contains + * a meaningful value ONLY in case of EAGAIN and EINTR. + * Nothing is said about it's value in case of other errors, so better + * not depend on the potential wrong assumption that is was not modified + * by the syscall. + */ + if (r == 0 || ((errno == EAGAIN || errno == EINTR) && len != 0)) { + req->off += len; + return (ssize_t) len; + } + + if (errno == EINVAL || + errno == EIO || + errno == ENOTSOCK || + errno == EXDEV) { + errno = 0; + return uv__fs_sendfile_emul(req); + } + + return -1; + } +#else + /* Squelch compiler warnings. */ + (void) &in_fd; + (void) &out_fd; + + return uv__fs_sendfile_emul(req); +#endif +} + + +static ssize_t uv__fs_utime(uv_fs_t* req) { +#if defined(__linux__) \ + || defined(_AIX71) \ + || defined(__sun) \ + || defined(__HAIKU__) + /* utimesat() has nanosecond resolution but we stick to microseconds + * for the sake of consistency with other platforms. + */ + struct timespec ts[2]; + ts[0] = uv__fs_to_timespec(req->atime); + ts[1] = uv__fs_to_timespec(req->mtime); + return utimensat(AT_FDCWD, req->path, ts, 0); +#elif defined(__APPLE__) \ + || defined(__DragonFly__) \ + || defined(__FreeBSD__) \ + || defined(__FreeBSD_kernel__) \ + || defined(__NetBSD__) \ + || defined(__OpenBSD__) + struct timeval tv[2]; + tv[0] = uv__fs_to_timeval(req->atime); + tv[1] = uv__fs_to_timeval(req->mtime); + return utimes(req->path, tv); +#elif defined(_AIX) \ + && !defined(_AIX71) + struct utimbuf buf; + buf.actime = req->atime; + buf.modtime = req->mtime; + return utime(req->path, &buf); +#elif defined(__MVS__) + attrib_t atr; + memset(&atr, 0, sizeof(atr)); + atr.att_mtimechg = 1; + atr.att_atimechg = 1; + atr.att_mtime = req->mtime; + atr.att_atime = req->atime; + return __lchattr((char*) req->path, &atr, sizeof(atr)); +#else + errno = ENOSYS; + return -1; +#endif +} + + +static ssize_t uv__fs_lutime(uv_fs_t* req) { +#if defined(__linux__) || \ + defined(_AIX71) || \ + defined(__sun) || \ + defined(__HAIKU__) + struct timespec ts[2]; + ts[0] = uv__fs_to_timespec(req->atime); + ts[1] = uv__fs_to_timespec(req->mtime); + return utimensat(AT_FDCWD, req->path, ts, AT_SYMLINK_NOFOLLOW); +#elif defined(__APPLE__) || \ + defined(__DragonFly__) || \ + defined(__FreeBSD__) || \ + defined(__FreeBSD_kernel__) || \ + defined(__NetBSD__) + struct timeval tv[2]; + tv[0] = uv__fs_to_timeval(req->atime); + tv[1] = uv__fs_to_timeval(req->mtime); + return lutimes(req->path, tv); +#else + errno = ENOSYS; + return -1; +#endif +} + + +static ssize_t uv__fs_write(uv_fs_t* req) { +#if defined(__linux__) + static int no_pwritev; +#endif + ssize_t r; + + /* Serialize writes on OS X, concurrent write() and pwrite() calls result in + * data loss. We can't use a per-file descriptor lock, the descriptor may be + * a dup(). + */ +#if defined(__APPLE__) + static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + + if (pthread_mutex_lock(&lock)) + abort(); +#endif + + if (req->off < 0) { + if (req->nbufs == 1) + r = write(req->file, req->bufs[0].base, req->bufs[0].len); + else + r = writev(req->file, (struct iovec*) req->bufs, req->nbufs); + } else { + if (req->nbufs == 1) { + r = pwrite(req->file, req->bufs[0].base, req->bufs[0].len, req->off); + goto done; + } +#if HAVE_PREADV + r = pwritev(req->file, (struct iovec*) req->bufs, req->nbufs, req->off); +#else +# if defined(__linux__) + if (no_pwritev) retry: +# endif + { + r = pwrite(req->file, req->bufs[0].base, req->bufs[0].len, req->off); + } +# if defined(__linux__) + else { + r = uv__pwritev(req->file, + (struct iovec*) req->bufs, + req->nbufs, + req->off); + if (r == -1 && errno == ENOSYS) { + no_pwritev = 1; + goto retry; + } + } +# endif +#endif + } + +done: +#if defined(__APPLE__) + if (pthread_mutex_unlock(&lock)) + abort(); +#endif + + return r; +} + +static ssize_t uv__fs_copyfile(uv_fs_t* req) { + uv_fs_t fs_req; + uv_file srcfd; + uv_file dstfd; + struct stat src_statsbuf; + struct stat dst_statsbuf; + int dst_flags; + int result; + int err; + off_t bytes_to_send; + off_t in_offset; + off_t bytes_written; + size_t bytes_chunk; + + dstfd = -1; + err = 0; + + /* Open the source file. */ + srcfd = uv_fs_open(NULL, &fs_req, req->path, O_RDONLY, 0, NULL); + uv_fs_req_cleanup(&fs_req); + + if (srcfd < 0) + return srcfd; + + /* Get the source file's mode. */ + if (fstat(srcfd, &src_statsbuf)) { + err = UV__ERR(errno); + goto out; + } + + dst_flags = O_WRONLY | O_CREAT; + + if (req->flags & UV_FS_COPYFILE_EXCL) + dst_flags |= O_EXCL; + + /* Open the destination file. */ + dstfd = uv_fs_open(NULL, + &fs_req, + req->new_path, + dst_flags, + src_statsbuf.st_mode, + NULL); + uv_fs_req_cleanup(&fs_req); + + if (dstfd < 0) { + err = dstfd; + goto out; + } + + /* If the file is not being opened exclusively, verify that the source and + destination are not the same file. If they are the same, bail out early. */ + if ((req->flags & UV_FS_COPYFILE_EXCL) == 0) { + /* Get the destination file's mode. */ + if (fstat(dstfd, &dst_statsbuf)) { + err = UV__ERR(errno); + goto out; + } + + /* Check if srcfd and dstfd refer to the same file */ + if (src_statsbuf.st_dev == dst_statsbuf.st_dev && + src_statsbuf.st_ino == dst_statsbuf.st_ino) { + goto out; + } + + /* Truncate the file in case the destination already existed. */ + if (ftruncate(dstfd, 0) != 0) { + err = UV__ERR(errno); + goto out; + } + } + + if (fchmod(dstfd, src_statsbuf.st_mode) == -1) { + err = UV__ERR(errno); +#ifdef __linux__ + if (err != UV_EPERM) + goto out; + + { + struct statfs s; + + /* fchmod() on CIFS shares always fails with EPERM unless the share is + * mounted with "noperm". As fchmod() is a meaningless operation on such + * shares anyway, detect that condition and squelch the error. + */ + if (fstatfs(dstfd, &s) == -1) + goto out; + + if (s.f_type != /* CIFS */ 0xFF534D42u) + goto out; + } + + err = 0; +#else /* !__linux__ */ + goto out; +#endif /* !__linux__ */ + } + +#ifdef FICLONE + if (req->flags & UV_FS_COPYFILE_FICLONE || + req->flags & UV_FS_COPYFILE_FICLONE_FORCE) { + if (ioctl(dstfd, FICLONE, srcfd) == 0) { + /* ioctl() with FICLONE succeeded. */ + goto out; + } + /* If an error occurred and force was set, return the error to the caller; + * fall back to sendfile() when force was not set. */ + if (req->flags & UV_FS_COPYFILE_FICLONE_FORCE) { + err = UV__ERR(errno); + goto out; + } + } +#else + if (req->flags & UV_FS_COPYFILE_FICLONE_FORCE) { + err = UV_ENOSYS; + goto out; + } +#endif + + bytes_to_send = src_statsbuf.st_size; + in_offset = 0; + while (bytes_to_send != 0) { + bytes_chunk = SSIZE_MAX; + if (bytes_to_send < (off_t) bytes_chunk) + bytes_chunk = bytes_to_send; + uv_fs_sendfile(NULL, &fs_req, dstfd, srcfd, in_offset, bytes_chunk, NULL); + bytes_written = fs_req.result; + uv_fs_req_cleanup(&fs_req); + + if (bytes_written < 0) { + err = bytes_written; + break; + } + + bytes_to_send -= bytes_written; + in_offset += bytes_written; + } + +out: + if (err < 0) + result = err; + else + result = 0; + + /* Close the source file. */ + err = uv__close_nocheckstdio(srcfd); + + /* Don't overwrite any existing errors. */ + if (err != 0 && result == 0) + result = err; + + /* Close the destination file if it is open. */ + if (dstfd >= 0) { + err = uv__close_nocheckstdio(dstfd); + + /* Don't overwrite any existing errors. */ + if (err != 0 && result == 0) + result = err; + + /* Remove the destination file if something went wrong. */ + if (result != 0) { + uv_fs_unlink(NULL, &fs_req, req->new_path, NULL); + /* Ignore the unlink return value, as an error already happened. */ + uv_fs_req_cleanup(&fs_req); + } + } + + if (result == 0) + return 0; + + errno = UV__ERR(result); + return -1; +} + +static void uv__to_stat(struct stat* src, uv_stat_t* dst) { + dst->st_dev = src->st_dev; + dst->st_mode = src->st_mode; + dst->st_nlink = src->st_nlink; + dst->st_uid = src->st_uid; + dst->st_gid = src->st_gid; + dst->st_rdev = src->st_rdev; + dst->st_ino = src->st_ino; + dst->st_size = src->st_size; + dst->st_blksize = src->st_blksize; + dst->st_blocks = src->st_blocks; + +#if defined(__APPLE__) + dst->st_atim.tv_sec = src->st_atimespec.tv_sec; + dst->st_atim.tv_nsec = src->st_atimespec.tv_nsec; + dst->st_mtim.tv_sec = src->st_mtimespec.tv_sec; + dst->st_mtim.tv_nsec = src->st_mtimespec.tv_nsec; + dst->st_ctim.tv_sec = src->st_ctimespec.tv_sec; + dst->st_ctim.tv_nsec = src->st_ctimespec.tv_nsec; + dst->st_birthtim.tv_sec = src->st_birthtimespec.tv_sec; + dst->st_birthtim.tv_nsec = src->st_birthtimespec.tv_nsec; + dst->st_flags = src->st_flags; + dst->st_gen = src->st_gen; +#elif defined(__ANDROID__) + dst->st_atim.tv_sec = src->st_atime; + dst->st_atim.tv_nsec = src->st_atimensec; + dst->st_mtim.tv_sec = src->st_mtime; + dst->st_mtim.tv_nsec = src->st_mtimensec; + dst->st_ctim.tv_sec = src->st_ctime; + dst->st_ctim.tv_nsec = src->st_ctimensec; + dst->st_birthtim.tv_sec = src->st_ctime; + dst->st_birthtim.tv_nsec = src->st_ctimensec; + dst->st_flags = 0; + dst->st_gen = 0; +#elif !defined(_AIX) && ( \ + defined(__DragonFly__) || \ + defined(__FreeBSD__) || \ + defined(__OpenBSD__) || \ + defined(__NetBSD__) || \ + defined(_GNU_SOURCE) || \ + defined(_BSD_SOURCE) || \ + defined(_SVID_SOURCE) || \ + defined(_XOPEN_SOURCE) || \ + defined(_DEFAULT_SOURCE)) + dst->st_atim.tv_sec = src->st_atim.tv_sec; + dst->st_atim.tv_nsec = src->st_atim.tv_nsec; + dst->st_mtim.tv_sec = src->st_mtim.tv_sec; + dst->st_mtim.tv_nsec = src->st_mtim.tv_nsec; + dst->st_ctim.tv_sec = src->st_ctim.tv_sec; + dst->st_ctim.tv_nsec = src->st_ctim.tv_nsec; +# if defined(__FreeBSD__) || \ + defined(__NetBSD__) + dst->st_birthtim.tv_sec = src->st_birthtim.tv_sec; + dst->st_birthtim.tv_nsec = src->st_birthtim.tv_nsec; + dst->st_flags = src->st_flags; + dst->st_gen = src->st_gen; +# else + dst->st_birthtim.tv_sec = src->st_ctim.tv_sec; + dst->st_birthtim.tv_nsec = src->st_ctim.tv_nsec; + dst->st_flags = 0; + dst->st_gen = 0; +# endif +#else + dst->st_atim.tv_sec = src->st_atime; + dst->st_atim.tv_nsec = 0; + dst->st_mtim.tv_sec = src->st_mtime; + dst->st_mtim.tv_nsec = 0; + dst->st_ctim.tv_sec = src->st_ctime; + dst->st_ctim.tv_nsec = 0; + dst->st_birthtim.tv_sec = src->st_ctime; + dst->st_birthtim.tv_nsec = 0; + dst->st_flags = 0; + dst->st_gen = 0; +#endif +} + + +static int uv__fs_statx(int fd, + const char* path, + int is_fstat, + int is_lstat, + uv_stat_t* buf) { + STATIC_ASSERT(UV_ENOSYS != -1); +#ifdef __linux__ + static int no_statx; + struct uv__statx statxbuf; + int dirfd; + int flags; + int mode; + int rc; + + if (uv__load_relaxed(&no_statx)) + return UV_ENOSYS; + + dirfd = AT_FDCWD; + flags = 0; /* AT_STATX_SYNC_AS_STAT */ + mode = 0xFFF; /* STATX_BASIC_STATS + STATX_BTIME */ + + if (is_fstat) { + dirfd = fd; + flags |= 0x1000; /* AT_EMPTY_PATH */ + } + + if (is_lstat) + flags |= AT_SYMLINK_NOFOLLOW; + + rc = uv__statx(dirfd, path, flags, mode, &statxbuf); + + switch (rc) { + case 0: + break; + case -1: + /* EPERM happens when a seccomp filter rejects the system call. + * Has been observed with libseccomp < 2.3.3 and docker < 18.04. + */ + if (errno != EINVAL && errno != EPERM && errno != ENOSYS) + return -1; + /* Fall through. */ + default: + /* Normally on success, zero is returned and On error, -1 is returned. + * Observed on S390 RHEL running in a docker container with statx not + * implemented, rc might return 1 with 0 set as the error code in which + * case we return ENOSYS. + */ + uv__store_relaxed(&no_statx, 1); + return UV_ENOSYS; + } + + buf->st_dev = 256 * statxbuf.stx_dev_major + statxbuf.stx_dev_minor; + buf->st_mode = statxbuf.stx_mode; + buf->st_nlink = statxbuf.stx_nlink; + buf->st_uid = statxbuf.stx_uid; + buf->st_gid = statxbuf.stx_gid; + buf->st_rdev = statxbuf.stx_rdev_major; + buf->st_ino = statxbuf.stx_ino; + buf->st_size = statxbuf.stx_size; + buf->st_blksize = statxbuf.stx_blksize; + buf->st_blocks = statxbuf.stx_blocks; + buf->st_atim.tv_sec = statxbuf.stx_atime.tv_sec; + buf->st_atim.tv_nsec = statxbuf.stx_atime.tv_nsec; + buf->st_mtim.tv_sec = statxbuf.stx_mtime.tv_sec; + buf->st_mtim.tv_nsec = statxbuf.stx_mtime.tv_nsec; + buf->st_ctim.tv_sec = statxbuf.stx_ctime.tv_sec; + buf->st_ctim.tv_nsec = statxbuf.stx_ctime.tv_nsec; + buf->st_birthtim.tv_sec = statxbuf.stx_btime.tv_sec; + buf->st_birthtim.tv_nsec = statxbuf.stx_btime.tv_nsec; + buf->st_flags = 0; + buf->st_gen = 0; + + return 0; +#else + return UV_ENOSYS; +#endif /* __linux__ */ +} + + +static int uv__fs_stat(const char *path, uv_stat_t *buf) { + struct stat pbuf; + int ret; + + ret = uv__fs_statx(-1, path, /* is_fstat */ 0, /* is_lstat */ 0, buf); + if (ret != UV_ENOSYS) + return ret; + + ret = stat(path, &pbuf); + if (ret == 0) + uv__to_stat(&pbuf, buf); + + return ret; +} + + +static int uv__fs_lstat(const char *path, uv_stat_t *buf) { + struct stat pbuf; + int ret; + + ret = uv__fs_statx(-1, path, /* is_fstat */ 0, /* is_lstat */ 1, buf); + if (ret != UV_ENOSYS) + return ret; + + ret = lstat(path, &pbuf); + if (ret == 0) + uv__to_stat(&pbuf, buf); + + return ret; +} + + +static int uv__fs_fstat(int fd, uv_stat_t *buf) { + struct stat pbuf; + int ret; + + ret = uv__fs_statx(fd, "", /* is_fstat */ 1, /* is_lstat */ 0, buf); + if (ret != UV_ENOSYS) + return ret; + + ret = fstat(fd, &pbuf); + if (ret == 0) + uv__to_stat(&pbuf, buf); + + return ret; +} + +static size_t uv__fs_buf_offset(uv_buf_t* bufs, size_t size) { + size_t offset; + /* Figure out which bufs are done */ + for (offset = 0; size > 0 && bufs[offset].len <= size; ++offset) + size -= bufs[offset].len; + + /* Fix a partial read/write */ + if (size > 0) { + bufs[offset].base += size; + bufs[offset].len -= size; + } + return offset; +} + +static ssize_t uv__fs_write_all(uv_fs_t* req) { + unsigned int iovmax; + unsigned int nbufs; + uv_buf_t* bufs; + ssize_t total; + ssize_t result; + + iovmax = uv__getiovmax(); + nbufs = req->nbufs; + bufs = req->bufs; + total = 0; + + while (nbufs > 0) { + req->nbufs = nbufs; + if (req->nbufs > iovmax) + req->nbufs = iovmax; + + do + result = uv__fs_write(req); + while (result < 0 && errno == EINTR); + + if (result <= 0) { + if (total == 0) + total = result; + break; + } + + if (req->off >= 0) + req->off += result; + + req->nbufs = uv__fs_buf_offset(req->bufs, result); + req->bufs += req->nbufs; + nbufs -= req->nbufs; + total += result; + } + + if (bufs != req->bufsml) + uv__free(bufs); + + req->bufs = NULL; + req->nbufs = 0; + + return total; +} + + +static void uv__fs_work(struct uv__work* w) { + int retry_on_eintr; + uv_fs_t* req; + ssize_t r; + + req = container_of(w, uv_fs_t, work_req); + retry_on_eintr = !(req->fs_type == UV_FS_CLOSE || + req->fs_type == UV_FS_READ); + + do { + errno = 0; + +#define X(type, action) \ + case UV_FS_ ## type: \ + r = action; \ + break; + + switch (req->fs_type) { + X(ACCESS, access(req->path, req->flags)); + X(CHMOD, chmod(req->path, req->mode)); + X(CHOWN, chown(req->path, req->uid, req->gid)); + X(CLOSE, uv__fs_close(req->file)); + X(COPYFILE, uv__fs_copyfile(req)); + X(FCHMOD, fchmod(req->file, req->mode)); + X(FCHOWN, fchown(req->file, req->uid, req->gid)); + X(LCHOWN, lchown(req->path, req->uid, req->gid)); + X(FDATASYNC, uv__fs_fdatasync(req)); + X(FSTAT, uv__fs_fstat(req->file, &req->statbuf)); + X(FSYNC, uv__fs_fsync(req)); + X(FTRUNCATE, ftruncate(req->file, req->off)); + X(FUTIME, uv__fs_futime(req)); + X(LUTIME, uv__fs_lutime(req)); + X(LSTAT, uv__fs_lstat(req->path, &req->statbuf)); + X(LINK, link(req->path, req->new_path)); + X(MKDIR, mkdir(req->path, req->mode)); + X(MKDTEMP, uv__fs_mkdtemp(req)); + X(MKSTEMP, uv__fs_mkstemp(req)); + X(OPEN, uv__fs_open(req)); + X(READ, uv__fs_read(req)); + X(SCANDIR, uv__fs_scandir(req)); + X(OPENDIR, uv__fs_opendir(req)); + X(READDIR, uv__fs_readdir(req)); + X(CLOSEDIR, uv__fs_closedir(req)); + X(READLINK, uv__fs_readlink(req)); + X(REALPATH, uv__fs_realpath(req)); + X(RENAME, rename(req->path, req->new_path)); + X(RMDIR, rmdir(req->path)); + X(SENDFILE, uv__fs_sendfile(req)); + X(STAT, uv__fs_stat(req->path, &req->statbuf)); + X(STATFS, uv__fs_statfs(req)); + X(SYMLINK, symlink(req->path, req->new_path)); + X(UNLINK, unlink(req->path)); + X(UTIME, uv__fs_utime(req)); + X(WRITE, uv__fs_write_all(req)); + default: abort(); + } +#undef X + } while (r == -1 && errno == EINTR && retry_on_eintr); + + if (r == -1) + req->result = UV__ERR(errno); + else + req->result = r; + + if (r == 0 && (req->fs_type == UV_FS_STAT || + req->fs_type == UV_FS_FSTAT || + req->fs_type == UV_FS_LSTAT)) { + req->ptr = &req->statbuf; + } +} + + +static void uv__fs_done(struct uv__work* w, int status) { + uv_fs_t* req; + + req = container_of(w, uv_fs_t, work_req); + uv__req_unregister(req->loop, req); + + if (status == UV_ECANCELED) { + assert(req->result == 0); + req->result = UV_ECANCELED; + } + + req->cb(req); +} + + +int uv_fs_access(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + int flags, + uv_fs_cb cb) { + INIT(ACCESS); + PATH; + req->flags = flags; + POST; +} + + +int uv_fs_chmod(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + int mode, + uv_fs_cb cb) { + INIT(CHMOD); + PATH; + req->mode = mode; + POST; +} + + +int uv_fs_chown(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + uv_uid_t uid, + uv_gid_t gid, + uv_fs_cb cb) { + INIT(CHOWN); + PATH; + req->uid = uid; + req->gid = gid; + POST; +} + + +int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { + INIT(CLOSE); + req->file = file; + POST; +} + + +int uv_fs_fchmod(uv_loop_t* loop, + uv_fs_t* req, + uv_file file, + int mode, + uv_fs_cb cb) { + INIT(FCHMOD); + req->file = file; + req->mode = mode; + POST; +} + + +int uv_fs_fchown(uv_loop_t* loop, + uv_fs_t* req, + uv_file file, + uv_uid_t uid, + uv_gid_t gid, + uv_fs_cb cb) { + INIT(FCHOWN); + req->file = file; + req->uid = uid; + req->gid = gid; + POST; +} + + +int uv_fs_lchown(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + uv_uid_t uid, + uv_gid_t gid, + uv_fs_cb cb) { + INIT(LCHOWN); + PATH; + req->uid = uid; + req->gid = gid; + POST; +} + + +int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { + INIT(FDATASYNC); + req->file = file; + POST; +} + + +int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { + INIT(FSTAT); + req->file = file; + POST; +} + + +int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) { + INIT(FSYNC); + req->file = file; + POST; +} + + +int uv_fs_ftruncate(uv_loop_t* loop, + uv_fs_t* req, + uv_file file, + int64_t off, + uv_fs_cb cb) { + INIT(FTRUNCATE); + req->file = file; + req->off = off; + POST; +} + + +int uv_fs_futime(uv_loop_t* loop, + uv_fs_t* req, + uv_file file, + double atime, + double mtime, + uv_fs_cb cb) { + INIT(FUTIME); + req->file = file; + req->atime = atime; + req->mtime = mtime; + POST; +} + +int uv_fs_lutime(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + double atime, + double mtime, + uv_fs_cb cb) { + INIT(LUTIME); + PATH; + req->atime = atime; + req->mtime = mtime; + POST; +} + + +int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { + INIT(LSTAT); + PATH; + POST; +} + + +int uv_fs_link(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + const char* new_path, + uv_fs_cb cb) { + INIT(LINK); + PATH2; + POST; +} + + +int uv_fs_mkdir(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + int mode, + uv_fs_cb cb) { + INIT(MKDIR); + PATH; + req->mode = mode; + POST; +} + + +int uv_fs_mkdtemp(uv_loop_t* loop, + uv_fs_t* req, + const char* tpl, + uv_fs_cb cb) { + INIT(MKDTEMP); + req->path = uv__strdup(tpl); + if (req->path == NULL) + return UV_ENOMEM; + POST; +} + + +int uv_fs_mkstemp(uv_loop_t* loop, + uv_fs_t* req, + const char* tpl, + uv_fs_cb cb) { + INIT(MKSTEMP); + req->path = uv__strdup(tpl); + if (req->path == NULL) + return UV_ENOMEM; + POST; +} + + +int uv_fs_open(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + int flags, + int mode, + uv_fs_cb cb) { + INIT(OPEN); + PATH; + req->flags = flags; + req->mode = mode; + POST; +} + + +int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, + uv_file file, + const uv_buf_t bufs[], + unsigned int nbufs, + int64_t off, + uv_fs_cb cb) { + INIT(READ); + + if (bufs == NULL || nbufs == 0) + return UV_EINVAL; + + req->file = file; + + req->nbufs = nbufs; + req->bufs = req->bufsml; + if (nbufs > ARRAY_SIZE(req->bufsml)) + req->bufs = uv__malloc(nbufs * sizeof(*bufs)); + + if (req->bufs == NULL) + return UV_ENOMEM; + + memcpy(req->bufs, bufs, nbufs * sizeof(*bufs)); + + req->off = off; + POST; +} + + +int uv_fs_scandir(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + int flags, + uv_fs_cb cb) { + INIT(SCANDIR); + PATH; + req->flags = flags; + POST; +} + +int uv_fs_opendir(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + uv_fs_cb cb) { + INIT(OPENDIR); + PATH; + POST; +} + +int uv_fs_readdir(uv_loop_t* loop, + uv_fs_t* req, + uv_dir_t* dir, + uv_fs_cb cb) { + INIT(READDIR); + + if (dir == NULL || dir->dir == NULL || dir->dirents == NULL) + return UV_EINVAL; + + req->ptr = dir; + POST; +} + +int uv_fs_closedir(uv_loop_t* loop, + uv_fs_t* req, + uv_dir_t* dir, + uv_fs_cb cb) { + INIT(CLOSEDIR); + + if (dir == NULL) + return UV_EINVAL; + + req->ptr = dir; + POST; +} + +int uv_fs_readlink(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + uv_fs_cb cb) { + INIT(READLINK); + PATH; + POST; +} + + +int uv_fs_realpath(uv_loop_t* loop, + uv_fs_t* req, + const char * path, + uv_fs_cb cb) { + INIT(REALPATH); + PATH; + POST; +} + + +int uv_fs_rename(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + const char* new_path, + uv_fs_cb cb) { + INIT(RENAME); + PATH2; + POST; +} + + +int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { + INIT(RMDIR); + PATH; + POST; +} + + +int uv_fs_sendfile(uv_loop_t* loop, + uv_fs_t* req, + uv_file out_fd, + uv_file in_fd, + int64_t off, + size_t len, + uv_fs_cb cb) { + INIT(SENDFILE); + req->flags = in_fd; /* hack */ + req->file = out_fd; + req->off = off; + req->bufsml[0].len = len; + POST; +} + + +int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { + INIT(STAT); + PATH; + POST; +} + + +int uv_fs_symlink(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + const char* new_path, + int flags, + uv_fs_cb cb) { + INIT(SYMLINK); + PATH2; + req->flags = flags; + POST; +} + + +int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { + INIT(UNLINK); + PATH; + POST; +} + + +int uv_fs_utime(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + double atime, + double mtime, + uv_fs_cb cb) { + INIT(UTIME); + PATH; + req->atime = atime; + req->mtime = mtime; + POST; +} + + +int uv_fs_write(uv_loop_t* loop, + uv_fs_t* req, + uv_file file, + const uv_buf_t bufs[], + unsigned int nbufs, + int64_t off, + uv_fs_cb cb) { + INIT(WRITE); + + if (bufs == NULL || nbufs == 0) + return UV_EINVAL; + + req->file = file; + + req->nbufs = nbufs; + req->bufs = req->bufsml; + if (nbufs > ARRAY_SIZE(req->bufsml)) + req->bufs = uv__malloc(nbufs * sizeof(*bufs)); + + if (req->bufs == NULL) + return UV_ENOMEM; + + memcpy(req->bufs, bufs, nbufs * sizeof(*bufs)); + + req->off = off; + POST; +} + + +void uv_fs_req_cleanup(uv_fs_t* req) { + if (req == NULL) + return; + + /* Only necessary for asychronous requests, i.e., requests with a callback. + * Synchronous ones don't copy their arguments and have req->path and + * req->new_path pointing to user-owned memory. UV_FS_MKDTEMP and + * UV_FS_MKSTEMP are the exception to the rule, they always allocate memory. + */ + if (req->path != NULL && + (req->cb != NULL || + req->fs_type == UV_FS_MKDTEMP || req->fs_type == UV_FS_MKSTEMP)) + uv__free((void*) req->path); /* Memory is shared with req->new_path. */ + + req->path = NULL; + req->new_path = NULL; + + if (req->fs_type == UV_FS_READDIR && req->ptr != NULL) + uv__fs_readdir_cleanup(req); + + if (req->fs_type == UV_FS_SCANDIR && req->ptr != NULL) + uv__fs_scandir_cleanup(req); + + if (req->bufs != req->bufsml) + uv__free(req->bufs); + req->bufs = NULL; + + if (req->fs_type != UV_FS_OPENDIR && req->ptr != &req->statbuf) + uv__free(req->ptr); + req->ptr = NULL; +} + + +int uv_fs_copyfile(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + const char* new_path, + int flags, + uv_fs_cb cb) { + INIT(COPYFILE); + + if (flags & ~(UV_FS_COPYFILE_EXCL | + UV_FS_COPYFILE_FICLONE | + UV_FS_COPYFILE_FICLONE_FORCE)) { + return UV_EINVAL; + } + + PATH2; + req->flags = flags; + POST; +} + + +int uv_fs_statfs(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + uv_fs_cb cb) { + INIT(STATFS); + PATH; + POST; +} + +int uv_fs_get_system_error(const uv_fs_t* req) { + return -req->result; +} diff --git a/include/libuv/src/unix/fsevents.c b/include/libuv/src/unix/fsevents.c new file mode 100644 index 000000000..a51f29b3f --- /dev/null +++ b/include/libuv/src/unix/fsevents.c @@ -0,0 +1,923 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#if TARGET_OS_IPHONE || MAC_OS_X_VERSION_MAX_ALLOWED < 1070 + +/* iOS (currently) doesn't provide the FSEvents-API (nor CoreServices) */ +/* macOS prior to 10.7 doesn't provide the full FSEvents API so use kqueue */ + +int uv__fsevents_init(uv_fs_event_t* handle) { + return 0; +} + + +int uv__fsevents_close(uv_fs_event_t* handle) { + return 0; +} + + +void uv__fsevents_loop_delete(uv_loop_t* loop) { +} + +#else /* TARGET_OS_IPHONE */ + +#include "darwin-stub.h" + +#include +#include +#include +#include + +static const int kFSEventsModified = + kFSEventStreamEventFlagItemChangeOwner | + kFSEventStreamEventFlagItemFinderInfoMod | + kFSEventStreamEventFlagItemInodeMetaMod | + kFSEventStreamEventFlagItemModified | + kFSEventStreamEventFlagItemXattrMod; + +static const int kFSEventsRenamed = + kFSEventStreamEventFlagItemCreated | + kFSEventStreamEventFlagItemRemoved | + kFSEventStreamEventFlagItemRenamed; + +static const int kFSEventsSystem = + kFSEventStreamEventFlagUserDropped | + kFSEventStreamEventFlagKernelDropped | + kFSEventStreamEventFlagEventIdsWrapped | + kFSEventStreamEventFlagHistoryDone | + kFSEventStreamEventFlagMount | + kFSEventStreamEventFlagUnmount | + kFSEventStreamEventFlagRootChanged; + +typedef struct uv__fsevents_event_s uv__fsevents_event_t; +typedef struct uv__cf_loop_signal_s uv__cf_loop_signal_t; +typedef struct uv__cf_loop_state_s uv__cf_loop_state_t; + +enum uv__cf_loop_signal_type_e { + kUVCFLoopSignalRegular, + kUVCFLoopSignalClosing +}; +typedef enum uv__cf_loop_signal_type_e uv__cf_loop_signal_type_t; + +struct uv__cf_loop_signal_s { + QUEUE member; + uv_fs_event_t* handle; + uv__cf_loop_signal_type_t type; +}; + +struct uv__fsevents_event_s { + QUEUE member; + int events; + char path[1]; +}; + +struct uv__cf_loop_state_s { + CFRunLoopRef loop; + CFRunLoopSourceRef signal_source; + int fsevent_need_reschedule; + FSEventStreamRef fsevent_stream; + uv_sem_t fsevent_sem; + uv_mutex_t fsevent_mutex; + void* fsevent_handles[2]; + unsigned int fsevent_handle_count; +}; + +/* Forward declarations */ +static void uv__cf_loop_cb(void* arg); +static void* uv__cf_loop_runner(void* arg); +static int uv__cf_loop_signal(uv_loop_t* loop, + uv_fs_event_t* handle, + uv__cf_loop_signal_type_t type); + +/* Lazy-loaded by uv__fsevents_global_init(). */ +static CFArrayRef (*pCFArrayCreate)(CFAllocatorRef, + const void**, + CFIndex, + const CFArrayCallBacks*); +static void (*pCFRelease)(CFTypeRef); +static void (*pCFRunLoopAddSource)(CFRunLoopRef, + CFRunLoopSourceRef, + CFStringRef); +static CFRunLoopRef (*pCFRunLoopGetCurrent)(void); +static void (*pCFRunLoopRemoveSource)(CFRunLoopRef, + CFRunLoopSourceRef, + CFStringRef); +static void (*pCFRunLoopRun)(void); +static CFRunLoopSourceRef (*pCFRunLoopSourceCreate)(CFAllocatorRef, + CFIndex, + CFRunLoopSourceContext*); +static void (*pCFRunLoopSourceSignal)(CFRunLoopSourceRef); +static void (*pCFRunLoopStop)(CFRunLoopRef); +static void (*pCFRunLoopWakeUp)(CFRunLoopRef); +static CFStringRef (*pCFStringCreateWithFileSystemRepresentation)( + CFAllocatorRef, + const char*); +static CFStringEncoding (*pCFStringGetSystemEncoding)(void); +static CFStringRef (*pkCFRunLoopDefaultMode); +static FSEventStreamRef (*pFSEventStreamCreate)(CFAllocatorRef, + FSEventStreamCallback, + FSEventStreamContext*, + CFArrayRef, + FSEventStreamEventId, + CFTimeInterval, + FSEventStreamCreateFlags); +static void (*pFSEventStreamFlushSync)(FSEventStreamRef); +static void (*pFSEventStreamInvalidate)(FSEventStreamRef); +static void (*pFSEventStreamRelease)(FSEventStreamRef); +static void (*pFSEventStreamScheduleWithRunLoop)(FSEventStreamRef, + CFRunLoopRef, + CFStringRef); +static int (*pFSEventStreamStart)(FSEventStreamRef); +static void (*pFSEventStreamStop)(FSEventStreamRef); + +#define UV__FSEVENTS_PROCESS(handle, block) \ + do { \ + QUEUE events; \ + QUEUE* q; \ + uv__fsevents_event_t* event; \ + int err; \ + uv_mutex_lock(&(handle)->cf_mutex); \ + /* Split-off all events and empty original queue */ \ + QUEUE_MOVE(&(handle)->cf_events, &events); \ + /* Get error (if any) and zero original one */ \ + err = (handle)->cf_error; \ + (handle)->cf_error = 0; \ + uv_mutex_unlock(&(handle)->cf_mutex); \ + /* Loop through events, deallocating each after processing */ \ + while (!QUEUE_EMPTY(&events)) { \ + q = QUEUE_HEAD(&events); \ + event = QUEUE_DATA(q, uv__fsevents_event_t, member); \ + QUEUE_REMOVE(q); \ + /* NOTE: Checking uv__is_active() is required here, because handle \ + * callback may close handle and invoking it after it will lead to \ + * incorrect behaviour */ \ + if (!uv__is_closing((handle)) && uv__is_active((handle))) \ + block \ + /* Free allocated data */ \ + uv__free(event); \ + } \ + if (err != 0 && !uv__is_closing((handle)) && uv__is_active((handle))) \ + (handle)->cb((handle), NULL, 0, err); \ + } while (0) + + +/* Runs in UV loop's thread, when there're events to report to handle */ +static void uv__fsevents_cb(uv_async_t* cb) { + uv_fs_event_t* handle; + + handle = cb->data; + + UV__FSEVENTS_PROCESS(handle, { + handle->cb(handle, event->path[0] ? event->path : NULL, event->events, 0); + }); +} + + +/* Runs in CF thread, pushed event into handle's event list */ +static void uv__fsevents_push_event(uv_fs_event_t* handle, + QUEUE* events, + int err) { + assert(events != NULL || err != 0); + uv_mutex_lock(&handle->cf_mutex); + + /* Concatenate two queues */ + if (events != NULL) + QUEUE_ADD(&handle->cf_events, events); + + /* Propagate error */ + if (err != 0) + handle->cf_error = err; + uv_mutex_unlock(&handle->cf_mutex); + + uv_async_send(handle->cf_cb); +} + + +/* Runs in CF thread, when there're events in FSEventStream */ +static void uv__fsevents_event_cb(const FSEventStreamRef streamRef, + void* info, + size_t numEvents, + void* eventPaths, + const FSEventStreamEventFlags eventFlags[], + const FSEventStreamEventId eventIds[]) { + size_t i; + int len; + char** paths; + char* path; + char* pos; + uv_fs_event_t* handle; + QUEUE* q; + uv_loop_t* loop; + uv__cf_loop_state_t* state; + uv__fsevents_event_t* event; + FSEventStreamEventFlags flags; + QUEUE head; + + loop = info; + state = loop->cf_state; + assert(state != NULL); + paths = eventPaths; + + /* For each handle */ + uv_mutex_lock(&state->fsevent_mutex); + QUEUE_FOREACH(q, &state->fsevent_handles) { + handle = QUEUE_DATA(q, uv_fs_event_t, cf_member); + QUEUE_INIT(&head); + + /* Process and filter out events */ + for (i = 0; i < numEvents; i++) { + flags = eventFlags[i]; + + /* Ignore system events */ + if (flags & kFSEventsSystem) + continue; + + path = paths[i]; + len = strlen(path); + + if (handle->realpath_len == 0) + continue; /* This should be unreachable */ + + /* Filter out paths that are outside handle's request */ + if (len < handle->realpath_len) + continue; + + /* Make sure that realpath actually named a directory, + * (unless watching root, which alone keeps a trailing slash on the realpath) + * or that we matched the whole string */ + if (handle->realpath_len != len && + handle->realpath_len > 1 && + path[handle->realpath_len] != '/') + continue; + + if (memcmp(path, handle->realpath, handle->realpath_len) != 0) + continue; + + if (!(handle->realpath_len == 1 && handle->realpath[0] == '/')) { + /* Remove common prefix, unless the watched folder is "/" */ + path += handle->realpath_len; + len -= handle->realpath_len; + + /* Ignore events with path equal to directory itself */ + if (len <= 1 && (flags & kFSEventStreamEventFlagItemIsDir)) + continue; + + if (len == 0) { + /* Since we're using fsevents to watch the file itself, + * realpath == path, and we now need to get the basename of the file back + * (for commonality with other codepaths and platforms). */ + while (len < handle->realpath_len && path[-1] != '/') { + path--; + len++; + } + /* Created and Removed seem to be always set, but don't make sense */ + flags &= ~kFSEventsRenamed; + } else { + /* Skip forward slash */ + path++; + len--; + } + } + + /* Do not emit events from subdirectories (without option set) */ + if ((handle->cf_flags & UV_FS_EVENT_RECURSIVE) == 0 && *path != '\0') { + pos = strchr(path + 1, '/'); + if (pos != NULL) + continue; + } + + event = uv__malloc(sizeof(*event) + len); + if (event == NULL) + break; + + memset(event, 0, sizeof(*event)); + memcpy(event->path, path, len + 1); + event->events = UV_RENAME; + + if (0 == (flags & kFSEventsRenamed)) { + if (0 != (flags & kFSEventsModified) || + 0 == (flags & kFSEventStreamEventFlagItemIsDir)) + event->events = UV_CHANGE; + } + + QUEUE_INSERT_TAIL(&head, &event->member); + } + + if (!QUEUE_EMPTY(&head)) + uv__fsevents_push_event(handle, &head, 0); + } + uv_mutex_unlock(&state->fsevent_mutex); +} + + +/* Runs in CF thread */ +static int uv__fsevents_create_stream(uv_loop_t* loop, CFArrayRef paths) { + uv__cf_loop_state_t* state; + FSEventStreamContext ctx; + FSEventStreamRef ref; + CFAbsoluteTime latency; + FSEventStreamCreateFlags flags; + + /* Initialize context */ + memset(&ctx, 0, sizeof(ctx)); + ctx.info = loop; + + latency = 0.05; + + /* Explanation of selected flags: + * 1. NoDefer - without this flag, events that are happening continuously + * (i.e. each event is happening after time interval less than `latency`, + * counted from previous event), will be deferred and passed to callback + * once they'll either fill whole OS buffer, or when this continuous stream + * will stop (i.e. there'll be delay between events, bigger than + * `latency`). + * Specifying this flag will invoke callback after `latency` time passed + * since event. + * 2. FileEvents - fire callback for file changes too (by default it is firing + * it only for directory changes). + */ + flags = kFSEventStreamCreateFlagNoDefer | kFSEventStreamCreateFlagFileEvents; + + /* + * NOTE: It might sound like a good idea to remember last seen StreamEventId, + * but in reality one dir might have last StreamEventId less than, the other, + * that is being watched now. Which will cause FSEventStream API to report + * changes to files from the past. + */ + ref = pFSEventStreamCreate(NULL, + &uv__fsevents_event_cb, + &ctx, + paths, + kFSEventStreamEventIdSinceNow, + latency, + flags); + assert(ref != NULL); + + state = loop->cf_state; + pFSEventStreamScheduleWithRunLoop(ref, + state->loop, + *pkCFRunLoopDefaultMode); + if (!pFSEventStreamStart(ref)) { + pFSEventStreamInvalidate(ref); + pFSEventStreamRelease(ref); + return UV_EMFILE; + } + + state->fsevent_stream = ref; + return 0; +} + + +/* Runs in CF thread */ +static void uv__fsevents_destroy_stream(uv_loop_t* loop) { + uv__cf_loop_state_t* state; + + state = loop->cf_state; + + if (state->fsevent_stream == NULL) + return; + + /* Stop emitting events */ + pFSEventStreamStop(state->fsevent_stream); + + /* Release stream */ + pFSEventStreamInvalidate(state->fsevent_stream); + pFSEventStreamRelease(state->fsevent_stream); + state->fsevent_stream = NULL; +} + + +/* Runs in CF thread, when there're new fsevent handles to add to stream */ +static void uv__fsevents_reschedule(uv_fs_event_t* handle, + uv__cf_loop_signal_type_t type) { + uv__cf_loop_state_t* state; + QUEUE* q; + uv_fs_event_t* curr; + CFArrayRef cf_paths; + CFStringRef* paths; + unsigned int i; + int err; + unsigned int path_count; + + state = handle->loop->cf_state; + paths = NULL; + cf_paths = NULL; + err = 0; + /* NOTE: `i` is used in deallocation loop below */ + i = 0; + + /* Optimization to prevent O(n^2) time spent when starting to watch + * many files simultaneously + */ + uv_mutex_lock(&state->fsevent_mutex); + if (state->fsevent_need_reschedule == 0) { + uv_mutex_unlock(&state->fsevent_mutex); + goto final; + } + state->fsevent_need_reschedule = 0; + uv_mutex_unlock(&state->fsevent_mutex); + + /* Destroy previous FSEventStream */ + uv__fsevents_destroy_stream(handle->loop); + + /* Any failure below will be a memory failure */ + err = UV_ENOMEM; + + /* Create list of all watched paths */ + uv_mutex_lock(&state->fsevent_mutex); + path_count = state->fsevent_handle_count; + if (path_count != 0) { + paths = uv__malloc(sizeof(*paths) * path_count); + if (paths == NULL) { + uv_mutex_unlock(&state->fsevent_mutex); + goto final; + } + + q = &state->fsevent_handles; + for (; i < path_count; i++) { + q = QUEUE_NEXT(q); + assert(q != &state->fsevent_handles); + curr = QUEUE_DATA(q, uv_fs_event_t, cf_member); + + assert(curr->realpath != NULL); + paths[i] = + pCFStringCreateWithFileSystemRepresentation(NULL, curr->realpath); + if (paths[i] == NULL) { + uv_mutex_unlock(&state->fsevent_mutex); + goto final; + } + } + } + uv_mutex_unlock(&state->fsevent_mutex); + err = 0; + + if (path_count != 0) { + /* Create new FSEventStream */ + cf_paths = pCFArrayCreate(NULL, (const void**) paths, path_count, NULL); + if (cf_paths == NULL) { + err = UV_ENOMEM; + goto final; + } + err = uv__fsevents_create_stream(handle->loop, cf_paths); + } + +final: + /* Deallocate all paths in case of failure */ + if (err != 0) { + if (cf_paths == NULL) { + while (i != 0) + pCFRelease(paths[--i]); + uv__free(paths); + } else { + /* CFArray takes ownership of both strings and original C-array */ + pCFRelease(cf_paths); + } + + /* Broadcast error to all handles */ + uv_mutex_lock(&state->fsevent_mutex); + QUEUE_FOREACH(q, &state->fsevent_handles) { + curr = QUEUE_DATA(q, uv_fs_event_t, cf_member); + uv__fsevents_push_event(curr, NULL, err); + } + uv_mutex_unlock(&state->fsevent_mutex); + } + + /* + * Main thread will block until the removal of handle from the list, + * we must tell it when we're ready. + * + * NOTE: This is coupled with `uv_sem_wait()` in `uv__fsevents_close` + */ + if (type == kUVCFLoopSignalClosing) + uv_sem_post(&state->fsevent_sem); +} + + +static int uv__fsevents_global_init(void) { + static pthread_mutex_t global_init_mutex = PTHREAD_MUTEX_INITIALIZER; + static void* core_foundation_handle; + static void* core_services_handle; + int err; + + err = 0; + pthread_mutex_lock(&global_init_mutex); + if (core_foundation_handle != NULL) + goto out; + + /* The libraries are never unloaded because we currently don't have a good + * mechanism for keeping a reference count. It's unlikely to be an issue + * but if it ever becomes one, we can turn the dynamic library handles into + * per-event loop properties and have the dynamic linker keep track for us. + */ + err = UV_ENOSYS; + core_foundation_handle = dlopen("/System/Library/Frameworks/" + "CoreFoundation.framework/" + "Versions/A/CoreFoundation", + RTLD_LAZY | RTLD_LOCAL); + if (core_foundation_handle == NULL) + goto out; + + core_services_handle = dlopen("/System/Library/Frameworks/" + "CoreServices.framework/" + "Versions/A/CoreServices", + RTLD_LAZY | RTLD_LOCAL); + if (core_services_handle == NULL) + goto out; + + err = UV_ENOENT; +#define V(handle, symbol) \ + do { \ + *(void **)(&p ## symbol) = dlsym((handle), #symbol); \ + if (p ## symbol == NULL) \ + goto out; \ + } \ + while (0) + V(core_foundation_handle, CFArrayCreate); + V(core_foundation_handle, CFRelease); + V(core_foundation_handle, CFRunLoopAddSource); + V(core_foundation_handle, CFRunLoopGetCurrent); + V(core_foundation_handle, CFRunLoopRemoveSource); + V(core_foundation_handle, CFRunLoopRun); + V(core_foundation_handle, CFRunLoopSourceCreate); + V(core_foundation_handle, CFRunLoopSourceSignal); + V(core_foundation_handle, CFRunLoopStop); + V(core_foundation_handle, CFRunLoopWakeUp); + V(core_foundation_handle, CFStringCreateWithFileSystemRepresentation); + V(core_foundation_handle, CFStringGetSystemEncoding); + V(core_foundation_handle, kCFRunLoopDefaultMode); + V(core_services_handle, FSEventStreamCreate); + V(core_services_handle, FSEventStreamFlushSync); + V(core_services_handle, FSEventStreamInvalidate); + V(core_services_handle, FSEventStreamRelease); + V(core_services_handle, FSEventStreamScheduleWithRunLoop); + V(core_services_handle, FSEventStreamStart); + V(core_services_handle, FSEventStreamStop); +#undef V + err = 0; + +out: + if (err && core_services_handle != NULL) { + dlclose(core_services_handle); + core_services_handle = NULL; + } + + if (err && core_foundation_handle != NULL) { + dlclose(core_foundation_handle); + core_foundation_handle = NULL; + } + + pthread_mutex_unlock(&global_init_mutex); + return err; +} + + +/* Runs in UV loop */ +static int uv__fsevents_loop_init(uv_loop_t* loop) { + CFRunLoopSourceContext ctx; + uv__cf_loop_state_t* state; + pthread_attr_t attr_storage; + pthread_attr_t* attr; + int err; + + if (loop->cf_state != NULL) + return 0; + + err = uv__fsevents_global_init(); + if (err) + return err; + + state = uv__calloc(1, sizeof(*state)); + if (state == NULL) + return UV_ENOMEM; + + err = uv_mutex_init(&loop->cf_mutex); + if (err) + goto fail_mutex_init; + + err = uv_sem_init(&loop->cf_sem, 0); + if (err) + goto fail_sem_init; + + QUEUE_INIT(&loop->cf_signals); + + err = uv_sem_init(&state->fsevent_sem, 0); + if (err) + goto fail_fsevent_sem_init; + + err = uv_mutex_init(&state->fsevent_mutex); + if (err) + goto fail_fsevent_mutex_init; + + QUEUE_INIT(&state->fsevent_handles); + state->fsevent_need_reschedule = 0; + state->fsevent_handle_count = 0; + + memset(&ctx, 0, sizeof(ctx)); + ctx.info = loop; + ctx.perform = uv__cf_loop_cb; + state->signal_source = pCFRunLoopSourceCreate(NULL, 0, &ctx); + if (state->signal_source == NULL) { + err = UV_ENOMEM; + goto fail_signal_source_create; + } + + /* In the unlikely event that pthread_attr_init() fails, create the thread + * with the default stack size. We'll use a little more address space but + * that in itself is not a fatal error. + */ + attr = &attr_storage; + if (pthread_attr_init(attr)) + attr = NULL; + + if (attr != NULL) + if (pthread_attr_setstacksize(attr, 4 * PTHREAD_STACK_MIN)) + abort(); + + loop->cf_state = state; + + /* uv_thread_t is an alias for pthread_t. */ + err = UV__ERR(pthread_create(&loop->cf_thread, attr, uv__cf_loop_runner, loop)); + + if (attr != NULL) + pthread_attr_destroy(attr); + + if (err) + goto fail_thread_create; + + /* Synchronize threads */ + uv_sem_wait(&loop->cf_sem); + return 0; + +fail_thread_create: + loop->cf_state = NULL; + +fail_signal_source_create: + uv_mutex_destroy(&state->fsevent_mutex); + +fail_fsevent_mutex_init: + uv_sem_destroy(&state->fsevent_sem); + +fail_fsevent_sem_init: + uv_sem_destroy(&loop->cf_sem); + +fail_sem_init: + uv_mutex_destroy(&loop->cf_mutex); + +fail_mutex_init: + uv__free(state); + return err; +} + + +/* Runs in UV loop */ +void uv__fsevents_loop_delete(uv_loop_t* loop) { + uv__cf_loop_signal_t* s; + uv__cf_loop_state_t* state; + QUEUE* q; + + if (loop->cf_state == NULL) + return; + + if (uv__cf_loop_signal(loop, NULL, kUVCFLoopSignalRegular) != 0) + abort(); + + uv_thread_join(&loop->cf_thread); + uv_sem_destroy(&loop->cf_sem); + uv_mutex_destroy(&loop->cf_mutex); + + /* Free any remaining data */ + while (!QUEUE_EMPTY(&loop->cf_signals)) { + q = QUEUE_HEAD(&loop->cf_signals); + s = QUEUE_DATA(q, uv__cf_loop_signal_t, member); + QUEUE_REMOVE(q); + uv__free(s); + } + + /* Destroy state */ + state = loop->cf_state; + uv_sem_destroy(&state->fsevent_sem); + uv_mutex_destroy(&state->fsevent_mutex); + pCFRelease(state->signal_source); + uv__free(state); + loop->cf_state = NULL; +} + + +/* Runs in CF thread. This is the CF loop's body */ +static void* uv__cf_loop_runner(void* arg) { + uv_loop_t* loop; + uv__cf_loop_state_t* state; + + loop = arg; + state = loop->cf_state; + state->loop = pCFRunLoopGetCurrent(); + + pCFRunLoopAddSource(state->loop, + state->signal_source, + *pkCFRunLoopDefaultMode); + + uv_sem_post(&loop->cf_sem); + + pCFRunLoopRun(); + pCFRunLoopRemoveSource(state->loop, + state->signal_source, + *pkCFRunLoopDefaultMode); + + state->loop = NULL; + + return NULL; +} + + +/* Runs in CF thread, executed after `uv__cf_loop_signal()` */ +static void uv__cf_loop_cb(void* arg) { + uv_loop_t* loop; + uv__cf_loop_state_t* state; + QUEUE* item; + QUEUE split_head; + uv__cf_loop_signal_t* s; + + loop = arg; + state = loop->cf_state; + + uv_mutex_lock(&loop->cf_mutex); + QUEUE_MOVE(&loop->cf_signals, &split_head); + uv_mutex_unlock(&loop->cf_mutex); + + while (!QUEUE_EMPTY(&split_head)) { + item = QUEUE_HEAD(&split_head); + QUEUE_REMOVE(item); + + s = QUEUE_DATA(item, uv__cf_loop_signal_t, member); + + /* This was a termination signal */ + if (s->handle == NULL) + pCFRunLoopStop(state->loop); + else + uv__fsevents_reschedule(s->handle, s->type); + + uv__free(s); + } +} + + +/* Runs in UV loop to notify CF thread */ +int uv__cf_loop_signal(uv_loop_t* loop, + uv_fs_event_t* handle, + uv__cf_loop_signal_type_t type) { + uv__cf_loop_signal_t* item; + uv__cf_loop_state_t* state; + + item = uv__malloc(sizeof(*item)); + if (item == NULL) + return UV_ENOMEM; + + item->handle = handle; + item->type = type; + + uv_mutex_lock(&loop->cf_mutex); + QUEUE_INSERT_TAIL(&loop->cf_signals, &item->member); + + state = loop->cf_state; + assert(state != NULL); + pCFRunLoopSourceSignal(state->signal_source); + pCFRunLoopWakeUp(state->loop); + + uv_mutex_unlock(&loop->cf_mutex); + + return 0; +} + + +/* Runs in UV loop to initialize handle */ +int uv__fsevents_init(uv_fs_event_t* handle) { + int err; + uv__cf_loop_state_t* state; + + err = uv__fsevents_loop_init(handle->loop); + if (err) + return err; + + /* Get absolute path to file */ + handle->realpath = realpath(handle->path, NULL); + if (handle->realpath == NULL) + return UV__ERR(errno); + handle->realpath_len = strlen(handle->realpath); + + /* Initialize event queue */ + QUEUE_INIT(&handle->cf_events); + handle->cf_error = 0; + + /* + * Events will occur in other thread. + * Initialize callback for getting them back into event loop's thread + */ + handle->cf_cb = uv__malloc(sizeof(*handle->cf_cb)); + if (handle->cf_cb == NULL) { + err = UV_ENOMEM; + goto fail_cf_cb_malloc; + } + + handle->cf_cb->data = handle; + uv_async_init(handle->loop, handle->cf_cb, uv__fsevents_cb); + handle->cf_cb->flags |= UV_HANDLE_INTERNAL; + uv_unref((uv_handle_t*) handle->cf_cb); + + err = uv_mutex_init(&handle->cf_mutex); + if (err) + goto fail_cf_mutex_init; + + /* Insert handle into the list */ + state = handle->loop->cf_state; + uv_mutex_lock(&state->fsevent_mutex); + QUEUE_INSERT_TAIL(&state->fsevent_handles, &handle->cf_member); + state->fsevent_handle_count++; + state->fsevent_need_reschedule = 1; + uv_mutex_unlock(&state->fsevent_mutex); + + /* Reschedule FSEventStream */ + assert(handle != NULL); + err = uv__cf_loop_signal(handle->loop, handle, kUVCFLoopSignalRegular); + if (err) + goto fail_loop_signal; + + return 0; + +fail_loop_signal: + uv_mutex_destroy(&handle->cf_mutex); + +fail_cf_mutex_init: + uv__free(handle->cf_cb); + handle->cf_cb = NULL; + +fail_cf_cb_malloc: + uv__free(handle->realpath); + handle->realpath = NULL; + handle->realpath_len = 0; + + return err; +} + + +/* Runs in UV loop to de-initialize handle */ +int uv__fsevents_close(uv_fs_event_t* handle) { + int err; + uv__cf_loop_state_t* state; + + if (handle->cf_cb == NULL) + return UV_EINVAL; + + /* Remove handle from the list */ + state = handle->loop->cf_state; + uv_mutex_lock(&state->fsevent_mutex); + QUEUE_REMOVE(&handle->cf_member); + state->fsevent_handle_count--; + state->fsevent_need_reschedule = 1; + uv_mutex_unlock(&state->fsevent_mutex); + + /* Reschedule FSEventStream */ + assert(handle != NULL); + err = uv__cf_loop_signal(handle->loop, handle, kUVCFLoopSignalClosing); + if (err) + return UV__ERR(err); + + /* Wait for deinitialization */ + uv_sem_wait(&state->fsevent_sem); + + uv_close((uv_handle_t*) handle->cf_cb, (uv_close_cb) uv__free); + handle->cf_cb = NULL; + + /* Free data in queue */ + UV__FSEVENTS_PROCESS(handle, { + /* NOP */ + }); + + uv_mutex_destroy(&handle->cf_mutex); + uv__free(handle->realpath); + handle->realpath = NULL; + handle->realpath_len = 0; + + return 0; +} + +#endif /* TARGET_OS_IPHONE */ diff --git a/include/libuv/src/unix/getaddrinfo.c b/include/libuv/src/unix/getaddrinfo.c new file mode 100644 index 000000000..d7ca7d1a4 --- /dev/null +++ b/include/libuv/src/unix/getaddrinfo.c @@ -0,0 +1,255 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* Expose glibc-specific EAI_* error codes. Needs to be defined before we + * include any headers. + */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#include "uv.h" +#include "internal.h" +#include "idna.h" + +#include +#include /* NULL */ +#include +#include +#include /* if_indextoname() */ + +/* EAI_* constants. */ +#include + + +int uv__getaddrinfo_translate_error(int sys_err) { + switch (sys_err) { + case 0: return 0; +#if defined(EAI_ADDRFAMILY) + case EAI_ADDRFAMILY: return UV_EAI_ADDRFAMILY; +#endif +#if defined(EAI_AGAIN) + case EAI_AGAIN: return UV_EAI_AGAIN; +#endif +#if defined(EAI_BADFLAGS) + case EAI_BADFLAGS: return UV_EAI_BADFLAGS; +#endif +#if defined(EAI_BADHINTS) + case EAI_BADHINTS: return UV_EAI_BADHINTS; +#endif +#if defined(EAI_CANCELED) + case EAI_CANCELED: return UV_EAI_CANCELED; +#endif +#if defined(EAI_FAIL) + case EAI_FAIL: return UV_EAI_FAIL; +#endif +#if defined(EAI_FAMILY) + case EAI_FAMILY: return UV_EAI_FAMILY; +#endif +#if defined(EAI_MEMORY) + case EAI_MEMORY: return UV_EAI_MEMORY; +#endif +#if defined(EAI_NODATA) + case EAI_NODATA: return UV_EAI_NODATA; +#endif +#if defined(EAI_NONAME) +# if !defined(EAI_NODATA) || EAI_NODATA != EAI_NONAME + case EAI_NONAME: return UV_EAI_NONAME; +# endif +#endif +#if defined(EAI_OVERFLOW) + case EAI_OVERFLOW: return UV_EAI_OVERFLOW; +#endif +#if defined(EAI_PROTOCOL) + case EAI_PROTOCOL: return UV_EAI_PROTOCOL; +#endif +#if defined(EAI_SERVICE) + case EAI_SERVICE: return UV_EAI_SERVICE; +#endif +#if defined(EAI_SOCKTYPE) + case EAI_SOCKTYPE: return UV_EAI_SOCKTYPE; +#endif +#if defined(EAI_SYSTEM) + case EAI_SYSTEM: return UV__ERR(errno); +#endif + } + assert(!"unknown EAI_* error code"); + abort(); +#ifndef __SUNPRO_C + return 0; /* Pacify compiler. */ +#endif +} + + +static void uv__getaddrinfo_work(struct uv__work* w) { + uv_getaddrinfo_t* req; + int err; + + req = container_of(w, uv_getaddrinfo_t, work_req); + err = getaddrinfo(req->hostname, req->service, req->hints, &req->addrinfo); + req->retcode = uv__getaddrinfo_translate_error(err); +} + + +static void uv__getaddrinfo_done(struct uv__work* w, int status) { + uv_getaddrinfo_t* req; + + req = container_of(w, uv_getaddrinfo_t, work_req); + uv__req_unregister(req->loop, req); + + /* See initialization in uv_getaddrinfo(). */ + if (req->hints) + uv__free(req->hints); + else if (req->service) + uv__free(req->service); + else if (req->hostname) + uv__free(req->hostname); + else + assert(0); + + req->hints = NULL; + req->service = NULL; + req->hostname = NULL; + + if (status == UV_ECANCELED) { + assert(req->retcode == 0); + req->retcode = UV_EAI_CANCELED; + } + + if (req->cb) + req->cb(req, req->retcode, req->addrinfo); +} + + +int uv_getaddrinfo(uv_loop_t* loop, + uv_getaddrinfo_t* req, + uv_getaddrinfo_cb cb, + const char* hostname, + const char* service, + const struct addrinfo* hints) { + char hostname_ascii[256]; + size_t hostname_len; + size_t service_len; + size_t hints_len; + size_t len; + char* buf; + long rc; + + if (req == NULL || (hostname == NULL && service == NULL)) + return UV_EINVAL; + + /* FIXME(bnoordhuis) IDNA does not seem to work z/OS, + * probably because it uses EBCDIC rather than ASCII. + */ +#ifdef __MVS__ + (void) &hostname_ascii; +#else + if (hostname != NULL) { + rc = uv__idna_toascii(hostname, + hostname + strlen(hostname), + hostname_ascii, + hostname_ascii + sizeof(hostname_ascii)); + if (rc < 0) + return rc; + hostname = hostname_ascii; + } +#endif + + hostname_len = hostname ? strlen(hostname) + 1 : 0; + service_len = service ? strlen(service) + 1 : 0; + hints_len = hints ? sizeof(*hints) : 0; + buf = uv__malloc(hostname_len + service_len + hints_len); + + if (buf == NULL) + return UV_ENOMEM; + + uv__req_init(loop, req, UV_GETADDRINFO); + req->loop = loop; + req->cb = cb; + req->addrinfo = NULL; + req->hints = NULL; + req->service = NULL; + req->hostname = NULL; + req->retcode = 0; + + /* order matters, see uv_getaddrinfo_done() */ + len = 0; + + if (hints) { + req->hints = memcpy(buf + len, hints, sizeof(*hints)); + len += sizeof(*hints); + } + + if (service) { + req->service = memcpy(buf + len, service, service_len); + len += service_len; + } + + if (hostname) + req->hostname = memcpy(buf + len, hostname, hostname_len); + + if (cb) { + uv__work_submit(loop, + &req->work_req, + UV__WORK_SLOW_IO, + uv__getaddrinfo_work, + uv__getaddrinfo_done); + return 0; + } else { + uv__getaddrinfo_work(&req->work_req); + uv__getaddrinfo_done(&req->work_req, 0); + return req->retcode; + } +} + + +void uv_freeaddrinfo(struct addrinfo* ai) { + if (ai) + freeaddrinfo(ai); +} + + +int uv_if_indextoname(unsigned int ifindex, char* buffer, size_t* size) { + char ifname_buf[UV_IF_NAMESIZE]; + size_t len; + + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + + if (if_indextoname(ifindex, ifname_buf) == NULL) + return UV__ERR(errno); + + len = strnlen(ifname_buf, sizeof(ifname_buf)); + + if (*size <= len) { + *size = len + 1; + return UV_ENOBUFS; + } + + memcpy(buffer, ifname_buf, len); + buffer[len] = '\0'; + *size = len; + + return 0; +} + +int uv_if_indextoiid(unsigned int ifindex, char* buffer, size_t* size) { + return uv_if_indextoname(ifindex, buffer, size); +} diff --git a/include/libuv/src/unix/getnameinfo.c b/include/libuv/src/unix/getnameinfo.c new file mode 100644 index 000000000..991002a67 --- /dev/null +++ b/include/libuv/src/unix/getnameinfo.c @@ -0,0 +1,121 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to +* deal in the Software without restriction, including without limitation the +* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +* sell copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +* IN THE SOFTWARE. +*/ + +#include +#include +#include +#include + +#include "uv.h" +#include "internal.h" + + +static void uv__getnameinfo_work(struct uv__work* w) { + uv_getnameinfo_t* req; + int err; + socklen_t salen; + + req = container_of(w, uv_getnameinfo_t, work_req); + + if (req->storage.ss_family == AF_INET) + salen = sizeof(struct sockaddr_in); + else if (req->storage.ss_family == AF_INET6) + salen = sizeof(struct sockaddr_in6); + else + abort(); + + err = getnameinfo((struct sockaddr*) &req->storage, + salen, + req->host, + sizeof(req->host), + req->service, + sizeof(req->service), + req->flags); + req->retcode = uv__getaddrinfo_translate_error(err); +} + +static void uv__getnameinfo_done(struct uv__work* w, int status) { + uv_getnameinfo_t* req; + char* host; + char* service; + + req = container_of(w, uv_getnameinfo_t, work_req); + uv__req_unregister(req->loop, req); + host = service = NULL; + + if (status == UV_ECANCELED) { + assert(req->retcode == 0); + req->retcode = UV_EAI_CANCELED; + } else if (req->retcode == 0) { + host = req->host; + service = req->service; + } + + if (req->getnameinfo_cb) + req->getnameinfo_cb(req, req->retcode, host, service); +} + +/* +* Entry point for getnameinfo +* return 0 if a callback will be made +* return error code if validation fails +*/ +int uv_getnameinfo(uv_loop_t* loop, + uv_getnameinfo_t* req, + uv_getnameinfo_cb getnameinfo_cb, + const struct sockaddr* addr, + int flags) { + if (req == NULL || addr == NULL) + return UV_EINVAL; + + if (addr->sa_family == AF_INET) { + memcpy(&req->storage, + addr, + sizeof(struct sockaddr_in)); + } else if (addr->sa_family == AF_INET6) { + memcpy(&req->storage, + addr, + sizeof(struct sockaddr_in6)); + } else { + return UV_EINVAL; + } + + uv__req_init(loop, (uv_req_t*)req, UV_GETNAMEINFO); + + req->getnameinfo_cb = getnameinfo_cb; + req->flags = flags; + req->type = UV_GETNAMEINFO; + req->loop = loop; + req->retcode = 0; + + if (getnameinfo_cb) { + uv__work_submit(loop, + &req->work_req, + UV__WORK_SLOW_IO, + uv__getnameinfo_work, + uv__getnameinfo_done); + return 0; + } else { + uv__getnameinfo_work(&req->work_req); + uv__getnameinfo_done(&req->work_req, 0); + return req->retcode; + } +} diff --git a/include/libuv/src/unix/haiku.c b/include/libuv/src/unix/haiku.c new file mode 100644 index 000000000..cf17d836b --- /dev/null +++ b/include/libuv/src/unix/haiku.c @@ -0,0 +1,167 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include /* find_path() */ +#include + + +void uv_loadavg(double avg[3]) { + avg[0] = 0; + avg[1] = 0; + avg[2] = 0; +} + + +int uv_exepath(char* buffer, size_t* size) { + char abspath[B_PATH_NAME_LENGTH]; + status_t status; + ssize_t abspath_len; + + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + + status = find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH, NULL, abspath, + sizeof(abspath)); + if (status != B_OK) + return UV__ERR(status); + + abspath_len = uv__strscpy(buffer, abspath, *size); + *size -= 1; + if (abspath_len >= 0 && *size > (size_t)abspath_len) + *size = (size_t)abspath_len; + + return 0; +} + + +uint64_t uv_get_free_memory(void) { + status_t status; + system_info sinfo; + + status = get_system_info(&sinfo); + if (status != B_OK) + return 0; + + return (sinfo.max_pages - sinfo.used_pages) * B_PAGE_SIZE; +} + + +uint64_t uv_get_total_memory(void) { + status_t status; + system_info sinfo; + + status = get_system_info(&sinfo); + if (status != B_OK) + return 0; + + return sinfo.max_pages * B_PAGE_SIZE; +} + + +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + +int uv_resident_set_memory(size_t* rss) { + area_info area; + ssize_t cookie; + status_t status; + thread_info thread; + + status = get_thread_info(find_thread(NULL), &thread); + if (status != B_OK) + return UV__ERR(status); + + cookie = 0; + *rss = 0; + while (get_next_area_info(thread.team, &cookie, &area) == B_OK) + *rss += area.ram_size; + + return 0; +} + + +int uv_uptime(double* uptime) { + /* system_time() returns time since booting in microseconds */ + *uptime = (double)system_time() / 1000000; + return 0; +} + + +int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + cpu_topology_node_info* topology_infos; + int i; + status_t status; + system_info system; + uint32_t topology_count; + uint64_t cpuspeed; + uv_cpu_info_t* cpu_info; + + if (cpu_infos == NULL || count == NULL) + return UV_EINVAL; + + status = get_cpu_topology_info(NULL, &topology_count); + if (status != B_OK) + return UV__ERR(status); + + topology_infos = uv__malloc(topology_count * sizeof(*topology_infos)); + if (topology_infos == NULL) + return UV_ENOMEM; + + status = get_cpu_topology_info(topology_infos, &topology_count); + if (status != B_OK) { + uv__free(topology_infos); + return UV__ERR(status); + } + + cpuspeed = 0; + for (i = 0; i < (int)topology_count; i++) { + if (topology_infos[i].type == B_TOPOLOGY_CORE) { + cpuspeed = topology_infos[i].data.core.default_frequency; + break; + } + } + + uv__free(topology_infos); + + status = get_system_info(&system); + if (status != B_OK) + return UV__ERR(status); + + *cpu_infos = uv__calloc(system.cpu_count, sizeof(**cpu_infos)); + if (*cpu_infos == NULL) + return UV_ENOMEM; + + /* CPU time and model are not exposed by Haiku. */ + cpu_info = *cpu_infos; + for (i = 0; i < (int)system.cpu_count; i++) { + cpu_info->model = uv__strdup("unknown"); + cpu_info->speed = (int)(cpuspeed / 1000000); + cpu_info++; + } + *count = system.cpu_count; + + return 0; +} diff --git a/include/libuv/src/unix/ibmi.c b/include/libuv/src/unix/ibmi.c new file mode 100644 index 000000000..96efc02ba --- /dev/null +++ b/include/libuv/src/unix/ibmi.c @@ -0,0 +1,501 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include + +char* original_exepath = NULL; +uv_mutex_t process_title_mutex; +uv_once_t process_title_mutex_once = UV_ONCE_INIT; + +typedef struct { + int bytes_available; + int bytes_returned; + char current_date_and_time[8]; + char system_name[8]; + char elapsed_time[6]; + char restricted_state_flag; + char reserved; + int percent_processing_unit_used; + int jobs_in_system; + int percent_permanent_addresses; + int percent_temporary_addresses; + int system_asp; + int percent_system_asp_used; + int total_auxiliary_storage; + int current_unprotected_storage_used; + int maximum_unprotected_storage_used; + int percent_db_capability; + int main_storage_size; + int number_of_partitions; + int partition_identifier; + int reserved1; + int current_processing_capacity; + char processor_sharing_attribute; + char reserved2[3]; + int number_of_processors; + int active_jobs_in_system; + int active_threads_in_system; + int maximum_jobs_in_system; + int percent_temporary_256mb_segments_used; + int percent_temporary_4gb_segments_used; + int percent_permanent_256mb_segments_used; + int percent_permanent_4gb_segments_used; + int percent_current_interactive_performance; + int percent_uncapped_cpu_capacity_used; + int percent_shared_processor_pool_used; + long main_storage_size_long; +} SSTS0200; + + +typedef struct { + char header[208]; + unsigned char loca_adapter_address[12]; +} LIND0500; + + +typedef struct { + int bytes_provided; + int bytes_available; + char msgid[7]; +} errcode_s; + + +static const unsigned char e2a[256] = { + 0, 1, 2, 3, 156, 9, 134, 127, 151, 141, 142, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 157, 133, 8, 135, 24, 25, 146, 143, 28, 29, 30, 31, + 128, 129, 130, 131, 132, 10, 23, 27, 136, 137, 138, 139, 140, 5, 6, 7, + 144, 145, 22, 147, 148, 149, 150, 4, 152, 153, 154, 155, 20, 21, 158, 26, + 32, 160, 161, 162, 163, 164, 165, 166, 167, 168, 91, 46, 60, 40, 43, 33, + 38, 169, 170, 171, 172, 173, 174, 175, 176, 177, 93, 36, 42, 41, 59, 94, + 45, 47, 178, 179, 180, 181, 182, 183, 184, 185, 124, 44, 37, 95, 62, 63, + 186, 187, 188, 189, 190, 191, 192, 193, 194, 96, 58, 35, 64, 39, 61, 34, + 195, 97, 98, 99, 100, 101, 102, 103, 104, 105, 196, 197, 198, 199, 200, 201, + 202, 106, 107, 108, 109, 110, 111, 112, 113, 114, 203, 204, 205, 206, 207, 208, + 209, 126, 115, 116, 117, 118, 119, 120, 121, 122, 210, 211, 212, 213, 214, 215, + 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, + 123, 65, 66, 67, 68, 69, 70, 71, 72, 73, 232, 233, 234, 235, 236, 237, + 125, 74, 75, 76, 77, 78, 79, 80, 81, 82, 238, 239, 240, 241, 242, 243, + 92, 159, 83, 84, 85, 86, 87, 88, 89, 90, 244, 245, 246, 247, 248, 249, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 250, 251, 252, 253, 254, 255}; + + +static const unsigned char a2e[256] = { + 0, 1, 2, 3, 55, 45, 46, 47, 22, 5, 37, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 60, 61, 50, 38, 24, 25, 63, 39, 28, 29, 30, 31, + 64, 79, 127, 123, 91, 108, 80, 125, 77, 93, 92, 78, 107, 96, 75, 97, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 122, 94, 76, 126, 110, 111, + 124, 193, 194, 195, 196, 197, 198, 199, 200, 201, 209, 210, 211, 212, 213, 214, + 215, 216, 217, 226, 227, 228, 229, 230, 231, 232, 233, 74, 224, 90, 95, 109, + 121, 129, 130, 131, 132, 133, 134, 135, 136, 137, 145, 146, 147, 148, 149, 150, + 151, 152, 153, 162, 163, 164, 165, 166, 167, 168, 169, 192, 106, 208, 161, 7, + 32, 33, 34, 35, 36, 21, 6, 23, 40, 41, 42, 43, 44, 9, 10, 27, + 48, 49, 26, 51, 52, 53, 54, 8, 56, 57, 58, 59, 4, 20, 62, 225, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 98, 99, 100, 101, 102, 103, 104, 105, 112, 113, 114, 115, 116, 117, + 118, 119, 120, 128, 138, 139, 140, 141, 142, 143, 144, 154, 155, 156, 157, 158, + 159, 160, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 186, 187, 188, 189, 190, 191, 202, 203, 204, 205, 206, 207, 218, 219, + 220, 221, 222, 223, 234, 235, 236, 237, 238, 239, 250, 251, 252, 253, 254, 255}; + + +static void iconv_e2a(unsigned char src[], unsigned char dst[], size_t length) { + size_t i; + for (i = 0; i < length; i++) + dst[i] = e2a[src[i]]; +} + + +static void iconv_a2e(const char* src, unsigned char dst[], size_t length) { + size_t srclen; + size_t i; + + srclen = strlen(src); + if (srclen > length) + abort(); + for (i = 0; i < srclen; i++) + dst[i] = a2e[src[i]]; + /* padding the remaining part with spaces */ + for (; i < length; i++) + dst[i] = a2e[' ']; +} + +void init_process_title_mutex_once(void) { + uv_mutex_init(&process_title_mutex); +} + +static int get_ibmi_system_status(SSTS0200* rcvr) { + /* rcvrlen is input parameter 2 to QWCRSSTS */ + unsigned int rcvrlen = sizeof(*rcvr); + unsigned char format[8], reset_status[10]; + + /* format is input parameter 3 to QWCRSSTS */ + iconv_a2e("SSTS0200", format, sizeof(format)); + /* reset_status is input parameter 4 */ + iconv_a2e("*NO", reset_status, sizeof(reset_status)); + + /* errcode is input parameter 5 to QWCRSSTS */ + errcode_s errcode; + + /* qwcrssts_pointer is the 16-byte tagged system pointer to QWCRSSTS */ + ILEpointer __attribute__((aligned(16))) qwcrssts_pointer; + + /* qwcrssts_argv is the array of argument pointers to QWCRSSTS */ + void* qwcrssts_argv[6]; + + /* Set the IBM i pointer to the QSYS/QWCRSSTS *PGM object */ + int rc = _RSLOBJ2(&qwcrssts_pointer, RSLOBJ_TS_PGM, "QWCRSSTS", "QSYS"); + + if (rc != 0) + return rc; + + /* initialize the QWCRSSTS returned info structure */ + memset(rcvr, 0, sizeof(*rcvr)); + + /* initialize the QWCRSSTS error code structure */ + memset(&errcode, 0, sizeof(errcode)); + errcode.bytes_provided = sizeof(errcode); + + /* initialize the array of argument pointers for the QWCRSSTS API */ + qwcrssts_argv[0] = rcvr; + qwcrssts_argv[1] = &rcvrlen; + qwcrssts_argv[2] = &format; + qwcrssts_argv[3] = &reset_status; + qwcrssts_argv[4] = &errcode; + qwcrssts_argv[5] = NULL; + + /* Call the IBM i QWCRSSTS API from PASE */ + rc = _PGMCALL(&qwcrssts_pointer, qwcrssts_argv, 0); + + return rc; +} + + +uint64_t uv_get_free_memory(void) { + SSTS0200 rcvr; + + if (get_ibmi_system_status(&rcvr)) + return 0; + + return (uint64_t)rcvr.main_storage_size * 1024ULL; +} + + +uint64_t uv_get_total_memory(void) { + SSTS0200 rcvr; + + if (get_ibmi_system_status(&rcvr)) + return 0; + + return (uint64_t)rcvr.main_storage_size * 1024ULL; +} + + +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + +void uv_loadavg(double avg[3]) { + SSTS0200 rcvr; + + if (get_ibmi_system_status(&rcvr)) { + avg[0] = avg[1] = avg[2] = 0; + return; + } + + /* The average (in tenths) of the elapsed time during which the processing + * units were in use. For example, a value of 411 in binary would be 41.1%. + * This percentage could be greater than 100% for an uncapped partition. + */ + double processing_unit_used_percent = + rcvr.percent_processing_unit_used / 1000.0; + + avg[0] = avg[1] = avg[2] = processing_unit_used_percent; +} + + +int uv_resident_set_memory(size_t* rss) { + *rss = 0; + return 0; +} + + +int uv_uptime(double* uptime) { + return UV_ENOSYS; +} + + +int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + unsigned int numcpus, idx = 0; + uv_cpu_info_t* cpu_info; + + *cpu_infos = NULL; + *count = 0; + + numcpus = sysconf(_SC_NPROCESSORS_ONLN); + + *cpu_infos = uv__malloc(numcpus * sizeof(uv_cpu_info_t)); + if (!*cpu_infos) { + return UV_ENOMEM; + } + + cpu_info = *cpu_infos; + for (idx = 0; idx < numcpus; idx++) { + cpu_info->speed = 0; + cpu_info->model = uv__strdup("unknown"); + cpu_info->cpu_times.user = 0; + cpu_info->cpu_times.sys = 0; + cpu_info->cpu_times.idle = 0; + cpu_info->cpu_times.irq = 0; + cpu_info->cpu_times.nice = 0; + cpu_info++; + } + *count = numcpus; + + return 0; +} + + +static int get_ibmi_physical_address(const char* line, char (*phys_addr)[6]) { + LIND0500 rcvr; + /* rcvrlen is input parameter 2 to QDCRLIND */ + unsigned int rcvrlen = sizeof(rcvr); + unsigned char format[8], line_name[10]; + unsigned char mac_addr[sizeof(rcvr.loca_adapter_address)]; + int c[6]; + + /* format is input parameter 3 to QDCRLIND */ + iconv_a2e("LIND0500", format, sizeof(format)); + + /* line_name is input parameter 4 to QDCRLIND */ + iconv_a2e(line, line_name, sizeof(line_name)); + + /* err is input parameter 5 to QDCRLIND */ + errcode_s err; + + /* qwcrssts_pointer is the 16-byte tagged system pointer to QDCRLIND */ + ILEpointer __attribute__((aligned(16))) qdcrlind_pointer; + + /* qwcrssts_argv is the array of argument pointers to QDCRLIND */ + void* qdcrlind_argv[6]; + + /* Set the IBM i pointer to the QSYS/QDCRLIND *PGM object */ + int rc = _RSLOBJ2(&qdcrlind_pointer, RSLOBJ_TS_PGM, "QDCRLIND", "QSYS"); + + if (rc != 0) + return rc; + + /* initialize the QDCRLIND returned info structure */ + memset(&rcvr, 0, sizeof(rcvr)); + + /* initialize the QDCRLIND error code structure */ + memset(&err, 0, sizeof(err)); + err.bytes_provided = sizeof(err); + + /* initialize the array of argument pointers for the QDCRLIND API */ + qdcrlind_argv[0] = &rcvr; + qdcrlind_argv[1] = &rcvrlen; + qdcrlind_argv[2] = &format; + qdcrlind_argv[3] = &line_name; + qdcrlind_argv[4] = &err; + qdcrlind_argv[5] = NULL; + + /* Call the IBM i QDCRLIND API from PASE */ + rc = _PGMCALL(&qdcrlind_pointer, qdcrlind_argv, 0); + if (rc != 0) + return rc; + + /* convert ebcdic loca_adapter_address to ascii first */ + iconv_e2a(rcvr.loca_adapter_address, mac_addr, + sizeof(rcvr.loca_adapter_address)); + + /* convert loca_adapter_address(char[12]) to phys_addr(char[6]) */ + int r = sscanf(mac_addr, "%02x%02x%02x%02x%02x%02x", + &c[0], &c[1], &c[2], &c[3], &c[4], &c[5]); + + if (r == ARRAY_SIZE(c)) { + (*phys_addr)[0] = c[0]; + (*phys_addr)[1] = c[1]; + (*phys_addr)[2] = c[2]; + (*phys_addr)[3] = c[3]; + (*phys_addr)[4] = c[4]; + (*phys_addr)[5] = c[5]; + } else { + memset(*phys_addr, 0, sizeof(*phys_addr)); + rc = -1; + } + return rc; +} + + +int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { + uv_interface_address_t* address; + struct ifaddrs_pase *ifap = NULL, *cur; + int inet6, r = 0; + + *count = 0; + *addresses = NULL; + + if (Qp2getifaddrs(&ifap)) + return UV_ENOSYS; + + /* The first loop to get the size of the array to be allocated */ + for (cur = ifap; cur; cur = cur->ifa_next) { + if (!(cur->ifa_addr->sa_family == AF_INET6 || + cur->ifa_addr->sa_family == AF_INET)) + continue; + + if (!(cur->ifa_flags & IFF_UP && cur->ifa_flags & IFF_RUNNING)) + continue; + + (*count)++; + } + + if (*count == 0) { + Qp2freeifaddrs(ifap); + return 0; + } + + /* Alloc the return interface structs */ + *addresses = uv__calloc(*count, sizeof(**addresses)); + if (*addresses == NULL) { + Qp2freeifaddrs(ifap); + return UV_ENOMEM; + } + address = *addresses; + + /* The second loop to fill in the array */ + for (cur = ifap; cur; cur = cur->ifa_next) { + if (!(cur->ifa_addr->sa_family == AF_INET6 || + cur->ifa_addr->sa_family == AF_INET)) + continue; + + if (!(cur->ifa_flags & IFF_UP && cur->ifa_flags & IFF_RUNNING)) + continue; + + address->name = uv__strdup(cur->ifa_name); + + inet6 = (cur->ifa_addr->sa_family == AF_INET6); + + if (inet6) { + address->address.address6 = *((struct sockaddr_in6*)cur->ifa_addr); + address->netmask.netmask6 = *((struct sockaddr_in6*)cur->ifa_netmask); + address->netmask.netmask6.sin6_family = AF_INET6; + } else { + address->address.address4 = *((struct sockaddr_in*)cur->ifa_addr); + address->netmask.netmask4 = *((struct sockaddr_in*)cur->ifa_netmask); + address->netmask.netmask4.sin_family = AF_INET; + } + address->is_internal = cur->ifa_flags & IFF_LOOPBACK ? 1 : 0; + if (!address->is_internal) { + int rc = get_ibmi_physical_address(address->name, &address->phys_addr); + if (rc != 0) + r = rc; + } + + address++; + } + + Qp2freeifaddrs(ifap); + return r; +} + + +void uv_free_interface_addresses(uv_interface_address_t* addresses, int count) { + int i; + + for (i = 0; i < count; ++i) { + uv__free(addresses[i].name); + } + + uv__free(addresses); +} + +char** uv_setup_args(int argc, char** argv) { + char exepath[UV__PATH_MAX]; + char* s; + size_t size; + + if (argc > 0) { + /* Use argv[0] to determine value for uv_exepath(). */ + size = sizeof(exepath); + if (uv__search_path(argv[0], exepath, &size) == 0) { + uv_once(&process_title_mutex_once, init_process_title_mutex_once); + uv_mutex_lock(&process_title_mutex); + original_exepath = uv__strdup(exepath); + uv_mutex_unlock(&process_title_mutex); + } + } + + return argv; +} + +int uv_set_process_title(const char* title) { + return 0; +} + +int uv_get_process_title(char* buffer, size_t size) { + if (buffer == NULL || size == 0) + return UV_EINVAL; + + buffer[0] = '\0'; + return 0; +} + +void uv__process_title_cleanup(void) { +} \ No newline at end of file diff --git a/include/libuv/src/unix/internal.h b/include/libuv/src/unix/internal.h new file mode 100644 index 000000000..570274ed6 --- /dev/null +++ b/include/libuv/src/unix/internal.h @@ -0,0 +1,344 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef UV_UNIX_INTERNAL_H_ +#define UV_UNIX_INTERNAL_H_ + +#include "uv-common.h" + +#include +#include /* _POSIX_PATH_MAX, PATH_MAX */ +#include /* abort */ +#include /* strrchr */ +#include /* O_CLOEXEC and O_NONBLOCK, if supported. */ +#include +#include +#include + +#if defined(__STRICT_ANSI__) +# define inline __inline +#endif + +#if defined(__linux__) +# include "linux-syscalls.h" +#endif /* __linux__ */ + +#if defined(__MVS__) +# include "os390-syscalls.h" +#endif /* __MVS__ */ + +#if defined(__sun) +# include +# include +#endif /* __sun */ + +#if defined(_AIX) +# define reqevents events +# define rtnevents revents +# include +#else +# include +#endif /* _AIX */ + +#if defined(__APPLE__) && !TARGET_OS_IPHONE +# include +#endif + +#if defined(PATH_MAX) +# define UV__PATH_MAX PATH_MAX +#else +# define UV__PATH_MAX 8192 +#endif + +#if defined(__ANDROID__) +int uv__pthread_sigmask(int how, const sigset_t* set, sigset_t* oset); +# ifdef pthread_sigmask +# undef pthread_sigmask +# endif +# define pthread_sigmask(how, set, oldset) uv__pthread_sigmask(how, set, oldset) +#endif + +#define ACCESS_ONCE(type, var) \ + (*(volatile type*) &(var)) + +#define ROUND_UP(a, b) \ + ((a) % (b) ? ((a) + (b)) - ((a) % (b)) : (a)) + +#define UNREACHABLE() \ + do { \ + assert(0 && "unreachable code"); \ + abort(); \ + } \ + while (0) + +#define SAVE_ERRNO(block) \ + do { \ + int _saved_errno = errno; \ + do { block; } while (0); \ + errno = _saved_errno; \ + } \ + while (0) + +/* The __clang__ and __INTEL_COMPILER checks are superfluous because they + * define __GNUC__. They are here to convey to you, dear reader, that these + * macros are enabled when compiling with clang or icc. + */ +#if defined(__clang__) || \ + defined(__GNUC__) || \ + defined(__INTEL_COMPILER) +# define UV_UNUSED(declaration) __attribute__((unused)) declaration +#else +# define UV_UNUSED(declaration) declaration +#endif + +/* Leans on the fact that, on Linux, POLLRDHUP == EPOLLRDHUP. */ +#ifdef POLLRDHUP +# define UV__POLLRDHUP POLLRDHUP +#else +# define UV__POLLRDHUP 0x2000 +#endif + +#ifdef POLLPRI +# define UV__POLLPRI POLLPRI +#else +# define UV__POLLPRI 0 +#endif + +#if !defined(O_CLOEXEC) && defined(__FreeBSD__) +/* + * It may be that we are just missing `__POSIX_VISIBLE >= 200809`. + * Try using fixed value const and give up, if it doesn't work + */ +# define O_CLOEXEC 0x00100000 +#endif + +typedef struct uv__stream_queued_fds_s uv__stream_queued_fds_t; + +/* loop flags */ +enum { + UV_LOOP_BLOCK_SIGPROF = 1 +}; + +/* flags of excluding ifaddr */ +enum { + UV__EXCLUDE_IFPHYS, + UV__EXCLUDE_IFADDR +}; + +typedef enum { + UV_CLOCK_PRECISE = 0, /* Use the highest resolution clock available. */ + UV_CLOCK_FAST = 1 /* Use the fastest clock with <= 1ms granularity. */ +} uv_clocktype_t; + +struct uv__stream_queued_fds_s { + unsigned int size; + unsigned int offset; + int fds[1]; +}; + + +#if defined(_AIX) || \ + defined(__APPLE__) || \ + defined(__DragonFly__) || \ + defined(__FreeBSD__) || \ + defined(__FreeBSD_kernel__) || \ + defined(__linux__) || \ + defined(__OpenBSD__) || \ + defined(__NetBSD__) +#define uv__cloexec uv__cloexec_ioctl +#define uv__nonblock uv__nonblock_ioctl +#else +#define uv__cloexec uv__cloexec_fcntl +#define uv__nonblock uv__nonblock_fcntl +#endif + +/* On Linux, uv__nonblock_fcntl() and uv__nonblock_ioctl() do not commute + * when O_NDELAY is not equal to O_NONBLOCK. Case in point: linux/sparc32 + * and linux/sparc64, where O_NDELAY is O_NONBLOCK + another bit. + * + * Libuv uses uv__nonblock_fcntl() directly sometimes so ensure that it + * commutes with uv__nonblock(). + */ +#if defined(__linux__) && O_NDELAY != O_NONBLOCK +#undef uv__nonblock +#define uv__nonblock uv__nonblock_fcntl +#endif + +/* core */ +int uv__cloexec_ioctl(int fd, int set); +int uv__cloexec_fcntl(int fd, int set); +int uv__nonblock_ioctl(int fd, int set); +int uv__nonblock_fcntl(int fd, int set); +int uv__close(int fd); /* preserves errno */ +int uv__close_nocheckstdio(int fd); +int uv__close_nocancel(int fd); +int uv__socket(int domain, int type, int protocol); +ssize_t uv__recvmsg(int fd, struct msghdr *msg, int flags); +void uv__make_close_pending(uv_handle_t* handle); +int uv__getiovmax(void); + +void uv__io_init(uv__io_t* w, uv__io_cb cb, int fd); +void uv__io_start(uv_loop_t* loop, uv__io_t* w, unsigned int events); +void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events); +void uv__io_close(uv_loop_t* loop, uv__io_t* w); +void uv__io_feed(uv_loop_t* loop, uv__io_t* w); +int uv__io_active(const uv__io_t* w, unsigned int events); +int uv__io_check_fd(uv_loop_t* loop, int fd); +void uv__io_poll(uv_loop_t* loop, int timeout); /* in milliseconds or -1 */ +int uv__io_fork(uv_loop_t* loop); +int uv__fd_exists(uv_loop_t* loop, int fd); + +/* async */ +void uv__async_stop(uv_loop_t* loop); +int uv__async_fork(uv_loop_t* loop); + + +/* loop */ +void uv__run_idle(uv_loop_t* loop); +void uv__run_check(uv_loop_t* loop); +void uv__run_prepare(uv_loop_t* loop); + +/* stream */ +void uv__stream_init(uv_loop_t* loop, uv_stream_t* stream, + uv_handle_type type); +int uv__stream_open(uv_stream_t*, int fd, int flags); +void uv__stream_destroy(uv_stream_t* stream); +#if defined(__APPLE__) +int uv__stream_try_select(uv_stream_t* stream, int* fd); +#endif /* defined(__APPLE__) */ +void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events); +int uv__accept(int sockfd); +int uv__dup2_cloexec(int oldfd, int newfd); +int uv__open_cloexec(const char* path, int flags); + +/* tcp */ +int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb); +int uv__tcp_nodelay(int fd, int on); +int uv__tcp_keepalive(int fd, int on, unsigned int delay); + +/* pipe */ +int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb); + +/* signal */ +void uv__signal_close(uv_signal_t* handle); +void uv__signal_global_once_init(void); +void uv__signal_loop_cleanup(uv_loop_t* loop); +int uv__signal_loop_fork(uv_loop_t* loop); + +/* platform specific */ +uint64_t uv__hrtime(uv_clocktype_t type); +int uv__kqueue_init(uv_loop_t* loop); +int uv__platform_loop_init(uv_loop_t* loop); +void uv__platform_loop_delete(uv_loop_t* loop); +void uv__platform_invalidate_fd(uv_loop_t* loop, int fd); + +/* various */ +void uv__async_close(uv_async_t* handle); +void uv__check_close(uv_check_t* handle); +void uv__fs_event_close(uv_fs_event_t* handle); +void uv__idle_close(uv_idle_t* handle); +void uv__pipe_close(uv_pipe_t* handle); +void uv__poll_close(uv_poll_t* handle); +void uv__prepare_close(uv_prepare_t* handle); +void uv__process_close(uv_process_t* handle); +void uv__stream_close(uv_stream_t* handle); +void uv__tcp_close(uv_tcp_t* handle); +void uv__udp_close(uv_udp_t* handle); +void uv__udp_finish_close(uv_udp_t* handle); +uv_handle_type uv__handle_type(int fd); +FILE* uv__open_file(const char* path); +int uv__getpwuid_r(uv_passwd_t* pwd); +int uv__search_path(const char* prog, char* buf, size_t* buflen); + +/* random */ +int uv__random_devurandom(void* buf, size_t buflen); +int uv__random_getrandom(void* buf, size_t buflen); +int uv__random_getentropy(void* buf, size_t buflen); +int uv__random_readpath(const char* path, void* buf, size_t buflen); +int uv__random_sysctl(void* buf, size_t buflen); + +#if defined(__APPLE__) +int uv___stream_fd(const uv_stream_t* handle); +#define uv__stream_fd(handle) (uv___stream_fd((const uv_stream_t*) (handle))) +#else +#define uv__stream_fd(handle) ((handle)->io_watcher.fd) +#endif /* defined(__APPLE__) */ + +#ifdef O_NONBLOCK +# define UV__F_NONBLOCK O_NONBLOCK +#else +# define UV__F_NONBLOCK 1 +#endif + +int uv__make_pipe(int fds[2], int flags); + +#if defined(__APPLE__) + +int uv__fsevents_init(uv_fs_event_t* handle); +int uv__fsevents_close(uv_fs_event_t* handle); +void uv__fsevents_loop_delete(uv_loop_t* loop); + +#endif /* defined(__APPLE__) */ + +UV_UNUSED(static void uv__update_time(uv_loop_t* loop)) { + /* Use a fast time source if available. We only need millisecond precision. + */ + loop->time = uv__hrtime(UV_CLOCK_FAST) / 1000000; +} + +UV_UNUSED(static char* uv__basename_r(const char* path)) { + char* s; + + s = strrchr(path, '/'); + if (s == NULL) + return (char*) path; + + return s + 1; +} + +#if defined(__linux__) +int uv__inotify_fork(uv_loop_t* loop, void* old_watchers); +#endif + +typedef int (*uv__peersockfunc)(int, struct sockaddr*, socklen_t*); + +int uv__getsockpeername(const uv_handle_t* handle, + uv__peersockfunc func, + struct sockaddr* name, + int* namelen); + +#if defined(__linux__) || \ + defined(__FreeBSD__) || \ + defined(__FreeBSD_kernel__) +#define HAVE_MMSG 1 +struct uv__mmsghdr { + struct msghdr msg_hdr; + unsigned int msg_len; +}; + +int uv__recvmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen); +int uv__sendmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen); +#else +#define HAVE_MMSG 0 +#endif + + +#endif /* UV_UNIX_INTERNAL_H_ */ diff --git a/include/libuv/src/unix/kqueue.c b/include/libuv/src/unix/kqueue.c new file mode 100644 index 000000000..bf183d5fd --- /dev/null +++ b/include/libuv/src/unix/kqueue.c @@ -0,0 +1,585 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* + * Required on + * - Until at least FreeBSD 11.0 + * - Older versions of Mac OS X + * + * http://www.boost.org/doc/libs/1_61_0/boost/asio/detail/kqueue_reactor.hpp + */ +#ifndef EV_OOBAND +#define EV_OOBAND EV_FLAG1 +#endif + +static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags); + + +int uv__kqueue_init(uv_loop_t* loop) { + loop->backend_fd = kqueue(); + if (loop->backend_fd == -1) + return UV__ERR(errno); + + uv__cloexec(loop->backend_fd, 1); + + return 0; +} + + +#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 +static int uv__has_forked_with_cfrunloop; +#endif + +int uv__io_fork(uv_loop_t* loop) { + int err; + loop->backend_fd = -1; + err = uv__kqueue_init(loop); + if (err) + return err; + +#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + if (loop->cf_state != NULL) { + /* We cannot start another CFRunloop and/or thread in the child + process; CF aborts if you try or if you try to touch the thread + at all to kill it. So the best we can do is ignore it from now + on. This means we can't watch directories in the same way + anymore (like other BSDs). It also means we cannot properly + clean up the allocated resources; calling + uv__fsevents_loop_delete from uv_loop_close will crash the + process. So we sidestep the issue by pretending like we never + started it in the first place. + */ + uv__store_relaxed(&uv__has_forked_with_cfrunloop, 1); + uv__free(loop->cf_state); + loop->cf_state = NULL; + } +#endif /* #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */ + return err; +} + + +int uv__io_check_fd(uv_loop_t* loop, int fd) { + struct kevent ev; + int rc; + + rc = 0; + EV_SET(&ev, fd, EVFILT_READ, EV_ADD, 0, 0, 0); + if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL)) + rc = UV__ERR(errno); + + EV_SET(&ev, fd, EVFILT_READ, EV_DELETE, 0, 0, 0); + if (rc == 0) + if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL)) + abort(); + + return rc; +} + + +void uv__io_poll(uv_loop_t* loop, int timeout) { + struct kevent events[1024]; + struct kevent* ev; + struct timespec spec; + unsigned int nevents; + unsigned int revents; + QUEUE* q; + uv__io_t* w; + sigset_t* pset; + sigset_t set; + uint64_t base; + uint64_t diff; + int have_signals; + int filter; + int fflags; + int count; + int nfds; + int fd; + int op; + int i; + int user_timeout; + int reset_timeout; + + if (loop->nfds == 0) { + assert(QUEUE_EMPTY(&loop->watcher_queue)); + return; + } + + nevents = 0; + + while (!QUEUE_EMPTY(&loop->watcher_queue)) { + q = QUEUE_HEAD(&loop->watcher_queue); + QUEUE_REMOVE(q); + QUEUE_INIT(q); + + w = QUEUE_DATA(q, uv__io_t, watcher_queue); + assert(w->pevents != 0); + assert(w->fd >= 0); + assert(w->fd < (int) loop->nwatchers); + + if ((w->events & POLLIN) == 0 && (w->pevents & POLLIN) != 0) { + filter = EVFILT_READ; + fflags = 0; + op = EV_ADD; + + if (w->cb == uv__fs_event) { + filter = EVFILT_VNODE; + fflags = NOTE_ATTRIB | NOTE_WRITE | NOTE_RENAME + | NOTE_DELETE | NOTE_EXTEND | NOTE_REVOKE; + op = EV_ADD | EV_ONESHOT; /* Stop the event from firing repeatedly. */ + } + + EV_SET(events + nevents, w->fd, filter, op, fflags, 0, 0); + + if (++nevents == ARRAY_SIZE(events)) { + if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL)) + abort(); + nevents = 0; + } + } + + if ((w->events & POLLOUT) == 0 && (w->pevents & POLLOUT) != 0) { + EV_SET(events + nevents, w->fd, EVFILT_WRITE, EV_ADD, 0, 0, 0); + + if (++nevents == ARRAY_SIZE(events)) { + if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL)) + abort(); + nevents = 0; + } + } + + if ((w->events & UV__POLLPRI) == 0 && (w->pevents & UV__POLLPRI) != 0) { + EV_SET(events + nevents, w->fd, EV_OOBAND, EV_ADD, 0, 0, 0); + + if (++nevents == ARRAY_SIZE(events)) { + if (kevent(loop->backend_fd, events, nevents, NULL, 0, NULL)) + abort(); + nevents = 0; + } + } + + w->events = w->pevents; + } + + pset = NULL; + if (loop->flags & UV_LOOP_BLOCK_SIGPROF) { + pset = &set; + sigemptyset(pset); + sigaddset(pset, SIGPROF); + } + + assert(timeout >= -1); + base = loop->time; + count = 48; /* Benchmarks suggest this gives the best throughput. */ + + if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { + reset_timeout = 1; + user_timeout = timeout; + timeout = 0; + } else { + reset_timeout = 0; + } + + for (;; nevents = 0) { + /* Only need to set the provider_entry_time if timeout != 0. The function + * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. + */ + if (timeout != 0) + uv__metrics_set_provider_entry_time(loop); + + if (timeout != -1) { + spec.tv_sec = timeout / 1000; + spec.tv_nsec = (timeout % 1000) * 1000000; + } + + if (pset != NULL) + pthread_sigmask(SIG_BLOCK, pset, NULL); + + nfds = kevent(loop->backend_fd, + events, + nevents, + events, + ARRAY_SIZE(events), + timeout == -1 ? NULL : &spec); + + if (pset != NULL) + pthread_sigmask(SIG_UNBLOCK, pset, NULL); + + /* Update loop->time unconditionally. It's tempting to skip the update when + * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the + * operating system didn't reschedule our process while in the syscall. + */ + SAVE_ERRNO(uv__update_time(loop)); + + if (nfds == 0) { + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + if (timeout == -1) + continue; + if (timeout > 0) + goto update_timeout; + } + + assert(timeout != -1); + return; + } + + if (nfds == -1) { + if (errno != EINTR) + abort(); + + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + + if (timeout == 0) + return; + + if (timeout == -1) + continue; + + /* Interrupted by a signal. Update timeout and poll again. */ + goto update_timeout; + } + + have_signals = 0; + nevents = 0; + + assert(loop->watchers != NULL); + loop->watchers[loop->nwatchers] = (void*) events; + loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds; + for (i = 0; i < nfds; i++) { + ev = events + i; + fd = ev->ident; + /* Skip invalidated events, see uv__platform_invalidate_fd */ + if (fd == -1) + continue; + w = loop->watchers[fd]; + + if (w == NULL) { + /* File descriptor that we've stopped watching, disarm it. + * TODO: batch up. */ + struct kevent events[1]; + + EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0); + if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL)) + if (errno != EBADF && errno != ENOENT) + abort(); + + continue; + } + + if (ev->filter == EVFILT_VNODE) { + assert(w->events == POLLIN); + assert(w->pevents == POLLIN); + uv__metrics_update_idle_time(loop); + w->cb(loop, w, ev->fflags); /* XXX always uv__fs_event() */ + nevents++; + continue; + } + + revents = 0; + + if (ev->filter == EVFILT_READ) { + if (w->pevents & POLLIN) { + revents |= POLLIN; + w->rcount = ev->data; + } else { + /* TODO batch up */ + struct kevent events[1]; + EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0); + if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL)) + if (errno != ENOENT) + abort(); + } + } + + if (ev->filter == EV_OOBAND) { + if (w->pevents & UV__POLLPRI) { + revents |= UV__POLLPRI; + w->rcount = ev->data; + } else { + /* TODO batch up */ + struct kevent events[1]; + EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0); + if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL)) + if (errno != ENOENT) + abort(); + } + } + + if (ev->filter == EVFILT_WRITE) { + if (w->pevents & POLLOUT) { + revents |= POLLOUT; + w->wcount = ev->data; + } else { + /* TODO batch up */ + struct kevent events[1]; + EV_SET(events + 0, fd, ev->filter, EV_DELETE, 0, 0, 0); + if (kevent(loop->backend_fd, events, 1, NULL, 0, NULL)) + if (errno != ENOENT) + abort(); + } + } + + if (ev->flags & EV_ERROR) + revents |= POLLERR; + + if ((ev->flags & EV_EOF) && (w->pevents & UV__POLLRDHUP)) + revents |= UV__POLLRDHUP; + + if (revents == 0) + continue; + + /* Run signal watchers last. This also affects child process watchers + * because those are implemented in terms of signal watchers. + */ + if (w == &loop->signal_io_watcher) { + have_signals = 1; + } else { + uv__metrics_update_idle_time(loop); + w->cb(loop, w, revents); + } + + nevents++; + } + + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + + if (have_signals != 0) { + uv__metrics_update_idle_time(loop); + loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN); + } + + loop->watchers[loop->nwatchers] = NULL; + loop->watchers[loop->nwatchers + 1] = NULL; + + if (have_signals != 0) + return; /* Event loop should cycle now so don't poll again. */ + + if (nevents != 0) { + if (nfds == ARRAY_SIZE(events) && --count != 0) { + /* Poll for more events but don't block this time. */ + timeout = 0; + continue; + } + return; + } + + if (timeout == 0) + return; + + if (timeout == -1) + continue; + +update_timeout: + assert(timeout > 0); + + diff = loop->time - base; + if (diff >= (uint64_t) timeout) + return; + + timeout -= diff; + } +} + + +void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { + struct kevent* events; + uintptr_t i; + uintptr_t nfds; + + assert(loop->watchers != NULL); + assert(fd >= 0); + + events = (struct kevent*) loop->watchers[loop->nwatchers]; + nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; + if (events == NULL) + return; + + /* Invalidate events with same file descriptor */ + for (i = 0; i < nfds; i++) + if ((int) events[i].ident == fd) + events[i].ident = -1; +} + + +static void uv__fs_event(uv_loop_t* loop, uv__io_t* w, unsigned int fflags) { + uv_fs_event_t* handle; + struct kevent ev; + int events; + const char* path; +#if defined(F_GETPATH) + /* MAXPATHLEN == PATH_MAX but the former is what XNU calls it internally. */ + char pathbuf[MAXPATHLEN]; +#endif + + handle = container_of(w, uv_fs_event_t, event_watcher); + + if (fflags & (NOTE_ATTRIB | NOTE_EXTEND)) + events = UV_CHANGE; + else + events = UV_RENAME; + + path = NULL; +#if defined(F_GETPATH) + /* Also works when the file has been unlinked from the file system. Passing + * in the path when the file has been deleted is arguably a little strange + * but it's consistent with what the inotify backend does. + */ + if (fcntl(handle->event_watcher.fd, F_GETPATH, pathbuf) == 0) + path = uv__basename_r(pathbuf); +#endif + handle->cb(handle, path, events, 0); + + if (handle->event_watcher.fd == -1) + return; + + /* Watcher operates in one-shot mode, re-arm it. */ + fflags = NOTE_ATTRIB | NOTE_WRITE | NOTE_RENAME + | NOTE_DELETE | NOTE_EXTEND | NOTE_REVOKE; + + EV_SET(&ev, w->fd, EVFILT_VNODE, EV_ADD | EV_ONESHOT, fflags, 0, 0); + + if (kevent(loop->backend_fd, &ev, 1, NULL, 0, NULL)) + abort(); +} + + +int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { + uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT); + return 0; +} + + +int uv_fs_event_start(uv_fs_event_t* handle, + uv_fs_event_cb cb, + const char* path, + unsigned int flags) { + int fd; +#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + struct stat statbuf; +#endif + + if (uv__is_active(handle)) + return UV_EINVAL; + + handle->cb = cb; + handle->path = uv__strdup(path); + if (handle->path == NULL) + return UV_ENOMEM; + + /* TODO open asynchronously - but how do we report back errors? */ + fd = open(handle->path, O_RDONLY); + if (fd == -1) { + uv__free(handle->path); + handle->path = NULL; + return UV__ERR(errno); + } + +#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + /* Nullify field to perform checks later */ + handle->cf_cb = NULL; + handle->realpath = NULL; + handle->realpath_len = 0; + handle->cf_flags = flags; + + if (fstat(fd, &statbuf)) + goto fallback; + /* FSEvents works only with directories */ + if (!(statbuf.st_mode & S_IFDIR)) + goto fallback; + + if (0 == uv__load_relaxed(&uv__has_forked_with_cfrunloop)) { + int r; + /* The fallback fd is no longer needed */ + uv__close_nocheckstdio(fd); + handle->event_watcher.fd = -1; + r = uv__fsevents_init(handle); + if (r == 0) { + uv__handle_start(handle); + } else { + uv__free(handle->path); + handle->path = NULL; + } + return r; + } +fallback: +#endif /* #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */ + + uv__handle_start(handle); + uv__io_init(&handle->event_watcher, uv__fs_event, fd); + uv__io_start(handle->loop, &handle->event_watcher, POLLIN); + + return 0; +} + + +int uv_fs_event_stop(uv_fs_event_t* handle) { + int r; + r = 0; + + if (!uv__is_active(handle)) + return 0; + + uv__handle_stop(handle); + +#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + if (0 == uv__load_relaxed(&uv__has_forked_with_cfrunloop)) + if (handle->cf_cb != NULL) + r = uv__fsevents_close(handle); +#endif + + if (handle->event_watcher.fd != -1) { + uv__io_close(handle->loop, &handle->event_watcher); + uv__close(handle->event_watcher.fd); + handle->event_watcher.fd = -1; + } + + uv__free(handle->path); + handle->path = NULL; + + return r; +} + + +void uv__fs_event_close(uv_fs_event_t* handle) { + uv_fs_event_stop(handle); +} diff --git a/include/libuv/src/unix/linux-core.c b/include/libuv/src/unix/linux-core.c new file mode 100644 index 000000000..4db2f0505 --- /dev/null +++ b/include/libuv/src/unix/linux-core.c @@ -0,0 +1,1146 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +/* We lean on the fact that POLL{IN,OUT,ERR,HUP} correspond with their + * EPOLL* counterparts. We use the POLL* variants in this file because that + * is what libuv uses elsewhere. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define HAVE_IFADDRS_H 1 + +#ifdef __UCLIBC__ +# if __UCLIBC_MAJOR__ < 0 && __UCLIBC_MINOR__ < 9 && __UCLIBC_SUBLEVEL__ < 32 +# undef HAVE_IFADDRS_H +# endif +#endif + +#ifdef HAVE_IFADDRS_H +# if defined(__ANDROID__) +# include "uv/android-ifaddrs.h" +# else +# include +# endif +# include +# include +# include +#endif /* HAVE_IFADDRS_H */ + +/* Available from 2.6.32 onwards. */ +#ifndef CLOCK_MONOTONIC_COARSE +# define CLOCK_MONOTONIC_COARSE 6 +#endif + +/* This is rather annoying: CLOCK_BOOTTIME lives in but we can't + * include that file because it conflicts with . We'll just have to + * define it ourselves. + */ +#ifndef CLOCK_BOOTTIME +# define CLOCK_BOOTTIME 7 +#endif + +static int read_models(unsigned int numcpus, uv_cpu_info_t* ci); +static int read_times(FILE* statfile_fp, + unsigned int numcpus, + uv_cpu_info_t* ci); +static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci); +static uint64_t read_cpufreq(unsigned int cpunum); + + +int uv__platform_loop_init(uv_loop_t* loop) { + int fd; + fd = epoll_create1(O_CLOEXEC); + + /* epoll_create1() can fail either because it's not implemented (old kernel) + * or because it doesn't understand the O_CLOEXEC flag. + */ + if (fd == -1 && (errno == ENOSYS || errno == EINVAL)) { + fd = epoll_create(256); + + if (fd != -1) + uv__cloexec(fd, 1); + } + + loop->backend_fd = fd; + loop->inotify_fd = -1; + loop->inotify_watchers = NULL; + + if (fd == -1) + return UV__ERR(errno); + + return 0; +} + + +int uv__io_fork(uv_loop_t* loop) { + int err; + void* old_watchers; + + old_watchers = loop->inotify_watchers; + + uv__close(loop->backend_fd); + loop->backend_fd = -1; + uv__platform_loop_delete(loop); + + err = uv__platform_loop_init(loop); + if (err) + return err; + + return uv__inotify_fork(loop, old_watchers); +} + + +void uv__platform_loop_delete(uv_loop_t* loop) { + if (loop->inotify_fd == -1) return; + uv__io_stop(loop, &loop->inotify_read_watcher, POLLIN); + uv__close(loop->inotify_fd); + loop->inotify_fd = -1; +} + + +void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { + struct epoll_event* events; + struct epoll_event dummy; + uintptr_t i; + uintptr_t nfds; + + assert(loop->watchers != NULL); + assert(fd >= 0); + + events = (struct epoll_event*) loop->watchers[loop->nwatchers]; + nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; + if (events != NULL) + /* Invalidate events with same file descriptor */ + for (i = 0; i < nfds; i++) + if (events[i].data.fd == fd) + events[i].data.fd = -1; + + /* Remove the file descriptor from the epoll. + * This avoids a problem where the same file description remains open + * in another process, causing repeated junk epoll events. + * + * We pass in a dummy epoll_event, to work around a bug in old kernels. + */ + if (loop->backend_fd >= 0) { + /* Work around a bug in kernels 3.10 to 3.19 where passing a struct that + * has the EPOLLWAKEUP flag set generates spurious audit syslog warnings. + */ + memset(&dummy, 0, sizeof(dummy)); + epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &dummy); + } +} + + +int uv__io_check_fd(uv_loop_t* loop, int fd) { + struct epoll_event e; + int rc; + + memset(&e, 0, sizeof(e)); + e.events = POLLIN; + e.data.fd = -1; + + rc = 0; + if (epoll_ctl(loop->backend_fd, EPOLL_CTL_ADD, fd, &e)) + if (errno != EEXIST) + rc = UV__ERR(errno); + + if (rc == 0) + if (epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &e)) + abort(); + + return rc; +} + + +void uv__io_poll(uv_loop_t* loop, int timeout) { + /* A bug in kernels < 2.6.37 makes timeouts larger than ~30 minutes + * effectively infinite on 32 bits architectures. To avoid blocking + * indefinitely, we cap the timeout and poll again if necessary. + * + * Note that "30 minutes" is a simplification because it depends on + * the value of CONFIG_HZ. The magic constant assumes CONFIG_HZ=1200, + * that being the largest value I have seen in the wild (and only once.) + */ + static const int max_safe_timeout = 1789569; + static int no_epoll_pwait_cached; + static int no_epoll_wait_cached; + int no_epoll_pwait; + int no_epoll_wait; + struct epoll_event events[1024]; + struct epoll_event* pe; + struct epoll_event e; + int real_timeout; + QUEUE* q; + uv__io_t* w; + sigset_t sigset; + uint64_t sigmask; + uint64_t base; + int have_signals; + int nevents; + int count; + int nfds; + int fd; + int op; + int i; + int user_timeout; + int reset_timeout; + + if (loop->nfds == 0) { + assert(QUEUE_EMPTY(&loop->watcher_queue)); + return; + } + + memset(&e, 0, sizeof(e)); + + while (!QUEUE_EMPTY(&loop->watcher_queue)) { + q = QUEUE_HEAD(&loop->watcher_queue); + QUEUE_REMOVE(q); + QUEUE_INIT(q); + + w = QUEUE_DATA(q, uv__io_t, watcher_queue); + assert(w->pevents != 0); + assert(w->fd >= 0); + assert(w->fd < (int) loop->nwatchers); + + e.events = w->pevents; + e.data.fd = w->fd; + + if (w->events == 0) + op = EPOLL_CTL_ADD; + else + op = EPOLL_CTL_MOD; + + /* XXX Future optimization: do EPOLL_CTL_MOD lazily if we stop watching + * events, skip the syscall and squelch the events after epoll_wait(). + */ + if (epoll_ctl(loop->backend_fd, op, w->fd, &e)) { + if (errno != EEXIST) + abort(); + + assert(op == EPOLL_CTL_ADD); + + /* We've reactivated a file descriptor that's been watched before. */ + if (epoll_ctl(loop->backend_fd, EPOLL_CTL_MOD, w->fd, &e)) + abort(); + } + + w->events = w->pevents; + } + + sigmask = 0; + if (loop->flags & UV_LOOP_BLOCK_SIGPROF) { + sigemptyset(&sigset); + sigaddset(&sigset, SIGPROF); + sigmask |= 1 << (SIGPROF - 1); + } + + assert(timeout >= -1); + base = loop->time; + count = 48; /* Benchmarks suggest this gives the best throughput. */ + real_timeout = timeout; + + if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { + reset_timeout = 1; + user_timeout = timeout; + timeout = 0; + } else { + reset_timeout = 0; + user_timeout = 0; + } + + /* You could argue there is a dependency between these two but + * ultimately we don't care about their ordering with respect + * to one another. Worst case, we make a few system calls that + * could have been avoided because another thread already knows + * they fail with ENOSYS. Hardly the end of the world. + */ + no_epoll_pwait = uv__load_relaxed(&no_epoll_pwait_cached); + no_epoll_wait = uv__load_relaxed(&no_epoll_wait_cached); + + for (;;) { + /* Only need to set the provider_entry_time if timeout != 0. The function + * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. + */ + if (timeout != 0) + uv__metrics_set_provider_entry_time(loop); + + /* See the comment for max_safe_timeout for an explanation of why + * this is necessary. Executive summary: kernel bug workaround. + */ + if (sizeof(int32_t) == sizeof(long) && timeout >= max_safe_timeout) + timeout = max_safe_timeout; + + if (sigmask != 0 && no_epoll_pwait != 0) + if (pthread_sigmask(SIG_BLOCK, &sigset, NULL)) + abort(); + + if (no_epoll_wait != 0 || (sigmask != 0 && no_epoll_pwait == 0)) { + nfds = epoll_pwait(loop->backend_fd, + events, + ARRAY_SIZE(events), + timeout, + &sigset); + if (nfds == -1 && errno == ENOSYS) { + uv__store_relaxed(&no_epoll_pwait_cached, 1); + no_epoll_pwait = 1; + } + } else { + nfds = epoll_wait(loop->backend_fd, + events, + ARRAY_SIZE(events), + timeout); + if (nfds == -1 && errno == ENOSYS) { + uv__store_relaxed(&no_epoll_wait_cached, 1); + no_epoll_wait = 1; + } + } + + if (sigmask != 0 && no_epoll_pwait != 0) + if (pthread_sigmask(SIG_UNBLOCK, &sigset, NULL)) + abort(); + + /* Update loop->time unconditionally. It's tempting to skip the update when + * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the + * operating system didn't reschedule our process while in the syscall. + */ + SAVE_ERRNO(uv__update_time(loop)); + + if (nfds == 0) { + assert(timeout != -1); + + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + + if (timeout == -1) + continue; + + if (timeout == 0) + return; + + /* We may have been inside the system call for longer than |timeout| + * milliseconds so we need to update the timestamp to avoid drift. + */ + goto update_timeout; + } + + if (nfds == -1) { + if (errno == ENOSYS) { + /* epoll_wait() or epoll_pwait() failed, try the other system call. */ + assert(no_epoll_wait == 0 || no_epoll_pwait == 0); + continue; + } + + if (errno != EINTR) + abort(); + + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + + if (timeout == -1) + continue; + + if (timeout == 0) + return; + + /* Interrupted by a signal. Update timeout and poll again. */ + goto update_timeout; + } + + have_signals = 0; + nevents = 0; + + { + /* Squelch a -Waddress-of-packed-member warning with gcc >= 9. */ + union { + struct epoll_event* events; + uv__io_t* watchers; + } x; + + x.events = events; + assert(loop->watchers != NULL); + loop->watchers[loop->nwatchers] = x.watchers; + loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds; + } + + for (i = 0; i < nfds; i++) { + pe = events + i; + fd = pe->data.fd; + + /* Skip invalidated events, see uv__platform_invalidate_fd */ + if (fd == -1) + continue; + + assert(fd >= 0); + assert((unsigned) fd < loop->nwatchers); + + w = loop->watchers[fd]; + + if (w == NULL) { + /* File descriptor that we've stopped watching, disarm it. + * + * Ignore all errors because we may be racing with another thread + * when the file descriptor is closed. + */ + epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, pe); + continue; + } + + /* Give users only events they're interested in. Prevents spurious + * callbacks when previous callback invocation in this loop has stopped + * the current watcher. Also, filters out events that users has not + * requested us to watch. + */ + pe->events &= w->pevents | POLLERR | POLLHUP; + + /* Work around an epoll quirk where it sometimes reports just the + * EPOLLERR or EPOLLHUP event. In order to force the event loop to + * move forward, we merge in the read/write events that the watcher + * is interested in; uv__read() and uv__write() will then deal with + * the error or hangup in the usual fashion. + * + * Note to self: happens when epoll reports EPOLLIN|EPOLLHUP, the user + * reads the available data, calls uv_read_stop(), then sometime later + * calls uv_read_start() again. By then, libuv has forgotten about the + * hangup and the kernel won't report EPOLLIN again because there's + * nothing left to read. If anything, libuv is to blame here. The + * current hack is just a quick bandaid; to properly fix it, libuv + * needs to remember the error/hangup event. We should get that for + * free when we switch over to edge-triggered I/O. + */ + if (pe->events == POLLERR || pe->events == POLLHUP) + pe->events |= + w->pevents & (POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI); + + if (pe->events != 0) { + /* Run signal watchers last. This also affects child process watchers + * because those are implemented in terms of signal watchers. + */ + if (w == &loop->signal_io_watcher) { + have_signals = 1; + } else { + uv__metrics_update_idle_time(loop); + w->cb(loop, w, pe->events); + } + + nevents++; + } + } + + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + + if (have_signals != 0) { + uv__metrics_update_idle_time(loop); + loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN); + } + + loop->watchers[loop->nwatchers] = NULL; + loop->watchers[loop->nwatchers + 1] = NULL; + + if (have_signals != 0) + return; /* Event loop should cycle now so don't poll again. */ + + if (nevents != 0) { + if (nfds == ARRAY_SIZE(events) && --count != 0) { + /* Poll for more events but don't block this time. */ + timeout = 0; + continue; + } + return; + } + + if (timeout == 0) + return; + + if (timeout == -1) + continue; + +update_timeout: + assert(timeout > 0); + + real_timeout -= (loop->time - base); + if (real_timeout <= 0) + return; + + timeout = real_timeout; + } +} + + +uint64_t uv__hrtime(uv_clocktype_t type) { + static clock_t fast_clock_id = -1; + struct timespec t; + clock_t clock_id; + + /* Prefer CLOCK_MONOTONIC_COARSE if available but only when it has + * millisecond granularity or better. CLOCK_MONOTONIC_COARSE is + * serviced entirely from the vDSO, whereas CLOCK_MONOTONIC may + * decide to make a costly system call. + */ + /* TODO(bnoordhuis) Use CLOCK_MONOTONIC_COARSE for UV_CLOCK_PRECISE + * when it has microsecond granularity or better (unlikely). + */ + clock_id = CLOCK_MONOTONIC; + if (type != UV_CLOCK_FAST) + goto done; + + clock_id = uv__load_relaxed(&fast_clock_id); + if (clock_id != -1) + goto done; + + clock_id = CLOCK_MONOTONIC; + if (0 == clock_getres(CLOCK_MONOTONIC_COARSE, &t)) + if (t.tv_nsec <= 1 * 1000 * 1000) + clock_id = CLOCK_MONOTONIC_COARSE; + + uv__store_relaxed(&fast_clock_id, clock_id); + +done: + + if (clock_gettime(clock_id, &t)) + return 0; /* Not really possible. */ + + return t.tv_sec * (uint64_t) 1e9 + t.tv_nsec; +} + + +int uv_resident_set_memory(size_t* rss) { + char buf[1024]; + const char* s; + ssize_t n; + long val; + int fd; + int i; + + do + fd = open("/proc/self/stat", O_RDONLY); + while (fd == -1 && errno == EINTR); + + if (fd == -1) + return UV__ERR(errno); + + do + n = read(fd, buf, sizeof(buf) - 1); + while (n == -1 && errno == EINTR); + + uv__close(fd); + if (n == -1) + return UV__ERR(errno); + buf[n] = '\0'; + + s = strchr(buf, ' '); + if (s == NULL) + goto err; + + s += 1; + if (*s != '(') + goto err; + + s = strchr(s, ')'); + if (s == NULL) + goto err; + + for (i = 1; i <= 22; i++) { + s = strchr(s + 1, ' '); + if (s == NULL) + goto err; + } + + errno = 0; + val = strtol(s, NULL, 10); + if (errno != 0) + goto err; + if (val < 0) + goto err; + + *rss = val * getpagesize(); + return 0; + +err: + return UV_EINVAL; +} + + +int uv_uptime(double* uptime) { + static volatile int no_clock_boottime; + struct timespec now; + int r; + + /* Try CLOCK_BOOTTIME first, fall back to CLOCK_MONOTONIC if not available + * (pre-2.6.39 kernels). CLOCK_MONOTONIC doesn't increase when the system + * is suspended. + */ + if (no_clock_boottime) { + retry: r = clock_gettime(CLOCK_MONOTONIC, &now); + } + else if ((r = clock_gettime(CLOCK_BOOTTIME, &now)) && errno == EINVAL) { + no_clock_boottime = 1; + goto retry; + } + + if (r) + return UV__ERR(errno); + + *uptime = now.tv_sec; + return 0; +} + + +static int uv__cpu_num(FILE* statfile_fp, unsigned int* numcpus) { + unsigned int num; + char buf[1024]; + + if (!fgets(buf, sizeof(buf), statfile_fp)) + return UV_EIO; + + num = 0; + while (fgets(buf, sizeof(buf), statfile_fp)) { + if (strncmp(buf, "cpu", 3)) + break; + num++; + } + + if (num == 0) + return UV_EIO; + + *numcpus = num; + return 0; +} + + +int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + unsigned int numcpus; + uv_cpu_info_t* ci; + int err; + FILE* statfile_fp; + + *cpu_infos = NULL; + *count = 0; + + statfile_fp = uv__open_file("/proc/stat"); + if (statfile_fp == NULL) + return UV__ERR(errno); + + err = uv__cpu_num(statfile_fp, &numcpus); + if (err < 0) + goto out; + + err = UV_ENOMEM; + ci = uv__calloc(numcpus, sizeof(*ci)); + if (ci == NULL) + goto out; + + err = read_models(numcpus, ci); + if (err == 0) + err = read_times(statfile_fp, numcpus, ci); + + if (err) { + uv_free_cpu_info(ci, numcpus); + goto out; + } + + /* read_models() on x86 also reads the CPU speed from /proc/cpuinfo. + * We don't check for errors here. Worst case, the field is left zero. + */ + if (ci[0].speed == 0) + read_speeds(numcpus, ci); + + *cpu_infos = ci; + *count = numcpus; + err = 0; + +out: + + if (fclose(statfile_fp)) + if (errno != EINTR && errno != EINPROGRESS) + abort(); + + return err; +} + + +static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci) { + unsigned int num; + + for (num = 0; num < numcpus; num++) + ci[num].speed = read_cpufreq(num) / 1000; +} + + +/* Also reads the CPU frequency on x86. The other architectures only have + * a BogoMIPS field, which may not be very accurate. + * + * Note: Simply returns on error, uv_cpu_info() takes care of the cleanup. + */ +static int read_models(unsigned int numcpus, uv_cpu_info_t* ci) { + static const char model_marker[] = "model name\t: "; + static const char speed_marker[] = "cpu MHz\t\t: "; + const char* inferred_model; + unsigned int model_idx; + unsigned int speed_idx; + char buf[1024]; + char* model; + FILE* fp; + + /* Most are unused on non-ARM, non-MIPS and non-x86 architectures. */ + (void) &model_marker; + (void) &speed_marker; + (void) &speed_idx; + (void) &model; + (void) &buf; + (void) &fp; + + model_idx = 0; + speed_idx = 0; + +#if defined(__arm__) || \ + defined(__i386__) || \ + defined(__mips__) || \ + defined(__x86_64__) + fp = uv__open_file("/proc/cpuinfo"); + if (fp == NULL) + return UV__ERR(errno); + + while (fgets(buf, sizeof(buf), fp)) { + if (model_idx < numcpus) { + if (strncmp(buf, model_marker, sizeof(model_marker) - 1) == 0) { + model = buf + sizeof(model_marker) - 1; + model = uv__strndup(model, strlen(model) - 1); /* Strip newline. */ + if (model == NULL) { + fclose(fp); + return UV_ENOMEM; + } + ci[model_idx++].model = model; + continue; + } + } +#if defined(__arm__) || defined(__mips__) + if (model_idx < numcpus) { +#if defined(__arm__) + /* Fallback for pre-3.8 kernels. */ + static const char model_marker[] = "Processor\t: "; +#else /* defined(__mips__) */ + static const char model_marker[] = "cpu model\t\t: "; +#endif + if (strncmp(buf, model_marker, sizeof(model_marker) - 1) == 0) { + model = buf + sizeof(model_marker) - 1; + model = uv__strndup(model, strlen(model) - 1); /* Strip newline. */ + if (model == NULL) { + fclose(fp); + return UV_ENOMEM; + } + ci[model_idx++].model = model; + continue; + } + } +#else /* !__arm__ && !__mips__ */ + if (speed_idx < numcpus) { + if (strncmp(buf, speed_marker, sizeof(speed_marker) - 1) == 0) { + ci[speed_idx++].speed = atoi(buf + sizeof(speed_marker) - 1); + continue; + } + } +#endif /* __arm__ || __mips__ */ + } + + fclose(fp); +#endif /* __arm__ || __i386__ || __mips__ || __x86_64__ */ + + /* Now we want to make sure that all the models contain *something* because + * it's not safe to leave them as null. Copy the last entry unless there + * isn't one, in that case we simply put "unknown" into everything. + */ + inferred_model = "unknown"; + if (model_idx > 0) + inferred_model = ci[model_idx - 1].model; + + while (model_idx < numcpus) { + model = uv__strndup(inferred_model, strlen(inferred_model)); + if (model == NULL) + return UV_ENOMEM; + ci[model_idx++].model = model; + } + + return 0; +} + + +static int read_times(FILE* statfile_fp, + unsigned int numcpus, + uv_cpu_info_t* ci) { + struct uv_cpu_times_s ts; + unsigned int ticks; + unsigned int multiplier; + uint64_t user; + uint64_t nice; + uint64_t sys; + uint64_t idle; + uint64_t dummy; + uint64_t irq; + uint64_t num; + uint64_t len; + char buf[1024]; + + ticks = (unsigned int)sysconf(_SC_CLK_TCK); + multiplier = ((uint64_t)1000L / ticks); + assert(ticks != (unsigned int) -1); + assert(ticks != 0); + + rewind(statfile_fp); + + if (!fgets(buf, sizeof(buf), statfile_fp)) + abort(); + + num = 0; + + while (fgets(buf, sizeof(buf), statfile_fp)) { + if (num >= numcpus) + break; + + if (strncmp(buf, "cpu", 3)) + break; + + /* skip "cpu " marker */ + { + unsigned int n; + int r = sscanf(buf, "cpu%u ", &n); + assert(r == 1); + (void) r; /* silence build warning */ + for (len = sizeof("cpu0"); n /= 10; len++); + } + + /* Line contains user, nice, system, idle, iowait, irq, softirq, steal, + * guest, guest_nice but we're only interested in the first four + irq. + * + * Don't use %*s to skip fields or %ll to read straight into the uint64_t + * fields, they're not allowed in C89 mode. + */ + if (6 != sscanf(buf + len, + "%" PRIu64 " %" PRIu64 " %" PRIu64 + "%" PRIu64 " %" PRIu64 " %" PRIu64, + &user, + &nice, + &sys, + &idle, + &dummy, + &irq)) + abort(); + + ts.user = user * multiplier; + ts.nice = nice * multiplier; + ts.sys = sys * multiplier; + ts.idle = idle * multiplier; + ts.irq = irq * multiplier; + ci[num++].cpu_times = ts; + } + assert(num == numcpus); + + return 0; +} + + +static uint64_t read_cpufreq(unsigned int cpunum) { + uint64_t val; + char buf[1024]; + FILE* fp; + + snprintf(buf, + sizeof(buf), + "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq", + cpunum); + + fp = uv__open_file(buf); + if (fp == NULL) + return 0; + + if (fscanf(fp, "%" PRIu64, &val) != 1) + val = 0; + + fclose(fp); + + return val; +} + + +static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) { + if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING))) + return 1; + if (ent->ifa_addr == NULL) + return 1; + /* + * On Linux getifaddrs returns information related to the raw underlying + * devices. We're not interested in this information yet. + */ + if (ent->ifa_addr->sa_family == PF_PACKET) + return exclude_type; + return !exclude_type; +} + +int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { +#ifndef HAVE_IFADDRS_H + *count = 0; + *addresses = NULL; + return UV_ENOSYS; +#else + struct ifaddrs *addrs, *ent; + uv_interface_address_t* address; + int i; + struct sockaddr_ll *sll; + + *count = 0; + *addresses = NULL; + + if (getifaddrs(&addrs)) + return UV__ERR(errno); + + /* Count the number of interfaces */ + for (ent = addrs; ent != NULL; ent = ent->ifa_next) { + if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR)) + continue; + + (*count)++; + } + + if (*count == 0) { + freeifaddrs(addrs); + return 0; + } + + /* Make sure the memory is initiallized to zero using calloc() */ + *addresses = uv__calloc(*count, sizeof(**addresses)); + if (!(*addresses)) { + freeifaddrs(addrs); + return UV_ENOMEM; + } + + address = *addresses; + + for (ent = addrs; ent != NULL; ent = ent->ifa_next) { + if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFADDR)) + continue; + + address->name = uv__strdup(ent->ifa_name); + + if (ent->ifa_addr->sa_family == AF_INET6) { + address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr); + } else { + address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr); + } + + if (ent->ifa_netmask->sa_family == AF_INET6) { + address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask); + } else { + address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask); + } + + address->is_internal = !!(ent->ifa_flags & IFF_LOOPBACK); + + address++; + } + + /* Fill in physical addresses for each interface */ + for (ent = addrs; ent != NULL; ent = ent->ifa_next) { + if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS)) + continue; + + address = *addresses; + + for (i = 0; i < (*count); i++) { + size_t namelen = strlen(ent->ifa_name); + /* Alias interface share the same physical address */ + if (strncmp(address->name, ent->ifa_name, namelen) == 0 && + (address->name[namelen] == 0 || address->name[namelen] == ':')) { + sll = (struct sockaddr_ll*)ent->ifa_addr; + memcpy(address->phys_addr, sll->sll_addr, sizeof(address->phys_addr)); + } + address++; + } + } + + freeifaddrs(addrs); + + return 0; +#endif +} + + +void uv_free_interface_addresses(uv_interface_address_t* addresses, + int count) { + int i; + + for (i = 0; i < count; i++) { + uv__free(addresses[i].name); + } + + uv__free(addresses); +} + + +void uv__set_process_title(const char* title) { +#if defined(PR_SET_NAME) + prctl(PR_SET_NAME, title); /* Only copies first 16 characters. */ +#endif +} + + +static int uv__slurp(const char* filename, char* buf, size_t len) { + ssize_t n; + int fd; + + assert(len > 0); + + fd = uv__open_cloexec(filename, O_RDONLY); + if (fd < 0) + return fd; + + do + n = read(fd, buf, len - 1); + while (n == -1 && errno == EINTR); + + if (uv__close_nocheckstdio(fd)) + abort(); + + if (n < 0) + return UV__ERR(errno); + + buf[n] = '\0'; + + return 0; +} + + +static uint64_t uv__read_proc_meminfo(const char* what) { + uint64_t rc; + char* p; + char buf[4096]; /* Large enough to hold all of /proc/meminfo. */ + + if (uv__slurp("/proc/meminfo", buf, sizeof(buf))) + return 0; + + p = strstr(buf, what); + + if (p == NULL) + return 0; + + p += strlen(what); + + rc = 0; + sscanf(p, "%" PRIu64 " kB", &rc); + + return rc * 1024; +} + + +uint64_t uv_get_free_memory(void) { + struct sysinfo info; + uint64_t rc; + + rc = uv__read_proc_meminfo("MemFree:"); + + if (rc != 0) + return rc; + + if (0 == sysinfo(&info)) + return (uint64_t) info.freeram * info.mem_unit; + + return 0; +} + + +uint64_t uv_get_total_memory(void) { + struct sysinfo info; + uint64_t rc; + + rc = uv__read_proc_meminfo("MemTotal:"); + + if (rc != 0) + return rc; + + if (0 == sysinfo(&info)) + return (uint64_t) info.totalram * info.mem_unit; + + return 0; +} + + +static uint64_t uv__read_cgroups_uint64(const char* cgroup, const char* param) { + char filename[256]; + char buf[32]; /* Large enough to hold an encoded uint64_t. */ + uint64_t rc; + + rc = 0; + snprintf(filename, sizeof(filename), "/sys/fs/cgroup/%s/%s", cgroup, param); + if (0 == uv__slurp(filename, buf, sizeof(buf))) + sscanf(buf, "%" PRIu64, &rc); + + return rc; +} + + +uint64_t uv_get_constrained_memory(void) { + /* + * This might return 0 if there was a problem getting the memory limit from + * cgroups. This is OK because a return value of 0 signifies that the memory + * limit is unknown. + */ + return uv__read_cgroups_uint64("memory", "memory.limit_in_bytes"); +} + + +void uv_loadavg(double avg[3]) { + struct sysinfo info; + char buf[128]; /* Large enough to hold all of /proc/loadavg. */ + + if (0 == uv__slurp("/proc/loadavg", buf, sizeof(buf))) + if (3 == sscanf(buf, "%lf %lf %lf", &avg[0], &avg[1], &avg[2])) + return; + + if (sysinfo(&info) < 0) + return; + + avg[0] = (double) info.loads[0] / 65536.0; + avg[1] = (double) info.loads[1] / 65536.0; + avg[2] = (double) info.loads[2] / 65536.0; +} diff --git a/include/libuv/src/unix/linux-inotify.c b/include/libuv/src/unix/linux-inotify.c new file mode 100644 index 000000000..42b601adb --- /dev/null +++ b/include/libuv/src/unix/linux-inotify.c @@ -0,0 +1,327 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "uv/tree.h" +#include "internal.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +struct watcher_list { + RB_ENTRY(watcher_list) entry; + QUEUE watchers; + int iterating; + char* path; + int wd; +}; + +struct watcher_root { + struct watcher_list* rbh_root; +}; +#define CAST(p) ((struct watcher_root*)(p)) + + +static int compare_watchers(const struct watcher_list* a, + const struct watcher_list* b) { + if (a->wd < b->wd) return -1; + if (a->wd > b->wd) return 1; + return 0; +} + + +RB_GENERATE_STATIC(watcher_root, watcher_list, entry, compare_watchers) + + +static void uv__inotify_read(uv_loop_t* loop, + uv__io_t* w, + unsigned int revents); + +static void maybe_free_watcher_list(struct watcher_list* w, + uv_loop_t* loop); + +static int init_inotify(uv_loop_t* loop) { + int fd; + + if (loop->inotify_fd != -1) + return 0; + + fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); + if (fd < 0) + return UV__ERR(errno); + + loop->inotify_fd = fd; + uv__io_init(&loop->inotify_read_watcher, uv__inotify_read, loop->inotify_fd); + uv__io_start(loop, &loop->inotify_read_watcher, POLLIN); + + return 0; +} + + +int uv__inotify_fork(uv_loop_t* loop, void* old_watchers) { + /* Open the inotify_fd, and re-arm all the inotify watchers. */ + int err; + struct watcher_list* tmp_watcher_list_iter; + struct watcher_list* watcher_list; + struct watcher_list tmp_watcher_list; + QUEUE queue; + QUEUE* q; + uv_fs_event_t* handle; + char* tmp_path; + + if (old_watchers != NULL) { + /* We must restore the old watcher list to be able to close items + * out of it. + */ + loop->inotify_watchers = old_watchers; + + QUEUE_INIT(&tmp_watcher_list.watchers); + /* Note that the queue we use is shared with the start and stop() + * functions, making QUEUE_FOREACH unsafe to use. So we use the + * QUEUE_MOVE trick to safely iterate. Also don't free the watcher + * list until we're done iterating. c.f. uv__inotify_read. + */ + RB_FOREACH_SAFE(watcher_list, watcher_root, + CAST(&old_watchers), tmp_watcher_list_iter) { + watcher_list->iterating = 1; + QUEUE_MOVE(&watcher_list->watchers, &queue); + while (!QUEUE_EMPTY(&queue)) { + q = QUEUE_HEAD(&queue); + handle = QUEUE_DATA(q, uv_fs_event_t, watchers); + /* It's critical to keep a copy of path here, because it + * will be set to NULL by stop() and then deallocated by + * maybe_free_watcher_list + */ + tmp_path = uv__strdup(handle->path); + assert(tmp_path != NULL); + QUEUE_REMOVE(q); + QUEUE_INSERT_TAIL(&watcher_list->watchers, q); + uv_fs_event_stop(handle); + + QUEUE_INSERT_TAIL(&tmp_watcher_list.watchers, &handle->watchers); + handle->path = tmp_path; + } + watcher_list->iterating = 0; + maybe_free_watcher_list(watcher_list, loop); + } + + QUEUE_MOVE(&tmp_watcher_list.watchers, &queue); + while (!QUEUE_EMPTY(&queue)) { + q = QUEUE_HEAD(&queue); + QUEUE_REMOVE(q); + handle = QUEUE_DATA(q, uv_fs_event_t, watchers); + tmp_path = handle->path; + handle->path = NULL; + err = uv_fs_event_start(handle, handle->cb, tmp_path, 0); + uv__free(tmp_path); + if (err) + return err; + } + } + + return 0; +} + + +static struct watcher_list* find_watcher(uv_loop_t* loop, int wd) { + struct watcher_list w; + w.wd = wd; + return RB_FIND(watcher_root, CAST(&loop->inotify_watchers), &w); +} + +static void maybe_free_watcher_list(struct watcher_list* w, uv_loop_t* loop) { + /* if the watcher_list->watchers is being iterated over, we can't free it. */ + if ((!w->iterating) && QUEUE_EMPTY(&w->watchers)) { + /* No watchers left for this path. Clean up. */ + RB_REMOVE(watcher_root, CAST(&loop->inotify_watchers), w); + inotify_rm_watch(loop->inotify_fd, w->wd); + uv__free(w); + } +} + +static void uv__inotify_read(uv_loop_t* loop, + uv__io_t* dummy, + unsigned int events) { + const struct inotify_event* e; + struct watcher_list* w; + uv_fs_event_t* h; + QUEUE queue; + QUEUE* q; + const char* path; + ssize_t size; + const char *p; + /* needs to be large enough for sizeof(inotify_event) + strlen(path) */ + char buf[4096]; + + while (1) { + do + size = read(loop->inotify_fd, buf, sizeof(buf)); + while (size == -1 && errno == EINTR); + + if (size == -1) { + assert(errno == EAGAIN || errno == EWOULDBLOCK); + break; + } + + assert(size > 0); /* pre-2.6.21 thing, size=0 == read buffer too small */ + + /* Now we have one or more inotify_event structs. */ + for (p = buf; p < buf + size; p += sizeof(*e) + e->len) { + e = (const struct inotify_event*) p; + + events = 0; + if (e->mask & (IN_ATTRIB|IN_MODIFY)) + events |= UV_CHANGE; + if (e->mask & ~(IN_ATTRIB|IN_MODIFY)) + events |= UV_RENAME; + + w = find_watcher(loop, e->wd); + if (w == NULL) + continue; /* Stale event, no watchers left. */ + + /* inotify does not return the filename when monitoring a single file + * for modifications. Repurpose the filename for API compatibility. + * I'm not convinced this is a good thing, maybe it should go. + */ + path = e->len ? (const char*) (e + 1) : uv__basename_r(w->path); + + /* We're about to iterate over the queue and call user's callbacks. + * What can go wrong? + * A callback could call uv_fs_event_stop() + * and the queue can change under our feet. + * So, we use QUEUE_MOVE() trick to safely iterate over the queue. + * And we don't free the watcher_list until we're done iterating. + * + * First, + * tell uv_fs_event_stop() (that could be called from a user's callback) + * not to free watcher_list. + */ + w->iterating = 1; + QUEUE_MOVE(&w->watchers, &queue); + while (!QUEUE_EMPTY(&queue)) { + q = QUEUE_HEAD(&queue); + h = QUEUE_DATA(q, uv_fs_event_t, watchers); + + QUEUE_REMOVE(q); + QUEUE_INSERT_TAIL(&w->watchers, q); + + h->cb(h, path, events, 0); + } + /* done iterating, time to (maybe) free empty watcher_list */ + w->iterating = 0; + maybe_free_watcher_list(w, loop); + } + } +} + + +int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { + uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT); + return 0; +} + + +int uv_fs_event_start(uv_fs_event_t* handle, + uv_fs_event_cb cb, + const char* path, + unsigned int flags) { + struct watcher_list* w; + size_t len; + int events; + int err; + int wd; + + if (uv__is_active(handle)) + return UV_EINVAL; + + err = init_inotify(handle->loop); + if (err) + return err; + + events = IN_ATTRIB + | IN_CREATE + | IN_MODIFY + | IN_DELETE + | IN_DELETE_SELF + | IN_MOVE_SELF + | IN_MOVED_FROM + | IN_MOVED_TO; + + wd = inotify_add_watch(handle->loop->inotify_fd, path, events); + if (wd == -1) + return UV__ERR(errno); + + w = find_watcher(handle->loop, wd); + if (w) + goto no_insert; + + len = strlen(path) + 1; + w = uv__malloc(sizeof(*w) + len); + if (w == NULL) + return UV_ENOMEM; + + w->wd = wd; + w->path = memcpy(w + 1, path, len); + QUEUE_INIT(&w->watchers); + w->iterating = 0; + RB_INSERT(watcher_root, CAST(&handle->loop->inotify_watchers), w); + +no_insert: + uv__handle_start(handle); + QUEUE_INSERT_TAIL(&w->watchers, &handle->watchers); + handle->path = w->path; + handle->cb = cb; + handle->wd = wd; + + return 0; +} + + +int uv_fs_event_stop(uv_fs_event_t* handle) { + struct watcher_list* w; + + if (!uv__is_active(handle)) + return 0; + + w = find_watcher(handle->loop, handle->wd); + assert(w != NULL); + + handle->wd = -1; + handle->path = NULL; + uv__handle_stop(handle); + QUEUE_REMOVE(&handle->watchers); + + maybe_free_watcher_list(w, handle->loop); + + return 0; +} + + +void uv__fs_event_close(uv_fs_event_t* handle) { + uv_fs_event_stop(handle); +} diff --git a/include/libuv/src/unix/linux-syscalls.c b/include/libuv/src/unix/linux-syscalls.c new file mode 100644 index 000000000..44daaf12d --- /dev/null +++ b/include/libuv/src/unix/linux-syscalls.c @@ -0,0 +1,267 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "linux-syscalls.h" +#include +#include +#include +#include +#include + +#if defined(__arm__) +# if defined(__thumb__) || defined(__ARM_EABI__) +# define UV_SYSCALL_BASE 0 +# else +# define UV_SYSCALL_BASE 0x900000 +# endif +#endif /* __arm__ */ + +#ifndef __NR_recvmmsg +# if defined(__x86_64__) +# define __NR_recvmmsg 299 +# elif defined(__arm__) +# define __NR_recvmmsg (UV_SYSCALL_BASE + 365) +# endif +#endif /* __NR_recvmsg */ + +#ifndef __NR_sendmmsg +# if defined(__x86_64__) +# define __NR_sendmmsg 307 +# elif defined(__arm__) +# define __NR_sendmmsg (UV_SYSCALL_BASE + 374) +# endif +#endif /* __NR_sendmmsg */ + +#ifndef __NR_utimensat +# if defined(__x86_64__) +# define __NR_utimensat 280 +# elif defined(__i386__) +# define __NR_utimensat 320 +# elif defined(__arm__) +# define __NR_utimensat (UV_SYSCALL_BASE + 348) +# endif +#endif /* __NR_utimensat */ + +#ifndef __NR_preadv +# if defined(__x86_64__) +# define __NR_preadv 295 +# elif defined(__i386__) +# define __NR_preadv 333 +# elif defined(__arm__) +# define __NR_preadv (UV_SYSCALL_BASE + 361) +# endif +#endif /* __NR_preadv */ + +#ifndef __NR_pwritev +# if defined(__x86_64__) +# define __NR_pwritev 296 +# elif defined(__i386__) +# define __NR_pwritev 334 +# elif defined(__arm__) +# define __NR_pwritev (UV_SYSCALL_BASE + 362) +# endif +#endif /* __NR_pwritev */ + +#ifndef __NR_dup3 +# if defined(__x86_64__) +# define __NR_dup3 292 +# elif defined(__i386__) +# define __NR_dup3 330 +# elif defined(__arm__) +# define __NR_dup3 (UV_SYSCALL_BASE + 358) +# endif +#endif /* __NR_pwritev */ + +#ifndef __NR_copy_file_range +# if defined(__x86_64__) +# define __NR_copy_file_range 326 +# elif defined(__i386__) +# define __NR_copy_file_range 377 +# elif defined(__s390__) +# define __NR_copy_file_range 375 +# elif defined(__arm__) +# define __NR_copy_file_range (UV_SYSCALL_BASE + 391) +# elif defined(__aarch64__) +# define __NR_copy_file_range 285 +# elif defined(__powerpc__) +# define __NR_copy_file_range 379 +# elif defined(__arc__) +# define __NR_copy_file_range 285 +# endif +#endif /* __NR_copy_file_range */ + +#ifndef __NR_statx +# if defined(__x86_64__) +# define __NR_statx 332 +# elif defined(__i386__) +# define __NR_statx 383 +# elif defined(__aarch64__) +# define __NR_statx 397 +# elif defined(__arm__) +# define __NR_statx (UV_SYSCALL_BASE + 397) +# elif defined(__ppc__) +# define __NR_statx 383 +# elif defined(__s390__) +# define __NR_statx 379 +# endif +#endif /* __NR_statx */ + +#ifndef __NR_getrandom +# if defined(__x86_64__) +# define __NR_getrandom 318 +# elif defined(__i386__) +# define __NR_getrandom 355 +# elif defined(__aarch64__) +# define __NR_getrandom 384 +# elif defined(__arm__) +# define __NR_getrandom (UV_SYSCALL_BASE + 384) +# elif defined(__ppc__) +# define __NR_getrandom 359 +# elif defined(__s390__) +# define __NR_getrandom 349 +# endif +#endif /* __NR_getrandom */ + +struct uv__mmsghdr; + +int uv__sendmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) { +#if defined(__i386__) + unsigned long args[4]; + int rc; + + args[0] = (unsigned long) fd; + args[1] = (unsigned long) mmsg; + args[2] = (unsigned long) vlen; + args[3] = /* flags */ 0; + + /* socketcall() raises EINVAL when SYS_SENDMMSG is not supported. */ + rc = syscall(/* __NR_socketcall */ 102, 20 /* SYS_SENDMMSG */, args); + if (rc == -1) + if (errno == EINVAL) + errno = ENOSYS; + + return rc; +#elif defined(__NR_sendmmsg) + return syscall(__NR_sendmmsg, fd, mmsg, vlen, /* flags */ 0); +#else + return errno = ENOSYS, -1; +#endif +} + + +int uv__recvmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) { +#if defined(__i386__) + unsigned long args[5]; + int rc; + + args[0] = (unsigned long) fd; + args[1] = (unsigned long) mmsg; + args[2] = (unsigned long) vlen; + args[3] = /* flags */ 0; + args[4] = /* timeout */ 0; + + /* socketcall() raises EINVAL when SYS_RECVMMSG is not supported. */ + rc = syscall(/* __NR_socketcall */ 102, 19 /* SYS_RECVMMSG */, args); + if (rc == -1) + if (errno == EINVAL) + errno = ENOSYS; + + return rc; +#elif defined(__NR_recvmmsg) + return syscall(__NR_recvmmsg, fd, mmsg, vlen, /* flags */ 0, /* timeout */ 0); +#else + return errno = ENOSYS, -1; +#endif +} + + +ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset) { +#if defined(__NR_preadv) + return syscall(__NR_preadv, fd, iov, iovcnt, (long)offset, (long)(offset >> 32)); +#else + return errno = ENOSYS, -1; +#endif +} + + +ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset) { +#if defined(__NR_pwritev) + return syscall(__NR_pwritev, fd, iov, iovcnt, (long)offset, (long)(offset >> 32)); +#else + return errno = ENOSYS, -1; +#endif +} + + +int uv__dup3(int oldfd, int newfd, int flags) { +#if defined(__NR_dup3) + return syscall(__NR_dup3, oldfd, newfd, flags); +#else + return errno = ENOSYS, -1; +#endif +} + + +ssize_t +uv__fs_copy_file_range(int fd_in, + ssize_t* off_in, + int fd_out, + ssize_t* off_out, + size_t len, + unsigned int flags) +{ +#ifdef __NR_copy_file_range + return syscall(__NR_copy_file_range, + fd_in, + off_in, + fd_out, + off_out, + len, + flags); +#else + return errno = ENOSYS, -1; +#endif +} + + +int uv__statx(int dirfd, + const char* path, + int flags, + unsigned int mask, + struct uv__statx* statxbuf) { + /* __NR_statx make Android box killed by SIGSYS. + * That looks like a seccomp2 sandbox filter rejecting the system call. + */ +#if defined(__NR_statx) && !defined(__ANDROID__) + return syscall(__NR_statx, dirfd, path, flags, mask, statxbuf); +#else + return errno = ENOSYS, -1; +#endif +} + + +ssize_t uv__getrandom(void* buf, size_t buflen, unsigned flags) { +#if defined(__NR_getrandom) + return syscall(__NR_getrandom, buf, buflen, flags); +#else + return errno = ENOSYS, -1; +#endif +} diff --git a/include/libuv/src/unix/linux-syscalls.h b/include/libuv/src/unix/linux-syscalls.h new file mode 100644 index 000000000..761ff32e2 --- /dev/null +++ b/include/libuv/src/unix/linux-syscalls.h @@ -0,0 +1,81 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef UV_LINUX_SYSCALL_H_ +#define UV_LINUX_SYSCALL_H_ + +#undef _GNU_SOURCE +#define _GNU_SOURCE + +#include +#include +#include +#include +#include + +struct uv__statx_timestamp { + int64_t tv_sec; + uint32_t tv_nsec; + int32_t unused0; +}; + +struct uv__statx { + uint32_t stx_mask; + uint32_t stx_blksize; + uint64_t stx_attributes; + uint32_t stx_nlink; + uint32_t stx_uid; + uint32_t stx_gid; + uint16_t stx_mode; + uint16_t unused0; + uint64_t stx_ino; + uint64_t stx_size; + uint64_t stx_blocks; + uint64_t stx_attributes_mask; + struct uv__statx_timestamp stx_atime; + struct uv__statx_timestamp stx_btime; + struct uv__statx_timestamp stx_ctime; + struct uv__statx_timestamp stx_mtime; + uint32_t stx_rdev_major; + uint32_t stx_rdev_minor; + uint32_t stx_dev_major; + uint32_t stx_dev_minor; + uint64_t unused1[14]; +}; + +ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset); +ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset); +int uv__dup3(int oldfd, int newfd, int flags); +ssize_t +uv__fs_copy_file_range(int fd_in, + ssize_t* off_in, + int fd_out, + ssize_t* off_out, + size_t len, + unsigned int flags); +int uv__statx(int dirfd, + const char* path, + int flags, + unsigned int mask, + struct uv__statx* statxbuf); +ssize_t uv__getrandom(void* buf, size_t buflen, unsigned flags); + +#endif /* UV_LINUX_SYSCALL_H_ */ diff --git a/include/libuv/src/unix/loop-watcher.c b/include/libuv/src/unix/loop-watcher.c new file mode 100644 index 000000000..b8c1c2a71 --- /dev/null +++ b/include/libuv/src/unix/loop-watcher.c @@ -0,0 +1,68 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#define UV_LOOP_WATCHER_DEFINE(name, type) \ + int uv_##name##_init(uv_loop_t* loop, uv_##name##_t* handle) { \ + uv__handle_init(loop, (uv_handle_t*)handle, UV_##type); \ + handle->name##_cb = NULL; \ + return 0; \ + } \ + \ + int uv_##name##_start(uv_##name##_t* handle, uv_##name##_cb cb) { \ + if (uv__is_active(handle)) return 0; \ + if (cb == NULL) return UV_EINVAL; \ + QUEUE_INSERT_HEAD(&handle->loop->name##_handles, &handle->queue); \ + handle->name##_cb = cb; \ + uv__handle_start(handle); \ + return 0; \ + } \ + \ + int uv_##name##_stop(uv_##name##_t* handle) { \ + if (!uv__is_active(handle)) return 0; \ + QUEUE_REMOVE(&handle->queue); \ + uv__handle_stop(handle); \ + return 0; \ + } \ + \ + void uv__run_##name(uv_loop_t* loop) { \ + uv_##name##_t* h; \ + QUEUE queue; \ + QUEUE* q; \ + QUEUE_MOVE(&loop->name##_handles, &queue); \ + while (!QUEUE_EMPTY(&queue)) { \ + q = QUEUE_HEAD(&queue); \ + h = QUEUE_DATA(q, uv_##name##_t, queue); \ + QUEUE_REMOVE(q); \ + QUEUE_INSERT_TAIL(&loop->name##_handles, q); \ + h->name##_cb(h); \ + } \ + } \ + \ + void uv__##name##_close(uv_##name##_t* handle) { \ + uv_##name##_stop(handle); \ + } + +UV_LOOP_WATCHER_DEFINE(prepare, PREPARE) +UV_LOOP_WATCHER_DEFINE(check, CHECK) +UV_LOOP_WATCHER_DEFINE(idle, IDLE) diff --git a/include/libuv/src/unix/loop.c b/include/libuv/src/unix/loop.c new file mode 100644 index 000000000..a88e71c33 --- /dev/null +++ b/include/libuv/src/unix/loop.c @@ -0,0 +1,228 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "uv/tree.h" +#include "internal.h" +#include "heap-inl.h" +#include +#include +#include + +int uv_loop_init(uv_loop_t* loop) { + uv__loop_internal_fields_t* lfields; + void* saved_data; + int err; + + + saved_data = loop->data; + memset(loop, 0, sizeof(*loop)); + loop->data = saved_data; + + lfields = (uv__loop_internal_fields_t*) uv__calloc(1, sizeof(*lfields)); + if (lfields == NULL) + return UV_ENOMEM; + loop->internal_fields = lfields; + + err = uv_mutex_init(&lfields->loop_metrics.lock); + if (err) + goto fail_metrics_mutex_init; + + heap_init((struct heap*) &loop->timer_heap); + QUEUE_INIT(&loop->wq); + QUEUE_INIT(&loop->idle_handles); + QUEUE_INIT(&loop->async_handles); + QUEUE_INIT(&loop->check_handles); + QUEUE_INIT(&loop->prepare_handles); + QUEUE_INIT(&loop->handle_queue); + + loop->active_handles = 0; + loop->active_reqs.count = 0; + loop->nfds = 0; + loop->watchers = NULL; + loop->nwatchers = 0; + QUEUE_INIT(&loop->pending_queue); + QUEUE_INIT(&loop->watcher_queue); + + loop->closing_handles = NULL; + uv__update_time(loop); + loop->async_io_watcher.fd = -1; + loop->async_wfd = -1; + loop->signal_pipefd[0] = -1; + loop->signal_pipefd[1] = -1; + loop->backend_fd = -1; + loop->emfile_fd = -1; + + loop->timer_counter = 0; + loop->stop_flag = 0; + + err = uv__platform_loop_init(loop); + if (err) + goto fail_platform_init; + + uv__signal_global_once_init(); + err = uv_signal_init(loop, &loop->child_watcher); + if (err) + goto fail_signal_init; + + uv__handle_unref(&loop->child_watcher); + loop->child_watcher.flags |= UV_HANDLE_INTERNAL; + QUEUE_INIT(&loop->process_handles); + + err = uv_rwlock_init(&loop->cloexec_lock); + if (err) + goto fail_rwlock_init; + + err = uv_mutex_init(&loop->wq_mutex); + if (err) + goto fail_mutex_init; + + err = uv_async_init(loop, &loop->wq_async, uv__work_done); + if (err) + goto fail_async_init; + + uv__handle_unref(&loop->wq_async); + loop->wq_async.flags |= UV_HANDLE_INTERNAL; + + return 0; + +fail_async_init: + uv_mutex_destroy(&loop->wq_mutex); + +fail_mutex_init: + uv_rwlock_destroy(&loop->cloexec_lock); + +fail_rwlock_init: + uv__signal_loop_cleanup(loop); + +fail_signal_init: + uv__platform_loop_delete(loop); + +fail_platform_init: + uv_mutex_destroy(&lfields->loop_metrics.lock); + +fail_metrics_mutex_init: + uv__free(lfields); + loop->internal_fields = NULL; + + uv__free(loop->watchers); + loop->nwatchers = 0; + return err; +} + + +int uv_loop_fork(uv_loop_t* loop) { + int err; + unsigned int i; + uv__io_t* w; + + err = uv__io_fork(loop); + if (err) + return err; + + err = uv__async_fork(loop); + if (err) + return err; + + err = uv__signal_loop_fork(loop); + if (err) + return err; + + /* Rearm all the watchers that aren't re-queued by the above. */ + for (i = 0; i < loop->nwatchers; i++) { + w = loop->watchers[i]; + if (w == NULL) + continue; + + if (w->pevents != 0 && QUEUE_EMPTY(&w->watcher_queue)) { + w->events = 0; /* Force re-registration in uv__io_poll. */ + QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue); + } + } + + return 0; +} + + +void uv__loop_close(uv_loop_t* loop) { + uv__loop_internal_fields_t* lfields; + + uv__signal_loop_cleanup(loop); + uv__platform_loop_delete(loop); + uv__async_stop(loop); + + if (loop->emfile_fd != -1) { + uv__close(loop->emfile_fd); + loop->emfile_fd = -1; + } + + if (loop->backend_fd != -1) { + uv__close(loop->backend_fd); + loop->backend_fd = -1; + } + + uv_mutex_lock(&loop->wq_mutex); + assert(QUEUE_EMPTY(&loop->wq) && "thread pool work queue not empty!"); + assert(!uv__has_active_reqs(loop)); + uv_mutex_unlock(&loop->wq_mutex); + uv_mutex_destroy(&loop->wq_mutex); + + /* + * Note that all thread pool stuff is finished at this point and + * it is safe to just destroy rw lock + */ + uv_rwlock_destroy(&loop->cloexec_lock); + +#if 0 + assert(QUEUE_EMPTY(&loop->pending_queue)); + assert(QUEUE_EMPTY(&loop->watcher_queue)); + assert(loop->nfds == 0); +#endif + + uv__free(loop->watchers); + loop->watchers = NULL; + loop->nwatchers = 0; + + lfields = uv__get_internal_fields(loop); + uv_mutex_destroy(&lfields->loop_metrics.lock); + uv__free(lfields); + loop->internal_fields = NULL; +} + + +int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) { + uv__loop_internal_fields_t* lfields; + + lfields = uv__get_internal_fields(loop); + if (option == UV_METRICS_IDLE_TIME) { + lfields->flags |= UV_METRICS_IDLE_TIME; + return 0; + } + + if (option != UV_LOOP_BLOCK_SIGNAL) + return UV_ENOSYS; + + if (va_arg(ap, int) != SIGPROF) + return UV_EINVAL; + + loop->flags |= UV_LOOP_BLOCK_SIGPROF; + return 0; +} diff --git a/include/libuv/src/unix/netbsd.c b/include/libuv/src/unix/netbsd.c new file mode 100644 index 000000000..c66333f52 --- /dev/null +++ b/include/libuv/src/unix/netbsd.c @@ -0,0 +1,259 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + + +int uv__platform_loop_init(uv_loop_t* loop) { + return uv__kqueue_init(loop); +} + + +void uv__platform_loop_delete(uv_loop_t* loop) { +} + + +void uv_loadavg(double avg[3]) { + struct loadavg info; + size_t size = sizeof(info); + int which[] = {CTL_VM, VM_LOADAVG}; + + if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0) == -1) return; + + avg[0] = (double) info.ldavg[0] / info.fscale; + avg[1] = (double) info.ldavg[1] / info.fscale; + avg[2] = (double) info.ldavg[2] / info.fscale; +} + + +int uv_exepath(char* buffer, size_t* size) { + /* Intermediate buffer, retrieving partial path name does not work + * As of NetBSD-8(beta), vnode->path translator does not handle files + * with longer names than 31 characters. + */ + char int_buf[PATH_MAX]; + size_t int_size; + int mib[4]; + + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC_ARGS; + mib[2] = -1; + mib[3] = KERN_PROC_PATHNAME; + int_size = ARRAY_SIZE(int_buf); + + if (sysctl(mib, 4, int_buf, &int_size, NULL, 0)) + return UV__ERR(errno); + + /* Copy string from the intermediate buffer to outer one with appropriate + * length. + */ + /* TODO(bnoordhuis) Check uv__strscpy() return value. */ + uv__strscpy(buffer, int_buf, *size); + + /* Set new size. */ + *size = strlen(buffer); + + return 0; +} + + +uint64_t uv_get_free_memory(void) { + struct uvmexp info; + size_t size = sizeof(info); + int which[] = {CTL_VM, VM_UVMEXP}; + + if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) + return UV__ERR(errno); + + return (uint64_t) info.free * sysconf(_SC_PAGESIZE); +} + + +uint64_t uv_get_total_memory(void) { +#if defined(HW_PHYSMEM64) + uint64_t info; + int which[] = {CTL_HW, HW_PHYSMEM64}; +#else + unsigned int info; + int which[] = {CTL_HW, HW_PHYSMEM}; +#endif + size_t size = sizeof(info); + + if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) + return UV__ERR(errno); + + return (uint64_t) info; +} + + +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + +int uv_resident_set_memory(size_t* rss) { + kvm_t *kd = NULL; + struct kinfo_proc2 *kinfo = NULL; + pid_t pid; + int nprocs; + int max_size = sizeof(struct kinfo_proc2); + int page_size; + + page_size = getpagesize(); + pid = getpid(); + + kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, "kvm_open"); + + if (kd == NULL) goto error; + + kinfo = kvm_getproc2(kd, KERN_PROC_PID, pid, max_size, &nprocs); + if (kinfo == NULL) goto error; + + *rss = kinfo->p_vm_rssize * page_size; + + kvm_close(kd); + + return 0; + +error: + if (kd) kvm_close(kd); + return UV_EPERM; +} + + +int uv_uptime(double* uptime) { + time_t now; + struct timeval info; + size_t size = sizeof(info); + static int which[] = {CTL_KERN, KERN_BOOTTIME}; + + if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) + return UV__ERR(errno); + + now = time(NULL); + + *uptime = (double)(now - info.tv_sec); + return 0; +} + + +int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK); + unsigned int multiplier = ((uint64_t)1000L / ticks); + unsigned int cur = 0; + uv_cpu_info_t* cpu_info; + u_int64_t* cp_times; + char model[512]; + u_int64_t cpuspeed; + int numcpus; + size_t size; + int i; + + size = sizeof(model); + if (sysctlbyname("machdep.cpu_brand", &model, &size, NULL, 0) && + sysctlbyname("hw.model", &model, &size, NULL, 0)) { + return UV__ERR(errno); + } + + size = sizeof(numcpus); + if (sysctlbyname("hw.ncpu", &numcpus, &size, NULL, 0)) + return UV__ERR(errno); + *count = numcpus; + + /* Only i386 and amd64 have machdep.tsc_freq */ + size = sizeof(cpuspeed); + if (sysctlbyname("machdep.tsc_freq", &cpuspeed, &size, NULL, 0)) + cpuspeed = 0; + + size = numcpus * CPUSTATES * sizeof(*cp_times); + cp_times = uv__malloc(size); + if (cp_times == NULL) + return UV_ENOMEM; + + if (sysctlbyname("kern.cp_time", cp_times, &size, NULL, 0)) + return UV__ERR(errno); + + *cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos)); + if (!(*cpu_infos)) { + uv__free(cp_times); + uv__free(*cpu_infos); + return UV_ENOMEM; + } + + for (i = 0; i < numcpus; i++) { + cpu_info = &(*cpu_infos)[i]; + cpu_info->cpu_times.user = (uint64_t)(cp_times[CP_USER+cur]) * multiplier; + cpu_info->cpu_times.nice = (uint64_t)(cp_times[CP_NICE+cur]) * multiplier; + cpu_info->cpu_times.sys = (uint64_t)(cp_times[CP_SYS+cur]) * multiplier; + cpu_info->cpu_times.idle = (uint64_t)(cp_times[CP_IDLE+cur]) * multiplier; + cpu_info->cpu_times.irq = (uint64_t)(cp_times[CP_INTR+cur]) * multiplier; + cpu_info->model = uv__strdup(model); + cpu_info->speed = (int)(cpuspeed/(uint64_t) 1e6); + cur += CPUSTATES; + } + uv__free(cp_times); + return 0; +} + +int uv__random_sysctl(void* buf, size_t len) { + static int name[] = {CTL_KERN, KERN_ARND}; + size_t count, req; + unsigned char* p; + + p = buf; + while (len) { + req = len < 32 ? len : 32; + count = req; + + if (sysctl(name, ARRAY_SIZE(name), p, &count, NULL, 0) == -1) + return UV__ERR(errno); + + if (count != req) + return UV_EIO; /* Can't happen. */ + + p += count; + len -= count; + } + + return 0; +} diff --git a/include/libuv/src/unix/no-fsevents.c b/include/libuv/src/unix/no-fsevents.c new file mode 100644 index 000000000..158643af1 --- /dev/null +++ b/include/libuv/src/unix/no-fsevents.c @@ -0,0 +1,42 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include + +int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { + return UV_ENOSYS; +} + +int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb, + const char* filename, unsigned int flags) { + return UV_ENOSYS; +} + +int uv_fs_event_stop(uv_fs_event_t* handle) { + return UV_ENOSYS; +} + +void uv__fs_event_close(uv_fs_event_t* handle) { + UNREACHABLE(); +} diff --git a/include/libuv/src/unix/no-proctitle.c b/include/libuv/src/unix/no-proctitle.c new file mode 100644 index 000000000..32aa0af1f --- /dev/null +++ b/include/libuv/src/unix/no-proctitle.c @@ -0,0 +1,45 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include + +char** uv_setup_args(int argc, char** argv) { + return argv; +} + +void uv__process_title_cleanup(void) { +} + +int uv_set_process_title(const char* title) { + return 0; +} + +int uv_get_process_title(char* buffer, size_t size) { + if (buffer == NULL || size == 0) + return UV_EINVAL; + + buffer[0] = '\0'; + return 0; +} diff --git a/include/libuv/src/unix/openbsd.c b/include/libuv/src/unix/openbsd.c new file mode 100644 index 000000000..f32a94df3 --- /dev/null +++ b/include/libuv/src/unix/openbsd.c @@ -0,0 +1,240 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +int uv__platform_loop_init(uv_loop_t* loop) { + return uv__kqueue_init(loop); +} + + +void uv__platform_loop_delete(uv_loop_t* loop) { +} + + +void uv_loadavg(double avg[3]) { + struct loadavg info; + size_t size = sizeof(info); + int which[] = {CTL_VM, VM_LOADAVG}; + + if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0) < 0) return; + + avg[0] = (double) info.ldavg[0] / info.fscale; + avg[1] = (double) info.ldavg[1] / info.fscale; + avg[2] = (double) info.ldavg[2] / info.fscale; +} + + +int uv_exepath(char* buffer, size_t* size) { + int mib[4]; + char **argsbuf = NULL; + size_t argsbuf_size = 100U; + size_t exepath_size; + pid_t mypid; + int err; + + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + + mypid = getpid(); + for (;;) { + err = UV_ENOMEM; + argsbuf = uv__reallocf(argsbuf, argsbuf_size); + if (argsbuf == NULL) + goto out; + mib[0] = CTL_KERN; + mib[1] = KERN_PROC_ARGS; + mib[2] = mypid; + mib[3] = KERN_PROC_ARGV; + if (sysctl(mib, ARRAY_SIZE(mib), argsbuf, &argsbuf_size, NULL, 0) == 0) { + break; + } + if (errno != ENOMEM) { + err = UV__ERR(errno); + goto out; + } + argsbuf_size *= 2U; + } + + if (argsbuf[0] == NULL) { + err = UV_EINVAL; /* FIXME(bnoordhuis) More appropriate error. */ + goto out; + } + + *size -= 1; + exepath_size = strlen(argsbuf[0]); + if (*size > exepath_size) + *size = exepath_size; + + memcpy(buffer, argsbuf[0], *size); + buffer[*size] = '\0'; + err = 0; + +out: + uv__free(argsbuf); + + return err; +} + + +uint64_t uv_get_free_memory(void) { + struct uvmexp info; + size_t size = sizeof(info); + int which[] = {CTL_VM, VM_UVMEXP}; + + if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) + return UV__ERR(errno); + + return (uint64_t) info.free * sysconf(_SC_PAGESIZE); +} + + +uint64_t uv_get_total_memory(void) { + uint64_t info; + int which[] = {CTL_HW, HW_PHYSMEM64}; + size_t size = sizeof(info); + + if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) + return UV__ERR(errno); + + return (uint64_t) info; +} + + +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + +int uv_resident_set_memory(size_t* rss) { + struct kinfo_proc kinfo; + size_t page_size = getpagesize(); + size_t size = sizeof(struct kinfo_proc); + int mib[6]; + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + mib[4] = sizeof(struct kinfo_proc); + mib[5] = 1; + + if (sysctl(mib, ARRAY_SIZE(mib), &kinfo, &size, NULL, 0) < 0) + return UV__ERR(errno); + + *rss = kinfo.p_vm_rssize * page_size; + return 0; +} + + +int uv_uptime(double* uptime) { + time_t now; + struct timeval info; + size_t size = sizeof(info); + static int which[] = {CTL_KERN, KERN_BOOTTIME}; + + if (sysctl(which, ARRAY_SIZE(which), &info, &size, NULL, 0)) + return UV__ERR(errno); + + now = time(NULL); + + *uptime = (double)(now - info.tv_sec); + return 0; +} + + +int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK), + multiplier = ((uint64_t)1000L / ticks), cpuspeed; + uint64_t info[CPUSTATES]; + char model[512]; + int numcpus = 1; + int which[] = {CTL_HW,HW_MODEL}; + int percpu[] = {CTL_KERN,KERN_CPTIME2,0}; + size_t size; + int i, j; + uv_cpu_info_t* cpu_info; + + size = sizeof(model); + if (sysctl(which, ARRAY_SIZE(which), &model, &size, NULL, 0)) + return UV__ERR(errno); + + which[1] = HW_NCPUONLINE; + size = sizeof(numcpus); + if (sysctl(which, ARRAY_SIZE(which), &numcpus, &size, NULL, 0)) + return UV__ERR(errno); + + *cpu_infos = uv__malloc(numcpus * sizeof(**cpu_infos)); + if (!(*cpu_infos)) + return UV_ENOMEM; + + i = 0; + *count = numcpus; + + which[1] = HW_CPUSPEED; + size = sizeof(cpuspeed); + if (sysctl(which, ARRAY_SIZE(which), &cpuspeed, &size, NULL, 0)) + goto error; + + size = sizeof(info); + for (i = 0; i < numcpus; i++) { + percpu[2] = i; + if (sysctl(percpu, ARRAY_SIZE(percpu), &info, &size, NULL, 0)) + goto error; + + cpu_info = &(*cpu_infos)[i]; + + cpu_info->cpu_times.user = (uint64_t)(info[CP_USER]) * multiplier; + cpu_info->cpu_times.nice = (uint64_t)(info[CP_NICE]) * multiplier; + cpu_info->cpu_times.sys = (uint64_t)(info[CP_SYS]) * multiplier; + cpu_info->cpu_times.idle = (uint64_t)(info[CP_IDLE]) * multiplier; + cpu_info->cpu_times.irq = (uint64_t)(info[CP_INTR]) * multiplier; + + cpu_info->model = uv__strdup(model); + cpu_info->speed = cpuspeed; + } + + return 0; + +error: + *count = 0; + for (j = 0; j < i; j++) + uv__free((*cpu_infos)[j].model); + + uv__free(*cpu_infos); + *cpu_infos = NULL; + return UV__ERR(errno); +} diff --git a/include/libuv/src/unix/os390-syscalls.c b/include/libuv/src/unix/os390-syscalls.c new file mode 100644 index 000000000..491e950c5 --- /dev/null +++ b/include/libuv/src/unix/os390-syscalls.c @@ -0,0 +1,584 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + + +#include "os390-syscalls.h" +#include +#include +#include +#include +#include + +#define CW_INTRPT 1 +#define CW_CONDVAR 32 + +#pragma linkage(BPX4CTW, OS) +#pragma linkage(BPX1CTW, OS) + +static QUEUE global_epoll_queue; +static uv_mutex_t global_epoll_lock; +static uv_once_t once = UV_ONCE_INIT; + +int scandir(const char* maindir, struct dirent*** namelist, + int (*filter)(const struct dirent*), + int (*compar)(const struct dirent**, + const struct dirent **)) { + struct dirent** nl; + struct dirent** nl_copy; + struct dirent* dirent; + unsigned count; + size_t allocated; + DIR* mdir; + + nl = NULL; + count = 0; + allocated = 0; + mdir = opendir(maindir); + if (!mdir) + return -1; + + while (1) { + dirent = readdir(mdir); + if (!dirent) + break; + if (!filter || filter(dirent)) { + struct dirent* copy; + copy = uv__malloc(sizeof(*copy)); + if (!copy) + goto error; + memcpy(copy, dirent, sizeof(*copy)); + + nl_copy = uv__realloc(nl, sizeof(*copy) * (count + 1)); + if (nl_copy == NULL) { + uv__free(copy); + goto error; + } + + nl = nl_copy; + nl[count++] = copy; + } + } + + qsort(nl, count, sizeof(struct dirent *), + (int (*)(const void *, const void *)) compar); + + closedir(mdir); + + *namelist = nl; + return count; + +error: + while (count > 0) { + dirent = nl[--count]; + uv__free(dirent); + } + uv__free(nl); + closedir(mdir); + errno = ENOMEM; + return -1; +} + + +static unsigned int next_power_of_two(unsigned int val) { + val -= 1; + val |= val >> 1; + val |= val >> 2; + val |= val >> 4; + val |= val >> 8; + val |= val >> 16; + val += 1; + return val; +} + + +static void maybe_resize(uv__os390_epoll* lst, unsigned int len) { + unsigned int newsize; + unsigned int i; + struct pollfd* newlst; + struct pollfd event; + + if (len <= lst->size) + return; + + if (lst->size == 0) + event.fd = -1; + else { + /* Extract the message queue at the end. */ + event = lst->items[lst->size - 1]; + lst->items[lst->size - 1].fd = -1; + } + + newsize = next_power_of_two(len); + newlst = uv__reallocf(lst->items, newsize * sizeof(lst->items[0])); + + if (newlst == NULL) + abort(); + for (i = lst->size; i < newsize; ++i) + newlst[i].fd = -1; + + /* Restore the message queue at the end */ + newlst[newsize - 1] = event; + + lst->items = newlst; + lst->size = newsize; +} + + +static void init_message_queue(uv__os390_epoll* lst) { + struct { + long int header; + char body; + } msg; + + /* initialize message queue */ + lst->msg_queue = msgget(IPC_PRIVATE, 0600 | IPC_CREAT); + if (lst->msg_queue == -1) + abort(); + + /* + On z/OS, the message queue will be affiliated with the process only + when a send is performed on it. Once this is done, the system + can be queried for all message queues belonging to our process id. + */ + msg.header = 1; + if (msgsnd(lst->msg_queue, &msg, sizeof(msg.body), 0) != 0) + abort(); + + /* Clean up the dummy message sent above */ + if (msgrcv(lst->msg_queue, &msg, sizeof(msg.body), 0, 0) != sizeof(msg.body)) + abort(); +} + + +static void before_fork(void) { + uv_mutex_lock(&global_epoll_lock); +} + + +static void after_fork(void) { + uv_mutex_unlock(&global_epoll_lock); +} + + +static void child_fork(void) { + QUEUE* q; + uv_once_t child_once = UV_ONCE_INIT; + + /* reset once */ + memcpy(&once, &child_once, sizeof(child_once)); + + /* reset epoll list */ + while (!QUEUE_EMPTY(&global_epoll_queue)) { + uv__os390_epoll* lst; + q = QUEUE_HEAD(&global_epoll_queue); + QUEUE_REMOVE(q); + lst = QUEUE_DATA(q, uv__os390_epoll, member); + uv__free(lst->items); + lst->items = NULL; + lst->size = 0; + } + + uv_mutex_unlock(&global_epoll_lock); + uv_mutex_destroy(&global_epoll_lock); +} + + +static void epoll_init(void) { + QUEUE_INIT(&global_epoll_queue); + if (uv_mutex_init(&global_epoll_lock)) + abort(); + + if (pthread_atfork(&before_fork, &after_fork, &child_fork)) + abort(); +} + + +uv__os390_epoll* epoll_create1(int flags) { + uv__os390_epoll* lst; + + lst = uv__malloc(sizeof(*lst)); + if (lst != NULL) { + /* initialize list */ + lst->size = 0; + lst->items = NULL; + init_message_queue(lst); + maybe_resize(lst, 1); + lst->items[lst->size - 1].fd = lst->msg_queue; + lst->items[lst->size - 1].events = POLLIN; + lst->items[lst->size - 1].revents = 0; + uv_once(&once, epoll_init); + uv_mutex_lock(&global_epoll_lock); + QUEUE_INSERT_TAIL(&global_epoll_queue, &lst->member); + uv_mutex_unlock(&global_epoll_lock); + } + + return lst; +} + + +int epoll_ctl(uv__os390_epoll* lst, + int op, + int fd, + struct epoll_event *event) { + uv_mutex_lock(&global_epoll_lock); + + if (op == EPOLL_CTL_DEL) { + if (fd >= lst->size || lst->items[fd].fd == -1) { + uv_mutex_unlock(&global_epoll_lock); + errno = ENOENT; + return -1; + } + lst->items[fd].fd = -1; + } else if (op == EPOLL_CTL_ADD) { + + /* Resizing to 'fd + 1' would expand the list to contain at least + * 'fd'. But we need to guarantee that the last index on the list + * is reserved for the message queue. So specify 'fd + 2' instead. + */ + maybe_resize(lst, fd + 2); + if (lst->items[fd].fd != -1) { + uv_mutex_unlock(&global_epoll_lock); + errno = EEXIST; + return -1; + } + lst->items[fd].fd = fd; + lst->items[fd].events = event->events; + lst->items[fd].revents = 0; + } else if (op == EPOLL_CTL_MOD) { + if (fd >= lst->size - 1 || lst->items[fd].fd == -1) { + uv_mutex_unlock(&global_epoll_lock); + errno = ENOENT; + return -1; + } + lst->items[fd].events = event->events; + lst->items[fd].revents = 0; + } else + abort(); + + uv_mutex_unlock(&global_epoll_lock); + return 0; +} + +#define EP_MAX_PFDS (ULONG_MAX / sizeof(struct pollfd)) +#define EP_MAX_EVENTS (INT_MAX / sizeof(struct epoll_event)) + +int epoll_wait(uv__os390_epoll* lst, struct epoll_event* events, + int maxevents, int timeout) { + nmsgsfds_t size; + struct pollfd* pfds; + int pollret; + int reventcount; + int nevents; + struct pollfd msg_fd; + int i; + + if (!lst || !lst->items || !events) { + errno = EFAULT; + return -1; + } + + if (lst->size > EP_MAX_PFDS) { + errno = EINVAL; + return -1; + } + + if (maxevents <= 0 || maxevents > EP_MAX_EVENTS) { + errno = EINVAL; + return -1; + } + + if (lst->size > 0) + _SET_FDS_MSGS(size, 1, lst->size - 1); + else + _SET_FDS_MSGS(size, 0, 0); + pfds = lst->items; + pollret = poll(pfds, size, timeout); + if (pollret <= 0) + return pollret; + + assert(lst->size > 0); + + pollret = _NFDS(pollret) + _NMSGS(pollret); + + reventcount = 0; + nevents = 0; + msg_fd = pfds[lst->size - 1]; + for (i = 0; + i < lst->size && i < maxevents && reventcount < pollret; ++i) { + struct epoll_event ev; + struct pollfd* pfd; + + pfd = &pfds[i]; + if (pfd->fd == -1 || pfd->revents == 0) + continue; + + ev.fd = pfd->fd; + ev.events = pfd->revents; + ev.is_msg = 0; + if (pfd->revents & POLLIN && pfd->revents & POLLOUT) + reventcount += 2; + else if (pfd->revents & (POLLIN | POLLOUT)) + ++reventcount; + + pfd->revents = 0; + events[nevents++] = ev; + } + + if (msg_fd.revents != 0 && msg_fd.fd != -1) + if (i == lst->size) + events[nevents - 1].is_msg = 1; + + return nevents; +} + + +int epoll_file_close(int fd) { + QUEUE* q; + + uv_once(&once, epoll_init); + uv_mutex_lock(&global_epoll_lock); + QUEUE_FOREACH(q, &global_epoll_queue) { + uv__os390_epoll* lst; + + lst = QUEUE_DATA(q, uv__os390_epoll, member); + if (fd < lst->size && lst->items != NULL && lst->items[fd].fd != -1) + lst->items[fd].fd = -1; + } + + uv_mutex_unlock(&global_epoll_lock); + return 0; +} + +void epoll_queue_close(uv__os390_epoll* lst) { + /* Remove epoll instance from global queue */ + uv_mutex_lock(&global_epoll_lock); + QUEUE_REMOVE(&lst->member); + uv_mutex_unlock(&global_epoll_lock); + + /* Free resources */ + msgctl(lst->msg_queue, IPC_RMID, NULL); + lst->msg_queue = -1; + uv__free(lst->items); + lst->items = NULL; +} + + +int nanosleep(const struct timespec* req, struct timespec* rem) { + unsigned nano; + unsigned seconds; + unsigned events; + unsigned secrem; + unsigned nanorem; + int rv; + int err; + int rsn; + + nano = (int)req->tv_nsec; + seconds = req->tv_sec; + events = CW_CONDVAR | CW_INTRPT; + secrem = 0; + nanorem = 0; + +#if defined(_LP64) + BPX4CTW(&seconds, &nano, &events, &secrem, &nanorem, &rv, &err, &rsn); +#else + BPX1CTW(&seconds, &nano, &events, &secrem, &nanorem, &rv, &err, &rsn); +#endif + + /* Don't clobber errno unless BPX1CTW/BPX4CTW errored. + * Don't leak EAGAIN, that just means the timeout expired. + */ + if (rv == -1) + if (err == EAGAIN) + rv = 0; + else + errno = err; + + if (rem != NULL && (rv == 0 || err == EINTR)) { + rem->tv_nsec = nanorem; + rem->tv_sec = secrem; + } + + return rv; +} + + +char* mkdtemp(char* path) { + static const char* tempchars = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + static const size_t num_chars = 62; + static const size_t num_x = 6; + char *ep, *cp; + unsigned int tries, i; + size_t len; + uint64_t v; + int fd; + int retval; + int saved_errno; + + len = strlen(path); + ep = path + len; + if (len < num_x || strncmp(ep - num_x, "XXXXXX", num_x)) { + errno = EINVAL; + return NULL; + } + + fd = open("/dev/urandom", O_RDONLY); + if (fd == -1) + return NULL; + + tries = TMP_MAX; + retval = -1; + do { + if (read(fd, &v, sizeof(v)) != sizeof(v)) + break; + + cp = ep - num_x; + for (i = 0; i < num_x; i++) { + *cp++ = tempchars[v % num_chars]; + v /= num_chars; + } + + if (mkdir(path, S_IRWXU) == 0) { + retval = 0; + break; + } + else if (errno != EEXIST) + break; + } while (--tries); + + saved_errno = errno; + uv__close(fd); + if (tries == 0) { + errno = EEXIST; + return NULL; + } + + if (retval == -1) { + errno = saved_errno; + return NULL; + } + + return path; +} + + +ssize_t os390_readlink(const char* path, char* buf, size_t len) { + ssize_t rlen; + ssize_t vlen; + ssize_t plen; + char* delimiter; + char old_delim; + char* tmpbuf; + char realpathstr[PATH_MAX + 1]; + + tmpbuf = uv__malloc(len + 1); + if (tmpbuf == NULL) { + errno = ENOMEM; + return -1; + } + + rlen = readlink(path, tmpbuf, len); + if (rlen < 0) { + uv__free(tmpbuf); + return rlen; + } + + if (rlen < 3 || strncmp("/$", tmpbuf, 2) != 0) { + /* Straightforward readlink. */ + memcpy(buf, tmpbuf, rlen); + uv__free(tmpbuf); + return rlen; + } + + /* + * There is a parmlib variable at the beginning + * which needs interpretation. + */ + tmpbuf[rlen] = '\0'; + delimiter = strchr(tmpbuf + 2, '/'); + if (delimiter == NULL) + /* No slash at the end */ + delimiter = strchr(tmpbuf + 2, '\0'); + + /* Read real path of the variable. */ + old_delim = *delimiter; + *delimiter = '\0'; + if (realpath(tmpbuf, realpathstr) == NULL) { + uv__free(tmpbuf); + return -1; + } + + /* realpathstr is not guaranteed to end with null byte.*/ + realpathstr[PATH_MAX] = '\0'; + + /* Reset the delimiter and fill up the buffer. */ + *delimiter = old_delim; + plen = strlen(delimiter); + vlen = strlen(realpathstr); + rlen = plen + vlen; + if (rlen > len) { + uv__free(tmpbuf); + errno = ENAMETOOLONG; + return -1; + } + memcpy(buf, realpathstr, vlen); + memcpy(buf + vlen, delimiter, plen); + + /* Done using temporary buffer. */ + uv__free(tmpbuf); + + return rlen; +} + + +size_t strnlen(const char* str, size_t maxlen) { + char* p = memchr(str, 0, maxlen); + if (p == NULL) + return maxlen; + else + return p - str; +} + + +int sem_init(UV_PLATFORM_SEM_T* semid, int pshared, unsigned int value) { + UNREACHABLE(); +} + + +int sem_destroy(UV_PLATFORM_SEM_T* semid) { + UNREACHABLE(); +} + + +int sem_post(UV_PLATFORM_SEM_T* semid) { + UNREACHABLE(); +} + + +int sem_trywait(UV_PLATFORM_SEM_T* semid) { + UNREACHABLE(); +} + + +int sem_wait(UV_PLATFORM_SEM_T* semid) { + UNREACHABLE(); +} diff --git a/include/libuv/src/unix/os390-syscalls.h b/include/libuv/src/unix/os390-syscalls.h new file mode 100644 index 000000000..86416bbc5 --- /dev/null +++ b/include/libuv/src/unix/os390-syscalls.h @@ -0,0 +1,74 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + + +#ifndef UV_OS390_SYSCALL_H_ +#define UV_OS390_SYSCALL_H_ + +#include "uv.h" +#include "internal.h" +#include +#include +#include + +#define EPOLL_CTL_ADD 1 +#define EPOLL_CTL_DEL 2 +#define EPOLL_CTL_MOD 3 +#define MAX_EPOLL_INSTANCES 256 +#define MAX_ITEMS_PER_EPOLL 1024 + +#define UV__O_CLOEXEC 0x80000 + +struct epoll_event { + int events; + int fd; + int is_msg; +}; + +typedef struct { + QUEUE member; + struct pollfd* items; + unsigned long size; + int msg_queue; +} uv__os390_epoll; + +/* epoll api */ +uv__os390_epoll* epoll_create1(int flags); +int epoll_ctl(uv__os390_epoll* ep, int op, int fd, struct epoll_event *event); +int epoll_wait(uv__os390_epoll* ep, struct epoll_event *events, int maxevents, int timeout); +int epoll_file_close(int fd); + +/* utility functions */ +int nanosleep(const struct timespec* req, struct timespec* rem); +int scandir(const char* maindir, struct dirent*** namelist, + int (*filter)(const struct dirent *), + int (*compar)(const struct dirent **, + const struct dirent **)); +char *mkdtemp(char* path); +ssize_t os390_readlink(const char* path, char* buf, size_t len); +size_t strnlen(const char* str, size_t maxlen); +int sem_init(UV_PLATFORM_SEM_T* semid, int pshared, unsigned int value); +int sem_destroy(UV_PLATFORM_SEM_T* semid); +int sem_post(UV_PLATFORM_SEM_T* semid); +int sem_trywait(UV_PLATFORM_SEM_T* semid); +int sem_wait(UV_PLATFORM_SEM_T* semid); + +#endif /* UV_OS390_SYSCALL_H_ */ diff --git a/include/libuv/src/unix/os390.c b/include/libuv/src/unix/os390.c new file mode 100644 index 000000000..3bb44266d --- /dev/null +++ b/include/libuv/src/unix/os390.c @@ -0,0 +1,975 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "internal.h" +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__clang__) +#include "csrsic.h" +#else +#include "//'SYS1.SAMPLIB(CSRSIC)'" +#endif + +#define CVT_PTR 0x10 +#define PSA_PTR 0x00 +#define CSD_OFFSET 0x294 + +/* + Long-term average CPU service used by this logical partition, + in millions of service units per hour. If this value is above + the partition's defined capacity, the partition will be capped. + It is calculated using the physical CPU adjustment factor + (RCTPCPUA) so it may not match other measures of service which + are based on the logical CPU adjustment factor. It is available + if the hardware supports LPAR cluster. +*/ +#define RCTLACS_OFFSET 0xC4 + +/* 32-bit count of alive CPUs. This includes both CPs and IFAs */ +#define CSD_NUMBER_ONLINE_CPUS 0xD4 + +/* Address of system resources manager (SRM) control table */ +#define CVTOPCTP_OFFSET 0x25C + +/* Address of the RCT table */ +#define RMCTRCT_OFFSET 0xE4 + +/* Address of the rsm control and enumeration area. */ +#define CVTRCEP_OFFSET 0x490 + +/* + Number of frames currently available to system. + Excluded are frames backing perm storage, frames offline, and bad frames. +*/ +#define RCEPOOL_OFFSET 0x004 + +/* Total number of frames currently on all available frame queues. */ +#define RCEAFC_OFFSET 0x088 + +/* CPC model length from the CSRSI Service. */ +#define CPCMODEL_LENGTH 16 + +/* Pointer to the home (current) ASCB. */ +#define PSAAOLD 0x224 + +/* Pointer to rsm address space block extension. */ +#define ASCBRSME 0x16C + +/* + NUMBER OF FRAMES CURRENTLY IN USE BY THIS ADDRESS SPACE. + It does not include 2G frames. +*/ +#define RAXFMCT 0x2C + +/* Thread Entry constants */ +#define PGTH_CURRENT 1 +#define PGTH_LEN 26 +#define PGTHAPATH 0x20 +#pragma linkage(BPX4GTH, OS) +#pragma linkage(BPX1GTH, OS) + +/* TOD Clock resolution in nanoseconds */ +#define TOD_RES 4.096 + +typedef unsigned data_area_ptr_assign_type; + +typedef union { + struct { +#if defined(_LP64) + data_area_ptr_assign_type lower; +#endif + data_area_ptr_assign_type assign; + }; + char* deref; +} data_area_ptr; + + +void uv_loadavg(double avg[3]) { + /* TODO: implement the following */ + avg[0] = 0; + avg[1] = 0; + avg[2] = 0; +} + + +int uv__platform_loop_init(uv_loop_t* loop) { + uv__os390_epoll* ep; + + ep = epoll_create1(0); + loop->ep = ep; + if (ep == NULL) + return UV__ERR(errno); + + return 0; +} + + +void uv__platform_loop_delete(uv_loop_t* loop) { + if (loop->ep != NULL) { + epoll_queue_close(loop->ep); + loop->ep = NULL; + } +} + + +uint64_t uv__hrtime(uv_clocktype_t type) { + unsigned long long timestamp; + __stckf(×tamp); + /* Convert to nanoseconds */ + return timestamp / TOD_RES; +} + + +/* + Get the exe path using the thread entry information + in the address space. +*/ +static int getexe(const int pid, char* buf, size_t len) { + struct { + int pid; + int thid[2]; + char accesspid; + char accessthid; + char asid[2]; + char loginname[8]; + char flag; + char len; + } Input_data; + + union { + struct { + char gthb[4]; + int pid; + int thid[2]; + char accesspid; + char accessthid[3]; + int lenused; + int offsetProcess; + int offsetConTTY; + int offsetPath; + int offsetCommand; + int offsetFileData; + int offsetThread; + } Output_data; + char buf[2048]; + } Output_buf; + + struct Output_path_type { + char gthe[4]; + short int len; + char path[1024]; + }; + + int Input_length; + int Output_length; + void* Input_address; + void* Output_address; + struct Output_path_type* Output_path; + int rv; + int rc; + int rsn; + + Input_length = PGTH_LEN; + Output_length = sizeof(Output_buf); + Output_address = &Output_buf; + Input_address = &Input_data; + memset(&Input_data, 0, sizeof Input_data); + Input_data.flag |= PGTHAPATH; + Input_data.pid = pid; + Input_data.accesspid = PGTH_CURRENT; + +#ifdef _LP64 + BPX4GTH(&Input_length, + &Input_address, + &Output_length, + &Output_address, + &rv, + &rc, + &rsn); +#else + BPX1GTH(&Input_length, + &Input_address, + &Output_length, + &Output_address, + &rv, + &rc, + &rsn); +#endif + + if (rv == -1) { + errno = rc; + return -1; + } + + /* Check highest byte to ensure data availability */ + assert(((Output_buf.Output_data.offsetPath >>24) & 0xFF) == 'A'); + + /* Get the offset from the lowest 3 bytes */ + Output_path = (struct Output_path_type*) ((char*) (&Output_buf) + + (Output_buf.Output_data.offsetPath & 0x00FFFFFF)); + + if (Output_path->len >= len) { + errno = ENOBUFS; + return -1; + } + + uv__strscpy(buf, Output_path->path, len); + + return 0; +} + + +/* + * We could use a static buffer for the path manipulations that we need outside + * of the function, but this function could be called by multiple consumers and + * we don't want to potentially create a race condition in the use of snprintf. + * There is no direct way of getting the exe path in zOS - either through /procfs + * or through some libc APIs. The below approach is to parse the argv[0]'s pattern + * and use it in conjunction with PATH environment variable to craft one. + */ +int uv_exepath(char* buffer, size_t* size) { + int res; + char args[PATH_MAX]; + int pid; + + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + + pid = getpid(); + res = getexe(pid, args, sizeof(args)); + if (res < 0) + return UV_EINVAL; + + return uv__search_path(args, buffer, size); +} + + +uint64_t uv_get_free_memory(void) { + uint64_t freeram; + + data_area_ptr cvt = {0}; + data_area_ptr rcep = {0}; + cvt.assign = *(data_area_ptr_assign_type*)(CVT_PTR); + rcep.assign = *(data_area_ptr_assign_type*)(cvt.deref + CVTRCEP_OFFSET); + freeram = *((uint64_t*)(rcep.deref + RCEAFC_OFFSET)) * 4; + return freeram; +} + + +uint64_t uv_get_total_memory(void) { + uint64_t totalram; + + data_area_ptr cvt = {0}; + data_area_ptr rcep = {0}; + cvt.assign = *(data_area_ptr_assign_type*)(CVT_PTR); + rcep.assign = *(data_area_ptr_assign_type*)(cvt.deref + CVTRCEP_OFFSET); + totalram = *((uint64_t*)(rcep.deref + RCEPOOL_OFFSET)) * 4; + return totalram; +} + + +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + +int uv_resident_set_memory(size_t* rss) { + char* ascb; + char* rax; + size_t nframes; + + ascb = *(char* __ptr32 *)(PSA_PTR + PSAAOLD); + rax = *(char* __ptr32 *)(ascb + ASCBRSME); + nframes = *(unsigned int*)(rax + RAXFMCT); + + *rss = nframes * sysconf(_SC_PAGESIZE); + return 0; +} + + +int uv_uptime(double* uptime) { + struct utmpx u ; + struct utmpx *v; + time64_t t; + + u.ut_type = BOOT_TIME; + v = getutxid(&u); + if (v == NULL) + return -1; + *uptime = difftime64(time64(&t), v->ut_tv.tv_sec); + return 0; +} + + +int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + uv_cpu_info_t* cpu_info; + int idx; + siv1v2 info; + data_area_ptr cvt = {0}; + data_area_ptr csd = {0}; + data_area_ptr rmctrct = {0}; + data_area_ptr cvtopctp = {0}; + int cpu_usage_avg; + + cvt.assign = *(data_area_ptr_assign_type*)(CVT_PTR); + + csd.assign = *((data_area_ptr_assign_type *) (cvt.deref + CSD_OFFSET)); + cvtopctp.assign = *((data_area_ptr_assign_type *) (cvt.deref + CVTOPCTP_OFFSET)); + rmctrct.assign = *((data_area_ptr_assign_type *) (cvtopctp.deref + RMCTRCT_OFFSET)); + + *count = *((int*) (csd.deref + CSD_NUMBER_ONLINE_CPUS)); + cpu_usage_avg = *((unsigned short int*) (rmctrct.deref + RCTLACS_OFFSET)); + + *cpu_infos = uv__malloc(*count * sizeof(uv_cpu_info_t)); + if (!*cpu_infos) + return UV_ENOMEM; + + cpu_info = *cpu_infos; + idx = 0; + while (idx < *count) { + cpu_info->speed = *(int*)(info.siv1v2si22v1.si22v1cpucapability); + cpu_info->model = uv__malloc(CPCMODEL_LENGTH + 1); + memset(cpu_info->model, '\0', CPCMODEL_LENGTH + 1); + memcpy(cpu_info->model, info.siv1v2si11v1.si11v1cpcmodel, CPCMODEL_LENGTH); + cpu_info->cpu_times.user = cpu_usage_avg; + /* TODO: implement the following */ + cpu_info->cpu_times.sys = 0; + cpu_info->cpu_times.idle = 0; + cpu_info->cpu_times.irq = 0; + cpu_info->cpu_times.nice = 0; + ++cpu_info; + ++idx; + } + + return 0; +} + + +static int uv__interface_addresses_v6(uv_interface_address_t** addresses, + int* count) { + uv_interface_address_t* address; + int sockfd; + int maxsize; + __net_ifconf6header_t ifc; + __net_ifconf6entry_t* ifr; + __net_ifconf6entry_t* p; + __net_ifconf6entry_t flg; + + *count = 0; + /* Assume maximum buffer size allowable */ + maxsize = 16384; + + if (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP))) + return UV__ERR(errno); + + ifc.__nif6h_version = 1; + ifc.__nif6h_buflen = maxsize; + ifc.__nif6h_buffer = uv__calloc(1, maxsize);; + + if (ioctl(sockfd, SIOCGIFCONF6, &ifc) == -1) { + uv__close(sockfd); + return UV__ERR(errno); + } + + + *count = 0; + ifr = (__net_ifconf6entry_t*)(ifc.__nif6h_buffer); + while ((char*)ifr < (char*)ifc.__nif6h_buffer + ifc.__nif6h_buflen) { + p = ifr; + ifr = (__net_ifconf6entry_t*)((char*)ifr + ifc.__nif6h_entrylen); + + if (!(p->__nif6e_addr.sin6_family == AF_INET6 || + p->__nif6e_addr.sin6_family == AF_INET)) + continue; + + if (!(p->__nif6e_flags & _NIF6E_FLAGS_ON_LINK_ACTIVE)) + continue; + + ++(*count); + } + + /* Alloc the return interface structs */ + *addresses = uv__malloc(*count * sizeof(uv_interface_address_t)); + if (!(*addresses)) { + uv__close(sockfd); + return UV_ENOMEM; + } + address = *addresses; + + ifr = (__net_ifconf6entry_t*)(ifc.__nif6h_buffer); + while ((char*)ifr < (char*)ifc.__nif6h_buffer + ifc.__nif6h_buflen) { + p = ifr; + ifr = (__net_ifconf6entry_t*)((char*)ifr + ifc.__nif6h_entrylen); + + if (!(p->__nif6e_addr.sin6_family == AF_INET6 || + p->__nif6e_addr.sin6_family == AF_INET)) + continue; + + if (!(p->__nif6e_flags & _NIF6E_FLAGS_ON_LINK_ACTIVE)) + continue; + + /* All conditions above must match count loop */ + + address->name = uv__strdup(p->__nif6e_name); + + if (p->__nif6e_addr.sin6_family == AF_INET6) + address->address.address6 = *((struct sockaddr_in6*) &p->__nif6e_addr); + else + address->address.address4 = *((struct sockaddr_in*) &p->__nif6e_addr); + + /* TODO: Retrieve netmask using SIOCGIFNETMASK ioctl */ + + address->is_internal = flg.__nif6e_flags & _NIF6E_FLAGS_LOOPBACK ? 1 : 0; + memset(address->phys_addr, 0, sizeof(address->phys_addr)); + address++; + } + + uv__close(sockfd); + return 0; +} + + +int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { + uv_interface_address_t* address; + int sockfd; + int maxsize; + struct ifconf ifc; + struct ifreq flg; + struct ifreq* ifr; + struct ifreq* p; + int count_v6; + + *count = 0; + *addresses = NULL; + + /* get the ipv6 addresses first */ + uv_interface_address_t* addresses_v6; + uv__interface_addresses_v6(&addresses_v6, &count_v6); + + /* now get the ipv4 addresses */ + + /* Assume maximum buffer size allowable */ + maxsize = 16384; + + sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); + if (0 > sockfd) + return UV__ERR(errno); + + ifc.ifc_req = uv__calloc(1, maxsize); + ifc.ifc_len = maxsize; + if (ioctl(sockfd, SIOCGIFCONF, &ifc) == -1) { + uv__close(sockfd); + return UV__ERR(errno); + } + +#define MAX(a,b) (((a)>(b))?(a):(b)) +#define ADDR_SIZE(p) MAX((p).sa_len, sizeof(p)) + + /* Count all up and running ipv4/ipv6 addresses */ + ifr = ifc.ifc_req; + while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) { + p = ifr; + ifr = (struct ifreq*) + ((char*)ifr + sizeof(ifr->ifr_name) + ADDR_SIZE(ifr->ifr_addr)); + + if (!(p->ifr_addr.sa_family == AF_INET6 || + p->ifr_addr.sa_family == AF_INET)) + continue; + + memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name)); + if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) { + uv__close(sockfd); + return UV__ERR(errno); + } + + if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING)) + continue; + + (*count)++; + } + + if (*count == 0) { + uv__close(sockfd); + return 0; + } + + /* Alloc the return interface structs */ + *addresses = uv__malloc((*count + count_v6) * + sizeof(uv_interface_address_t)); + + if (!(*addresses)) { + uv__close(sockfd); + return UV_ENOMEM; + } + address = *addresses; + + /* copy over the ipv6 addresses */ + memcpy(address, addresses_v6, count_v6 * sizeof(uv_interface_address_t)); + address += count_v6; + *count += count_v6; + uv__free(addresses_v6); + + ifr = ifc.ifc_req; + while ((char*)ifr < (char*)ifc.ifc_req + ifc.ifc_len) { + p = ifr; + ifr = (struct ifreq*) + ((char*)ifr + sizeof(ifr->ifr_name) + ADDR_SIZE(ifr->ifr_addr)); + + if (!(p->ifr_addr.sa_family == AF_INET6 || + p->ifr_addr.sa_family == AF_INET)) + continue; + + memcpy(flg.ifr_name, p->ifr_name, sizeof(flg.ifr_name)); + if (ioctl(sockfd, SIOCGIFFLAGS, &flg) == -1) { + uv__close(sockfd); + return UV_ENOSYS; + } + + if (!(flg.ifr_flags & IFF_UP && flg.ifr_flags & IFF_RUNNING)) + continue; + + /* All conditions above must match count loop */ + + address->name = uv__strdup(p->ifr_name); + + if (p->ifr_addr.sa_family == AF_INET6) { + address->address.address6 = *((struct sockaddr_in6*) &p->ifr_addr); + } else { + address->address.address4 = *((struct sockaddr_in*) &p->ifr_addr); + } + + address->is_internal = flg.ifr_flags & IFF_LOOPBACK ? 1 : 0; + memset(address->phys_addr, 0, sizeof(address->phys_addr)); + address++; + } + +#undef ADDR_SIZE +#undef MAX + + uv__close(sockfd); + return 0; +} + + +void uv_free_interface_addresses(uv_interface_address_t* addresses, + int count) { + int i; + for (i = 0; i < count; ++i) + uv__free(addresses[i].name); + uv__free(addresses); +} + + +void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { + struct epoll_event* events; + struct epoll_event dummy; + uintptr_t i; + uintptr_t nfds; + + assert(loop->watchers != NULL); + assert(fd >= 0); + + events = (struct epoll_event*) loop->watchers[loop->nwatchers]; + nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; + if (events != NULL) + /* Invalidate events with same file descriptor */ + for (i = 0; i < nfds; i++) + if ((int) events[i].fd == fd) + events[i].fd = -1; + + /* Remove the file descriptor from the epoll. */ + if (loop->ep != NULL) + epoll_ctl(loop->ep, EPOLL_CTL_DEL, fd, &dummy); +} + + +int uv__io_check_fd(uv_loop_t* loop, int fd) { + struct pollfd p[1]; + int rv; + + p[0].fd = fd; + p[0].events = POLLIN; + + do + rv = poll(p, 1, 0); + while (rv == -1 && errno == EINTR); + + if (rv == -1) + abort(); + + if (p[0].revents & POLLNVAL) + return -1; + + return 0; +} + + +void uv__fs_event_close(uv_fs_event_t* handle) { + uv_fs_event_stop(handle); +} + + +int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { + uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT); + return 0; +} + + +int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb, + const char* filename, unsigned int flags) { + uv__os390_epoll* ep; + _RFIS reg_struct; + char* path; + int rc; + + if (uv__is_active(handle)) + return UV_EINVAL; + + ep = handle->loop->ep; + assert(ep->msg_queue != -1); + + reg_struct.__rfis_cmd = _RFIS_REG; + reg_struct.__rfis_qid = ep->msg_queue; + reg_struct.__rfis_type = 1; + memcpy(reg_struct.__rfis_utok, &handle, sizeof(handle)); + + path = uv__strdup(filename); + if (path == NULL) + return UV_ENOMEM; + + rc = __w_pioctl(path, _IOCC_REGFILEINT, sizeof(reg_struct), ®_struct); + if (rc != 0) + return UV__ERR(errno); + + uv__handle_start(handle); + handle->path = path; + handle->cb = cb; + memcpy(handle->rfis_rftok, reg_struct.__rfis_rftok, + sizeof(handle->rfis_rftok)); + + return 0; +} + + +int uv_fs_event_stop(uv_fs_event_t* handle) { + uv__os390_epoll* ep; + _RFIS reg_struct; + int rc; + + if (!uv__is_active(handle)) + return 0; + + ep = handle->loop->ep; + assert(ep->msg_queue != -1); + + reg_struct.__rfis_cmd = _RFIS_UNREG; + reg_struct.__rfis_qid = ep->msg_queue; + reg_struct.__rfis_type = 1; + memcpy(reg_struct.__rfis_rftok, handle->rfis_rftok, + sizeof(handle->rfis_rftok)); + + /* + * This call will take "/" as the path argument in case we + * don't care to supply the correct path. The system will simply + * ignore it. + */ + rc = __w_pioctl("/", _IOCC_REGFILEINT, sizeof(reg_struct), ®_struct); + if (rc != 0 && errno != EALREADY && errno != ENOENT) + abort(); + + uv__handle_stop(handle); + + return 0; +} + + +static int os390_message_queue_handler(uv__os390_epoll* ep) { + uv_fs_event_t* handle; + int msglen; + int events; + _RFIM msg; + + if (ep->msg_queue == -1) + return 0; + + msglen = msgrcv(ep->msg_queue, &msg, sizeof(msg), 0, IPC_NOWAIT); + + if (msglen == -1 && errno == ENOMSG) + return 0; + + if (msglen == -1) + abort(); + + events = 0; + if (msg.__rfim_event == _RFIM_ATTR || msg.__rfim_event == _RFIM_WRITE) + events = UV_CHANGE; + else if (msg.__rfim_event == _RFIM_RENAME) + events = UV_RENAME; + else + /* Some event that we are not interested in. */ + return 0; + + handle = *(uv_fs_event_t**)(msg.__rfim_utok); + handle->cb(handle, uv__basename_r(handle->path), events, 0); + return 1; +} + + +void uv__io_poll(uv_loop_t* loop, int timeout) { + static const int max_safe_timeout = 1789569; + struct epoll_event events[1024]; + struct epoll_event* pe; + struct epoll_event e; + uv__os390_epoll* ep; + int real_timeout; + QUEUE* q; + uv__io_t* w; + uint64_t base; + int count; + int nfds; + int fd; + int op; + int i; + int user_timeout; + int reset_timeout; + + if (loop->nfds == 0) { + assert(QUEUE_EMPTY(&loop->watcher_queue)); + return; + } + + while (!QUEUE_EMPTY(&loop->watcher_queue)) { + uv_stream_t* stream; + + q = QUEUE_HEAD(&loop->watcher_queue); + QUEUE_REMOVE(q); + QUEUE_INIT(q); + w = QUEUE_DATA(q, uv__io_t, watcher_queue); + + assert(w->pevents != 0); + assert(w->fd >= 0); + + stream= container_of(w, uv_stream_t, io_watcher); + + assert(w->fd < (int) loop->nwatchers); + + e.events = w->pevents; + e.fd = w->fd; + + if (w->events == 0) + op = EPOLL_CTL_ADD; + else + op = EPOLL_CTL_MOD; + + /* XXX Future optimization: do EPOLL_CTL_MOD lazily if we stop watching + * events, skip the syscall and squelch the events after epoll_wait(). + */ + if (epoll_ctl(loop->ep, op, w->fd, &e)) { + if (errno != EEXIST) + abort(); + + assert(op == EPOLL_CTL_ADD); + + /* We've reactivated a file descriptor that's been watched before. */ + if (epoll_ctl(loop->ep, EPOLL_CTL_MOD, w->fd, &e)) + abort(); + } + + w->events = w->pevents; + } + + assert(timeout >= -1); + base = loop->time; + count = 48; /* Benchmarks suggest this gives the best throughput. */ + real_timeout = timeout; + int nevents = 0; + + if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { + reset_timeout = 1; + user_timeout = timeout; + timeout = 0; + } else { + reset_timeout = 0; + } + + nfds = 0; + for (;;) { + /* Only need to set the provider_entry_time if timeout != 0. The function + * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. + */ + if (timeout != 0) + uv__metrics_set_provider_entry_time(loop); + + if (sizeof(int32_t) == sizeof(long) && timeout >= max_safe_timeout) + timeout = max_safe_timeout; + + nfds = epoll_wait(loop->ep, events, + ARRAY_SIZE(events), timeout); + + /* Update loop->time unconditionally. It's tempting to skip the update when + * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the + * operating system didn't reschedule our process while in the syscall. + */ + base = loop->time; + SAVE_ERRNO(uv__update_time(loop)); + if (nfds == 0) { + assert(timeout != -1); + + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + + if (timeout == -1) + continue; + + if (timeout == 0) + return; + + /* We may have been inside the system call for longer than |timeout| + * milliseconds so we need to update the timestamp to avoid drift. + */ + goto update_timeout; + } + + if (nfds == -1) { + + if (errno != EINTR) + abort(); + + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + + if (timeout == -1) + continue; + + if (timeout == 0) + return; + + /* Interrupted by a signal. Update timeout and poll again. */ + goto update_timeout; + } + + + assert(loop->watchers != NULL); + loop->watchers[loop->nwatchers] = (void*) events; + loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds; + for (i = 0; i < nfds; i++) { + pe = events + i; + fd = pe->fd; + + /* Skip invalidated events, see uv__platform_invalidate_fd */ + if (fd == -1) + continue; + + ep = loop->ep; + if (pe->is_msg) { + os390_message_queue_handler(ep); + continue; + } + + assert(fd >= 0); + assert((unsigned) fd < loop->nwatchers); + + w = loop->watchers[fd]; + + if (w == NULL) { + /* File descriptor that we've stopped watching, disarm it. + * + * Ignore all errors because we may be racing with another thread + * when the file descriptor is closed. + */ + epoll_ctl(loop->ep, EPOLL_CTL_DEL, fd, pe); + continue; + } + + /* Give users only events they're interested in. Prevents spurious + * callbacks when previous callback invocation in this loop has stopped + * the current watcher. Also, filters out events that users has not + * requested us to watch. + */ + pe->events &= w->pevents | POLLERR | POLLHUP; + + if (pe->events == POLLERR || pe->events == POLLHUP) + pe->events |= w->pevents & (POLLIN | POLLOUT); + + if (pe->events != 0) { + uv__metrics_update_idle_time(loop); + w->cb(loop, w, pe->events); + nevents++; + } + } + loop->watchers[loop->nwatchers] = NULL; + loop->watchers[loop->nwatchers + 1] = NULL; + + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + + if (nevents != 0) { + if (nfds == ARRAY_SIZE(events) && --count != 0) { + /* Poll for more events but don't block this time. */ + timeout = 0; + continue; + } + return; + } + + if (timeout == 0) + return; + + if (timeout == -1) + continue; + +update_timeout: + assert(timeout > 0); + + real_timeout -= (loop->time - base); + if (real_timeout <= 0) + return; + + timeout = real_timeout; + } +} + +void uv__set_process_title(const char* title) { + /* do nothing */ +} + +int uv__io_fork(uv_loop_t* loop) { + /* + Nullify the msg queue but don't close it because + it is still being used by the parent. + */ + loop->ep = NULL; + + uv__platform_loop_delete(loop); + return uv__platform_loop_init(loop); +} diff --git a/include/libuv/src/unix/pipe.c b/include/libuv/src/unix/pipe.c new file mode 100644 index 000000000..040d57817 --- /dev/null +++ b/include/libuv/src/unix/pipe.c @@ -0,0 +1,381 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include +#include +#include +#include + + +int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc) { + uv__stream_init(loop, (uv_stream_t*)handle, UV_NAMED_PIPE); + handle->shutdown_req = NULL; + handle->connect_req = NULL; + handle->pipe_fname = NULL; + handle->ipc = ipc; + return 0; +} + + +int uv_pipe_bind(uv_pipe_t* handle, const char* name) { + struct sockaddr_un saddr; + const char* pipe_fname; + int sockfd; + int err; + + pipe_fname = NULL; + + /* Already bound? */ + if (uv__stream_fd(handle) >= 0) + return UV_EINVAL; + + /* Make a copy of the file name, it outlives this function's scope. */ + pipe_fname = uv__strdup(name); + if (pipe_fname == NULL) + return UV_ENOMEM; + + /* We've got a copy, don't touch the original any more. */ + name = NULL; + + err = uv__socket(AF_UNIX, SOCK_STREAM, 0); + if (err < 0) + goto err_socket; + sockfd = err; + + memset(&saddr, 0, sizeof saddr); + uv__strscpy(saddr.sun_path, pipe_fname, sizeof(saddr.sun_path)); + saddr.sun_family = AF_UNIX; + + if (bind(sockfd, (struct sockaddr*)&saddr, sizeof saddr)) { + err = UV__ERR(errno); + /* Convert ENOENT to EACCES for compatibility with Windows. */ + if (err == UV_ENOENT) + err = UV_EACCES; + + uv__close(sockfd); + goto err_socket; + } + + /* Success. */ + handle->flags |= UV_HANDLE_BOUND; + handle->pipe_fname = pipe_fname; /* Is a strdup'ed copy. */ + handle->io_watcher.fd = sockfd; + return 0; + +err_socket: + uv__free((void*)pipe_fname); + return err; +} + + +int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) { + if (uv__stream_fd(handle) == -1) + return UV_EINVAL; + + if (handle->ipc) + return UV_EINVAL; + +#if defined(__MVS__) || defined(__PASE__) + /* On zOS, backlog=0 has undefined behaviour */ + /* On IBMi PASE, backlog=0 leads to "Connection refused" error */ + if (backlog == 0) + backlog = 1; + else if (backlog < 0) + backlog = SOMAXCONN; +#endif + + if (listen(uv__stream_fd(handle), backlog)) + return UV__ERR(errno); + + handle->connection_cb = cb; + handle->io_watcher.cb = uv__server_io; + uv__io_start(handle->loop, &handle->io_watcher, POLLIN); + return 0; +} + + +void uv__pipe_close(uv_pipe_t* handle) { + if (handle->pipe_fname) { + /* + * Unlink the file system entity before closing the file descriptor. + * Doing it the other way around introduces a race where our process + * unlinks a socket with the same name that's just been created by + * another thread or process. + */ + unlink(handle->pipe_fname); + uv__free((void*)handle->pipe_fname); + handle->pipe_fname = NULL; + } + + uv__stream_close((uv_stream_t*)handle); +} + + +int uv_pipe_open(uv_pipe_t* handle, uv_file fd) { + int flags; + int mode; + int err; + flags = 0; + + if (uv__fd_exists(handle->loop, fd)) + return UV_EEXIST; + + do + mode = fcntl(fd, F_GETFL); + while (mode == -1 && errno == EINTR); + + if (mode == -1) + return UV__ERR(errno); /* according to docs, must be EBADF */ + + err = uv__nonblock(fd, 1); + if (err) + return err; + +#if defined(__APPLE__) + err = uv__stream_try_select((uv_stream_t*) handle, &fd); + if (err) + return err; +#endif /* defined(__APPLE__) */ + + mode &= O_ACCMODE; + if (mode != O_WRONLY) + flags |= UV_HANDLE_READABLE; + if (mode != O_RDONLY) + flags |= UV_HANDLE_WRITABLE; + + return uv__stream_open((uv_stream_t*)handle, fd, flags); +} + + +void uv_pipe_connect(uv_connect_t* req, + uv_pipe_t* handle, + const char* name, + uv_connect_cb cb) { + struct sockaddr_un saddr; + int new_sock; + int err; + int r; + + new_sock = (uv__stream_fd(handle) == -1); + + if (new_sock) { + err = uv__socket(AF_UNIX, SOCK_STREAM, 0); + if (err < 0) + goto out; + handle->io_watcher.fd = err; + } + + memset(&saddr, 0, sizeof saddr); + uv__strscpy(saddr.sun_path, name, sizeof(saddr.sun_path)); + saddr.sun_family = AF_UNIX; + + do { + r = connect(uv__stream_fd(handle), + (struct sockaddr*)&saddr, sizeof saddr); + } + while (r == -1 && errno == EINTR); + + if (r == -1 && errno != EINPROGRESS) { + err = UV__ERR(errno); +#if defined(__CYGWIN__) || defined(__MSYS__) + /* EBADF is supposed to mean that the socket fd is bad, but + Cygwin reports EBADF instead of ENOTSOCK when the file is + not a socket. We do not expect to see a bad fd here + (e.g. due to new_sock), so translate the error. */ + if (err == UV_EBADF) + err = UV_ENOTSOCK; +#endif + goto out; + } + + err = 0; + if (new_sock) { + err = uv__stream_open((uv_stream_t*)handle, + uv__stream_fd(handle), + UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); + } + + if (err == 0) + uv__io_start(handle->loop, &handle->io_watcher, POLLOUT); + +out: + handle->delayed_error = err; + handle->connect_req = req; + + uv__req_init(handle->loop, req, UV_CONNECT); + req->handle = (uv_stream_t*)handle; + req->cb = cb; + QUEUE_INIT(&req->queue); + + /* Force callback to run on next tick in case of error. */ + if (err) + uv__io_feed(handle->loop, &handle->io_watcher); + +} + + +static int uv__pipe_getsockpeername(const uv_pipe_t* handle, + uv__peersockfunc func, + char* buffer, + size_t* size) { + struct sockaddr_un sa; + socklen_t addrlen; + int err; + + addrlen = sizeof(sa); + memset(&sa, 0, addrlen); + err = uv__getsockpeername((const uv_handle_t*) handle, + func, + (struct sockaddr*) &sa, + (int*) &addrlen); + if (err < 0) { + *size = 0; + return err; + } + +#if defined(__linux__) + if (sa.sun_path[0] == 0) + /* Linux abstract namespace */ + addrlen -= offsetof(struct sockaddr_un, sun_path); + else +#endif + addrlen = strlen(sa.sun_path); + + + if ((size_t)addrlen >= *size) { + *size = addrlen + 1; + return UV_ENOBUFS; + } + + memcpy(buffer, sa.sun_path, addrlen); + *size = addrlen; + + /* only null-terminate if it's not an abstract socket */ + if (buffer[0] != '\0') + buffer[addrlen] = '\0'; + + return 0; +} + + +int uv_pipe_getsockname(const uv_pipe_t* handle, char* buffer, size_t* size) { + return uv__pipe_getsockpeername(handle, getsockname, buffer, size); +} + + +int uv_pipe_getpeername(const uv_pipe_t* handle, char* buffer, size_t* size) { + return uv__pipe_getsockpeername(handle, getpeername, buffer, size); +} + + +void uv_pipe_pending_instances(uv_pipe_t* handle, int count) { +} + + +int uv_pipe_pending_count(uv_pipe_t* handle) { + uv__stream_queued_fds_t* queued_fds; + + if (!handle->ipc) + return 0; + + if (handle->accepted_fd == -1) + return 0; + + if (handle->queued_fds == NULL) + return 1; + + queued_fds = handle->queued_fds; + return queued_fds->offset + 1; +} + + +uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle) { + if (!handle->ipc) + return UV_UNKNOWN_HANDLE; + + if (handle->accepted_fd == -1) + return UV_UNKNOWN_HANDLE; + else + return uv__handle_type(handle->accepted_fd); +} + + +int uv_pipe_chmod(uv_pipe_t* handle, int mode) { + unsigned desired_mode; + struct stat pipe_stat; + char* name_buffer; + size_t name_len; + int r; + + if (handle == NULL || uv__stream_fd(handle) == -1) + return UV_EBADF; + + if (mode != UV_READABLE && + mode != UV_WRITABLE && + mode != (UV_WRITABLE | UV_READABLE)) + return UV_EINVAL; + + /* Unfortunately fchmod does not work on all platforms, we will use chmod. */ + name_len = 0; + r = uv_pipe_getsockname(handle, NULL, &name_len); + if (r != UV_ENOBUFS) + return r; + + name_buffer = uv__malloc(name_len); + if (name_buffer == NULL) + return UV_ENOMEM; + + r = uv_pipe_getsockname(handle, name_buffer, &name_len); + if (r != 0) { + uv__free(name_buffer); + return r; + } + + /* stat must be used as fstat has a bug on Darwin */ + if (stat(name_buffer, &pipe_stat) == -1) { + uv__free(name_buffer); + return -errno; + } + + desired_mode = 0; + if (mode & UV_READABLE) + desired_mode |= S_IRUSR | S_IRGRP | S_IROTH; + if (mode & UV_WRITABLE) + desired_mode |= S_IWUSR | S_IWGRP | S_IWOTH; + + /* Exit early if pipe already has desired mode. */ + if ((pipe_stat.st_mode & desired_mode) == desired_mode) { + uv__free(name_buffer); + return 0; + } + + pipe_stat.st_mode |= desired_mode; + + r = chmod(name_buffer, pipe_stat.st_mode); + uv__free(name_buffer); + + return r != -1 ? 0 : UV__ERR(errno); +} diff --git a/include/libuv/src/unix/poll.c b/include/libuv/src/unix/poll.c new file mode 100644 index 000000000..3d5022b22 --- /dev/null +++ b/include/libuv/src/unix/poll.c @@ -0,0 +1,150 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include + + +static void uv__poll_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { + uv_poll_t* handle; + int pevents; + + handle = container_of(w, uv_poll_t, io_watcher); + + /* + * As documented in the kernel source fs/kernfs/file.c #780 + * poll will return POLLERR|POLLPRI in case of sysfs + * polling. This does not happen in case of out-of-band + * TCP messages. + * + * The above is the case on (at least) FreeBSD and Linux. + * + * So to properly determine a POLLPRI or a POLLERR we need + * to check for both. + */ + if ((events & POLLERR) && !(events & UV__POLLPRI)) { + uv__io_stop(loop, w, POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI); + uv__handle_stop(handle); + handle->poll_cb(handle, UV_EBADF, 0); + return; + } + + pevents = 0; + if (events & POLLIN) + pevents |= UV_READABLE; + if (events & UV__POLLPRI) + pevents |= UV_PRIORITIZED; + if (events & POLLOUT) + pevents |= UV_WRITABLE; + if (events & UV__POLLRDHUP) + pevents |= UV_DISCONNECT; + + handle->poll_cb(handle, 0, pevents); +} + + +int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd) { + int err; + + if (uv__fd_exists(loop, fd)) + return UV_EEXIST; + + err = uv__io_check_fd(loop, fd); + if (err) + return err; + + /* If ioctl(FIONBIO) reports ENOTTY, try fcntl(F_GETFL) + fcntl(F_SETFL). + * Workaround for e.g. kqueue fds not supporting ioctls. + */ + err = uv__nonblock(fd, 1); + if (err == UV_ENOTTY) + if (uv__nonblock == uv__nonblock_ioctl) + err = uv__nonblock_fcntl(fd, 1); + + if (err) + return err; + + uv__handle_init(loop, (uv_handle_t*) handle, UV_POLL); + uv__io_init(&handle->io_watcher, uv__poll_io, fd); + handle->poll_cb = NULL; + return 0; +} + + +int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle, + uv_os_sock_t socket) { + return uv_poll_init(loop, handle, socket); +} + + +static void uv__poll_stop(uv_poll_t* handle) { + uv__io_stop(handle->loop, + &handle->io_watcher, + POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI); + uv__handle_stop(handle); + uv__platform_invalidate_fd(handle->loop, handle->io_watcher.fd); +} + + +int uv_poll_stop(uv_poll_t* handle) { + assert(!uv__is_closing(handle)); + uv__poll_stop(handle); + return 0; +} + + +int uv_poll_start(uv_poll_t* handle, int pevents, uv_poll_cb poll_cb) { + int events; + + assert((pevents & ~(UV_READABLE | UV_WRITABLE | UV_DISCONNECT | + UV_PRIORITIZED)) == 0); + assert(!uv__is_closing(handle)); + + uv__poll_stop(handle); + + if (pevents == 0) + return 0; + + events = 0; + if (pevents & UV_READABLE) + events |= POLLIN; + if (pevents & UV_PRIORITIZED) + events |= UV__POLLPRI; + if (pevents & UV_WRITABLE) + events |= POLLOUT; + if (pevents & UV_DISCONNECT) + events |= UV__POLLRDHUP; + + uv__io_start(handle->loop, &handle->io_watcher, events); + uv__handle_start(handle); + handle->poll_cb = poll_cb; + + return 0; +} + + +void uv__poll_close(uv_poll_t* handle) { + uv__poll_stop(handle); +} diff --git a/include/libuv/src/unix/posix-hrtime.c b/include/libuv/src/unix/posix-hrtime.c new file mode 100644 index 000000000..323dfc203 --- /dev/null +++ b/include/libuv/src/unix/posix-hrtime.c @@ -0,0 +1,35 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include + +#undef NANOSEC +#define NANOSEC ((uint64_t) 1e9) + +uint64_t uv__hrtime(uv_clocktype_t type) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (((uint64_t) ts.tv_sec) * NANOSEC + ts.tv_nsec); +} diff --git a/include/libuv/src/unix/posix-poll.c b/include/libuv/src/unix/posix-poll.c new file mode 100644 index 000000000..0f4bf9387 --- /dev/null +++ b/include/libuv/src/unix/posix-poll.c @@ -0,0 +1,374 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +/* POSIX defines poll() as a portable way to wait on file descriptors. + * Here we maintain a dynamically sized array of file descriptors and + * events to pass as the first argument to poll(). + */ + +#include +#include +#include +#include +#include + +int uv__platform_loop_init(uv_loop_t* loop) { + loop->poll_fds = NULL; + loop->poll_fds_used = 0; + loop->poll_fds_size = 0; + loop->poll_fds_iterating = 0; + return 0; +} + +void uv__platform_loop_delete(uv_loop_t* loop) { + uv__free(loop->poll_fds); + loop->poll_fds = NULL; +} + +int uv__io_fork(uv_loop_t* loop) { + uv__platform_loop_delete(loop); + return uv__platform_loop_init(loop); +} + +/* Allocate or dynamically resize our poll fds array. */ +static void uv__pollfds_maybe_resize(uv_loop_t* loop) { + size_t i; + size_t n; + struct pollfd* p; + + if (loop->poll_fds_used < loop->poll_fds_size) + return; + + n = loop->poll_fds_size ? loop->poll_fds_size * 2 : 64; + p = uv__reallocf(loop->poll_fds, n * sizeof(*loop->poll_fds)); + if (p == NULL) + abort(); + + loop->poll_fds = p; + for (i = loop->poll_fds_size; i < n; i++) { + loop->poll_fds[i].fd = -1; + loop->poll_fds[i].events = 0; + loop->poll_fds[i].revents = 0; + } + loop->poll_fds_size = n; +} + +/* Primitive swap operation on poll fds array elements. */ +static void uv__pollfds_swap(uv_loop_t* loop, size_t l, size_t r) { + struct pollfd pfd; + pfd = loop->poll_fds[l]; + loop->poll_fds[l] = loop->poll_fds[r]; + loop->poll_fds[r] = pfd; +} + +/* Add a watcher's fd to our poll fds array with its pending events. */ +static void uv__pollfds_add(uv_loop_t* loop, uv__io_t* w) { + size_t i; + struct pollfd* pe; + + /* If the fd is already in the set just update its events. */ + assert(!loop->poll_fds_iterating); + for (i = 0; i < loop->poll_fds_used; ++i) { + if (loop->poll_fds[i].fd == w->fd) { + loop->poll_fds[i].events = w->pevents; + return; + } + } + + /* Otherwise, allocate a new slot in the set for the fd. */ + uv__pollfds_maybe_resize(loop); + pe = &loop->poll_fds[loop->poll_fds_used++]; + pe->fd = w->fd; + pe->events = w->pevents; +} + +/* Remove a watcher's fd from our poll fds array. */ +static void uv__pollfds_del(uv_loop_t* loop, int fd) { + size_t i; + assert(!loop->poll_fds_iterating); + for (i = 0; i < loop->poll_fds_used;) { + if (loop->poll_fds[i].fd == fd) { + /* swap to last position and remove */ + --loop->poll_fds_used; + uv__pollfds_swap(loop, i, loop->poll_fds_used); + loop->poll_fds[loop->poll_fds_used].fd = -1; + loop->poll_fds[loop->poll_fds_used].events = 0; + loop->poll_fds[loop->poll_fds_used].revents = 0; + /* This method is called with an fd of -1 to purge the invalidated fds, + * so we may possibly have multiples to remove. + */ + if (-1 != fd) + return; + } else { + /* We must only increment the loop counter when the fds do not match. + * Otherwise, when we are purging an invalidated fd, the value just + * swapped here from the previous end of the array will be skipped. + */ + ++i; + } + } +} + + +void uv__io_poll(uv_loop_t* loop, int timeout) { + sigset_t* pset; + sigset_t set; + uint64_t time_base; + uint64_t time_diff; + QUEUE* q; + uv__io_t* w; + size_t i; + unsigned int nevents; + int nfds; + int have_signals; + struct pollfd* pe; + int fd; + int user_timeout; + int reset_timeout; + + if (loop->nfds == 0) { + assert(QUEUE_EMPTY(&loop->watcher_queue)); + return; + } + + /* Take queued watchers and add their fds to our poll fds array. */ + while (!QUEUE_EMPTY(&loop->watcher_queue)) { + q = QUEUE_HEAD(&loop->watcher_queue); + QUEUE_REMOVE(q); + QUEUE_INIT(q); + + w = QUEUE_DATA(q, uv__io_t, watcher_queue); + assert(w->pevents != 0); + assert(w->fd >= 0); + assert(w->fd < (int) loop->nwatchers); + + uv__pollfds_add(loop, w); + + w->events = w->pevents; + } + + /* Prepare a set of signals to block around poll(), if any. */ + pset = NULL; + if (loop->flags & UV_LOOP_BLOCK_SIGPROF) { + pset = &set; + sigemptyset(pset); + sigaddset(pset, SIGPROF); + } + + assert(timeout >= -1); + time_base = loop->time; + + if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { + reset_timeout = 1; + user_timeout = timeout; + timeout = 0; + } else { + reset_timeout = 0; + } + + /* Loop calls to poll() and processing of results. If we get some + * results from poll() but they turn out not to be interesting to + * our caller then we need to loop around and poll() again. + */ + for (;;) { + /* Only need to set the provider_entry_time if timeout != 0. The function + * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. + */ + if (timeout != 0) + uv__metrics_set_provider_entry_time(loop); + + if (pset != NULL) + if (pthread_sigmask(SIG_BLOCK, pset, NULL)) + abort(); + nfds = poll(loop->poll_fds, (nfds_t)loop->poll_fds_used, timeout); + if (pset != NULL) + if (pthread_sigmask(SIG_UNBLOCK, pset, NULL)) + abort(); + + /* Update loop->time unconditionally. It's tempting to skip the update when + * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the + * operating system didn't reschedule our process while in the syscall. + */ + SAVE_ERRNO(uv__update_time(loop)); + + if (nfds == 0) { + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + if (timeout == -1) + continue; + if (timeout > 0) + goto update_timeout; + } + + assert(timeout != -1); + return; + } + + if (nfds == -1) { + if (errno != EINTR) + abort(); + + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + + if (timeout == -1) + continue; + + if (timeout == 0) + return; + + /* Interrupted by a signal. Update timeout and poll again. */ + goto update_timeout; + } + + /* Tell uv__platform_invalidate_fd not to manipulate our array + * while we are iterating over it. + */ + loop->poll_fds_iterating = 1; + + /* Initialize a count of events that we care about. */ + nevents = 0; + have_signals = 0; + + /* Loop over the entire poll fds array looking for returned events. */ + for (i = 0; i < loop->poll_fds_used; i++) { + pe = loop->poll_fds + i; + fd = pe->fd; + + /* Skip invalidated events, see uv__platform_invalidate_fd. */ + if (fd == -1) + continue; + + assert(fd >= 0); + assert((unsigned) fd < loop->nwatchers); + + w = loop->watchers[fd]; + + if (w == NULL) { + /* File descriptor that we've stopped watching, ignore. */ + uv__platform_invalidate_fd(loop, fd); + continue; + } + + /* Filter out events that user has not requested us to watch + * (e.g. POLLNVAL). + */ + pe->revents &= w->pevents | POLLERR | POLLHUP; + + if (pe->revents != 0) { + /* Run signal watchers last. */ + if (w == &loop->signal_io_watcher) { + have_signals = 1; + } else { + uv__metrics_update_idle_time(loop); + w->cb(loop, w, pe->revents); + } + + nevents++; + } + } + + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + + if (have_signals != 0) { + uv__metrics_update_idle_time(loop); + loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN); + } + + loop->poll_fds_iterating = 0; + + /* Purge invalidated fds from our poll fds array. */ + uv__pollfds_del(loop, -1); + + if (have_signals != 0) + return; /* Event loop should cycle now so don't poll again. */ + + if (nevents != 0) + return; + + if (timeout == 0) + return; + + if (timeout == -1) + continue; + +update_timeout: + assert(timeout > 0); + + time_diff = loop->time - time_base; + if (time_diff >= (uint64_t) timeout) + return; + + timeout -= time_diff; + } +} + +/* Remove the given fd from our poll fds array because no one + * is interested in its events anymore. + */ +void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { + size_t i; + + assert(fd >= 0); + + if (loop->poll_fds_iterating) { + /* uv__io_poll is currently iterating. Just invalidate fd. */ + for (i = 0; i < loop->poll_fds_used; i++) + if (loop->poll_fds[i].fd == fd) { + loop->poll_fds[i].fd = -1; + loop->poll_fds[i].events = 0; + loop->poll_fds[i].revents = 0; + } + } else { + /* uv__io_poll is not iterating. Delete fd from the set. */ + uv__pollfds_del(loop, fd); + } +} + +/* Check whether the given fd is supported by poll(). */ +int uv__io_check_fd(uv_loop_t* loop, int fd) { + struct pollfd p[1]; + int rv; + + p[0].fd = fd; + p[0].events = POLLIN; + + do + rv = poll(p, 1, 0); + while (rv == -1 && (errno == EINTR || errno == EAGAIN)); + + if (rv == -1) + return UV__ERR(errno); + + if (p[0].revents & POLLNVAL) + return UV_EINVAL; + + return 0; +} diff --git a/include/libuv/src/unix/process.c b/include/libuv/src/unix/process.c new file mode 100644 index 000000000..b021aaeba --- /dev/null +++ b/include/libuv/src/unix/process.c @@ -0,0 +1,595 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#if defined(__APPLE__) && !TARGET_OS_IPHONE +# include +# define environ (*_NSGetEnviron()) +#else +extern char **environ; +#endif + +#if defined(__linux__) || defined(__GLIBC__) +# include +#endif + + +static void uv__chld(uv_signal_t* handle, int signum) { + uv_process_t* process; + uv_loop_t* loop; + int exit_status; + int term_signal; + int status; + pid_t pid; + QUEUE pending; + QUEUE* q; + QUEUE* h; + + assert(signum == SIGCHLD); + + QUEUE_INIT(&pending); + loop = handle->loop; + + h = &loop->process_handles; + q = QUEUE_HEAD(h); + while (q != h) { + process = QUEUE_DATA(q, uv_process_t, queue); + q = QUEUE_NEXT(q); + + do + pid = waitpid(process->pid, &status, WNOHANG); + while (pid == -1 && errno == EINTR); + + if (pid == 0) + continue; + + if (pid == -1) { + if (errno != ECHILD) + abort(); + continue; + } + + process->status = status; + QUEUE_REMOVE(&process->queue); + QUEUE_INSERT_TAIL(&pending, &process->queue); + } + + h = &pending; + q = QUEUE_HEAD(h); + while (q != h) { + process = QUEUE_DATA(q, uv_process_t, queue); + q = QUEUE_NEXT(q); + + QUEUE_REMOVE(&process->queue); + QUEUE_INIT(&process->queue); + uv__handle_stop(process); + + if (process->exit_cb == NULL) + continue; + + exit_status = 0; + if (WIFEXITED(process->status)) + exit_status = WEXITSTATUS(process->status); + + term_signal = 0; + if (WIFSIGNALED(process->status)) + term_signal = WTERMSIG(process->status); + + process->exit_cb(process, exit_status, term_signal); + } + assert(QUEUE_EMPTY(&pending)); +} + + +static int uv__make_socketpair(int fds[2]) { +#if defined(__FreeBSD__) || defined(__linux__) + if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds)) + return UV__ERR(errno); + + return 0; +#else + int err; + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) + return UV__ERR(errno); + + err = uv__cloexec(fds[0], 1); + if (err == 0) + err = uv__cloexec(fds[1], 1); + + if (err != 0) { + uv__close(fds[0]); + uv__close(fds[1]); + return UV__ERR(errno); + } + + return 0; +#endif +} + + +int uv__make_pipe(int fds[2], int flags) { +#if defined(__FreeBSD__) || defined(__linux__) + if (pipe2(fds, flags | O_CLOEXEC)) + return UV__ERR(errno); + + return 0; +#else + if (pipe(fds)) + return UV__ERR(errno); + + if (uv__cloexec(fds[0], 1)) + goto fail; + + if (uv__cloexec(fds[1], 1)) + goto fail; + + if (flags & UV__F_NONBLOCK) { + if (uv__nonblock(fds[0], 1)) + goto fail; + + if (uv__nonblock(fds[1], 1)) + goto fail; + } + + return 0; + +fail: + uv__close(fds[0]); + uv__close(fds[1]); + return UV__ERR(errno); +#endif +} + + +/* + * Used for initializing stdio streams like options.stdin_stream. Returns + * zero on success. See also the cleanup section in uv_spawn(). + */ +static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2]) { + int mask; + int fd; + + mask = UV_IGNORE | UV_CREATE_PIPE | UV_INHERIT_FD | UV_INHERIT_STREAM; + + switch (container->flags & mask) { + case UV_IGNORE: + return 0; + + case UV_CREATE_PIPE: + assert(container->data.stream != NULL); + if (container->data.stream->type != UV_NAMED_PIPE) + return UV_EINVAL; + else + return uv__make_socketpair(fds); + + case UV_INHERIT_FD: + case UV_INHERIT_STREAM: + if (container->flags & UV_INHERIT_FD) + fd = container->data.fd; + else + fd = uv__stream_fd(container->data.stream); + + if (fd == -1) + return UV_EINVAL; + + fds[1] = fd; + return 0; + + default: + assert(0 && "Unexpected flags"); + return UV_EINVAL; + } +} + + +static int uv__process_open_stream(uv_stdio_container_t* container, + int pipefds[2]) { + int flags; + int err; + + if (!(container->flags & UV_CREATE_PIPE) || pipefds[0] < 0) + return 0; + + err = uv__close(pipefds[1]); + if (err != 0) + abort(); + + pipefds[1] = -1; + uv__nonblock(pipefds[0], 1); + + flags = 0; + if (container->flags & UV_WRITABLE_PIPE) + flags |= UV_HANDLE_READABLE; + if (container->flags & UV_READABLE_PIPE) + flags |= UV_HANDLE_WRITABLE; + + return uv__stream_open(container->data.stream, pipefds[0], flags); +} + + +static void uv__process_close_stream(uv_stdio_container_t* container) { + if (!(container->flags & UV_CREATE_PIPE)) return; + uv__stream_close(container->data.stream); +} + + +static void uv__write_int(int fd, int val) { + ssize_t n; + + do + n = write(fd, &val, sizeof(val)); + while (n == -1 && errno == EINTR); + + if (n == -1 && errno == EPIPE) + return; /* parent process has quit */ + + assert(n == sizeof(val)); +} + + +#if !(defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH)) +/* execvp is marked __WATCHOS_PROHIBITED __TVOS_PROHIBITED, so must be + * avoided. Since this isn't called on those targets, the function + * doesn't even need to be defined for them. + */ +static void uv__process_child_init(const uv_process_options_t* options, + int stdio_count, + int (*pipes)[2], + int error_fd) { + sigset_t set; + int close_fd; + int use_fd; + int err; + int fd; + int n; + + if (options->flags & UV_PROCESS_DETACHED) + setsid(); + + /* First duplicate low numbered fds, since it's not safe to duplicate them, + * they could get replaced. Example: swapping stdout and stderr; without + * this fd 2 (stderr) would be duplicated into fd 1, thus making both + * stdout and stderr go to the same fd, which was not the intention. */ + for (fd = 0; fd < stdio_count; fd++) { + use_fd = pipes[fd][1]; + if (use_fd < 0 || use_fd >= fd) + continue; + pipes[fd][1] = fcntl(use_fd, F_DUPFD, stdio_count); + if (pipes[fd][1] == -1) { + uv__write_int(error_fd, UV__ERR(errno)); + _exit(127); + } + } + + for (fd = 0; fd < stdio_count; fd++) { + close_fd = pipes[fd][0]; + use_fd = pipes[fd][1]; + + if (use_fd < 0) { + if (fd >= 3) + continue; + else { + /* redirect stdin, stdout and stderr to /dev/null even if UV_IGNORE is + * set + */ + use_fd = open("/dev/null", fd == 0 ? O_RDONLY : O_RDWR); + close_fd = use_fd; + + if (use_fd < 0) { + uv__write_int(error_fd, UV__ERR(errno)); + _exit(127); + } + } + } + + if (fd == use_fd) + uv__cloexec_fcntl(use_fd, 0); + else + fd = dup2(use_fd, fd); + + if (fd == -1) { + uv__write_int(error_fd, UV__ERR(errno)); + _exit(127); + } + + if (fd <= 2) + uv__nonblock_fcntl(fd, 0); + + if (close_fd >= stdio_count) + uv__close(close_fd); + } + + for (fd = 0; fd < stdio_count; fd++) { + use_fd = pipes[fd][1]; + + if (use_fd >= stdio_count) + uv__close(use_fd); + } + + if (options->cwd != NULL && chdir(options->cwd)) { + uv__write_int(error_fd, UV__ERR(errno)); + _exit(127); + } + + if (options->flags & (UV_PROCESS_SETUID | UV_PROCESS_SETGID)) { + /* When dropping privileges from root, the `setgroups` call will + * remove any extraneous groups. If we don't call this, then + * even though our uid has dropped, we may still have groups + * that enable us to do super-user things. This will fail if we + * aren't root, so don't bother checking the return value, this + * is just done as an optimistic privilege dropping function. + */ + SAVE_ERRNO(setgroups(0, NULL)); + } + + if ((options->flags & UV_PROCESS_SETGID) && setgid(options->gid)) { + uv__write_int(error_fd, UV__ERR(errno)); + _exit(127); + } + + if ((options->flags & UV_PROCESS_SETUID) && setuid(options->uid)) { + uv__write_int(error_fd, UV__ERR(errno)); + _exit(127); + } + + if (options->env != NULL) { + environ = options->env; + } + + /* Reset signal disposition. Use a hard-coded limit because NSIG + * is not fixed on Linux: it's either 32, 34 or 64, depending on + * whether RT signals are enabled. We are not allowed to touch + * RT signal handlers, glibc uses them internally. + */ + for (n = 1; n < 32; n += 1) { + if (n == SIGKILL || n == SIGSTOP) + continue; /* Can't be changed. */ + +#if defined(__HAIKU__) + if (n == SIGKILLTHR) + continue; /* Can't be changed. */ +#endif + + if (SIG_ERR != signal(n, SIG_DFL)) + continue; + + uv__write_int(error_fd, UV__ERR(errno)); + _exit(127); + } + + /* Reset signal mask. */ + sigemptyset(&set); + err = pthread_sigmask(SIG_SETMASK, &set, NULL); + + if (err != 0) { + uv__write_int(error_fd, UV__ERR(err)); + _exit(127); + } + + execvp(options->file, options->args); + uv__write_int(error_fd, UV__ERR(errno)); + _exit(127); +} +#endif + + +int uv_spawn(uv_loop_t* loop, + uv_process_t* process, + const uv_process_options_t* options) { +#if defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH) + /* fork is marked __WATCHOS_PROHIBITED __TVOS_PROHIBITED. */ + return UV_ENOSYS; +#else + int signal_pipe[2] = { -1, -1 }; + int pipes_storage[8][2]; + int (*pipes)[2]; + int stdio_count; + ssize_t r; + pid_t pid; + int err; + int exec_errorno; + int i; + int status; + + assert(options->file != NULL); + assert(!(options->flags & ~(UV_PROCESS_DETACHED | + UV_PROCESS_SETGID | + UV_PROCESS_SETUID | + UV_PROCESS_WINDOWS_HIDE | + UV_PROCESS_WINDOWS_HIDE_CONSOLE | + UV_PROCESS_WINDOWS_HIDE_GUI | + UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS))); + + uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS); + QUEUE_INIT(&process->queue); + + stdio_count = options->stdio_count; + if (stdio_count < 3) + stdio_count = 3; + + err = UV_ENOMEM; + pipes = pipes_storage; + if (stdio_count > (int) ARRAY_SIZE(pipes_storage)) + pipes = uv__malloc(stdio_count * sizeof(*pipes)); + + if (pipes == NULL) + goto error; + + for (i = 0; i < stdio_count; i++) { + pipes[i][0] = -1; + pipes[i][1] = -1; + } + + for (i = 0; i < options->stdio_count; i++) { + err = uv__process_init_stdio(options->stdio + i, pipes[i]); + if (err) + goto error; + } + + /* This pipe is used by the parent to wait until + * the child has called `execve()`. We need this + * to avoid the following race condition: + * + * if ((pid = fork()) > 0) { + * kill(pid, SIGTERM); + * } + * else if (pid == 0) { + * execve("/bin/cat", argp, envp); + * } + * + * The parent sends a signal immediately after forking. + * Since the child may not have called `execve()` yet, + * there is no telling what process receives the signal, + * our fork or /bin/cat. + * + * To avoid ambiguity, we create a pipe with both ends + * marked close-on-exec. Then, after the call to `fork()`, + * the parent polls the read end until it EOFs or errors with EPIPE. + */ + err = uv__make_pipe(signal_pipe, 0); + if (err) + goto error; + + uv_signal_start(&loop->child_watcher, uv__chld, SIGCHLD); + + /* Acquire write lock to prevent opening new fds in worker threads */ + uv_rwlock_wrlock(&loop->cloexec_lock); + pid = fork(); + + if (pid == -1) { + err = UV__ERR(errno); + uv_rwlock_wrunlock(&loop->cloexec_lock); + uv__close(signal_pipe[0]); + uv__close(signal_pipe[1]); + goto error; + } + + if (pid == 0) { + uv__process_child_init(options, stdio_count, pipes, signal_pipe[1]); + abort(); + } + + /* Release lock in parent process */ + uv_rwlock_wrunlock(&loop->cloexec_lock); + uv__close(signal_pipe[1]); + + process->status = 0; + exec_errorno = 0; + do + r = read(signal_pipe[0], &exec_errorno, sizeof(exec_errorno)); + while (r == -1 && errno == EINTR); + + if (r == 0) + ; /* okay, EOF */ + else if (r == sizeof(exec_errorno)) { + do + err = waitpid(pid, &status, 0); /* okay, read errorno */ + while (err == -1 && errno == EINTR); + assert(err == pid); + } else if (r == -1 && errno == EPIPE) { + do + err = waitpid(pid, &status, 0); /* okay, got EPIPE */ + while (err == -1 && errno == EINTR); + assert(err == pid); + } else + abort(); + + uv__close_nocheckstdio(signal_pipe[0]); + + for (i = 0; i < options->stdio_count; i++) { + err = uv__process_open_stream(options->stdio + i, pipes[i]); + if (err == 0) + continue; + + while (i--) + uv__process_close_stream(options->stdio + i); + + goto error; + } + + /* Only activate this handle if exec() happened successfully */ + if (exec_errorno == 0) { + QUEUE_INSERT_TAIL(&loop->process_handles, &process->queue); + uv__handle_start(process); + } + + process->pid = pid; + process->exit_cb = options->exit_cb; + + if (pipes != pipes_storage) + uv__free(pipes); + + return exec_errorno; + +error: + if (pipes != NULL) { + for (i = 0; i < stdio_count; i++) { + if (i < options->stdio_count) + if (options->stdio[i].flags & (UV_INHERIT_FD | UV_INHERIT_STREAM)) + continue; + if (pipes[i][0] != -1) + uv__close_nocheckstdio(pipes[i][0]); + if (pipes[i][1] != -1) + uv__close_nocheckstdio(pipes[i][1]); + } + + if (pipes != pipes_storage) + uv__free(pipes); + } + + return err; +#endif +} + + +int uv_process_kill(uv_process_t* process, int signum) { + return uv_kill(process->pid, signum); +} + + +int uv_kill(int pid, int signum) { + if (kill(pid, signum)) + return UV__ERR(errno); + else + return 0; +} + + +void uv__process_close(uv_process_t* handle) { + QUEUE_REMOVE(&handle->queue); + uv__handle_stop(handle); + if (QUEUE_EMPTY(&handle->loop->process_handles)) + uv_signal_stop(&handle->loop->child_watcher); +} diff --git a/include/libuv/src/unix/procfs-exepath.c b/include/libuv/src/unix/procfs-exepath.c new file mode 100644 index 000000000..00dc021f2 --- /dev/null +++ b/include/libuv/src/unix/procfs-exepath.c @@ -0,0 +1,45 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include + +int uv_exepath(char* buffer, size_t* size) { + ssize_t n; + + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + + n = *size - 1; + if (n > 0) + n = readlink("/proc/self/exe", buffer, n); + + if (n == -1) + return UV__ERR(errno); + + buffer[n] = '\0'; + *size = n; + + return 0; +} diff --git a/include/libuv/src/unix/proctitle.c b/include/libuv/src/unix/proctitle.c new file mode 100644 index 000000000..9ffe5b629 --- /dev/null +++ b/include/libuv/src/unix/proctitle.c @@ -0,0 +1,159 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include + +struct uv__process_title { + char* str; + size_t len; /* Length of the current process title. */ + size_t cap; /* Maximum capacity. Computed once in uv_setup_args(). */ +}; + +extern void uv__set_process_title(const char* title); + +static uv_mutex_t process_title_mutex; +static uv_once_t process_title_mutex_once = UV_ONCE_INIT; +static struct uv__process_title process_title; +static void* args_mem; + + +static void init_process_title_mutex_once(void) { + uv_mutex_init(&process_title_mutex); +} + + +char** uv_setup_args(int argc, char** argv) { + struct uv__process_title pt; + char** new_argv; + size_t size; + char* s; + int i; + + if (argc <= 0) + return argv; + + pt.str = argv[0]; + pt.len = strlen(argv[0]); + pt.cap = pt.len + 1; + + /* Calculate how much memory we need for the argv strings. */ + size = pt.cap; + for (i = 1; i < argc; i++) + size += strlen(argv[i]) + 1; + + /* Add space for the argv pointers. */ + size += (argc + 1) * sizeof(char*); + + new_argv = uv__malloc(size); + if (new_argv == NULL) + return argv; + + /* Copy over the strings and set up the pointer table. */ + i = 0; + s = (char*) &new_argv[argc + 1]; + size = pt.cap; + goto loop; + + for (/* empty */; i < argc; i++) { + size = strlen(argv[i]) + 1; + loop: + memcpy(s, argv[i], size); + new_argv[i] = s; + s += size; + } + new_argv[i] = NULL; + + /* argv is not adjacent on z/os, we use just argv[0] on that platform. */ +#ifndef __MVS__ + pt.cap = argv[i - 1] + size - argv[0]; +#endif + + args_mem = new_argv; + process_title = pt; + + return new_argv; +} + + +int uv_set_process_title(const char* title) { + struct uv__process_title* pt; + size_t len; + + /* If uv_setup_args wasn't called or failed, we can't continue. */ + if (args_mem == NULL) + return UV_ENOBUFS; + + pt = &process_title; + len = strlen(title); + + uv_once(&process_title_mutex_once, init_process_title_mutex_once); + uv_mutex_lock(&process_title_mutex); + + if (len >= pt->cap) { + len = 0; + if (pt->cap > 0) + len = pt->cap - 1; + } + + memcpy(pt->str, title, len); + memset(pt->str + len, '\0', pt->cap - len); + pt->len = len; + + uv_mutex_unlock(&process_title_mutex); + + return 0; +} + + +int uv_get_process_title(char* buffer, size_t size) { + if (buffer == NULL || size == 0) + return UV_EINVAL; + + /* If uv_setup_args wasn't called or failed, we can't continue. */ + if (args_mem == NULL) + return UV_ENOBUFS; + + uv_once(&process_title_mutex_once, init_process_title_mutex_once); + uv_mutex_lock(&process_title_mutex); + + if (size <= process_title.len) { + uv_mutex_unlock(&process_title_mutex); + return UV_ENOBUFS; + } + + if (process_title.len != 0) + memcpy(buffer, process_title.str, process_title.len + 1); + + buffer[process_title.len] = '\0'; + + uv_mutex_unlock(&process_title_mutex); + + return 0; +} + + +void uv__process_title_cleanup(void) { + uv__free(args_mem); /* Keep valgrind happy. */ + args_mem = NULL; +} diff --git a/include/libuv/src/unix/pthread-fixes.c b/include/libuv/src/unix/pthread-fixes.c new file mode 100644 index 000000000..022d79c4e --- /dev/null +++ b/include/libuv/src/unix/pthread-fixes.c @@ -0,0 +1,58 @@ +/* Copyright (c) 2013, Sony Mobile Communications AB + * Copyright (c) 2012, Google Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +/* Android versions < 4.1 have a broken pthread_sigmask. */ +#include "uv-common.h" + +#include +#include +#include + +int uv__pthread_sigmask(int how, const sigset_t* set, sigset_t* oset) { + static int workaround; + int err; + + if (uv__load_relaxed(&workaround)) { + return sigprocmask(how, set, oset); + } else { + err = pthread_sigmask(how, set, oset); + if (err) { + if (err == EINVAL && sigprocmask(how, set, oset) == 0) { + uv__store_relaxed(&workaround, 1); + return 0; + } else { + return -1; + } + } + } + + return 0; +} diff --git a/include/libuv/src/unix/qnx.c b/include/libuv/src/unix/qnx.c new file mode 100644 index 000000000..ca148d349 --- /dev/null +++ b/include/libuv/src/unix/qnx.c @@ -0,0 +1,137 @@ +/* Copyright libuv contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include +#include +#include +#include + +static void +get_mem_info(uint64_t* totalmem, uint64_t* freemem) { + mem_info_t msg; + + memset(&msg, 0, sizeof(msg)); + msg.i.type = _MEM_INFO; + msg.i.fd = -1; + + if (MsgSend(MEMMGR_COID, &msg.i, sizeof(msg.i), &msg.o, sizeof(msg.o)) + != -1) { + *totalmem = msg.o.info.__posix_tmi_total; + *freemem = msg.o.info.posix_tmi_length; + } else { + *totalmem = 0; + *freemem = 0; + } +} + + +void uv_loadavg(double avg[3]) { + avg[0] = 0.0; + avg[1] = 0.0; + avg[2] = 0.0; +} + + +int uv_exepath(char* buffer, size_t* size) { + char path[PATH_MAX]; + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + + realpath(_cmdname(NULL), path); + strlcpy(buffer, path, *size); + *size = strlen(buffer); + return 0; +} + + +uint64_t uv_get_free_memory(void) { + uint64_t totalmem; + uint64_t freemem; + get_mem_info(&totalmem, &freemem); + return freemem; +} + + +uint64_t uv_get_total_memory(void) { + uint64_t totalmem; + uint64_t freemem; + get_mem_info(&totalmem, &freemem); + return totalmem; +} + + +uint64_t uv_get_constrained_memory(void) { + return 0; +} + + +int uv_resident_set_memory(size_t* rss) { + int fd; + procfs_asinfo asinfo; + + fd = uv__open_cloexec("/proc/self/ctl", O_RDONLY); + if (fd == -1) + return UV__ERR(errno); + + if (devctl(fd, DCMD_PROC_ASINFO, &asinfo, sizeof(asinfo), 0) == -1) { + uv__close(fd); + return UV__ERR(errno); + } + + uv__close(fd); + *rss = asinfo.rss; + return 0; +} + + +int uv_uptime(double* uptime) { + struct qtime_entry* qtime = _SYSPAGE_ENTRY(_syspage_ptr, qtime); + *uptime = (qtime->nsec / 1000000000.0); + return 0; +} + + +int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + struct cpuinfo_entry* cpuinfo = + (struct cpuinfo_entry*)_SYSPAGE_ENTRY(_syspage_ptr, new_cpuinfo); + size_t cpuinfo_size = _SYSPAGE_ELEMENT_SIZE(_syspage_ptr, cpuinfo); + struct strings_entry* strings = _SYSPAGE_ENTRY(_syspage_ptr, strings); + int num_cpus = _syspage_ptr->num_cpu; + int i; + + *count = num_cpus; + *cpu_infos = uv__malloc(num_cpus * sizeof(**cpu_infos)); + if (*cpu_infos == NULL) + return UV_ENOMEM; + + for (i = 0; i < num_cpus; i++) { + (*cpu_infos)[i].model = strdup(&strings->data[cpuinfo->name]); + (*cpu_infos)[i].speed = cpuinfo->speed; + SYSPAGE_ARRAY_ADJ_OFFSET(cpuinfo, cpuinfo, cpuinfo_size); + } + + return 0; +} diff --git a/include/libuv/src/unix/random-devurandom.c b/include/libuv/src/unix/random-devurandom.c new file mode 100644 index 000000000..05e52a56a --- /dev/null +++ b/include/libuv/src/unix/random-devurandom.c @@ -0,0 +1,93 @@ +/* Copyright libuv contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include + +static uv_once_t once = UV_ONCE_INIT; +static int status; + + +int uv__random_readpath(const char* path, void* buf, size_t buflen) { + struct stat s; + size_t pos; + ssize_t n; + int fd; + + fd = uv__open_cloexec(path, O_RDONLY); + + if (fd < 0) + return fd; + + if (fstat(fd, &s)) { + uv__close(fd); + return UV__ERR(errno); + } + + if (!S_ISCHR(s.st_mode)) { + uv__close(fd); + return UV_EIO; + } + + for (pos = 0; pos != buflen; pos += n) { + do + n = read(fd, (char*) buf + pos, buflen - pos); + while (n == -1 && errno == EINTR); + + if (n == -1) { + uv__close(fd); + return UV__ERR(errno); + } + + if (n == 0) { + uv__close(fd); + return UV_EIO; + } + } + + uv__close(fd); + return 0; +} + + +static void uv__random_devurandom_init(void) { + char c; + + /* Linux's random(4) man page suggests applications should read at least + * once from /dev/random before switching to /dev/urandom in order to seed + * the system RNG. Reads from /dev/random can of course block indefinitely + * until entropy is available but that's the point. + */ + status = uv__random_readpath("/dev/random", &c, 1); +} + + +int uv__random_devurandom(void* buf, size_t buflen) { + uv_once(&once, uv__random_devurandom_init); + + if (status != 0) + return status; + + return uv__random_readpath("/dev/urandom", buf, buflen); +} diff --git a/include/libuv/src/unix/random-getentropy.c b/include/libuv/src/unix/random-getentropy.c new file mode 100644 index 000000000..c45d9fd4a --- /dev/null +++ b/include/libuv/src/unix/random-getentropy.c @@ -0,0 +1,57 @@ +/* Copyright libuv contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include + +typedef int (*uv__getentropy_cb)(void *, size_t); + +static uv__getentropy_cb uv__getentropy; +static uv_once_t once = UV_ONCE_INIT; + + +static void uv__random_getentropy_init(void) { + uv__getentropy = (uv__getentropy_cb) dlsym(RTLD_DEFAULT, "getentropy"); +} + + +int uv__random_getentropy(void* buf, size_t buflen) { + size_t pos; + size_t stride; + + uv_once(&once, uv__random_getentropy_init); + + if (uv__getentropy == NULL) + return UV_ENOSYS; + + /* getentropy() returns an error for requests > 256 bytes. */ + for (pos = 0, stride = 256; pos + stride < buflen; pos += stride) + if (uv__getentropy((char *) buf + pos, stride)) + return UV__ERR(errno); + + if (uv__getentropy((char *) buf + pos, buflen - pos)) + return UV__ERR(errno); + + return 0; +} diff --git a/include/libuv/src/unix/random-getrandom.c b/include/libuv/src/unix/random-getrandom.c new file mode 100644 index 000000000..bcc94089b --- /dev/null +++ b/include/libuv/src/unix/random-getrandom.c @@ -0,0 +1,88 @@ +/* Copyright libuv contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#ifdef __linux__ + +#include "linux-syscalls.h" + +#define uv__random_getrandom_init() 0 + +#else /* !__linux__ */ + +#include +#include + +typedef ssize_t (*uv__getrandom_cb)(void *, size_t, unsigned); + +static uv__getrandom_cb uv__getrandom; +static uv_once_t once = UV_ONCE_INIT; + +static void uv__random_getrandom_init_once(void) { + uv__getrandom = (uv__getrandom_cb) dlsym(RTLD_DEFAULT, "getrandom"); +} + +static int uv__random_getrandom_init(void) { + uv_once(&once, uv__random_getrandom_init_once); + + if (uv__getrandom == NULL) + return UV_ENOSYS; + + return 0; +} + +#endif /* !__linux__ */ + +int uv__random_getrandom(void* buf, size_t buflen) { + ssize_t n; + size_t pos; + int rc; + + rc = uv__random_getrandom_init(); + if (rc != 0) + return rc; + + for (pos = 0; pos != buflen; pos += n) { + do { + n = buflen - pos; + + /* Most getrandom() implementations promise that reads <= 256 bytes + * will always succeed and won't be interrupted by signals. + * It's therefore useful to split it up in smaller reads because + * one big read may, in theory, continuously fail with EINTR. + */ + if (n > 256) + n = 256; + + n = uv__getrandom((char *) buf + pos, n, 0); + } while (n == -1 && errno == EINTR); + + if (n == -1) + return UV__ERR(errno); + + if (n == 0) + return UV_EIO; + } + + return 0; +} diff --git a/include/libuv/src/unix/random-sysctl-linux.c b/include/libuv/src/unix/random-sysctl-linux.c new file mode 100644 index 000000000..66ba8d74e --- /dev/null +++ b/include/libuv/src/unix/random-sysctl-linux.c @@ -0,0 +1,99 @@ +/* Copyright libuv contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include + +#include +#include + + +struct uv__sysctl_args { + int* name; + int nlen; + void* oldval; + size_t* oldlenp; + void* newval; + size_t newlen; + unsigned long unused[4]; +}; + + +int uv__random_sysctl(void* buf, size_t buflen) { + static int name[] = {1 /*CTL_KERN*/, 40 /*KERN_RANDOM*/, 6 /*RANDOM_UUID*/}; + struct uv__sysctl_args args; + char uuid[16]; + char* p; + char* pe; + size_t n; + + p = buf; + pe = p + buflen; + + while (p < pe) { + memset(&args, 0, sizeof(args)); + + args.name = name; + args.nlen = ARRAY_SIZE(name); + args.oldval = uuid; + args.oldlenp = &n; + n = sizeof(uuid); + + /* Emits a deprecation warning with some kernels but that seems like + * an okay trade-off for the fallback of the fallback: this function is + * only called when neither getrandom(2) nor /dev/urandom are available. + * Fails with ENOSYS on kernels configured without CONFIG_SYSCTL_SYSCALL. + * At least arm64 never had a _sysctl system call and therefore doesn't + * have a SYS__sysctl define either. + */ +#ifdef SYS__sysctl + if (syscall(SYS__sysctl, &args) == -1) + return UV__ERR(errno); +#else + { + (void) &args; + return UV_ENOSYS; + } +#endif + + if (n != sizeof(uuid)) + return UV_EIO; /* Can't happen. */ + + /* uuid[] is now a type 4 UUID. Bytes 6 and 8 (counting from zero) contain + * 4 and 5 bits of entropy, respectively. For ease of use, we skip those + * and only use 14 of the 16 bytes. + */ + uuid[6] = uuid[14]; + uuid[8] = uuid[15]; + + n = pe - p; + if (n > 14) + n = 14; + + memcpy(p, uuid, n); + p += n; + } + + return 0; +} diff --git a/include/libuv/src/unix/signal.c b/include/libuv/src/unix/signal.c new file mode 100644 index 000000000..f40a3e54e --- /dev/null +++ b/include/libuv/src/unix/signal.c @@ -0,0 +1,558 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include +#include +#include +#include + +#ifndef SA_RESTART +# define SA_RESTART 0 +#endif + +typedef struct { + uv_signal_t* handle; + int signum; +} uv__signal_msg_t; + +RB_HEAD(uv__signal_tree_s, uv_signal_s); + + +static int uv__signal_unlock(void); +static int uv__signal_start(uv_signal_t* handle, + uv_signal_cb signal_cb, + int signum, + int oneshot); +static void uv__signal_event(uv_loop_t* loop, uv__io_t* w, unsigned int events); +static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2); +static void uv__signal_stop(uv_signal_t* handle); +static void uv__signal_unregister_handler(int signum); + + +static uv_once_t uv__signal_global_init_guard = UV_ONCE_INIT; +static struct uv__signal_tree_s uv__signal_tree = + RB_INITIALIZER(uv__signal_tree); +static int uv__signal_lock_pipefd[2] = { -1, -1 }; + +RB_GENERATE_STATIC(uv__signal_tree_s, + uv_signal_s, tree_entry, + uv__signal_compare) + +static void uv__signal_global_reinit(void); + +static void uv__signal_global_init(void) { + if (uv__signal_lock_pipefd[0] == -1) + /* pthread_atfork can register before and after handlers, one + * for each child. This only registers one for the child. That + * state is both persistent and cumulative, so if we keep doing + * it the handler functions will be called multiple times. Thus + * we only want to do it once. + */ + if (pthread_atfork(NULL, NULL, &uv__signal_global_reinit)) + abort(); + + uv__signal_global_reinit(); +} + + +void uv__signal_cleanup(void) { + /* We can only use signal-safe functions here. + * That includes read/write and close, fortunately. + * We do all of this directly here instead of resetting + * uv__signal_global_init_guard because + * uv__signal_global_once_init is only called from uv_loop_init + * and this needs to function in existing loops. + */ + if (uv__signal_lock_pipefd[0] != -1) { + uv__close(uv__signal_lock_pipefd[0]); + uv__signal_lock_pipefd[0] = -1; + } + + if (uv__signal_lock_pipefd[1] != -1) { + uv__close(uv__signal_lock_pipefd[1]); + uv__signal_lock_pipefd[1] = -1; + } +} + + +static void uv__signal_global_reinit(void) { + uv__signal_cleanup(); + + if (uv__make_pipe(uv__signal_lock_pipefd, 0)) + abort(); + + if (uv__signal_unlock()) + abort(); +} + + +void uv__signal_global_once_init(void) { + uv_once(&uv__signal_global_init_guard, uv__signal_global_init); +} + + +static int uv__signal_lock(void) { + int r; + char data; + + do { + r = read(uv__signal_lock_pipefd[0], &data, sizeof data); + } while (r < 0 && errno == EINTR); + + return (r < 0) ? -1 : 0; +} + + +static int uv__signal_unlock(void) { + int r; + char data = 42; + + do { + r = write(uv__signal_lock_pipefd[1], &data, sizeof data); + } while (r < 0 && errno == EINTR); + + return (r < 0) ? -1 : 0; +} + + +static void uv__signal_block_and_lock(sigset_t* saved_sigmask) { + sigset_t new_mask; + + if (sigfillset(&new_mask)) + abort(); + + /* to shut up valgrind */ + sigemptyset(saved_sigmask); + if (pthread_sigmask(SIG_SETMASK, &new_mask, saved_sigmask)) + abort(); + + if (uv__signal_lock()) + abort(); +} + + +static void uv__signal_unlock_and_unblock(sigset_t* saved_sigmask) { + if (uv__signal_unlock()) + abort(); + + if (pthread_sigmask(SIG_SETMASK, saved_sigmask, NULL)) + abort(); +} + + +static uv_signal_t* uv__signal_first_handle(int signum) { + /* This function must be called with the signal lock held. */ + uv_signal_t lookup; + uv_signal_t* handle; + + lookup.signum = signum; + lookup.flags = 0; + lookup.loop = NULL; + + handle = RB_NFIND(uv__signal_tree_s, &uv__signal_tree, &lookup); + + if (handle != NULL && handle->signum == signum) + return handle; + + return NULL; +} + + +static void uv__signal_handler(int signum) { + uv__signal_msg_t msg; + uv_signal_t* handle; + int saved_errno; + + saved_errno = errno; + memset(&msg, 0, sizeof msg); + + if (uv__signal_lock()) { + errno = saved_errno; + return; + } + + for (handle = uv__signal_first_handle(signum); + handle != NULL && handle->signum == signum; + handle = RB_NEXT(uv__signal_tree_s, &uv__signal_tree, handle)) { + int r; + + msg.signum = signum; + msg.handle = handle; + + /* write() should be atomic for small data chunks, so the entire message + * should be written at once. In theory the pipe could become full, in + * which case the user is out of luck. + */ + do { + r = write(handle->loop->signal_pipefd[1], &msg, sizeof msg); + } while (r == -1 && errno == EINTR); + + assert(r == sizeof msg || + (r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))); + + if (r != -1) + handle->caught_signals++; + } + + uv__signal_unlock(); + errno = saved_errno; +} + + +static int uv__signal_register_handler(int signum, int oneshot) { + /* When this function is called, the signal lock must be held. */ + struct sigaction sa; + + /* XXX use a separate signal stack? */ + memset(&sa, 0, sizeof(sa)); + if (sigfillset(&sa.sa_mask)) + abort(); + sa.sa_handler = uv__signal_handler; + sa.sa_flags = SA_RESTART; + if (oneshot) + sa.sa_flags |= SA_RESETHAND; + + /* XXX save old action so we can restore it later on? */ + if (sigaction(signum, &sa, NULL)) + return UV__ERR(errno); + + return 0; +} + + +static void uv__signal_unregister_handler(int signum) { + /* When this function is called, the signal lock must be held. */ + struct sigaction sa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = SIG_DFL; + + /* sigaction can only fail with EINVAL or EFAULT; an attempt to deregister a + * signal implies that it was successfully registered earlier, so EINVAL + * should never happen. + */ + if (sigaction(signum, &sa, NULL)) + abort(); +} + + +static int uv__signal_loop_once_init(uv_loop_t* loop) { + int err; + + /* Return if already initialized. */ + if (loop->signal_pipefd[0] != -1) + return 0; + + err = uv__make_pipe(loop->signal_pipefd, UV__F_NONBLOCK); + if (err) + return err; + + uv__io_init(&loop->signal_io_watcher, + uv__signal_event, + loop->signal_pipefd[0]); + uv__io_start(loop, &loop->signal_io_watcher, POLLIN); + + return 0; +} + + +int uv__signal_loop_fork(uv_loop_t* loop) { + uv__io_stop(loop, &loop->signal_io_watcher, POLLIN); + uv__close(loop->signal_pipefd[0]); + uv__close(loop->signal_pipefd[1]); + loop->signal_pipefd[0] = -1; + loop->signal_pipefd[1] = -1; + return uv__signal_loop_once_init(loop); +} + + +void uv__signal_loop_cleanup(uv_loop_t* loop) { + QUEUE* q; + + /* Stop all the signal watchers that are still attached to this loop. This + * ensures that the (shared) signal tree doesn't contain any invalid entries + * entries, and that signal handlers are removed when appropriate. + * It's safe to use QUEUE_FOREACH here because the handles and the handle + * queue are not modified by uv__signal_stop(). + */ + QUEUE_FOREACH(q, &loop->handle_queue) { + uv_handle_t* handle = QUEUE_DATA(q, uv_handle_t, handle_queue); + + if (handle->type == UV_SIGNAL) + uv__signal_stop((uv_signal_t*) handle); + } + + if (loop->signal_pipefd[0] != -1) { + uv__close(loop->signal_pipefd[0]); + loop->signal_pipefd[0] = -1; + } + + if (loop->signal_pipefd[1] != -1) { + uv__close(loop->signal_pipefd[1]); + loop->signal_pipefd[1] = -1; + } +} + + +int uv_signal_init(uv_loop_t* loop, uv_signal_t* handle) { + int err; + + err = uv__signal_loop_once_init(loop); + if (err) + return err; + + uv__handle_init(loop, (uv_handle_t*) handle, UV_SIGNAL); + handle->signum = 0; + handle->caught_signals = 0; + handle->dispatched_signals = 0; + + return 0; +} + + +void uv__signal_close(uv_signal_t* handle) { + uv__signal_stop(handle); +} + + +int uv_signal_start(uv_signal_t* handle, uv_signal_cb signal_cb, int signum) { + return uv__signal_start(handle, signal_cb, signum, 0); +} + + +int uv_signal_start_oneshot(uv_signal_t* handle, + uv_signal_cb signal_cb, + int signum) { + return uv__signal_start(handle, signal_cb, signum, 1); +} + + +static int uv__signal_start(uv_signal_t* handle, + uv_signal_cb signal_cb, + int signum, + int oneshot) { + sigset_t saved_sigmask; + int err; + uv_signal_t* first_handle; + + assert(!uv__is_closing(handle)); + + /* If the user supplies signum == 0, then return an error already. If the + * signum is otherwise invalid then uv__signal_register will find out + * eventually. + */ + if (signum == 0) + return UV_EINVAL; + + /* Short circuit: if the signal watcher is already watching {signum} don't + * go through the process of deregistering and registering the handler. + * Additionally, this avoids pending signals getting lost in the small + * time frame that handle->signum == 0. + */ + if (signum == handle->signum) { + handle->signal_cb = signal_cb; + return 0; + } + + /* If the signal handler was already active, stop it first. */ + if (handle->signum != 0) { + uv__signal_stop(handle); + } + + uv__signal_block_and_lock(&saved_sigmask); + + /* If at this point there are no active signal watchers for this signum (in + * any of the loops), it's time to try and register a handler for it here. + * Also in case there's only one-shot handlers and a regular handler comes in. + */ + first_handle = uv__signal_first_handle(signum); + if (first_handle == NULL || + (!oneshot && (first_handle->flags & UV_SIGNAL_ONE_SHOT))) { + err = uv__signal_register_handler(signum, oneshot); + if (err) { + /* Registering the signal handler failed. Must be an invalid signal. */ + uv__signal_unlock_and_unblock(&saved_sigmask); + return err; + } + } + + handle->signum = signum; + if (oneshot) + handle->flags |= UV_SIGNAL_ONE_SHOT; + + RB_INSERT(uv__signal_tree_s, &uv__signal_tree, handle); + + uv__signal_unlock_and_unblock(&saved_sigmask); + + handle->signal_cb = signal_cb; + uv__handle_start(handle); + + return 0; +} + + +static void uv__signal_event(uv_loop_t* loop, + uv__io_t* w, + unsigned int events) { + uv__signal_msg_t* msg; + uv_signal_t* handle; + char buf[sizeof(uv__signal_msg_t) * 32]; + size_t bytes, end, i; + int r; + + bytes = 0; + end = 0; + + do { + r = read(loop->signal_pipefd[0], buf + bytes, sizeof(buf) - bytes); + + if (r == -1 && errno == EINTR) + continue; + + if (r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { + /* If there are bytes in the buffer already (which really is extremely + * unlikely if possible at all) we can't exit the function here. We'll + * spin until more bytes are read instead. + */ + if (bytes > 0) + continue; + + /* Otherwise, there was nothing there. */ + return; + } + + /* Other errors really should never happen. */ + if (r == -1) + abort(); + + bytes += r; + + /* `end` is rounded down to a multiple of sizeof(uv__signal_msg_t). */ + end = (bytes / sizeof(uv__signal_msg_t)) * sizeof(uv__signal_msg_t); + + for (i = 0; i < end; i += sizeof(uv__signal_msg_t)) { + msg = (uv__signal_msg_t*) (buf + i); + handle = msg->handle; + + if (msg->signum == handle->signum) { + assert(!(handle->flags & UV_HANDLE_CLOSING)); + handle->signal_cb(handle, handle->signum); + } + + handle->dispatched_signals++; + + if (handle->flags & UV_SIGNAL_ONE_SHOT) + uv__signal_stop(handle); + } + + bytes -= end; + + /* If there are any "partial" messages left, move them to the start of the + * the buffer, and spin. This should not happen. + */ + if (bytes) { + memmove(buf, buf + end, bytes); + continue; + } + } while (end == sizeof buf); +} + + +static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2) { + int f1; + int f2; + /* Compare signums first so all watchers with the same signnum end up + * adjacent. + */ + if (w1->signum < w2->signum) return -1; + if (w1->signum > w2->signum) return 1; + + /* Handlers without UV_SIGNAL_ONE_SHOT set will come first, so if the first + * handler returned is a one-shot handler, the rest will be too. + */ + f1 = w1->flags & UV_SIGNAL_ONE_SHOT; + f2 = w2->flags & UV_SIGNAL_ONE_SHOT; + if (f1 < f2) return -1; + if (f1 > f2) return 1; + + /* Sort by loop pointer, so we can easily look up the first item after + * { .signum = x, .loop = NULL }. + */ + if (w1->loop < w2->loop) return -1; + if (w1->loop > w2->loop) return 1; + + if (w1 < w2) return -1; + if (w1 > w2) return 1; + + return 0; +} + + +int uv_signal_stop(uv_signal_t* handle) { + assert(!uv__is_closing(handle)); + uv__signal_stop(handle); + return 0; +} + + +static void uv__signal_stop(uv_signal_t* handle) { + uv_signal_t* removed_handle; + sigset_t saved_sigmask; + uv_signal_t* first_handle; + int rem_oneshot; + int first_oneshot; + int ret; + + /* If the watcher wasn't started, this is a no-op. */ + if (handle->signum == 0) + return; + + uv__signal_block_and_lock(&saved_sigmask); + + removed_handle = RB_REMOVE(uv__signal_tree_s, &uv__signal_tree, handle); + assert(removed_handle == handle); + (void) removed_handle; + + /* Check if there are other active signal watchers observing this signal. If + * not, unregister the signal handler. + */ + first_handle = uv__signal_first_handle(handle->signum); + if (first_handle == NULL) { + uv__signal_unregister_handler(handle->signum); + } else { + rem_oneshot = handle->flags & UV_SIGNAL_ONE_SHOT; + first_oneshot = first_handle->flags & UV_SIGNAL_ONE_SHOT; + if (first_oneshot && !rem_oneshot) { + ret = uv__signal_register_handler(handle->signum, 1); + assert(ret == 0); + (void)ret; + } + } + + uv__signal_unlock_and_unblock(&saved_sigmask); + + handle->signum = 0; + uv__handle_stop(handle); +} diff --git a/include/libuv/src/unix/spinlock.h b/include/libuv/src/unix/spinlock.h new file mode 100644 index 000000000..a20c83cc6 --- /dev/null +++ b/include/libuv/src/unix/spinlock.h @@ -0,0 +1,53 @@ +/* Copyright (c) 2013, Ben Noordhuis + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef UV_SPINLOCK_H_ +#define UV_SPINLOCK_H_ + +#include "internal.h" /* ACCESS_ONCE, UV_UNUSED */ +#include "atomic-ops.h" + +#define UV_SPINLOCK_INITIALIZER { 0 } + +typedef struct { + int lock; +} uv_spinlock_t; + +UV_UNUSED(static void uv_spinlock_init(uv_spinlock_t* spinlock)); +UV_UNUSED(static void uv_spinlock_lock(uv_spinlock_t* spinlock)); +UV_UNUSED(static void uv_spinlock_unlock(uv_spinlock_t* spinlock)); +UV_UNUSED(static int uv_spinlock_trylock(uv_spinlock_t* spinlock)); + +UV_UNUSED(static void uv_spinlock_init(uv_spinlock_t* spinlock)) { + ACCESS_ONCE(int, spinlock->lock) = 0; +} + +UV_UNUSED(static void uv_spinlock_lock(uv_spinlock_t* spinlock)) { + while (!uv_spinlock_trylock(spinlock)) cpu_relax(); +} + +UV_UNUSED(static void uv_spinlock_unlock(uv_spinlock_t* spinlock)) { + ACCESS_ONCE(int, spinlock->lock) = 0; +} + +UV_UNUSED(static int uv_spinlock_trylock(uv_spinlock_t* spinlock)) { + /* TODO(bnoordhuis) Maybe change to a ticket lock to guarantee fair queueing. + * Not really critical until we have locks that are (frequently) contended + * for by several threads. + */ + return 0 == cmpxchgi(&spinlock->lock, 0, 1); +} + +#endif /* UV_SPINLOCK_H_ */ diff --git a/include/libuv/src/unix/stream.c b/include/libuv/src/unix/stream.c new file mode 100644 index 000000000..8327f9ccf --- /dev/null +++ b/include/libuv/src/unix/stream.c @@ -0,0 +1,1693 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include /* IOV_MAX */ + +#if defined(__APPLE__) +# include +# include +# include + +/* Forward declaration */ +typedef struct uv__stream_select_s uv__stream_select_t; + +struct uv__stream_select_s { + uv_stream_t* stream; + uv_thread_t thread; + uv_sem_t close_sem; + uv_sem_t async_sem; + uv_async_t async; + int events; + int fake_fd; + int int_fd; + int fd; + fd_set* sread; + size_t sread_sz; + fd_set* swrite; + size_t swrite_sz; +}; + +/* Due to a possible kernel bug at least in OS X 10.10 "Yosemite", + * EPROTOTYPE can be returned while trying to write to a socket that is + * shutting down. If we retry the write, we should get the expected EPIPE + * instead. + */ +# define RETRY_ON_WRITE_ERROR(errno) (errno == EINTR || errno == EPROTOTYPE) +# define IS_TRANSIENT_WRITE_ERROR(errno, send_handle) \ + (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS || \ + (errno == EMSGSIZE && send_handle != NULL)) +#else +# define RETRY_ON_WRITE_ERROR(errno) (errno == EINTR) +# define IS_TRANSIENT_WRITE_ERROR(errno, send_handle) \ + (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS) +#endif /* defined(__APPLE__) */ + +static void uv__stream_connect(uv_stream_t*); +static void uv__write(uv_stream_t* stream); +static void uv__read(uv_stream_t* stream); +static void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events); +static void uv__write_callbacks(uv_stream_t* stream); +static size_t uv__write_req_size(uv_write_t* req); + + +void uv__stream_init(uv_loop_t* loop, + uv_stream_t* stream, + uv_handle_type type) { + int err; + + uv__handle_init(loop, (uv_handle_t*)stream, type); + stream->read_cb = NULL; + stream->alloc_cb = NULL; + stream->close_cb = NULL; + stream->connection_cb = NULL; + stream->connect_req = NULL; + stream->shutdown_req = NULL; + stream->accepted_fd = -1; + stream->queued_fds = NULL; + stream->delayed_error = 0; + QUEUE_INIT(&stream->write_queue); + QUEUE_INIT(&stream->write_completed_queue); + stream->write_queue_size = 0; + + if (loop->emfile_fd == -1) { + err = uv__open_cloexec("/dev/null", O_RDONLY); + if (err < 0) + /* In the rare case that "/dev/null" isn't mounted open "/" + * instead. + */ + err = uv__open_cloexec("/", O_RDONLY); + if (err >= 0) + loop->emfile_fd = err; + } + +#if defined(__APPLE__) + stream->select = NULL; +#endif /* defined(__APPLE_) */ + + uv__io_init(&stream->io_watcher, uv__stream_io, -1); +} + + +static void uv__stream_osx_interrupt_select(uv_stream_t* stream) { +#if defined(__APPLE__) + /* Notify select() thread about state change */ + uv__stream_select_t* s; + int r; + + s = stream->select; + if (s == NULL) + return; + + /* Interrupt select() loop + * NOTE: fake_fd and int_fd are socketpair(), thus writing to one will + * emit read event on other side + */ + do + r = write(s->fake_fd, "x", 1); + while (r == -1 && errno == EINTR); + + assert(r == 1); +#else /* !defined(__APPLE__) */ + /* No-op on any other platform */ +#endif /* !defined(__APPLE__) */ +} + + +#if defined(__APPLE__) +static void uv__stream_osx_select(void* arg) { + uv_stream_t* stream; + uv__stream_select_t* s; + char buf[1024]; + int events; + int fd; + int r; + int max_fd; + + stream = arg; + s = stream->select; + fd = s->fd; + + if (fd > s->int_fd) + max_fd = fd; + else + max_fd = s->int_fd; + + while (1) { + /* Terminate on semaphore */ + if (uv_sem_trywait(&s->close_sem) == 0) + break; + + /* Watch fd using select(2) */ + memset(s->sread, 0, s->sread_sz); + memset(s->swrite, 0, s->swrite_sz); + + if (uv__io_active(&stream->io_watcher, POLLIN)) + FD_SET(fd, s->sread); + if (uv__io_active(&stream->io_watcher, POLLOUT)) + FD_SET(fd, s->swrite); + FD_SET(s->int_fd, s->sread); + + /* Wait indefinitely for fd events */ + r = select(max_fd + 1, s->sread, s->swrite, NULL, NULL); + if (r == -1) { + if (errno == EINTR) + continue; + + /* XXX: Possible?! */ + abort(); + } + + /* Ignore timeouts */ + if (r == 0) + continue; + + /* Empty socketpair's buffer in case of interruption */ + if (FD_ISSET(s->int_fd, s->sread)) + while (1) { + r = read(s->int_fd, buf, sizeof(buf)); + + if (r == sizeof(buf)) + continue; + + if (r != -1) + break; + + if (errno == EAGAIN || errno == EWOULDBLOCK) + break; + + if (errno == EINTR) + continue; + + abort(); + } + + /* Handle events */ + events = 0; + if (FD_ISSET(fd, s->sread)) + events |= POLLIN; + if (FD_ISSET(fd, s->swrite)) + events |= POLLOUT; + + assert(events != 0 || FD_ISSET(s->int_fd, s->sread)); + if (events != 0) { + ACCESS_ONCE(int, s->events) = events; + + uv_async_send(&s->async); + uv_sem_wait(&s->async_sem); + + /* Should be processed at this stage */ + assert((s->events == 0) || (stream->flags & UV_HANDLE_CLOSING)); + } + } +} + + +static void uv__stream_osx_select_cb(uv_async_t* handle) { + uv__stream_select_t* s; + uv_stream_t* stream; + int events; + + s = container_of(handle, uv__stream_select_t, async); + stream = s->stream; + + /* Get and reset stream's events */ + events = s->events; + ACCESS_ONCE(int, s->events) = 0; + + assert(events != 0); + assert(events == (events & (POLLIN | POLLOUT))); + + /* Invoke callback on event-loop */ + if ((events & POLLIN) && uv__io_active(&stream->io_watcher, POLLIN)) + uv__stream_io(stream->loop, &stream->io_watcher, POLLIN); + + if ((events & POLLOUT) && uv__io_active(&stream->io_watcher, POLLOUT)) + uv__stream_io(stream->loop, &stream->io_watcher, POLLOUT); + + if (stream->flags & UV_HANDLE_CLOSING) + return; + + /* NOTE: It is important to do it here, otherwise `select()` might be called + * before the actual `uv__read()`, leading to the blocking syscall + */ + uv_sem_post(&s->async_sem); +} + + +static void uv__stream_osx_cb_close(uv_handle_t* async) { + uv__stream_select_t* s; + + s = container_of(async, uv__stream_select_t, async); + uv__free(s); +} + + +int uv__stream_try_select(uv_stream_t* stream, int* fd) { + /* + * kqueue doesn't work with some files from /dev mount on osx. + * select(2) in separate thread for those fds + */ + + struct kevent filter[1]; + struct kevent events[1]; + struct timespec timeout; + uv__stream_select_t* s; + int fds[2]; + int err; + int ret; + int kq; + int old_fd; + int max_fd; + size_t sread_sz; + size_t swrite_sz; + + kq = kqueue(); + if (kq == -1) { + perror("(libuv) kqueue()"); + return UV__ERR(errno); + } + + EV_SET(&filter[0], *fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, 0); + + /* Use small timeout, because we only want to capture EINVALs */ + timeout.tv_sec = 0; + timeout.tv_nsec = 1; + + do + ret = kevent(kq, filter, 1, events, 1, &timeout); + while (ret == -1 && errno == EINTR); + + uv__close(kq); + + if (ret == -1) + return UV__ERR(errno); + + if (ret == 0 || (events[0].flags & EV_ERROR) == 0 || events[0].data != EINVAL) + return 0; + + /* At this point we definitely know that this fd won't work with kqueue */ + + /* + * Create fds for io watcher and to interrupt the select() loop. + * NOTE: do it ahead of malloc below to allocate enough space for fd_sets + */ + if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) + return UV__ERR(errno); + + max_fd = *fd; + if (fds[1] > max_fd) + max_fd = fds[1]; + + sread_sz = ROUND_UP(max_fd + 1, sizeof(uint32_t) * NBBY) / NBBY; + swrite_sz = sread_sz; + + s = uv__malloc(sizeof(*s) + sread_sz + swrite_sz); + if (s == NULL) { + err = UV_ENOMEM; + goto failed_malloc; + } + + s->events = 0; + s->fd = *fd; + s->sread = (fd_set*) ((char*) s + sizeof(*s)); + s->sread_sz = sread_sz; + s->swrite = (fd_set*) ((char*) s->sread + sread_sz); + s->swrite_sz = swrite_sz; + + err = uv_async_init(stream->loop, &s->async, uv__stream_osx_select_cb); + if (err) + goto failed_async_init; + + s->async.flags |= UV_HANDLE_INTERNAL; + uv__handle_unref(&s->async); + + err = uv_sem_init(&s->close_sem, 0); + if (err != 0) + goto failed_close_sem_init; + + err = uv_sem_init(&s->async_sem, 0); + if (err != 0) + goto failed_async_sem_init; + + s->fake_fd = fds[0]; + s->int_fd = fds[1]; + + old_fd = *fd; + s->stream = stream; + stream->select = s; + *fd = s->fake_fd; + + err = uv_thread_create(&s->thread, uv__stream_osx_select, stream); + if (err != 0) + goto failed_thread_create; + + return 0; + +failed_thread_create: + s->stream = NULL; + stream->select = NULL; + *fd = old_fd; + + uv_sem_destroy(&s->async_sem); + +failed_async_sem_init: + uv_sem_destroy(&s->close_sem); + +failed_close_sem_init: + uv__close(fds[0]); + uv__close(fds[1]); + uv_close((uv_handle_t*) &s->async, uv__stream_osx_cb_close); + return err; + +failed_async_init: + uv__free(s); + +failed_malloc: + uv__close(fds[0]); + uv__close(fds[1]); + + return err; +} +#endif /* defined(__APPLE__) */ + + +int uv__stream_open(uv_stream_t* stream, int fd, int flags) { +#if defined(__APPLE__) + int enable; +#endif + + if (!(stream->io_watcher.fd == -1 || stream->io_watcher.fd == fd)) + return UV_EBUSY; + + assert(fd >= 0); + stream->flags |= flags; + + if (stream->type == UV_TCP) { + if ((stream->flags & UV_HANDLE_TCP_NODELAY) && uv__tcp_nodelay(fd, 1)) + return UV__ERR(errno); + + /* TODO Use delay the user passed in. */ + if ((stream->flags & UV_HANDLE_TCP_KEEPALIVE) && + uv__tcp_keepalive(fd, 1, 60)) { + return UV__ERR(errno); + } + } + +#if defined(__APPLE__) + enable = 1; + if (setsockopt(fd, SOL_SOCKET, SO_OOBINLINE, &enable, sizeof(enable)) && + errno != ENOTSOCK && + errno != EINVAL) { + return UV__ERR(errno); + } +#endif + + stream->io_watcher.fd = fd; + + return 0; +} + + +void uv__stream_flush_write_queue(uv_stream_t* stream, int error) { + uv_write_t* req; + QUEUE* q; + while (!QUEUE_EMPTY(&stream->write_queue)) { + q = QUEUE_HEAD(&stream->write_queue); + QUEUE_REMOVE(q); + + req = QUEUE_DATA(q, uv_write_t, queue); + req->error = error; + + QUEUE_INSERT_TAIL(&stream->write_completed_queue, &req->queue); + } +} + + +void uv__stream_destroy(uv_stream_t* stream) { + assert(!uv__io_active(&stream->io_watcher, POLLIN | POLLOUT)); + assert(stream->flags & UV_HANDLE_CLOSED); + + if (stream->connect_req) { + uv__req_unregister(stream->loop, stream->connect_req); + stream->connect_req->cb(stream->connect_req, UV_ECANCELED); + stream->connect_req = NULL; + } + + uv__stream_flush_write_queue(stream, UV_ECANCELED); + uv__write_callbacks(stream); + + if (stream->shutdown_req) { + /* The ECANCELED error code is a lie, the shutdown(2) syscall is a + * fait accompli at this point. Maybe we should revisit this in v0.11. + * A possible reason for leaving it unchanged is that it informs the + * callee that the handle has been destroyed. + */ + uv__req_unregister(stream->loop, stream->shutdown_req); + stream->shutdown_req->cb(stream->shutdown_req, UV_ECANCELED); + stream->shutdown_req = NULL; + } + + assert(stream->write_queue_size == 0); +} + + +/* Implements a best effort approach to mitigating accept() EMFILE errors. + * We have a spare file descriptor stashed away that we close to get below + * the EMFILE limit. Next, we accept all pending connections and close them + * immediately to signal the clients that we're overloaded - and we are, but + * we still keep on trucking. + * + * There is one caveat: it's not reliable in a multi-threaded environment. + * The file descriptor limit is per process. Our party trick fails if another + * thread opens a file or creates a socket in the time window between us + * calling close() and accept(). + */ +static int uv__emfile_trick(uv_loop_t* loop, int accept_fd) { + int err; + int emfile_fd; + + if (loop->emfile_fd == -1) + return UV_EMFILE; + + uv__close(loop->emfile_fd); + loop->emfile_fd = -1; + + do { + err = uv__accept(accept_fd); + if (err >= 0) + uv__close(err); + } while (err >= 0 || err == UV_EINTR); + + emfile_fd = uv__open_cloexec("/", O_RDONLY); + if (emfile_fd >= 0) + loop->emfile_fd = emfile_fd; + + return err; +} + + +#if defined(UV_HAVE_KQUEUE) +# define UV_DEC_BACKLOG(w) w->rcount--; +#else +# define UV_DEC_BACKLOG(w) /* no-op */ +#endif /* defined(UV_HAVE_KQUEUE) */ + + +void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { + uv_stream_t* stream; + int err; + + stream = container_of(w, uv_stream_t, io_watcher); + assert(events & POLLIN); + assert(stream->accepted_fd == -1); + assert(!(stream->flags & UV_HANDLE_CLOSING)); + + uv__io_start(stream->loop, &stream->io_watcher, POLLIN); + + /* connection_cb can close the server socket while we're + * in the loop so check it on each iteration. + */ + while (uv__stream_fd(stream) != -1) { + assert(stream->accepted_fd == -1); + +#if defined(UV_HAVE_KQUEUE) + if (w->rcount <= 0) + return; +#endif /* defined(UV_HAVE_KQUEUE) */ + + err = uv__accept(uv__stream_fd(stream)); + if (err < 0) { + if (err == UV_EAGAIN || err == UV__ERR(EWOULDBLOCK)) + return; /* Not an error. */ + + if (err == UV_ECONNABORTED) + continue; /* Ignore. Nothing we can do about that. */ + + if (err == UV_EMFILE || err == UV_ENFILE) { + err = uv__emfile_trick(loop, uv__stream_fd(stream)); + if (err == UV_EAGAIN || err == UV__ERR(EWOULDBLOCK)) + break; + } + + stream->connection_cb(stream, err); + continue; + } + + UV_DEC_BACKLOG(w) + stream->accepted_fd = err; + stream->connection_cb(stream, 0); + + if (stream->accepted_fd != -1) { + /* The user hasn't yet accepted called uv_accept() */ + uv__io_stop(loop, &stream->io_watcher, POLLIN); + return; + } + + if (stream->type == UV_TCP && + (stream->flags & UV_HANDLE_TCP_SINGLE_ACCEPT)) { + /* Give other processes a chance to accept connections. */ + struct timespec timeout = { 0, 1 }; + nanosleep(&timeout, NULL); + } + } +} + + +#undef UV_DEC_BACKLOG + + +int uv_accept(uv_stream_t* server, uv_stream_t* client) { + int err; + + assert(server->loop == client->loop); + + if (server->accepted_fd == -1) + return UV_EAGAIN; + + switch (client->type) { + case UV_NAMED_PIPE: + case UV_TCP: + err = uv__stream_open(client, + server->accepted_fd, + UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); + if (err) { + /* TODO handle error */ + uv__close(server->accepted_fd); + goto done; + } + break; + + case UV_UDP: + err = uv_udp_open((uv_udp_t*) client, server->accepted_fd); + if (err) { + uv__close(server->accepted_fd); + goto done; + } + break; + + default: + return UV_EINVAL; + } + + client->flags |= UV_HANDLE_BOUND; + +done: + /* Process queued fds */ + if (server->queued_fds != NULL) { + uv__stream_queued_fds_t* queued_fds; + + queued_fds = server->queued_fds; + + /* Read first */ + server->accepted_fd = queued_fds->fds[0]; + + /* All read, free */ + assert(queued_fds->offset > 0); + if (--queued_fds->offset == 0) { + uv__free(queued_fds); + server->queued_fds = NULL; + } else { + /* Shift rest */ + memmove(queued_fds->fds, + queued_fds->fds + 1, + queued_fds->offset * sizeof(*queued_fds->fds)); + } + } else { + server->accepted_fd = -1; + if (err == 0) + uv__io_start(server->loop, &server->io_watcher, POLLIN); + } + return err; +} + + +int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) { + int err; + + switch (stream->type) { + case UV_TCP: + err = uv_tcp_listen((uv_tcp_t*)stream, backlog, cb); + break; + + case UV_NAMED_PIPE: + err = uv_pipe_listen((uv_pipe_t*)stream, backlog, cb); + break; + + default: + err = UV_EINVAL; + } + + if (err == 0) + uv__handle_start(stream); + + return err; +} + + +static void uv__drain(uv_stream_t* stream) { + uv_shutdown_t* req; + int err; + + assert(QUEUE_EMPTY(&stream->write_queue)); + uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT); + uv__stream_osx_interrupt_select(stream); + + /* Shutdown? */ + if ((stream->flags & UV_HANDLE_SHUTTING) && + !(stream->flags & UV_HANDLE_CLOSING) && + !(stream->flags & UV_HANDLE_SHUT)) { + assert(stream->shutdown_req); + + req = stream->shutdown_req; + stream->shutdown_req = NULL; + stream->flags &= ~UV_HANDLE_SHUTTING; + uv__req_unregister(stream->loop, req); + + err = 0; + if (shutdown(uv__stream_fd(stream), SHUT_WR)) + err = UV__ERR(errno); + + if (err == 0) + stream->flags |= UV_HANDLE_SHUT; + + if (req->cb != NULL) + req->cb(req, err); + } +} + + +static ssize_t uv__writev(int fd, struct iovec* vec, size_t n) { + if (n == 1) + return write(fd, vec->iov_base, vec->iov_len); + else + return writev(fd, vec, n); +} + + +static size_t uv__write_req_size(uv_write_t* req) { + size_t size; + + assert(req->bufs != NULL); + size = uv__count_bufs(req->bufs + req->write_index, + req->nbufs - req->write_index); + assert(req->handle->write_queue_size >= size); + + return size; +} + + +/* Returns 1 if all write request data has been written, or 0 if there is still + * more data to write. + * + * Note: the return value only says something about the *current* request. + * There may still be other write requests sitting in the queue. + */ +static int uv__write_req_update(uv_stream_t* stream, + uv_write_t* req, + size_t n) { + uv_buf_t* buf; + size_t len; + + assert(n <= stream->write_queue_size); + stream->write_queue_size -= n; + + buf = req->bufs + req->write_index; + + do { + len = n < buf->len ? n : buf->len; + buf->base += len; + buf->len -= len; + buf += (buf->len == 0); /* Advance to next buffer if this one is empty. */ + n -= len; + } while (n > 0); + + req->write_index = buf - req->bufs; + + return req->write_index == req->nbufs; +} + + +static void uv__write_req_finish(uv_write_t* req) { + uv_stream_t* stream = req->handle; + + /* Pop the req off tcp->write_queue. */ + QUEUE_REMOVE(&req->queue); + + /* Only free when there was no error. On error, we touch up write_queue_size + * right before making the callback. The reason we don't do that right away + * is that a write_queue_size > 0 is our only way to signal to the user that + * they should stop writing - which they should if we got an error. Something + * to revisit in future revisions of the libuv API. + */ + if (req->error == 0) { + if (req->bufs != req->bufsml) + uv__free(req->bufs); + req->bufs = NULL; + } + + /* Add it to the write_completed_queue where it will have its + * callback called in the near future. + */ + QUEUE_INSERT_TAIL(&stream->write_completed_queue, &req->queue); + uv__io_feed(stream->loop, &stream->io_watcher); +} + + +static int uv__handle_fd(uv_handle_t* handle) { + switch (handle->type) { + case UV_NAMED_PIPE: + case UV_TCP: + return ((uv_stream_t*) handle)->io_watcher.fd; + + case UV_UDP: + return ((uv_udp_t*) handle)->io_watcher.fd; + + default: + return -1; + } +} + +static void uv__write(uv_stream_t* stream) { + struct iovec* iov; + QUEUE* q; + uv_write_t* req; + int iovmax; + int iovcnt; + ssize_t n; + int err; + +start: + + assert(uv__stream_fd(stream) >= 0); + + if (QUEUE_EMPTY(&stream->write_queue)) + return; + + q = QUEUE_HEAD(&stream->write_queue); + req = QUEUE_DATA(q, uv_write_t, queue); + assert(req->handle == stream); + + /* + * Cast to iovec. We had to have our own uv_buf_t instead of iovec + * because Windows's WSABUF is not an iovec. + */ + assert(sizeof(uv_buf_t) == sizeof(struct iovec)); + iov = (struct iovec*) &(req->bufs[req->write_index]); + iovcnt = req->nbufs - req->write_index; + + iovmax = uv__getiovmax(); + + /* Limit iov count to avoid EINVALs from writev() */ + if (iovcnt > iovmax) + iovcnt = iovmax; + + /* + * Now do the actual writev. Note that we've been updating the pointers + * inside the iov each time we write. So there is no need to offset it. + */ + + if (req->send_handle) { + int fd_to_send; + struct msghdr msg; + struct cmsghdr *cmsg; + union { + char data[64]; + struct cmsghdr alias; + } scratch; + + if (uv__is_closing(req->send_handle)) { + err = UV_EBADF; + goto error; + } + + fd_to_send = uv__handle_fd((uv_handle_t*) req->send_handle); + + memset(&scratch, 0, sizeof(scratch)); + + assert(fd_to_send >= 0); + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = iov; + msg.msg_iovlen = iovcnt; + msg.msg_flags = 0; + + msg.msg_control = &scratch.alias; + msg.msg_controllen = CMSG_SPACE(sizeof(fd_to_send)); + + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(fd_to_send)); + + /* silence aliasing warning */ + { + void* pv = CMSG_DATA(cmsg); + int* pi = pv; + *pi = fd_to_send; + } + + do + n = sendmsg(uv__stream_fd(stream), &msg, 0); + while (n == -1 && RETRY_ON_WRITE_ERROR(errno)); + + /* Ensure the handle isn't sent again in case this is a partial write. */ + if (n >= 0) + req->send_handle = NULL; + } else { + do + n = uv__writev(uv__stream_fd(stream), iov, iovcnt); + while (n == -1 && RETRY_ON_WRITE_ERROR(errno)); + } + + if (n == -1 && !IS_TRANSIENT_WRITE_ERROR(errno, req->send_handle)) { + err = UV__ERR(errno); + goto error; + } + + if (n >= 0 && uv__write_req_update(stream, req, n)) { + uv__write_req_finish(req); + return; /* TODO(bnoordhuis) Start trying to write the next request. */ + } + + /* If this is a blocking stream, try again. */ + if (stream->flags & UV_HANDLE_BLOCKING_WRITES) + goto start; + + /* We're not done. */ + uv__io_start(stream->loop, &stream->io_watcher, POLLOUT); + + /* Notify select() thread about state change */ + uv__stream_osx_interrupt_select(stream); + + return; + +error: + req->error = err; + uv__write_req_finish(req); + uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT); + if (!uv__io_active(&stream->io_watcher, POLLIN)) + uv__handle_stop(stream); + uv__stream_osx_interrupt_select(stream); +} + + +static void uv__write_callbacks(uv_stream_t* stream) { + uv_write_t* req; + QUEUE* q; + QUEUE pq; + + if (QUEUE_EMPTY(&stream->write_completed_queue)) + return; + + QUEUE_MOVE(&stream->write_completed_queue, &pq); + + while (!QUEUE_EMPTY(&pq)) { + /* Pop a req off write_completed_queue. */ + q = QUEUE_HEAD(&pq); + req = QUEUE_DATA(q, uv_write_t, queue); + QUEUE_REMOVE(q); + uv__req_unregister(stream->loop, req); + + if (req->bufs != NULL) { + stream->write_queue_size -= uv__write_req_size(req); + if (req->bufs != req->bufsml) + uv__free(req->bufs); + req->bufs = NULL; + } + + /* NOTE: call callback AFTER freeing the request data. */ + if (req->cb) + req->cb(req, req->error); + } +} + + +uv_handle_type uv__handle_type(int fd) { + struct sockaddr_storage ss; + socklen_t sslen; + socklen_t len; + int type; + + memset(&ss, 0, sizeof(ss)); + sslen = sizeof(ss); + + if (getsockname(fd, (struct sockaddr*)&ss, &sslen)) + return UV_UNKNOWN_HANDLE; + + len = sizeof type; + + if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &len)) + return UV_UNKNOWN_HANDLE; + + if (type == SOCK_STREAM) { +#if defined(_AIX) || defined(__DragonFly__) + /* on AIX/DragonFly the getsockname call returns an empty sa structure + * for sockets of type AF_UNIX. For all other types it will + * return a properly filled in structure. + */ + if (sslen == 0) + return UV_NAMED_PIPE; +#endif + switch (ss.ss_family) { + case AF_UNIX: + return UV_NAMED_PIPE; + case AF_INET: + case AF_INET6: + return UV_TCP; + } + } + + if (type == SOCK_DGRAM && + (ss.ss_family == AF_INET || ss.ss_family == AF_INET6)) + return UV_UDP; + + return UV_UNKNOWN_HANDLE; +} + + +static void uv__stream_eof(uv_stream_t* stream, const uv_buf_t* buf) { + stream->flags |= UV_HANDLE_READ_EOF; + stream->flags &= ~UV_HANDLE_READING; + uv__io_stop(stream->loop, &stream->io_watcher, POLLIN); + if (!uv__io_active(&stream->io_watcher, POLLOUT)) + uv__handle_stop(stream); + uv__stream_osx_interrupt_select(stream); + stream->read_cb(stream, UV_EOF, buf); +} + + +static int uv__stream_queue_fd(uv_stream_t* stream, int fd) { + uv__stream_queued_fds_t* queued_fds; + unsigned int queue_size; + + queued_fds = stream->queued_fds; + if (queued_fds == NULL) { + queue_size = 8; + queued_fds = uv__malloc((queue_size - 1) * sizeof(*queued_fds->fds) + + sizeof(*queued_fds)); + if (queued_fds == NULL) + return UV_ENOMEM; + queued_fds->size = queue_size; + queued_fds->offset = 0; + stream->queued_fds = queued_fds; + + /* Grow */ + } else if (queued_fds->size == queued_fds->offset) { + queue_size = queued_fds->size + 8; + queued_fds = uv__realloc(queued_fds, + (queue_size - 1) * sizeof(*queued_fds->fds) + + sizeof(*queued_fds)); + + /* + * Allocation failure, report back. + * NOTE: if it is fatal - sockets will be closed in uv__stream_close + */ + if (queued_fds == NULL) + return UV_ENOMEM; + queued_fds->size = queue_size; + stream->queued_fds = queued_fds; + } + + /* Put fd in a queue */ + queued_fds->fds[queued_fds->offset++] = fd; + + return 0; +} + + +#if defined(__PASE__) +/* on IBMi PASE the control message length can not exceed 256. */ +# define UV__CMSG_FD_COUNT 60 +#else +# define UV__CMSG_FD_COUNT 64 +#endif +#define UV__CMSG_FD_SIZE (UV__CMSG_FD_COUNT * sizeof(int)) + + +static int uv__stream_recv_cmsg(uv_stream_t* stream, struct msghdr* msg) { + struct cmsghdr* cmsg; + + for (cmsg = CMSG_FIRSTHDR(msg); cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) { + char* start; + char* end; + int err; + void* pv; + int* pi; + unsigned int i; + unsigned int count; + + if (cmsg->cmsg_type != SCM_RIGHTS) { + fprintf(stderr, "ignoring non-SCM_RIGHTS ancillary data: %d\n", + cmsg->cmsg_type); + continue; + } + + /* silence aliasing warning */ + pv = CMSG_DATA(cmsg); + pi = pv; + + /* Count available fds */ + start = (char*) cmsg; + end = (char*) cmsg + cmsg->cmsg_len; + count = 0; + while (start + CMSG_LEN(count * sizeof(*pi)) < end) + count++; + assert(start + CMSG_LEN(count * sizeof(*pi)) == end); + + for (i = 0; i < count; i++) { + /* Already has accepted fd, queue now */ + if (stream->accepted_fd != -1) { + err = uv__stream_queue_fd(stream, pi[i]); + if (err != 0) { + /* Close rest */ + for (; i < count; i++) + uv__close(pi[i]); + return err; + } + } else { + stream->accepted_fd = pi[i]; + } + } + } + + return 0; +} + + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wgnu-folding-constant" +# pragma clang diagnostic ignored "-Wvla-extension" +#endif + +static void uv__read(uv_stream_t* stream) { + uv_buf_t buf; + ssize_t nread; + struct msghdr msg; + char cmsg_space[CMSG_SPACE(UV__CMSG_FD_SIZE)]; + int count; + int err; + int is_ipc; + + stream->flags &= ~UV_HANDLE_READ_PARTIAL; + + /* Prevent loop starvation when the data comes in as fast as (or faster than) + * we can read it. XXX Need to rearm fd if we switch to edge-triggered I/O. + */ + count = 32; + + is_ipc = stream->type == UV_NAMED_PIPE && ((uv_pipe_t*) stream)->ipc; + + /* XXX: Maybe instead of having UV_HANDLE_READING we just test if + * tcp->read_cb is NULL or not? + */ + while (stream->read_cb + && (stream->flags & UV_HANDLE_READING) + && (count-- > 0)) { + assert(stream->alloc_cb != NULL); + + buf = uv_buf_init(NULL, 0); + stream->alloc_cb((uv_handle_t*)stream, 64 * 1024, &buf); + if (buf.base == NULL || buf.len == 0) { + /* User indicates it can't or won't handle the read. */ + stream->read_cb(stream, UV_ENOBUFS, &buf); + return; + } + + assert(buf.base != NULL); + assert(uv__stream_fd(stream) >= 0); + + if (!is_ipc) { + do { + nread = read(uv__stream_fd(stream), buf.base, buf.len); + } + while (nread < 0 && errno == EINTR); + } else { + /* ipc uses recvmsg */ + msg.msg_flags = 0; + msg.msg_iov = (struct iovec*) &buf; + msg.msg_iovlen = 1; + msg.msg_name = NULL; + msg.msg_namelen = 0; + /* Set up to receive a descriptor even if one isn't in the message */ + msg.msg_controllen = sizeof(cmsg_space); + msg.msg_control = cmsg_space; + + do { + nread = uv__recvmsg(uv__stream_fd(stream), &msg, 0); + } + while (nread < 0 && errno == EINTR); + } + + if (nread < 0) { + /* Error */ + if (errno == EAGAIN || errno == EWOULDBLOCK) { + /* Wait for the next one. */ + if (stream->flags & UV_HANDLE_READING) { + uv__io_start(stream->loop, &stream->io_watcher, POLLIN); + uv__stream_osx_interrupt_select(stream); + } + stream->read_cb(stream, 0, &buf); +#if defined(__CYGWIN__) || defined(__MSYS__) + } else if (errno == ECONNRESET && stream->type == UV_NAMED_PIPE) { + uv__stream_eof(stream, &buf); + return; +#endif + } else { + /* Error. User should call uv_close(). */ + stream->read_cb(stream, UV__ERR(errno), &buf); + if (stream->flags & UV_HANDLE_READING) { + stream->flags &= ~UV_HANDLE_READING; + uv__io_stop(stream->loop, &stream->io_watcher, POLLIN); + if (!uv__io_active(&stream->io_watcher, POLLOUT)) + uv__handle_stop(stream); + uv__stream_osx_interrupt_select(stream); + } + } + return; + } else if (nread == 0) { + uv__stream_eof(stream, &buf); + return; + } else { + /* Successful read */ + ssize_t buflen = buf.len; + + if (is_ipc) { + err = uv__stream_recv_cmsg(stream, &msg); + if (err != 0) { + stream->read_cb(stream, err, &buf); + return; + } + } + +#if defined(__MVS__) + if (is_ipc && msg.msg_controllen > 0) { + uv_buf_t blankbuf; + int nread; + struct iovec *old; + + blankbuf.base = 0; + blankbuf.len = 0; + old = msg.msg_iov; + msg.msg_iov = (struct iovec*) &blankbuf; + nread = 0; + do { + nread = uv__recvmsg(uv__stream_fd(stream), &msg, 0); + err = uv__stream_recv_cmsg(stream, &msg); + if (err != 0) { + stream->read_cb(stream, err, &buf); + msg.msg_iov = old; + return; + } + } while (nread == 0 && msg.msg_controllen > 0); + msg.msg_iov = old; + } +#endif + stream->read_cb(stream, nread, &buf); + + /* Return if we didn't fill the buffer, there is no more data to read. */ + if (nread < buflen) { + stream->flags |= UV_HANDLE_READ_PARTIAL; + return; + } + } + } +} + + +#ifdef __clang__ +# pragma clang diagnostic pop +#endif + +#undef UV__CMSG_FD_COUNT +#undef UV__CMSG_FD_SIZE + + +int uv_shutdown(uv_shutdown_t* req, uv_stream_t* stream, uv_shutdown_cb cb) { + assert(stream->type == UV_TCP || + stream->type == UV_TTY || + stream->type == UV_NAMED_PIPE); + + if (!(stream->flags & UV_HANDLE_WRITABLE) || + stream->flags & UV_HANDLE_SHUT || + stream->flags & UV_HANDLE_SHUTTING || + uv__is_closing(stream)) { + return UV_ENOTCONN; + } + + assert(uv__stream_fd(stream) >= 0); + + /* Initialize request */ + uv__req_init(stream->loop, req, UV_SHUTDOWN); + req->handle = stream; + req->cb = cb; + stream->shutdown_req = req; + stream->flags |= UV_HANDLE_SHUTTING; + + uv__io_start(stream->loop, &stream->io_watcher, POLLOUT); + uv__stream_osx_interrupt_select(stream); + + return 0; +} + + +static void uv__stream_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { + uv_stream_t* stream; + + stream = container_of(w, uv_stream_t, io_watcher); + + assert(stream->type == UV_TCP || + stream->type == UV_NAMED_PIPE || + stream->type == UV_TTY); + assert(!(stream->flags & UV_HANDLE_CLOSING)); + + if (stream->connect_req) { + uv__stream_connect(stream); + return; + } + + assert(uv__stream_fd(stream) >= 0); + + /* Ignore POLLHUP here. Even if it's set, there may still be data to read. */ + if (events & (POLLIN | POLLERR | POLLHUP)) + uv__read(stream); + + if (uv__stream_fd(stream) == -1) + return; /* read_cb closed stream. */ + + /* Short-circuit iff POLLHUP is set, the user is still interested in read + * events and uv__read() reported a partial read but not EOF. If the EOF + * flag is set, uv__read() called read_cb with err=UV_EOF and we don't + * have to do anything. If the partial read flag is not set, we can't + * report the EOF yet because there is still data to read. + */ + if ((events & POLLHUP) && + (stream->flags & UV_HANDLE_READING) && + (stream->flags & UV_HANDLE_READ_PARTIAL) && + !(stream->flags & UV_HANDLE_READ_EOF)) { + uv_buf_t buf = { NULL, 0 }; + uv__stream_eof(stream, &buf); + } + + if (uv__stream_fd(stream) == -1) + return; /* read_cb closed stream. */ + + if (events & (POLLOUT | POLLERR | POLLHUP)) { + uv__write(stream); + uv__write_callbacks(stream); + + /* Write queue drained. */ + if (QUEUE_EMPTY(&stream->write_queue)) + uv__drain(stream); + } +} + + +/** + * We get called here from directly following a call to connect(2). + * In order to determine if we've errored out or succeeded must call + * getsockopt. + */ +static void uv__stream_connect(uv_stream_t* stream) { + int error; + uv_connect_t* req = stream->connect_req; + socklen_t errorsize = sizeof(int); + + assert(stream->type == UV_TCP || stream->type == UV_NAMED_PIPE); + assert(req); + + if (stream->delayed_error) { + /* To smooth over the differences between unixes errors that + * were reported synchronously on the first connect can be delayed + * until the next tick--which is now. + */ + error = stream->delayed_error; + stream->delayed_error = 0; + } else { + /* Normal situation: we need to get the socket error from the kernel. */ + assert(uv__stream_fd(stream) >= 0); + getsockopt(uv__stream_fd(stream), + SOL_SOCKET, + SO_ERROR, + &error, + &errorsize); + error = UV__ERR(error); + } + + if (error == UV__ERR(EINPROGRESS)) + return; + + stream->connect_req = NULL; + uv__req_unregister(stream->loop, req); + + if (error < 0 || QUEUE_EMPTY(&stream->write_queue)) { + uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT); + } + + if (req->cb) + req->cb(req, error); + + if (uv__stream_fd(stream) == -1) + return; + + if (error < 0) { + uv__stream_flush_write_queue(stream, UV_ECANCELED); + uv__write_callbacks(stream); + } +} + + +int uv_write2(uv_write_t* req, + uv_stream_t* stream, + const uv_buf_t bufs[], + unsigned int nbufs, + uv_stream_t* send_handle, + uv_write_cb cb) { + int empty_queue; + + assert(nbufs > 0); + assert((stream->type == UV_TCP || + stream->type == UV_NAMED_PIPE || + stream->type == UV_TTY) && + "uv_write (unix) does not yet support other types of streams"); + + if (uv__stream_fd(stream) < 0) + return UV_EBADF; + + if (!(stream->flags & UV_HANDLE_WRITABLE)) + return UV_EPIPE; + + if (send_handle) { + if (stream->type != UV_NAMED_PIPE || !((uv_pipe_t*)stream)->ipc) + return UV_EINVAL; + + /* XXX We abuse uv_write2() to send over UDP handles to child processes. + * Don't call uv__stream_fd() on those handles, it's a macro that on OS X + * evaluates to a function that operates on a uv_stream_t with a couple of + * OS X specific fields. On other Unices it does (handle)->io_watcher.fd, + * which works but only by accident. + */ + if (uv__handle_fd((uv_handle_t*) send_handle) < 0) + return UV_EBADF; + +#if defined(__CYGWIN__) || defined(__MSYS__) + /* Cygwin recvmsg always sets msg_controllen to zero, so we cannot send it. + See https://github.com/mirror/newlib-cygwin/blob/86fc4bf0/winsup/cygwin/fhandler_socket.cc#L1736-L1743 */ + return UV_ENOSYS; +#endif + } + + /* It's legal for write_queue_size > 0 even when the write_queue is empty; + * it means there are error-state requests in the write_completed_queue that + * will touch up write_queue_size later, see also uv__write_req_finish(). + * We could check that write_queue is empty instead but that implies making + * a write() syscall when we know that the handle is in error mode. + */ + empty_queue = (stream->write_queue_size == 0); + + /* Initialize the req */ + uv__req_init(stream->loop, req, UV_WRITE); + req->cb = cb; + req->handle = stream; + req->error = 0; + req->send_handle = send_handle; + QUEUE_INIT(&req->queue); + + req->bufs = req->bufsml; + if (nbufs > ARRAY_SIZE(req->bufsml)) + req->bufs = uv__malloc(nbufs * sizeof(bufs[0])); + + if (req->bufs == NULL) + return UV_ENOMEM; + + memcpy(req->bufs, bufs, nbufs * sizeof(bufs[0])); + req->nbufs = nbufs; + req->write_index = 0; + stream->write_queue_size += uv__count_bufs(bufs, nbufs); + + /* Append the request to write_queue. */ + QUEUE_INSERT_TAIL(&stream->write_queue, &req->queue); + + /* If the queue was empty when this function began, we should attempt to + * do the write immediately. Otherwise start the write_watcher and wait + * for the fd to become writable. + */ + if (stream->connect_req) { + /* Still connecting, do nothing. */ + } + else if (empty_queue) { + uv__write(stream); + } + else { + /* + * blocking streams should never have anything in the queue. + * if this assert fires then somehow the blocking stream isn't being + * sufficiently flushed in uv__write. + */ + assert(!(stream->flags & UV_HANDLE_BLOCKING_WRITES)); + uv__io_start(stream->loop, &stream->io_watcher, POLLOUT); + uv__stream_osx_interrupt_select(stream); + } + + return 0; +} + + +/* The buffers to be written must remain valid until the callback is called. + * This is not required for the uv_buf_t array. + */ +int uv_write(uv_write_t* req, + uv_stream_t* handle, + const uv_buf_t bufs[], + unsigned int nbufs, + uv_write_cb cb) { + return uv_write2(req, handle, bufs, nbufs, NULL, cb); +} + + +void uv_try_write_cb(uv_write_t* req, int status) { + /* Should not be called */ + abort(); +} + + +int uv_try_write(uv_stream_t* stream, + const uv_buf_t bufs[], + unsigned int nbufs) { + int r; + int has_pollout; + size_t written; + size_t req_size; + uv_write_t req; + + /* Connecting or already writing some data */ + if (stream->connect_req != NULL || stream->write_queue_size != 0) + return UV_EAGAIN; + + has_pollout = uv__io_active(&stream->io_watcher, POLLOUT); + + r = uv_write(&req, stream, bufs, nbufs, uv_try_write_cb); + if (r != 0) + return r; + + /* Remove not written bytes from write queue size */ + written = uv__count_bufs(bufs, nbufs); + if (req.bufs != NULL) + req_size = uv__write_req_size(&req); + else + req_size = 0; + written -= req_size; + stream->write_queue_size -= req_size; + + /* Unqueue request, regardless of immediateness */ + QUEUE_REMOVE(&req.queue); + uv__req_unregister(stream->loop, &req); + if (req.bufs != req.bufsml) + uv__free(req.bufs); + req.bufs = NULL; + + /* Do not poll for writable, if we wasn't before calling this */ + if (!has_pollout) { + uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT); + uv__stream_osx_interrupt_select(stream); + } + + if (written == 0 && req_size != 0) + return req.error < 0 ? req.error : UV_EAGAIN; + else + return written; +} + + +int uv_read_start(uv_stream_t* stream, + uv_alloc_cb alloc_cb, + uv_read_cb read_cb) { + assert(stream->type == UV_TCP || stream->type == UV_NAMED_PIPE || + stream->type == UV_TTY); + + if (stream->flags & UV_HANDLE_CLOSING) + return UV_EINVAL; + + if (!(stream->flags & UV_HANDLE_READABLE)) + return UV_ENOTCONN; + + /* The UV_HANDLE_READING flag is irrelevant of the state of the tcp - it just + * expresses the desired state of the user. + */ + stream->flags |= UV_HANDLE_READING; + + /* TODO: try to do the read inline? */ + /* TODO: keep track of tcp state. If we've gotten a EOF then we should + * not start the IO watcher. + */ + assert(uv__stream_fd(stream) >= 0); + assert(alloc_cb); + + stream->read_cb = read_cb; + stream->alloc_cb = alloc_cb; + + uv__io_start(stream->loop, &stream->io_watcher, POLLIN); + uv__handle_start(stream); + uv__stream_osx_interrupt_select(stream); + + return 0; +} + + +int uv_read_stop(uv_stream_t* stream) { + if (!(stream->flags & UV_HANDLE_READING)) + return 0; + + stream->flags &= ~UV_HANDLE_READING; + uv__io_stop(stream->loop, &stream->io_watcher, POLLIN); + if (!uv__io_active(&stream->io_watcher, POLLOUT)) + uv__handle_stop(stream); + uv__stream_osx_interrupt_select(stream); + + stream->read_cb = NULL; + stream->alloc_cb = NULL; + return 0; +} + + +int uv_is_readable(const uv_stream_t* stream) { + return !!(stream->flags & UV_HANDLE_READABLE); +} + + +int uv_is_writable(const uv_stream_t* stream) { + return !!(stream->flags & UV_HANDLE_WRITABLE); +} + + +#if defined(__APPLE__) +int uv___stream_fd(const uv_stream_t* handle) { + const uv__stream_select_t* s; + + assert(handle->type == UV_TCP || + handle->type == UV_TTY || + handle->type == UV_NAMED_PIPE); + + s = handle->select; + if (s != NULL) + return s->fd; + + return handle->io_watcher.fd; +} +#endif /* defined(__APPLE__) */ + + +void uv__stream_close(uv_stream_t* handle) { + unsigned int i; + uv__stream_queued_fds_t* queued_fds; + +#if defined(__APPLE__) + /* Terminate select loop first */ + if (handle->select != NULL) { + uv__stream_select_t* s; + + s = handle->select; + + uv_sem_post(&s->close_sem); + uv_sem_post(&s->async_sem); + uv__stream_osx_interrupt_select(handle); + uv_thread_join(&s->thread); + uv_sem_destroy(&s->close_sem); + uv_sem_destroy(&s->async_sem); + uv__close(s->fake_fd); + uv__close(s->int_fd); + uv_close((uv_handle_t*) &s->async, uv__stream_osx_cb_close); + + handle->select = NULL; + } +#endif /* defined(__APPLE__) */ + + uv__io_close(handle->loop, &handle->io_watcher); + uv_read_stop(handle); + uv__handle_stop(handle); + handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); + + if (handle->io_watcher.fd != -1) { + /* Don't close stdio file descriptors. Nothing good comes from it. */ + if (handle->io_watcher.fd > STDERR_FILENO) + uv__close(handle->io_watcher.fd); + handle->io_watcher.fd = -1; + } + + if (handle->accepted_fd != -1) { + uv__close(handle->accepted_fd); + handle->accepted_fd = -1; + } + + /* Close all queued fds */ + if (handle->queued_fds != NULL) { + queued_fds = handle->queued_fds; + for (i = 0; i < queued_fds->offset; i++) + uv__close(queued_fds->fds[i]); + uv__free(handle->queued_fds); + handle->queued_fds = NULL; + } + + assert(!uv__io_active(&handle->io_watcher, POLLIN | POLLOUT)); +} + + +int uv_stream_set_blocking(uv_stream_t* handle, int blocking) { + /* Don't need to check the file descriptor, uv__nonblock() + * will fail with EBADF if it's not valid. + */ + return uv__nonblock(uv__stream_fd(handle), !blocking); +} diff --git a/include/libuv/src/unix/sunos.c b/include/libuv/src/unix/sunos.c new file mode 100644 index 000000000..d511c18b8 --- /dev/null +++ b/include/libuv/src/unix/sunos.c @@ -0,0 +1,867 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include +#include +#include +#include + +#ifndef SUNOS_NO_IFADDRS +# include +#endif +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#define PORT_FIRED 0x69 +#define PORT_UNUSED 0x0 +#define PORT_LOADED 0x99 +#define PORT_DELETED -1 + +#if (!defined(_LP64)) && (_FILE_OFFSET_BITS - 0 == 64) +#define PROCFS_FILE_OFFSET_BITS_HACK 1 +#undef _FILE_OFFSET_BITS +#else +#define PROCFS_FILE_OFFSET_BITS_HACK 0 +#endif + +#include + +#if (PROCFS_FILE_OFFSET_BITS_HACK - 0 == 1) +#define _FILE_OFFSET_BITS 64 +#endif + + +int uv__platform_loop_init(uv_loop_t* loop) { + int err; + int fd; + + loop->fs_fd = -1; + loop->backend_fd = -1; + + fd = port_create(); + if (fd == -1) + return UV__ERR(errno); + + err = uv__cloexec(fd, 1); + if (err) { + uv__close(fd); + return err; + } + loop->backend_fd = fd; + + return 0; +} + + +void uv__platform_loop_delete(uv_loop_t* loop) { + if (loop->fs_fd != -1) { + uv__close(loop->fs_fd); + loop->fs_fd = -1; + } + + if (loop->backend_fd != -1) { + uv__close(loop->backend_fd); + loop->backend_fd = -1; + } +} + + +int uv__io_fork(uv_loop_t* loop) { +#if defined(PORT_SOURCE_FILE) + if (loop->fs_fd != -1) { + /* stop the watcher before we blow away its fileno */ + uv__io_stop(loop, &loop->fs_event_watcher, POLLIN); + } +#endif + uv__platform_loop_delete(loop); + return uv__platform_loop_init(loop); +} + + +void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { + struct port_event* events; + uintptr_t i; + uintptr_t nfds; + + assert(loop->watchers != NULL); + assert(fd >= 0); + + events = (struct port_event*) loop->watchers[loop->nwatchers]; + nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; + if (events == NULL) + return; + + /* Invalidate events with same file descriptor */ + for (i = 0; i < nfds; i++) + if ((int) events[i].portev_object == fd) + events[i].portev_object = -1; +} + + +int uv__io_check_fd(uv_loop_t* loop, int fd) { + if (port_associate(loop->backend_fd, PORT_SOURCE_FD, fd, POLLIN, 0)) + return UV__ERR(errno); + + if (port_dissociate(loop->backend_fd, PORT_SOURCE_FD, fd)) { + perror("(libuv) port_dissociate()"); + abort(); + } + + return 0; +} + + +void uv__io_poll(uv_loop_t* loop, int timeout) { + struct port_event events[1024]; + struct port_event* pe; + struct timespec spec; + QUEUE* q; + uv__io_t* w; + sigset_t* pset; + sigset_t set; + uint64_t base; + uint64_t diff; + uint64_t idle_poll; + unsigned int nfds; + unsigned int i; + int saved_errno; + int have_signals; + int nevents; + int count; + int err; + int fd; + int user_timeout; + int reset_timeout; + + if (loop->nfds == 0) { + assert(QUEUE_EMPTY(&loop->watcher_queue)); + return; + } + + while (!QUEUE_EMPTY(&loop->watcher_queue)) { + q = QUEUE_HEAD(&loop->watcher_queue); + QUEUE_REMOVE(q); + QUEUE_INIT(q); + + w = QUEUE_DATA(q, uv__io_t, watcher_queue); + assert(w->pevents != 0); + + if (port_associate(loop->backend_fd, + PORT_SOURCE_FD, + w->fd, + w->pevents, + 0)) { + perror("(libuv) port_associate()"); + abort(); + } + + w->events = w->pevents; + } + + pset = NULL; + if (loop->flags & UV_LOOP_BLOCK_SIGPROF) { + pset = &set; + sigemptyset(pset); + sigaddset(pset, SIGPROF); + } + + assert(timeout >= -1); + base = loop->time; + count = 48; /* Benchmarks suggest this gives the best throughput. */ + + if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { + reset_timeout = 1; + user_timeout = timeout; + timeout = 0; + } else { + reset_timeout = 0; + } + + for (;;) { + /* Only need to set the provider_entry_time if timeout != 0. The function + * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. + */ + if (timeout != 0) + uv__metrics_set_provider_entry_time(loop); + + if (timeout != -1) { + spec.tv_sec = timeout / 1000; + spec.tv_nsec = (timeout % 1000) * 1000000; + } + + /* Work around a kernel bug where nfds is not updated. */ + events[0].portev_source = 0; + + nfds = 1; + saved_errno = 0; + + if (pset != NULL) + pthread_sigmask(SIG_BLOCK, pset, NULL); + + err = port_getn(loop->backend_fd, + events, + ARRAY_SIZE(events), + &nfds, + timeout == -1 ? NULL : &spec); + + if (pset != NULL) + pthread_sigmask(SIG_UNBLOCK, pset, NULL); + + if (err) { + /* Work around another kernel bug: port_getn() may return events even + * on error. + */ + if (errno == EINTR || errno == ETIME) { + saved_errno = errno; + } else { + perror("(libuv) port_getn()"); + abort(); + } + } + + /* Update loop->time unconditionally. It's tempting to skip the update when + * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the + * operating system didn't reschedule our process while in the syscall. + */ + SAVE_ERRNO(uv__update_time(loop)); + + if (events[0].portev_source == 0) { + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + + if (timeout == 0) + return; + + if (timeout == -1) + continue; + + goto update_timeout; + } + + if (nfds == 0) { + assert(timeout != -1); + return; + } + + have_signals = 0; + nevents = 0; + + assert(loop->watchers != NULL); + loop->watchers[loop->nwatchers] = (void*) events; + loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds; + for (i = 0; i < nfds; i++) { + pe = events + i; + fd = pe->portev_object; + + /* Skip invalidated events, see uv__platform_invalidate_fd */ + if (fd == -1) + continue; + + assert(fd >= 0); + assert((unsigned) fd < loop->nwatchers); + + w = loop->watchers[fd]; + + /* File descriptor that we've stopped watching, ignore. */ + if (w == NULL) + continue; + + /* Run signal watchers last. This also affects child process watchers + * because those are implemented in terms of signal watchers. + */ + if (w == &loop->signal_io_watcher) { + have_signals = 1; + } else { + uv__metrics_update_idle_time(loop); + w->cb(loop, w, pe->portev_events); + } + + nevents++; + + if (w != loop->watchers[fd]) + continue; /* Disabled by callback. */ + + /* Events Ports operates in oneshot mode, rearm timer on next run. */ + if (w->pevents != 0 && QUEUE_EMPTY(&w->watcher_queue)) + QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue); + } + + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + + if (have_signals != 0) { + uv__metrics_update_idle_time(loop); + loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN); + } + + loop->watchers[loop->nwatchers] = NULL; + loop->watchers[loop->nwatchers + 1] = NULL; + + if (have_signals != 0) + return; /* Event loop should cycle now so don't poll again. */ + + if (nevents != 0) { + if (nfds == ARRAY_SIZE(events) && --count != 0) { + /* Poll for more events but don't block this time. */ + timeout = 0; + continue; + } + return; + } + + if (saved_errno == ETIME) { + assert(timeout != -1); + return; + } + + if (timeout == 0) + return; + + if (timeout == -1) + continue; + +update_timeout: + assert(timeout > 0); + + diff = loop->time - base; + if (diff >= (uint64_t) timeout) + return; + + timeout -= diff; + } +} + + +uint64_t uv__hrtime(uv_clocktype_t type) { + return gethrtime(); +} + + +/* + * We could use a static buffer for the path manipulations that we need outside + * of the function, but this function could be called by multiple consumers and + * we don't want to potentially create a race condition in the use of snprintf. + */ +int uv_exepath(char* buffer, size_t* size) { + ssize_t res; + char buf[128]; + + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + + snprintf(buf, sizeof(buf), "/proc/%lu/path/a.out", (unsigned long) getpid()); + + res = *size - 1; + if (res > 0) + res = readlink(buf, buffer, res); + + if (res == -1) + return UV__ERR(errno); + + buffer[res] = '\0'; + *size = res; + return 0; +} + + +uint64_t uv_get_free_memory(void) { + return (uint64_t) sysconf(_SC_PAGESIZE) * sysconf(_SC_AVPHYS_PAGES); +} + + +uint64_t uv_get_total_memory(void) { + return (uint64_t) sysconf(_SC_PAGESIZE) * sysconf(_SC_PHYS_PAGES); +} + + +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + +void uv_loadavg(double avg[3]) { + (void) getloadavg(avg, 3); +} + + +#if defined(PORT_SOURCE_FILE) + +static int uv__fs_event_rearm(uv_fs_event_t *handle) { + if (handle->fd == -1) + return UV_EBADF; + + if (port_associate(handle->loop->fs_fd, + PORT_SOURCE_FILE, + (uintptr_t) &handle->fo, + FILE_ATTRIB | FILE_MODIFIED, + handle) == -1) { + return UV__ERR(errno); + } + handle->fd = PORT_LOADED; + + return 0; +} + + +static void uv__fs_event_read(uv_loop_t* loop, + uv__io_t* w, + unsigned int revents) { + uv_fs_event_t *handle = NULL; + timespec_t timeout; + port_event_t pe; + int events; + int r; + + (void) w; + (void) revents; + + do { + uint_t n = 1; + + /* + * Note that our use of port_getn() here (and not port_get()) is deliberate: + * there is a bug in event ports (Sun bug 6456558) whereby a zeroed timeout + * causes port_get() to return success instead of ETIME when there aren't + * actually any events (!); by using port_getn() in lieu of port_get(), + * we can at least workaround the bug by checking for zero returned events + * and treating it as we would ETIME. + */ + do { + memset(&timeout, 0, sizeof timeout); + r = port_getn(loop->fs_fd, &pe, 1, &n, &timeout); + } + while (r == -1 && errno == EINTR); + + if ((r == -1 && errno == ETIME) || n == 0) + break; + + handle = (uv_fs_event_t*) pe.portev_user; + assert((r == 0) && "unexpected port_get() error"); + + events = 0; + if (pe.portev_events & (FILE_ATTRIB | FILE_MODIFIED)) + events |= UV_CHANGE; + if (pe.portev_events & ~(FILE_ATTRIB | FILE_MODIFIED)) + events |= UV_RENAME; + assert(events != 0); + handle->fd = PORT_FIRED; + handle->cb(handle, NULL, events, 0); + + if (handle->fd != PORT_DELETED) { + r = uv__fs_event_rearm(handle); + if (r != 0) + handle->cb(handle, NULL, 0, r); + } + } + while (handle->fd != PORT_DELETED); +} + + +int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { + uv__handle_init(loop, (uv_handle_t*)handle, UV_FS_EVENT); + return 0; +} + + +int uv_fs_event_start(uv_fs_event_t* handle, + uv_fs_event_cb cb, + const char* path, + unsigned int flags) { + int portfd; + int first_run; + int err; + + if (uv__is_active(handle)) + return UV_EINVAL; + + first_run = 0; + if (handle->loop->fs_fd == -1) { + portfd = port_create(); + if (portfd == -1) + return UV__ERR(errno); + handle->loop->fs_fd = portfd; + first_run = 1; + } + + uv__handle_start(handle); + handle->path = uv__strdup(path); + handle->fd = PORT_UNUSED; + handle->cb = cb; + + memset(&handle->fo, 0, sizeof handle->fo); + handle->fo.fo_name = handle->path; + err = uv__fs_event_rearm(handle); + if (err != 0) { + uv_fs_event_stop(handle); + return err; + } + + if (first_run) { + uv__io_init(&handle->loop->fs_event_watcher, uv__fs_event_read, portfd); + uv__io_start(handle->loop, &handle->loop->fs_event_watcher, POLLIN); + } + + return 0; +} + + +int uv_fs_event_stop(uv_fs_event_t* handle) { + if (!uv__is_active(handle)) + return 0; + + if (handle->fd == PORT_FIRED || handle->fd == PORT_LOADED) { + port_dissociate(handle->loop->fs_fd, + PORT_SOURCE_FILE, + (uintptr_t) &handle->fo); + } + + handle->fd = PORT_DELETED; + uv__free(handle->path); + handle->path = NULL; + handle->fo.fo_name = NULL; + uv__handle_stop(handle); + + return 0; +} + +void uv__fs_event_close(uv_fs_event_t* handle) { + uv_fs_event_stop(handle); +} + +#else /* !defined(PORT_SOURCE_FILE) */ + +int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { + return UV_ENOSYS; +} + + +int uv_fs_event_start(uv_fs_event_t* handle, + uv_fs_event_cb cb, + const char* filename, + unsigned int flags) { + return UV_ENOSYS; +} + + +int uv_fs_event_stop(uv_fs_event_t* handle) { + return UV_ENOSYS; +} + + +void uv__fs_event_close(uv_fs_event_t* handle) { + UNREACHABLE(); +} + +#endif /* defined(PORT_SOURCE_FILE) */ + + +int uv_resident_set_memory(size_t* rss) { + psinfo_t psinfo; + int err; + int fd; + + fd = open("/proc/self/psinfo", O_RDONLY); + if (fd == -1) + return UV__ERR(errno); + + /* FIXME(bnoordhuis) Handle EINTR. */ + err = UV_EINVAL; + if (read(fd, &psinfo, sizeof(psinfo)) == sizeof(psinfo)) { + *rss = (size_t)psinfo.pr_rssize * 1024; + err = 0; + } + uv__close(fd); + + return err; +} + + +int uv_uptime(double* uptime) { + kstat_ctl_t *kc; + kstat_t *ksp; + kstat_named_t *knp; + + long hz = sysconf(_SC_CLK_TCK); + + kc = kstat_open(); + if (kc == NULL) + return UV_EPERM; + + ksp = kstat_lookup(kc, (char*) "unix", 0, (char*) "system_misc"); + if (kstat_read(kc, ksp, NULL) == -1) { + *uptime = -1; + } else { + knp = (kstat_named_t*) kstat_data_lookup(ksp, (char*) "clk_intr"); + *uptime = knp->value.ul / hz; + } + kstat_close(kc); + + return 0; +} + + +int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + int lookup_instance; + kstat_ctl_t *kc; + kstat_t *ksp; + kstat_named_t *knp; + uv_cpu_info_t* cpu_info; + + kc = kstat_open(); + if (kc == NULL) + return UV_EPERM; + + /* Get count of cpus */ + lookup_instance = 0; + while ((ksp = kstat_lookup(kc, (char*) "cpu_info", lookup_instance, NULL))) { + lookup_instance++; + } + + *cpu_infos = uv__malloc(lookup_instance * sizeof(**cpu_infos)); + if (!(*cpu_infos)) { + kstat_close(kc); + return UV_ENOMEM; + } + + *count = lookup_instance; + + cpu_info = *cpu_infos; + lookup_instance = 0; + while ((ksp = kstat_lookup(kc, (char*) "cpu_info", lookup_instance, NULL))) { + if (kstat_read(kc, ksp, NULL) == -1) { + cpu_info->speed = 0; + cpu_info->model = NULL; + } else { + knp = kstat_data_lookup(ksp, (char*) "clock_MHz"); + assert(knp->data_type == KSTAT_DATA_INT32 || + knp->data_type == KSTAT_DATA_INT64); + cpu_info->speed = (knp->data_type == KSTAT_DATA_INT32) ? knp->value.i32 + : knp->value.i64; + + knp = kstat_data_lookup(ksp, (char*) "brand"); + assert(knp->data_type == KSTAT_DATA_STRING); + cpu_info->model = uv__strdup(KSTAT_NAMED_STR_PTR(knp)); + } + + lookup_instance++; + cpu_info++; + } + + cpu_info = *cpu_infos; + lookup_instance = 0; + for (;;) { + ksp = kstat_lookup(kc, (char*) "cpu", lookup_instance, (char*) "sys"); + + if (ksp == NULL) + break; + + if (kstat_read(kc, ksp, NULL) == -1) { + cpu_info->cpu_times.user = 0; + cpu_info->cpu_times.nice = 0; + cpu_info->cpu_times.sys = 0; + cpu_info->cpu_times.idle = 0; + cpu_info->cpu_times.irq = 0; + } else { + knp = kstat_data_lookup(ksp, (char*) "cpu_ticks_user"); + assert(knp->data_type == KSTAT_DATA_UINT64); + cpu_info->cpu_times.user = knp->value.ui64; + + knp = kstat_data_lookup(ksp, (char*) "cpu_ticks_kernel"); + assert(knp->data_type == KSTAT_DATA_UINT64); + cpu_info->cpu_times.sys = knp->value.ui64; + + knp = kstat_data_lookup(ksp, (char*) "cpu_ticks_idle"); + assert(knp->data_type == KSTAT_DATA_UINT64); + cpu_info->cpu_times.idle = knp->value.ui64; + + knp = kstat_data_lookup(ksp, (char*) "intr"); + assert(knp->data_type == KSTAT_DATA_UINT64); + cpu_info->cpu_times.irq = knp->value.ui64; + cpu_info->cpu_times.nice = 0; + } + + lookup_instance++; + cpu_info++; + } + + kstat_close(kc); + + return 0; +} + + +#ifdef SUNOS_NO_IFADDRS +int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { + *count = 0; + *addresses = NULL; + return UV_ENOSYS; +} +#else /* SUNOS_NO_IFADDRS */ +/* + * Inspired By: + * https://blogs.oracle.com/paulie/entry/retrieving_mac_address_in_solaris + * http://www.pauliesworld.org/project/getmac.c + */ +static int uv__set_phys_addr(uv_interface_address_t* address, + struct ifaddrs* ent) { + + struct sockaddr_dl* sa_addr; + int sockfd; + size_t i; + struct arpreq arpreq; + + /* This appears to only work as root */ + sa_addr = (struct sockaddr_dl*)(ent->ifa_addr); + memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr)); + for (i = 0; i < sizeof(address->phys_addr); i++) { + /* Check that all bytes of phys_addr are zero. */ + if (address->phys_addr[i] != 0) + return 0; + } + memset(&arpreq, 0, sizeof(arpreq)); + if (address->address.address4.sin_family == AF_INET) { + struct sockaddr_in* sin = ((struct sockaddr_in*)&arpreq.arp_pa); + sin->sin_addr.s_addr = address->address.address4.sin_addr.s_addr; + } else if (address->address.address4.sin_family == AF_INET6) { + struct sockaddr_in6* sin = ((struct sockaddr_in6*)&arpreq.arp_pa); + memcpy(sin->sin6_addr.s6_addr, + address->address.address6.sin6_addr.s6_addr, + sizeof(address->address.address6.sin6_addr.s6_addr)); + } else { + return 0; + } + + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + if (sockfd < 0) + return UV__ERR(errno); + + if (ioctl(sockfd, SIOCGARP, (char*)&arpreq) == -1) { + uv__close(sockfd); + return UV__ERR(errno); + } + memcpy(address->phys_addr, arpreq.arp_ha.sa_data, sizeof(address->phys_addr)); + uv__close(sockfd); + return 0; +} + + +static int uv__ifaddr_exclude(struct ifaddrs *ent) { + if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING))) + return 1; + if (ent->ifa_addr == NULL) + return 1; + if (ent->ifa_addr->sa_family != AF_INET && + ent->ifa_addr->sa_family != AF_INET6) + return 1; + return 0; +} + +int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { + uv_interface_address_t* address; + struct ifaddrs* addrs; + struct ifaddrs* ent; + + *count = 0; + *addresses = NULL; + + if (getifaddrs(&addrs)) + return UV__ERR(errno); + + /* Count the number of interfaces */ + for (ent = addrs; ent != NULL; ent = ent->ifa_next) { + if (uv__ifaddr_exclude(ent)) + continue; + (*count)++; + } + + if (*count == 0) { + freeifaddrs(addrs); + return 0; + } + + *addresses = uv__malloc(*count * sizeof(**addresses)); + if (!(*addresses)) { + freeifaddrs(addrs); + return UV_ENOMEM; + } + + address = *addresses; + + for (ent = addrs; ent != NULL; ent = ent->ifa_next) { + if (uv__ifaddr_exclude(ent)) + continue; + + address->name = uv__strdup(ent->ifa_name); + + if (ent->ifa_addr->sa_family == AF_INET6) { + address->address.address6 = *((struct sockaddr_in6*) ent->ifa_addr); + } else { + address->address.address4 = *((struct sockaddr_in*) ent->ifa_addr); + } + + if (ent->ifa_netmask->sa_family == AF_INET6) { + address->netmask.netmask6 = *((struct sockaddr_in6*) ent->ifa_netmask); + } else { + address->netmask.netmask4 = *((struct sockaddr_in*) ent->ifa_netmask); + } + + address->is_internal = !!((ent->ifa_flags & IFF_PRIVATE) || + (ent->ifa_flags & IFF_LOOPBACK)); + + uv__set_phys_addr(address, ent); + address++; + } + + freeifaddrs(addrs); + + return 0; +} +#endif /* SUNOS_NO_IFADDRS */ + +void uv_free_interface_addresses(uv_interface_address_t* addresses, + int count) { + int i; + + for (i = 0; i < count; i++) { + uv__free(addresses[i].name); + } + + uv__free(addresses); +} diff --git a/include/libuv/src/unix/sysinfo-loadavg.c b/include/libuv/src/unix/sysinfo-loadavg.c new file mode 100644 index 000000000..ebad0e89d --- /dev/null +++ b/include/libuv/src/unix/sysinfo-loadavg.c @@ -0,0 +1,36 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include + +void uv_loadavg(double avg[3]) { + struct sysinfo info; + + if (sysinfo(&info) < 0) return; + + avg[0] = (double) info.loads[0] / 65536.0; + avg[1] = (double) info.loads[1] / 65536.0; + avg[2] = (double) info.loads[2] / 65536.0; +} diff --git a/include/libuv/src/unix/sysinfo-memory.c b/include/libuv/src/unix/sysinfo-memory.c new file mode 100644 index 000000000..23b4fc6e9 --- /dev/null +++ b/include/libuv/src/unix/sysinfo-memory.c @@ -0,0 +1,42 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include + +uint64_t uv_get_free_memory(void) { + struct sysinfo info; + + if (sysinfo(&info) == 0) + return (uint64_t) info.freeram * info.mem_unit; + return 0; +} + +uint64_t uv_get_total_memory(void) { + struct sysinfo info; + + if (sysinfo(&info) == 0) + return (uint64_t) info.totalram * info.mem_unit; + return 0; +} diff --git a/include/libuv/src/unix/tcp.c b/include/libuv/src/unix/tcp.c new file mode 100644 index 000000000..18acd20df --- /dev/null +++ b/include/libuv/src/unix/tcp.c @@ -0,0 +1,461 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include +#include + + +static int new_socket(uv_tcp_t* handle, int domain, unsigned long flags) { + struct sockaddr_storage saddr; + socklen_t slen; + int sockfd; + int err; + + err = uv__socket(domain, SOCK_STREAM, 0); + if (err < 0) + return err; + sockfd = err; + + err = uv__stream_open((uv_stream_t*) handle, sockfd, flags); + if (err) { + uv__close(sockfd); + return err; + } + + if (flags & UV_HANDLE_BOUND) { + /* Bind this new socket to an arbitrary port */ + slen = sizeof(saddr); + memset(&saddr, 0, sizeof(saddr)); + if (getsockname(uv__stream_fd(handle), (struct sockaddr*) &saddr, &slen)) { + uv__close(sockfd); + return UV__ERR(errno); + } + + if (bind(uv__stream_fd(handle), (struct sockaddr*) &saddr, slen)) { + uv__close(sockfd); + return UV__ERR(errno); + } + } + + return 0; +} + + +static int maybe_new_socket(uv_tcp_t* handle, int domain, unsigned long flags) { + struct sockaddr_storage saddr; + socklen_t slen; + + if (domain == AF_UNSPEC) { + handle->flags |= flags; + return 0; + } + + if (uv__stream_fd(handle) != -1) { + + if (flags & UV_HANDLE_BOUND) { + + if (handle->flags & UV_HANDLE_BOUND) { + /* It is already bound to a port. */ + handle->flags |= flags; + return 0; + } + + /* Query to see if tcp socket is bound. */ + slen = sizeof(saddr); + memset(&saddr, 0, sizeof(saddr)); + if (getsockname(uv__stream_fd(handle), (struct sockaddr*) &saddr, &slen)) + return UV__ERR(errno); + + if ((saddr.ss_family == AF_INET6 && + ((struct sockaddr_in6*) &saddr)->sin6_port != 0) || + (saddr.ss_family == AF_INET && + ((struct sockaddr_in*) &saddr)->sin_port != 0)) { + /* Handle is already bound to a port. */ + handle->flags |= flags; + return 0; + } + + /* Bind to arbitrary port */ + if (bind(uv__stream_fd(handle), (struct sockaddr*) &saddr, slen)) + return UV__ERR(errno); + } + + handle->flags |= flags; + return 0; + } + + return new_socket(handle, domain, flags); +} + + +int uv_tcp_init_ex(uv_loop_t* loop, uv_tcp_t* tcp, unsigned int flags) { + int domain; + + /* Use the lower 8 bits for the domain */ + domain = flags & 0xFF; + if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC) + return UV_EINVAL; + + if (flags & ~0xFF) + return UV_EINVAL; + + uv__stream_init(loop, (uv_stream_t*)tcp, UV_TCP); + + /* If anything fails beyond this point we need to remove the handle from + * the handle queue, since it was added by uv__handle_init in uv_stream_init. + */ + + if (domain != AF_UNSPEC) { + int err = maybe_new_socket(tcp, domain, 0); + if (err) { + QUEUE_REMOVE(&tcp->handle_queue); + return err; + } + } + + return 0; +} + + +int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* tcp) { + return uv_tcp_init_ex(loop, tcp, AF_UNSPEC); +} + + +int uv__tcp_bind(uv_tcp_t* tcp, + const struct sockaddr* addr, + unsigned int addrlen, + unsigned int flags) { + int err; + int on; + + /* Cannot set IPv6-only mode on non-IPv6 socket. */ + if ((flags & UV_TCP_IPV6ONLY) && addr->sa_family != AF_INET6) + return UV_EINVAL; + + err = maybe_new_socket(tcp, addr->sa_family, 0); + if (err) + return err; + + on = 1; + if (setsockopt(tcp->io_watcher.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) + return UV__ERR(errno); + +#ifndef __OpenBSD__ +#ifdef IPV6_V6ONLY + if (addr->sa_family == AF_INET6) { + on = (flags & UV_TCP_IPV6ONLY) != 0; + if (setsockopt(tcp->io_watcher.fd, + IPPROTO_IPV6, + IPV6_V6ONLY, + &on, + sizeof on) == -1) { +#if defined(__MVS__) + if (errno == EOPNOTSUPP) + return UV_EINVAL; +#endif + return UV__ERR(errno); + } + } +#endif +#endif + + errno = 0; + if (bind(tcp->io_watcher.fd, addr, addrlen) && errno != EADDRINUSE) { + if (errno == EAFNOSUPPORT) + /* OSX, other BSDs and SunoS fail with EAFNOSUPPORT when binding a + * socket created with AF_INET to an AF_INET6 address or vice versa. */ + return UV_EINVAL; + return UV__ERR(errno); + } + tcp->delayed_error = UV__ERR(errno); + + tcp->flags |= UV_HANDLE_BOUND; + if (addr->sa_family == AF_INET6) + tcp->flags |= UV_HANDLE_IPV6; + + return 0; +} + + +int uv__tcp_connect(uv_connect_t* req, + uv_tcp_t* handle, + const struct sockaddr* addr, + unsigned int addrlen, + uv_connect_cb cb) { + int err; + int r; + + assert(handle->type == UV_TCP); + + if (handle->connect_req != NULL) + return UV_EALREADY; /* FIXME(bnoordhuis) UV_EINVAL or maybe UV_EBUSY. */ + + err = maybe_new_socket(handle, + addr->sa_family, + UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); + if (err) + return err; + + handle->delayed_error = 0; + + do { + errno = 0; + r = connect(uv__stream_fd(handle), addr, addrlen); + } while (r == -1 && errno == EINTR); + + /* We not only check the return value, but also check the errno != 0. + * Because in rare cases connect() will return -1 but the errno + * is 0 (for example, on Android 4.3, OnePlus phone A0001_12_150227) + * and actually the tcp three-way handshake is completed. + */ + if (r == -1 && errno != 0) { + if (errno == EINPROGRESS) + ; /* not an error */ + else if (errno == ECONNREFUSED +#if defined(__OpenBSD__) + || errno == EINVAL +#endif + ) + /* If we get ECONNREFUSED (Solaris) or EINVAL (OpenBSD) wait until the + * next tick to report the error. Solaris and OpenBSD wants to report + * immediately -- other unixes want to wait. + */ + handle->delayed_error = UV__ERR(ECONNREFUSED); + else + return UV__ERR(errno); + } + + uv__req_init(handle->loop, req, UV_CONNECT); + req->cb = cb; + req->handle = (uv_stream_t*) handle; + QUEUE_INIT(&req->queue); + handle->connect_req = req; + + uv__io_start(handle->loop, &handle->io_watcher, POLLOUT); + + if (handle->delayed_error) + uv__io_feed(handle->loop, &handle->io_watcher); + + return 0; +} + + +int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) { + int err; + + if (uv__fd_exists(handle->loop, sock)) + return UV_EEXIST; + + err = uv__nonblock(sock, 1); + if (err) + return err; + + return uv__stream_open((uv_stream_t*)handle, + sock, + UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); +} + + +int uv_tcp_getsockname(const uv_tcp_t* handle, + struct sockaddr* name, + int* namelen) { + + if (handle->delayed_error) + return handle->delayed_error; + + return uv__getsockpeername((const uv_handle_t*) handle, + getsockname, + name, + namelen); +} + + +int uv_tcp_getpeername(const uv_tcp_t* handle, + struct sockaddr* name, + int* namelen) { + + if (handle->delayed_error) + return handle->delayed_error; + + return uv__getsockpeername((const uv_handle_t*) handle, + getpeername, + name, + namelen); +} + + +int uv_tcp_close_reset(uv_tcp_t* handle, uv_close_cb close_cb) { + int fd; + struct linger l = { 1, 0 }; + + /* Disallow setting SO_LINGER to zero due to some platform inconsistencies */ + if (handle->flags & UV_HANDLE_SHUTTING) + return UV_EINVAL; + + fd = uv__stream_fd(handle); + if (0 != setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l))) + return UV__ERR(errno); + + uv_close((uv_handle_t*) handle, close_cb); + return 0; +} + + +int uv_tcp_listen(uv_tcp_t* tcp, int backlog, uv_connection_cb cb) { + static int single_accept_cached = -1; + unsigned long flags; + int single_accept; + int err; + + if (tcp->delayed_error) + return tcp->delayed_error; + + single_accept = uv__load_relaxed(&single_accept_cached); + if (single_accept == -1) { + const char* val = getenv("UV_TCP_SINGLE_ACCEPT"); + single_accept = (val != NULL && atoi(val) != 0); /* Off by default. */ + uv__store_relaxed(&single_accept_cached, single_accept); + } + + if (single_accept) + tcp->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT; + + flags = 0; +#if defined(__MVS__) + /* on zOS the listen call does not bind automatically + if the socket is unbound. Hence the manual binding to + an arbitrary port is required to be done manually + */ + flags |= UV_HANDLE_BOUND; +#endif + err = maybe_new_socket(tcp, AF_INET, flags); + if (err) + return err; + + if (listen(tcp->io_watcher.fd, backlog)) + return UV__ERR(errno); + + tcp->connection_cb = cb; + tcp->flags |= UV_HANDLE_BOUND; + + /* Start listening for connections. */ + tcp->io_watcher.cb = uv__server_io; + uv__io_start(tcp->loop, &tcp->io_watcher, POLLIN); + + return 0; +} + + +int uv__tcp_nodelay(int fd, int on) { + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on))) + return UV__ERR(errno); + return 0; +} + + +int uv__tcp_keepalive(int fd, int on, unsigned int delay) { + if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on))) + return UV__ERR(errno); + +#ifdef TCP_KEEPIDLE + if (on) { + int intvl = 1; /* 1 second; same as default on Win32 */ + int cnt = 10; /* 10 retries; same as hardcoded on Win32 */ + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &delay, sizeof(delay))) + return UV__ERR(errno); + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &intvl, sizeof(intvl))) + return UV__ERR(errno); + if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt))) + return UV__ERR(errno); + } +#endif + + /* Solaris/SmartOS, if you don't support keep-alive, + * then don't advertise it in your system headers... + */ + /* FIXME(bnoordhuis) That's possibly because sizeof(delay) should be 1. */ +#if defined(TCP_KEEPALIVE) && !defined(__sun) + if (on && setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE, &delay, sizeof(delay))) + return UV__ERR(errno); +#endif + + return 0; +} + + +int uv_tcp_nodelay(uv_tcp_t* handle, int on) { + int err; + + if (uv__stream_fd(handle) != -1) { + err = uv__tcp_nodelay(uv__stream_fd(handle), on); + if (err) + return err; + } + + if (on) + handle->flags |= UV_HANDLE_TCP_NODELAY; + else + handle->flags &= ~UV_HANDLE_TCP_NODELAY; + + return 0; +} + + +int uv_tcp_keepalive(uv_tcp_t* handle, int on, unsigned int delay) { + int err; + + if (uv__stream_fd(handle) != -1) { + err =uv__tcp_keepalive(uv__stream_fd(handle), on, delay); + if (err) + return err; + } + + if (on) + handle->flags |= UV_HANDLE_TCP_KEEPALIVE; + else + handle->flags &= ~UV_HANDLE_TCP_KEEPALIVE; + + /* TODO Store delay if uv__stream_fd(handle) == -1 but don't want to enlarge + * uv_tcp_t with an int that's almost never used... + */ + + return 0; +} + + +int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) { + if (enable) + handle->flags &= ~UV_HANDLE_TCP_SINGLE_ACCEPT; + else + handle->flags |= UV_HANDLE_TCP_SINGLE_ACCEPT; + return 0; +} + + +void uv__tcp_close(uv_tcp_t* handle) { + uv__stream_close((uv_stream_t*)handle); +} diff --git a/include/libuv/src/unix/thread.c b/include/libuv/src/unix/thread.c new file mode 100644 index 000000000..1a85d1d4f --- /dev/null +++ b/include/libuv/src/unix/thread.c @@ -0,0 +1,842 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include + +#include +#include /* getrlimit() */ +#include /* getpagesize() */ + +#include + +#ifdef __MVS__ +#include +#include +#endif + +#if defined(__GLIBC__) && !defined(__UCLIBC__) +#include /* gnu_get_libc_version() */ +#endif + +#undef NANOSEC +#define NANOSEC ((uint64_t) 1e9) + +#if defined(PTHREAD_BARRIER_SERIAL_THREAD) +STATIC_ASSERT(sizeof(uv_barrier_t) == sizeof(pthread_barrier_t)); +#endif + +/* Note: guard clauses should match uv_barrier_t's in include/uv/unix.h. */ +#if defined(_AIX) || \ + defined(__OpenBSD__) || \ + !defined(PTHREAD_BARRIER_SERIAL_THREAD) +int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) { + struct _uv_barrier* b; + int rc; + + if (barrier == NULL || count == 0) + return UV_EINVAL; + + b = uv__malloc(sizeof(*b)); + if (b == NULL) + return UV_ENOMEM; + + b->in = 0; + b->out = 0; + b->threshold = count; + + rc = uv_mutex_init(&b->mutex); + if (rc != 0) + goto error2; + + rc = uv_cond_init(&b->cond); + if (rc != 0) + goto error; + + barrier->b = b; + return 0; + +error: + uv_mutex_destroy(&b->mutex); +error2: + uv__free(b); + return rc; +} + + +int uv_barrier_wait(uv_barrier_t* barrier) { + struct _uv_barrier* b; + int last; + + if (barrier == NULL || barrier->b == NULL) + return UV_EINVAL; + + b = barrier->b; + uv_mutex_lock(&b->mutex); + + if (++b->in == b->threshold) { + b->in = 0; + b->out = b->threshold; + uv_cond_signal(&b->cond); + } else { + do + uv_cond_wait(&b->cond, &b->mutex); + while (b->in != 0); + } + + last = (--b->out == 0); + if (!last) + uv_cond_signal(&b->cond); /* Not needed for last thread. */ + + uv_mutex_unlock(&b->mutex); + return last; +} + + +void uv_barrier_destroy(uv_barrier_t* barrier) { + struct _uv_barrier* b; + + b = barrier->b; + uv_mutex_lock(&b->mutex); + + assert(b->in == 0); + assert(b->out == 0); + + if (b->in != 0 || b->out != 0) + abort(); + + uv_mutex_unlock(&b->mutex); + uv_mutex_destroy(&b->mutex); + uv_cond_destroy(&b->cond); + + uv__free(barrier->b); + barrier->b = NULL; +} + +#else + +int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) { + return UV__ERR(pthread_barrier_init(barrier, NULL, count)); +} + + +int uv_barrier_wait(uv_barrier_t* barrier) { + int rc; + + rc = pthread_barrier_wait(barrier); + if (rc != 0) + if (rc != PTHREAD_BARRIER_SERIAL_THREAD) + abort(); + + return rc == PTHREAD_BARRIER_SERIAL_THREAD; +} + + +void uv_barrier_destroy(uv_barrier_t* barrier) { + if (pthread_barrier_destroy(barrier)) + abort(); +} + +#endif + + +/* On MacOS, threads other than the main thread are created with a reduced + * stack size by default. Adjust to RLIMIT_STACK aligned to the page size. + * + * On Linux, threads created by musl have a much smaller stack than threads + * created by glibc (80 vs. 2048 or 4096 kB.) Follow glibc for consistency. + */ +static size_t thread_stack_size(void) { +#if defined(__APPLE__) || defined(__linux__) + struct rlimit lim; + + /* getrlimit() can fail on some aarch64 systems due to a glibc bug where + * the system call wrapper invokes the wrong system call. Don't treat + * that as fatal, just use the default stack size instead. + */ + if (0 == getrlimit(RLIMIT_STACK, &lim) && lim.rlim_cur != RLIM_INFINITY) { + /* pthread_attr_setstacksize() expects page-aligned values. */ + lim.rlim_cur -= lim.rlim_cur % (rlim_t) getpagesize(); + + /* Musl's PTHREAD_STACK_MIN is 2 KB on all architectures, which is + * too small to safely receive signals on. + * + * Musl's PTHREAD_STACK_MIN + MINSIGSTKSZ == 8192 on arm64 (which has + * the largest MINSIGSTKSZ of the architectures that musl supports) so + * let's use that as a lower bound. + * + * We use a hardcoded value because PTHREAD_STACK_MIN + MINSIGSTKSZ + * is between 28 and 133 KB when compiling against glibc, depending + * on the architecture. + */ + if (lim.rlim_cur >= 8192) + if (lim.rlim_cur >= PTHREAD_STACK_MIN) + return lim.rlim_cur; + } +#endif + +#if !defined(__linux__) + return 0; +#elif defined(__PPC__) || defined(__ppc__) || defined(__powerpc__) + return 4 << 20; /* glibc default. */ +#else + return 2 << 20; /* glibc default. */ +#endif +} + + +int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) { + uv_thread_options_t params; + params.flags = UV_THREAD_NO_FLAGS; + return uv_thread_create_ex(tid, ¶ms, entry, arg); +} + +int uv_thread_create_ex(uv_thread_t* tid, + const uv_thread_options_t* params, + void (*entry)(void *arg), + void *arg) { + int err; + pthread_attr_t* attr; + pthread_attr_t attr_storage; + size_t pagesize; + size_t stack_size; + + /* Used to squelch a -Wcast-function-type warning. */ + union { + void (*in)(void*); + void* (*out)(void*); + } f; + + stack_size = + params->flags & UV_THREAD_HAS_STACK_SIZE ? params->stack_size : 0; + + attr = NULL; + if (stack_size == 0) { + stack_size = thread_stack_size(); + } else { + pagesize = (size_t)getpagesize(); + /* Round up to the nearest page boundary. */ + stack_size = (stack_size + pagesize - 1) &~ (pagesize - 1); +#ifdef PTHREAD_STACK_MIN + if (stack_size < PTHREAD_STACK_MIN) + stack_size = PTHREAD_STACK_MIN; +#endif + } + + if (stack_size > 0) { + attr = &attr_storage; + + if (pthread_attr_init(attr)) + abort(); + + if (pthread_attr_setstacksize(attr, stack_size)) + abort(); + } + + f.in = entry; + err = pthread_create(tid, attr, f.out, arg); + + if (attr != NULL) + pthread_attr_destroy(attr); + + return UV__ERR(err); +} + + +uv_thread_t uv_thread_self(void) { + return pthread_self(); +} + +int uv_thread_join(uv_thread_t *tid) { + return UV__ERR(pthread_join(*tid, NULL)); +} + + +int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2) { + return pthread_equal(*t1, *t2); +} + + +int uv_mutex_init(uv_mutex_t* mutex) { +#if defined(NDEBUG) || !defined(PTHREAD_MUTEX_ERRORCHECK) + return UV__ERR(pthread_mutex_init(mutex, NULL)); +#else + pthread_mutexattr_t attr; + int err; + + if (pthread_mutexattr_init(&attr)) + abort(); + + if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK)) + abort(); + + err = pthread_mutex_init(mutex, &attr); + + if (pthread_mutexattr_destroy(&attr)) + abort(); + + return UV__ERR(err); +#endif +} + + +int uv_mutex_init_recursive(uv_mutex_t* mutex) { + pthread_mutexattr_t attr; + int err; + + if (pthread_mutexattr_init(&attr)) + abort(); + + if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) + abort(); + + err = pthread_mutex_init(mutex, &attr); + + if (pthread_mutexattr_destroy(&attr)) + abort(); + + return UV__ERR(err); +} + + +void uv_mutex_destroy(uv_mutex_t* mutex) { + if (pthread_mutex_destroy(mutex)) + abort(); +} + + +void uv_mutex_lock(uv_mutex_t* mutex) { + if (pthread_mutex_lock(mutex)) + abort(); +} + + +int uv_mutex_trylock(uv_mutex_t* mutex) { + int err; + + err = pthread_mutex_trylock(mutex); + if (err) { + if (err != EBUSY && err != EAGAIN) + abort(); + return UV_EBUSY; + } + + return 0; +} + + +void uv_mutex_unlock(uv_mutex_t* mutex) { + if (pthread_mutex_unlock(mutex)) + abort(); +} + + +int uv_rwlock_init(uv_rwlock_t* rwlock) { + return UV__ERR(pthread_rwlock_init(rwlock, NULL)); +} + + +void uv_rwlock_destroy(uv_rwlock_t* rwlock) { + if (pthread_rwlock_destroy(rwlock)) + abort(); +} + + +void uv_rwlock_rdlock(uv_rwlock_t* rwlock) { + if (pthread_rwlock_rdlock(rwlock)) + abort(); +} + + +int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) { + int err; + + err = pthread_rwlock_tryrdlock(rwlock); + if (err) { + if (err != EBUSY && err != EAGAIN) + abort(); + return UV_EBUSY; + } + + return 0; +} + + +void uv_rwlock_rdunlock(uv_rwlock_t* rwlock) { + if (pthread_rwlock_unlock(rwlock)) + abort(); +} + + +void uv_rwlock_wrlock(uv_rwlock_t* rwlock) { + if (pthread_rwlock_wrlock(rwlock)) + abort(); +} + + +int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) { + int err; + + err = pthread_rwlock_trywrlock(rwlock); + if (err) { + if (err != EBUSY && err != EAGAIN) + abort(); + return UV_EBUSY; + } + + return 0; +} + + +void uv_rwlock_wrunlock(uv_rwlock_t* rwlock) { + if (pthread_rwlock_unlock(rwlock)) + abort(); +} + + +void uv_once(uv_once_t* guard, void (*callback)(void)) { + if (pthread_once(guard, callback)) + abort(); +} + +#if defined(__APPLE__) && defined(__MACH__) + +int uv_sem_init(uv_sem_t* sem, unsigned int value) { + kern_return_t err; + + err = semaphore_create(mach_task_self(), sem, SYNC_POLICY_FIFO, value); + if (err == KERN_SUCCESS) + return 0; + if (err == KERN_INVALID_ARGUMENT) + return UV_EINVAL; + if (err == KERN_RESOURCE_SHORTAGE) + return UV_ENOMEM; + + abort(); + return UV_EINVAL; /* Satisfy the compiler. */ +} + + +void uv_sem_destroy(uv_sem_t* sem) { + if (semaphore_destroy(mach_task_self(), *sem)) + abort(); +} + + +void uv_sem_post(uv_sem_t* sem) { + if (semaphore_signal(*sem)) + abort(); +} + + +void uv_sem_wait(uv_sem_t* sem) { + int r; + + do + r = semaphore_wait(*sem); + while (r == KERN_ABORTED); + + if (r != KERN_SUCCESS) + abort(); +} + + +int uv_sem_trywait(uv_sem_t* sem) { + mach_timespec_t interval; + kern_return_t err; + + interval.tv_sec = 0; + interval.tv_nsec = 0; + + err = semaphore_timedwait(*sem, interval); + if (err == KERN_SUCCESS) + return 0; + if (err == KERN_OPERATION_TIMED_OUT) + return UV_EAGAIN; + + abort(); + return UV_EINVAL; /* Satisfy the compiler. */ +} + +#else /* !(defined(__APPLE__) && defined(__MACH__)) */ + +#if defined(__GLIBC__) && !defined(__UCLIBC__) + +/* Hack around https://sourceware.org/bugzilla/show_bug.cgi?id=12674 + * by providing a custom implementation for glibc < 2.21 in terms of other + * concurrency primitives. + * Refs: https://github.com/nodejs/node/issues/19903 */ + +/* To preserve ABI compatibility, we treat the uv_sem_t as storage for + * a pointer to the actual struct we're using underneath. */ + +static uv_once_t glibc_version_check_once = UV_ONCE_INIT; +static int platform_needs_custom_semaphore = 0; + +static void glibc_version_check(void) { + const char* version = gnu_get_libc_version(); + platform_needs_custom_semaphore = + version[0] == '2' && version[1] == '.' && + atoi(version + 2) < 21; +} + +#elif defined(__MVS__) + +#define platform_needs_custom_semaphore 1 + +#else /* !defined(__GLIBC__) && !defined(__MVS__) */ + +#define platform_needs_custom_semaphore 0 + +#endif + +typedef struct uv_semaphore_s { + uv_mutex_t mutex; + uv_cond_t cond; + unsigned int value; +} uv_semaphore_t; + +#if (defined(__GLIBC__) && !defined(__UCLIBC__)) || \ + platform_needs_custom_semaphore +STATIC_ASSERT(sizeof(uv_sem_t) >= sizeof(uv_semaphore_t*)); +#endif + +static int uv__custom_sem_init(uv_sem_t* sem_, unsigned int value) { + int err; + uv_semaphore_t* sem; + + sem = uv__malloc(sizeof(*sem)); + if (sem == NULL) + return UV_ENOMEM; + + if ((err = uv_mutex_init(&sem->mutex)) != 0) { + uv__free(sem); + return err; + } + + if ((err = uv_cond_init(&sem->cond)) != 0) { + uv_mutex_destroy(&sem->mutex); + uv__free(sem); + return err; + } + + sem->value = value; + *(uv_semaphore_t**)sem_ = sem; + return 0; +} + + +static void uv__custom_sem_destroy(uv_sem_t* sem_) { + uv_semaphore_t* sem; + + sem = *(uv_semaphore_t**)sem_; + uv_cond_destroy(&sem->cond); + uv_mutex_destroy(&sem->mutex); + uv__free(sem); +} + + +static void uv__custom_sem_post(uv_sem_t* sem_) { + uv_semaphore_t* sem; + + sem = *(uv_semaphore_t**)sem_; + uv_mutex_lock(&sem->mutex); + sem->value++; + if (sem->value == 1) + uv_cond_signal(&sem->cond); + uv_mutex_unlock(&sem->mutex); +} + + +static void uv__custom_sem_wait(uv_sem_t* sem_) { + uv_semaphore_t* sem; + + sem = *(uv_semaphore_t**)sem_; + uv_mutex_lock(&sem->mutex); + while (sem->value == 0) + uv_cond_wait(&sem->cond, &sem->mutex); + sem->value--; + uv_mutex_unlock(&sem->mutex); +} + + +static int uv__custom_sem_trywait(uv_sem_t* sem_) { + uv_semaphore_t* sem; + + sem = *(uv_semaphore_t**)sem_; + if (uv_mutex_trylock(&sem->mutex) != 0) + return UV_EAGAIN; + + if (sem->value == 0) { + uv_mutex_unlock(&sem->mutex); + return UV_EAGAIN; + } + + sem->value--; + uv_mutex_unlock(&sem->mutex); + + return 0; +} + +static int uv__sem_init(uv_sem_t* sem, unsigned int value) { + if (sem_init(sem, 0, value)) + return UV__ERR(errno); + return 0; +} + + +static void uv__sem_destroy(uv_sem_t* sem) { + if (sem_destroy(sem)) + abort(); +} + + +static void uv__sem_post(uv_sem_t* sem) { + if (sem_post(sem)) + abort(); +} + + +static void uv__sem_wait(uv_sem_t* sem) { + int r; + + do + r = sem_wait(sem); + while (r == -1 && errno == EINTR); + + if (r) + abort(); +} + + +static int uv__sem_trywait(uv_sem_t* sem) { + int r; + + do + r = sem_trywait(sem); + while (r == -1 && errno == EINTR); + + if (r) { + if (errno == EAGAIN) + return UV_EAGAIN; + abort(); + } + + return 0; +} + +int uv_sem_init(uv_sem_t* sem, unsigned int value) { +#if defined(__GLIBC__) && !defined(__UCLIBC__) + uv_once(&glibc_version_check_once, glibc_version_check); +#endif + + if (platform_needs_custom_semaphore) + return uv__custom_sem_init(sem, value); + else + return uv__sem_init(sem, value); +} + + +void uv_sem_destroy(uv_sem_t* sem) { + if (platform_needs_custom_semaphore) + uv__custom_sem_destroy(sem); + else + uv__sem_destroy(sem); +} + + +void uv_sem_post(uv_sem_t* sem) { + if (platform_needs_custom_semaphore) + uv__custom_sem_post(sem); + else + uv__sem_post(sem); +} + + +void uv_sem_wait(uv_sem_t* sem) { + if (platform_needs_custom_semaphore) + uv__custom_sem_wait(sem); + else + uv__sem_wait(sem); +} + + +int uv_sem_trywait(uv_sem_t* sem) { + if (platform_needs_custom_semaphore) + return uv__custom_sem_trywait(sem); + else + return uv__sem_trywait(sem); +} + +#endif /* defined(__APPLE__) && defined(__MACH__) */ + + +#if defined(__APPLE__) && defined(__MACH__) || defined(__MVS__) + +int uv_cond_init(uv_cond_t* cond) { + return UV__ERR(pthread_cond_init(cond, NULL)); +} + +#else /* !(defined(__APPLE__) && defined(__MACH__)) */ + +int uv_cond_init(uv_cond_t* cond) { + pthread_condattr_t attr; + int err; + + err = pthread_condattr_init(&attr); + if (err) + return UV__ERR(err); + + err = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); + if (err) + goto error2; + + err = pthread_cond_init(cond, &attr); + if (err) + goto error2; + + err = pthread_condattr_destroy(&attr); + if (err) + goto error; + + return 0; + +error: + pthread_cond_destroy(cond); +error2: + pthread_condattr_destroy(&attr); + return UV__ERR(err); +} + +#endif /* defined(__APPLE__) && defined(__MACH__) */ + +void uv_cond_destroy(uv_cond_t* cond) { +#if defined(__APPLE__) && defined(__MACH__) + /* It has been reported that destroying condition variables that have been + * signalled but not waited on can sometimes result in application crashes. + * See https://codereview.chromium.org/1323293005. + */ + pthread_mutex_t mutex; + struct timespec ts; + int err; + + if (pthread_mutex_init(&mutex, NULL)) + abort(); + + if (pthread_mutex_lock(&mutex)) + abort(); + + ts.tv_sec = 0; + ts.tv_nsec = 1; + + err = pthread_cond_timedwait_relative_np(cond, &mutex, &ts); + if (err != 0 && err != ETIMEDOUT) + abort(); + + if (pthread_mutex_unlock(&mutex)) + abort(); + + if (pthread_mutex_destroy(&mutex)) + abort(); +#endif /* defined(__APPLE__) && defined(__MACH__) */ + + if (pthread_cond_destroy(cond)) + abort(); +} + +void uv_cond_signal(uv_cond_t* cond) { + if (pthread_cond_signal(cond)) + abort(); +} + +void uv_cond_broadcast(uv_cond_t* cond) { + if (pthread_cond_broadcast(cond)) + abort(); +} + +void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) { + if (pthread_cond_wait(cond, mutex)) + abort(); +} + + +int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) { + int r; + struct timespec ts; +#if defined(__MVS__) + struct timeval tv; +#endif + +#if defined(__APPLE__) && defined(__MACH__) + ts.tv_sec = timeout / NANOSEC; + ts.tv_nsec = timeout % NANOSEC; + r = pthread_cond_timedwait_relative_np(cond, mutex, &ts); +#else +#if defined(__MVS__) + if (gettimeofday(&tv, NULL)) + abort(); + timeout += tv.tv_sec * NANOSEC + tv.tv_usec * 1e3; +#else + timeout += uv__hrtime(UV_CLOCK_PRECISE); +#endif + ts.tv_sec = timeout / NANOSEC; + ts.tv_nsec = timeout % NANOSEC; + r = pthread_cond_timedwait(cond, mutex, &ts); +#endif + + + if (r == 0) + return 0; + + if (r == ETIMEDOUT) + return UV_ETIMEDOUT; + + abort(); +#ifndef __SUNPRO_C + return UV_EINVAL; /* Satisfy the compiler. */ +#endif +} + + +int uv_key_create(uv_key_t* key) { + return UV__ERR(pthread_key_create(key, NULL)); +} + + +void uv_key_delete(uv_key_t* key) { + if (pthread_key_delete(*key)) + abort(); +} + + +void* uv_key_get(uv_key_t* key) { + return pthread_getspecific(*key); +} + + +void uv_key_set(uv_key_t* key, void* value) { + if (pthread_setspecific(*key, value)) + abort(); +} diff --git a/include/libuv/src/unix/tty.c b/include/libuv/src/unix/tty.c new file mode 100644 index 000000000..6f60abaad --- /dev/null +++ b/include/libuv/src/unix/tty.c @@ -0,0 +1,402 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" +#include "spinlock.h" + +#include +#include +#include +#include +#include +#include + +#if defined(__MVS__) && !defined(IMAXBEL) +#define IMAXBEL 0 +#endif + +#if defined(__PASE__) +/* On IBM i PASE, for better compatibility with running interactive programs in + * a 5250 environment, isatty() will return true for the stdin/stdout/stderr + * streams created by QSH/QP2TERM. + * + * For more, see docs on PASE_STDIO_ISATTY in + * https://www.ibm.com/support/knowledgecenter/ssw_ibm_i_74/apis/pase_environ.htm + * + * This behavior causes problems for Node as it expects that if isatty() returns + * true that TTY ioctls will be supported by that fd (which is not an + * unreasonable expectation) and when they don't it crashes with assertion + * errors. + * + * Here, we create our own version of isatty() that uses ioctl() to identify + * whether the fd is *really* a TTY or not. + */ +static int isreallyatty(int file) { + int rc; + + rc = !ioctl(file, TXISATTY + 0x81, NULL); + if (!rc && errno != EBADF) + errno = ENOTTY; + + return rc; +} +#define isatty(fd) isreallyatty(fd) +#endif + +static int orig_termios_fd = -1; +static struct termios orig_termios; +static uv_spinlock_t termios_spinlock = UV_SPINLOCK_INITIALIZER; + +static int uv__tty_is_slave(const int fd) { + int result; +#if defined(__linux__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + int dummy; + + result = ioctl(fd, TIOCGPTN, &dummy) != 0; +#elif defined(__APPLE__) + char dummy[256]; + + result = ioctl(fd, TIOCPTYGNAME, &dummy) != 0; +#elif defined(__NetBSD__) + /* + * NetBSD as an extension returns with ptsname(3) and ptsname_r(3) the slave + * device name for both descriptors, the master one and slave one. + * + * Implement function to compare major device number with pts devices. + * + * The major numbers are machine-dependent, on NetBSD/amd64 they are + * respectively: + * - master tty: ptc - major 6 + * - slave tty: pts - major 5 + */ + + struct stat sb; + /* Lookup device's major for the pts driver and cache it. */ + static devmajor_t pts = NODEVMAJOR; + + if (pts == NODEVMAJOR) { + pts = getdevmajor("pts", S_IFCHR); + if (pts == NODEVMAJOR) + abort(); + } + + /* Lookup stat structure behind the file descriptor. */ + if (fstat(fd, &sb) != 0) + abort(); + + /* Assert character device. */ + if (!S_ISCHR(sb.st_mode)) + abort(); + + /* Assert valid major. */ + if (major(sb.st_rdev) == NODEVMAJOR) + abort(); + + result = (pts == major(sb.st_rdev)); +#else + /* Fallback to ptsname + */ + result = ptsname(fd) == NULL; +#endif + return result; +} + +int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, int fd, int unused) { + uv_handle_type type; + int flags; + int newfd; + int r; + int saved_flags; + int mode; + char path[256]; + (void)unused; /* deprecated parameter is no longer needed */ + + /* File descriptors that refer to files cannot be monitored with epoll. + * That restriction also applies to character devices like /dev/random + * (but obviously not /dev/tty.) + */ + type = uv_guess_handle(fd); + if (type == UV_FILE || type == UV_UNKNOWN_HANDLE) + return UV_EINVAL; + + flags = 0; + newfd = -1; + + /* Save the fd flags in case we need to restore them due to an error. */ + do + saved_flags = fcntl(fd, F_GETFL); + while (saved_flags == -1 && errno == EINTR); + + if (saved_flags == -1) + return UV__ERR(errno); + mode = saved_flags & O_ACCMODE; + + /* Reopen the file descriptor when it refers to a tty. This lets us put the + * tty in non-blocking mode without affecting other processes that share it + * with us. + * + * Example: `node | cat` - if we put our fd 0 in non-blocking mode, it also + * affects fd 1 of `cat` because both file descriptors refer to the same + * struct file in the kernel. When we reopen our fd 0, it points to a + * different struct file, hence changing its properties doesn't affect + * other processes. + */ + if (type == UV_TTY) { + /* Reopening a pty in master mode won't work either because the reopened + * pty will be in slave mode (*BSD) or reopening will allocate a new + * master/slave pair (Linux). Therefore check if the fd points to a + * slave device. + */ + if (uv__tty_is_slave(fd) && ttyname_r(fd, path, sizeof(path)) == 0) + r = uv__open_cloexec(path, mode | O_NOCTTY); + else + r = -1; + + if (r < 0) { + /* fallback to using blocking writes */ + if (mode != O_RDONLY) + flags |= UV_HANDLE_BLOCKING_WRITES; + goto skip; + } + + newfd = r; + + r = uv__dup2_cloexec(newfd, fd); + if (r < 0 && r != UV_EINVAL) { + /* EINVAL means newfd == fd which could conceivably happen if another + * thread called close(fd) between our calls to isatty() and open(). + * That's a rather unlikely event but let's handle it anyway. + */ + uv__close(newfd); + return r; + } + + fd = newfd; + } + +skip: + uv__stream_init(loop, (uv_stream_t*) tty, UV_TTY); + + /* If anything fails beyond this point we need to remove the handle from + * the handle queue, since it was added by uv__handle_init in uv_stream_init. + */ + + if (!(flags & UV_HANDLE_BLOCKING_WRITES)) + uv__nonblock(fd, 1); + +#if defined(__APPLE__) + r = uv__stream_try_select((uv_stream_t*) tty, &fd); + if (r) { + int rc = r; + if (newfd != -1) + uv__close(newfd); + QUEUE_REMOVE(&tty->handle_queue); + do + r = fcntl(fd, F_SETFL, saved_flags); + while (r == -1 && errno == EINTR); + return rc; + } +#endif + + if (mode != O_WRONLY) + flags |= UV_HANDLE_READABLE; + if (mode != O_RDONLY) + flags |= UV_HANDLE_WRITABLE; + + uv__stream_open((uv_stream_t*) tty, fd, flags); + tty->mode = UV_TTY_MODE_NORMAL; + + return 0; +} + +static void uv__tty_make_raw(struct termios* tio) { + assert(tio != NULL); + +#if defined __sun || defined __MVS__ + /* + * This implementation of cfmakeraw for Solaris and derivatives is taken from + * http://www.perkin.org.uk/posts/solaris-portability-cfmakeraw.html. + */ + tio->c_iflag &= ~(IMAXBEL | IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | + IGNCR | ICRNL | IXON); + tio->c_oflag &= ~OPOST; + tio->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + tio->c_cflag &= ~(CSIZE | PARENB); + tio->c_cflag |= CS8; +#else + cfmakeraw(tio); +#endif /* #ifdef __sun */ +} + +int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) { + struct termios tmp; + int fd; + + if (tty->mode == (int) mode) + return 0; + + fd = uv__stream_fd(tty); + if (tty->mode == UV_TTY_MODE_NORMAL && mode != UV_TTY_MODE_NORMAL) { + if (tcgetattr(fd, &tty->orig_termios)) + return UV__ERR(errno); + + /* This is used for uv_tty_reset_mode() */ + uv_spinlock_lock(&termios_spinlock); + if (orig_termios_fd == -1) { + orig_termios = tty->orig_termios; + orig_termios_fd = fd; + } + uv_spinlock_unlock(&termios_spinlock); + } + + tmp = tty->orig_termios; + switch (mode) { + case UV_TTY_MODE_NORMAL: + break; + case UV_TTY_MODE_RAW: + tmp.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); + tmp.c_oflag |= (ONLCR); + tmp.c_cflag |= (CS8); + tmp.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); + tmp.c_cc[VMIN] = 1; + tmp.c_cc[VTIME] = 0; + break; + case UV_TTY_MODE_IO: + uv__tty_make_raw(&tmp); + break; + } + + /* Apply changes after draining */ + if (tcsetattr(fd, TCSADRAIN, &tmp)) + return UV__ERR(errno); + + tty->mode = mode; + return 0; +} + + +int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) { + struct winsize ws; + int err; + + do + err = ioctl(uv__stream_fd(tty), TIOCGWINSZ, &ws); + while (err == -1 && errno == EINTR); + + if (err == -1) + return UV__ERR(errno); + + *width = ws.ws_col; + *height = ws.ws_row; + + return 0; +} + + +uv_handle_type uv_guess_handle(uv_file file) { + struct sockaddr sa; + struct stat s; + socklen_t len; + int type; + + if (file < 0) + return UV_UNKNOWN_HANDLE; + + if (isatty(file)) + return UV_TTY; + + if (fstat(file, &s)) + return UV_UNKNOWN_HANDLE; + + if (S_ISREG(s.st_mode)) + return UV_FILE; + + if (S_ISCHR(s.st_mode)) + return UV_FILE; /* XXX UV_NAMED_PIPE? */ + + if (S_ISFIFO(s.st_mode)) + return UV_NAMED_PIPE; + + if (!S_ISSOCK(s.st_mode)) + return UV_UNKNOWN_HANDLE; + + len = sizeof(type); + if (getsockopt(file, SOL_SOCKET, SO_TYPE, &type, &len)) + return UV_UNKNOWN_HANDLE; + + len = sizeof(sa); + if (getsockname(file, &sa, &len)) + return UV_UNKNOWN_HANDLE; + + if (type == SOCK_DGRAM) + if (sa.sa_family == AF_INET || sa.sa_family == AF_INET6) + return UV_UDP; + + if (type == SOCK_STREAM) { +#if defined(_AIX) || defined(__DragonFly__) + /* on AIX/DragonFly the getsockname call returns an empty sa structure + * for sockets of type AF_UNIX. For all other types it will + * return a properly filled in structure. + */ + if (len == 0) + return UV_NAMED_PIPE; +#endif /* defined(_AIX) || defined(__DragonFly__) */ + + if (sa.sa_family == AF_INET || sa.sa_family == AF_INET6) + return UV_TCP; + if (sa.sa_family == AF_UNIX) + return UV_NAMED_PIPE; + } + + return UV_UNKNOWN_HANDLE; +} + + +/* This function is async signal-safe, meaning that it's safe to call from + * inside a signal handler _unless_ execution was inside uv_tty_set_mode()'s + * critical section when the signal was raised. + */ +int uv_tty_reset_mode(void) { + int saved_errno; + int err; + + saved_errno = errno; + if (!uv_spinlock_trylock(&termios_spinlock)) + return UV_EBUSY; /* In uv_tty_set_mode(). */ + + err = 0; + if (orig_termios_fd != -1) + if (tcsetattr(orig_termios_fd, TCSANOW, &orig_termios)) + err = UV__ERR(errno); + + uv_spinlock_unlock(&termios_spinlock); + errno = saved_errno; + + return err; +} + +void uv_tty_set_vterm_state(uv_tty_vtermstate_t state) { +} + +int uv_tty_get_vterm_state(uv_tty_vtermstate_t* state) { + return UV_ENOTSUP; +} diff --git a/include/libuv/src/unix/udp.c b/include/libuv/src/unix/udp.c new file mode 100644 index 000000000..7d699a167 --- /dev/null +++ b/include/libuv/src/unix/udp.c @@ -0,0 +1,1332 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include +#include +#include +#include +#if defined(__MVS__) +#include +#endif +#include + +#define UV__UDP_DGRAM_MAXSIZE (64 * 1024) + +#if defined(IPV6_JOIN_GROUP) && !defined(IPV6_ADD_MEMBERSHIP) +# define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP +#endif + +#if defined(IPV6_LEAVE_GROUP) && !defined(IPV6_DROP_MEMBERSHIP) +# define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP +#endif + +union uv__sockaddr { + struct sockaddr_in6 in6; + struct sockaddr_in in; + struct sockaddr addr; +}; + +static void uv__udp_run_completed(uv_udp_t* handle); +static void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents); +static void uv__udp_recvmsg(uv_udp_t* handle); +static void uv__udp_sendmsg(uv_udp_t* handle); +static int uv__udp_maybe_deferred_bind(uv_udp_t* handle, + int domain, + unsigned int flags); + +#if HAVE_MMSG + +#define UV__MMSG_MAXWIDTH 20 + +static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf); +static void uv__udp_sendmmsg(uv_udp_t* handle); + +static int uv__recvmmsg_avail; +static int uv__sendmmsg_avail; +static uv_once_t once = UV_ONCE_INIT; + +static void uv__udp_mmsg_init(void) { + int ret; + int s; + s = uv__socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) + return; + ret = uv__sendmmsg(s, NULL, 0); + if (ret == 0 || errno != ENOSYS) { + uv__sendmmsg_avail = 1; + uv__recvmmsg_avail = 1; + } else { + ret = uv__recvmmsg(s, NULL, 0); + if (ret == 0 || errno != ENOSYS) + uv__recvmmsg_avail = 1; + } + uv__close(s); +} + +#endif + +void uv__udp_close(uv_udp_t* handle) { + uv__io_close(handle->loop, &handle->io_watcher); + uv__handle_stop(handle); + + if (handle->io_watcher.fd != -1) { + uv__close(handle->io_watcher.fd); + handle->io_watcher.fd = -1; + } +} + + +void uv__udp_finish_close(uv_udp_t* handle) { + uv_udp_send_t* req; + QUEUE* q; + + assert(!uv__io_active(&handle->io_watcher, POLLIN | POLLOUT)); + assert(handle->io_watcher.fd == -1); + + while (!QUEUE_EMPTY(&handle->write_queue)) { + q = QUEUE_HEAD(&handle->write_queue); + QUEUE_REMOVE(q); + + req = QUEUE_DATA(q, uv_udp_send_t, queue); + req->status = UV_ECANCELED; + QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue); + } + + uv__udp_run_completed(handle); + + assert(handle->send_queue_size == 0); + assert(handle->send_queue_count == 0); + + /* Now tear down the handle. */ + handle->recv_cb = NULL; + handle->alloc_cb = NULL; + /* but _do not_ touch close_cb */ +} + + +static void uv__udp_run_completed(uv_udp_t* handle) { + uv_udp_send_t* req; + QUEUE* q; + + assert(!(handle->flags & UV_HANDLE_UDP_PROCESSING)); + handle->flags |= UV_HANDLE_UDP_PROCESSING; + + while (!QUEUE_EMPTY(&handle->write_completed_queue)) { + q = QUEUE_HEAD(&handle->write_completed_queue); + QUEUE_REMOVE(q); + + req = QUEUE_DATA(q, uv_udp_send_t, queue); + uv__req_unregister(handle->loop, req); + + handle->send_queue_size -= uv__count_bufs(req->bufs, req->nbufs); + handle->send_queue_count--; + + if (req->bufs != req->bufsml) + uv__free(req->bufs); + req->bufs = NULL; + + if (req->send_cb == NULL) + continue; + + /* req->status >= 0 == bytes written + * req->status < 0 == errno + */ + if (req->status >= 0) + req->send_cb(req, 0); + else + req->send_cb(req, req->status); + } + + if (QUEUE_EMPTY(&handle->write_queue)) { + /* Pending queue and completion queue empty, stop watcher. */ + uv__io_stop(handle->loop, &handle->io_watcher, POLLOUT); + if (!uv__io_active(&handle->io_watcher, POLLIN)) + uv__handle_stop(handle); + } + + handle->flags &= ~UV_HANDLE_UDP_PROCESSING; +} + + +static void uv__udp_io(uv_loop_t* loop, uv__io_t* w, unsigned int revents) { + uv_udp_t* handle; + + handle = container_of(w, uv_udp_t, io_watcher); + assert(handle->type == UV_UDP); + + if (revents & POLLIN) + uv__udp_recvmsg(handle); + + if (revents & POLLOUT) { + uv__udp_sendmsg(handle); + uv__udp_run_completed(handle); + } +} + +#if HAVE_MMSG +static int uv__udp_recvmmsg(uv_udp_t* handle, uv_buf_t* buf) { + struct sockaddr_in6 peers[UV__MMSG_MAXWIDTH]; + struct iovec iov[UV__MMSG_MAXWIDTH]; + struct uv__mmsghdr msgs[UV__MMSG_MAXWIDTH]; + ssize_t nread; + uv_buf_t chunk_buf; + size_t chunks; + int flags; + size_t k; + + /* prepare structures for recvmmsg */ + chunks = buf->len / UV__UDP_DGRAM_MAXSIZE; + if (chunks > ARRAY_SIZE(iov)) + chunks = ARRAY_SIZE(iov); + for (k = 0; k < chunks; ++k) { + iov[k].iov_base = buf->base + k * UV__UDP_DGRAM_MAXSIZE; + iov[k].iov_len = UV__UDP_DGRAM_MAXSIZE; + msgs[k].msg_hdr.msg_iov = iov + k; + msgs[k].msg_hdr.msg_iovlen = 1; + msgs[k].msg_hdr.msg_name = peers + k; + msgs[k].msg_hdr.msg_namelen = sizeof(peers[0]); + msgs[k].msg_hdr.msg_control = NULL; + msgs[k].msg_hdr.msg_controllen = 0; + msgs[k].msg_hdr.msg_flags = 0; + } + + do + nread = uv__recvmmsg(handle->io_watcher.fd, msgs, chunks); + while (nread == -1 && errno == EINTR); + + if (nread < 1) { + if (nread == 0 || errno == EAGAIN || errno == EWOULDBLOCK) + handle->recv_cb(handle, 0, buf, NULL, 0); + else + handle->recv_cb(handle, UV__ERR(errno), buf, NULL, 0); + } else { + /* pass each chunk to the application */ + for (k = 0; k < (size_t) nread && handle->recv_cb != NULL; k++) { + flags = UV_UDP_MMSG_CHUNK; + if (msgs[k].msg_hdr.msg_flags & MSG_TRUNC) + flags |= UV_UDP_PARTIAL; + + chunk_buf = uv_buf_init(iov[k].iov_base, iov[k].iov_len); + handle->recv_cb(handle, + msgs[k].msg_len, + &chunk_buf, + msgs[k].msg_hdr.msg_name, + flags); + } + + /* one last callback so the original buffer is freed */ + if (handle->recv_cb != NULL) + handle->recv_cb(handle, 0, buf, NULL, UV_UDP_MMSG_FREE); + } + return nread; +} +#endif + +static void uv__udp_recvmsg(uv_udp_t* handle) { + struct sockaddr_storage peer; + struct msghdr h; + ssize_t nread; + uv_buf_t buf; + int flags; + int count; + + assert(handle->recv_cb != NULL); + assert(handle->alloc_cb != NULL); + + /* Prevent loop starvation when the data comes in as fast as (or faster than) + * we can read it. XXX Need to rearm fd if we switch to edge-triggered I/O. + */ + count = 32; + + do { + buf = uv_buf_init(NULL, 0); + handle->alloc_cb((uv_handle_t*) handle, UV__UDP_DGRAM_MAXSIZE, &buf); + if (buf.base == NULL || buf.len == 0) { + handle->recv_cb(handle, UV_ENOBUFS, &buf, NULL, 0); + return; + } + assert(buf.base != NULL); + +#if HAVE_MMSG + if (uv_udp_using_recvmmsg(handle)) { + nread = uv__udp_recvmmsg(handle, &buf); + if (nread > 0) + count -= nread; + continue; + } +#endif + + memset(&h, 0, sizeof(h)); + memset(&peer, 0, sizeof(peer)); + h.msg_name = &peer; + h.msg_namelen = sizeof(peer); + h.msg_iov = (void*) &buf; + h.msg_iovlen = 1; + + do { + nread = recvmsg(handle->io_watcher.fd, &h, 0); + } + while (nread == -1 && errno == EINTR); + + if (nread == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) + handle->recv_cb(handle, 0, &buf, NULL, 0); + else + handle->recv_cb(handle, UV__ERR(errno), &buf, NULL, 0); + } + else { + flags = 0; + if (h.msg_flags & MSG_TRUNC) + flags |= UV_UDP_PARTIAL; + + handle->recv_cb(handle, nread, &buf, (const struct sockaddr*) &peer, flags); + } + count--; + } + /* recv_cb callback may decide to pause or close the handle */ + while (nread != -1 + && count > 0 + && handle->io_watcher.fd != -1 + && handle->recv_cb != NULL); +} + +#if HAVE_MMSG +static void uv__udp_sendmmsg(uv_udp_t* handle) { + uv_udp_send_t* req; + struct uv__mmsghdr h[UV__MMSG_MAXWIDTH]; + struct uv__mmsghdr *p; + QUEUE* q; + ssize_t npkts; + size_t pkts; + size_t i; + + if (QUEUE_EMPTY(&handle->write_queue)) + return; + +write_queue_drain: + for (pkts = 0, q = QUEUE_HEAD(&handle->write_queue); + pkts < UV__MMSG_MAXWIDTH && q != &handle->write_queue; + ++pkts, q = QUEUE_HEAD(q)) { + assert(q != NULL); + req = QUEUE_DATA(q, uv_udp_send_t, queue); + assert(req != NULL); + + p = &h[pkts]; + memset(p, 0, sizeof(*p)); + if (req->addr.ss_family == AF_UNSPEC) { + p->msg_hdr.msg_name = NULL; + p->msg_hdr.msg_namelen = 0; + } else { + p->msg_hdr.msg_name = &req->addr; + if (req->addr.ss_family == AF_INET6) + p->msg_hdr.msg_namelen = sizeof(struct sockaddr_in6); + else if (req->addr.ss_family == AF_INET) + p->msg_hdr.msg_namelen = sizeof(struct sockaddr_in); + else if (req->addr.ss_family == AF_UNIX) + p->msg_hdr.msg_namelen = sizeof(struct sockaddr_un); + else { + assert(0 && "unsupported address family"); + abort(); + } + } + h[pkts].msg_hdr.msg_iov = (struct iovec*) req->bufs; + h[pkts].msg_hdr.msg_iovlen = req->nbufs; + } + + do + npkts = uv__sendmmsg(handle->io_watcher.fd, h, pkts); + while (npkts == -1 && errno == EINTR); + + if (npkts < 1) { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS) + return; + for (i = 0, q = QUEUE_HEAD(&handle->write_queue); + i < pkts && q != &handle->write_queue; + ++i, q = QUEUE_HEAD(&handle->write_queue)) { + assert(q != NULL); + req = QUEUE_DATA(q, uv_udp_send_t, queue); + assert(req != NULL); + + req->status = UV__ERR(errno); + QUEUE_REMOVE(&req->queue); + QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue); + } + uv__io_feed(handle->loop, &handle->io_watcher); + return; + } + + for (i = 0, q = QUEUE_HEAD(&handle->write_queue); + i < pkts && q != &handle->write_queue; + ++i, q = QUEUE_HEAD(&handle->write_queue)) { + assert(q != NULL); + req = QUEUE_DATA(q, uv_udp_send_t, queue); + assert(req != NULL); + + req->status = req->bufs[0].len; + + /* Sending a datagram is an atomic operation: either all data + * is written or nothing is (and EMSGSIZE is raised). That is + * why we don't handle partial writes. Just pop the request + * off the write queue and onto the completed queue, done. + */ + QUEUE_REMOVE(&req->queue); + QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue); + } + + /* couldn't batch everything, continue sending (jump to avoid stack growth) */ + if (!QUEUE_EMPTY(&handle->write_queue)) + goto write_queue_drain; + uv__io_feed(handle->loop, &handle->io_watcher); + return; +} +#endif + +static void uv__udp_sendmsg(uv_udp_t* handle) { + uv_udp_send_t* req; + struct msghdr h; + QUEUE* q; + ssize_t size; + +#if HAVE_MMSG + uv_once(&once, uv__udp_mmsg_init); + if (uv__sendmmsg_avail) { + uv__udp_sendmmsg(handle); + return; + } +#endif + + while (!QUEUE_EMPTY(&handle->write_queue)) { + q = QUEUE_HEAD(&handle->write_queue); + assert(q != NULL); + + req = QUEUE_DATA(q, uv_udp_send_t, queue); + assert(req != NULL); + + memset(&h, 0, sizeof h); + if (req->addr.ss_family == AF_UNSPEC) { + h.msg_name = NULL; + h.msg_namelen = 0; + } else { + h.msg_name = &req->addr; + if (req->addr.ss_family == AF_INET6) + h.msg_namelen = sizeof(struct sockaddr_in6); + else if (req->addr.ss_family == AF_INET) + h.msg_namelen = sizeof(struct sockaddr_in); + else if (req->addr.ss_family == AF_UNIX) + h.msg_namelen = sizeof(struct sockaddr_un); + else { + assert(0 && "unsupported address family"); + abort(); + } + } + h.msg_iov = (struct iovec*) req->bufs; + h.msg_iovlen = req->nbufs; + + do { + size = sendmsg(handle->io_watcher.fd, &h, 0); + } while (size == -1 && errno == EINTR); + + if (size == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS) + break; + } + + req->status = (size == -1 ? UV__ERR(errno) : size); + + /* Sending a datagram is an atomic operation: either all data + * is written or nothing is (and EMSGSIZE is raised). That is + * why we don't handle partial writes. Just pop the request + * off the write queue and onto the completed queue, done. + */ + QUEUE_REMOVE(&req->queue); + QUEUE_INSERT_TAIL(&handle->write_completed_queue, &req->queue); + uv__io_feed(handle->loop, &handle->io_watcher); + } +} + +/* On the BSDs, SO_REUSEPORT implies SO_REUSEADDR but with some additional + * refinements for programs that use multicast. + * + * Linux as of 3.9 has a SO_REUSEPORT socket option but with semantics that + * are different from the BSDs: it _shares_ the port rather than steal it + * from the current listener. While useful, it's not something we can emulate + * on other platforms so we don't enable it. + * + * zOS does not support getsockname with SO_REUSEPORT option when using + * AF_UNIX. + */ +static int uv__set_reuse(int fd) { + int yes; + yes = 1; + +#if defined(SO_REUSEPORT) && defined(__MVS__) + struct sockaddr_in sockfd; + unsigned int sockfd_len = sizeof(sockfd); + if (getsockname(fd, (struct sockaddr*) &sockfd, &sockfd_len) == -1) + return UV__ERR(errno); + if (sockfd.sin_family == AF_UNIX) { + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes))) + return UV__ERR(errno); + } else { + if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes))) + return UV__ERR(errno); + } +#elif defined(SO_REUSEPORT) && !defined(__linux__) + if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes))) + return UV__ERR(errno); +#else + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes))) + return UV__ERR(errno); +#endif + + return 0; +} + + +int uv__udp_bind(uv_udp_t* handle, + const struct sockaddr* addr, + unsigned int addrlen, + unsigned int flags) { + int err; + int yes; + int fd; + + /* Check for bad flags. */ + if (flags & ~(UV_UDP_IPV6ONLY | UV_UDP_REUSEADDR)) + return UV_EINVAL; + + /* Cannot set IPv6-only mode on non-IPv6 socket. */ + if ((flags & UV_UDP_IPV6ONLY) && addr->sa_family != AF_INET6) + return UV_EINVAL; + + fd = handle->io_watcher.fd; + if (fd == -1) { + err = uv__socket(addr->sa_family, SOCK_DGRAM, 0); + if (err < 0) + return err; + fd = err; + handle->io_watcher.fd = fd; + } + + if (flags & UV_UDP_REUSEADDR) { + err = uv__set_reuse(fd); + if (err) + return err; + } + + if (flags & UV_UDP_IPV6ONLY) { +#ifdef IPV6_V6ONLY + yes = 1; + if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof yes) == -1) { + err = UV__ERR(errno); + return err; + } +#else + err = UV_ENOTSUP; + return err; +#endif + } + + if (bind(fd, addr, addrlen)) { + err = UV__ERR(errno); + if (errno == EAFNOSUPPORT) + /* OSX, other BSDs and SunoS fail with EAFNOSUPPORT when binding a + * socket created with AF_INET to an AF_INET6 address or vice versa. */ + err = UV_EINVAL; + return err; + } + + if (addr->sa_family == AF_INET6) + handle->flags |= UV_HANDLE_IPV6; + + handle->flags |= UV_HANDLE_BOUND; + return 0; +} + + +static int uv__udp_maybe_deferred_bind(uv_udp_t* handle, + int domain, + unsigned int flags) { + union uv__sockaddr taddr; + socklen_t addrlen; + + if (handle->io_watcher.fd != -1) + return 0; + + switch (domain) { + case AF_INET: + { + struct sockaddr_in* addr = &taddr.in; + memset(addr, 0, sizeof *addr); + addr->sin_family = AF_INET; + addr->sin_addr.s_addr = INADDR_ANY; + addrlen = sizeof *addr; + break; + } + case AF_INET6: + { + struct sockaddr_in6* addr = &taddr.in6; + memset(addr, 0, sizeof *addr); + addr->sin6_family = AF_INET6; + addr->sin6_addr = in6addr_any; + addrlen = sizeof *addr; + break; + } + default: + assert(0 && "unsupported address family"); + abort(); + } + + return uv__udp_bind(handle, &taddr.addr, addrlen, flags); +} + + +int uv__udp_connect(uv_udp_t* handle, + const struct sockaddr* addr, + unsigned int addrlen) { + int err; + + err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0); + if (err) + return err; + + do { + errno = 0; + err = connect(handle->io_watcher.fd, addr, addrlen); + } while (err == -1 && errno == EINTR); + + if (err) + return UV__ERR(errno); + + handle->flags |= UV_HANDLE_UDP_CONNECTED; + + return 0; +} + + +int uv__udp_disconnect(uv_udp_t* handle) { + int r; + struct sockaddr addr; + + memset(&addr, 0, sizeof(addr)); + + addr.sa_family = AF_UNSPEC; + + do { + errno = 0; + r = connect(handle->io_watcher.fd, &addr, sizeof(addr)); + } while (r == -1 && errno == EINTR); + + if (r == -1 && errno != EAFNOSUPPORT) + return UV__ERR(errno); + + handle->flags &= ~UV_HANDLE_UDP_CONNECTED; + return 0; +} + + +int uv__udp_send(uv_udp_send_t* req, + uv_udp_t* handle, + const uv_buf_t bufs[], + unsigned int nbufs, + const struct sockaddr* addr, + unsigned int addrlen, + uv_udp_send_cb send_cb) { + int err; + int empty_queue; + + assert(nbufs > 0); + + if (addr) { + err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0); + if (err) + return err; + } + + /* It's legal for send_queue_count > 0 even when the write_queue is empty; + * it means there are error-state requests in the write_completed_queue that + * will touch up send_queue_size/count later. + */ + empty_queue = (handle->send_queue_count == 0); + + uv__req_init(handle->loop, req, UV_UDP_SEND); + assert(addrlen <= sizeof(req->addr)); + if (addr == NULL) + req->addr.ss_family = AF_UNSPEC; + else + memcpy(&req->addr, addr, addrlen); + req->send_cb = send_cb; + req->handle = handle; + req->nbufs = nbufs; + + req->bufs = req->bufsml; + if (nbufs > ARRAY_SIZE(req->bufsml)) + req->bufs = uv__malloc(nbufs * sizeof(bufs[0])); + + if (req->bufs == NULL) { + uv__req_unregister(handle->loop, req); + return UV_ENOMEM; + } + + memcpy(req->bufs, bufs, nbufs * sizeof(bufs[0])); + handle->send_queue_size += uv__count_bufs(req->bufs, req->nbufs); + handle->send_queue_count++; + QUEUE_INSERT_TAIL(&handle->write_queue, &req->queue); + uv__handle_start(handle); + + if (empty_queue && !(handle->flags & UV_HANDLE_UDP_PROCESSING)) { + uv__udp_sendmsg(handle); + + /* `uv__udp_sendmsg` may not be able to do non-blocking write straight + * away. In such cases the `io_watcher` has to be queued for asynchronous + * write. + */ + if (!QUEUE_EMPTY(&handle->write_queue)) + uv__io_start(handle->loop, &handle->io_watcher, POLLOUT); + } else { + uv__io_start(handle->loop, &handle->io_watcher, POLLOUT); + } + + return 0; +} + + +int uv__udp_try_send(uv_udp_t* handle, + const uv_buf_t bufs[], + unsigned int nbufs, + const struct sockaddr* addr, + unsigned int addrlen) { + int err; + struct msghdr h; + ssize_t size; + + assert(nbufs > 0); + + /* already sending a message */ + if (handle->send_queue_count != 0) + return UV_EAGAIN; + + if (addr) { + err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0); + if (err) + return err; + } else { + assert(handle->flags & UV_HANDLE_UDP_CONNECTED); + } + + memset(&h, 0, sizeof h); + h.msg_name = (struct sockaddr*) addr; + h.msg_namelen = addrlen; + h.msg_iov = (struct iovec*) bufs; + h.msg_iovlen = nbufs; + + do { + size = sendmsg(handle->io_watcher.fd, &h, 0); + } while (size == -1 && errno == EINTR); + + if (size == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENOBUFS) + return UV_EAGAIN; + else + return UV__ERR(errno); + } + + return size; +} + + +static int uv__udp_set_membership4(uv_udp_t* handle, + const struct sockaddr_in* multicast_addr, + const char* interface_addr, + uv_membership membership) { + struct ip_mreq mreq; + int optname; + int err; + + memset(&mreq, 0, sizeof mreq); + + if (interface_addr) { + err = uv_inet_pton(AF_INET, interface_addr, &mreq.imr_interface.s_addr); + if (err) + return err; + } else { + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + } + + mreq.imr_multiaddr.s_addr = multicast_addr->sin_addr.s_addr; + + switch (membership) { + case UV_JOIN_GROUP: + optname = IP_ADD_MEMBERSHIP; + break; + case UV_LEAVE_GROUP: + optname = IP_DROP_MEMBERSHIP; + break; + default: + return UV_EINVAL; + } + + if (setsockopt(handle->io_watcher.fd, + IPPROTO_IP, + optname, + &mreq, + sizeof(mreq))) { +#if defined(__MVS__) + if (errno == ENXIO) + return UV_ENODEV; +#endif + return UV__ERR(errno); + } + + return 0; +} + + +static int uv__udp_set_membership6(uv_udp_t* handle, + const struct sockaddr_in6* multicast_addr, + const char* interface_addr, + uv_membership membership) { + int optname; + struct ipv6_mreq mreq; + struct sockaddr_in6 addr6; + + memset(&mreq, 0, sizeof mreq); + + if (interface_addr) { + if (uv_ip6_addr(interface_addr, 0, &addr6)) + return UV_EINVAL; + mreq.ipv6mr_interface = addr6.sin6_scope_id; + } else { + mreq.ipv6mr_interface = 0; + } + + mreq.ipv6mr_multiaddr = multicast_addr->sin6_addr; + + switch (membership) { + case UV_JOIN_GROUP: + optname = IPV6_ADD_MEMBERSHIP; + break; + case UV_LEAVE_GROUP: + optname = IPV6_DROP_MEMBERSHIP; + break; + default: + return UV_EINVAL; + } + + if (setsockopt(handle->io_watcher.fd, + IPPROTO_IPV6, + optname, + &mreq, + sizeof(mreq))) { +#if defined(__MVS__) + if (errno == ENXIO) + return UV_ENODEV; +#endif + return UV__ERR(errno); + } + + return 0; +} + + +#if !defined(__OpenBSD__) && \ + !defined(__NetBSD__) && \ + !defined(__ANDROID__) && \ + !defined(__DragonFly__) & \ + !defined(__QNX__) +static int uv__udp_set_source_membership4(uv_udp_t* handle, + const struct sockaddr_in* multicast_addr, + const char* interface_addr, + const struct sockaddr_in* source_addr, + uv_membership membership) { + struct ip_mreq_source mreq; + int optname; + int err; + + err = uv__udp_maybe_deferred_bind(handle, AF_INET, UV_UDP_REUSEADDR); + if (err) + return err; + + memset(&mreq, 0, sizeof(mreq)); + + if (interface_addr != NULL) { + err = uv_inet_pton(AF_INET, interface_addr, &mreq.imr_interface.s_addr); + if (err) + return err; + } else { + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + } + + mreq.imr_multiaddr.s_addr = multicast_addr->sin_addr.s_addr; + mreq.imr_sourceaddr.s_addr = source_addr->sin_addr.s_addr; + + if (membership == UV_JOIN_GROUP) + optname = IP_ADD_SOURCE_MEMBERSHIP; + else if (membership == UV_LEAVE_GROUP) + optname = IP_DROP_SOURCE_MEMBERSHIP; + else + return UV_EINVAL; + + if (setsockopt(handle->io_watcher.fd, + IPPROTO_IP, + optname, + &mreq, + sizeof(mreq))) { + return UV__ERR(errno); + } + + return 0; +} + + +static int uv__udp_set_source_membership6(uv_udp_t* handle, + const struct sockaddr_in6* multicast_addr, + const char* interface_addr, + const struct sockaddr_in6* source_addr, + uv_membership membership) { + struct group_source_req mreq; + struct sockaddr_in6 addr6; + int optname; + int err; + + err = uv__udp_maybe_deferred_bind(handle, AF_INET6, UV_UDP_REUSEADDR); + if (err) + return err; + + memset(&mreq, 0, sizeof(mreq)); + + if (interface_addr != NULL) { + err = uv_ip6_addr(interface_addr, 0, &addr6); + if (err) + return err; + mreq.gsr_interface = addr6.sin6_scope_id; + } else { + mreq.gsr_interface = 0; + } + + STATIC_ASSERT(sizeof(mreq.gsr_group) >= sizeof(*multicast_addr)); + STATIC_ASSERT(sizeof(mreq.gsr_source) >= sizeof(*source_addr)); + memcpy(&mreq.gsr_group, multicast_addr, sizeof(*multicast_addr)); + memcpy(&mreq.gsr_source, source_addr, sizeof(*source_addr)); + + if (membership == UV_JOIN_GROUP) + optname = MCAST_JOIN_SOURCE_GROUP; + else if (membership == UV_LEAVE_GROUP) + optname = MCAST_LEAVE_SOURCE_GROUP; + else + return UV_EINVAL; + + if (setsockopt(handle->io_watcher.fd, + IPPROTO_IPV6, + optname, + &mreq, + sizeof(mreq))) { + return UV__ERR(errno); + } + + return 0; +} +#endif + + +int uv__udp_init_ex(uv_loop_t* loop, + uv_udp_t* handle, + unsigned flags, + int domain) { + int fd; + + fd = -1; + if (domain != AF_UNSPEC) { + fd = uv__socket(domain, SOCK_DGRAM, 0); + if (fd < 0) + return fd; + } + + uv__handle_init(loop, (uv_handle_t*)handle, UV_UDP); + handle->alloc_cb = NULL; + handle->recv_cb = NULL; + handle->send_queue_size = 0; + handle->send_queue_count = 0; + uv__io_init(&handle->io_watcher, uv__udp_io, fd); + QUEUE_INIT(&handle->write_queue); + QUEUE_INIT(&handle->write_completed_queue); + + return 0; +} + + +int uv_udp_using_recvmmsg(const uv_udp_t* handle) { +#if HAVE_MMSG + if (handle->flags & UV_HANDLE_UDP_RECVMMSG) { + uv_once(&once, uv__udp_mmsg_init); + return uv__recvmmsg_avail; + } +#endif + return 0; +} + + +int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) { + int err; + + /* Check for already active socket. */ + if (handle->io_watcher.fd != -1) + return UV_EBUSY; + + if (uv__fd_exists(handle->loop, sock)) + return UV_EEXIST; + + err = uv__nonblock(sock, 1); + if (err) + return err; + + err = uv__set_reuse(sock); + if (err) + return err; + + handle->io_watcher.fd = sock; + if (uv__udp_is_connected(handle)) + handle->flags |= UV_HANDLE_UDP_CONNECTED; + + return 0; +} + + +int uv_udp_set_membership(uv_udp_t* handle, + const char* multicast_addr, + const char* interface_addr, + uv_membership membership) { + int err; + struct sockaddr_in addr4; + struct sockaddr_in6 addr6; + + if (uv_ip4_addr(multicast_addr, 0, &addr4) == 0) { + err = uv__udp_maybe_deferred_bind(handle, AF_INET, UV_UDP_REUSEADDR); + if (err) + return err; + return uv__udp_set_membership4(handle, &addr4, interface_addr, membership); + } else if (uv_ip6_addr(multicast_addr, 0, &addr6) == 0) { + err = uv__udp_maybe_deferred_bind(handle, AF_INET6, UV_UDP_REUSEADDR); + if (err) + return err; + return uv__udp_set_membership6(handle, &addr6, interface_addr, membership); + } else { + return UV_EINVAL; + } +} + + +int uv_udp_set_source_membership(uv_udp_t* handle, + const char* multicast_addr, + const char* interface_addr, + const char* source_addr, + uv_membership membership) { +#if !defined(__OpenBSD__) && \ + !defined(__NetBSD__) && \ + !defined(__ANDROID__) && \ + !defined(__DragonFly__) && \ + !defined(__QNX__) + int err; + union uv__sockaddr mcast_addr; + union uv__sockaddr src_addr; + + err = uv_ip4_addr(multicast_addr, 0, &mcast_addr.in); + if (err) { + err = uv_ip6_addr(multicast_addr, 0, &mcast_addr.in6); + if (err) + return err; + err = uv_ip6_addr(source_addr, 0, &src_addr.in6); + if (err) + return err; + return uv__udp_set_source_membership6(handle, + &mcast_addr.in6, + interface_addr, + &src_addr.in6, + membership); + } + + err = uv_ip4_addr(source_addr, 0, &src_addr.in); + if (err) + return err; + return uv__udp_set_source_membership4(handle, + &mcast_addr.in, + interface_addr, + &src_addr.in, + membership); +#else + return UV_ENOSYS; +#endif +} + + +static int uv__setsockopt(uv_udp_t* handle, + int option4, + int option6, + const void* val, + socklen_t size) { + int r; + + if (handle->flags & UV_HANDLE_IPV6) + r = setsockopt(handle->io_watcher.fd, + IPPROTO_IPV6, + option6, + val, + size); + else + r = setsockopt(handle->io_watcher.fd, + IPPROTO_IP, + option4, + val, + size); + if (r) + return UV__ERR(errno); + + return 0; +} + +static int uv__setsockopt_maybe_char(uv_udp_t* handle, + int option4, + int option6, + int val) { +#if defined(__sun) || defined(_AIX) || defined(__MVS__) + char arg = val; +#elif defined(__OpenBSD__) + unsigned char arg = val; +#else + int arg = val; +#endif + + if (val < 0 || val > 255) + return UV_EINVAL; + + return uv__setsockopt(handle, option4, option6, &arg, sizeof(arg)); +} + + +int uv_udp_set_broadcast(uv_udp_t* handle, int on) { + if (setsockopt(handle->io_watcher.fd, + SOL_SOCKET, + SO_BROADCAST, + &on, + sizeof(on))) { + return UV__ERR(errno); + } + + return 0; +} + + +int uv_udp_set_ttl(uv_udp_t* handle, int ttl) { + if (ttl < 1 || ttl > 255) + return UV_EINVAL; + +#if defined(__MVS__) + if (!(handle->flags & UV_HANDLE_IPV6)) + return UV_ENOTSUP; /* zOS does not support setting ttl for IPv4 */ +#endif + +/* + * On Solaris and derivatives such as SmartOS, the length of socket options + * is sizeof(int) for IP_TTL and IPV6_UNICAST_HOPS, + * so hardcode the size of these options on this platform, + * and use the general uv__setsockopt_maybe_char call on other platforms. + */ +#if defined(__sun) || defined(_AIX) || defined(__OpenBSD__) || \ + defined(__MVS__) || defined(__QNX__) + + return uv__setsockopt(handle, + IP_TTL, + IPV6_UNICAST_HOPS, + &ttl, + sizeof(ttl)); + +#else /* !(defined(__sun) || defined(_AIX) || defined (__OpenBSD__) || + defined(__MVS__) || defined(__QNX__)) */ + + return uv__setsockopt_maybe_char(handle, + IP_TTL, + IPV6_UNICAST_HOPS, + ttl); + +#endif /* defined(__sun) || defined(_AIX) || defined (__OpenBSD__) || + defined(__MVS__) || defined(__QNX__) */ +} + + +int uv_udp_set_multicast_ttl(uv_udp_t* handle, int ttl) { +/* + * On Solaris and derivatives such as SmartOS, the length of socket options + * is sizeof(int) for IPV6_MULTICAST_HOPS and sizeof(char) for + * IP_MULTICAST_TTL, so hardcode the size of the option in the IPv6 case, + * and use the general uv__setsockopt_maybe_char call otherwise. + */ +#if defined(__sun) || defined(_AIX) || defined(__OpenBSD__) || \ + defined(__MVS__) || defined(__QNX__) + if (handle->flags & UV_HANDLE_IPV6) + return uv__setsockopt(handle, + IP_MULTICAST_TTL, + IPV6_MULTICAST_HOPS, + &ttl, + sizeof(ttl)); +#endif /* defined(__sun) || defined(_AIX) || defined(__OpenBSD__) || \ + defined(__MVS__) || defined(__QNX__) */ + + return uv__setsockopt_maybe_char(handle, + IP_MULTICAST_TTL, + IPV6_MULTICAST_HOPS, + ttl); +} + + +int uv_udp_set_multicast_loop(uv_udp_t* handle, int on) { +/* + * On Solaris and derivatives such as SmartOS, the length of socket options + * is sizeof(int) for IPV6_MULTICAST_LOOP and sizeof(char) for + * IP_MULTICAST_LOOP, so hardcode the size of the option in the IPv6 case, + * and use the general uv__setsockopt_maybe_char call otherwise. + */ +#if defined(__sun) || defined(_AIX) || defined(__OpenBSD__) || \ + defined(__MVS__) || defined(__QNX__) + if (handle->flags & UV_HANDLE_IPV6) + return uv__setsockopt(handle, + IP_MULTICAST_LOOP, + IPV6_MULTICAST_LOOP, + &on, + sizeof(on)); +#endif /* defined(__sun) || defined(_AIX) ||defined(__OpenBSD__) || + defined(__MVS__) || defined(__QNX__) */ + + return uv__setsockopt_maybe_char(handle, + IP_MULTICAST_LOOP, + IPV6_MULTICAST_LOOP, + on); +} + +int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr) { + struct sockaddr_storage addr_st; + struct sockaddr_in* addr4; + struct sockaddr_in6* addr6; + + addr4 = (struct sockaddr_in*) &addr_st; + addr6 = (struct sockaddr_in6*) &addr_st; + + if (!interface_addr) { + memset(&addr_st, 0, sizeof addr_st); + if (handle->flags & UV_HANDLE_IPV6) { + addr_st.ss_family = AF_INET6; + addr6->sin6_scope_id = 0; + } else { + addr_st.ss_family = AF_INET; + addr4->sin_addr.s_addr = htonl(INADDR_ANY); + } + } else if (uv_ip4_addr(interface_addr, 0, addr4) == 0) { + /* nothing, address was parsed */ + } else if (uv_ip6_addr(interface_addr, 0, addr6) == 0) { + /* nothing, address was parsed */ + } else { + return UV_EINVAL; + } + + if (addr_st.ss_family == AF_INET) { + if (setsockopt(handle->io_watcher.fd, + IPPROTO_IP, + IP_MULTICAST_IF, + (void*) &addr4->sin_addr, + sizeof(addr4->sin_addr)) == -1) { + return UV__ERR(errno); + } + } else if (addr_st.ss_family == AF_INET6) { + if (setsockopt(handle->io_watcher.fd, + IPPROTO_IPV6, + IPV6_MULTICAST_IF, + &addr6->sin6_scope_id, + sizeof(addr6->sin6_scope_id)) == -1) { + return UV__ERR(errno); + } + } else { + assert(0 && "unexpected address family"); + abort(); + } + + return 0; +} + +int uv_udp_getpeername(const uv_udp_t* handle, + struct sockaddr* name, + int* namelen) { + + return uv__getsockpeername((const uv_handle_t*) handle, + getpeername, + name, + namelen); +} + +int uv_udp_getsockname(const uv_udp_t* handle, + struct sockaddr* name, + int* namelen) { + + return uv__getsockpeername((const uv_handle_t*) handle, + getsockname, + name, + namelen); +} + + +int uv__udp_recv_start(uv_udp_t* handle, + uv_alloc_cb alloc_cb, + uv_udp_recv_cb recv_cb) { + int err; + + if (alloc_cb == NULL || recv_cb == NULL) + return UV_EINVAL; + + if (uv__io_active(&handle->io_watcher, POLLIN)) + return UV_EALREADY; /* FIXME(bnoordhuis) Should be UV_EBUSY. */ + + err = uv__udp_maybe_deferred_bind(handle, AF_INET, 0); + if (err) + return err; + + handle->alloc_cb = alloc_cb; + handle->recv_cb = recv_cb; + + uv__io_start(handle->loop, &handle->io_watcher, POLLIN); + uv__handle_start(handle); + + return 0; +} + + +int uv__udp_recv_stop(uv_udp_t* handle) { + uv__io_stop(handle->loop, &handle->io_watcher, POLLIN); + + if (!uv__io_active(&handle->io_watcher, POLLOUT)) + uv__handle_stop(handle); + + handle->alloc_cb = NULL; + handle->recv_cb = NULL; + + return 0; +} diff --git a/include/libuv/src/uv-common.c b/include/libuv/src/uv-common.c index ba2644691..602e5f492 100644 --- a/include/libuv/src/uv-common.c +++ b/include/libuv/src/uv-common.c @@ -34,6 +34,7 @@ # include /* malloc */ #else # include /* if_nametoindex */ +# include /* AF_UNIX, sockaddr_un */ #endif @@ -72,7 +73,9 @@ char* uv__strndup(const char* s, size_t n) { } void* uv__malloc(size_t size) { - return uv__allocator.local_malloc(size); + if (size > 0) + return uv__allocator.local_malloc(size); + return NULL; } void uv__free(void* ptr) { @@ -91,7 +94,21 @@ void* uv__calloc(size_t count, size_t size) { } void* uv__realloc(void* ptr, size_t size) { - return uv__allocator.local_realloc(ptr, size); + if (size > 0) + return uv__allocator.local_realloc(ptr, size); + uv__free(ptr); + return NULL; +} + +void* uv__reallocf(void* ptr, size_t size) { + void* newptr; + + newptr = uv__realloc(ptr, size); + if (newptr == NULL) + if (size > 0) + uv__free(ptr); + + return newptr; } int uv_replace_allocator(uv_malloc_func malloc_func, @@ -155,6 +172,18 @@ static const char* uv__unknown_err_code(int err) { return copy != NULL ? copy : "Unknown system error"; } +#define UV_ERR_NAME_GEN_R(name, _) \ +case UV_## name: \ + uv__strscpy(buf, #name, buflen); break; +char* uv_err_name_r(int err, char* buf, size_t buflen) { + switch (err) { + UV_ERRNO_MAP(UV_ERR_NAME_GEN_R) + default: snprintf(buf, buflen, "Unknown system error %d", err); + } + return buf; +} +#undef UV_ERR_NAME_GEN_R + #define UV_ERR_NAME_GEN(name, _) case UV_ ## name: return #name; const char* uv_err_name(int err) { @@ -166,6 +195,19 @@ const char* uv_err_name(int err) { #undef UV_ERR_NAME_GEN +#define UV_STRERROR_GEN_R(name, msg) \ +case UV_ ## name: \ + snprintf(buf, buflen, "%s", msg); break; +char* uv_strerror_r(int err, char* buf, size_t buflen) { + switch (err) { + UV_ERRNO_MAP(UV_STRERROR_GEN_R) + default: snprintf(buf, buflen, "Unknown system error %d", err); + } + return buf; +} +#undef UV_STRERROR_GEN_R + + #define UV_STRERROR_GEN(name, msg) case UV_ ## name: return msg; const char* uv_strerror(int err) { switch (err) { @@ -180,6 +222,9 @@ int uv_ip4_addr(const char* ip, int port, struct sockaddr_in* addr) { memset(addr, 0, sizeof(*addr)); addr->sin_family = AF_INET; addr->sin_port = htons(port); +#ifdef SIN6_LEN + addr->sin_len = sizeof(*addr); +#endif return uv_inet_pton(AF_INET, ip, &(addr->sin_addr.s_addr)); } @@ -192,6 +237,9 @@ int uv_ip6_addr(const char* ip, int port, struct sockaddr_in6* addr) { memset(addr, 0, sizeof(*addr)); addr->sin6_family = AF_INET6; addr->sin6_port = htons(port); +#ifdef SIN6_LEN + addr->sin6_len = sizeof(*addr); +#endif zone_index = strchr(ip, '%'); if (zone_index != NULL) { @@ -245,6 +293,36 @@ int uv_tcp_bind(uv_tcp_t* handle, } +int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned flags) { + unsigned extra_flags; + int domain; + int rc; + + /* Use the lower 8 bits for the domain. */ + domain = flags & 0xFF; + if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC) + return UV_EINVAL; + + /* Use the higher bits for extra flags. */ + extra_flags = flags & ~0xFF; + if (extra_flags & ~UV_UDP_RECVMMSG) + return UV_EINVAL; + + rc = uv__udp_init_ex(loop, handle, flags, domain); + + if (rc == 0) + if (extra_flags & UV_UDP_RECVMMSG) + handle->flags |= UV_HANDLE_UDP_RECVMMSG; + + return rc; +} + + +int uv_udp_init(uv_loop_t* loop, uv_udp_t* handle) { + return uv_udp_init_ex(loop, handle, AF_UNSPEC); +} + + int uv_udp_bind(uv_udp_t* handle, const struct sockaddr* addr, unsigned int flags) { @@ -284,17 +362,20 @@ int uv_tcp_connect(uv_connect_t* req, } -int uv_udp_send(uv_udp_send_t* req, - uv_udp_t* handle, - const uv_buf_t bufs[], - unsigned int nbufs, - const struct sockaddr* addr, - uv_udp_send_cb send_cb) { +int uv_udp_connect(uv_udp_t* handle, const struct sockaddr* addr) { unsigned int addrlen; if (handle->type != UV_UDP) return UV_EINVAL; + /* Disconnect the handle */ + if (addr == NULL) { + if (!(handle->flags & UV_HANDLE_UDP_CONNECTED)) + return UV_ENOTCONN; + + return uv__udp_disconnect(handle); + } + if (addr->sa_family == AF_INET) addrlen = sizeof(struct sockaddr_in); else if (addr->sa_family == AF_INET6) @@ -302,6 +383,70 @@ int uv_udp_send(uv_udp_send_t* req, else return UV_EINVAL; + if (handle->flags & UV_HANDLE_UDP_CONNECTED) + return UV_EISCONN; + + return uv__udp_connect(handle, addr, addrlen); +} + + +int uv__udp_is_connected(uv_udp_t* handle) { + struct sockaddr_storage addr; + int addrlen; + if (handle->type != UV_UDP) + return 0; + + addrlen = sizeof(addr); + if (uv_udp_getpeername(handle, (struct sockaddr*) &addr, &addrlen) != 0) + return 0; + + return addrlen > 0; +} + + +int uv__udp_check_before_send(uv_udp_t* handle, const struct sockaddr* addr) { + unsigned int addrlen; + + if (handle->type != UV_UDP) + return UV_EINVAL; + + if (addr != NULL && (handle->flags & UV_HANDLE_UDP_CONNECTED)) + return UV_EISCONN; + + if (addr == NULL && !(handle->flags & UV_HANDLE_UDP_CONNECTED)) + return UV_EDESTADDRREQ; + + if (addr != NULL) { + if (addr->sa_family == AF_INET) + addrlen = sizeof(struct sockaddr_in); + else if (addr->sa_family == AF_INET6) + addrlen = sizeof(struct sockaddr_in6); +#if defined(AF_UNIX) && !defined(_WIN32) + else if (addr->sa_family == AF_UNIX) + addrlen = sizeof(struct sockaddr_un); +#endif + else + return UV_EINVAL; + } else { + addrlen = 0; + } + + return addrlen; +} + + +int uv_udp_send(uv_udp_send_t* req, + uv_udp_t* handle, + const uv_buf_t bufs[], + unsigned int nbufs, + const struct sockaddr* addr, + uv_udp_send_cb send_cb) { + int addrlen; + + addrlen = uv__udp_check_before_send(handle, addr); + if (addrlen < 0) + return addrlen; + return uv__udp_send(req, handle, bufs, nbufs, addr, addrlen, send_cb); } @@ -310,17 +455,11 @@ int uv_udp_try_send(uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr) { - unsigned int addrlen; + int addrlen; - if (handle->type != UV_UDP) - return UV_EINVAL; - - if (addr->sa_family == AF_INET) - addrlen = sizeof(struct sockaddr_in); - else if (addr->sa_family == AF_INET6) - addrlen = sizeof(struct sockaddr_in6); - else - return UV_EINVAL; + addrlen = uv__udp_check_before_send(handle, addr); + if (addrlen < 0) + return addrlen; return uv__udp_try_send(handle, bufs, nbufs, addr, addrlen); } @@ -357,7 +496,7 @@ void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg) { QUEUE_REMOVE(q); QUEUE_INSERT_TAIL(&loop->handle_queue, q); - if (h->flags & UV__HANDLE_INTERNAL) continue; + if (h->flags & UV_HANDLE_INTERNAL) continue; walk_cb(h, arg); } } @@ -386,9 +525,9 @@ static void uv__print_handles(uv_loop_t* loop, int only_active, FILE* stream) { fprintf(stream, "[%c%c%c] %-8s %p\n", - "R-"[!(h->flags & UV__HANDLE_REF)], - "A-"[!(h->flags & UV__HANDLE_ACTIVE)], - "I-"[!(h->flags & UV__HANDLE_INTERNAL)], + "R-"[!(h->flags & UV_HANDLE_REF)], + "A-"[!(h->flags & UV_HANDLE_ACTIVE)], + "I-"[!(h->flags & UV_HANDLE_INTERNAL)], type, (void*)h); } @@ -512,8 +651,18 @@ void uv__fs_scandir_cleanup(uv_fs_t* req) { int uv_fs_scandir_next(uv_fs_t* req, uv_dirent_t* ent) { uv__dirent_t** dents; uv__dirent_t* dent; + unsigned int* nbufs; - unsigned int* nbufs = uv__get_nbufs(req); + /* Check to see if req passed */ + if (req->result < 0) + return req->result; + + /* Ptr will be null if req was canceled or no files found */ + if (!req->ptr) + return UV_EOF; + + nbufs = uv__get_nbufs(req); + assert(nbufs); dents = req->ptr; @@ -531,37 +680,66 @@ int uv_fs_scandir_next(uv_fs_t* req, uv_dirent_t* ent) { dent = dents[(*nbufs)++]; ent->name = dent->d_name; + ent->type = uv__fs_get_dirent_type(dent); + + return 0; +} + +uv_dirent_type_t uv__fs_get_dirent_type(uv__dirent_t* dent) { + uv_dirent_type_t type; + #ifdef HAVE_DIRENT_TYPES switch (dent->d_type) { case UV__DT_DIR: - ent->type = UV_DIRENT_DIR; + type = UV_DIRENT_DIR; break; case UV__DT_FILE: - ent->type = UV_DIRENT_FILE; + type = UV_DIRENT_FILE; break; case UV__DT_LINK: - ent->type = UV_DIRENT_LINK; + type = UV_DIRENT_LINK; break; case UV__DT_FIFO: - ent->type = UV_DIRENT_FIFO; + type = UV_DIRENT_FIFO; break; case UV__DT_SOCKET: - ent->type = UV_DIRENT_SOCKET; + type = UV_DIRENT_SOCKET; break; case UV__DT_CHAR: - ent->type = UV_DIRENT_CHAR; + type = UV_DIRENT_CHAR; break; case UV__DT_BLOCK: - ent->type = UV_DIRENT_BLOCK; + type = UV_DIRENT_BLOCK; break; default: - ent->type = UV_DIRENT_UNKNOWN; + type = UV_DIRENT_UNKNOWN; } #else - ent->type = UV_DIRENT_UNKNOWN; + type = UV_DIRENT_UNKNOWN; #endif - return 0; + return type; +} + +void uv__fs_readdir_cleanup(uv_fs_t* req) { + uv_dir_t* dir; + uv_dirent_t* dirents; + int i; + + if (req->ptr == NULL) + return; + + dir = req->ptr; + dirents = dir->dirents; + req->ptr = NULL; + + if (dirents == NULL) + return; + + for (i = 0; i < req->result; ++i) { + uv__free((char*) dirents[i].name); + dirents[i].name = NULL; + } } @@ -613,20 +791,25 @@ uv_loop_t* uv_loop_new(void) { int uv_loop_close(uv_loop_t* loop) { QUEUE* q; uv_handle_t* h; +#ifndef NDEBUG + void* saved_data; +#endif - if (!QUEUE_EMPTY(&(loop)->active_reqs)) + if (uv__has_active_reqs(loop)) return UV_EBUSY; QUEUE_FOREACH(q, &loop->handle_queue) { h = QUEUE_DATA(q, uv_handle_t, handle_queue); - if (!(h->flags & UV__HANDLE_INTERNAL)) + if (!(h->flags & UV_HANDLE_INTERNAL)) return UV_EBUSY; } uv__loop_close(loop); #ifndef NDEBUG + saved_data = loop->data; memset(loop, -1, sizeof(*loop)); + loop->data = saved_data; #endif if (loop == default_loop_ptr) default_loop_ptr = NULL; @@ -647,3 +830,99 @@ void uv_loop_delete(uv_loop_t* loop) { if (loop != default_loop) uv__free(loop); } + + +void uv_os_free_environ(uv_env_item_t* envitems, int count) { + int i; + + for (i = 0; i < count; i++) { + uv__free(envitems[i].name); + } + + uv__free(envitems); +} + + +void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { + int i; + + for (i = 0; i < count; i++) + uv__free(cpu_infos[i].model); + + uv__free(cpu_infos); +} + + +#ifdef __GNUC__ /* Also covers __clang__ and __INTEL_COMPILER. */ +__attribute__((destructor)) +#endif +void uv_library_shutdown(void) { + static int was_shutdown; + + if (uv__load_relaxed(&was_shutdown)) + return; + + uv__process_title_cleanup(); + uv__signal_cleanup(); + uv__threadpool_cleanup(); + uv__store_relaxed(&was_shutdown, 1); +} + + +void uv__metrics_update_idle_time(uv_loop_t* loop) { + uv__loop_metrics_t* loop_metrics; + uint64_t entry_time; + uint64_t exit_time; + + if (!(uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME)) + return; + + loop_metrics = uv__get_loop_metrics(loop); + + /* The thread running uv__metrics_update_idle_time() is always the same + * thread that sets provider_entry_time. So it's unnecessary to lock before + * retrieving this value. + */ + if (loop_metrics->provider_entry_time == 0) + return; + + exit_time = uv_hrtime(); + + uv_mutex_lock(&loop_metrics->lock); + entry_time = loop_metrics->provider_entry_time; + loop_metrics->provider_entry_time = 0; + loop_metrics->provider_idle_time += exit_time - entry_time; + uv_mutex_unlock(&loop_metrics->lock); +} + + +void uv__metrics_set_provider_entry_time(uv_loop_t* loop) { + uv__loop_metrics_t* loop_metrics; + uint64_t now; + + if (!(uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME)) + return; + + now = uv_hrtime(); + loop_metrics = uv__get_loop_metrics(loop); + uv_mutex_lock(&loop_metrics->lock); + loop_metrics->provider_entry_time = now; + uv_mutex_unlock(&loop_metrics->lock); +} + + +uint64_t uv_metrics_idle_time(uv_loop_t* loop) { + uv__loop_metrics_t* loop_metrics; + uint64_t entry_time; + uint64_t idle_time; + + loop_metrics = uv__get_loop_metrics(loop); + uv_mutex_lock(&loop_metrics->lock); + idle_time = loop_metrics->provider_idle_time; + entry_time = loop_metrics->provider_entry_time; + uv_mutex_unlock(&loop_metrics->lock); + + if (entry_time > 0) + idle_time += uv_hrtime() - entry_time; + return idle_time; +} diff --git a/include/libuv/src/uv-common.h b/include/libuv/src/uv-common.h index 27902fdf8..e851291cc 100644 --- a/include/libuv/src/uv-common.h +++ b/include/libuv/src/uv-common.h @@ -32,14 +32,21 @@ #include #if defined(_MSC_VER) && _MSC_VER < 1600 -# include "stdint-msvc2008.h" +# include "uv/stdint-msvc2008.h" #else # include #endif #include "uv.h" -#include "tree.h" +#include "uv/tree.h" #include "queue.h" +#include "strscpy.h" + +#if EDOM > 0 +# define UV__ERR(x) (-(x)) +#else +# define UV__ERR(x) (x) +#endif #if !defined(snprintf) && defined(_MSC_VER) && _MSC_VER < 1900 extern int snprintf(char*, size_t, const char*, ...); @@ -53,20 +60,78 @@ extern int snprintf(char*, size_t, const char*, ...); #define STATIC_ASSERT(expr) \ void uv__static_assert(int static_assert_failed[1 - 2 * !(expr)]) -#ifndef _WIN32 -enum { - UV__HANDLE_INTERNAL = 0x8000, - UV__HANDLE_ACTIVE = 0x4000, - UV__HANDLE_REF = 0x2000, - UV__HANDLE_CLOSING = 0 /* no-op on unix */ -}; +#if defined(__GNUC__) && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 7) +#define uv__load_relaxed(p) __atomic_load_n(p, __ATOMIC_RELAXED) +#define uv__store_relaxed(p, v) __atomic_store_n(p, v, __ATOMIC_RELAXED) #else -# define UV__HANDLE_INTERNAL 0x80 -# define UV__HANDLE_ACTIVE 0x40 -# define UV__HANDLE_REF 0x20 -# define UV__HANDLE_CLOSING 0x01 +#define uv__load_relaxed(p) (*p) +#define uv__store_relaxed(p, v) do *p = v; while (0) #endif +/* Handle flags. Some flags are specific to Windows or UNIX. */ +enum { + /* Used by all handles. */ + UV_HANDLE_CLOSING = 0x00000001, + UV_HANDLE_CLOSED = 0x00000002, + UV_HANDLE_ACTIVE = 0x00000004, + UV_HANDLE_REF = 0x00000008, + UV_HANDLE_INTERNAL = 0x00000010, + UV_HANDLE_ENDGAME_QUEUED = 0x00000020, + + /* Used by streams. */ + UV_HANDLE_LISTENING = 0x00000040, + UV_HANDLE_CONNECTION = 0x00000080, + UV_HANDLE_SHUTTING = 0x00000100, + UV_HANDLE_SHUT = 0x00000200, + UV_HANDLE_READ_PARTIAL = 0x00000400, + UV_HANDLE_READ_EOF = 0x00000800, + + /* Used by streams and UDP handles. */ + UV_HANDLE_READING = 0x00001000, + UV_HANDLE_BOUND = 0x00002000, + UV_HANDLE_READABLE = 0x00004000, + UV_HANDLE_WRITABLE = 0x00008000, + UV_HANDLE_READ_PENDING = 0x00010000, + UV_HANDLE_SYNC_BYPASS_IOCP = 0x00020000, + UV_HANDLE_ZERO_READ = 0x00040000, + UV_HANDLE_EMULATE_IOCP = 0x00080000, + UV_HANDLE_BLOCKING_WRITES = 0x00100000, + UV_HANDLE_CANCELLATION_PENDING = 0x00200000, + + /* Used by uv_tcp_t and uv_udp_t handles */ + UV_HANDLE_IPV6 = 0x00400000, + + /* Only used by uv_tcp_t handles. */ + UV_HANDLE_TCP_NODELAY = 0x01000000, + UV_HANDLE_TCP_KEEPALIVE = 0x02000000, + UV_HANDLE_TCP_SINGLE_ACCEPT = 0x04000000, + UV_HANDLE_TCP_ACCEPT_STATE_CHANGING = 0x08000000, + UV_HANDLE_TCP_SOCKET_CLOSED = 0x10000000, + UV_HANDLE_SHARED_TCP_SOCKET = 0x20000000, + + /* Only used by uv_udp_t handles. */ + UV_HANDLE_UDP_PROCESSING = 0x01000000, + UV_HANDLE_UDP_CONNECTED = 0x02000000, + UV_HANDLE_UDP_RECVMMSG = 0x04000000, + + /* Only used by uv_pipe_t handles. */ + UV_HANDLE_NON_OVERLAPPED_PIPE = 0x01000000, + UV_HANDLE_PIPESERVER = 0x02000000, + + /* Only used by uv_tty_t handles. */ + UV_HANDLE_TTY_READABLE = 0x01000000, + UV_HANDLE_TTY_RAW = 0x02000000, + UV_HANDLE_TTY_SAVED_POSITION = 0x04000000, + UV_HANDLE_TTY_SAVED_ATTRIBUTES = 0x08000000, + + /* Only used by uv_signal_t handles. */ + UV_SIGNAL_ONE_SHOT_DISPATCHED = 0x01000000, + UV_SIGNAL_ONE_SHOT = 0x02000000, + + /* Only used by uv_poll_t handles. */ + UV_HANDLE_POLL_SLOW = 0x01000000 +}; + int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap); void uv__loop_close(uv_loop_t* loop); @@ -82,11 +147,24 @@ int uv__tcp_connect(uv_connect_t* req, unsigned int addrlen, uv_connect_cb cb); +int uv__udp_init_ex(uv_loop_t* loop, + uv_udp_t* handle, + unsigned flags, + int domain); + int uv__udp_bind(uv_udp_t* handle, const struct sockaddr* addr, unsigned int addrlen, unsigned int flags); +int uv__udp_connect(uv_udp_t* handle, + const struct sockaddr* addr, + unsigned int addrlen); + +int uv__udp_disconnect(uv_udp_t* handle); + +int uv__udp_is_connected(uv_udp_t* handle); + int uv__udp_send(uv_udp_send_t* req, uv_udp_t* handle, const uv_buf_t bufs[], @@ -110,8 +188,15 @@ void uv__fs_poll_close(uv_fs_poll_t* handle); int uv__getaddrinfo_translate_error(int sys_err); /* EAI_* error. */ +enum uv__work_kind { + UV__WORK_CPU, + UV__WORK_FAST_IO, + UV__WORK_SLOW_IO +}; + void uv__work_submit(uv_loop_t* loop, struct uv__work *w, + enum uv__work_kind kind, void (*work)(struct uv__work *w), void (*done)(struct uv__work *w, int status)); @@ -122,20 +207,30 @@ size_t uv__count_bufs(const uv_buf_t bufs[], unsigned int nbufs); int uv__socket_sockopt(uv_handle_t* handle, int optname, int* value); void uv__fs_scandir_cleanup(uv_fs_t* req); +void uv__fs_readdir_cleanup(uv_fs_t* req); +uv_dirent_type_t uv__fs_get_dirent_type(uv__dirent_t* dent); + +int uv__next_timeout(const uv_loop_t* loop); +void uv__run_timers(uv_loop_t* loop); +void uv__timer_close(uv_timer_t* handle); + +void uv__process_title_cleanup(void); +void uv__signal_cleanup(void); +void uv__threadpool_cleanup(void); #define uv__has_active_reqs(loop) \ - (QUEUE_EMPTY(&(loop)->active_reqs) == 0) + ((loop)->active_reqs.count > 0) #define uv__req_register(loop, req) \ do { \ - QUEUE_INSERT_TAIL(&(loop)->active_reqs, &(req)->active_queue); \ + (loop)->active_reqs.count++; \ } \ while (0) #define uv__req_unregister(loop, req) \ do { \ assert(uv__has_active_reqs(loop)); \ - QUEUE_REMOVE(&(req)->active_queue); \ + (loop)->active_reqs.count--; \ } \ while (0) @@ -155,49 +250,47 @@ void uv__fs_scandir_cleanup(uv_fs_t* req); while (0) #define uv__is_active(h) \ - (((h)->flags & UV__HANDLE_ACTIVE) != 0) + (((h)->flags & UV_HANDLE_ACTIVE) != 0) #define uv__is_closing(h) \ - (((h)->flags & (UV_CLOSING | UV_CLOSED)) != 0) + (((h)->flags & (UV_HANDLE_CLOSING | UV_HANDLE_CLOSED)) != 0) #define uv__handle_start(h) \ do { \ - assert(((h)->flags & UV__HANDLE_CLOSING) == 0); \ - if (((h)->flags & UV__HANDLE_ACTIVE) != 0) break; \ - (h)->flags |= UV__HANDLE_ACTIVE; \ - if (((h)->flags & UV__HANDLE_REF) != 0) uv__active_handle_add(h); \ + if (((h)->flags & UV_HANDLE_ACTIVE) != 0) break; \ + (h)->flags |= UV_HANDLE_ACTIVE; \ + if (((h)->flags & UV_HANDLE_REF) != 0) uv__active_handle_add(h); \ } \ while (0) #define uv__handle_stop(h) \ do { \ - assert(((h)->flags & UV__HANDLE_CLOSING) == 0); \ - if (((h)->flags & UV__HANDLE_ACTIVE) == 0) break; \ - (h)->flags &= ~UV__HANDLE_ACTIVE; \ - if (((h)->flags & UV__HANDLE_REF) != 0) uv__active_handle_rm(h); \ + if (((h)->flags & UV_HANDLE_ACTIVE) == 0) break; \ + (h)->flags &= ~UV_HANDLE_ACTIVE; \ + if (((h)->flags & UV_HANDLE_REF) != 0) uv__active_handle_rm(h); \ } \ while (0) #define uv__handle_ref(h) \ do { \ - if (((h)->flags & UV__HANDLE_REF) != 0) break; \ - (h)->flags |= UV__HANDLE_REF; \ - if (((h)->flags & UV__HANDLE_CLOSING) != 0) break; \ - if (((h)->flags & UV__HANDLE_ACTIVE) != 0) uv__active_handle_add(h); \ + if (((h)->flags & UV_HANDLE_REF) != 0) break; \ + (h)->flags |= UV_HANDLE_REF; \ + if (((h)->flags & UV_HANDLE_CLOSING) != 0) break; \ + if (((h)->flags & UV_HANDLE_ACTIVE) != 0) uv__active_handle_add(h); \ } \ while (0) #define uv__handle_unref(h) \ do { \ - if (((h)->flags & UV__HANDLE_REF) == 0) break; \ - (h)->flags &= ~UV__HANDLE_REF; \ - if (((h)->flags & UV__HANDLE_CLOSING) != 0) break; \ - if (((h)->flags & UV__HANDLE_ACTIVE) != 0) uv__active_handle_rm(h); \ + if (((h)->flags & UV_HANDLE_REF) == 0) break; \ + (h)->flags &= ~UV_HANDLE_REF; \ + if (((h)->flags & UV_HANDLE_CLOSING) != 0) break; \ + if (((h)->flags & UV_HANDLE_ACTIVE) != 0) uv__active_handle_rm(h); \ } \ while (0) #define uv__has_ref(h) \ - (((h)->flags & UV__HANDLE_REF) != 0) + (((h)->flags & UV_HANDLE_REF) != 0) #if defined(_WIN32) # define uv__handle_platform_init(h) ((h)->u.fd = -1) @@ -209,12 +302,42 @@ void uv__fs_scandir_cleanup(uv_fs_t* req); do { \ (h)->loop = (loop_); \ (h)->type = (type_); \ - (h)->flags = UV__HANDLE_REF; /* Ref the loop when active. */ \ + (h)->flags = UV_HANDLE_REF; /* Ref the loop when active. */ \ QUEUE_INSERT_TAIL(&(loop_)->handle_queue, &(h)->handle_queue); \ uv__handle_platform_init(h); \ } \ while (0) +/* Note: uses an open-coded version of SET_REQ_SUCCESS() because of + * a circular dependency between src/uv-common.h and src/win/internal.h. + */ +#if defined(_WIN32) +# define UV_REQ_INIT(req, typ) \ + do { \ + (req)->type = (typ); \ + (req)->u.io.overlapped.Internal = 0; /* SET_REQ_SUCCESS() */ \ + } \ + while (0) +#else +# define UV_REQ_INIT(req, typ) \ + do { \ + (req)->type = (typ); \ + } \ + while (0) +#endif + +#define uv__req_init(loop, req, typ) \ + do { \ + UV_REQ_INIT(req, typ); \ + uv__req_register(loop, req); \ + } \ + while (0) + +#define uv__get_internal_fields(loop) \ + ((uv__loop_internal_fields_t*) loop->internal_fields) + +#define uv__get_loop_metrics(loop) \ + (&uv__get_internal_fields(loop)->loop_metrics) /* Allocator prototypes */ void *uv__calloc(size_t count, size_t size); @@ -223,5 +346,23 @@ char *uv__strndup(const char* s, size_t n); void* uv__malloc(size_t size); void uv__free(void* ptr); void* uv__realloc(void* ptr, size_t size); +void* uv__reallocf(void* ptr, size_t size); + +typedef struct uv__loop_metrics_s uv__loop_metrics_t; +typedef struct uv__loop_internal_fields_s uv__loop_internal_fields_t; + +struct uv__loop_metrics_s { + uint64_t provider_entry_time; + uint64_t provider_idle_time; + uv_mutex_t lock; +}; + +void uv__metrics_update_idle_time(uv_loop_t* loop); +void uv__metrics_set_provider_entry_time(uv_loop_t* loop); + +struct uv__loop_internal_fields_s { + unsigned int flags; + uv__loop_metrics_t loop_metrics; +}; #endif /* UV_COMMON_H_ */ diff --git a/include/libuv/src/uv-data-getter-setters.c b/include/libuv/src/uv-data-getter-setters.c new file mode 100644 index 000000000..0bd044861 --- /dev/null +++ b/include/libuv/src/uv-data-getter-setters.c @@ -0,0 +1,119 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#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: + default: /* UV_REQ_TYPE_PRIVATE */ + break; + } + 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/include/libuv/src/win/async.c b/include/libuv/src/win/async.c index ad240ab89..d787f6604 100644 --- a/include/libuv/src/win/async.c +++ b/include/libuv/src/win/async.c @@ -29,7 +29,7 @@ void uv_async_endgame(uv_loop_t* loop, uv_async_t* handle) { - if (handle->flags & UV__HANDLE_CLOSING && + if (handle->flags & UV_HANDLE_CLOSING && !handle->async_sent) { assert(!(handle->flags & UV_HANDLE_CLOSED)); uv__handle_close(handle); @@ -45,8 +45,7 @@ int uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) { handle->async_cb = async_cb; req = &handle->async_req; - uv_req_init(loop, req); - req->type = UV_WAKEUP; + UV_REQ_INIT(req, UV_WAKEUP); req->data = handle; uv__handle_start(handle); @@ -72,9 +71,9 @@ int uv_async_send(uv_async_t* handle) { return -1; } - /* The user should make sure never to call uv_async_send to a closing */ - /* or closed handle. */ - assert(!(handle->flags & UV__HANDLE_CLOSING)); + /* The user should make sure never to call uv_async_send to a closing or + * closed handle. */ + assert(!(handle->flags & UV_HANDLE_CLOSING)); if (!uv__atomic_exchange_set(&handle->async_sent)) { POST_COMPLETION_FOR_REQ(loop, &handle->async_req); @@ -91,7 +90,7 @@ void uv_process_async_wakeup_req(uv_loop_t* loop, uv_async_t* handle, handle->async_sent = 0; - if (handle->flags & UV__HANDLE_CLOSING) { + if (handle->flags & UV_HANDLE_CLOSING) { uv_want_endgame(loop, (uv_handle_t*)handle); } else if (handle->async_cb != NULL) { handle->async_cb(handle); diff --git a/include/libuv/src/win/atomicops-inl.h b/include/libuv/src/win/atomicops-inl.h index 61e006026..52713cf30 100644 --- a/include/libuv/src/win/atomicops-inl.h +++ b/include/libuv/src/win/atomicops-inl.h @@ -23,18 +23,19 @@ #define UV_WIN_ATOMICOPS_INL_H_ #include "uv.h" +#include "internal.h" /* Atomic set operation on char */ #ifdef _MSC_VER /* MSVC */ -/* _InterlockedOr8 is supported by MSVC on x32 and x64. It is slightly less */ -/* efficient than InterlockedExchange, but InterlockedExchange8 does not */ -/* exist, and interlocked operations on larger targets might require the */ -/* target to be aligned. */ +/* _InterlockedOr8 is supported by MSVC on x32 and x64. It is slightly less + * efficient than InterlockedExchange, but InterlockedExchange8 does not exist, + * and interlocked operations on larger targets might require the target to be + * aligned. */ #pragma intrinsic(_InterlockedOr8) -static char __declspec(inline) uv__atomic_exchange_set(char volatile* target) { +static char INLINE uv__atomic_exchange_set(char volatile* target) { return _InterlockedOr8(target, 1); } diff --git a/include/libuv/src/win/core.c b/include/libuv/src/win/core.c index ba306ebc0..e53a0f8e2 100644 --- a/include/libuv/src/win/core.c +++ b/include/libuv/src/win/core.c @@ -31,13 +31,11 @@ #include "uv.h" #include "internal.h" +#include "queue.h" #include "handle-inl.h" +#include "heap-inl.h" #include "req-inl.h" - -static uv_loop_t default_loop_struct; -static uv_loop_t* default_loop_ptr; - /* uv_once initialization guards */ static uv_once_t uv_init_guard_ = UV_ONCE_INIT; @@ -80,6 +78,100 @@ static void uv__crt_invalid_parameter_handler(const wchar_t* expression, } #endif +static uv_loop_t** uv__loops; +static int uv__loops_size; +static int uv__loops_capacity; +#define UV__LOOPS_CHUNK_SIZE 8 +static uv_mutex_t uv__loops_lock; + +static void uv__loops_init(void) { + uv_mutex_init(&uv__loops_lock); +} + +static int uv__loops_add(uv_loop_t* loop) { + uv_loop_t** new_loops; + int new_capacity, i; + + uv_mutex_lock(&uv__loops_lock); + + if (uv__loops_size == uv__loops_capacity) { + new_capacity = uv__loops_capacity + UV__LOOPS_CHUNK_SIZE; + new_loops = uv__realloc(uv__loops, sizeof(uv_loop_t*) * new_capacity); + if (!new_loops) + goto failed_loops_realloc; + uv__loops = new_loops; + for (i = uv__loops_capacity; i < new_capacity; ++i) + uv__loops[i] = NULL; + uv__loops_capacity = new_capacity; + } + uv__loops[uv__loops_size] = loop; + ++uv__loops_size; + + uv_mutex_unlock(&uv__loops_lock); + return 0; + +failed_loops_realloc: + uv_mutex_unlock(&uv__loops_lock); + return ERROR_OUTOFMEMORY; +} + +static void uv__loops_remove(uv_loop_t* loop) { + int loop_index; + int smaller_capacity; + uv_loop_t** new_loops; + + uv_mutex_lock(&uv__loops_lock); + + for (loop_index = 0; loop_index < uv__loops_size; ++loop_index) { + if (uv__loops[loop_index] == loop) + break; + } + /* If loop was not found, ignore */ + if (loop_index == uv__loops_size) + goto loop_removed; + + uv__loops[loop_index] = uv__loops[uv__loops_size - 1]; + uv__loops[uv__loops_size - 1] = NULL; + --uv__loops_size; + + if (uv__loops_size == 0) { + uv__loops_capacity = 0; + uv__free(uv__loops); + uv__loops = NULL; + goto loop_removed; + } + + /* If we didn't grow to big skip downsizing */ + if (uv__loops_capacity < 4 * UV__LOOPS_CHUNK_SIZE) + goto loop_removed; + + /* Downsize only if more than half of buffer is free */ + smaller_capacity = uv__loops_capacity / 2; + if (uv__loops_size >= smaller_capacity) + goto loop_removed; + new_loops = uv__realloc(uv__loops, sizeof(uv_loop_t*) * smaller_capacity); + if (!new_loops) + goto loop_removed; + uv__loops = new_loops; + uv__loops_capacity = smaller_capacity; + +loop_removed: + uv_mutex_unlock(&uv__loops_lock); +} + +void uv__wake_all_loops(void) { + int i; + uv_loop_t* loop; + + uv_mutex_lock(&uv__loops_lock); + for (i = 0; i < uv__loops_size; ++i) { + loop = uv__loops[i]; + assert(loop); + if (loop->iocp != INVALID_HANDLE_VALUE) + PostQueuedCompletionStatus(loop->iocp, 0, 0, NULL); + } + uv_mutex_unlock(&uv__loops_lock); +} static void uv_init(void) { /* Tell Windows that we will handle critical errors. */ @@ -101,6 +193,9 @@ static void uv_init(void) { _CrtSetReportHook(uv__crt_dbg_report_handler); #endif + /* Initialize tracking of all uv loops */ + uv__loops_init(); + /* Fetch winapi function pointers. This must be done first because other * initialization code might need these function pointers to be loaded. */ @@ -120,10 +215,15 @@ static void uv_init(void) { /* Initialize utilities */ uv__util_init(); + + /* Initialize system wakeup detection */ + uv__init_detect_system_wakeup(); } int uv_loop_init(uv_loop_t* loop) { + uv__loop_internal_fields_t* lfields; + struct heap* timer_heap; int err; /* Initialize libuv itself first */ @@ -134,6 +234,15 @@ int uv_loop_init(uv_loop_t* loop) { if (loop->iocp == NULL) return uv_translate_sys_error(GetLastError()); + lfields = (uv__loop_internal_fields_t*) uv__calloc(1, sizeof(*lfields)); + if (lfields == NULL) + return UV_ENOMEM; + loop->internal_fields = lfields; + + err = uv_mutex_init(&lfields->loop_metrics.lock); + if (err) + goto fail_metrics_mutex_init; + /* To prevent uninitialized memory access, loop->time must be initialized * to zero before calling uv_update_time for the first time. */ @@ -142,14 +251,20 @@ int uv_loop_init(uv_loop_t* loop) { QUEUE_INIT(&loop->wq); QUEUE_INIT(&loop->handle_queue); - QUEUE_INIT(&loop->active_reqs); + loop->active_reqs.count = 0; loop->active_handles = 0; loop->pending_reqs_tail = NULL; loop->endgame_handles = NULL; - RB_INIT(&loop->timers); + loop->timer_heap = timer_heap = uv__malloc(sizeof(*timer_heap)); + if (timer_heap == NULL) { + err = UV_ENOMEM; + goto fail_timers_alloc; + } + + heap_init(timer_heap); loop->check_handles = NULL; loop->prepare_handles = NULL; @@ -176,7 +291,11 @@ int uv_loop_init(uv_loop_t* loop) { goto fail_async_init; uv__handle_unref(&loop->wq_async); - loop->wq_async.flags |= UV__HANDLE_INTERNAL; + loop->wq_async.flags |= UV_HANDLE_INTERNAL; + + err = uv__loops_add(loop); + if (err) + goto fail_async_init; return 0; @@ -184,6 +303,15 @@ int uv_loop_init(uv_loop_t* loop) { uv_mutex_destroy(&loop->wq_mutex); fail_mutex_init: + uv__free(timer_heap); + loop->timer_heap = NULL; + +fail_timers_alloc: + uv_mutex_destroy(&lfields->loop_metrics.lock); + +fail_metrics_mutex_init: + uv__free(lfields); + loop->internal_fields = NULL; CloseHandle(loop->iocp); loop->iocp = INVALID_HANDLE_VALUE; @@ -191,16 +319,31 @@ int uv_loop_init(uv_loop_t* loop) { } +void uv_update_time(uv_loop_t* loop) { + uint64_t new_time = uv__hrtime(1000); + assert(new_time >= loop->time); + loop->time = new_time; +} + + void uv__once_init(void) { uv_once(&uv_init_guard_, uv_init); } void uv__loop_close(uv_loop_t* loop) { + uv__loop_internal_fields_t* lfields; size_t i; - /* close the async handle without needing an extra loop iteration */ - assert(!loop->wq_async.async_sent); + uv__loops_remove(loop); + + /* Close the async handle without needing an extra loop iteration. + * We might have a pending message, but we're just going to destroy the IOCP + * soon, so we can just discard it now without the usual risk of a getting + * another notification from GetQueuedCompletionStatusEx after calling the + * close_cb (which we also skip defining). We'll assert later that queue was + * actually empty and all reqs handled. */ + loop->wq_async.async_sent = 0; loop->wq_async.close_cb = NULL; uv__handle_closing(&loop->wq_async); uv__handle_close(&loop->wq_async); @@ -217,11 +360,27 @@ void uv__loop_close(uv_loop_t* loop) { uv_mutex_unlock(&loop->wq_mutex); uv_mutex_destroy(&loop->wq_mutex); + uv__free(loop->timer_heap); + loop->timer_heap = NULL; + + lfields = uv__get_internal_fields(loop); + uv_mutex_destroy(&lfields->loop_metrics.lock); + uv__free(lfields); + loop->internal_fields = NULL; + CloseHandle(loop->iocp); } int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap) { + uv__loop_internal_fields_t* lfields; + + lfields = uv__get_internal_fields(loop); + if (option == UV_METRICS_IDLE_TIME) { + lfields->flags |= UV_METRICS_IDLE_TIME; + return 0; + } + return UV_ENOSYS; } @@ -231,6 +390,11 @@ int uv_backend_fd(const uv_loop_t* loop) { } +int uv_loop_fork(uv_loop_t* loop) { + return UV_ENOSYS; +} + + int uv_backend_timeout(const uv_loop_t* loop) { if (loop->stop_flag != 0) return 0; @@ -251,23 +415,51 @@ int uv_backend_timeout(const uv_loop_t* loop) { } -static void uv_poll(uv_loop_t* loop, DWORD timeout) { +static void uv__poll_wine(uv_loop_t* loop, DWORD timeout) { DWORD bytes; ULONG_PTR key; OVERLAPPED* overlapped; uv_req_t* req; int repeat; uint64_t timeout_time; + uint64_t user_timeout; + int reset_timeout; timeout_time = loop->time + timeout; + if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { + reset_timeout = 1; + user_timeout = timeout; + timeout = 0; + } else { + reset_timeout = 0; + } + for (repeat = 0; ; repeat++) { + /* Only need to set the provider_entry_time if timeout != 0. The function + * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. + */ + if (timeout != 0) + uv__metrics_set_provider_entry_time(loop); + GetQueuedCompletionStatus(loop->iocp, &bytes, &key, &overlapped, timeout); + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + + /* Placed here because on success the loop will break whether there is an + * empty package or not, or if GetQueuedCompletionStatus returned early then + * the timeout will be updated and the loop will run again. In either case + * the idle time will need to be updated. + */ + uv__metrics_update_idle_time(loop); + if (overlapped) { /* Package was dequeued */ req = uv_overlapped_to_req(overlapped); @@ -302,7 +494,7 @@ static void uv_poll(uv_loop_t* loop, DWORD timeout) { } -static void uv_poll_ex(uv_loop_t* loop, DWORD timeout) { +static void uv__poll(uv_loop_t* loop, DWORD timeout) { BOOL success; uv_req_t* req; OVERLAPPED_ENTRY overlappeds[128]; @@ -310,10 +502,26 @@ static void uv_poll_ex(uv_loop_t* loop, DWORD timeout) { ULONG i; int repeat; uint64_t timeout_time; + uint64_t user_timeout; + int reset_timeout; timeout_time = loop->time + timeout; + if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { + reset_timeout = 1; + user_timeout = timeout; + timeout = 0; + } else { + reset_timeout = 0; + } + for (repeat = 0; ; repeat++) { + /* Only need to set the provider_entry_time if timeout != 0. The function + * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. + */ + if (timeout != 0) + uv__metrics_set_provider_entry_time(loop); + success = pGetQueuedCompletionStatusEx(loop->iocp, overlappeds, ARRAY_SIZE(overlappeds), @@ -321,11 +529,27 @@ static void uv_poll_ex(uv_loop_t* loop, DWORD timeout) { timeout, FALSE); + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + + /* Placed here because on success the loop will break whether there is an + * empty package or not, or if GetQueuedCompletionStatus returned early then + * the timeout will be updated and the loop will run again. In either case + * the idle time will need to be updated. + */ + uv__metrics_update_idle_time(loop); + if (success) { for (i = 0; i < count; i++) { - /* Package was dequeued */ - req = uv_overlapped_to_req(overlappeds[i].lpOverlapped); - uv_insert_pending_req(loop, req); + /* Package was dequeued, but see if it is not a empty package + * meant only to wake us up. + */ + if (overlappeds[i].lpOverlapped) { + req = uv_overlapped_to_req(overlappeds[i].lpOverlapped); + uv_insert_pending_req(loop, req); + } } /* Some time might have passed waiting for I/O, @@ -358,8 +582,8 @@ static void uv_poll_ex(uv_loop_t* loop, DWORD timeout) { static int uv__loop_alive(const uv_loop_t* loop) { - return loop->active_handles > 0 || - !QUEUE_EMPTY(&loop->active_reqs) || + return uv__has_active_handles(loop) || + uv__has_active_reqs(loop) || loop->endgame_handles != NULL; } @@ -373,12 +597,6 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) { DWORD timeout; int r; int ran_pending; - void (*poll)(uv_loop_t* loop, DWORD timeout); - - if (pGetQueuedCompletionStatusEx) - poll = &uv_poll_ex; - else - poll = &uv_poll; r = uv__loop_alive(loop); if (!r) @@ -386,7 +604,7 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) { while (r != 0 && loop->stop_flag == 0) { uv_update_time(loop); - uv_process_timers(loop); + uv__run_timers(loop); ran_pending = uv_process_reqs(loop); uv_idle_invoke(loop); @@ -396,7 +614,17 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) { if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT) timeout = uv_backend_timeout(loop); - (*poll)(loop, timeout); + if (pGetQueuedCompletionStatusEx) + uv__poll(loop, timeout); + else + uv__poll_wine(loop, timeout); + + /* Run one final update on the provider_idle_time in case uv__poll* + * returned because the timeout expired, but no events were received. This + * call will be ignored if the provider_entry_time was either never set (if + * the timeout == 0) or was already updated b/c an event was received. + */ + uv__metrics_update_idle_time(loop); uv_check_invoke(loop); uv_process_endgames(loop); @@ -410,7 +638,7 @@ int uv_run(uv_loop_t *loop, uv_run_mode mode) { * UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from * the check. */ - uv_process_timers(loop); + uv__run_timers(loop); } r = uv__loop_alive(loop); @@ -491,3 +719,30 @@ int uv__socket_sockopt(uv_handle_t* handle, int optname, int* value) { return 0; } + +int uv_cpumask_size(void) { + return (int)(sizeof(DWORD_PTR) * 8); +} + +int uv__getsockpeername(const uv_handle_t* handle, + uv__peersockfunc func, + struct sockaddr* name, + int* namelen, + int delayed_error) { + + int result; + uv_os_fd_t fd; + + result = uv_fileno(handle, &fd); + if (result != 0) + return result; + + if (delayed_error) + return uv_translate_sys_error(delayed_error); + + result = func((SOCKET) fd, name, namelen); + if (result != 0) + return uv_translate_sys_error(WSAGetLastError()); + + return 0; +} diff --git a/include/libuv/src/win/detect-wakeup.c b/include/libuv/src/win/detect-wakeup.c new file mode 100644 index 000000000..ab1936157 --- /dev/null +++ b/include/libuv/src/win/detect-wakeup.c @@ -0,0 +1,56 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" +#include "winapi.h" + +static void uv__register_system_resume_callback(void); + +void uv__init_detect_system_wakeup(void) { + /* Try registering system power event callback. This is the cleanest + * method, but it will only work on Win8 and above. + */ + uv__register_system_resume_callback(); +} + +static ULONG CALLBACK uv__system_resume_callback(PVOID Context, + ULONG Type, + PVOID Setting) { + if (Type == PBT_APMRESUMESUSPEND || Type == PBT_APMRESUMEAUTOMATIC) + uv__wake_all_loops(); + + return 0; +} + +static void uv__register_system_resume_callback(void) { + _DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS recipient; + _HPOWERNOTIFY registration_handle; + + if (pPowerRegisterSuspendResumeNotification == NULL) + return; + + recipient.Callback = uv__system_resume_callback; + recipient.Context = NULL; + (*pPowerRegisterSuspendResumeNotification)(DEVICE_NOTIFY_CALLBACK, + &recipient, + ®istration_handle); +} diff --git a/include/libuv/src/win/dl.c b/include/libuv/src/win/dl.c index 39e400ab2..676be4dc7 100644 --- a/include/libuv/src/win/dl.c +++ b/include/libuv/src/win/dl.c @@ -22,7 +22,7 @@ #include "uv.h" #include "internal.h" -static int uv__dlerror(uv_lib_t* lib, int errorno); +static int uv__dlerror(uv_lib_t* lib, const char* filename, DWORD errorno); int uv_dlopen(const char* filename, uv_lib_t* lib) { @@ -37,12 +37,12 @@ int uv_dlopen(const char* filename, uv_lib_t* lib) { -1, filename_w, ARRAY_SIZE(filename_w))) { - return uv__dlerror(lib, GetLastError()); + return uv__dlerror(lib, filename, GetLastError()); } lib->handle = LoadLibraryExW(filename_w, NULL, LOAD_WITH_ALTERED_SEARCH_PATH); if (lib->handle == NULL) { - return uv__dlerror(lib, GetLastError()); + return uv__dlerror(lib, filename, GetLastError()); } return 0; @@ -64,8 +64,9 @@ void uv_dlclose(uv_lib_t* lib) { int uv_dlsym(uv_lib_t* lib, const char* name, void** ptr) { - *ptr = (void*) GetProcAddress(lib->handle, name); - return uv__dlerror(lib, *ptr ? 0 : GetLastError()); + /* Cast though integer to suppress pedantic warning about forbidden cast. */ + *ptr = (void*)(uintptr_t) GetProcAddress(lib->handle, name); + return uv__dlerror(lib, "", *ptr ? 0 : GetLastError()); } @@ -75,8 +76,9 @@ const char* uv_dlerror(const uv_lib_t* lib) { static void uv__format_fallback_error(uv_lib_t* lib, int errorno){ - DWORD_PTR args[1] = { (DWORD_PTR) errorno }; - LPSTR fallback_error = "error: %1!d!"; + static const CHAR fallback_error[] = "error: %1!d!"; + DWORD_PTR args[1]; + args[0] = (DWORD_PTR) errorno; FormatMessageA(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY | @@ -88,31 +90,47 @@ static void uv__format_fallback_error(uv_lib_t* lib, int errorno){ -static int uv__dlerror(uv_lib_t* lib, int errorno) { +static int uv__dlerror(uv_lib_t* lib, const char* filename, DWORD errorno) { + DWORD_PTR arg; DWORD res; + char* msg; if (lib->errmsg) { - LocalFree((void*)lib->errmsg); + LocalFree(lib->errmsg); lib->errmsg = NULL; } - if (errorno) { + if (errorno == 0) + return 0; + + res = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno, + MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + (LPSTR) &lib->errmsg, 0, NULL); + + if (!res && (GetLastError() == ERROR_MUI_FILE_NOT_FOUND || + GetLastError() == ERROR_RESOURCE_TYPE_NOT_FOUND)) { res = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno, - MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), - (LPSTR) &lib->errmsg, 0, NULL); - if (!res && GetLastError() == ERROR_MUI_FILE_NOT_FOUND) { - res = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorno, - 0, (LPSTR) &lib->errmsg, 0, NULL); - } - - if (!res) { - uv__format_fallback_error(lib, errorno); - } + 0, (LPSTR) &lib->errmsg, 0, NULL); + } + + if (res && errorno == ERROR_BAD_EXE_FORMAT && strstr(lib->errmsg, "%1")) { + msg = lib->errmsg; + lib->errmsg = NULL; + arg = (DWORD_PTR) filename; + res = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_ARGUMENT_ARRAY | + FORMAT_MESSAGE_FROM_STRING, + msg, + 0, 0, (LPSTR) &lib->errmsg, 0, (va_list*) &arg); + LocalFree(msg); } - return errorno ? -1 : 0; + if (!res) + uv__format_fallback_error(lib, errorno); + + return -1; } diff --git a/include/libuv/src/win/error.c b/include/libuv/src/win/error.c index c512f35af..3ec984c83 100644 --- a/include/libuv/src/win/error.c +++ b/include/libuv/src/win/error.c @@ -46,8 +46,8 @@ void uv_fatal_error(const int errorno, const char* syscall) { errmsg = "Unknown error"; } - /* FormatMessage messages include a newline character already, */ - /* so don't add another. */ + /* FormatMessage messages include a newline character already, so don't add + * another. */ if (syscall) { fprintf(stderr, "%s: (%d) %s", syscall, errorno, errmsg); } else { @@ -58,7 +58,7 @@ void uv_fatal_error(const int errorno, const char* syscall) { LocalFree(buf); } - *((char*)NULL) = 0xff; /* Force debug break */ + DebugBreak(); abort(); } @@ -71,6 +71,8 @@ int uv_translate_sys_error(int sys_errno) { switch (sys_errno) { case ERROR_NOACCESS: return UV_EACCES; case WSAEACCES: return UV_EACCES; + case ERROR_ELEVATION_REQUIRED: return UV_EACCES; + case ERROR_CANT_ACCESS_FILE: return UV_EACCES; case ERROR_ADDRESS_ALREADY_ASSOCIATED: return UV_EADDRINUSE; case WSAEADDRINUSE: return UV_EADDRINUSE; case WSAEADDRNOTAVAIL: return UV_EADDRNOTAVAIL; @@ -131,6 +133,7 @@ int uv_translate_sys_error(int sys_errno) { case WSAENOBUFS: return UV_ENOBUFS; case ERROR_BAD_PATHNAME: return UV_ENOENT; case ERROR_DIRECTORY: return UV_ENOENT; + case ERROR_ENVVAR_NOT_FOUND: return UV_ENOENT; case ERROR_FILE_NOT_FOUND: return UV_ENOENT; case ERROR_INVALID_NAME: return UV_ENOENT; case ERROR_INVALID_DRIVE: return UV_ENOENT; diff --git a/include/libuv/src/win/fs-event.c b/include/libuv/src/win/fs-event.c index e79a48d0e..0126c5ede 100644 --- a/include/libuv/src/win/fs-event.c +++ b/include/libuv/src/win/fs-event.c @@ -69,6 +69,7 @@ static void uv_relative_path(const WCHAR* filename, size_t relpathlen; size_t filenamelen = wcslen(filename); size_t dirlen = wcslen(dir); + assert(!_wcsnicmp(filename, dir, dirlen)); if (dirlen > 0 && dir[dirlen - 1] == '\\') dirlen--; relpathlen = filenamelen - dirlen - 1; @@ -81,18 +82,32 @@ static void uv_relative_path(const WCHAR* filename, static int uv_split_path(const WCHAR* filename, WCHAR** dir, WCHAR** file) { - int len = wcslen(filename); - int i = len; + size_t len, i; + DWORD dir_len; + + if (filename == NULL) { + if (dir != NULL) + *dir = NULL; + *file = NULL; + return 0; + } + + len = wcslen(filename); + i = len; while (i > 0 && filename[--i] != '\\' && filename[i] != '/'); if (i == 0) { if (dir) { - *dir = (WCHAR*)uv__malloc((MAX_PATH + 1) * sizeof(WCHAR)); + dir_len = GetCurrentDirectoryW(0, NULL); + if (dir_len == 0) { + return -1; + } + *dir = (WCHAR*)uv__malloc(dir_len * sizeof(WCHAR)); if (!*dir) { uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); } - if (!GetCurrentDirectoryW(MAX_PATH, *dir)) { + if (!GetCurrentDirectoryW(dir_len, *dir)) { uv__free(*dir); *dir = NULL; return -1; @@ -131,8 +146,7 @@ int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) { handle->short_filew = NULL; handle->dirw = NULL; - uv_req_init(loop, (uv_req_t*)&handle->req); - handle->req.type = UV_FS_EVENT_REQ; + UV_REQ_INIT(&handle->req, UV_FS_EVENT_REQ); handle->req.data = handle; return 0; @@ -143,11 +157,14 @@ int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb, const char* path, unsigned int flags) { - int name_size, is_path_dir; + int name_size, is_path_dir, size; DWORD attr, last_error; WCHAR* dir = NULL, *dir_to_watch, *pathw = NULL; - WCHAR short_path[MAX_PATH]; + DWORD short_path_buffer_len; + WCHAR *short_path_buffer; + WCHAR* short_path, *long_path; + short_path = NULL; if (uv__is_active(handle)) return UV_EINVAL; @@ -188,7 +205,30 @@ int uv_fs_event_start(uv_fs_event_t* handle, if (is_path_dir) { /* path is a directory, so that's the directory that we will watch. */ - handle->dirw = pathw; + + /* Convert to long path. */ + size = GetLongPathNameW(pathw, NULL, 0); + + if (size) { + long_path = (WCHAR*)uv__malloc(size * sizeof(WCHAR)); + if (!long_path) { + uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); + } + + size = GetLongPathNameW(pathw, long_path, size); + if (size) { + long_path[size] = '\0'; + } else { + uv__free(long_path); + long_path = NULL; + } + + if (long_path) { + uv__free(pathw); + pathw = long_path; + } + } + dir_to_watch = pathw; } else { /* @@ -197,10 +237,23 @@ int uv_fs_event_start(uv_fs_event_t* handle, */ /* Convert to short path. */ - if (!GetShortPathNameW(pathw, short_path, ARRAY_SIZE(short_path))) { - last_error = GetLastError(); - goto error; + short_path_buffer = NULL; + short_path_buffer_len = GetShortPathNameW(pathw, NULL, 0); + if (short_path_buffer_len == 0) { + goto short_path_done; + } + short_path_buffer = uv__malloc(short_path_buffer_len * sizeof(WCHAR)); + if (short_path_buffer == NULL) { + goto short_path_done; } + if (GetShortPathNameW(pathw, + short_path_buffer, + short_path_buffer_len) == 0) { + uv__free(short_path_buffer); + short_path_buffer = NULL; + } +short_path_done: + short_path = short_path_buffer; if (uv_split_path(pathw, &dir, &handle->filew) != 0) { last_error = GetLastError(); @@ -274,6 +327,8 @@ int uv_fs_event_start(uv_fs_event_t* handle, goto error; } + assert(is_path_dir ? pathw != NULL : pathw == NULL); + handle->dirw = pathw; handle->req_pending = 1; return 0; @@ -305,6 +360,11 @@ int uv_fs_event_start(uv_fs_event_t* handle, handle->buffer = NULL; } + if (uv__is_active(handle)) + uv__handle_stop(handle); + + uv__free(short_path); + return uv_translate_sys_error(last_error); } @@ -344,6 +404,25 @@ int uv_fs_event_stop(uv_fs_event_t* handle) { } +static int file_info_cmp(WCHAR* str, WCHAR* file_name, size_t file_name_len) { + size_t str_len; + + if (str == NULL) + return -1; + + str_len = wcslen(str); + + /* + Since we only care about equality, return early if the strings + aren't the same length + */ + if (str_len != (file_name_len / sizeof(WCHAR))) + return -1; + + return _wcsnicmp(str, file_name, str_len); +} + + void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req, uv_fs_event_t* handle) { FILE_NOTIFY_INFORMATION* file_info; @@ -362,7 +441,7 @@ void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req, * - We are not active, just ignore the callback */ if (!uv__is_active(handle)) { - if (handle->flags & UV__HANDLE_CLOSING) { + if (handle->flags & UV_HANDLE_CLOSING) { uv_want_endgame(loop, (uv_handle_t*) handle); } return; @@ -383,10 +462,12 @@ void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req, * or if the filename filter matches. */ if (handle->dirw || - _wcsnicmp(handle->filew, file_info->FileName, - file_info->FileNameLength / sizeof(WCHAR)) == 0 || - _wcsnicmp(handle->short_filew, file_info->FileName, - file_info->FileNameLength / sizeof(WCHAR)) == 0) { + file_info_cmp(handle->filew, + file_info->FileName, + file_info->FileNameLength) == 0 || + file_info_cmp(handle->short_filew, + file_info->FileName, + file_info->FileNameLength) == 0) { if (handle->dirw) { /* @@ -407,7 +488,7 @@ void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req, } _snwprintf(filenamew, size, L"%s\\%.*s", handle->dirw, - file_info->FileNameLength / sizeof(WCHAR), + file_info->FileNameLength / (DWORD)sizeof(WCHAR), file_info->FileName); filenamew[size - 1] = L'\0'; @@ -484,7 +565,7 @@ void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req, } offset = file_info->NextEntryOffset; - } while (offset && !(handle->flags & UV__HANDLE_CLOSING)); + } while (offset && !(handle->flags & UV_HANDLE_CLOSING)); } else { handle->cb(handle, NULL, UV_CHANGE, 0); } @@ -493,7 +574,7 @@ void uv_process_fs_event_req(uv_loop_t* loop, uv_req_t* req, handle->cb(handle, NULL, 0, uv_translate_sys_error(err)); } - if (!(handle->flags & UV__HANDLE_CLOSING)) { + if (!(handle->flags & UV_HANDLE_CLOSING)) { uv_fs_event_queue_readdirchanges(loop, handle); } else { uv_want_endgame(loop, (uv_handle_t*)handle); @@ -514,7 +595,7 @@ void uv_fs_event_close(uv_loop_t* loop, uv_fs_event_t* handle) { void uv_fs_event_endgame(uv_loop_t* loop, uv_fs_event_t* handle) { - if ((handle->flags & UV__HANDLE_CLOSING) && !handle->req_pending) { + if ((handle->flags & UV_HANDLE_CLOSING) && !handle->req_pending) { assert(!(handle->flags & UV_HANDLE_CLOSED)); if (handle->buffer) { diff --git a/include/libuv/src/win/fs-fd-hash-inl.h b/include/libuv/src/win/fs-fd-hash-inl.h new file mode 100644 index 000000000..0b532af12 --- /dev/null +++ b/include/libuv/src/win/fs-fd-hash-inl.h @@ -0,0 +1,200 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef UV_WIN_FS_FD_HASH_INL_H_ +#define UV_WIN_FS_FD_HASH_INL_H_ + +#include "uv.h" +#include "internal.h" + +/* Files are only inserted in uv__fd_hash when the UV_FS_O_FILEMAP flag is + * specified. Thus, when uv__fd_hash_get returns true, the file mapping in the + * info structure should be used for read/write operations. + * + * If the file is empty, the mapping field will be set to + * INVALID_HANDLE_VALUE. This is not an issue since the file mapping needs to + * be created anyway when the file size changes. + * + * Since file descriptors are sequential integers, the modulo operator is used + * as hashing function. For each bucket, a single linked list of arrays is + * kept to minimize allocations. A statically allocated memory buffer is kept + * for the first array in each bucket. */ + + +#define UV__FD_HASH_SIZE 256 +#define UV__FD_HASH_GROUP_SIZE 16 + +struct uv__fd_info_s { + int flags; + BOOLEAN is_directory; + HANDLE mapping; + LARGE_INTEGER size; + LARGE_INTEGER current_pos; +}; + +struct uv__fd_hash_entry_s { + uv_file fd; + struct uv__fd_info_s info; +}; + +struct uv__fd_hash_entry_group_s { + struct uv__fd_hash_entry_s entries[UV__FD_HASH_GROUP_SIZE]; + struct uv__fd_hash_entry_group_s* next; +}; + +struct uv__fd_hash_bucket_s { + size_t size; + struct uv__fd_hash_entry_group_s* data; +}; + + +static uv_mutex_t uv__fd_hash_mutex; + +static struct uv__fd_hash_entry_group_s + uv__fd_hash_entry_initial[UV__FD_HASH_SIZE * UV__FD_HASH_GROUP_SIZE]; +static struct uv__fd_hash_bucket_s uv__fd_hash[UV__FD_HASH_SIZE]; + + +INLINE static void uv__fd_hash_init(void) { + size_t i; + int err; + + err = uv_mutex_init(&uv__fd_hash_mutex); + if (err) { + uv_fatal_error(err, "uv_mutex_init"); + } + + for (i = 0; i < ARRAY_SIZE(uv__fd_hash); ++i) { + uv__fd_hash[i].size = 0; + uv__fd_hash[i].data = + uv__fd_hash_entry_initial + i * UV__FD_HASH_GROUP_SIZE; + } +} + +#define FIND_COMMON_VARIABLES \ + unsigned i; \ + unsigned bucket = fd % ARRAY_SIZE(uv__fd_hash); \ + struct uv__fd_hash_entry_s* entry_ptr = NULL; \ + struct uv__fd_hash_entry_group_s* group_ptr; \ + struct uv__fd_hash_bucket_s* bucket_ptr = &uv__fd_hash[bucket]; + +#define FIND_IN_GROUP_PTR(group_size) \ + do { \ + for (i = 0; i < group_size; ++i) { \ + if (group_ptr->entries[i].fd == fd) { \ + entry_ptr = &group_ptr->entries[i]; \ + break; \ + } \ + } \ + } while (0) + +#define FIND_IN_BUCKET_PTR() \ + do { \ + size_t first_group_size = bucket_ptr->size % UV__FD_HASH_GROUP_SIZE; \ + if (bucket_ptr->size != 0 && first_group_size == 0) \ + first_group_size = UV__FD_HASH_GROUP_SIZE; \ + group_ptr = bucket_ptr->data; \ + FIND_IN_GROUP_PTR(first_group_size); \ + for (group_ptr = group_ptr->next; \ + group_ptr != NULL && entry_ptr == NULL; \ + group_ptr = group_ptr->next) \ + FIND_IN_GROUP_PTR(UV__FD_HASH_GROUP_SIZE); \ + } while (0) + +INLINE static int uv__fd_hash_get(int fd, struct uv__fd_info_s* info) { + FIND_COMMON_VARIABLES + + uv_mutex_lock(&uv__fd_hash_mutex); + + FIND_IN_BUCKET_PTR(); + + if (entry_ptr != NULL) { + *info = entry_ptr->info; + } + + uv_mutex_unlock(&uv__fd_hash_mutex); + return entry_ptr != NULL; +} + +INLINE static void uv__fd_hash_add(int fd, struct uv__fd_info_s* info) { + FIND_COMMON_VARIABLES + + uv_mutex_lock(&uv__fd_hash_mutex); + + FIND_IN_BUCKET_PTR(); + + if (entry_ptr == NULL) { + i = bucket_ptr->size % UV__FD_HASH_GROUP_SIZE; + + if (bucket_ptr->size != 0 && i == 0) { + struct uv__fd_hash_entry_group_s* new_group_ptr = + uv__malloc(sizeof(*new_group_ptr)); + if (new_group_ptr == NULL) { + uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); + } + new_group_ptr->next = bucket_ptr->data; + bucket_ptr->data = new_group_ptr; + } + + bucket_ptr->size += 1; + entry_ptr = &bucket_ptr->data->entries[i]; + entry_ptr->fd = fd; + } + + entry_ptr->info = *info; + + uv_mutex_unlock(&uv__fd_hash_mutex); +} + +INLINE static int uv__fd_hash_remove(int fd, struct uv__fd_info_s* info) { + FIND_COMMON_VARIABLES + + uv_mutex_lock(&uv__fd_hash_mutex); + + FIND_IN_BUCKET_PTR(); + + if (entry_ptr != NULL) { + *info = entry_ptr->info; + + bucket_ptr->size -= 1; + + i = bucket_ptr->size % UV__FD_HASH_GROUP_SIZE; + if (entry_ptr != &bucket_ptr->data->entries[i]) { + *entry_ptr = bucket_ptr->data->entries[i]; + } + + if (bucket_ptr->size != 0 && + bucket_ptr->size % UV__FD_HASH_GROUP_SIZE == 0) { + struct uv__fd_hash_entry_group_s* old_group_ptr = bucket_ptr->data; + bucket_ptr->data = old_group_ptr->next; + uv__free(old_group_ptr); + } + } + + uv_mutex_unlock(&uv__fd_hash_mutex); + return entry_ptr != NULL; +} + +#undef FIND_COMMON_VARIABLES +#undef FIND_IN_GROUP_PTR +#undef FIND_IN_BUCKET_PTR + +#endif /* UV_WIN_FS_FD_HASH_INL_H_ */ diff --git a/include/libuv/src/win/fs.c b/include/libuv/src/win/fs.c index 54dfea724..8a801749d 100644 --- a/include/libuv/src/win/fs.c +++ b/include/libuv/src/win/fs.c @@ -34,8 +34,7 @@ #include "internal.h" #include "req-inl.h" #include "handle-inl.h" - -#include +#include "fs-fd-hash-inl.h" #define UV_FS_FREE_PATHS 0x0002 @@ -43,19 +42,35 @@ #define UV_FS_CLEANEDUP 0x0010 -#define QUEUE_FS_TP_JOB(loop, req) \ - do { \ - uv__req_register(loop, req); \ - uv__work_submit((loop), &(req)->work_req, uv__fs_work, uv__fs_done); \ - } while (0) +#define INIT(subtype) \ + do { \ + if (req == NULL) \ + return UV_EINVAL; \ + uv_fs_req_init(loop, req, subtype, cb); \ + } \ + while (0) + +#define POST \ + do { \ + if (cb != NULL) { \ + uv__req_register(loop, req); \ + uv__work_submit(loop, \ + &req->work_req, \ + UV__WORK_FAST_IO, \ + uv__fs_work, \ + uv__fs_done); \ + return 0; \ + } else { \ + uv__fs_work(&req->work_req); \ + return req->result; \ + } \ + } \ + while (0) #define SET_REQ_RESULT(req, result_value) \ do { \ req->result = (result_value); \ - if (req->result == -1) { \ - req->sys_errno_ = _doserrno; \ - req->result = uv_translate_sys_error(req->sys_errno_); \ - } \ + assert(req->result != -1); \ } while (0) #define SET_REQ_WIN32_ERROR(req, sys_errno) \ @@ -77,14 +92,17 @@ return; \ } +#define MILLIONu (1000U * 1000U) +#define BILLIONu (1000U * 1000U * 1000U) + #define FILETIME_TO_UINT(filetime) \ - (*((uint64_t*) &(filetime)) - 116444736000000000ULL) + (*((uint64_t*) &(filetime)) - (uint64_t) 116444736 * BILLIONu) #define FILETIME_TO_TIME_T(filetime) \ - (FILETIME_TO_UINT(filetime) / 10000000ULL) + (FILETIME_TO_UINT(filetime) / (10u * MILLIONu)) #define FILETIME_TO_TIME_NS(filetime, secs) \ - ((FILETIME_TO_UINT(filetime) - (secs * 10000000ULL)) * 100) + ((FILETIME_TO_UINT(filetime) - (secs * (uint64_t) 10 * MILLIONu)) * 100U) #define FILETIME_TO_TIMESPEC(ts, filetime) \ do { \ @@ -94,8 +112,8 @@ #define TIME_T_TO_FILETIME(time, filetime_ptr) \ do { \ - uint64_t bigtime = ((int64_t) (time) * 10000000LL) + \ - 116444736000000000ULL; \ + uint64_t bigtime = ((uint64_t) ((time) * (uint64_t) 10 * MILLIONu)) + \ + (uint64_t) 116444736 * BILLIONu; \ (filetime_ptr)->dwLowDateTime = bigtime & 0xFFFFFFFF; \ (filetime_ptr)->dwHighDateTime = bigtime >> 32; \ } while(0) @@ -104,6 +122,8 @@ #define IS_LETTER(c) (((c) >= L'a' && (c) <= L'z') || \ ((c) >= L'A' && (c) <= L'Z')) +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) + const WCHAR JUNCTION_PREFIX[] = L"\\??\\"; const WCHAR JUNCTION_PREFIX_LEN = 4; @@ -113,9 +133,18 @@ const WCHAR LONG_PATH_PREFIX_LEN = 4; const WCHAR UNC_PATH_PREFIX[] = L"\\\\?\\UNC\\"; const WCHAR UNC_PATH_PREFIX_LEN = 8; +static int uv__file_symlink_usermode_flag = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; + +static DWORD uv__allocation_granularity; + + +void uv_fs_init(void) { + SYSTEM_INFO system_info; -void uv_fs_init() { - _fmode = _O_BINARY; + GetSystemInfo(&system_info); + uv__allocation_granularity = system_info.dwAllocationGranularity; + + uv__fd_hash_init(); } @@ -123,7 +152,7 @@ INLINE static int fs__capture_path(uv_fs_t* req, const char* path, const char* new_path, const int copy_path) { char* buf; char* pos; - ssize_t buf_sz = 0, path_len, pathw_len = 0, new_pathw_len = 0; + ssize_t buf_sz = 0, path_len = 0, pathw_len = 0, new_pathw_len = 0; /* new_path can only be set if path is also set. */ assert(new_path == NULL || path != NULL); @@ -204,14 +233,11 @@ INLINE static int fs__capture_path(uv_fs_t* req, const char* path, req->fs.info.new_pathw = NULL; } - if (!copy_path) { - req->path = path; - } else if (path) { + req->path = path; + if (path != NULL && copy_path) { memcpy(pos, path, path_len); assert(path_len == buf_sz - (pos - buf)); req->path = pos; - } else { - req->path = NULL; } req->flags |= UV_FS_FREE_PATHS; @@ -223,16 +249,17 @@ INLINE static int fs__capture_path(uv_fs_t* req, const char* path, INLINE static void uv_fs_req_init(uv_loop_t* loop, uv_fs_t* req, uv_fs_type fs_type, const uv_fs_cb cb) { - uv_req_init(loop, (uv_req_t*) req); - - req->type = UV_FS; + uv__once_init(); + UV_REQ_INIT(req, UV_FS); req->loop = loop; req->flags = 0; req->fs_type = fs_type; + req->sys_errno_ = 0; req->result = 0; req->ptr = NULL; req->path = NULL; req->cb = cb; + memset(&req->fs, 0, sizeof(req->fs)); } @@ -292,6 +319,8 @@ INLINE static int fs__readlink_handle(HANDLE handle, char** target_ptr, WCHAR* w_target; DWORD w_target_len; DWORD bytes; + size_t i; + size_t len; if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, @@ -313,12 +342,11 @@ INLINE static int fs__readlink_handle(HANDLE handle, char** target_ptr, reparse_data->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR); - /* Real symlinks can contain pretty much everything, but the only thing */ - /* we really care about is undoing the implicit conversion to an NT */ - /* namespaced path that CreateSymbolicLink will perform on absolute */ - /* paths. If the path is win32-namespaced then the user must have */ - /* explicitly made it so, and we better just return the unmodified */ - /* reparse data. */ + /* Real symlinks can contain pretty much everything, but the only thing we + * really care about is undoing the implicit conversion to an NT namespaced + * path that CreateSymbolicLink will perform on absolute paths. If the path + * is win32-namespaced then the user must have explicitly made it so, and + * we better just return the unmodified reparse data. */ if (w_target_len >= 4 && w_target[0] == L'\\' && w_target[1] == L'?' && @@ -339,8 +367,8 @@ INLINE static int fs__readlink_handle(HANDLE handle, char** target_ptr, (w_target[5] == L'N' || w_target[5] == L'n') && (w_target[6] == L'C' || w_target[6] == L'c') && w_target[7] == L'\\') { - /* \??\UNC\\\ - make sure the final path looks like */ - /* \\\\ */ + /* \??\UNC\\\ - make sure the final path looks like + * \\\\ */ w_target += 6; w_target[0] = L'\\'; w_target_len -= 6; @@ -355,11 +383,11 @@ INLINE static int fs__readlink_handle(HANDLE handle, char** target_ptr, w_target_len = reparse_data->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR); - /* Only treat junctions that look like \??\:\ as symlink. */ - /* Junctions can also be used as mount points, like \??\Volume{}, */ - /* but that's confusing for programs since they wouldn't be able to */ - /* actually understand such a path when returned by uv_readlink(). */ - /* UNC paths are never valid for junctions so we don't care about them. */ + /* Only treat junctions that look like \??\:\ as symlink. Junctions + * can also be used as mount points, like \??\Volume{}, but that's + * confusing for programs since they wouldn't be able to actually + * understand such a path when returned by uv_readlink(). UNC paths are + * never valid for junctions so we don't care about them. */ if (!(w_target_len >= 6 && w_target[0] == L'\\' && w_target[1] == L'?' && @@ -377,6 +405,38 @@ INLINE static int fs__readlink_handle(HANDLE handle, char** target_ptr, w_target += 4; w_target_len -= 4; + } else if (reparse_data->ReparseTag == IO_REPARSE_TAG_APPEXECLINK) { + /* String #3 in the list has the target filename. */ + if (reparse_data->AppExecLinkReparseBuffer.StringCount < 3) { + SetLastError(ERROR_SYMLINK_NOT_SUPPORTED); + return -1; + } + w_target = reparse_data->AppExecLinkReparseBuffer.StringList; + /* The StringList buffer contains a list of strings separated by "\0", */ + /* with "\0\0" terminating the list. Move to the 3rd string in the list: */ + for (i = 0; i < 2; ++i) { + len = wcslen(w_target); + if (len == 0) { + SetLastError(ERROR_SYMLINK_NOT_SUPPORTED); + return -1; + } + w_target += len + 1; + } + w_target_len = wcslen(w_target); + if (w_target_len == 0) { + SetLastError(ERROR_SYMLINK_NOT_SUPPORTED); + return -1; + } + /* Make sure it is an absolute path. */ + if (!(w_target_len >= 3 && + ((w_target[0] >= L'a' && w_target[0] <= L'z') || + (w_target[0] >= L'A' && w_target[0] <= L'Z')) && + w_target[1] == L':' && + w_target[2] == L'\\')) { + SetLastError(ERROR_SYMLINK_NOT_SUPPORTED); + return -1; + } + } else { /* Reparse tag does not indicate a symlink. */ SetLastError(ERROR_SYMLINK_NOT_SUPPORTED); @@ -395,59 +455,86 @@ void fs__open(uv_fs_t* req) { HANDLE file; int fd, current_umask; int flags = req->fs.info.file_flags; + struct uv__fd_info_s fd_info; + + /* Adjust flags to be compatible with the memory file mapping. Save the + * original flags to emulate the correct behavior. */ + if (flags & UV_FS_O_FILEMAP) { + fd_info.flags = flags; + fd_info.current_pos.QuadPart = 0; + + if ((flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR)) == + UV_FS_O_WRONLY) { + /* CreateFileMapping always needs read access */ + flags = (flags & ~UV_FS_O_WRONLY) | UV_FS_O_RDWR; + } + + if (flags & UV_FS_O_APPEND) { + /* Clear the append flag and ensure RDRW mode */ + flags &= ~UV_FS_O_APPEND; + flags &= ~(UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR); + flags |= UV_FS_O_RDWR; + } + } - /* Obtain the active umask. umask() never fails and returns the previous */ - /* umask. */ + /* Obtain the active umask. umask() never fails and returns the previous + * umask. */ current_umask = umask(0); umask(current_umask); /* convert flags and mode to CreateFile parameters */ - switch (flags & (_O_RDONLY | _O_WRONLY | _O_RDWR)) { - case _O_RDONLY: + switch (flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR)) { + case UV_FS_O_RDONLY: access = FILE_GENERIC_READ; - attributes |= FILE_FLAG_BACKUP_SEMANTICS; break; - case _O_WRONLY: + case UV_FS_O_WRONLY: access = FILE_GENERIC_WRITE; break; - case _O_RDWR: + case UV_FS_O_RDWR: access = FILE_GENERIC_READ | FILE_GENERIC_WRITE; break; default: goto einval; } - if (flags & _O_APPEND) { + if (flags & UV_FS_O_APPEND) { access &= ~FILE_WRITE_DATA; access |= FILE_APPEND_DATA; - attributes &= ~FILE_FLAG_BACKUP_SEMANTICS; } /* * Here is where we deviate significantly from what CRT's _open() * does. We indiscriminately use all the sharing modes, to match * UNIX semantics. In particular, this ensures that the file can - * be deleted even whilst it's open, fixing issue #1449. + * be deleted even whilst it's open, fixing issue + * https://github.com/nodejs/node-v0.x-archive/issues/1449. + * We still support exclusive sharing mode, since it is necessary + * for opening raw block devices, otherwise Windows will prevent + * any attempt to write past the master boot record. */ - share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + if (flags & UV_FS_O_EXLOCK) { + share = 0; + } else { + share = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + } - switch (flags & (_O_CREAT | _O_EXCL | _O_TRUNC)) { + switch (flags & (UV_FS_O_CREAT | UV_FS_O_EXCL | UV_FS_O_TRUNC)) { case 0: - case _O_EXCL: + case UV_FS_O_EXCL: disposition = OPEN_EXISTING; break; - case _O_CREAT: + case UV_FS_O_CREAT: disposition = OPEN_ALWAYS; break; - case _O_CREAT | _O_EXCL: - case _O_CREAT | _O_TRUNC | _O_EXCL: + case UV_FS_O_CREAT | UV_FS_O_EXCL: + case UV_FS_O_CREAT | UV_FS_O_TRUNC | UV_FS_O_EXCL: disposition = CREATE_NEW; break; - case _O_TRUNC: - case _O_TRUNC | _O_EXCL: + case UV_FS_O_TRUNC: + case UV_FS_O_TRUNC | UV_FS_O_EXCL: disposition = TRUNCATE_EXISTING; break; - case _O_CREAT | _O_TRUNC: + case UV_FS_O_CREAT | UV_FS_O_TRUNC: disposition = CREATE_ALWAYS; break; default: @@ -455,34 +542,76 @@ void fs__open(uv_fs_t* req) { } attributes |= FILE_ATTRIBUTE_NORMAL; - if (flags & _O_CREAT) { + if (flags & UV_FS_O_CREAT) { if (!((req->fs.info.mode & ~current_umask) & _S_IWRITE)) { attributes |= FILE_ATTRIBUTE_READONLY; } } - if (flags & _O_TEMPORARY ) { + if (flags & UV_FS_O_TEMPORARY ) { attributes |= FILE_FLAG_DELETE_ON_CLOSE | FILE_ATTRIBUTE_TEMPORARY; access |= DELETE; } - if (flags & _O_SHORT_LIVED) { + if (flags & UV_FS_O_SHORT_LIVED) { attributes |= FILE_ATTRIBUTE_TEMPORARY; } - switch (flags & (_O_SEQUENTIAL | _O_RANDOM)) { + switch (flags & (UV_FS_O_SEQUENTIAL | UV_FS_O_RANDOM)) { case 0: break; - case _O_SEQUENTIAL: + case UV_FS_O_SEQUENTIAL: attributes |= FILE_FLAG_SEQUENTIAL_SCAN; break; - case _O_RANDOM: + case UV_FS_O_RANDOM: attributes |= FILE_FLAG_RANDOM_ACCESS; break; default: goto einval; } + if (flags & UV_FS_O_DIRECT) { + /* + * FILE_APPEND_DATA and FILE_FLAG_NO_BUFFERING are mutually exclusive. + * Windows returns 87, ERROR_INVALID_PARAMETER if these are combined. + * + * FILE_APPEND_DATA is included in FILE_GENERIC_WRITE: + * + * FILE_GENERIC_WRITE = STANDARD_RIGHTS_WRITE | + * FILE_WRITE_DATA | + * FILE_WRITE_ATTRIBUTES | + * FILE_WRITE_EA | + * FILE_APPEND_DATA | + * SYNCHRONIZE + * + * Note: Appends are also permitted by FILE_WRITE_DATA. + * + * In order for direct writes and direct appends to succeed, we therefore + * exclude FILE_APPEND_DATA if FILE_WRITE_DATA is specified, and otherwise + * fail if the user's sole permission is a direct append, since this + * particular combination is invalid. + */ + if (access & FILE_APPEND_DATA) { + if (access & FILE_WRITE_DATA) { + access &= ~FILE_APPEND_DATA; + } else { + goto einval; + } + } + attributes |= FILE_FLAG_NO_BUFFERING; + } + + switch (flags & (UV_FS_O_DSYNC | UV_FS_O_SYNC)) { + case 0: + break; + case UV_FS_O_DSYNC: + case UV_FS_O_SYNC: + attributes |= FILE_FLAG_WRITE_THROUGH; + break; + default: + goto einval; + } + /* Setting this flag makes it possible to open a directory. */ attributes |= FILE_FLAG_BACKUP_SEMANTICS; @@ -495,10 +624,10 @@ void fs__open(uv_fs_t* req) { NULL); if (file == INVALID_HANDLE_VALUE) { DWORD error = GetLastError(); - if (error == ERROR_FILE_EXISTS && (flags & _O_CREAT) && - !(flags & _O_EXCL)) { - /* Special case: when ERROR_FILE_EXISTS happens and O_CREAT was */ - /* specified, it means the path referred to a directory. */ + if (error == ERROR_FILE_EXISTS && (flags & UV_FS_O_CREAT) && + !(flags & UV_FS_O_EXCL)) { + /* Special case: when ERROR_FILE_EXISTS happens and UV_FS_O_CREAT was + * specified, it means the path referred to a directory. */ SET_REQ_UV_ERROR(req, UV_EISDIR, error); } else { SET_REQ_WIN32_ERROR(req, GetLastError()); @@ -517,11 +646,55 @@ void fs__open(uv_fs_t* req) { else if (GetLastError() != ERROR_SUCCESS) SET_REQ_WIN32_ERROR(req, GetLastError()); else - SET_REQ_WIN32_ERROR(req, UV_UNKNOWN); + SET_REQ_WIN32_ERROR(req, (DWORD) UV_UNKNOWN); CloseHandle(file); return; } + if (flags & UV_FS_O_FILEMAP) { + FILE_STANDARD_INFO file_info; + if (!GetFileInformationByHandleEx(file, + FileStandardInfo, + &file_info, + sizeof file_info)) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + CloseHandle(file); + return; + } + fd_info.is_directory = file_info.Directory; + + if (fd_info.is_directory) { + fd_info.size.QuadPart = 0; + fd_info.mapping = INVALID_HANDLE_VALUE; + } else { + if (!GetFileSizeEx(file, &fd_info.size)) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + CloseHandle(file); + return; + } + + if (fd_info.size.QuadPart == 0) { + fd_info.mapping = INVALID_HANDLE_VALUE; + } else { + DWORD flProtect = (fd_info.flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | + UV_FS_O_RDWR)) == UV_FS_O_RDONLY ? PAGE_READONLY : PAGE_READWRITE; + fd_info.mapping = CreateFileMapping(file, + NULL, + flProtect, + fd_info.size.HighPart, + fd_info.size.LowPart, + NULL); + if (fd_info.mapping == NULL) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + CloseHandle(file); + return; + } + } + } + + uv__fd_hash_add(fd, &fd_info); + } + SET_REQ_RESULT(req, fd); return; @@ -532,9 +705,16 @@ void fs__open(uv_fs_t* req) { void fs__close(uv_fs_t* req) { int fd = req->file.fd; int result; + struct uv__fd_info_s fd_info; VERIFY_FD(fd, req); + if (uv__fd_hash_remove(fd, &fd_info)) { + if (fd_info.mapping != INVALID_HANDLE_VALUE) { + CloseHandle(fd_info.mapping); + } + } + if (fd > 2) result = _close(fd); else @@ -547,11 +727,128 @@ void fs__close(uv_fs_t* req) { assert(errno == EBADF); SET_REQ_UV_ERROR(req, UV_EBADF, ERROR_INVALID_HANDLE); } else { - req->result = 0; + SET_REQ_RESULT(req, 0); + } +} + + +LONG fs__filemap_ex_filter(LONG excode, PEXCEPTION_POINTERS pep, + int* perror) { + if (excode != (LONG)EXCEPTION_IN_PAGE_ERROR) { + return EXCEPTION_CONTINUE_SEARCH; + } + + assert(perror != NULL); + if (pep != NULL && pep->ExceptionRecord != NULL && + pep->ExceptionRecord->NumberParameters >= 3) { + NTSTATUS status = (NTSTATUS)pep->ExceptionRecord->ExceptionInformation[3]; + *perror = pRtlNtStatusToDosError(status); + if (*perror != ERROR_SUCCESS) { + return EXCEPTION_EXECUTE_HANDLER; + } } + *perror = UV_UNKNOWN; + return EXCEPTION_EXECUTE_HANDLER; } +void fs__read_filemap(uv_fs_t* req, struct uv__fd_info_s* fd_info) { + int fd = req->file.fd; /* VERIFY_FD done in fs__read */ + int rw_flags = fd_info->flags & + (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR); + size_t read_size, done_read; + unsigned int index; + LARGE_INTEGER pos, end_pos; + size_t view_offset; + LARGE_INTEGER view_base; + void* view; + + if (rw_flags == UV_FS_O_WRONLY) { + SET_REQ_WIN32_ERROR(req, ERROR_ACCESS_DENIED); + return; + } + if (fd_info->is_directory) { + SET_REQ_WIN32_ERROR(req, ERROR_INVALID_FUNCTION); + return; + } + + if (req->fs.info.offset == -1) { + pos = fd_info->current_pos; + } else { + pos.QuadPart = req->fs.info.offset; + } + + /* Make sure we wont read past EOF. */ + if (pos.QuadPart >= fd_info->size.QuadPart) { + SET_REQ_RESULT(req, 0); + return; + } + + read_size = 0; + for (index = 0; index < req->fs.info.nbufs; ++index) { + read_size += req->fs.info.bufs[index].len; + } + read_size = (size_t) MIN((LONGLONG) read_size, + fd_info->size.QuadPart - pos.QuadPart); + if (read_size == 0) { + SET_REQ_RESULT(req, 0); + return; + } + + end_pos.QuadPart = pos.QuadPart + read_size; + + view_offset = pos.QuadPart % uv__allocation_granularity; + view_base.QuadPart = pos.QuadPart - view_offset; + view = MapViewOfFile(fd_info->mapping, + FILE_MAP_READ, + view_base.HighPart, + view_base.LowPart, + view_offset + read_size); + if (view == NULL) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + return; + } + + done_read = 0; + for (index = 0; + index < req->fs.info.nbufs && done_read < read_size; + ++index) { + size_t this_read_size = MIN(req->fs.info.bufs[index].len, + read_size - done_read); +#ifdef _MSC_VER + int err = 0; + __try { +#endif + memcpy(req->fs.info.bufs[index].base, + (char*)view + view_offset + done_read, + this_read_size); +#ifdef _MSC_VER + } + __except (fs__filemap_ex_filter(GetExceptionCode(), + GetExceptionInformation(), &err)) { + SET_REQ_WIN32_ERROR(req, err); + UnmapViewOfFile(view); + return; + } +#endif + done_read += this_read_size; + } + assert(done_read == read_size); + + if (!UnmapViewOfFile(view)) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + return; + } + + if (req->fs.info.offset == -1) { + fd_info->current_pos = end_pos; + uv__fd_hash_add(fd, fd_info); + } + + SET_REQ_RESULT(req, read_size); + return; +} + void fs__read(uv_fs_t* req) { int fd = req->file.fd; int64_t offset = req->fs.info.offset; @@ -562,9 +859,20 @@ void fs__read(uv_fs_t* req) { DWORD error; int result; unsigned int index; + LARGE_INTEGER original_position; + LARGE_INTEGER zero_offset; + int restore_position; + struct uv__fd_info_s fd_info; VERIFY_FD(fd, req); + if (uv__fd_hash_get(fd, &fd_info)) { + fs__read_filemap(req, &fd_info); + return; + } + + zero_offset.QuadPart = 0; + restore_position = 0; handle = uv__get_osfhandle(fd); if (handle == INVALID_HANDLE_VALUE) { @@ -575,6 +883,10 @@ void fs__read(uv_fs_t* req) { if (offset != -1) { memset(&overlapped, 0, sizeof overlapped); overlapped_ptr = &overlapped; + if (SetFilePointerEx(handle, zero_offset, &original_position, + FILE_CURRENT)) { + restore_position = 1; + } } else { overlapped_ptr = NULL; } @@ -599,6 +911,9 @@ void fs__read(uv_fs_t* req) { ++index; } while (result && index < req->fs.info.nbufs); + if (restore_position) + SetFilePointerEx(handle, original_position, NULL, FILE_BEGIN); + if (result || bytes > 0) { SET_REQ_RESULT(req, bytes); } else { @@ -612,6 +927,130 @@ void fs__read(uv_fs_t* req) { } +void fs__write_filemap(uv_fs_t* req, HANDLE file, + struct uv__fd_info_s* fd_info) { + int fd = req->file.fd; /* VERIFY_FD done in fs__write */ + int force_append = fd_info->flags & UV_FS_O_APPEND; + int rw_flags = fd_info->flags & + (UV_FS_O_RDONLY | UV_FS_O_WRONLY | UV_FS_O_RDWR); + size_t write_size, done_write; + unsigned int index; + LARGE_INTEGER pos, end_pos; + size_t view_offset; + LARGE_INTEGER view_base; + void* view; + FILETIME ft; + + if (rw_flags == UV_FS_O_RDONLY) { + SET_REQ_WIN32_ERROR(req, ERROR_ACCESS_DENIED); + return; + } + if (fd_info->is_directory) { + SET_REQ_WIN32_ERROR(req, ERROR_INVALID_FUNCTION); + return; + } + + write_size = 0; + for (index = 0; index < req->fs.info.nbufs; ++index) { + write_size += req->fs.info.bufs[index].len; + } + + if (write_size == 0) { + SET_REQ_RESULT(req, 0); + return; + } + + if (force_append) { + pos = fd_info->size; + } else if (req->fs.info.offset == -1) { + pos = fd_info->current_pos; + } else { + pos.QuadPart = req->fs.info.offset; + } + + end_pos.QuadPart = pos.QuadPart + write_size; + + /* Recreate the mapping to enlarge the file if needed */ + if (end_pos.QuadPart > fd_info->size.QuadPart) { + if (fd_info->mapping != INVALID_HANDLE_VALUE) { + CloseHandle(fd_info->mapping); + } + + fd_info->mapping = CreateFileMapping(file, + NULL, + PAGE_READWRITE, + end_pos.HighPart, + end_pos.LowPart, + NULL); + if (fd_info->mapping == NULL) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + CloseHandle(file); + fd_info->mapping = INVALID_HANDLE_VALUE; + fd_info->size.QuadPart = 0; + fd_info->current_pos.QuadPart = 0; + uv__fd_hash_add(fd, fd_info); + return; + } + + fd_info->size = end_pos; + uv__fd_hash_add(fd, fd_info); + } + + view_offset = pos.QuadPart % uv__allocation_granularity; + view_base.QuadPart = pos.QuadPart - view_offset; + view = MapViewOfFile(fd_info->mapping, + FILE_MAP_WRITE, + view_base.HighPart, + view_base.LowPart, + view_offset + write_size); + if (view == NULL) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + return; + } + + done_write = 0; + for (index = 0; index < req->fs.info.nbufs; ++index) { +#ifdef _MSC_VER + int err = 0; + __try { +#endif + memcpy((char*)view + view_offset + done_write, + req->fs.info.bufs[index].base, + req->fs.info.bufs[index].len); +#ifdef _MSC_VER + } + __except (fs__filemap_ex_filter(GetExceptionCode(), + GetExceptionInformation(), &err)) { + SET_REQ_WIN32_ERROR(req, err); + UnmapViewOfFile(view); + return; + } +#endif + done_write += req->fs.info.bufs[index].len; + } + assert(done_write == write_size); + + if (!FlushViewOfFile(view, 0)) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + UnmapViewOfFile(view); + return; + } + if (!UnmapViewOfFile(view)) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + return; + } + + if (req->fs.info.offset == -1) { + fd_info->current_pos = end_pos; + uv__fd_hash_add(fd, fd_info); + } + + GetSystemTimeAsFileTime(&ft); + SetFileTime(file, NULL, NULL, &ft); + + SET_REQ_RESULT(req, done_write); +} + void fs__write(uv_fs_t* req) { int fd = req->file.fd; int64_t offset = req->fs.info.offset; @@ -621,18 +1060,33 @@ void fs__write(uv_fs_t* req) { DWORD bytes; int result; unsigned int index; + LARGE_INTEGER original_position; + LARGE_INTEGER zero_offset; + int restore_position; + struct uv__fd_info_s fd_info; VERIFY_FD(fd, req); + zero_offset.QuadPart = 0; + restore_position = 0; handle = uv__get_osfhandle(fd); if (handle == INVALID_HANDLE_VALUE) { SET_REQ_WIN32_ERROR(req, ERROR_INVALID_HANDLE); return; } + if (uv__fd_hash_get(fd, &fd_info)) { + fs__write_filemap(req, handle, &fd_info); + return; + } + if (offset != -1) { memset(&overlapped, 0, sizeof overlapped); overlapped_ptr = &overlapped; + if (SetFilePointerEx(handle, zero_offset, &original_position, + FILE_CURRENT)) { + restore_position = 1; + } } else { overlapped_ptr = NULL; } @@ -657,6 +1111,9 @@ void fs__write(uv_fs_t* req) { ++index; } while (result && index < req->fs.info.nbufs); + if (restore_position) + SetFilePointerEx(handle, original_position, NULL, FILE_BEGIN); + if (result || bytes > 0) { SET_REQ_RESULT(req, bytes); } else { @@ -667,7 +1124,10 @@ void fs__write(uv_fs_t* req) { void fs__rmdir(uv_fs_t* req) { int result = _wrmdir(req->file.pathw); - SET_REQ_RESULT(req, result); + if (result == -1) + SET_REQ_WIN32_ERROR(req, _doserrno); + else + SET_REQ_RESULT(req, 0); } @@ -699,9 +1159,9 @@ void fs__unlink(uv_fs_t* req) { } if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - /* Do not allow deletion of directories, unless it is a symlink. When */ - /* the path refers to a non-symlink directory, report EPERM as mandated */ - /* by POSIX.1. */ + /* Do not allow deletion of directories, unless it is a symlink. When the + * path refers to a non-symlink directory, report EPERM as mandated by + * POSIX.1. */ /* Check if it is a reparse point. If it's not, it's a normal directory. */ if (!(info.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { @@ -710,8 +1170,8 @@ void fs__unlink(uv_fs_t* req) { return; } - /* Read the reparse point and check if it is a valid symlink. */ - /* If not, don't unlink. */ + /* Read the reparse point and check if it is a valid symlink. If not, don't + * unlink. */ if (fs__readlink_handle(handle, NULL, NULL) < 0) { DWORD error = GetLastError(); if (error == ERROR_SYMLINK_NOT_SUPPORTED) @@ -726,7 +1186,8 @@ void fs__unlink(uv_fs_t* req) { /* Remove read-only attribute */ FILE_BASIC_INFORMATION basic = { 0 }; - basic.FileAttributes = info.dwFileAttributes & ~(FILE_ATTRIBUTE_READONLY); + basic.FileAttributes = (info.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY) | + FILE_ATTRIBUTE_ARCHIVE; status = pNtSetInformationFile(handle, &iosb, @@ -759,13 +1220,19 @@ void fs__unlink(uv_fs_t* req) { void fs__mkdir(uv_fs_t* req) { /* TODO: use req->mode. */ - int result = _wmkdir(req->file.pathw); - SET_REQ_RESULT(req, result); + if (CreateDirectoryW(req->file.pathw, NULL)) { + SET_REQ_RESULT(req, 0); + } else { + SET_REQ_WIN32_ERROR(req, GetLastError()); + if (req->sys_errno_ == ERROR_INVALID_NAME) + req->result = UV_EINVAL; + } } +typedef int (*uv__fs_mktemp_func)(uv_fs_t* req); /* OpenBSD original: lib/libc/stdio/mktemp.c */ -void fs__mkdtemp(uv_fs_t* req) { +void fs__mktemp(uv_fs_t* req, uv__fs_mktemp_func func) { static const WCHAR *tempchars = L"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; static const size_t num_chars = 62; @@ -773,28 +1240,22 @@ void fs__mkdtemp(uv_fs_t* req) { WCHAR *cp, *ep; unsigned int tries, i; size_t len; - HCRYPTPROV h_crypt_prov; uint64_t v; - BOOL released; - + char* path; + + path = req->path; len = wcslen(req->file.pathw); ep = req->file.pathw + len; if (len < num_x || wcsncmp(ep - num_x, L"XXXXXX", num_x)) { SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); - return; - } - - if (!CryptAcquireContext(&h_crypt_prov, NULL, NULL, PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT)) { - SET_REQ_WIN32_ERROR(req, GetLastError()); - return; + goto clobber; } tries = TMP_MAX; do { - if (!CryptGenRandom(h_crypt_prov, sizeof(v), (BYTE*) &v)) { - SET_REQ_WIN32_ERROR(req, GetLastError()); - break; + if (uv__random_rtlgenrandom((void *)&v, sizeof(v)) < 0) { + SET_REQ_UV_ERROR(req, UV_EIO, ERROR_IO_DEVICE); + goto clobber; } cp = ep - num_x; @@ -803,22 +1264,93 @@ void fs__mkdtemp(uv_fs_t* req) { v /= num_chars; } - if (_wmkdir(req->file.pathw) == 0) { - len = strlen(req->path); - wcstombs((char*) req->path + len - num_x, ep - num_x, num_x); - SET_REQ_RESULT(req, 0); - break; - } else if (errno != EEXIST) { - SET_REQ_RESULT(req, -1); - break; + if (func(req)) { + if (req->result >= 0) { + len = strlen(path); + wcstombs(path + len - num_x, ep - num_x, num_x); + } + return; } } while (--tries); - released = CryptReleaseContext(h_crypt_prov, 0); - assert(released); - if (tries == 0) { - SET_REQ_RESULT(req, -1); + SET_REQ_WIN32_ERROR(req, GetLastError()); + +clobber: + path[0] = '\0'; +} + + +static int fs__mkdtemp_func(uv_fs_t* req) { + DWORD error; + if (CreateDirectoryW(req->file.pathw, NULL)) { + SET_REQ_RESULT(req, 0); + return 1; + } + error = GetLastError(); + if (error != ERROR_ALREADY_EXISTS) { + SET_REQ_WIN32_ERROR(req, error); + return 1; + } + + return 0; +} + + +void fs__mkdtemp(uv_fs_t* req) { + fs__mktemp(req, fs__mkdtemp_func); +} + + +static int fs__mkstemp_func(uv_fs_t* req) { + HANDLE file; + int fd; + + file = CreateFileW(req->file.pathw, + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, + CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (file == INVALID_HANDLE_VALUE) { + DWORD error; + error = GetLastError(); + + /* If the file exists, the main fs__mktemp() function + will retry. If it's another error, we want to stop. */ + if (error != ERROR_FILE_EXISTS) { + SET_REQ_WIN32_ERROR(req, error); + return 1; + } + + return 0; + } + + fd = _open_osfhandle((intptr_t) file, 0); + if (fd < 0) { + /* The only known failure mode for _open_osfhandle() is EMFILE, in which + * case GetLastError() will return zero. However we'll try to handle other + * errors as well, should they ever occur. + */ + if (errno == EMFILE) + SET_REQ_UV_ERROR(req, UV_EMFILE, ERROR_TOO_MANY_OPEN_FILES); + else if (GetLastError() != ERROR_SUCCESS) + SET_REQ_WIN32_ERROR(req, GetLastError()); + else + SET_REQ_WIN32_ERROR(req, UV_UNKNOWN); + CloseHandle(file); + return 1; } + + SET_REQ_RESULT(req, fd); + + return 1; +} + + +void fs__mkstemp(uv_fs_t* req) { + fs__mktemp(req, fs__mkstemp_func); } @@ -877,7 +1409,7 @@ void fs__scandir(uv_fs_t* req) { /* If the handle is not a directory, we'll get STATUS_INVALID_PARAMETER. * This should be reported back as UV_ENOTDIR. */ - if (status == STATUS_INVALID_PARAMETER) + if (status == (NTSTATUS)STATUS_INVALID_PARAMETER) goto not_a_directory_error; while (NT_SUCCESS(status)) { @@ -1034,29 +1566,161 @@ void fs__scandir(uv_fs_t* req) { uv__free(dirents); } +void fs__opendir(uv_fs_t* req) { + WCHAR* pathw; + size_t len; + const WCHAR* fmt; + WCHAR* find_path; + uv_dir_t* dir; -INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf) { - FILE_ALL_INFORMATION file_info; - FILE_FS_VOLUME_INFORMATION volume_info; - NTSTATUS nt_status; - IO_STATUS_BLOCK io_status; + pathw = req->file.pathw; + dir = NULL; + find_path = NULL; - nt_status = pNtQueryInformationFile(handle, - &io_status, - &file_info, - sizeof file_info, - FileAllInformation); + /* Figure out whether path is a file or a directory. */ + if (!(GetFileAttributesW(pathw) & FILE_ATTRIBUTE_DIRECTORY)) { + SET_REQ_UV_ERROR(req, UV_ENOTDIR, ERROR_DIRECTORY); + goto error; + } - /* Buffer overflow (a warning status code) is expected here. */ - if (NT_ERROR(nt_status)) { - SetLastError(pRtlNtStatusToDosError(nt_status)); - return -1; + dir = uv__malloc(sizeof(*dir)); + if (dir == NULL) { + SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY); + goto error; } - nt_status = pNtQueryVolumeInformationFile(handle, - &io_status, - &volume_info, - sizeof volume_info, + len = wcslen(pathw); + + if (len == 0) + fmt = L"./*"; + else if (IS_SLASH(pathw[len - 1])) + fmt = L"%s*"; + else + fmt = L"%s\\*"; + + find_path = uv__malloc(sizeof(WCHAR) * (len + 4)); + if (find_path == NULL) { + SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY); + goto error; + } + + _snwprintf(find_path, len + 3, fmt, pathw); + dir->dir_handle = FindFirstFileW(find_path, &dir->find_data); + uv__free(find_path); + find_path = NULL; + if (dir->dir_handle == INVALID_HANDLE_VALUE && + GetLastError() != ERROR_FILE_NOT_FOUND) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + goto error; + } + + dir->need_find_call = FALSE; + req->ptr = dir; + SET_REQ_RESULT(req, 0); + return; + +error: + uv__free(dir); + uv__free(find_path); + req->ptr = NULL; +} + +void fs__readdir(uv_fs_t* req) { + uv_dir_t* dir; + uv_dirent_t* dirents; + uv__dirent_t dent; + unsigned int dirent_idx; + PWIN32_FIND_DATAW find_data; + unsigned int i; + int r; + + req->flags |= UV_FS_FREE_PTR; + dir = req->ptr; + dirents = dir->dirents; + memset(dirents, 0, dir->nentries * sizeof(*dir->dirents)); + find_data = &dir->find_data; + dirent_idx = 0; + + while (dirent_idx < dir->nentries) { + if (dir->need_find_call && FindNextFileW(dir->dir_handle, find_data) == 0) { + if (GetLastError() == ERROR_NO_MORE_FILES) + break; + goto error; + } + + /* Skip "." and ".." entries. */ + if (find_data->cFileName[0] == L'.' && + (find_data->cFileName[1] == L'\0' || + (find_data->cFileName[1] == L'.' && + find_data->cFileName[2] == L'\0'))) { + dir->need_find_call = TRUE; + continue; + } + + r = uv__convert_utf16_to_utf8((const WCHAR*) &find_data->cFileName, + -1, + (char**) &dirents[dirent_idx].name); + if (r != 0) + goto error; + + /* Copy file type. */ + if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) + dent.d_type = UV__DT_DIR; + else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) + dent.d_type = UV__DT_LINK; + else if ((find_data->dwFileAttributes & FILE_ATTRIBUTE_DEVICE) != 0) + dent.d_type = UV__DT_CHAR; + else + dent.d_type = UV__DT_FILE; + + dirents[dirent_idx].type = uv__fs_get_dirent_type(&dent); + dir->need_find_call = TRUE; + ++dirent_idx; + } + + SET_REQ_RESULT(req, dirent_idx); + return; + +error: + SET_REQ_WIN32_ERROR(req, GetLastError()); + for (i = 0; i < dirent_idx; ++i) { + uv__free((char*) dirents[i].name); + dirents[i].name = NULL; + } +} + +void fs__closedir(uv_fs_t* req) { + uv_dir_t* dir; + + dir = req->ptr; + FindClose(dir->dir_handle); + uv__free(req->ptr); + SET_REQ_RESULT(req, 0); +} + +INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf, + int do_lstat) { + FILE_ALL_INFORMATION file_info; + FILE_FS_VOLUME_INFORMATION volume_info; + NTSTATUS nt_status; + IO_STATUS_BLOCK io_status; + + nt_status = pNtQueryInformationFile(handle, + &io_status, + &file_info, + sizeof file_info, + FileAllInformation); + + /* Buffer overflow (a warning status code) is expected here. */ + if (NT_ERROR(nt_status)) { + SetLastError(pRtlNtStatusToDosError(nt_status)); + return -1; + } + + nt_status = pNtQueryVolumeInformationFile(handle, + &io_status, + &volume_info, + sizeof volume_info, FileFsVolumeInformation); /* Buffer overflow (a warning status code) is expected here. */ @@ -1090,18 +1754,35 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf) { */ statbuf->st_mode = 0; - if (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { - statbuf->st_mode |= S_IFLNK; + /* + * On Windows, FILE_ATTRIBUTE_REPARSE_POINT is a general purpose mechanism + * by which filesystem drivers can intercept and alter file system requests. + * + * The only reparse points we care about are symlinks and mount points, both + * of which are treated as POSIX symlinks. Further, we only care when + * invoked via lstat, which seeks information about the link instead of its + * target. Otherwise, reparse points must be treated as regular files. + */ + if (do_lstat && + (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { + /* + * If reading the link fails, the reparse point is not a symlink and needs + * to be treated as a regular file. The higher level lstat function will + * detect this failure and retry without do_lstat if appropriate. + */ if (fs__readlink_handle(handle, NULL, &statbuf->st_size) != 0) return -1; + statbuf->st_mode |= S_IFLNK; + } - } else if (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - statbuf->st_mode |= _S_IFDIR; - statbuf->st_size = 0; - - } else { - statbuf->st_mode |= _S_IFREG; - statbuf->st_size = file_info.StandardInformation.EndOfFile.QuadPart; + if (statbuf->st_mode == 0) { + if (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + statbuf->st_mode |= _S_IFDIR; + statbuf->st_size = 0; + } else { + statbuf->st_mode |= _S_IFREG; + statbuf->st_size = file_info.StandardInformation.EndOfFile.QuadPart; + } } if (file_info.BasicInformation.FileAttributes & FILE_ATTRIBUTE_READONLY) @@ -1119,7 +1800,7 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf) { /* st_blocks contains the on-disk allocation size in 512-byte units. */ statbuf->st_blocks = - file_info.StandardInformation.AllocationSize.QuadPart >> 9ULL; + (uint64_t) file_info.StandardInformation.AllocationSize.QuadPart >> 9; statbuf->st_nlink = file_info.StandardInformation.NumberOfLinks; @@ -1134,8 +1815,12 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf) { * * Therefore we'll just report a sensible value that's quite commonly okay * on modern hardware. + * + * 4096 is the minimum required to be compatible with newer Advanced Format + * drives (which have 4096 bytes per physical sector), and to be backwards + * compatible with older drives (which have 512 bytes per physical sector). */ - statbuf->st_blksize = 2048; + statbuf->st_blksize = 4096; /* Todo: set st_flags to something meaningful. Also provide a wrapper for * chattr(2). @@ -1165,45 +1850,57 @@ INLINE static void fs__stat_prepare_path(WCHAR* pathw) { } -INLINE static void fs__stat_impl(uv_fs_t* req, int do_lstat) { +INLINE static DWORD fs__stat_impl_from_path(WCHAR* path, + int do_lstat, + uv_stat_t* statbuf) { HANDLE handle; DWORD flags; + DWORD ret; flags = FILE_FLAG_BACKUP_SEMANTICS; - if (do_lstat) { + if (do_lstat) flags |= FILE_FLAG_OPEN_REPARSE_POINT; - } - handle = CreateFileW(req->file.pathw, + handle = CreateFileW(path, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, flags, NULL); - if (handle == INVALID_HANDLE_VALUE) { - SET_REQ_WIN32_ERROR(req, GetLastError()); - return; - } - if (fs__stat_handle(handle, &req->statbuf) != 0) { - DWORD error = GetLastError(); - if (do_lstat && error == ERROR_SYMLINK_NOT_SUPPORTED) { + if (handle == INVALID_HANDLE_VALUE) + ret = GetLastError(); + else if (fs__stat_handle(handle, statbuf, do_lstat) != 0) + ret = GetLastError(); + else + ret = 0; + + CloseHandle(handle); + return ret; +} + + +INLINE static void fs__stat_impl(uv_fs_t* req, int do_lstat) { + DWORD error; + + error = fs__stat_impl_from_path(req->file.pathw, do_lstat, &req->statbuf); + if (error != 0) { + if (do_lstat && + (error == ERROR_SYMLINK_NOT_SUPPORTED || + error == ERROR_NOT_A_REPARSE_POINT)) { /* We opened a reparse point but it was not a symlink. Try again. */ fs__stat_impl(req, 0); - } else { /* Stat failed. */ - SET_REQ_WIN32_ERROR(req, GetLastError()); + SET_REQ_WIN32_ERROR(req, error); } - CloseHandle(handle); return; } req->ptr = &req->statbuf; - req->result = 0; - CloseHandle(handle); + SET_REQ_RESULT(req, 0); } @@ -1232,13 +1929,13 @@ static void fs__fstat(uv_fs_t* req) { return; } - if (fs__stat_handle(handle, &req->statbuf) != 0) { + if (fs__stat_handle(handle, &req->statbuf, 0) != 0) { SET_REQ_WIN32_ERROR(req, GetLastError()); return; } req->ptr = &req->statbuf; - req->result = 0; + SET_REQ_RESULT(req, 0); } @@ -1280,6 +1977,7 @@ static void fs__fdatasync(uv_fs_t* req) { static void fs__ftruncate(uv_fs_t* req) { int fd = req->file.fd; HANDLE handle; + struct uv__fd_info_s fd_info = { 0 }; NTSTATUS status; IO_STATUS_BLOCK io_status; FILE_END_OF_FILE_INFORMATION eof_info; @@ -1288,6 +1986,17 @@ static void fs__ftruncate(uv_fs_t* req) { handle = uv__get_osfhandle(fd); + if (uv__fd_hash_get(fd, &fd_info)) { + if (fd_info.is_directory) { + SET_REQ_WIN32_ERROR(req, ERROR_ACCESS_DENIED); + return; + } + + if (fd_info.mapping != INVALID_HANDLE_VALUE) { + CloseHandle(fd_info.mapping); + } + } + eof_info.EndOfFile.QuadPart = req->fs.info.offset; status = pNtSetInformationFile(handle, @@ -1300,6 +2009,80 @@ static void fs__ftruncate(uv_fs_t* req) { SET_REQ_RESULT(req, 0); } else { SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(status)); + + if (fd_info.flags) { + CloseHandle(handle); + fd_info.mapping = INVALID_HANDLE_VALUE; + fd_info.size.QuadPart = 0; + fd_info.current_pos.QuadPart = 0; + uv__fd_hash_add(fd, &fd_info); + return; + } + } + + if (fd_info.flags) { + fd_info.size = eof_info.EndOfFile; + + if (fd_info.size.QuadPart == 0) { + fd_info.mapping = INVALID_HANDLE_VALUE; + } else { + DWORD flProtect = (fd_info.flags & (UV_FS_O_RDONLY | UV_FS_O_WRONLY | + UV_FS_O_RDWR)) == UV_FS_O_RDONLY ? PAGE_READONLY : PAGE_READWRITE; + fd_info.mapping = CreateFileMapping(handle, + NULL, + flProtect, + fd_info.size.HighPart, + fd_info.size.LowPart, + NULL); + if (fd_info.mapping == NULL) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + CloseHandle(handle); + fd_info.mapping = INVALID_HANDLE_VALUE; + fd_info.size.QuadPart = 0; + fd_info.current_pos.QuadPart = 0; + uv__fd_hash_add(fd, &fd_info); + return; + } + } + + uv__fd_hash_add(fd, &fd_info); + } +} + + +static void fs__copyfile(uv_fs_t* req) { + int flags; + int overwrite; + uv_stat_t statbuf; + uv_stat_t new_statbuf; + + flags = req->fs.info.file_flags; + + if (flags & UV_FS_COPYFILE_FICLONE_FORCE) { + SET_REQ_UV_ERROR(req, UV_ENOSYS, ERROR_NOT_SUPPORTED); + return; + } + + overwrite = flags & UV_FS_COPYFILE_EXCL; + + if (CopyFileW(req->file.pathw, req->fs.info.new_pathw, overwrite) != 0) { + SET_REQ_RESULT(req, 0); + return; + } + + SET_REQ_WIN32_ERROR(req, GetLastError()); + if (req->result != UV_EBUSY) + return; + + /* if error UV_EBUSY check if src and dst file are the same */ + if (fs__stat_impl_from_path(req->file.pathw, 0, &statbuf) != 0 || + fs__stat_impl_from_path(req->fs.info.new_pathw, 0, &new_statbuf) != 0) { + return; + } + + if (statbuf.st_dev == new_statbuf.st_dev && + statbuf.st_ino == new_statbuf.st_ino) { + SET_REQ_RESULT(req, 0); } } @@ -1366,7 +2149,7 @@ static void fs__access(uv_fs_t* req) { * - or it's a directory. * (Directories cannot be read-only on Windows.) */ - if (!(req->flags & W_OK) || + if (!(req->fs.info.mode & W_OK) || !(attr & FILE_ATTRIBUTE_READONLY) || (attr & FILE_ATTRIBUTE_DIRECTORY)) { SET_REQ_RESULT(req, 0); @@ -1379,12 +2162,16 @@ static void fs__access(uv_fs_t* req) { static void fs__chmod(uv_fs_t* req) { int result = _wchmod(req->file.pathw, req->fs.info.mode); - SET_REQ_RESULT(req, result); + if (result == -1) + SET_REQ_WIN32_ERROR(req, _doserrno); + else + SET_REQ_RESULT(req, 0); } static void fs__fchmod(uv_fs_t* req) { int fd = req->file.fd; + int clear_archive_flag; HANDLE handle; NTSTATUS nt_status; IO_STATUS_BLOCK io_status; @@ -1392,7 +2179,11 @@ static void fs__fchmod(uv_fs_t* req) { VERIFY_FD(fd, req); - handle = uv__get_osfhandle(fd); + handle = ReOpenFile(uv__get_osfhandle(fd), FILE_WRITE_ATTRIBUTES, 0, 0); + if (handle == INVALID_HANDLE_VALUE) { + SET_REQ_WIN32_ERROR(req, GetLastError()); + return; + } nt_status = pNtQueryInformationFile(handle, &io_status, @@ -1402,7 +2193,27 @@ static void fs__fchmod(uv_fs_t* req) { if (!NT_SUCCESS(nt_status)) { SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status)); - return; + goto fchmod_cleanup; + } + + /* Test if the Archive attribute is cleared */ + if ((file_info.FileAttributes & FILE_ATTRIBUTE_ARCHIVE) == 0) { + /* Set Archive flag, otherwise setting or clearing the read-only + flag will not work */ + file_info.FileAttributes |= FILE_ATTRIBUTE_ARCHIVE; + nt_status = pNtSetInformationFile(handle, + &io_status, + &file_info, + sizeof file_info, + FileBasicInformation); + if (!NT_SUCCESS(nt_status)) { + SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status)); + goto fchmod_cleanup; + } + /* Remeber to clear the flag later on */ + clear_archive_flag = 1; + } else { + clear_archive_flag = 0; } if (req->fs.info.mode & _S_IWRITE) { @@ -1419,18 +2230,36 @@ static void fs__fchmod(uv_fs_t* req) { if (!NT_SUCCESS(nt_status)) { SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status)); - return; + goto fchmod_cleanup; + } + + if (clear_archive_flag) { + file_info.FileAttributes &= ~FILE_ATTRIBUTE_ARCHIVE; + if (file_info.FileAttributes == 0) { + file_info.FileAttributes = FILE_ATTRIBUTE_NORMAL; + } + nt_status = pNtSetInformationFile(handle, + &io_status, + &file_info, + sizeof file_info, + FileBasicInformation); + if (!NT_SUCCESS(nt_status)) { + SET_REQ_WIN32_ERROR(req, pRtlNtStatusToDosError(nt_status)); + goto fchmod_cleanup; + } } SET_REQ_SUCCESS(req); +fchmod_cleanup: + CloseHandle(handle); } INLINE static int fs__utime_handle(HANDLE handle, double atime, double mtime) { FILETIME filetime_a, filetime_m; - TIME_T_TO_FILETIME((time_t) atime, &filetime_a); - TIME_T_TO_FILETIME((time_t) mtime, &filetime_m); + TIME_T_TO_FILETIME(atime, &filetime_a); + TIME_T_TO_FILETIME(mtime, &filetime_m); if (!SetFileTime(handle, NULL, &filetime_a, &filetime_m)) { return -1; @@ -1439,32 +2268,66 @@ INLINE static int fs__utime_handle(HANDLE handle, double atime, double mtime) { return 0; } - -static void fs__utime(uv_fs_t* req) { +INLINE static DWORD fs__utime_impl_from_path(WCHAR* path, + double atime, + double mtime, + int do_lutime) { HANDLE handle; + DWORD flags; + DWORD ret; - handle = CreateFileW(req->file.pathw, + flags = FILE_FLAG_BACKUP_SEMANTICS; + if (do_lutime) { + flags |= FILE_FLAG_OPEN_REPARSE_POINT; + } + + handle = CreateFileW(path, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, + flags, NULL); if (handle == INVALID_HANDLE_VALUE) { - SET_REQ_WIN32_ERROR(req, GetLastError()); - return; + ret = GetLastError(); + } else if (fs__utime_handle(handle, atime, mtime) != 0) { + ret = GetLastError(); + } else { + ret = 0; } - if (fs__utime_handle(handle, req->fs.time.atime, req->fs.time.mtime) != 0) { - SET_REQ_WIN32_ERROR(req, GetLastError()); - CloseHandle(handle); + CloseHandle(handle); + return ret; +} + +INLINE static void fs__utime_impl(uv_fs_t* req, int do_lutime) { + DWORD error; + + error = fs__utime_impl_from_path(req->file.pathw, + req->fs.time.atime, + req->fs.time.mtime, + do_lutime); + + if (error != 0) { + if (do_lutime && + (error == ERROR_SYMLINK_NOT_SUPPORTED || + error == ERROR_NOT_A_REPARSE_POINT)) { + /* Opened file is a reparse point but not a symlink. Try again. */ + fs__utime_impl(req, 0); + } else { + /* utime failed. */ + SET_REQ_WIN32_ERROR(req, error); + } + return; } - CloseHandle(handle); + SET_REQ_RESULT(req, 0); +} - req->result = 0; +static void fs__utime(uv_fs_t* req) { + fs__utime_impl(req, /* do_lutime */ 0); } @@ -1485,17 +2348,20 @@ static void fs__futime(uv_fs_t* req) { return; } - req->result = 0; + SET_REQ_RESULT(req, 0); +} + +static void fs__lutime(uv_fs_t* req) { + fs__utime_impl(req, /* do_lutime */ 1); } static void fs__link(uv_fs_t* req) { DWORD r = CreateHardLinkW(req->fs.info.new_pathw, req->file.pathw, NULL); - if (r == 0) { + if (r == 0) SET_REQ_WIN32_ERROR(req, GetLastError()); - } else { - req->result = 0; - } + else + SET_REQ_RESULT(req, 0); } @@ -1670,25 +2536,42 @@ static void fs__create_junction(uv_fs_t* req, const WCHAR* path, static void fs__symlink(uv_fs_t* req) { - WCHAR* pathw = req->file.pathw; - WCHAR* new_pathw = req->fs.info.new_pathw; - int flags = req->fs.info.file_flags; - int result; + WCHAR* pathw; + WCHAR* new_pathw; + int flags; + int err; + pathw = req->file.pathw; + new_pathw = req->fs.info.new_pathw; - if (flags & UV_FS_SYMLINK_JUNCTION) { + if (req->fs.info.file_flags & UV_FS_SYMLINK_JUNCTION) { fs__create_junction(req, pathw, new_pathw); - } else if (pCreateSymbolicLinkW) { - result = pCreateSymbolicLinkW(new_pathw, - pathw, - flags & UV_FS_SYMLINK_DIR ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0) ? 0 : -1; - if (result == -1) { - SET_REQ_WIN32_ERROR(req, GetLastError()); - } else { - SET_REQ_RESULT(req, result); - } + return; + } + + if (req->fs.info.file_flags & UV_FS_SYMLINK_DIR) + flags = SYMBOLIC_LINK_FLAG_DIRECTORY | uv__file_symlink_usermode_flag; + else + flags = uv__file_symlink_usermode_flag; + + if (CreateSymbolicLinkW(new_pathw, pathw, flags)) { + SET_REQ_RESULT(req, 0); + return; + } + + /* Something went wrong. We will test if it is because of user-mode + * symlinks. + */ + err = GetLastError(); + if (err == ERROR_INVALID_PARAMETER && + flags & SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) { + /* This system does not support user-mode symlinks. We will clear the + * unsupported flag and retry. + */ + uv__file_symlink_usermode_flag = 0; + fs__symlink(req); } else { - SET_REQ_UV_ERROR(req, UV_ENOSYS, ERROR_NOT_SUPPORTED); + SET_REQ_WIN32_ERROR(req, err); } } @@ -1722,13 +2605,13 @@ static void fs__readlink(uv_fs_t* req) { } -static size_t fs__realpath_handle(HANDLE handle, char** realpath_ptr) { +static ssize_t fs__realpath_handle(HANDLE handle, char** realpath_ptr) { int r; DWORD w_realpath_len; WCHAR* w_realpath_ptr = NULL; WCHAR* w_realpath_buf; - w_realpath_len = pGetFinalPathNameByHandleW(handle, NULL, 0, VOLUME_NAME_DOS); + w_realpath_len = GetFinalPathNameByHandleW(handle, NULL, 0, VOLUME_NAME_DOS); if (w_realpath_len == 0) { return -1; } @@ -1740,10 +2623,8 @@ static size_t fs__realpath_handle(HANDLE handle, char** realpath_ptr) { } w_realpath_ptr = w_realpath_buf; - if (pGetFinalPathNameByHandleW(handle, - w_realpath_ptr, - w_realpath_len, - VOLUME_NAME_DOS) == 0) { + if (GetFinalPathNameByHandleW( + handle, w_realpath_ptr, w_realpath_len, VOLUME_NAME_DOS) == 0) { uv__free(w_realpath_buf); SetLastError(ERROR_INVALID_HANDLE); return -1; @@ -1775,11 +2656,6 @@ static size_t fs__realpath_handle(HANDLE handle, char** realpath_ptr) { static void fs__realpath(uv_fs_t* req) { HANDLE handle; - if (!pGetFinalPathNameByHandleW) { - SET_REQ_UV_ERROR(req, UV_ENOSYS, ERROR_NOT_SUPPORTED); - return; - } - handle = CreateFileW(req->file.pathw, 0, 0, @@ -1805,12 +2681,100 @@ static void fs__realpath(uv_fs_t* req) { static void fs__chown(uv_fs_t* req) { - req->result = 0; + SET_REQ_RESULT(req, 0); } static void fs__fchown(uv_fs_t* req) { - req->result = 0; + SET_REQ_RESULT(req, 0); +} + + +static void fs__lchown(uv_fs_t* req) { + SET_REQ_RESULT(req, 0); +} + + +static void fs__statfs(uv_fs_t* req) { + uv_statfs_t* stat_fs; + DWORD sectors_per_cluster; + DWORD bytes_per_sector; + DWORD free_clusters; + DWORD total_clusters; + WCHAR* pathw; + + pathw = req->file.pathw; +retry_get_disk_free_space: + if (0 == GetDiskFreeSpaceW(pathw, + §ors_per_cluster, + &bytes_per_sector, + &free_clusters, + &total_clusters)) { + DWORD err; + WCHAR* fpart; + size_t len; + DWORD ret; + BOOL is_second; + + err = GetLastError(); + is_second = pathw != req->file.pathw; + if (err != ERROR_DIRECTORY || is_second) { + if (is_second) + uv__free(pathw); + + SET_REQ_WIN32_ERROR(req, err); + return; + } + + len = MAX_PATH + 1; + pathw = uv__malloc(len * sizeof(*pathw)); + if (pathw == NULL) { + SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY); + return; + } +retry_get_full_path_name: + ret = GetFullPathNameW(req->file.pathw, + len, + pathw, + &fpart); + if (ret == 0) { + uv__free(pathw); + SET_REQ_WIN32_ERROR(req, err); + return; + } else if (ret > len) { + len = ret; + pathw = uv__reallocf(pathw, len * sizeof(*pathw)); + if (pathw == NULL) { + SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY); + return; + } + goto retry_get_full_path_name; + } + if (fpart != 0) + *fpart = L'\0'; + + goto retry_get_disk_free_space; + } + if (pathw != req->file.pathw) { + uv__free(pathw); + } + + stat_fs = uv__malloc(sizeof(*stat_fs)); + if (stat_fs == NULL) { + SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY); + return; + } + + stat_fs->f_type = 0; + stat_fs->f_bsize = bytes_per_sector * sectors_per_cluster; + stat_fs->f_blocks = total_clusters; + stat_fs->f_bfree = free_clusters; + stat_fs->f_bavail = free_clusters; + stat_fs->f_files = 0; + stat_fs->f_ffree = 0; + req->ptr = stat_fs; + req->flags |= UV_FS_FREE_PTR; + SET_REQ_RESULT(req, 0); } @@ -1826,6 +2790,7 @@ static void uv__fs_work(struct uv__work* w) { XX(CLOSE, close) XX(READ, read) XX(WRITE, write) + XX(COPYFILE, copyfile) XX(SENDFILE, sendfile) XX(STAT, stat) XX(LSTAT, lstat) @@ -1833,6 +2798,7 @@ static void uv__fs_work(struct uv__work* w) { XX(FTRUNCATE, ftruncate) XX(UTIME, utime) XX(FUTIME, futime) + XX(LUTIME, lutime) XX(ACCESS, access) XX(CHMOD, chmod) XX(FCHMOD, fchmod) @@ -1842,14 +2808,20 @@ static void uv__fs_work(struct uv__work* w) { XX(RMDIR, rmdir) XX(MKDIR, mkdir) XX(MKDTEMP, mkdtemp) + XX(MKSTEMP, mkstemp) XX(RENAME, rename) XX(SCANDIR, scandir) + XX(READDIR, readdir) + XX(OPENDIR, opendir) + XX(CLOSEDIR, closedir) XX(LINK, link) XX(SYMLINK, symlink) XX(READLINK, readlink) XX(REALPATH, realpath) XX(CHOWN, chown) - XX(FCHOWN, fchown); + XX(FCHOWN, fchown) + XX(LCHOWN, lchown) + XX(STATFS, statfs) default: assert(!"bad uv_fs_type"); } @@ -1864,7 +2836,7 @@ static void uv__fs_done(struct uv__work* w, int status) { if (status == UV_ECANCELED) { assert(req->result == 0); - req->result = UV_ECANCELED; + SET_REQ_UV_ERROR(req, UV_ECANCELED, 0); } req->cb(req); @@ -1872,6 +2844,9 @@ static void uv__fs_done(struct uv__work* w, int status) { void uv_fs_req_cleanup(uv_fs_t* req) { + if (req == NULL) + return; + if (req->flags & UV_FS_CLEANEDUP) return; @@ -1881,13 +2856,19 @@ void uv_fs_req_cleanup(uv_fs_t* req) { if (req->flags & UV_FS_FREE_PTR) { if (req->fs_type == UV_FS_SCANDIR && req->ptr != NULL) uv__fs_scandir_cleanup(req); + else if (req->fs_type == UV_FS_READDIR) + uv__fs_readdir_cleanup(req); else uv__free(req->ptr); } + if (req->fs.info.bufs != req->fs.info.bufsml) + uv__free(req->fs.info.bufs); + req->path = NULL; req->file.pathw = NULL; req->fs.info.new_pathw = NULL; + req->fs.info.bufs = NULL; req->ptr = NULL; req->flags |= UV_FS_CLEANEDUP; @@ -1898,37 +2879,23 @@ int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_OPEN, cb); - + INIT(UV_FS_OPEN); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } req->fs.info.file_flags = flags; req->fs.info.mode = mode; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__open(req); - return req->result; - } + POST; } int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) { - uv_fs_req_init(loop, req, UV_FS_CLOSE, cb); + INIT(UV_FS_CLOSE); req->file.fd = fd; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__close(req); - return req->result; - } + POST; } @@ -1939,10 +2906,12 @@ int uv_fs_read(uv_loop_t* loop, unsigned int nbufs, int64_t offset, uv_fs_cb cb) { - if (bufs == NULL || nbufs == 0) - return UV_EINVAL; + INIT(UV_FS_READ); - uv_fs_req_init(loop, req, UV_FS_READ, cb); + if (bufs == NULL || nbufs == 0) { + SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); + return UV_EINVAL; + } req->file.fd = fd; @@ -1951,20 +2920,15 @@ int uv_fs_read(uv_loop_t* loop, if (nbufs > ARRAY_SIZE(req->fs.info.bufsml)) req->fs.info.bufs = uv__malloc(nbufs * sizeof(*bufs)); - if (req->fs.info.bufs == NULL) + if (req->fs.info.bufs == NULL) { + SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY); return UV_ENOMEM; + } memcpy(req->fs.info.bufs, bufs, nbufs * sizeof(*bufs)); req->fs.info.offset = offset; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__read(req); - return req->result; - } + POST; } @@ -1975,10 +2939,12 @@ int uv_fs_write(uv_loop_t* loop, unsigned int nbufs, int64_t offset, uv_fs_cb cb) { - if (bufs == NULL || nbufs == 0) - return UV_EINVAL; + INIT(UV_FS_WRITE); - uv_fs_req_init(loop, req, UV_FS_WRITE, cb); + if (bufs == NULL || nbufs == 0) { + SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); + return UV_EINVAL; + } req->file.fd = fd; @@ -1987,20 +2953,15 @@ int uv_fs_write(uv_loop_t* loop, if (nbufs > ARRAY_SIZE(req->fs.info.bufsml)) req->fs.info.bufs = uv__malloc(nbufs * sizeof(*bufs)); - if (req->fs.info.bufs == NULL) + if (req->fs.info.bufs == NULL) { + SET_REQ_UV_ERROR(req, UV_ENOMEM, ERROR_OUTOFMEMORY); return UV_ENOMEM; + } memcpy(req->fs.info.bufs, bufs, nbufs * sizeof(*bufs)); req->fs.info.offset = offset; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__write(req); - return req->result; - } + POST; } @@ -2008,20 +2969,14 @@ int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_UNLINK, cb); - + INIT(UV_FS_UNLINK); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); - } - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__unlink(req); + SET_REQ_WIN32_ERROR(req, err); return req->result; } + + POST; } @@ -2029,62 +2984,63 @@ int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_MKDIR, cb); - + INIT(UV_FS_MKDIR); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } req->fs.info.mode = mode; + POST; +} - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__mkdir(req); + +int uv_fs_mkdtemp(uv_loop_t* loop, + uv_fs_t* req, + const char* tpl, + uv_fs_cb cb) { + int err; + + INIT(UV_FS_MKDTEMP); + err = fs__capture_path(req, tpl, NULL, TRUE); + if (err) { + SET_REQ_WIN32_ERROR(req, err); return req->result; } + + POST; } -int uv_fs_mkdtemp(uv_loop_t* loop, uv_fs_t* req, const char* tpl, - uv_fs_cb cb) { +int uv_fs_mkstemp(uv_loop_t* loop, + uv_fs_t* req, + const char* tpl, + uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_MKDTEMP, cb); - + INIT(UV_FS_MKSTEMP); err = fs__capture_path(req, tpl, NULL, TRUE); - if (err) - return uv_translate_sys_error(err); - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__mkdtemp(req); + if (err) { + SET_REQ_WIN32_ERROR(req, err); return req->result; } + + POST; } int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_RMDIR, cb); - + INIT(UV_FS_RMDIR); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); - } - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__rmdir(req); + SET_REQ_WIN32_ERROR(req, err); return req->result; } + + POST; } @@ -2092,43 +3048,74 @@ int uv_fs_scandir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_SCANDIR, cb); - + INIT(UV_FS_SCANDIR); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } req->fs.info.file_flags = flags; + POST; +} - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__scandir(req); +int uv_fs_opendir(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + uv_fs_cb cb) { + int err; + + INIT(UV_FS_OPENDIR); + err = fs__capture_path(req, path, NULL, cb != NULL); + if (err) { + SET_REQ_WIN32_ERROR(req, err); return req->result; } + POST; } +int uv_fs_readdir(uv_loop_t* loop, + uv_fs_t* req, + uv_dir_t* dir, + uv_fs_cb cb) { + INIT(UV_FS_READDIR); + + if (dir == NULL || + dir->dirents == NULL || + dir->dir_handle == INVALID_HANDLE_VALUE) { + SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); + return UV_EINVAL; + } + + req->ptr = dir; + POST; +} + +int uv_fs_closedir(uv_loop_t* loop, + uv_fs_t* req, + uv_dir_t* dir, + uv_fs_cb cb) { + INIT(UV_FS_CLOSEDIR); + if (dir == NULL) { + SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); + return UV_EINVAL; + } + req->ptr = dir; + POST; +} int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_LINK, cb); - + INIT(UV_FS_LINK); err = fs__capture_path(req, path, new_path, cb != NULL); if (err) { - return uv_translate_sys_error(err); - } - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__link(req); + SET_REQ_WIN32_ERROR(req, err); return req->result; } + + POST; } @@ -2136,22 +3123,15 @@ int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_SYMLINK, cb); - + INIT(UV_FS_SYMLINK); err = fs__capture_path(req, path, new_path, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } req->fs.info.file_flags = flags; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__symlink(req); - return req->result; - } + POST; } @@ -2159,20 +3139,14 @@ int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_READLINK, cb); - + INIT(UV_FS_READLINK); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); - } - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__readlink(req); + SET_REQ_WIN32_ERROR(req, err); return req->result; } + + POST; } @@ -2180,24 +3154,20 @@ int uv_fs_realpath(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { int err; - if (!req || !path) { + INIT(UV_FS_REALPATH); + + if (!path) { + SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); return UV_EINVAL; } - uv_fs_req_init(loop, req, UV_FS_REALPATH, cb); - err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); - } - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__realpath(req); + SET_REQ_WIN32_ERROR(req, err); return req->result; } + + POST; } @@ -2205,88 +3175,71 @@ int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_CHOWN, cb); - + INIT(UV_FS_CHOWN); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); - } - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__chown(req); + SET_REQ_WIN32_ERROR(req, err); return req->result; } + + POST; } int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb) { - uv_fs_req_init(loop, req, UV_FS_FCHOWN, cb); + INIT(UV_FS_FCHOWN); + POST; +} - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__fchown(req); + +int uv_fs_lchown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, + uv_gid_t gid, uv_fs_cb cb) { + int err; + + INIT(UV_FS_LCHOWN); + err = fs__capture_path(req, path, NULL, cb != NULL); + if (err) { + SET_REQ_WIN32_ERROR(req, err); return req->result; } + + POST; } int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_STAT, cb); - + INIT(UV_FS_STAT); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); - } - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__stat(req); + SET_REQ_WIN32_ERROR(req, err); return req->result; } + + POST; } int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_LSTAT, cb); - + INIT(UV_FS_LSTAT); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); - } - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__lstat(req); + SET_REQ_WIN32_ERROR(req, err); return req->result; } + + POST; } int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) { - uv_fs_req_init(loop, req, UV_FS_FSTAT, cb); + INIT(UV_FS_FSTAT); req->file.fd = fd; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__fstat(req); - return req->result; - } + POST; } @@ -2294,85 +3247,76 @@ int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_RENAME, cb); - + INIT(UV_FS_RENAME); err = fs__capture_path(req, path, new_path, cb != NULL); if (err) { - return uv_translate_sys_error(err); - } - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__rename(req); + SET_REQ_WIN32_ERROR(req, err); return req->result; } + + POST; } int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) { - uv_fs_req_init(loop, req, UV_FS_FSYNC, cb); + INIT(UV_FS_FSYNC); req->file.fd = fd; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__fsync(req); - return req->result; - } + POST; } int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file fd, uv_fs_cb cb) { - uv_fs_req_init(loop, req, UV_FS_FDATASYNC, cb); + INIT(UV_FS_FDATASYNC); req->file.fd = fd; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__fdatasync(req); - return req->result; - } + POST; } int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file fd, int64_t offset, uv_fs_cb cb) { - uv_fs_req_init(loop, req, UV_FS_FTRUNCATE, cb); - + INIT(UV_FS_FTRUNCATE); req->file.fd = fd; req->fs.info.offset = offset; + POST; +} - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__ftruncate(req); + +int uv_fs_copyfile(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + const char* new_path, + int flags, + uv_fs_cb cb) { + int err; + + INIT(UV_FS_COPYFILE); + + if (flags & ~(UV_FS_COPYFILE_EXCL | + UV_FS_COPYFILE_FICLONE | + UV_FS_COPYFILE_FICLONE_FORCE)) { + SET_REQ_UV_ERROR(req, UV_EINVAL, ERROR_INVALID_PARAMETER); + return UV_EINVAL; + } + + err = fs__capture_path(req, path, new_path, cb != NULL); + if (err) { + SET_REQ_WIN32_ERROR(req, err); return req->result; } -} + req->fs.info.file_flags = flags; + POST; +} int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file fd_out, uv_file fd_in, int64_t in_offset, size_t length, uv_fs_cb cb) { - uv_fs_req_init(loop, req, UV_FS_SENDFILE, cb); - + INIT(UV_FS_SENDFILE); req->file.fd = fd_in; req->fs.info.fd_out = fd_out; req->fs.info.offset = in_offset; req->fs.info.bufsml[0].len = length; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__sendfile(req); - return req->result; - } + POST; } @@ -2383,21 +3327,15 @@ int uv_fs_access(uv_loop_t* loop, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_ACCESS, cb); - + INIT(UV_FS_ACCESS); err = fs__capture_path(req, path, NULL, cb != NULL); - if (err) - return uv_translate_sys_error(err); - - req->flags = flags; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; + if (err) { + SET_REQ_WIN32_ERROR(req, err); + return req->result; } - fs__access(req); - return req->result; + req->fs.info.mode = flags; + POST; } @@ -2405,39 +3343,24 @@ int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_CHMOD, cb); - + INIT(UV_FS_CHMOD); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } req->fs.info.mode = mode; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__chmod(req); - return req->result; - } + POST; } int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file fd, int mode, uv_fs_cb cb) { - uv_fs_req_init(loop, req, UV_FS_FCHMOD, cb); - + INIT(UV_FS_FCHMOD); req->file.fd = fd; req->fs.info.mode = mode; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__fchmod(req); - return req->result; - } + POST; } @@ -2445,39 +3368,61 @@ int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, uv_fs_cb cb) { int err; - uv_fs_req_init(loop, req, UV_FS_UTIME, cb); - + INIT(UV_FS_UTIME); err = fs__capture_path(req, path, NULL, cb != NULL); if (err) { - return uv_translate_sys_error(err); + SET_REQ_WIN32_ERROR(req, err); + return req->result; } req->fs.time.atime = atime; req->fs.time.mtime = mtime; - - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__utime(req); - return req->result; - } + POST; } int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file fd, double atime, double mtime, uv_fs_cb cb) { - uv_fs_req_init(loop, req, UV_FS_FUTIME, cb); - + INIT(UV_FS_FUTIME); req->file.fd = fd; req->fs.time.atime = atime; req->fs.time.mtime = mtime; + POST; +} - if (cb) { - QUEUE_FS_TP_JOB(loop, req); - return 0; - } else { - fs__futime(req); +int uv_fs_lutime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, + double mtime, uv_fs_cb cb) { + int err; + + INIT(UV_FS_LUTIME); + err = fs__capture_path(req, path, NULL, cb != NULL); + if (err) { + SET_REQ_WIN32_ERROR(req, err); + return req->result; + } + + req->fs.time.atime = atime; + req->fs.time.mtime = mtime; + POST; +} + + +int uv_fs_statfs(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + uv_fs_cb cb) { + int err; + + INIT(UV_FS_STATFS); + err = fs__capture_path(req, path, NULL, cb != NULL); + if (err) { + SET_REQ_WIN32_ERROR(req, err); return req->result; } + + POST; +} + +int uv_fs_get_system_error(const uv_fs_t* req) { + return req->sys_errno_; } diff --git a/include/libuv/src/win/getaddrinfo.c b/include/libuv/src/win/getaddrinfo.c index 744f8e026..dfab860a7 100644 --- a/include/libuv/src/win/getaddrinfo.c +++ b/include/libuv/src/win/getaddrinfo.c @@ -24,10 +24,13 @@ #include "uv.h" #include "internal.h" #include "req-inl.h" +#include "idna.h" /* EAI_* constants. */ #include +/* Needed for ConvertInterfaceIndexToLuid and ConvertInterfaceLuidToNameA */ +#include int uv__getaddrinfo_translate_error(int sys_err) { switch (sys_err) { @@ -69,10 +72,13 @@ int uv__getaddrinfo_translate_error(int sys_err) { #endif -/* adjust size value to be multiple of 4. Use to keep pointer aligned */ -/* Do we need different versions of this for different architectures? */ +/* Adjust size value to be multiple of 4. Use to keep pointer aligned. + * Do we need different versions of this for different architectures? */ #define ALIGNED_SIZE(X) ((((X) + 3) >> 2) << 2) +#ifndef NDIS_IF_MAX_STRING_SIZE +#define NDIS_IF_MAX_STRING_SIZE IF_MAX_STRING_SIZE +#endif static void uv__getaddrinfo_work(struct uv__work* w) { uv_getaddrinfo_t* req; @@ -119,8 +125,7 @@ static void uv__getaddrinfo_done(struct uv__work* w, int status) { } if (req->retcode == 0) { - /* convert addrinfoW to addrinfo */ - /* first calculate required length */ + /* Convert addrinfoW to addrinfo. First calculate required length. */ addrinfow_ptr = req->addrinfow; while (addrinfow_ptr != NULL) { addrinfo_len += addrinfo_struct_len + @@ -255,33 +260,39 @@ int uv_getaddrinfo(uv_loop_t* loop, const char* node, const char* service, const struct addrinfo* hints) { + char hostname_ascii[256]; int nodesize = 0; int servicesize = 0; int hintssize = 0; char* alloc_ptr = NULL; int err; + long rc; if (req == NULL || (node == NULL && service == NULL)) { - err = WSAEINVAL; - goto error; + return UV_EINVAL; } - uv_req_init(loop, (uv_req_t*)req); - + UV_REQ_INIT(req, UV_GETADDRINFO); req->getaddrinfo_cb = getaddrinfo_cb; req->addrinfo = NULL; - req->type = UV_GETADDRINFO; req->loop = loop; req->retcode = 0; /* calculate required memory size for all input values */ if (node != NULL) { - nodesize = ALIGNED_SIZE(MultiByteToWideChar(CP_UTF8, 0, node, -1, NULL, 0) * - sizeof(WCHAR)); + rc = uv__idna_toascii(node, + node + strlen(node), + hostname_ascii, + hostname_ascii + sizeof(hostname_ascii)); + if (rc < 0) + return rc; + nodesize = ALIGNED_SIZE(MultiByteToWideChar(CP_UTF8, 0, hostname_ascii, + -1, NULL, 0) * sizeof(WCHAR)); if (nodesize == 0) { err = GetLastError(); goto error; } + node = hostname_ascii; } if (service != NULL) { @@ -311,8 +322,8 @@ int uv_getaddrinfo(uv_loop_t* loop, /* save alloc_ptr now so we can free if error */ req->alloc = (void*)alloc_ptr; - /* convert node string to UTF16 into allocated memory and save pointer in */ - /* the request. */ + /* Convert node string to UTF16 into allocated memory and save pointer in the + * request. */ if (node != NULL) { req->node = (WCHAR*)alloc_ptr; if (MultiByteToWideChar(CP_UTF8, @@ -329,8 +340,8 @@ int uv_getaddrinfo(uv_loop_t* loop, req->node = NULL; } - /* convert service string to UTF16 into allocated memory and save pointer */ - /* in the req. */ + /* Convert service string to UTF16 into allocated memory and save pointer in + * the req. */ if (service != NULL) { req->service = (WCHAR*)alloc_ptr; if (MultiByteToWideChar(CP_UTF8, @@ -367,6 +378,7 @@ int uv_getaddrinfo(uv_loop_t* loop, if (getaddrinfo_cb) { uv__work_submit(loop, &req->work_req, + UV__WORK_SLOW_IO, uv__getaddrinfo_work, uv__getaddrinfo_done); return 0; @@ -383,3 +395,69 @@ int uv_getaddrinfo(uv_loop_t* loop, } return uv_translate_sys_error(err); } + +int uv_if_indextoname(unsigned int ifindex, char* buffer, size_t* size) { + NET_LUID luid; + wchar_t wname[NDIS_IF_MAX_STRING_SIZE + 1]; /* Add one for the NUL. */ + DWORD bufsize; + int r; + + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + + r = ConvertInterfaceIndexToLuid(ifindex, &luid); + + if (r != 0) + return uv_translate_sys_error(r); + + r = ConvertInterfaceLuidToNameW(&luid, wname, ARRAY_SIZE(wname)); + + if (r != 0) + return uv_translate_sys_error(r); + + /* Check how much space we need */ + bufsize = WideCharToMultiByte(CP_UTF8, 0, wname, -1, NULL, 0, NULL, NULL); + + if (bufsize == 0) { + return uv_translate_sys_error(GetLastError()); + } else if (bufsize > *size) { + *size = bufsize; + return UV_ENOBUFS; + } + + /* Convert to UTF-8 */ + bufsize = WideCharToMultiByte(CP_UTF8, + 0, + wname, + -1, + buffer, + *size, + NULL, + NULL); + + if (bufsize == 0) + return uv_translate_sys_error(GetLastError()); + + *size = bufsize - 1; + return 0; +} + +int uv_if_indextoiid(unsigned int ifindex, char* buffer, size_t* size) { + int r; + + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + + r = snprintf(buffer, *size, "%d", ifindex); + + if (r < 0) + return uv_translate_sys_error(r); + + if (r >= (int) *size) { + *size = r + 1; + return UV_ENOBUFS; + } + + *size = r; + return 0; +} diff --git a/include/libuv/src/win/getnameinfo.c b/include/libuv/src/win/getnameinfo.c index 66b64b883..b3773380c 100644 --- a/include/libuv/src/win/getnameinfo.c +++ b/include/libuv/src/win/getnameinfo.c @@ -42,7 +42,7 @@ static void uv__getnameinfo_work(struct uv__work* w) { uv_getnameinfo_t* req; WCHAR host[NI_MAXHOST]; WCHAR service[NI_MAXSERV]; - int ret = 0; + int ret; req = container_of(w, uv_getnameinfo_t, work_req); if (GetNameInfoW((struct sockaddr*)&req->storage, @@ -53,27 +53,34 @@ static void uv__getnameinfo_work(struct uv__work* w) { ARRAY_SIZE(service), req->flags)) { ret = WSAGetLastError(); + req->retcode = uv__getaddrinfo_translate_error(ret); + return; + } + + ret = WideCharToMultiByte(CP_UTF8, + 0, + host, + -1, + req->host, + sizeof(req->host), + NULL, + NULL); + if (ret == 0) { + req->retcode = uv_translate_sys_error(GetLastError()); + return; + } + + ret = WideCharToMultiByte(CP_UTF8, + 0, + service, + -1, + req->service, + sizeof(req->service), + NULL, + NULL); + if (ret == 0) { + req->retcode = uv_translate_sys_error(GetLastError()); } - req->retcode = uv__getaddrinfo_translate_error(ret); - - /* convert results to UTF-8 */ - WideCharToMultiByte(CP_UTF8, - 0, - host, - -1, - req->host, - sizeof(req->host), - NULL, - NULL); - - WideCharToMultiByte(CP_UTF8, - 0, - service, - -1, - req->service, - sizeof(req->service), - NULL, - NULL); } @@ -127,18 +134,18 @@ int uv_getnameinfo(uv_loop_t* loop, return UV_EINVAL; } - uv_req_init(loop, (uv_req_t*)req); + UV_REQ_INIT(req, UV_GETNAMEINFO); uv__req_register(loop, req); req->getnameinfo_cb = getnameinfo_cb; req->flags = flags; - req->type = UV_GETNAMEINFO; req->loop = loop; req->retcode = 0; if (getnameinfo_cb) { uv__work_submit(loop, &req->work_req, + UV__WORK_SLOW_IO, uv__getnameinfo_work, uv__getnameinfo_done); return 0; diff --git a/include/libuv/src/win/handle-inl.h b/include/libuv/src/win/handle-inl.h index 8d0334cc5..82c657d57 100644 --- a/include/libuv/src/win/handle-inl.h +++ b/include/libuv/src/win/handle-inl.h @@ -32,7 +32,7 @@ #define DECREASE_ACTIVE_COUNT(loop, handle) \ do { \ if (--(handle)->activecnt == 0 && \ - !((handle)->flags & UV__HANDLE_CLOSING)) { \ + !((handle)->flags & UV_HANDLE_CLOSING)) { \ uv__handle_stop((handle)); \ } \ assert((handle)->activecnt >= 0); \ @@ -53,7 +53,7 @@ assert(handle->reqs_pending > 0); \ handle->reqs_pending--; \ \ - if (handle->flags & UV__HANDLE_CLOSING && \ + if (handle->flags & UV_HANDLE_CLOSING && \ handle->reqs_pending == 0) { \ uv_want_endgame(loop, (uv_handle_t*)handle); \ } \ @@ -62,14 +62,14 @@ #define uv__handle_closing(handle) \ do { \ - assert(!((handle)->flags & UV__HANDLE_CLOSING)); \ + assert(!((handle)->flags & UV_HANDLE_CLOSING)); \ \ - if (!(((handle)->flags & UV__HANDLE_ACTIVE) && \ - ((handle)->flags & UV__HANDLE_REF))) \ + if (!(((handle)->flags & UV_HANDLE_ACTIVE) && \ + ((handle)->flags & UV_HANDLE_REF))) \ uv__active_handle_add((uv_handle_t*) (handle)); \ \ - (handle)->flags |= UV__HANDLE_CLOSING; \ - (handle)->flags &= ~UV__HANDLE_ACTIVE; \ + (handle)->flags |= UV_HANDLE_CLOSING; \ + (handle)->flags &= ~UV_HANDLE_ACTIVE; \ } while (0) @@ -126,7 +126,8 @@ INLINE static void uv_process_endgames(uv_loop_t* loop) { break; case UV_TIMER: - uv_timer_endgame(loop, (uv_timer_t*) handle); + uv__timer_close((uv_timer_t*) handle); + uv__handle_close(handle); break; case UV_PREPARE: @@ -164,10 +165,10 @@ INLINE static void uv_process_endgames(uv_loop_t* loop) { INLINE static HANDLE uv__get_osfhandle(int fd) { - /* _get_osfhandle() raises an assert in debug builds if the FD is invalid. */ - /* But it also correctly checks the FD and returns INVALID_HANDLE_VALUE */ - /* for invalid FDs in release builds (or if you let the assert continue). */ - /* So this wrapper function disables asserts when calling _get_osfhandle. */ + /* _get_osfhandle() raises an assert in debug builds if the FD is invalid. + * But it also correctly checks the FD and returns INVALID_HANDLE_VALUE for + * invalid FDs in release builds (or if you let the assert continue). So this + * wrapper function disables asserts when calling _get_osfhandle. */ HANDLE handle; UV_BEGIN_DISABLE_CRT_ASSERT(); diff --git a/include/libuv/src/win/handle.c b/include/libuv/src/win/handle.c index 72b49d979..61e4df61b 100644 --- a/include/libuv/src/win/handle.c +++ b/include/libuv/src/win/handle.c @@ -59,15 +59,15 @@ uv_handle_type uv_guess_handle(uv_file file) { int uv_is_active(const uv_handle_t* handle) { - return (handle->flags & UV__HANDLE_ACTIVE) && - !(handle->flags & UV__HANDLE_CLOSING); + return (handle->flags & UV_HANDLE_ACTIVE) && + !(handle->flags & UV_HANDLE_CLOSING); } void uv_close(uv_handle_t* handle, uv_close_cb cb) { uv_loop_t* loop = handle->loop; - if (handle->flags & UV__HANDLE_CLOSING) { + if (handle->flags & UV_HANDLE_CLOSING) { assert(0); return; } @@ -139,7 +139,6 @@ void uv_close(uv_handle_t* handle, uv_close_cb cb) { case UV_FS_POLL: uv__fs_poll_close((uv_fs_poll_t*) handle); uv__handle_closing(handle); - uv_want_endgame(loop, handle); return; default: @@ -150,5 +149,14 @@ void uv_close(uv_handle_t* handle, uv_close_cb cb) { int uv_is_closing(const uv_handle_t* handle) { - return !!(handle->flags & (UV__HANDLE_CLOSING | UV_HANDLE_CLOSED)); + return !!(handle->flags & (UV_HANDLE_CLOSING | UV_HANDLE_CLOSED)); +} + + +uv_os_fd_t uv_get_osfhandle(int fd) { + return uv__get_osfhandle(fd); +} + +int uv_open_osfhandle(uv_os_fd_t os_fd) { + return _open_osfhandle((intptr_t) os_fd, 0); } diff --git a/include/libuv/src/win/internal.h b/include/libuv/src/win/internal.h index 0a7c9404f..b096255e4 100644 --- a/include/libuv/src/win/internal.h +++ b/include/libuv/src/win/internal.h @@ -25,7 +25,7 @@ #include "uv.h" #include "../uv-common.h" -#include "tree.h" +#include "uv/tree.h" #include "winapi.h" #include "winsock.h" @@ -57,78 +57,20 @@ extern UV_THREAD_LOCAL int uv__crt_assert_enabled; #define UV_END_DISABLE_CRT_ASSERT() #endif -/* - * Handles - * (also see handle-inl.h) - */ - -/* Used by all handles. */ -#define UV_HANDLE_CLOSED 0x00000002 -#define UV_HANDLE_ENDGAME_QUEUED 0x00000008 - -/* uv-common.h: #define UV__HANDLE_CLOSING 0x00000001 */ -/* uv-common.h: #define UV__HANDLE_ACTIVE 0x00000040 */ -/* uv-common.h: #define UV__HANDLE_REF 0x00000020 */ -/* uv-common.h: #define UV_HANDLE_INTERNAL 0x00000080 */ - -/* Used by streams and UDP handles. */ -#define UV_HANDLE_READING 0x00000100 -#define UV_HANDLE_BOUND 0x00000200 -#define UV_HANDLE_LISTENING 0x00000800 -#define UV_HANDLE_CONNECTION 0x00001000 -#define UV_HANDLE_READABLE 0x00008000 -#define UV_HANDLE_WRITABLE 0x00010000 -#define UV_HANDLE_READ_PENDING 0x00020000 -#define UV_HANDLE_SYNC_BYPASS_IOCP 0x00040000 -#define UV_HANDLE_ZERO_READ 0x00080000 -#define UV_HANDLE_EMULATE_IOCP 0x00100000 -#define UV_HANDLE_BLOCKING_WRITES 0x00200000 -#define UV_HANDLE_CANCELLATION_PENDING 0x00400000 - -/* Used by uv_tcp_t and uv_udp_t handles */ -#define UV_HANDLE_IPV6 0x01000000 - -/* Only used by uv_tcp_t handles. */ -#define UV_HANDLE_TCP_NODELAY 0x02000000 -#define UV_HANDLE_TCP_KEEPALIVE 0x04000000 -#define UV_HANDLE_TCP_SINGLE_ACCEPT 0x08000000 -#define UV_HANDLE_TCP_ACCEPT_STATE_CHANGING 0x10000000 -#define UV_HANDLE_TCP_SOCKET_CLOSED 0x20000000 -#define UV_HANDLE_SHARED_TCP_SOCKET 0x40000000 - -/* Only used by uv_pipe_t handles. */ -#define UV_HANDLE_NON_OVERLAPPED_PIPE 0x01000000 -#define UV_HANDLE_PIPESERVER 0x02000000 -#define UV_HANDLE_PIPE_READ_CANCELABLE 0x04000000 - -/* Only used by uv_tty_t handles. */ -#define UV_HANDLE_TTY_READABLE 0x01000000 -#define UV_HANDLE_TTY_RAW 0x02000000 -#define UV_HANDLE_TTY_SAVED_POSITION 0x04000000 -#define UV_HANDLE_TTY_SAVED_ATTRIBUTES 0x08000000 - -/* Only used by uv_poll_t handles. */ -#define UV_HANDLE_POLL_SLOW 0x02000000 - - -/* - * Requests: see req-inl.h - */ - - -/* - * Streams: see stream-inl.h - */ - - /* * TCP */ +typedef enum { + UV__IPC_SOCKET_XFER_NONE = 0, + UV__IPC_SOCKET_XFER_TCP_CONNECTION, + UV__IPC_SOCKET_XFER_TCP_SERVER +} uv__ipc_socket_xfer_type_t; + typedef struct { WSAPROTOCOL_INFOW socket_info; - int delayed_error; -} uv__ipc_socket_info_ex; + uint32_t delayed_error; +} uv__ipc_socket_xfer_info_t; int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb); int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client); @@ -150,11 +92,13 @@ void uv_process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle, void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp); void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle); -int uv_tcp_import(uv_tcp_t* tcp, uv__ipc_socket_info_ex* socket_info_ex, - int tcp_connection); - -int uv_tcp_duplicate_socket(uv_tcp_t* handle, int pid, - LPWSAPROTOCOL_INFOW protocol_info); +int uv__tcp_xfer_export(uv_tcp_t* handle, + int pid, + uv__ipc_socket_xfer_type_t* xfer_type, + uv__ipc_socket_xfer_info_t* xfer_info); +int uv__tcp_xfer_import(uv_tcp_t* tcp, + uv__ipc_socket_xfer_type_t xfer_type, + uv__ipc_socket_xfer_info_t* xfer_info); /* @@ -178,14 +122,14 @@ int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb); int uv_pipe_accept(uv_pipe_t* server, uv_stream_t* client); int uv_pipe_read_start(uv_pipe_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb); -int uv_pipe_write(uv_loop_t* loop, uv_write_t* req, uv_pipe_t* handle, - const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb); -int uv_pipe_write2(uv_loop_t* loop, uv_write_t* req, uv_pipe_t* handle, - const uv_buf_t bufs[], unsigned int nbufs, uv_stream_t* send_handle, - uv_write_cb cb); -void uv__pipe_pause_read(uv_pipe_t* handle); -void uv__pipe_unpause_read(uv_pipe_t* handle); -void uv__pipe_stop_read(uv_pipe_t* handle); +void uv__pipe_read_stop(uv_pipe_t* handle); +int uv__pipe_write(uv_loop_t* loop, + uv_write_t* req, + uv_pipe_t* handle, + const uv_buf_t bufs[], + size_t nbufs, + uv_stream_t* send_handle, + uv_write_cb cb); void uv_process_pipe_read_req(uv_loop_t* loop, uv_pipe_t* handle, uv_req_t* req); @@ -206,7 +150,7 @@ void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle); /* * TTY */ -void uv_console_init(); +void uv_console_init(void); int uv_tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb, uv_read_cb read_cb); @@ -221,10 +165,16 @@ void uv_process_tty_read_req(uv_loop_t* loop, uv_tty_t* handle, uv_req_t* req); void uv_process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle, uv_write_t* req); -/* TODO: remove me */ +/* + * uv_process_tty_accept_req() is a stub to keep DELEGATE_STREAM_REQ working + * TODO: find a way to remove it + */ void uv_process_tty_accept_req(uv_loop_t* loop, uv_tty_t* handle, uv_req_t* raw_req); -/* TODO: remove me */ +/* + * uv_process_tty_connect_req() is a stub to keep DELEGATE_STREAM_REQ working + * TODO: find a way to remove it + */ void uv_process_tty_connect_req(uv_loop_t* loop, uv_tty_t* handle, uv_connect_t* req); @@ -241,15 +191,6 @@ int uv_poll_close(uv_loop_t* loop, uv_poll_t* handle); void uv_poll_endgame(uv_loop_t* loop, uv_poll_t* handle); -/* - * Timers - */ -void uv_timer_endgame(uv_loop_t* loop, uv_timer_t* handle); - -DWORD uv__next_timeout(const uv_loop_t* loop); -void uv_process_timers(uv_loop_t* loop); - - /* * Loop watchers */ @@ -259,7 +200,7 @@ void uv_prepare_invoke(uv_loop_t* loop); void uv_check_invoke(uv_loop_t* loop); void uv_idle_invoke(uv_loop_t* loop); -void uv__once_init(); +void uv__once_init(void); /* @@ -275,7 +216,7 @@ void uv_process_async_wakeup_req(uv_loop_t* loop, uv_async_t* handle, /* * Signal watcher */ -void uv_signals_init(); +void uv_signals_init(void); int uv__signal_dispatch(int signum); void uv_signal_close(uv_loop_t* loop, uv_signal_t* handle); @@ -302,7 +243,7 @@ int uv_translate_sys_error(int sys_errno); /* * FS */ -void uv_fs_init(); +void uv_fs_init(void); /* @@ -323,14 +264,23 @@ void uv__fs_poll_endgame(uv_loop_t* loop, uv_fs_poll_t* handle); /* * Utilities. */ -void uv__util_init(); +void uv__util_init(void); -uint64_t uv__hrtime(double scale); -int uv_parent_pid(); -int uv_current_pid(); +uint64_t uv__hrtime(unsigned int scale); __declspec(noreturn) void uv_fatal_error(const int errorno, const char* syscall); int uv__getpwuid_r(uv_passwd_t* pwd); int uv__convert_utf16_to_utf8(const WCHAR* utf16, int utf16len, char** utf8); +int uv__convert_utf8_to_utf16(const char* utf8, int utf8len, WCHAR** utf16); + +typedef int (WINAPI *uv__peersockfunc)(SOCKET, struct sockaddr*, int*); + +int uv__getsockpeername(const uv_handle_t* handle, + uv__peersockfunc func, + struct sockaddr* name, + int* namelen, + int delayed_error); + +int uv__random_rtlgenrandom(void* buf, size_t buflen); /* @@ -349,13 +299,13 @@ HANDLE uv__stdio_handle(BYTE* buffer, int fd); /* * Winapi and ntapi utility functions */ -void uv_winapi_init(); +void uv_winapi_init(void); /* * Winsock utility functions */ -void uv_winsock_init(); +void uv_winsock_init(void); int uv_ntstatus_to_winsock_error(NTSTATUS status); @@ -381,4 +331,14 @@ extern int uv_tcp_non_ifs_lsp_ipv6; extern struct sockaddr_in uv_addr_ip4_any_; extern struct sockaddr_in6 uv_addr_ip6_any_; +/* + * Wake all loops with fake message + */ +void uv__wake_all_loops(void); + +/* + * Init system wake-up detection + */ +void uv__init_detect_system_wakeup(void); + #endif /* UV_WIN_INTERNAL_H_ */ diff --git a/include/libuv/src/win/loop-watcher.c b/include/libuv/src/win/loop-watcher.c index 20e4509f8..ad7fbea16 100644 --- a/include/libuv/src/win/loop-watcher.c +++ b/include/libuv/src/win/loop-watcher.c @@ -27,7 +27,7 @@ void uv_loop_watcher_endgame(uv_loop_t* loop, uv_handle_t* handle) { - if (handle->flags & UV__HANDLE_CLOSING) { + if (handle->flags & UV_HANDLE_CLOSING) { assert(!(handle->flags & UV_HANDLE_CLOSED)); handle->flags |= UV_HANDLE_CLOSED; uv__handle_close(handle); diff --git a/include/libuv/src/win/pipe.c b/include/libuv/src/win/pipe.c index a784325c5..f81245ec6 100644 --- a/include/libuv/src/win/pipe.c +++ b/include/libuv/src/win/pipe.c @@ -21,27 +21,19 @@ #include #include -#include #include #include +#include -#include "uv.h" -#include "internal.h" #include "handle-inl.h" -#include "stream-inl.h" +#include "internal.h" #include "req-inl.h" +#include "stream-inl.h" +#include "uv-common.h" +#include "uv.h" -typedef struct uv__ipc_queue_item_s uv__ipc_queue_item_t; - -struct uv__ipc_queue_item_s { - /* - * NOTE: It is important for socket_info_ex to be the first field, - * because we will we assigning it to the pending_ipc_info.socket_info - */ - uv__ipc_socket_info_ex socket_info_ex; - QUEUE member; - int tcp_connection; -}; +#include +#include /* A zero-size buffer for use by uv_pipe_read */ static char uv_zero_[] = ""; @@ -49,8 +41,8 @@ static char uv_zero_[] = ""; /* Null uv_buf_t */ static const uv_buf_t uv_null_buf_ = { 0, NULL }; -/* The timeout that the pipe will wait for the remote end to write data */ -/* when the local ends wants to shut it down. */ +/* The timeout that the pipe will wait for the remote end to write data when + * the local ends wants to shut it down. */ static const int64_t eof_timeout = 50; /* ms */ static const int default_pending_pipe_instances = 4; @@ -59,22 +51,44 @@ static const int default_pending_pipe_instances = 4; static char pipe_prefix[] = "\\\\?\\pipe"; static const int pipe_prefix_len = sizeof(pipe_prefix) - 1; -/* IPC protocol flags. */ -#define UV_IPC_RAW_DATA 0x0001 -#define UV_IPC_TCP_SERVER 0x0002 -#define UV_IPC_TCP_CONNECTION 0x0004 +/* IPC incoming xfer queue item. */ +typedef struct { + uv__ipc_socket_xfer_type_t xfer_type; + uv__ipc_socket_xfer_info_t xfer_info; + QUEUE member; +} uv__ipc_xfer_queue_item_t; + +/* IPC frame header flags. */ +/* clang-format off */ +enum { + UV__IPC_FRAME_HAS_DATA = 0x01, + UV__IPC_FRAME_HAS_SOCKET_XFER = 0x02, + UV__IPC_FRAME_XFER_IS_TCP_CONNECTION = 0x04, + /* These are combinations of the flags above. */ + UV__IPC_FRAME_XFER_FLAGS = 0x06, + UV__IPC_FRAME_VALID_FLAGS = 0x07 +}; +/* clang-format on */ /* IPC frame header. */ typedef struct { - int flags; - uint64_t raw_data_length; -} uv_ipc_frame_header_t; - -/* IPC frame, which contains an imported TCP socket stream. */ + uint32_t flags; + uint32_t reserved1; /* Ignored. */ + uint32_t data_length; /* Must be zero if there is no data. */ + uint32_t reserved2; /* Must be zero. */ +} uv__ipc_frame_header_t; + +/* To implement the IPC protocol correctly, these structures must have exactly + * the right size. */ +STATIC_ASSERT(sizeof(uv__ipc_frame_header_t) == 16); +STATIC_ASSERT(sizeof(uv__ipc_socket_xfer_info_t) == 632); + +/* Coalesced write request. */ typedef struct { - uv_ipc_frame_header_t header; - uv__ipc_socket_info_ex socket_info_ex; -} uv_ipc_frame_uv_stream; + uv_write_t req; /* Internal heap-allocated write request. */ + uv_write_t* user_req; /* Pointer to user-specified uv_write_t. */ +} uv__coalesced_write_t; + static void eof_timer_init(uv_pipe_t* pipe); static void eof_timer_start(uv_pipe_t* pipe); @@ -85,7 +99,7 @@ static void eof_timer_close_cb(uv_handle_t* handle); static void uv_unique_pipe_name(char* ptr, char* name, size_t size) { - snprintf(name, size, "\\\\?\\pipe\\uv\\%p-%u", ptr, GetCurrentProcessId()); + snprintf(name, size, "\\\\?\\pipe\\uv\\%p-%lu", ptr, GetCurrentProcessId()); } @@ -95,15 +109,12 @@ int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc) { handle->reqs_pending = 0; handle->handle = INVALID_HANDLE_VALUE; handle->name = NULL; - handle->pipe.conn.ipc_pid = 0; - handle->pipe.conn.remaining_ipc_rawdata_bytes = 0; - QUEUE_INIT(&handle->pipe.conn.pending_ipc_info.queue); - handle->pipe.conn.pending_ipc_info.queue_len = 0; + handle->pipe.conn.ipc_remote_pid = 0; + handle->pipe.conn.ipc_data_frame.payload_remaining = 0; + QUEUE_INIT(&handle->pipe.conn.ipc_xfer_queue); + handle->pipe.conn.ipc_xfer_queue_length = 0; handle->ipc = ipc; handle->pipe.conn.non_overlapped_writes_tail = NULL; - handle->pipe.conn.readfile_thread = NULL; - - uv_req_init(loop, (uv_req_t*) &handle->pipe.conn.ipc_header_write_req); return 0; } @@ -114,10 +125,9 @@ static void uv_pipe_connection_init(uv_pipe_t* handle) { handle->read_req.data = handle; handle->pipe.conn.eof_timer = NULL; assert(!(handle->flags & UV_HANDLE_PIPESERVER)); - if (pCancelSynchronousIo && - handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) { - uv_mutex_init(&handle->pipe.conn.readfile_mutex); - handle->flags |= UV_HANDLE_PIPE_READ_CANCELABLE; + if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) { + handle->pipe.conn.readfile_thread_handle = NULL; + InitializeCriticalSection(&handle->pipe.conn.readfile_thread_lock); } } @@ -202,7 +212,7 @@ int uv_stdio_pipe_server(uv_loop_t* loop, uv_pipe_t* handle, DWORD access, uv_unique_pipe_name(ptr, name, nameSize); pipeHandle = CreateNamedPipeA(name, - access | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE, + access | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE | WRITE_DAC, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 65536, 65536, 0, NULL); @@ -234,9 +244,8 @@ int uv_stdio_pipe_server(uv_loop_t* loop, uv_pipe_t* handle, DWORD access, return 0; error: - if (pipeHandle != INVALID_HANDLE_VALUE) { + if (pipeHandle != INVALID_HANDLE_VALUE) CloseHandle(pipeHandle); - } return err; } @@ -254,8 +263,9 @@ static int uv_set_pipe_handle(uv_loop_t* loop, DWORD current_mode = 0; DWORD err = 0; - if (!(handle->flags & UV_HANDLE_PIPESERVER) && - handle->handle != INVALID_HANDLE_VALUE) + if (handle->flags & UV_HANDLE_PIPESERVER) + return UV_EINVAL; + if (handle->handle != INVALID_HANDLE_VALUE) return UV_EBUSY; if (!SetNamedPipeHandleState(pipeHandle, &mode, NULL, NULL)) { @@ -302,7 +312,7 @@ static int uv_set_pipe_handle(uv_loop_t* loop, /* Overlapped pipe. Try to associate with IOCP. */ if (CreateIoCompletionPort(pipeHandle, loop->iocp, - (ULONG_PTR)handle, + (ULONG_PTR) handle, 0) == NULL) { handle->flags |= UV_HANDLE_EMULATE_IOCP; } @@ -316,6 +326,38 @@ static int uv_set_pipe_handle(uv_loop_t* loop, } +static int pipe_alloc_accept(uv_loop_t* loop, uv_pipe_t* handle, + uv_pipe_accept_t* req, BOOL firstInstance) { + assert(req->pipeHandle == INVALID_HANDLE_VALUE); + + req->pipeHandle = + CreateNamedPipeW(handle->name, + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | WRITE_DAC | + (firstInstance ? FILE_FLAG_FIRST_PIPE_INSTANCE : 0), + PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, 65536, 65536, 0, NULL); + + if (req->pipeHandle == INVALID_HANDLE_VALUE) { + return 0; + } + + /* Associate it with IOCP so we can get events. */ + if (CreateIoCompletionPort(req->pipeHandle, + loop->iocp, + (ULONG_PTR) handle, + 0) == NULL) { + uv_fatal_error(GetLastError(), "CreateIoCompletionPort"); + } + + /* Stash a handle in the server object for use from places such as + * getsockname and chmod. As we transfer ownership of these to client + * objects, we'll allocate new ones here. */ + handle->handle = req->pipeHandle; + + return 1; +} + + static DWORD WINAPI pipe_shutdown_thread_proc(void* parameter) { uv_loop_t* loop; uv_pipe_t* handle; @@ -344,12 +386,7 @@ void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) { NTSTATUS nt_status; IO_STATUS_BLOCK io_status; FILE_PIPE_LOCAL_INFORMATION pipe_info; - uv__ipc_queue_item_t* item; - - if (handle->flags & UV_HANDLE_PIPE_READ_CANCELABLE) { - handle->flags &= ~UV_HANDLE_PIPE_READ_CANCELABLE; - uv_mutex_destroy(&handle->pipe.conn.readfile_mutex); - } + uv__ipc_xfer_queue_item_t* xfer_queue_item; if ((handle->flags & UV_HANDLE_CONNECTION) && handle->stream.conn.shutdown_req != NULL && @@ -359,7 +396,7 @@ void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) { /* Clear the shutdown_req field so we don't go here again. */ handle->stream.conn.shutdown_req = NULL; - if (handle->flags & UV__HANDLE_CLOSING) { + if (handle->flags & UV_HANDLE_CLOSING) { UNREGISTER_HANDLE_REQ(loop, handle, req); /* Already closing. Cancel the shutdown. */ @@ -420,44 +457,47 @@ void uv_pipe_endgame(uv_loop_t* loop, uv_pipe_t* handle) { } } - if (handle->flags & UV__HANDLE_CLOSING && + if (handle->flags & UV_HANDLE_CLOSING && handle->reqs_pending == 0) { assert(!(handle->flags & UV_HANDLE_CLOSED)); if (handle->flags & UV_HANDLE_CONNECTION) { /* Free pending sockets */ - while (!QUEUE_EMPTY(&handle->pipe.conn.pending_ipc_info.queue)) { + while (!QUEUE_EMPTY(&handle->pipe.conn.ipc_xfer_queue)) { QUEUE* q; SOCKET socket; - q = QUEUE_HEAD(&handle->pipe.conn.pending_ipc_info.queue); + q = QUEUE_HEAD(&handle->pipe.conn.ipc_xfer_queue); QUEUE_REMOVE(q); - item = QUEUE_DATA(q, uv__ipc_queue_item_t, member); + xfer_queue_item = QUEUE_DATA(q, uv__ipc_xfer_queue_item_t, member); /* Materialize socket and close it */ socket = WSASocketW(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, - &item->socket_info_ex.socket_info, + &xfer_queue_item->xfer_info.socket_info, 0, WSA_FLAG_OVERLAPPED); - uv__free(item); + uv__free(xfer_queue_item); if (socket != INVALID_SOCKET) closesocket(socket); } - handle->pipe.conn.pending_ipc_info.queue_len = 0; + handle->pipe.conn.ipc_xfer_queue_length = 0; if (handle->flags & UV_HANDLE_EMULATE_IOCP) { if (handle->read_req.wait_handle != INVALID_HANDLE_VALUE) { UnregisterWait(handle->read_req.wait_handle); handle->read_req.wait_handle = INVALID_HANDLE_VALUE; } - if (handle->read_req.event_handle) { + if (handle->read_req.event_handle != NULL) { CloseHandle(handle->read_req.event_handle); handle->read_req.event_handle = NULL; } } + + if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) + DeleteCriticalSection(&handle->pipe.conn.readfile_thread_lock); } if (handle->flags & UV_HANDLE_PIPESERVER) { @@ -505,8 +545,7 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { for (i = 0; i < handle->pipe.serv.pending_instances; i++) { req = &handle->pipe.serv.accept_reqs[i]; - uv_req_init(loop, (uv_req_t*) req); - req->type = UV_ACCEPT; + UV_REQ_INIT(req, UV_ACCEPT); req->data = handle; req->pipeHandle = INVALID_HANDLE_VALUE; req->next_pending = NULL; @@ -514,7 +553,7 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { /* Convert name to UTF16. */ nameSize = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0) * sizeof(WCHAR); - handle->name = (WCHAR*)uv__malloc(nameSize); + handle->name = uv__malloc(nameSize); if (!handle->name) { uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); } @@ -533,13 +572,10 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { * Attempt to create the first pipe with FILE_FLAG_FIRST_PIPE_INSTANCE. * If this fails then there's already a pipe server for the given pipe name. */ - handle->pipe.serv.accept_reqs[0].pipeHandle = CreateNamedPipeW(handle->name, - PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | - FILE_FLAG_FIRST_PIPE_INSTANCE, - PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, - PIPE_UNLIMITED_INSTANCES, 65536, 65536, 0, NULL); - - if (handle->pipe.serv.accept_reqs[0].pipeHandle == INVALID_HANDLE_VALUE) { + if (!pipe_alloc_accept(loop, + handle, + &handle->pipe.serv.accept_reqs[0], + TRUE)) { err = GetLastError(); if (err == ERROR_ACCESS_DENIED) { err = WSAEADDRINUSE; /* Translates to UV_EADDRINUSE. */ @@ -549,15 +585,6 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { goto error; } - if (uv_set_pipe_handle(loop, - handle, - handle->pipe.serv.accept_reqs[0].pipeHandle, - -1, - 0)) { - err = GetLastError(); - goto error; - } - handle->pipe.serv.pending_accepts = NULL; handle->flags |= UV_HANDLE_PIPESERVER; handle->flags |= UV_HANDLE_BOUND; @@ -570,11 +597,6 @@ int uv_pipe_bind(uv_pipe_t* handle, const char* name) { handle->name = NULL; } - if (handle->pipe.serv.accept_reqs[0].pipeHandle != INVALID_HANDLE_VALUE) { - CloseHandle(handle->pipe.serv.accept_reqs[0].pipeHandle); - handle->pipe.serv.accept_reqs[0].pipeHandle = INVALID_HANDLE_VALUE; - } - return uv_translate_sys_error(err); } @@ -593,14 +615,13 @@ static DWORD WINAPI pipe_connect_thread_proc(void* parameter) { loop = handle->loop; assert(loop); - /* We're here because CreateFile on a pipe returned ERROR_PIPE_BUSY. */ - /* We wait for the pipe to become available with WaitNamedPipe. */ + /* We're here because CreateFile on a pipe returned ERROR_PIPE_BUSY. We wait + * for the pipe to become available with WaitNamedPipe. */ while (WaitNamedPipeW(handle->name, 30000)) { /* The pipe is now available, try to connect. */ pipeHandle = open_named_pipe(handle->name, &duplex_flags); - if (pipeHandle != INVALID_HANDLE_VALUE) { + if (pipeHandle != INVALID_HANDLE_VALUE) break; - } SwitchToThread(); } @@ -626,14 +647,13 @@ void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, HANDLE pipeHandle = INVALID_HANDLE_VALUE; DWORD duplex_flags; - uv_req_init(loop, (uv_req_t*) req); - req->type = UV_CONNECT; + UV_REQ_INIT(req, UV_CONNECT); req->handle = (uv_stream_t*) handle; req->cb = cb; /* Convert name to UTF16. */ nameSize = MultiByteToWideChar(CP_UTF8, 0, name, -1, NULL, 0) * sizeof(WCHAR); - handle->name = (WCHAR*)uv__malloc(nameSize); + handle->name = uv__malloc(nameSize); if (!handle->name) { uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); } @@ -705,48 +725,68 @@ void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, } -void uv__pipe_pause_read(uv_pipe_t* handle) { - if (handle->flags & UV_HANDLE_PIPE_READ_CANCELABLE) { - /* Pause the ReadFile task briefly, to work - around the Windows kernel bug that causes - any access to a NamedPipe to deadlock if - any process has called ReadFile */ - HANDLE h; - uv_mutex_lock(&handle->pipe.conn.readfile_mutex); - h = handle->pipe.conn.readfile_thread; - while (h) { - /* spinlock: we expect this to finish quickly, - or we are probably about to deadlock anyways - (in the kernel), so it doesn't matter */ - pCancelSynchronousIo(h); - SwitchToThread(); /* yield thread control briefly */ - h = handle->pipe.conn.readfile_thread; - } - } -} +void uv__pipe_interrupt_read(uv_pipe_t* handle) { + BOOL r; + + if (!(handle->flags & UV_HANDLE_READ_PENDING)) + return; /* No pending reads. */ + if (handle->flags & UV_HANDLE_CANCELLATION_PENDING) + return; /* Already cancelled. */ + if (handle->handle == INVALID_HANDLE_VALUE) + return; /* Pipe handle closed. */ + if (!(handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE)) { + /* Cancel asynchronous read. */ + r = CancelIoEx(handle->handle, &handle->read_req.u.io.overlapped); + assert(r || GetLastError() == ERROR_NOT_FOUND); -void uv__pipe_unpause_read(uv_pipe_t* handle) { - if (handle->flags & UV_HANDLE_PIPE_READ_CANCELABLE) { - uv_mutex_unlock(&handle->pipe.conn.readfile_mutex); + } else { + /* Cancel synchronous read (which is happening in the thread pool). */ + HANDLE thread; + volatile HANDLE* thread_ptr = &handle->pipe.conn.readfile_thread_handle; + + EnterCriticalSection(&handle->pipe.conn.readfile_thread_lock); + + thread = *thread_ptr; + if (thread == NULL) { + /* The thread pool thread has not yet reached the point of blocking, we + * can pre-empt it by setting thread_handle to INVALID_HANDLE_VALUE. */ + *thread_ptr = INVALID_HANDLE_VALUE; + + } else { + /* Spin until the thread has acknowledged (by setting the thread to + * INVALID_HANDLE_VALUE) that it is past the point of blocking. */ + while (thread != INVALID_HANDLE_VALUE) { + r = CancelSynchronousIo(thread); + assert(r || GetLastError() == ERROR_NOT_FOUND); + SwitchToThread(); /* Yield thread. */ + thread = *thread_ptr; + } + } + + LeaveCriticalSection(&handle->pipe.conn.readfile_thread_lock); } + + /* Set flag to indicate that read has been cancelled. */ + handle->flags |= UV_HANDLE_CANCELLATION_PENDING; } -void uv__pipe_stop_read(uv_pipe_t* handle) { +void uv__pipe_read_stop(uv_pipe_t* handle) { handle->flags &= ~UV_HANDLE_READING; - uv__pipe_pause_read((uv_pipe_t*)handle); - uv__pipe_unpause_read((uv_pipe_t*)handle); + DECREASE_ACTIVE_COUNT(handle->loop, handle); + + uv__pipe_interrupt_read(handle); } -/* Cleans up uv_pipe_t (server or connection) and all resources associated */ -/* with it. */ +/* Cleans up uv_pipe_t (server or connection) and all resources associated with + * it. */ void uv_pipe_cleanup(uv_loop_t* loop, uv_pipe_t* handle) { int i; HANDLE pipeHandle; - uv__pipe_stop_read(handle); + uv__pipe_interrupt_read(handle); if (handle->name) { uv__free(handle->name); @@ -801,29 +841,11 @@ static void uv_pipe_queue_accept(uv_loop_t* loop, uv_pipe_t* handle, uv_pipe_accept_t* req, BOOL firstInstance) { assert(handle->flags & UV_HANDLE_LISTENING); - if (!firstInstance) { - assert(req->pipeHandle == INVALID_HANDLE_VALUE); - - req->pipeHandle = CreateNamedPipeW(handle->name, - PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, - PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, - PIPE_UNLIMITED_INSTANCES, 65536, 65536, 0, NULL); - - if (req->pipeHandle == INVALID_HANDLE_VALUE) { - SET_REQ_ERROR(req, GetLastError()); - uv_insert_pending_req(loop, (uv_req_t*) req); - handle->reqs_pending++; - return; - } - - if (uv_set_pipe_handle(loop, handle, req->pipeHandle, -1, 0)) { - CloseHandle(req->pipeHandle); - req->pipeHandle = INVALID_HANDLE_VALUE; - SET_REQ_ERROR(req, GetLastError()); - uv_insert_pending_req(loop, (uv_req_t*) req); - handle->reqs_pending++; - return; - } + if (!firstInstance && !pipe_alloc_accept(loop, handle, req, FALSE)) { + SET_REQ_ERROR(req, GetLastError()); + uv_insert_pending_req(loop, (uv_req_t*) req); + handle->reqs_pending++; + return; } assert(req->pipeHandle != INVALID_HANDLE_VALUE); @@ -846,6 +868,7 @@ static void uv_pipe_queue_accept(uv_loop_t* loop, uv_pipe_t* handle, return; } + /* Wait for completion via IOCP */ handle->reqs_pending++; } @@ -855,33 +878,32 @@ int uv_pipe_accept(uv_pipe_t* server, uv_stream_t* client) { uv_pipe_t* pipe_client; uv_pipe_accept_t* req; QUEUE* q; - uv__ipc_queue_item_t* item; + uv__ipc_xfer_queue_item_t* item; int err; if (server->ipc) { - if (QUEUE_EMPTY(&server->pipe.conn.pending_ipc_info.queue)) { + if (QUEUE_EMPTY(&server->pipe.conn.ipc_xfer_queue)) { /* No valid pending sockets. */ return WSAEWOULDBLOCK; } - q = QUEUE_HEAD(&server->pipe.conn.pending_ipc_info.queue); + q = QUEUE_HEAD(&server->pipe.conn.ipc_xfer_queue); QUEUE_REMOVE(q); - server->pipe.conn.pending_ipc_info.queue_len--; - item = QUEUE_DATA(q, uv__ipc_queue_item_t, member); + server->pipe.conn.ipc_xfer_queue_length--; + item = QUEUE_DATA(q, uv__ipc_xfer_queue_item_t, member); - err = uv_tcp_import((uv_tcp_t*)client, - &item->socket_info_ex, - item->tcp_connection); + err = uv__tcp_xfer_import( + (uv_tcp_t*) client, item->xfer_type, &item->xfer_info); if (err != 0) return err; uv__free(item); } else { - pipe_client = (uv_pipe_t*)client; + pipe_client = (uv_pipe_t*) client; - /* Find a connection instance that has been connected, but not yet */ - /* accepted. */ + /* Find a connection instance that has been connected, but not yet + * accepted. */ req = server->pipe.serv.pending_accepts; if (!req) { @@ -899,7 +921,8 @@ int uv_pipe_accept(uv_pipe_t* server, uv_stream_t* client) { req->next_pending = NULL; req->pipeHandle = INVALID_HANDLE_VALUE; - if (!(server->flags & UV__HANDLE_CLOSING)) { + server->handle = INVALID_HANDLE_VALUE; + if (!(server->flags & UV_HANDLE_CLOSING)) { uv_pipe_queue_accept(loop, server, req, FALSE); } } @@ -929,6 +952,10 @@ int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) { return ERROR_NOT_SUPPORTED; } + if (handle->ipc) { + return WSAEINVAL; + } + handle->flags |= UV_HANDLE_LISTENING; INCREASE_ACTIVE_COUNT(loop, handle); handle->stream.serv.connection_cb = cb; @@ -944,70 +971,75 @@ int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb) { } -static DWORD WINAPI uv_pipe_zero_readfile_thread_proc(void* parameter) { - int result; - DWORD bytes; - uv_read_t* req = (uv_read_t*) parameter; +static DWORD WINAPI uv_pipe_zero_readfile_thread_proc(void* arg) { + uv_read_t* req = (uv_read_t*) arg; uv_pipe_t* handle = (uv_pipe_t*) req->data; uv_loop_t* loop = handle->loop; - HANDLE hThread = NULL; + volatile HANDLE* thread_ptr = &handle->pipe.conn.readfile_thread_handle; + CRITICAL_SECTION* lock = &handle->pipe.conn.readfile_thread_lock; + HANDLE thread; + DWORD bytes; DWORD err; - uv_mutex_t *m = &handle->pipe.conn.readfile_mutex; - assert(req != NULL); assert(req->type == UV_READ); assert(handle->type == UV_NAMED_PIPE); - if (handle->flags & UV_HANDLE_PIPE_READ_CANCELABLE) { - uv_mutex_lock(m); /* mutex controls *setting* of readfile_thread */ - if (DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), - GetCurrentProcess(), &hThread, - 0, TRUE, DUPLICATE_SAME_ACCESS)) { - handle->pipe.conn.readfile_thread = hThread; - } else { - hThread = NULL; - } - uv_mutex_unlock(m); - } -restart_readfile: - result = ReadFile(handle->handle, - &uv_zero_, - 0, - &bytes, - NULL); - if (!result) { + err = 0; + + /* Create a handle to the current thread. */ + if (!DuplicateHandle(GetCurrentProcess(), + GetCurrentThread(), + GetCurrentProcess(), + &thread, + 0, + FALSE, + DUPLICATE_SAME_ACCESS)) { err = GetLastError(); - if (err == ERROR_OPERATION_ABORTED && - handle->flags & UV_HANDLE_PIPE_READ_CANCELABLE) { - if (handle->flags & UV_HANDLE_READING) { - /* just a brief break to do something else */ - handle->pipe.conn.readfile_thread = NULL; - /* resume after it is finished */ - uv_mutex_lock(m); - handle->pipe.conn.readfile_thread = hThread; - uv_mutex_unlock(m); - goto restart_readfile; - } else { - result = 1; /* successfully stopped reading */ - } - } - } - if (hThread) { - assert(hThread == handle->pipe.conn.readfile_thread); - /* mutex does not control clearing readfile_thread */ - handle->pipe.conn.readfile_thread = NULL; - uv_mutex_lock(m); - /* only when we hold the mutex lock is it safe to - open or close the handle */ - CloseHandle(hThread); - uv_mutex_unlock(m); + goto out1; } - if (!result) { - SET_REQ_ERROR(req, err); + /* The lock needs to be held when thread handle is modified. */ + EnterCriticalSection(lock); + if (*thread_ptr == INVALID_HANDLE_VALUE) { + /* uv__pipe_interrupt_read() cancelled reading before we got here. */ + err = ERROR_OPERATION_ABORTED; + } else { + /* Let main thread know which worker thread is doing the blocking read. */ + assert(*thread_ptr == NULL); + *thread_ptr = thread; } + LeaveCriticalSection(lock); + if (err) + goto out2; + + /* Block the thread until data is available on the pipe, or the read is + * cancelled. */ + if (!ReadFile(handle->handle, &uv_zero_, 0, &bytes, NULL)) + err = GetLastError(); + + /* Let the main thread know the worker is past the point of blocking. */ + assert(thread == *thread_ptr); + *thread_ptr = INVALID_HANDLE_VALUE; + + /* Briefly acquire the mutex. Since the main thread holds the lock while it + * is spinning trying to cancel this thread's I/O, we will block here until + * it stops doing that. */ + EnterCriticalSection(lock); + LeaveCriticalSection(lock); + +out2: + /* Close the handle to the current thread. */ + CloseHandle(thread); + +out1: + /* Set request status and post a completion record to the IOCP. */ + if (err) + SET_REQ_ERROR(req, err); + else + SET_REQ_SUCCESS(req); POST_COMPLETION_FOR_REQ(loop, req); + return 0; } @@ -1089,6 +1121,7 @@ static void uv_pipe_queue_read(uv_loop_t* loop, uv_pipe_t* handle) { req = &handle->read_req; if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) { + handle->pipe.conn.readfile_thread_handle = NULL; /* Reset cancellation. */ if (!QueueUserWorkItem(&uv_pipe_zero_readfile_thread_proc, req, WT_EXECUTELONGFUNCTION)) { @@ -1099,6 +1132,7 @@ static void uv_pipe_queue_read(uv_loop_t* loop, uv_pipe_t* handle) { } else { memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped)); if (handle->flags & UV_HANDLE_EMULATE_IOCP) { + assert(req->event_handle != NULL); req->u.io.overlapped.hEvent = (HANDLE) ((uintptr_t) req->event_handle | 1); } @@ -1116,15 +1150,9 @@ static void uv_pipe_queue_read(uv_loop_t* loop, uv_pipe_t* handle) { } if (handle->flags & UV_HANDLE_EMULATE_IOCP) { - if (!req->event_handle) { - req->event_handle = CreateEvent(NULL, 0, 0, NULL); - if (!req->event_handle) { - uv_fatal_error(GetLastError(), "CreateEvent"); - } - } if (req->wait_handle == INVALID_HANDLE_VALUE) { if (!RegisterWaitForSingleObject(&req->wait_handle, - req->u.io.overlapped.hEvent, post_completion_read_wait, (void*) req, + req->event_handle, post_completion_read_wait, (void*) req, INFINITE, WT_EXECUTEINWAITTHREAD)) { SET_REQ_ERROR(req, GetLastError()); goto error; @@ -1156,10 +1184,18 @@ int uv_pipe_read_start(uv_pipe_t* handle, handle->read_cb = read_cb; handle->alloc_cb = alloc_cb; - /* If reading was stopped and then started again, there could still be a */ - /* read request pending. */ - if (!(handle->flags & UV_HANDLE_READ_PENDING)) + /* If reading was stopped and then started again, there could still be a read + * request pending. */ + if (!(handle->flags & UV_HANDLE_READ_PENDING)) { + if (handle->flags & UV_HANDLE_EMULATE_IOCP && + handle->read_req.event_handle == NULL) { + handle->read_req.event_handle = CreateEvent(NULL, 0, 0, NULL); + if (handle->read_req.event_handle == NULL) { + uv_fatal_error(GetLastError(), "CreateEvent"); + } + } uv_pipe_queue_read(loop, handle); + } return 0; } @@ -1213,156 +1249,119 @@ static void uv_queue_non_overlapped_write(uv_pipe_t* handle) { } -static int uv_pipe_write_impl(uv_loop_t* loop, - uv_write_t* req, - uv_pipe_t* handle, - const uv_buf_t bufs[], - unsigned int nbufs, - uv_stream_t* send_handle, - uv_write_cb cb) { - int err; - int result; - uv_tcp_t* tcp_send_handle; - uv_write_t* ipc_header_req = NULL; - uv_ipc_frame_uv_stream ipc_frame; +static int uv__build_coalesced_write_req(uv_write_t* user_req, + const uv_buf_t bufs[], + size_t nbufs, + uv_write_t** req_out, + uv_buf_t* write_buf_out) { + /* Pack into a single heap-allocated buffer: + * (a) a uv_write_t structure where libuv stores the actual state. + * (b) a pointer to the original uv_write_t. + * (c) data from all `bufs` entries. + */ + char* heap_buffer; + size_t heap_buffer_length, heap_buffer_offset; + uv__coalesced_write_t* coalesced_write_req; /* (a) + (b) */ + char* data_start; /* (c) */ + size_t data_length; + unsigned int i; + + /* Compute combined size of all combined buffers from `bufs`. */ + data_length = 0; + for (i = 0; i < nbufs; i++) + data_length += bufs[i].len; + + /* The total combined size of data buffers should not exceed UINT32_MAX, + * because WriteFile() won't accept buffers larger than that. */ + if (data_length > UINT32_MAX) + return WSAENOBUFS; /* Maps to UV_ENOBUFS. */ + + /* Compute heap buffer size. */ + heap_buffer_length = sizeof *coalesced_write_req + /* (a) + (b) */ + data_length; /* (c) */ + + /* Allocate buffer. */ + heap_buffer = uv__malloc(heap_buffer_length); + if (heap_buffer == NULL) + return ERROR_NOT_ENOUGH_MEMORY; /* Maps to UV_ENOMEM. */ + + /* Copy uv_write_t information to the buffer. */ + coalesced_write_req = (uv__coalesced_write_t*) heap_buffer; + coalesced_write_req->req = *user_req; /* copy (a) */ + coalesced_write_req->req.coalesced = 1; + coalesced_write_req->user_req = user_req; /* copy (b) */ + heap_buffer_offset = sizeof *coalesced_write_req; /* offset (a) + (b) */ + + /* Copy data buffers to the heap buffer. */ + data_start = &heap_buffer[heap_buffer_offset]; + for (i = 0; i < nbufs; i++) { + memcpy(&heap_buffer[heap_buffer_offset], + bufs[i].base, + bufs[i].len); /* copy (c) */ + heap_buffer_offset += bufs[i].len; /* offset (c) */ + } + assert(heap_buffer_offset == heap_buffer_length); + + /* Set out arguments and return. */ + *req_out = &coalesced_write_req->req; + *write_buf_out = uv_buf_init(data_start, (unsigned int) data_length); + return 0; +} - if (nbufs != 1 && (nbufs != 0 || !send_handle)) { - return ERROR_NOT_SUPPORTED; - } - /* Only TCP handles are supported for sharing. */ - if (send_handle && ((send_handle->type != UV_TCP) || - (!(send_handle->flags & UV_HANDLE_BOUND) && - !(send_handle->flags & UV_HANDLE_CONNECTION)))) { - return ERROR_NOT_SUPPORTED; - } +static int uv__pipe_write_data(uv_loop_t* loop, + uv_write_t* req, + uv_pipe_t* handle, + const uv_buf_t bufs[], + size_t nbufs, + uv_write_cb cb, + int copy_always) { + int err; + int result; + uv_buf_t write_buf; assert(handle->handle != INVALID_HANDLE_VALUE); - uv_req_init(loop, (uv_req_t*) req); - req->type = UV_WRITE; + UV_REQ_INIT(req, UV_WRITE); req->handle = (uv_stream_t*) handle; + req->send_handle = NULL; req->cb = cb; - req->ipc_header = 0; + /* Private fields. */ + req->coalesced = 0; req->event_handle = NULL; req->wait_handle = INVALID_HANDLE_VALUE; - memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped)); - - if (handle->ipc) { - assert(!(handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE)); - ipc_frame.header.flags = 0; - - /* Use the IPC framing protocol. */ - if (send_handle) { - tcp_send_handle = (uv_tcp_t*)send_handle; - - if (handle->pipe.conn.ipc_pid == 0) { - handle->pipe.conn.ipc_pid = uv_current_pid(); - } - - err = uv_tcp_duplicate_socket(tcp_send_handle, handle->pipe.conn.ipc_pid, - &ipc_frame.socket_info_ex.socket_info); - if (err) { - return err; - } - - ipc_frame.socket_info_ex.delayed_error = tcp_send_handle->delayed_error; - ipc_frame.header.flags |= UV_IPC_TCP_SERVER; - - if (tcp_send_handle->flags & UV_HANDLE_CONNECTION) { - ipc_frame.header.flags |= UV_IPC_TCP_CONNECTION; - } - } - - if (nbufs == 1) { - ipc_frame.header.flags |= UV_IPC_RAW_DATA; - ipc_frame.header.raw_data_length = bufs[0].len; - } - - /* - * Use the provided req if we're only doing a single write. - * If we're doing multiple writes, use ipc_header_write_req to do - * the first write, and then use the provided req for the second write. - */ - if (!(ipc_frame.header.flags & UV_IPC_RAW_DATA)) { - ipc_header_req = req; - } else { - /* - * Try to use the preallocated write req if it's available. - * Otherwise allocate a new one. - */ - if (handle->pipe.conn.ipc_header_write_req.type != UV_WRITE) { - ipc_header_req = (uv_write_t*)&handle->pipe.conn.ipc_header_write_req; - } else { - ipc_header_req = (uv_write_t*)uv__malloc(sizeof(uv_write_t)); - if (!ipc_header_req) { - uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); - } - } - - uv_req_init(loop, (uv_req_t*) ipc_header_req); - ipc_header_req->type = UV_WRITE; - ipc_header_req->handle = (uv_stream_t*) handle; - ipc_header_req->cb = NULL; - ipc_header_req->ipc_header = 1; - } - - /* Write the header or the whole frame. */ - memset(&ipc_header_req->u.io.overlapped, 0, - sizeof(ipc_header_req->u.io.overlapped)); - - /* Using overlapped IO, but wait for completion before returning. - This write is blocking because ipc_frame is on stack. */ - ipc_header_req->u.io.overlapped.hEvent = CreateEvent(NULL, 1, 0, NULL); - if (!ipc_header_req->u.io.overlapped.hEvent) { + /* Prepare the overlapped structure. */ + memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped)); + if (handle->flags & (UV_HANDLE_EMULATE_IOCP | UV_HANDLE_BLOCKING_WRITES)) { + req->event_handle = CreateEvent(NULL, 0, 0, NULL); + if (req->event_handle == NULL) { uv_fatal_error(GetLastError(), "CreateEvent"); } + req->u.io.overlapped.hEvent = (HANDLE) ((uintptr_t) req->event_handle | 1); + } + req->write_buffer = uv_null_buf_; - result = WriteFile(handle->handle, - &ipc_frame, - ipc_frame.header.flags & UV_IPC_TCP_SERVER ? - sizeof(ipc_frame) : sizeof(ipc_frame.header), - NULL, - &ipc_header_req->u.io.overlapped); - if (!result && GetLastError() != ERROR_IO_PENDING) { - err = GetLastError(); - CloseHandle(ipc_header_req->u.io.overlapped.hEvent); + if (nbufs == 0) { + /* Write empty buffer. */ + write_buf = uv_null_buf_; + } else if (nbufs == 1 && !copy_always) { + /* Write directly from bufs[0]. */ + write_buf = bufs[0]; + } else { + /* Coalesce all `bufs` into one big buffer. This also creates a new + * write-request structure that replaces the old one. */ + err = uv__build_coalesced_write_req(req, bufs, nbufs, &req, &write_buf); + if (err != 0) return err; - } - - if (!result) { - /* Request not completed immediately. Wait for it.*/ - if (WaitForSingleObject(ipc_header_req->u.io.overlapped.hEvent, INFINITE) != - WAIT_OBJECT_0) { - err = GetLastError(); - CloseHandle(ipc_header_req->u.io.overlapped.hEvent); - return err; - } - } - ipc_header_req->u.io.queued_bytes = 0; - CloseHandle(ipc_header_req->u.io.overlapped.hEvent); - ipc_header_req->u.io.overlapped.hEvent = NULL; - - REGISTER_HANDLE_REQ(loop, handle, ipc_header_req); - handle->reqs_pending++; - handle->stream.conn.write_reqs_pending++; - - /* If we don't have any raw data to write - we're done. */ - if (!(ipc_frame.header.flags & UV_IPC_RAW_DATA)) { - return 0; - } } if ((handle->flags & (UV_HANDLE_BLOCKING_WRITES | UV_HANDLE_NON_OVERLAPPED_PIPE)) == (UV_HANDLE_BLOCKING_WRITES | UV_HANDLE_NON_OVERLAPPED_PIPE)) { DWORD bytes; - result = WriteFile(handle->handle, - bufs[0].base, - bufs[0].len, - &bytes, - NULL); + result = + WriteFile(handle->handle, write_buf.base, write_buf.len, &bytes, NULL); if (!result) { err = GetLastError(); @@ -1378,31 +1377,27 @@ static int uv_pipe_write_impl(uv_loop_t* loop, POST_COMPLETION_FOR_REQ(loop, req); return 0; } else if (handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) { - req->write_buffer = bufs[0]; + req->write_buffer = write_buf; uv_insert_non_overlapped_write_req(handle, req); if (handle->stream.conn.write_reqs_pending == 0) { uv_queue_non_overlapped_write(handle); } /* Request queued by the kernel. */ - req->u.io.queued_bytes = bufs[0].len; + req->u.io.queued_bytes = write_buf.len; handle->write_queue_size += req->u.io.queued_bytes; } else if (handle->flags & UV_HANDLE_BLOCKING_WRITES) { /* Using overlapped IO, but wait for completion before returning */ - req->u.io.overlapped.hEvent = CreateEvent(NULL, 1, 0, NULL); - if (!req->u.io.overlapped.hEvent) { - uv_fatal_error(GetLastError(), "CreateEvent"); - } - result = WriteFile(handle->handle, - bufs[0].base, - bufs[0].len, + write_buf.base, + write_buf.len, NULL, &req->u.io.overlapped); if (!result && GetLastError() != ERROR_IO_PENDING) { err = GetLastError(); - CloseHandle(req->u.io.overlapped.hEvent); + CloseHandle(req->event_handle); + req->event_handle = NULL; return err; } @@ -1411,16 +1406,18 @@ static int uv_pipe_write_impl(uv_loop_t* loop, req->u.io.queued_bytes = 0; } else { /* Request queued by the kernel. */ - req->u.io.queued_bytes = bufs[0].len; + req->u.io.queued_bytes = write_buf.len; handle->write_queue_size += req->u.io.queued_bytes; - if (WaitForSingleObject(req->u.io.overlapped.hEvent, INFINITE) != + if (WaitForSingleObject(req->event_handle, INFINITE) != WAIT_OBJECT_0) { err = GetLastError(); - CloseHandle(req->u.io.overlapped.hEvent); - return uv_translate_sys_error(err); + CloseHandle(req->event_handle); + req->event_handle = NULL; + return err; } } - CloseHandle(req->u.io.overlapped.hEvent); + CloseHandle(req->event_handle); + req->event_handle = NULL; REGISTER_HANDLE_REQ(loop, handle, req); handle->reqs_pending++; @@ -1428,8 +1425,8 @@ static int uv_pipe_write_impl(uv_loop_t* loop, return 0; } else { result = WriteFile(handle->handle, - bufs[0].base, - bufs[0].len, + write_buf.base, + write_buf.len, NULL, &req->u.io.overlapped); @@ -1442,17 +1439,13 @@ static int uv_pipe_write_impl(uv_loop_t* loop, req->u.io.queued_bytes = 0; } else { /* Request queued by the kernel. */ - req->u.io.queued_bytes = bufs[0].len; + req->u.io.queued_bytes = write_buf.len; handle->write_queue_size += req->u.io.queued_bytes; } if (handle->flags & UV_HANDLE_EMULATE_IOCP) { - req->event_handle = CreateEvent(NULL, 0, 0, NULL); - if (!req->event_handle) { - uv_fatal_error(GetLastError(), "CreateEvent"); - } if (!RegisterWaitForSingleObject(&req->wait_handle, - req->u.io.overlapped.hEvent, post_completion_write_wait, (void*) req, + req->event_handle, post_completion_write_wait, (void*) req, INFINITE, WT_EXECUTEINWAITTHREAD)) { return GetLastError(); } @@ -1467,35 +1460,143 @@ static int uv_pipe_write_impl(uv_loop_t* loop, } -int uv_pipe_write(uv_loop_t* loop, - uv_write_t* req, - uv_pipe_t* handle, - const uv_buf_t bufs[], - unsigned int nbufs, - uv_write_cb cb) { - return uv_pipe_write_impl(loop, req, handle, bufs, nbufs, NULL, cb); +static DWORD uv__pipe_get_ipc_remote_pid(uv_pipe_t* handle) { + DWORD* pid = &handle->pipe.conn.ipc_remote_pid; + + /* If the both ends of the IPC pipe are owned by the same process, + * the remote end pid may not yet be set. If so, do it here. + * TODO: this is weird; it'd probably better to use a handshake. */ + if (*pid == 0) + *pid = GetCurrentProcessId(); + + return *pid; +} + + +int uv__pipe_write_ipc(uv_loop_t* loop, + uv_write_t* req, + uv_pipe_t* handle, + const uv_buf_t data_bufs[], + size_t data_buf_count, + uv_stream_t* send_handle, + uv_write_cb cb) { + uv_buf_t stack_bufs[6]; + uv_buf_t* bufs; + size_t buf_count, buf_index; + uv__ipc_frame_header_t frame_header; + uv__ipc_socket_xfer_type_t xfer_type = UV__IPC_SOCKET_XFER_NONE; + uv__ipc_socket_xfer_info_t xfer_info; + uint64_t data_length; + size_t i; + int err; + + /* Compute the combined size of data buffers. */ + data_length = 0; + for (i = 0; i < data_buf_count; i++) + data_length += data_bufs[i].len; + if (data_length > UINT32_MAX) + return WSAENOBUFS; /* Maps to UV_ENOBUFS. */ + + /* Prepare the frame's socket xfer payload. */ + if (send_handle != NULL) { + uv_tcp_t* send_tcp_handle = (uv_tcp_t*) send_handle; + + /* Verify that `send_handle` it is indeed a tcp handle. */ + if (send_tcp_handle->type != UV_TCP) + return ERROR_NOT_SUPPORTED; + + /* Export the tcp handle. */ + err = uv__tcp_xfer_export(send_tcp_handle, + uv__pipe_get_ipc_remote_pid(handle), + &xfer_type, + &xfer_info); + if (err != 0) + return err; + } + + /* Compute the number of uv_buf_t's required. */ + buf_count = 1 + data_buf_count; /* Frame header and data buffers. */ + if (send_handle != NULL) + buf_count += 1; /* One extra for the socket xfer information. */ + + /* Use the on-stack buffer array if it is big enough; otherwise allocate + * space for it on the heap. */ + if (buf_count < ARRAY_SIZE(stack_bufs)) { + /* Use on-stack buffer array. */ + bufs = stack_bufs; + } else { + /* Use heap-allocated buffer array. */ + bufs = uv__calloc(buf_count, sizeof(uv_buf_t)); + if (bufs == NULL) + return ERROR_NOT_ENOUGH_MEMORY; /* Maps to UV_ENOMEM. */ + } + buf_index = 0; + + /* Initialize frame header and add it to the buffers list. */ + memset(&frame_header, 0, sizeof frame_header); + bufs[buf_index++] = uv_buf_init((char*) &frame_header, sizeof frame_header); + + if (send_handle != NULL) { + /* Add frame header flags. */ + switch (xfer_type) { + case UV__IPC_SOCKET_XFER_TCP_CONNECTION: + frame_header.flags |= UV__IPC_FRAME_HAS_SOCKET_XFER | + UV__IPC_FRAME_XFER_IS_TCP_CONNECTION; + break; + case UV__IPC_SOCKET_XFER_TCP_SERVER: + frame_header.flags |= UV__IPC_FRAME_HAS_SOCKET_XFER; + break; + default: + assert(0); /* Unreachable. */ + } + /* Add xfer info buffer. */ + bufs[buf_index++] = uv_buf_init((char*) &xfer_info, sizeof xfer_info); + } + + if (data_length > 0) { + /* Update frame header. */ + frame_header.flags |= UV__IPC_FRAME_HAS_DATA; + frame_header.data_length = (uint32_t) data_length; + /* Add data buffers to buffers list. */ + for (i = 0; i < data_buf_count; i++) + bufs[buf_index++] = data_bufs[i]; + } + + /* Write buffers. We set the `always_copy` flag, so it is not a problem that + * some of the written data lives on the stack. */ + err = uv__pipe_write_data(loop, req, handle, bufs, buf_count, cb, 1); + + /* If we had to heap-allocate the bufs array, free it now. */ + if (bufs != stack_bufs) { + uv__free(bufs); + } + + return err; } -int uv_pipe_write2(uv_loop_t* loop, +int uv__pipe_write(uv_loop_t* loop, uv_write_t* req, uv_pipe_t* handle, const uv_buf_t bufs[], - unsigned int nbufs, + size_t nbufs, uv_stream_t* send_handle, uv_write_cb cb) { - if (!handle->ipc) { - return WSAEINVAL; + if (handle->ipc) { + /* IPC pipe write: use framing protocol. */ + return uv__pipe_write_ipc(loop, req, handle, bufs, nbufs, send_handle, cb); + } else { + /* Non-IPC pipe write: put data on the wire directly. */ + assert(send_handle == NULL); + return uv__pipe_write_data(loop, req, handle, bufs, nbufs, cb, 0); } - - return uv_pipe_write_impl(loop, req, handle, bufs, nbufs, send_handle, cb); } static void uv_pipe_read_eof(uv_loop_t* loop, uv_pipe_t* handle, uv_buf_t buf) { - /* If there is an eof timer running, we don't need it any more, */ - /* so discard it. */ + /* If there is an eof timer running, we don't need it any more, so discard + * it. */ eof_timer_destroy(handle); handle->flags &= ~UV_HANDLE_READABLE; @@ -1507,8 +1608,8 @@ static void uv_pipe_read_eof(uv_loop_t* loop, uv_pipe_t* handle, static void uv_pipe_read_error(uv_loop_t* loop, uv_pipe_t* handle, int error, uv_buf_t buf) { - /* If there is an eof timer running, we don't need it any more, */ - /* so discard it. */ + /* If there is an eof timer running, we don't need it any more, so discard + * it. */ eof_timer_destroy(handle); uv_read_stop((uv_stream_t*) handle); @@ -1527,151 +1628,228 @@ static void uv_pipe_read_error_or_eof(uv_loop_t* loop, uv_pipe_t* handle, } -void uv__pipe_insert_pending_socket(uv_pipe_t* handle, - uv__ipc_socket_info_ex* info, - int tcp_connection) { - uv__ipc_queue_item_t* item; +static void uv__pipe_queue_ipc_xfer_info( + uv_pipe_t* handle, + uv__ipc_socket_xfer_type_t xfer_type, + uv__ipc_socket_xfer_info_t* xfer_info) { + uv__ipc_xfer_queue_item_t* item; - item = (uv__ipc_queue_item_t*) uv__malloc(sizeof(*item)); + item = (uv__ipc_xfer_queue_item_t*) uv__malloc(sizeof(*item)); if (item == NULL) uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); - memcpy(&item->socket_info_ex, info, sizeof(item->socket_info_ex)); - item->tcp_connection = tcp_connection; - QUEUE_INSERT_TAIL(&handle->pipe.conn.pending_ipc_info.queue, &item->member); - handle->pipe.conn.pending_ipc_info.queue_len++; + item->xfer_type = xfer_type; + item->xfer_info = *xfer_info; + + QUEUE_INSERT_TAIL(&handle->pipe.conn.ipc_xfer_queue, &item->member); + handle->pipe.conn.ipc_xfer_queue_length++; } -void uv_process_pipe_read_req(uv_loop_t* loop, uv_pipe_t* handle, - uv_req_t* req) { - DWORD bytes, avail; +/* Read an exact number of bytes from a pipe. If an error or end-of-file is + * encountered before the requested number of bytes are read, an error is + * returned. */ +static int uv__pipe_read_exactly(HANDLE h, void* buffer, DWORD count) { + DWORD bytes_read, bytes_read_now; + + bytes_read = 0; + while (bytes_read < count) { + if (!ReadFile(h, + (char*) buffer + bytes_read, + count - bytes_read, + &bytes_read_now, + NULL)) { + return GetLastError(); + } + + bytes_read += bytes_read_now; + } + + assert(bytes_read == count); + return 0; +} + + +static DWORD uv__pipe_read_data(uv_loop_t* loop, + uv_pipe_t* handle, + DWORD suggested_bytes, + DWORD max_bytes) { + DWORD bytes_read; uv_buf_t buf; - uv_ipc_frame_uv_stream ipc_frame; + /* Ask the user for a buffer to read data into. */ + buf = uv_buf_init(NULL, 0); + handle->alloc_cb((uv_handle_t*) handle, suggested_bytes, &buf); + if (buf.base == NULL || buf.len == 0) { + handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &buf); + return 0; /* Break out of read loop. */ + } + + /* Ensure we read at most the smaller of: + * (a) the length of the user-allocated buffer. + * (b) the maximum data length as specified by the `max_bytes` argument. + */ + if (max_bytes > buf.len) + max_bytes = buf.len; + + /* Read into the user buffer. */ + if (!ReadFile(handle->handle, buf.base, max_bytes, &bytes_read, NULL)) { + uv_pipe_read_error_or_eof(loop, handle, GetLastError(), buf); + return 0; /* Break out of read loop. */ + } + + /* Call the read callback. */ + handle->read_cb((uv_stream_t*) handle, bytes_read, &buf); + + return bytes_read; +} + + +static DWORD uv__pipe_read_ipc(uv_loop_t* loop, uv_pipe_t* handle) { + uint32_t* data_remaining = &handle->pipe.conn.ipc_data_frame.payload_remaining; + int err; + + if (*data_remaining > 0) { + /* Read frame data payload. */ + DWORD bytes_read = + uv__pipe_read_data(loop, handle, *data_remaining, *data_remaining); + *data_remaining -= bytes_read; + return bytes_read; + + } else { + /* Start of a new IPC frame. */ + uv__ipc_frame_header_t frame_header; + uint32_t xfer_flags; + uv__ipc_socket_xfer_type_t xfer_type; + uv__ipc_socket_xfer_info_t xfer_info; + + /* Read the IPC frame header. */ + err = uv__pipe_read_exactly( + handle->handle, &frame_header, sizeof frame_header); + if (err) + goto error; + + /* Validate that flags are valid. */ + if ((frame_header.flags & ~UV__IPC_FRAME_VALID_FLAGS) != 0) + goto invalid; + /* Validate that reserved2 is zero. */ + if (frame_header.reserved2 != 0) + goto invalid; + + /* Parse xfer flags. */ + xfer_flags = frame_header.flags & UV__IPC_FRAME_XFER_FLAGS; + if (xfer_flags & UV__IPC_FRAME_HAS_SOCKET_XFER) { + /* Socket coming -- determine the type. */ + xfer_type = xfer_flags & UV__IPC_FRAME_XFER_IS_TCP_CONNECTION + ? UV__IPC_SOCKET_XFER_TCP_CONNECTION + : UV__IPC_SOCKET_XFER_TCP_SERVER; + } else if (xfer_flags == 0) { + /* No socket. */ + xfer_type = UV__IPC_SOCKET_XFER_NONE; + } else { + /* Invalid flags. */ + goto invalid; + } + + /* Parse data frame information. */ + if (frame_header.flags & UV__IPC_FRAME_HAS_DATA) { + *data_remaining = frame_header.data_length; + } else if (frame_header.data_length != 0) { + /* Data length greater than zero but data flag not set -- invalid. */ + goto invalid; + } + + /* If no socket xfer info follows, return here. Data will be read in a + * subsequent invocation of uv__pipe_read_ipc(). */ + if (xfer_type == UV__IPC_SOCKET_XFER_NONE) + return sizeof frame_header; /* Number of bytes read. */ + + /* Read transferred socket information. */ + err = uv__pipe_read_exactly(handle->handle, &xfer_info, sizeof xfer_info); + if (err) + goto error; + + /* Store the pending socket info. */ + uv__pipe_queue_ipc_xfer_info(handle, xfer_type, &xfer_info); + + /* Return number of bytes read. */ + return sizeof frame_header + sizeof xfer_info; + } + +invalid: + /* Invalid frame. */ + err = WSAECONNABORTED; /* Maps to UV_ECONNABORTED. */ + +error: + uv_pipe_read_error_or_eof(loop, handle, err, uv_null_buf_); + return 0; /* Break out of read loop. */ +} + + +void uv_process_pipe_read_req(uv_loop_t* loop, + uv_pipe_t* handle, + uv_req_t* req) { assert(handle->type == UV_NAMED_PIPE); - handle->flags &= ~UV_HANDLE_READ_PENDING; + handle->flags &= ~(UV_HANDLE_READ_PENDING | UV_HANDLE_CANCELLATION_PENDING); + DECREASE_PENDING_REQ_COUNT(handle); eof_timer_stop(handle); - if (!REQ_SUCCESS(req)) { - /* An error occurred doing the 0-read. */ - if (handle->flags & UV_HANDLE_READING) { - uv_pipe_read_error_or_eof(loop, - handle, - GET_REQ_ERROR(req), - uv_null_buf_); - } - } else { - /* Do non-blocking reads until the buffer is empty */ - while (handle->flags & UV_HANDLE_READING) { - if (!PeekNamedPipe(handle->handle, - NULL, - 0, - NULL, - &avail, - NULL)) { - uv_pipe_read_error_or_eof(loop, handle, GetLastError(), uv_null_buf_); - break; - } + /* At this point, we're done with bookkeeping. If the user has stopped + * reading the pipe in the meantime, there is nothing left to do, since there + * is no callback that we can call. */ + if (!(handle->flags & UV_HANDLE_READING)) + return; - if (avail == 0) { - /* There is nothing to read after all. */ - break; - } + if (!REQ_SUCCESS(req)) { + /* An error occurred doing the zero-read. */ + DWORD err = GET_REQ_ERROR(req); - if (handle->ipc) { - /* Use the IPC framing protocol to read the incoming data. */ - if (handle->pipe.conn.remaining_ipc_rawdata_bytes == 0) { - /* We're reading a new frame. First, read the header. */ - assert(avail >= sizeof(ipc_frame.header)); - - if (!ReadFile(handle->handle, - &ipc_frame.header, - sizeof(ipc_frame.header), - &bytes, - NULL)) { - uv_pipe_read_error_or_eof(loop, handle, GetLastError(), - uv_null_buf_); - break; - } - - assert(bytes == sizeof(ipc_frame.header)); - assert(ipc_frame.header.flags <= (UV_IPC_TCP_SERVER | UV_IPC_RAW_DATA | - UV_IPC_TCP_CONNECTION)); - - if (ipc_frame.header.flags & UV_IPC_TCP_SERVER) { - assert(avail - sizeof(ipc_frame.header) >= - sizeof(ipc_frame.socket_info_ex)); - - /* Read the TCP socket info. */ - if (!ReadFile(handle->handle, - &ipc_frame.socket_info_ex, - sizeof(ipc_frame) - sizeof(ipc_frame.header), - &bytes, - NULL)) { - uv_pipe_read_error_or_eof(loop, handle, GetLastError(), - uv_null_buf_); - break; - } - - assert(bytes == sizeof(ipc_frame) - sizeof(ipc_frame.header)); - - /* Store the pending socket info. */ - uv__pipe_insert_pending_socket( - handle, - &ipc_frame.socket_info_ex, - ipc_frame.header.flags & UV_IPC_TCP_CONNECTION); - } - - if (ipc_frame.header.flags & UV_IPC_RAW_DATA) { - handle->pipe.conn.remaining_ipc_rawdata_bytes = - ipc_frame.header.raw_data_length; - continue; - } - } else { - avail = min(avail, (DWORD)handle->pipe.conn.remaining_ipc_rawdata_bytes); - } - } + /* If the read was cancelled by uv__pipe_interrupt_read(), the request may + * indicate an ERROR_OPERATION_ABORTED error. This error isn't relevant to + * the user; we'll start a new zero-read at the end of this function. */ + if (err != ERROR_OPERATION_ABORTED) + uv_pipe_read_error_or_eof(loop, handle, err, uv_null_buf_); - handle->alloc_cb((uv_handle_t*) handle, avail, &buf); - if (buf.len == 0) { - handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &buf); + } else { + /* The zero-read completed without error, indicating there is data + * available in the kernel buffer. */ + DWORD avail; + + /* Get the number of bytes available. */ + avail = 0; + if (!PeekNamedPipe(handle->handle, NULL, 0, NULL, &avail, NULL)) + uv_pipe_read_error_or_eof(loop, handle, GetLastError(), uv_null_buf_); + + /* Read until we've either read all the bytes available, or the 'reading' + * flag is cleared. */ + while (avail > 0 && handle->flags & UV_HANDLE_READING) { + /* Depending on the type of pipe, read either IPC frames or raw data. */ + DWORD bytes_read = + handle->ipc ? uv__pipe_read_ipc(loop, handle) + : uv__pipe_read_data(loop, handle, avail, (DWORD) -1); + + /* If no bytes were read, treat this as an indication that an error + * occurred, and break out of the read loop. */ + if (bytes_read == 0) break; - } - assert(buf.base != NULL); - - if (ReadFile(handle->handle, - buf.base, - min(buf.len, avail), - &bytes, - NULL)) { - /* Successful read */ - if (handle->ipc) { - assert(handle->pipe.conn.remaining_ipc_rawdata_bytes >= bytes); - handle->pipe.conn.remaining_ipc_rawdata_bytes = - handle->pipe.conn.remaining_ipc_rawdata_bytes - bytes; - } - handle->read_cb((uv_stream_t*)handle, bytes, &buf); - /* Read again only if bytes == buf.len */ - if (bytes <= buf.len) { - break; - } - } else { - uv_pipe_read_error_or_eof(loop, handle, GetLastError(), buf); + /* It is possible that more bytes were read than we thought were + * available. To prevent `avail` from underflowing, break out of the loop + * if this is the case. */ + if (bytes_read > avail) break; - } - } - /* Post another 0-read if still reading and not closing. */ - if ((handle->flags & UV_HANDLE_READING) && - !(handle->flags & UV_HANDLE_READ_PENDING)) { - uv_pipe_queue_read(loop, handle); + /* Recompute the number of bytes available. */ + avail -= bytes_read; } } - DECREASE_PENDING_REQ_COUNT(handle); + /* Start another zero-read request if necessary. */ + if ((handle->flags & UV_HANDLE_READING) && + !(handle->flags & UV_HANDLE_READ_PENDING)) { + uv_pipe_queue_read(loop, handle); + } } @@ -1697,17 +1875,19 @@ void uv_process_pipe_write_req(uv_loop_t* loop, uv_pipe_t* handle, } } - if (req->ipc_header) { - if (req == &handle->pipe.conn.ipc_header_write_req) { - req->type = UV_UNKNOWN_REQ; - } else { - uv__free(req); - } - } else { - if (req->cb) { - err = GET_REQ_ERROR(req); - req->cb(req, uv_translate_sys_error(err)); - } + err = GET_REQ_ERROR(req); + + /* If this was a coalesced write, extract pointer to the user_provided + * uv_write_t structure so we can pass the expected pointer to the callback, + * then free the heap-allocated write req. */ + if (req->coalesced) { + uv__coalesced_write_t* coalesced_write = + container_of(req, uv__coalesced_write_t, req); + req = coalesced_write->user_req; + uv__free(coalesced_write); + } + if (req->cb) { + req->cb(req, uv_translate_sys_error(err)); } handle->stream.conn.write_reqs_pending--; @@ -1733,7 +1913,7 @@ void uv_process_pipe_accept_req(uv_loop_t* loop, uv_pipe_t* handle, assert(handle->type == UV_NAMED_PIPE); - if (handle->flags & UV__HANDLE_CLOSING) { + if (handle->flags & UV_HANDLE_CLOSING) { /* The req->pipeHandle should be freed already in uv_pipe_cleanup(). */ assert(req->pipeHandle == INVALID_HANDLE_VALUE); DECREASE_PENDING_REQ_COUNT(handle); @@ -1753,7 +1933,7 @@ void uv_process_pipe_accept_req(uv_loop_t* loop, uv_pipe_t* handle, CloseHandle(req->pipeHandle); req->pipeHandle = INVALID_HANDLE_VALUE; } - if (!(handle->flags & UV__HANDLE_CLOSING)) { + if (!(handle->flags & UV_HANDLE_CLOSING)) { uv_pipe_queue_accept(loop, handle, req, FALSE); } } @@ -1791,19 +1971,19 @@ void uv_process_pipe_shutdown_req(uv_loop_t* loop, uv_pipe_t* handle, UNREGISTER_HANDLE_REQ(loop, handle, req); if (handle->flags & UV_HANDLE_READABLE) { - /* Initialize and optionally start the eof timer. Only do this if the */ - /* pipe is readable and we haven't seen EOF come in ourselves. */ + /* Initialize and optionally start the eof timer. Only do this if the pipe + * is readable and we haven't seen EOF come in ourselves. */ eof_timer_init(handle); - /* If reading start the timer right now. */ - /* Otherwise uv_pipe_queue_read will start it. */ + /* If reading start the timer right now. Otherwise uv_pipe_queue_read will + * start it. */ if (handle->flags & UV_HANDLE_READ_PENDING) { eof_timer_start(handle); } } else { - /* This pipe is not readable. We can just close it to let the other end */ - /* know that we're done writing. */ + /* This pipe is not readable. We can just close it to let the other end + * know that we're done writing. */ close_pipe(handle); } @@ -1854,17 +2034,16 @@ static void eof_timer_cb(uv_timer_t* timer) { assert(pipe->type == UV_NAMED_PIPE); - /* This should always be true, since we start the timer only */ - /* in uv_pipe_queue_read after successfully calling ReadFile, */ - /* or in uv_process_pipe_shutdown_req if a read is pending, */ - /* and we always immediately stop the timer in */ - /* uv_process_pipe_read_req. */ + /* This should always be true, since we start the timer only in + * uv_pipe_queue_read after successfully calling ReadFile, or in + * uv_process_pipe_shutdown_req if a read is pending, and we always + * immediately stop the timer in uv_process_pipe_read_req. */ assert(pipe->flags & UV_HANDLE_READ_PENDING); - /* If there are many packets coming off the iocp then the timer callback */ - /* may be called before the read request is coming off the queue. */ - /* Therefore we check here if the read request has completed but will */ - /* be processed later. */ + /* If there are many packets coming off the iocp then the timer callback may + * be called before the read request is coming off the queue. Therefore we + * check here if the read request has completed but will be processed later. + */ if ((pipe->flags & UV_HANDLE_READ_PENDING) && HasOverlappedIoCompleted(&pipe->read_req.u.io.overlapped)) { return; @@ -1873,12 +2052,12 @@ static void eof_timer_cb(uv_timer_t* timer) { /* Force both ends off the pipe. */ close_pipe(pipe); - /* Stop reading, so the pending read that is going to fail will */ - /* not be reported to the user. */ + /* Stop reading, so the pending read that is going to fail will not be + * reported to the user. */ uv_read_stop((uv_stream_t*) pipe); - /* Report the eof and update flags. This will get reported even if the */ - /* user stopped reading in the meantime. TODO: is that okay? */ + /* Report the eof and update flags. This will get reported even if the user + * stopped reading in the meantime. TODO: is that okay? */ uv_pipe_read_eof(loop, pipe, uv_null_buf_); } @@ -1909,6 +2088,7 @@ int uv_pipe_open(uv_pipe_t* pipe, uv_file file) { if (os_handle == INVALID_HANDLE_VALUE) return UV_EBADF; + uv__once_init(); /* In order to avoid closing a stdio file descriptor 0-2, duplicate the * underlying OS handle and forget about the original fd. * We could also opt to use the original OS handle and just never close it, @@ -1964,8 +2144,8 @@ int uv_pipe_open(uv_pipe_t* pipe, uv_file file) { if (pipe->ipc) { assert(!(pipe->flags & UV_HANDLE_NON_OVERLAPPED_PIPE)); - pipe->pipe.conn.ipc_pid = uv_parent_pid(); - assert(pipe->pipe.conn.ipc_pid != -1); + pipe->pipe.conn.ipc_remote_pid = uv_os_getppid(); + assert(pipe->pipe.conn.ipc_remote_pid != (DWORD)(uv_pid_t) -1); } return 0; } @@ -1982,6 +2162,7 @@ static int uv__pipe_getname(const uv_pipe_t* handle, char* buffer, size_t* size) unsigned int name_len; int err; + uv__once_init(); name_info = NULL; if (handle->handle == INVALID_HANDLE_VALUE) { @@ -1989,7 +2170,15 @@ static int uv__pipe_getname(const uv_pipe_t* handle, char* buffer, size_t* size) return UV_EINVAL; } - uv__pipe_pause_read((uv_pipe_t*)handle); /* cast away const warning */ + /* NtQueryInformationFile will block if another thread is performing a + * blocking operation on the queried handle. If the pipe handle is + * synchronous, there may be a worker thread currently calling ReadFile() on + * the pipe handle, which could cause a deadlock. To avoid this, interrupt + * the read. */ + if (handle->flags & UV_HANDLE_CONNECTION && + handle->flags & UV_HANDLE_NON_OVERLAPPED_PIPE) { + uv__pipe_interrupt_read((uv_pipe_t*) handle); /* cast away const warning */ + } nt_status = pNtQueryInformationFile(handle->handle, &io_status, @@ -2075,13 +2264,11 @@ static int uv__pipe_getname(const uv_pipe_t* handle, char* buffer, size_t* size) buffer[addrlen] = '\0'; err = 0; - goto cleanup; error: uv__free(name_info); cleanup: - uv__pipe_unpause_read((uv_pipe_t*)handle); /* cast away const warning */ return err; } @@ -2089,7 +2276,7 @@ static int uv__pipe_getname(const uv_pipe_t* handle, char* buffer, size_t* size) int uv_pipe_pending_count(uv_pipe_t* handle) { if (!handle->ipc) return 0; - return handle->pipe.conn.pending_ipc_info.queue_len; + return handle->pipe.conn.ipc_xfer_queue_length; } @@ -2122,8 +2309,85 @@ int uv_pipe_getpeername(const uv_pipe_t* handle, char* buffer, size_t* size) { uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle) { if (!handle->ipc) return UV_UNKNOWN_HANDLE; - if (handle->pipe.conn.pending_ipc_info.queue_len == 0) + if (handle->pipe.conn.ipc_xfer_queue_length == 0) return UV_UNKNOWN_HANDLE; else return UV_TCP; } + +int uv_pipe_chmod(uv_pipe_t* handle, int mode) { + SID_IDENTIFIER_AUTHORITY sid_world = { SECURITY_WORLD_SID_AUTHORITY }; + PACL old_dacl, new_dacl; + PSECURITY_DESCRIPTOR sd; + EXPLICIT_ACCESS ea; + PSID everyone; + int error; + + if (handle == NULL || handle->handle == INVALID_HANDLE_VALUE) + return UV_EBADF; + + if (mode != UV_READABLE && + mode != UV_WRITABLE && + mode != (UV_WRITABLE | UV_READABLE)) + return UV_EINVAL; + + if (!AllocateAndInitializeSid(&sid_world, + 1, + SECURITY_WORLD_RID, + 0, 0, 0, 0, 0, 0, 0, + &everyone)) { + error = GetLastError(); + goto done; + } + + if (GetSecurityInfo(handle->handle, + SE_KERNEL_OBJECT, + DACL_SECURITY_INFORMATION, + NULL, + NULL, + &old_dacl, + NULL, + &sd)) { + error = GetLastError(); + goto clean_sid; + } + + memset(&ea, 0, sizeof(EXPLICIT_ACCESS)); + if (mode & UV_READABLE) + ea.grfAccessPermissions |= GENERIC_READ | FILE_WRITE_ATTRIBUTES; + if (mode & UV_WRITABLE) + ea.grfAccessPermissions |= GENERIC_WRITE | FILE_READ_ATTRIBUTES; + ea.grfAccessPermissions |= SYNCHRONIZE; + ea.grfAccessMode = SET_ACCESS; + ea.grfInheritance = NO_INHERITANCE; + ea.Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea.Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP; + ea.Trustee.ptstrName = (LPTSTR)everyone; + + if (SetEntriesInAcl(1, &ea, old_dacl, &new_dacl)) { + error = GetLastError(); + goto clean_sd; + } + + if (SetSecurityInfo(handle->handle, + SE_KERNEL_OBJECT, + DACL_SECURITY_INFORMATION, + NULL, + NULL, + new_dacl, + NULL)) { + error = GetLastError(); + goto clean_dacl; + } + + error = 0; + +clean_dacl: + LocalFree((HLOCAL) new_dacl); +clean_sd: + LocalFree((HLOCAL) sd); +clean_sid: + FreeSid(everyone); +done: + return uv_translate_sys_error(error); +} diff --git a/include/libuv/src/win/poll.c b/include/libuv/src/win/poll.c index d479e521e..87858590c 100644 --- a/include/libuv/src/win/poll.c +++ b/include/libuv/src/win/poll.c @@ -61,13 +61,13 @@ static void uv__init_overlapped_dummy(void) { } -static OVERLAPPED* uv__get_overlapped_dummy() { +static OVERLAPPED* uv__get_overlapped_dummy(void) { uv_once(&overlapped_dummy_init_guard_, uv__init_overlapped_dummy); return &overlapped_dummy_; } -static AFD_POLL_INFO* uv__get_afd_poll_info_dummy() { +static AFD_POLL_INFO* uv__get_afd_poll_info_dummy(void) { return &afd_poll_info_dummy_; } @@ -75,7 +75,7 @@ static AFD_POLL_INFO* uv__get_afd_poll_info_dummy() { static void uv__fast_poll_submit_poll_req(uv_loop_t* loop, uv_poll_t* handle) { uv_req_t* req; AFD_POLL_INFO* afd_poll_info; - DWORD result; + int result; /* Find a yet unsubmitted req to submit. */ if (handle->submitted_events_1 == 0) { @@ -91,16 +91,16 @@ static void uv__fast_poll_submit_poll_req(uv_loop_t* loop, uv_poll_t* handle) { handle->mask_events_1 = handle->events; handle->mask_events_2 = 0; } else { - /* Just wait until there's an unsubmitted req. */ - /* This will happen almost immediately as one of the 2 outstanding */ - /* requests is about to return. When this happens, */ - /* uv__fast_poll_process_poll_req will be called, and the pending */ - /* events, if needed, will be processed in a subsequent request. */ + /* Just wait until there's an unsubmitted req. This will happen almost + * immediately as one of the 2 outstanding requests is about to return. + * When this happens, uv__fast_poll_process_poll_req will be called, and + * the pending events, if needed, will be processed in a subsequent + * request. */ return; } - /* Setting Exclusive to TRUE makes the other poll request return if there */ - /* is any. */ + /* Setting Exclusive to TRUE makes the other poll request return if there is + * any. */ afd_poll_info->Exclusive = TRUE; afd_poll_info->NumberOfHandles = 1; afd_poll_info->Timeout.QuadPart = INT64_MAX; @@ -134,32 +134,6 @@ static void uv__fast_poll_submit_poll_req(uv_loop_t* loop, uv_poll_t* handle) { } -static int uv__fast_poll_cancel_poll_req(uv_loop_t* loop, uv_poll_t* handle) { - AFD_POLL_INFO afd_poll_info; - DWORD result; - - afd_poll_info.Exclusive = TRUE; - afd_poll_info.NumberOfHandles = 1; - afd_poll_info.Timeout.QuadPart = INT64_MAX; - afd_poll_info.Handles[0].Handle = (HANDLE) handle->socket; - afd_poll_info.Handles[0].Status = 0; - afd_poll_info.Handles[0].Events = AFD_POLL_ALL; - - result = uv_msafd_poll(handle->socket, - &afd_poll_info, - uv__get_afd_poll_info_dummy(), - uv__get_overlapped_dummy()); - - if (result == SOCKET_ERROR) { - DWORD error = WSAGetLastError(); - if (error != WSA_IO_PENDING) - return error; - } - - return 0; -} - - static void uv__fast_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, uv_req_t* req) { unsigned char mask_events; @@ -218,7 +192,7 @@ static void uv__fast_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, if ((handle->events & ~(handle->submitted_events_1 | handle->submitted_events_2)) != 0) { uv__fast_poll_submit_poll_req(loop, handle); - } else if ((handle->flags & UV__HANDLE_CLOSING) && + } else if ((handle->flags & UV_HANDLE_CLOSING) && handle->submitted_events_1 == 0 && handle->submitted_events_2 == 0) { uv_want_endgame(loop, (uv_handle_t*) handle); @@ -226,44 +200,6 @@ static void uv__fast_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, } -static int uv__fast_poll_set(uv_loop_t* loop, uv_poll_t* handle, int events) { - assert(handle->type == UV_POLL); - assert(!(handle->flags & UV__HANDLE_CLOSING)); - assert((events & ~(UV_READABLE | UV_WRITABLE | UV_DISCONNECT)) == 0); - - handle->events = events; - - if (handle->events != 0) { - uv__handle_start(handle); - } else { - uv__handle_stop(handle); - } - - if ((handle->events & ~(handle->submitted_events_1 | - handle->submitted_events_2)) != 0) { - uv__fast_poll_submit_poll_req(handle->loop, handle); - } - - return 0; -} - - -static int uv__fast_poll_close(uv_loop_t* loop, uv_poll_t* handle) { - handle->events = 0; - uv__handle_closing(handle); - - if (handle->submitted_events_1 == 0 && - handle->submitted_events_2 == 0) { - uv_want_endgame(loop, (uv_handle_t*) handle); - return 0; - } else { - /* Cancel outstanding poll requests by executing another, unique poll */ - /* request that forces the outstanding ones to return. */ - return uv__fast_poll_cancel_poll_req(loop, handle); - } -} - - static SOCKET uv__fast_poll_create_peer_socket(HANDLE iocp, WSAPROTOCOL_INFOW* protocol_info) { SOCKET sock = 0; @@ -316,9 +252,8 @@ static SOCKET uv__fast_poll_get_peer_socket(uv_loop_t* loop, return INVALID_SOCKET; } - /* If we didn't (try) to create a peer socket yet, try to make one. Don't */ - /* try again if the peer socket creation failed earlier for the same */ - /* protocol. */ + /* If we didn't (try) to create a peer socket yet, try to make one. Don't try + * again if the peer socket creation failed earlier for the same protocol. */ peer_socket = loop->poll_peer_sockets[index]; if (peer_socket == 0) { peer_socket = uv__fast_poll_create_peer_socket(loop->iocp, protocol_info); @@ -357,8 +292,8 @@ static DWORD WINAPI uv__slow_poll_thread_proc(void* arg) { efds.fd_count = 0; } - /* Make the select() time out after 3 minutes. If select() hangs because */ - /* the user closed the socket, we will at least not hang indefinitely. */ + /* Make the select() time out after 3 minutes. If select() hangs because the + * user closed the socket, we will at least not hang indefinitely. */ timeout.tv_sec = 3 * 60; timeout.tv_usec = 0; @@ -462,7 +397,7 @@ static void uv__slow_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, if ((handle->events & ~(handle->submitted_events_1 | handle->submitted_events_2)) != 0) { uv__slow_poll_submit_poll_req(loop, handle); - } else if ((handle->flags & UV__HANDLE_CLOSING) && + } else if ((handle->flags & UV_HANDLE_CLOSING) && handle->submitted_events_1 == 0 && handle->submitted_events_2 == 0) { uv_want_endgame(loop, (uv_handle_t*) handle); @@ -470,41 +405,6 @@ static void uv__slow_poll_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, } -static int uv__slow_poll_set(uv_loop_t* loop, uv_poll_t* handle, int events) { - assert(handle->type == UV_POLL); - assert(!(handle->flags & UV__HANDLE_CLOSING)); - assert((events & ~(UV_READABLE | UV_WRITABLE)) == 0); - - handle->events = events; - - if (handle->events != 0) { - uv__handle_start(handle); - } else { - uv__handle_stop(handle); - } - - if ((handle->events & - ~(handle->submitted_events_1 | handle->submitted_events_2)) != 0) { - uv__slow_poll_submit_poll_req(handle->loop, handle); - } - - return 0; -} - - -static int uv__slow_poll_close(uv_loop_t* loop, uv_poll_t* handle) { - handle->events = 0; - uv__handle_closing(handle); - - if (handle->submitted_events_1 == 0 && - handle->submitted_events_2 == 0) { - uv_want_endgame(loop, (uv_handle_t*) handle); - } - - return 0; -} - - int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd) { return uv_poll_init_socket(loop, handle, (SOCKET) uv__get_osfhandle(fd)); } @@ -522,10 +422,10 @@ int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle, if (ioctlsocket(socket, FIONBIO, &yes) == SOCKET_ERROR) return uv_translate_sys_error(WSAGetLastError()); - /* Try to obtain a base handle for the socket. This increases this chances */ - /* that we find an AFD handle and are able to use the fast poll mechanism. */ - /* This will always fail on windows XP/2k3, since they don't support the */ - /* SIO_BASE_HANDLE ioctl. */ +/* Try to obtain a base handle for the socket. This increases this chances that + * we find an AFD handle and are able to use the fast poll mechanism. This will + * always fail on windows XP/2k3, since they don't support the. SIO_BASE_HANDLE + * ioctl. */ #ifndef NDEBUG base_socket = INVALID_SOCKET; #endif @@ -557,9 +457,9 @@ int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle, return uv_translate_sys_error(WSAGetLastError()); } - /* Get the peer socket that is needed to enable fast poll. If the returned */ - /* value is NULL, the protocol is not implemented by MSAFD and we'll have */ - /* to use slow mode. */ + /* Get the peer socket that is needed to enable fast poll. If the returned + * value is NULL, the protocol is not implemented by MSAFD and we'll have to + * use slow mode. */ peer_socket = uv__fast_poll_get_peer_socket(loop, &protocol_info); if (peer_socket != INVALID_SOCKET) { @@ -572,48 +472,54 @@ int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle, /* Initialize 2 poll reqs. */ handle->submitted_events_1 = 0; - uv_req_init(loop, (uv_req_t*) &(handle->poll_req_1)); - handle->poll_req_1.type = UV_POLL_REQ; + UV_REQ_INIT(&handle->poll_req_1, UV_POLL_REQ); handle->poll_req_1.data = handle; handle->submitted_events_2 = 0; - uv_req_init(loop, (uv_req_t*) &(handle->poll_req_2)); - handle->poll_req_2.type = UV_POLL_REQ; + UV_REQ_INIT(&handle->poll_req_2, UV_POLL_REQ); handle->poll_req_2.data = handle; return 0; } -int uv_poll_start(uv_poll_t* handle, int events, uv_poll_cb cb) { - int err; +static int uv__poll_set(uv_poll_t* handle, int events, uv_poll_cb cb) { + int submitted_events; - if (!(handle->flags & UV_HANDLE_POLL_SLOW)) { - err = uv__fast_poll_set(handle->loop, handle, events); - } else { - err = uv__slow_poll_set(handle->loop, handle, events); - } + assert(handle->type == UV_POLL); + assert(!(handle->flags & UV_HANDLE_CLOSING)); + assert((events & ~(UV_READABLE | UV_WRITABLE | UV_DISCONNECT)) == 0); + + handle->events = events; + handle->poll_cb = cb; - if (err) { - return uv_translate_sys_error(err); + if (handle->events == 0) { + uv__handle_stop(handle); + return 0; } - handle->poll_cb = cb; + uv__handle_start(handle); + submitted_events = handle->submitted_events_1 | handle->submitted_events_2; + + if (handle->events & ~submitted_events) { + if (handle->flags & UV_HANDLE_POLL_SLOW) { + uv__slow_poll_submit_poll_req(handle->loop, handle); + } else { + uv__fast_poll_submit_poll_req(handle->loop, handle); + } + } return 0; } -int uv_poll_stop(uv_poll_t* handle) { - int err; +int uv_poll_start(uv_poll_t* handle, int events, uv_poll_cb cb) { + return uv__poll_set(handle, events, cb); +} - if (!(handle->flags & UV_HANDLE_POLL_SLOW)) { - err = uv__fast_poll_set(handle->loop, handle, 0); - } else { - err = uv__slow_poll_set(handle->loop, handle, 0); - } - return uv_translate_sys_error(err); +int uv_poll_stop(uv_poll_t* handle) { + return uv__poll_set(handle, 0, handle->poll_cb); } @@ -627,16 +533,48 @@ void uv_process_poll_req(uv_loop_t* loop, uv_poll_t* handle, uv_req_t* req) { int uv_poll_close(uv_loop_t* loop, uv_poll_t* handle) { - if (!(handle->flags & UV_HANDLE_POLL_SLOW)) { - return uv__fast_poll_close(loop, handle); - } else { - return uv__slow_poll_close(loop, handle); + AFD_POLL_INFO afd_poll_info; + DWORD error; + int result; + + handle->events = 0; + uv__handle_closing(handle); + + if (handle->submitted_events_1 == 0 && + handle->submitted_events_2 == 0) { + uv_want_endgame(loop, (uv_handle_t*) handle); + return 0; } + + if (handle->flags & UV_HANDLE_POLL_SLOW) + return 0; + + /* Cancel outstanding poll requests by executing another, unique poll + * request that forces the outstanding ones to return. */ + afd_poll_info.Exclusive = TRUE; + afd_poll_info.NumberOfHandles = 1; + afd_poll_info.Timeout.QuadPart = INT64_MAX; + afd_poll_info.Handles[0].Handle = (HANDLE) handle->socket; + afd_poll_info.Handles[0].Status = 0; + afd_poll_info.Handles[0].Events = AFD_POLL_ALL; + + result = uv_msafd_poll(handle->socket, + &afd_poll_info, + uv__get_afd_poll_info_dummy(), + uv__get_overlapped_dummy()); + + if (result == SOCKET_ERROR) { + error = WSAGetLastError(); + if (error != WSA_IO_PENDING) + return uv_translate_sys_error(error); + } + + return 0; } void uv_poll_endgame(uv_loop_t* loop, uv_poll_t* handle) { - assert(handle->flags & UV__HANDLE_CLOSING); + assert(handle->flags & UV_HANDLE_CLOSING); assert(!(handle->flags & UV_HANDLE_CLOSED)); assert(handle->submitted_events_1 == 0); diff --git a/include/libuv/src/win/process-stdio.c b/include/libuv/src/win/process-stdio.c index e3c06f57d..355d61880 100644 --- a/include/libuv/src/win/process-stdio.c +++ b/include/libuv/src/win/process-stdio.c @@ -103,12 +103,12 @@ static int uv__create_stdio_pipe_pair(uv_loop_t* loop, DWORD client_access = 0; HANDLE child_pipe = INVALID_HANDLE_VALUE; int err; + int overlap; if (flags & UV_READABLE_PIPE) { - /* The server needs inbound access too, otherwise CreateNamedPipe() */ - /* won't give us the FILE_READ_ATTRIBUTES permission. We need that to */ - /* probe the state of the write buffer when we're trying to shutdown */ - /* the pipe. */ + /* The server needs inbound access too, otherwise CreateNamedPipe() won't + * give us the FILE_READ_ATTRIBUTES permission. We need that to probe the + * state of the write buffer when we're trying to shutdown the pipe. */ server_access |= PIPE_ACCESS_OUTBOUND | PIPE_ACCESS_INBOUND; client_access |= GENERIC_READ | FILE_WRITE_ATTRIBUTES; } @@ -131,12 +131,13 @@ static int uv__create_stdio_pipe_pair(uv_loop_t* loop, sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; + overlap = server_pipe->ipc || (flags & UV_OVERLAPPED_PIPE); child_pipe = CreateFileA(pipe_name, client_access, 0, &sa, OPEN_EXISTING, - server_pipe->ipc ? FILE_FLAG_OVERLAPPED : 0, + overlap ? FILE_FLAG_OVERLAPPED : 0, NULL); if (child_pipe == INVALID_HANDLE_VALUE) { err = GetLastError(); @@ -159,8 +160,8 @@ static int uv__create_stdio_pipe_pair(uv_loop_t* loop, } #endif - /* Do a blocking ConnectNamedPipe. This should not block because we have */ - /* both ends of the pipe created. */ + /* Do a blocking ConnectNamedPipe. This should not block because we have both + * ends of the pipe created. */ if (!ConnectNamedPipe(server_pipe->handle, NULL)) { if (GetLastError() != ERROR_PIPE_CONNECTED) { err = GetLastError(); @@ -194,11 +195,11 @@ static int uv__duplicate_handle(uv_loop_t* loop, HANDLE handle, HANDLE* dup) { HANDLE current_process; - /* _get_osfhandle will sometimes return -2 in case of an error. This seems */ - /* to happen when fd <= 2 and the process' corresponding stdio handle is */ - /* set to NULL. Unfortunately DuplicateHandle will happily duplicate */ - /* (HANDLE) -2, so this situation goes unnoticed until someone tries to */ - /* use the duplicate. Therefore we filter out known-invalid handles here. */ + /* _get_osfhandle will sometimes return -2 in case of an error. This seems to + * happen when fd <= 2 and the process' corresponding stdio handle is set to + * NULL. Unfortunately DuplicateHandle will happily duplicate (HANDLE) -2, so + * this situation goes unnoticed until someone tries to use the duplicate. + * Therefore we filter out known-invalid handles here. */ if (handle == INVALID_HANDLE_VALUE || handle == NULL || handle == (HANDLE) -2) { @@ -284,8 +285,8 @@ int uv__stdio_create(uv_loop_t* loop, return ERROR_OUTOFMEMORY; } - /* Prepopulate the buffer with INVALID_HANDLE_VALUE handles so we can */ - /* clean up on failure. */ + /* Prepopulate the buffer with INVALID_HANDLE_VALUE handles so we can clean + * up on failure. */ CHILD_STDIO_COUNT(buffer) = count; for (i = 0; i < count; i++) { CHILD_STDIO_CRT_FLAGS(buffer, i) = 0; @@ -303,12 +304,12 @@ int uv__stdio_create(uv_loop_t* loop, switch (fdopt.flags & (UV_IGNORE | UV_CREATE_PIPE | UV_INHERIT_FD | UV_INHERIT_STREAM)) { case UV_IGNORE: - /* Starting a process with no stdin/stout/stderr can confuse it. */ - /* So no matter what the user specified, we make sure the first */ - /* three FDs are always open in their typical modes, e.g. stdin */ - /* be readable and stdout/err should be writable. For FDs > 2, don't */ - /* do anything - all handles in the stdio buffer are initialized with */ - /* INVALID_HANDLE_VALUE, which should be okay. */ + /* Starting a process with no stdin/stout/stderr can confuse it. So no + * matter what the user specified, we make sure the first three FDs are + * always open in their typical modes, e. g. stdin be readable and + * stdout/err should be writable. For FDs > 2, don't do anything - all + * handles in the stdio buffer are initialized with. + * INVALID_HANDLE_VALUE, which should be okay. */ if (i <= 2) { DWORD access = (i == 0) ? FILE_GENERIC_READ : FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES; @@ -323,14 +324,14 @@ int uv__stdio_create(uv_loop_t* loop, break; case UV_CREATE_PIPE: { - /* Create a pair of two connected pipe ends; one end is turned into */ - /* an uv_pipe_t for use by the parent. The other one is given to */ - /* the child. */ + /* Create a pair of two connected pipe ends; one end is turned into an + * uv_pipe_t for use by the parent. The other one is given to the + * child. */ uv_pipe_t* parent_pipe = (uv_pipe_t*) fdopt.data.stream; HANDLE child_pipe = INVALID_HANDLE_VALUE; - /* Create a new, connected pipe pair. stdio[i].stream should point */ - /* to an uninitialized, but not connected pipe handle. */ + /* Create a new, connected pipe pair. stdio[i]. stream should point to + * an uninitialized, but not connected pipe handle. */ assert(fdopt.data.stream->type == UV_NAMED_PIPE); assert(!(fdopt.data.stream->flags & UV_HANDLE_CONNECTION)); assert(!(fdopt.data.stream->flags & UV_HANDLE_PIPESERVER)); @@ -354,8 +355,8 @@ int uv__stdio_create(uv_loop_t* loop, /* Make an inheritable duplicate of the handle. */ err = uv__duplicate_fd(loop, fdopt.data.fd, &child_handle); if (err) { - /* If fdopt.data.fd is not valid and fd fd <= 2, then ignore the */ - /* error. */ + /* If fdopt. data. fd is not valid and fd <= 2, then ignore the + * error. */ if (fdopt.data.fd <= 2 && err == ERROR_INVALID_HANDLE) { CHILD_STDIO_CRT_FLAGS(buffer, i) = 0; CHILD_STDIO_HANDLE(buffer, i) = INVALID_HANDLE_VALUE; @@ -372,6 +373,7 @@ int uv__stdio_create(uv_loop_t* loop, case FILE_TYPE_PIPE: CHILD_STDIO_CRT_FLAGS(buffer, i) = FOPEN | FPIPE; + break; case FILE_TYPE_CHAR: case FILE_TYPE_REMOTE: @@ -417,8 +419,8 @@ int uv__stdio_create(uv_loop_t* loop, if (stream_handle == NULL || stream_handle == INVALID_HANDLE_VALUE) { - /* The handle is already closed, or not yet created, or the */ - /* stream type is not supported. */ + /* The handle is already closed, or not yet created, or the stream + * type is not supported. */ err = ERROR_NOT_SUPPORTED; goto error; } diff --git a/include/libuv/src/win/process.c b/include/libuv/src/win/process.c index 855c37408..73543c6ed 100644 --- a/include/libuv/src/win/process.c +++ b/include/libuv/src/win/process.c @@ -58,7 +58,6 @@ static const env_var_t required_vars[] = { /* keep me sorted */ E_V("USERPROFILE"), E_V("WINDIR"), }; -static size_t n_required_vars = ARRAY_SIZE(required_vars); static HANDLE uv_global_job_handle_; @@ -148,8 +147,7 @@ static void uv_process_init(uv_loop_t* loop, uv_process_t* handle) { handle->child_stdio_buffer = NULL; handle->exit_cb_pending = 0; - uv_req_init(loop, (uv_req_t*)&handle->exit_req); - handle->exit_req.type = UV_PROCESS_EXIT; + UV_REQ_INIT(&handle->exit_req, UV_PROCESS_EXIT); handle->exit_req.data = handle; } @@ -361,8 +359,8 @@ static WCHAR* search_path(const WCHAR *file, return NULL; } - /* Find the start of the filename so we can split the directory from the */ - /* name. */ + /* Find the start of the filename so we can split the directory from the + * name. */ for (file_name_start = (WCHAR*)file + file_len; file_name_start > file && file_name_start[-1] != L'\\' @@ -406,8 +404,15 @@ static WCHAR* search_path(const WCHAR *file, /* Next slice starts just after where the previous one ended */ dir_start = dir_end; + /* If path is quoted, find quote end */ + if (*dir_start == L'"' || *dir_start == L'\'') { + dir_end = wcschr(dir_start + 1, *dir_start); + if (dir_end == NULL) { + dir_end = wcschr(dir_start, L'\0'); + } + } /* Slice until the next ; or \0 is found */ - dir_end = wcschr(dir_start, L';'); + dir_end = wcschr(dir_end, L';'); if (dir_end == NULL) { dir_end = wcschr(dir_start, L'\0'); } @@ -492,7 +497,7 @@ WCHAR* quote_cmd_arg(const WCHAR *source, WCHAR *target) { * input : hello\\"world * output: "hello\\\\\"world" * input : hello world\ - * output: "hello world\" + * output: "hello world\\" */ *(target++) = L'"'; @@ -550,8 +555,8 @@ int make_program_args(char** args, int verbatim_arguments, WCHAR** dst_ptr) { arg_count++; } - /* Adjust for potential quotes. Also assume the worst-case scenario */ - /* that every character needs escaping, so we need twice as much space. */ + /* Adjust for potential quotes. Also assume the worst-case scenario that + * every character needs escaping, so we need twice as much space. */ dst_len = dst_len * 2 + arg_count * 2; /* Allocate buffer for the final command line. */ @@ -686,7 +691,7 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) { WCHAR* dst_copy; WCHAR** ptr_copy; WCHAR** env_copy; - DWORD* required_vars_value_len = alloca(n_required_vars * sizeof(DWORD*)); + DWORD required_vars_value_len[ARRAY_SIZE(required_vars)]; /* first pass: determine size in UTF-16 */ for (env = env_block; *env; env++) { @@ -708,7 +713,7 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) { /* second pass: copy to UTF-16 environment block */ dst_copy = (WCHAR*)uv__malloc(env_len * sizeof(WCHAR)); - if (!dst_copy) { + if (dst_copy == NULL && env_len > 0) { return ERROR_OUTOFMEMORY; } env_copy = alloca(env_block_count * sizeof(WCHAR*)); @@ -733,13 +738,13 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) { } } *ptr_copy = NULL; - assert(env_len == ptr - dst_copy); + assert(env_len == 0 || env_len == (size_t) (ptr - dst_copy)); /* sort our (UTF-16) copy */ qsort(env_copy, env_block_count-1, sizeof(wchar_t*), qsort_wcscmp); /* third pass: check for required variables */ - for (ptr_copy = env_copy, i = 0; i < n_required_vars; ) { + for (ptr_copy = env_copy, i = 0; i < ARRAY_SIZE(required_vars); ) { int cmp; if (!*ptr_copy) { cmp = -1; @@ -772,10 +777,10 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) { } for (ptr = dst, ptr_copy = env_copy, i = 0; - *ptr_copy || i < n_required_vars; + *ptr_copy || i < ARRAY_SIZE(required_vars); ptr += len) { int cmp; - if (i >= n_required_vars) { + if (i >= ARRAY_SIZE(required_vars)) { cmp = 1; } else if (!*ptr_copy) { cmp = -1; @@ -793,7 +798,7 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) { var_size = GetEnvironmentVariableW(required_vars[i].wide, ptr, (int) (env_len - (ptr - dst))); - if (var_size != len-1) { /* race condition? */ + if (var_size != (DWORD) (len - 1)) { /* TODO: handle race condition? */ uv_fatal_error(GetLastError(), "GetEnvironmentVariableW"); } } @@ -809,7 +814,7 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) { } /* Terminate with an extra NULL. */ - assert(env_len == (ptr - dst)); + assert(env_len == (size_t) (ptr - dst)); *ptr = L'\0'; uv__free(dst_copy); @@ -825,8 +830,13 @@ int make_program_env(char* env_block[], WCHAR** dst_ptr) { */ static WCHAR* find_path(WCHAR *env) { for (; env != NULL && *env != 0; env += wcslen(env) + 1) { - if (wcsncmp(env, L"PATH=", 5) == 0) + if ((env[0] == L'P' || env[0] == L'p') && + (env[1] == L'A' || env[1] == L'a') && + (env[2] == L'T' || env[2] == L't') && + (env[3] == L'H' || env[3] == L'h') && + (env[4] == L'=')) { return &env[5]; + } } return NULL; @@ -859,9 +869,9 @@ void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle) { assert(handle->exit_cb_pending); handle->exit_cb_pending = 0; - /* If we're closing, don't call the exit callback. Just schedule a close */ - /* callback now. */ - if (handle->flags & UV__HANDLE_CLOSING) { + /* If we're closing, don't call the exit callback. Just schedule a close + * callback now. */ + if (handle->flags & UV_HANDLE_CLOSING) { uv_want_endgame(loop, (uv_handle_t*) handle); return; } @@ -872,14 +882,14 @@ void uv_process_proc_exit(uv_loop_t* loop, uv_process_t* handle) { handle->wait_handle = INVALID_HANDLE_VALUE; } - /* Set the handle to inactive: no callbacks will be made after the exit */ - /* callback.*/ + /* Set the handle to inactive: no callbacks will be made after the exit + * callback. */ uv__handle_stop(handle); if (GetExitCodeProcess(handle->process_handle, &status)) { exit_code = status; } else { - /* Unable to to obtain the exit code. This should never happen. */ + /* Unable to obtain the exit code. This should never happen. */ exit_code = uv_translate_sys_error(GetLastError()); } @@ -894,8 +904,8 @@ void uv_process_close(uv_loop_t* loop, uv_process_t* handle) { uv__handle_closing(handle); if (handle->wait_handle != INVALID_HANDLE_VALUE) { - /* This blocks until either the wait was cancelled, or the callback has */ - /* completed. */ + /* This blocks until either the wait was cancelled, or the callback has + * completed. */ BOOL r = UnregisterWaitEx(handle->wait_handle, INVALID_HANDLE_VALUE); if (!r) { /* This should never happen, and if it happens, we can't recover... */ @@ -913,7 +923,7 @@ void uv_process_close(uv_loop_t* loop, uv_process_t* handle) { void uv_process_endgame(uv_loop_t* loop, uv_process_t* handle) { assert(!handle->exit_cb_pending); - assert(handle->flags & UV__HANDLE_CLOSING); + assert(handle->flags & UV_HANDLE_CLOSING); assert(!(handle->flags & UV_HANDLE_CLOSED)); /* Clean-up the process handle. */ @@ -953,6 +963,8 @@ int uv_spawn(uv_loop_t* loop, UV_PROCESS_SETGID | UV_PROCESS_SETUID | UV_PROCESS_WINDOWS_HIDE | + UV_PROCESS_WINDOWS_HIDE_CONSOLE | + UV_PROCESS_WINDOWS_HIDE_GUI | UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS))); err = uv_utf8_to_utf16_alloc(options->file, &application); @@ -1052,15 +1064,26 @@ int uv_spawn(uv_loop_t* loop, startup.hStdOutput = uv__stdio_handle(process->child_stdio_buffer, 1); startup.hStdError = uv__stdio_handle(process->child_stdio_buffer, 2); - if (options->flags & UV_PROCESS_WINDOWS_HIDE) { + process_flags = CREATE_UNICODE_ENVIRONMENT; + + if ((options->flags & UV_PROCESS_WINDOWS_HIDE_CONSOLE) || + (options->flags & UV_PROCESS_WINDOWS_HIDE)) { + /* Avoid creating console window if stdio is not inherited. */ + for (i = 0; i < options->stdio_count; i++) { + if (options->stdio[i].flags & UV_INHERIT_FD) + break; + if (i == options->stdio_count - 1) + process_flags |= CREATE_NO_WINDOW; + } + } + if ((options->flags & UV_PROCESS_WINDOWS_HIDE_GUI) || + (options->flags & UV_PROCESS_WINDOWS_HIDE)) { /* Use SW_HIDE to avoid any potential process window. */ startup.wShowWindow = SW_HIDE; } else { startup.wShowWindow = SW_SHOWDEFAULT; } - process_flags = CREATE_UNICODE_ENVIRONMENT; - if (options->flags & UV_PROCESS_DETACHED) { /* Note that we're not setting the CREATE_BREAKAWAY_FROM_JOB flag. That * means that libuv might not let you create a fully daemonized process @@ -1090,14 +1113,13 @@ int uv_spawn(uv_loop_t* loop, goto done; } - /* Spawn succeeded */ - /* Beyond this point, failure is reported asynchronously. */ + /* Spawn succeeded. Beyond this point, failure is reported asynchronously. */ process->process_handle = info.hProcess; process->pid = info.dwProcessId; - /* If the process isn't spawned as detached, assign to the global job */ - /* object so windows will kill it when the parent process dies. */ + /* If the process isn't spawned as detached, assign to the global job object + * so windows will kill it when the parent process dies. */ if (!(options->flags & UV_PROCESS_DETACHED)) { uv_once(&uv_global_job_handle_init_guard_, uv__init_global_job_handle); @@ -1124,7 +1146,8 @@ int uv_spawn(uv_loop_t* loop, if (fdopt->flags & UV_CREATE_PIPE && fdopt->data.stream->type == UV_NAMED_PIPE && ((uv_pipe_t*) fdopt->data.stream)->ipc) { - ((uv_pipe_t*) fdopt->data.stream)->pipe.conn.ipc_pid = info.dwProcessId; + ((uv_pipe_t*) fdopt->data.stream)->pipe.conn.ipc_remote_pid = + info.dwProcessId; } } @@ -1140,8 +1163,8 @@ int uv_spawn(uv_loop_t* loop, assert(!err); - /* Make the handle active. It will remain active until the exit callback */ - /* is made or the handle is closed, whichever happens first. */ + /* Make the handle active. It will remain active until the exit callback is + * made or the handle is closed, whichever happens first. */ uv__handle_start(process); /* Cleanup, whether we succeeded or failed. */ @@ -1164,20 +1187,24 @@ int uv_spawn(uv_loop_t* loop, static int uv__kill(HANDLE process_handle, int signum) { + if (signum < 0 || signum >= NSIG) { + return UV_EINVAL; + } + switch (signum) { case SIGTERM: case SIGKILL: case SIGINT: { - /* Unconditionally terminate the process. On Windows, killed processes */ - /* normally return 1. */ + /* Unconditionally terminate the process. On Windows, killed processes + * normally return 1. */ DWORD status; int err; if (TerminateProcess(process_handle, 1)) return 0; - /* If the process already exited before TerminateProcess was called, */ - /* TerminateProcess will fail with ERROR_ACCESS_DENIED. */ + /* If the process already exited before TerminateProcess was called,. + * TerminateProcess will fail with ERROR_ACCESS_DENIED. */ err = GetLastError(); if (err == ERROR_ACCESS_DENIED && GetExitCodeProcess(process_handle, &status) && @@ -1228,8 +1255,15 @@ int uv_process_kill(uv_process_t* process, int signum) { int uv_kill(int pid, int signum) { int err; - HANDLE process_handle = OpenProcess(PROCESS_TERMINATE | - PROCESS_QUERY_INFORMATION, FALSE, pid); + HANDLE process_handle; + + if (pid == 0) { + process_handle = GetCurrentProcess(); + } else { + process_handle = OpenProcess(PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION, + FALSE, + pid); + } if (process_handle == NULL) { err = GetLastError(); diff --git a/include/libuv/src/win/req-inl.h b/include/libuv/src/win/req-inl.h index b5e502eef..f2513b7d3 100644 --- a/include/libuv/src/win/req-inl.h +++ b/include/libuv/src/win/req-inl.h @@ -34,6 +34,9 @@ #define SET_REQ_ERROR(req, error) \ SET_REQ_STATUS((req), NTSTATUS_FROM_WIN32((error))) +/* Note: used open-coded in UV_REQ_INIT() because of a circular dependency + * between src/uv-common.h and src/win/internal.h. + */ #define SET_REQ_SUCCESS(req) \ SET_REQ_STATUS((req), STATUS_SUCCESS) @@ -79,12 +82,6 @@ } -INLINE static void uv_req_init(uv_loop_t* loop, uv_req_t* req) { - req->type = UV_UNKNOWN_REQ; - SET_REQ_SUCCESS(req); -} - - INLINE static uv_req_t* uv_overlapped_to_req(OVERLAPPED* overlapped) { return CONTAINING_RECORD(overlapped, uv_req_t, u.io.overlapped); } diff --git a/include/libuv/src/win/signal.c b/include/libuv/src/win/signal.c index 2c64a55dc..3d9f92cfb 100644 --- a/include/libuv/src/win/signal.c +++ b/include/libuv/src/win/signal.c @@ -30,23 +30,35 @@ RB_HEAD(uv_signal_tree_s, uv_signal_s); static struct uv_signal_tree_s uv__signal_tree = RB_INITIALIZER(uv__signal_tree); -static ssize_t volatile uv__signal_control_handler_refs = 0; static CRITICAL_SECTION uv__signal_lock; +static BOOL WINAPI uv__signal_control_handler(DWORD type); -void uv_signals_init() { +int uv__signal_start(uv_signal_t* handle, + uv_signal_cb signal_cb, + int signum, + int oneshot); + +void uv_signals_init(void) { InitializeCriticalSection(&uv__signal_lock); + if (!SetConsoleCtrlHandler(uv__signal_control_handler, TRUE)) + abort(); +} + + +void uv__signal_cleanup(void) { + /* TODO(bnoordhuis) Undo effects of uv_signal_init()? */ } static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2) { - /* Compare signums first so all watchers with the same signnum end up */ - /* adjacent. */ + /* Compare signums first so all watchers with the same signnum end up + * adjacent. */ if (w1->signum < w2->signum) return -1; if (w1->signum > w2->signum) return 1; - /* Sort by loop pointer, so we can easily look up the first item after */ - /* { .signum = x, .loop = NULL } */ + /* Sort by loop pointer, so we can easily look up the first item after + * { .signum = x, .loop = NULL }. */ if ((uintptr_t) w1->loop < (uintptr_t) w2->loop) return -1; if ((uintptr_t) w1->loop > (uintptr_t) w2->loop) return 1; @@ -57,7 +69,7 @@ static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2) { } -RB_GENERATE_STATIC(uv_signal_tree_s, uv_signal_s, tree_entry, uv__signal_compare); +RB_GENERATE_STATIC(uv_signal_tree_s, uv_signal_s, tree_entry, uv__signal_compare) /* @@ -68,7 +80,9 @@ RB_GENERATE_STATIC(uv_signal_tree_s, uv_signal_s, tree_entry, uv__signal_compare int uv__signal_dispatch(int signum) { uv_signal_t lookup; uv_signal_t* handle; - int dispatched = 0; + int dispatched; + + dispatched = 0; EnterCriticalSection(&uv__signal_lock); @@ -81,11 +95,16 @@ int uv__signal_dispatch(int signum) { unsigned long previous = InterlockedExchange( (volatile LONG*) &handle->pending_signum, signum); + if (handle->flags & UV_SIGNAL_ONE_SHOT_DISPATCHED) + continue; + if (!previous) { POST_COMPLETION_FOR_REQ(handle->loop, &handle->signal_req); } dispatched = 1; + if (handle->flags & UV_SIGNAL_ONE_SHOT) + handle->flags |= UV_SIGNAL_ONE_SHOT_DISPATCHED; } LeaveCriticalSection(&uv__signal_lock); @@ -104,10 +123,10 @@ static BOOL WINAPI uv__signal_control_handler(DWORD type) { case CTRL_CLOSE_EVENT: if (uv__signal_dispatch(SIGHUP)) { - /* Windows will terminate the process after the control handler */ - /* returns. After that it will just terminate our process. Therefore */ - /* block the signal handler so the main loop has some time to pick */ - /* up the signal and do something for a few seconds. */ + /* Windows will terminate the process after the control handler + * returns. After that it will just terminate our process. Therefore + * block the signal handler so the main loop has some time to pick up + * the signal and do something for a few seconds. */ Sleep(INFINITE); return TRUE; } @@ -115,8 +134,8 @@ static BOOL WINAPI uv__signal_control_handler(DWORD type) { case CTRL_LOGOFF_EVENT: case CTRL_SHUTDOWN_EVENT: - /* These signals are only sent to services. Services have their own */ - /* notification mechanism, so there's no point in handling these. */ + /* These signals are only sent to services. Services have their own + * notification mechanism, so there's no point in handling these. */ default: /* We don't handle these. */ @@ -125,114 +144,14 @@ static BOOL WINAPI uv__signal_control_handler(DWORD type) { } -static int uv__signal_register_control_handler() { - /* When this function is called, the uv__signal_lock must be held. */ - - /* If the console control handler has already been hooked, just add a */ - /* reference. */ - if (uv__signal_control_handler_refs > 0) { - uv__signal_control_handler_refs++; - return 0; - } - - if (!SetConsoleCtrlHandler(uv__signal_control_handler, TRUE)) - return GetLastError(); - - uv__signal_control_handler_refs++; - - return 0; -} - - -static void uv__signal_unregister_control_handler() { - /* When this function is called, the uv__signal_lock must be held. */ - BOOL r; - - /* Don't unregister if the number of console control handlers exceeds one. */ - /* Just remove a reference in that case. */ - if (uv__signal_control_handler_refs > 1) { - uv__signal_control_handler_refs--; - return; - } - - assert(uv__signal_control_handler_refs == 1); - - r = SetConsoleCtrlHandler(uv__signal_control_handler, FALSE); - /* This should never fail; if it does it is probably a bug in libuv. */ - assert(r); - - uv__signal_control_handler_refs--; -} - - -static int uv__signal_register(int signum) { - switch (signum) { - case SIGINT: - case SIGBREAK: - case SIGHUP: - return uv__signal_register_control_handler(); - - case SIGWINCH: - /* SIGWINCH is generated in tty.c. No need to register anything. */ - return 0; - - case SIGILL: - case SIGABRT_COMPAT: - case SIGFPE: - case SIGSEGV: - case SIGTERM: - case SIGABRT: - /* Signal is never raised. */ - return 0; - - default: - /* Invalid signal. */ - return ERROR_INVALID_PARAMETER; - } -} - - -static void uv__signal_unregister(int signum) { - switch (signum) { - case SIGINT: - case SIGBREAK: - case SIGHUP: - uv__signal_unregister_control_handler(); - return; - - case SIGWINCH: - /* SIGWINCH is generated in tty.c. No need to unregister anything. */ - return; - - case SIGILL: - case SIGABRT_COMPAT: - case SIGFPE: - case SIGSEGV: - case SIGTERM: - case SIGABRT: - /* Nothing is registered for this signal. */ - return; - - default: - /* Libuv bug. */ - assert(0 && "Invalid signum"); - return; - } -} - - int uv_signal_init(uv_loop_t* loop, uv_signal_t* handle) { - uv_req_t* req; - uv__handle_init(loop, (uv_handle_t*) handle, UV_SIGNAL); handle->pending_signum = 0; handle->signum = 0; handle->signal_cb = NULL; - req = &handle->signal_req; - uv_req_init(loop, req); - req->type = UV_SIGNAL_REQ; - req->data = handle; + UV_REQ_INIT(&handle->signal_req, UV_SIGNAL_REQ); + handle->signal_req.data = handle; return 0; } @@ -247,8 +166,6 @@ int uv_signal_stop(uv_signal_t* handle) { EnterCriticalSection(&uv__signal_lock); - uv__signal_unregister(handle->signum); - removed_handle = RB_REMOVE(uv_signal_tree_s, &uv__signal_tree, handle); assert(removed_handle == handle); @@ -262,19 +179,29 @@ int uv_signal_stop(uv_signal_t* handle) { int uv_signal_start(uv_signal_t* handle, uv_signal_cb signal_cb, int signum) { - int err; + return uv__signal_start(handle, signal_cb, signum, 0); +} + + +int uv_signal_start_oneshot(uv_signal_t* handle, + uv_signal_cb signal_cb, + int signum) { + return uv__signal_start(handle, signal_cb, signum, 1); +} - /* If the user supplies signum == 0, then return an error already. If the */ - /* signum is otherwise invalid then uv__signal_register will find out */ - /* eventually. */ - if (signum == 0) { + +int uv__signal_start(uv_signal_t* handle, + uv_signal_cb signal_cb, + int signum, + int oneshot) { + /* Test for invalid signal values. */ + if (signum <= 0 || signum >= NSIG) return UV_EINVAL; - } - /* Short circuit: if the signal watcher is already watching {signum} don't */ - /* go through the process of deregistering and registering the handler. */ - /* Additionally, this avoids pending signals getting lost in the (small) */ - /* time frame that handle->signum == 0. */ + /* Short circuit: if the signal watcher is already watching {signum} don't go + * through the process of deregistering and registering the handler. + * Additionally, this avoids pending signals getting lost in the (small) time + * frame that handle->signum == 0. */ if (signum == handle->signum) { handle->signal_cb = signal_cb; return 0; @@ -289,14 +216,10 @@ int uv_signal_start(uv_signal_t* handle, uv_signal_cb signal_cb, int signum) { EnterCriticalSection(&uv__signal_lock); - err = uv__signal_register(signum); - if (err) { - /* Uh-oh, didn't work. */ - LeaveCriticalSection(&uv__signal_lock); - return uv_translate_sys_error(err); - } - handle->signum = signum; + if (oneshot) + handle->flags |= UV_SIGNAL_ONE_SHOT; + RB_INSERT(uv_signal_tree_s, &uv__signal_tree, handle); LeaveCriticalSection(&uv__signal_lock); @@ -319,13 +242,16 @@ void uv_process_signal_req(uv_loop_t* loop, uv_signal_t* handle, (volatile LONG*) &handle->pending_signum, 0); assert(dispatched_signum != 0); - /* Check if the pending signal equals the signum that we are watching for. */ - /* These can get out of sync when the handler is stopped and restarted */ - /* while the signal_req is pending. */ + /* Check if the pending signal equals the signum that we are watching for. + * These can get out of sync when the handler is stopped and restarted while + * the signal_req is pending. */ if (dispatched_signum == handle->signum) handle->signal_cb(handle, dispatched_signum); - if (handle->flags & UV__HANDLE_CLOSING) { + if (handle->flags & UV_SIGNAL_ONE_SHOT) + uv_signal_stop(handle); + + if (handle->flags & UV_HANDLE_CLOSING) { /* When it is closing, it must be stopped at this point. */ assert(handle->signum == 0); uv_want_endgame(loop, (uv_handle_t*) handle); @@ -344,7 +270,7 @@ void uv_signal_close(uv_loop_t* loop, uv_signal_t* handle) { void uv_signal_endgame(uv_loop_t* loop, uv_signal_t* handle) { - assert(handle->flags & UV__HANDLE_CLOSING); + assert(handle->flags & UV_HANDLE_CLOSING); assert(!(handle->flags & UV_HANDLE_CLOSED)); assert(handle->signum == 0); diff --git a/include/libuv/src/win/stream-inl.h b/include/libuv/src/win/stream-inl.h index b7a3c1195..40f5ddd51 100644 --- a/include/libuv/src/win/stream-inl.h +++ b/include/libuv/src/win/stream-inl.h @@ -36,20 +36,18 @@ INLINE static void uv_stream_init(uv_loop_t* loop, uv__handle_init(loop, (uv_handle_t*) handle, type); handle->write_queue_size = 0; handle->activecnt = 0; -} - - -INLINE static void uv_connection_init(uv_stream_t* handle) { - handle->flags |= UV_HANDLE_CONNECTION; + handle->stream.conn.shutdown_req = NULL; handle->stream.conn.write_reqs_pending = 0; - uv_req_init(handle->loop, (uv_req_t*) &(handle->read_req)); + UV_REQ_INIT(&handle->read_req, UV_READ); handle->read_req.event_handle = NULL; handle->read_req.wait_handle = INVALID_HANDLE_VALUE; - handle->read_req.type = UV_READ; handle->read_req.data = handle; +} - handle->stream.conn.shutdown_req = NULL; + +INLINE static void uv_connection_init(uv_stream_t* handle) { + handle->flags |= UV_HANDLE_CONNECTION; } diff --git a/include/libuv/src/win/stream.c b/include/libuv/src/win/stream.c index a2466e5e9..46a0709a3 100644 --- a/include/libuv/src/win/stream.c +++ b/include/libuv/src/win/stream.c @@ -105,12 +105,10 @@ int uv_read_stop(uv_stream_t* handle) { err = 0; if (handle->type == UV_TTY) { err = uv_tty_read_stop((uv_tty_t*) handle); + } else if (handle->type == UV_NAMED_PIPE) { + uv__pipe_read_stop((uv_pipe_t*) handle); } else { - if (handle->type == UV_NAMED_PIPE) { - uv__pipe_stop_read((uv_pipe_t*) handle); - } else { - handle->flags &= ~UV_HANDLE_READING; - } + handle->flags &= ~UV_HANDLE_READING; DECREASE_ACTIVE_COUNT(handle->loop, handle); } @@ -136,7 +134,8 @@ int uv_write(uv_write_t* req, err = uv_tcp_write(loop, req, (uv_tcp_t*) handle, bufs, nbufs, cb); break; case UV_NAMED_PIPE: - err = uv_pipe_write(loop, req, (uv_pipe_t*) handle, bufs, nbufs, cb); + err = uv__pipe_write( + loop, req, (uv_pipe_t*) handle, bufs, nbufs, NULL, cb); break; case UV_TTY: err = uv_tty_write(loop, req, (uv_tty_t*) handle, bufs, nbufs, cb); @@ -158,25 +157,18 @@ int uv_write2(uv_write_t* req, uv_loop_t* loop = handle->loop; int err; - if (!(handle->flags & UV_HANDLE_WRITABLE)) { - return UV_EPIPE; + if (send_handle == NULL) { + return uv_write(req, handle, bufs, nbufs, cb); } - err = ERROR_INVALID_PARAMETER; - switch (handle->type) { - case UV_NAMED_PIPE: - err = uv_pipe_write2(loop, - req, - (uv_pipe_t*) handle, - bufs, - nbufs, - send_handle, - cb); - break; - default: - assert(0); + if (handle->type != UV_NAMED_PIPE || !((uv_pipe_t*) handle)->ipc) { + return UV_EINVAL; + } else if (!(handle->flags & UV_HANDLE_WRITABLE)) { + return UV_EPIPE; } + err = uv__pipe_write( + loop, req, (uv_pipe_t*) handle, bufs, nbufs, send_handle, cb); return uv_translate_sys_error(err); } @@ -184,7 +176,7 @@ int uv_write2(uv_write_t* req, int uv_try_write(uv_stream_t* stream, const uv_buf_t bufs[], unsigned int nbufs) { - if (stream->flags & UV__HANDLE_CLOSING) + if (stream->flags & UV_HANDLE_CLOSING) return UV_EBADF; if (!(stream->flags & UV_HANDLE_WRITABLE)) return UV_EPIPE; @@ -206,16 +198,18 @@ int uv_try_write(uv_stream_t* stream, int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) { uv_loop_t* loop = handle->loop; - if (!(handle->flags & UV_HANDLE_WRITABLE)) { - return UV_EPIPE; + if (!(handle->flags & UV_HANDLE_WRITABLE) || + handle->flags & UV_HANDLE_SHUTTING || + uv__is_closing(handle)) { + return UV_ENOTCONN; } - uv_req_init(loop, (uv_req_t*) req); - req->type = UV_SHUTDOWN; + UV_REQ_INIT(req, UV_SHUTDOWN); req->handle = handle; req->cb = cb; handle->flags &= ~UV_HANDLE_WRITABLE; + handle->flags |= UV_HANDLE_SHUTTING; handle->stream.conn.shutdown_req = req; handle->reqs_pending++; REGISTER_HANDLE_REQ(loop, handle, req); diff --git a/include/libuv/src/win/tcp.c b/include/libuv/src/win/tcp.c index 0f5654863..0dcaa97df 100644 --- a/include/libuv/src/win/tcp.c +++ b/include/libuv/src/win/tcp.c @@ -99,8 +99,8 @@ static int uv_tcp_set_socket(uv_loop_t* loop, if (!SetHandleInformation((HANDLE) socket, HANDLE_FLAG_INHERIT, 0)) return GetLastError(); - /* Associate it with the I/O completion port. */ - /* Use uv_handle_t pointer as completion key. */ + /* Associate it with the I/O completion port. Use uv_handle_t pointer as + * completion key. */ if (CreateIoCompletionPort((HANDLE)socket, loop->iocp, (ULONG_PTR)socket, @@ -118,15 +118,12 @@ static int uv_tcp_set_socket(uv_loop_t* loop, non_ifs_lsp = uv_tcp_non_ifs_lsp_ipv4; } - if (pSetFileCompletionNotificationModes && - !(handle->flags & UV_HANDLE_EMULATE_IOCP) && !non_ifs_lsp) { - if (pSetFileCompletionNotificationModes((HANDLE) socket, - FILE_SKIP_SET_EVENT_ON_HANDLE | - FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)) { - handle->flags |= UV_HANDLE_SYNC_BYPASS_IOCP; - } else if (GetLastError() != ERROR_INVALID_FUNCTION) { + if (!(handle->flags & UV_HANDLE_EMULATE_IOCP) && !non_ifs_lsp) { + UCHAR sfcnm_flags = + FILE_SKIP_SET_EVENT_ON_HANDLE | FILE_SKIP_COMPLETION_PORT_ON_SUCCESS; + if (!SetFileCompletionNotificationModes((HANDLE) socket, sfcnm_flags)) return GetLastError(); - } + handle->flags |= UV_HANDLE_SYNC_BYPASS_IOCP; } if (handle->flags & UV_HANDLE_TCP_NODELAY) { @@ -220,7 +217,7 @@ void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) { UNREGISTER_HANDLE_REQ(loop, handle, handle->stream.conn.shutdown_req); err = 0; - if (handle->flags & UV__HANDLE_CLOSING) { + if (handle->flags & UV_HANDLE_CLOSING) { err = ERROR_OPERATION_ABORTED; } else if (shutdown(handle->socket, SD_SEND) == SOCKET_ERROR) { err = WSAGetLastError(); @@ -236,7 +233,7 @@ void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) { return; } - if (handle->flags & UV__HANDLE_CLOSING && + if (handle->flags & UV_HANDLE_CLOSING && handle->reqs_pending == 0) { assert(!(handle->flags & UV_HANDLE_CLOSED)); @@ -254,7 +251,7 @@ void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) { UnregisterWait(req->wait_handle); req->wait_handle = INVALID_HANDLE_VALUE; } - if (req->event_handle) { + if (req->event_handle != NULL) { CloseHandle(req->event_handle); req->event_handle = NULL; } @@ -271,7 +268,7 @@ void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) { UnregisterWait(handle->read_req.wait_handle); handle->read_req.wait_handle = INVALID_HANDLE_VALUE; } - if (handle->read_req.event_handle) { + if (handle->read_req.event_handle != NULL) { CloseHandle(handle->read_req.event_handle); handle->read_req.event_handle = NULL; } @@ -326,9 +323,9 @@ static int uv_tcp_try_bind(uv_tcp_t* handle, on = (flags & UV_TCP_IPV6ONLY) != 0; - /* TODO: how to handle errors? This may fail if there is no ipv4 stack */ - /* available, or when run on XP/2003 which have no support for dualstack */ - /* sockets. For now we're silently ignoring the error. */ + /* TODO: how to handle errors? This may fail if there is no ipv4 stack + * available, or when run on XP/2003 which have no support for dualstack + * sockets. For now we're silently ignoring the error. */ setsockopt(handle->socket, IPPROTO_IPV6, IPV6_V6ONLY, @@ -431,6 +428,7 @@ static void uv_tcp_queue_accept(uv_tcp_t* handle, uv_tcp_accept_t* req) { /* Prepare the overlapped structure. */ memset(&(req->u.io.overlapped), 0, sizeof(req->u.io.overlapped)); if (handle->flags & UV_HANDLE_EMULATE_IOCP) { + assert(req->event_handle != NULL); req->u.io.overlapped.hEvent = (HANDLE) ((ULONG_PTR) req->event_handle | 1); } @@ -459,8 +457,6 @@ static void uv_tcp_queue_accept(uv_tcp_t* handle, uv_tcp_accept_t* req) { INFINITE, WT_EXECUTEINWAITTHREAD)) { SET_REQ_ERROR(req, GetLastError()); uv_insert_pending_req(loop, (uv_req_t*)req); - handle->reqs_pending++; - return; } } else { /* Make this req pending reporting an error. */ @@ -471,7 +467,7 @@ static void uv_tcp_queue_accept(uv_tcp_t* handle, uv_tcp_accept_t* req) { closesocket(accept_socket); /* Destroy the event handle */ if (handle->flags & UV_HANDLE_EMULATE_IOCP) { - CloseHandle(req->u.io.overlapped.hEvent); + CloseHandle(req->event_handle); req->event_handle = NULL; } } @@ -496,8 +492,10 @@ static void uv_tcp_queue_read(uv_loop_t* loop, uv_tcp_t* handle) { */ if (loop->active_tcp_streams < uv_active_tcp_streams_threshold) { handle->flags &= ~UV_HANDLE_ZERO_READ; + handle->tcp.conn.read_buffer = uv_buf_init(NULL, 0); handle->alloc_cb((uv_handle_t*) handle, 65536, &handle->tcp.conn.read_buffer); - if (handle->tcp.conn.read_buffer.len == 0) { + if (handle->tcp.conn.read_buffer.base == NULL || + handle->tcp.conn.read_buffer.len == 0) { handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &handle->tcp.conn.read_buffer); return; } @@ -512,7 +510,7 @@ static void uv_tcp_queue_read(uv_loop_t* loop, uv_tcp_t* handle) { /* Prepare the overlapped structure. */ memset(&(req->u.io.overlapped), 0, sizeof(req->u.io.overlapped)); if (handle->flags & UV_HANDLE_EMULATE_IOCP) { - assert(req->event_handle); + assert(req->event_handle != NULL); req->u.io.overlapped.hEvent = (HANDLE) ((ULONG_PTR) req->event_handle | 1); } @@ -525,16 +523,15 @@ static void uv_tcp_queue_read(uv_loop_t* loop, uv_tcp_t* handle) { &req->u.io.overlapped, NULL); + handle->flags |= UV_HANDLE_READ_PENDING; + handle->reqs_pending++; + if (UV_SUCCEEDED_WITHOUT_IOCP(result == 0)) { /* Process the req without IOCP. */ - handle->flags |= UV_HANDLE_READ_PENDING; req->u.io.overlapped.InternalHigh = bytes; - handle->reqs_pending++; uv_insert_pending_req(loop, (uv_req_t*)req); } else if (UV_SUCCEEDED_WITH_IOCP(result == 0)) { /* The req will be processed with IOCP. */ - handle->flags |= UV_HANDLE_READ_PENDING; - handle->reqs_pending++; if (handle->flags & UV_HANDLE_EMULATE_IOCP && req->wait_handle == INVALID_HANDLE_VALUE && !RegisterWaitForSingleObject(&req->wait_handle, @@ -547,13 +544,26 @@ static void uv_tcp_queue_read(uv_loop_t* loop, uv_tcp_t* handle) { /* Make this req pending reporting an error. */ SET_REQ_ERROR(req, WSAGetLastError()); uv_insert_pending_req(loop, (uv_req_t*)req); - handle->reqs_pending++; } } +int uv_tcp_close_reset(uv_tcp_t* handle, uv_close_cb close_cb) { + struct linger l = { 1, 0 }; + + /* Disallow setting SO_LINGER to zero due to some platform inconsistencies */ + if (handle->flags & UV_HANDLE_SHUTTING) + return UV_EINVAL; + + if (0 != setsockopt(handle->socket, SOL_SOCKET, SO_LINGER, (const char*)&l, sizeof(l))) + return uv_translate_sys_error(WSAGetLastError()); + + uv_close((uv_handle_t*) handle, close_cb); + return 0; +} + + int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) { - uv_loop_t* loop = handle->loop; unsigned int i, simultaneous_accepts; uv_tcp_accept_t* req; int err; @@ -601,8 +611,8 @@ int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) { simultaneous_accepts = handle->flags & UV_HANDLE_TCP_SINGLE_ACCEPT ? 1 : uv_simultaneous_server_accepts; - if(!handle->tcp.serv.accept_reqs) { - handle->tcp.serv.accept_reqs = (uv_tcp_accept_t*) + if (handle->tcp.serv.accept_reqs == NULL) { + handle->tcp.serv.accept_reqs = uv__malloc(uv_simultaneous_server_accepts * sizeof(uv_tcp_accept_t)); if (!handle->tcp.serv.accept_reqs) { uv_fatal_error(ERROR_OUTOFMEMORY, "uv__malloc"); @@ -610,15 +620,14 @@ int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) { for (i = 0; i < simultaneous_accepts; i++) { req = &handle->tcp.serv.accept_reqs[i]; - uv_req_init(loop, (uv_req_t*)req); - req->type = UV_ACCEPT; + UV_REQ_INIT(req, UV_ACCEPT); req->accept_socket = INVALID_SOCKET; req->data = handle; req->wait_handle = INVALID_HANDLE_VALUE; if (handle->flags & UV_HANDLE_EMULATE_IOCP) { req->event_handle = CreateEvent(NULL, 0, 0, NULL); - if (!req->event_handle) { + if (req->event_handle == NULL) { uv_fatal_error(GetLastError(), "CreateEvent"); } } else { @@ -628,13 +637,12 @@ int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) { uv_tcp_queue_accept(handle, req); } - /* Initialize other unused requests too, because uv_tcp_endgame */ - /* doesn't know how how many requests were initialized, so it will */ - /* try to clean up {uv_simultaneous_server_accepts} requests. */ + /* Initialize other unused requests too, because uv_tcp_endgame doesn't + * know how many requests were initialized, so it will try to clean up + * {uv_simultaneous_server_accepts} requests. */ for (i = simultaneous_accepts; i < uv_simultaneous_server_accepts; i++) { req = &handle->tcp.serv.accept_reqs[i]; - uv_req_init(loop, (uv_req_t*) req); - req->type = UV_ACCEPT; + UV_REQ_INIT(req, UV_ACCEPT); req->accept_socket = INVALID_SOCKET; req->data = handle; req->wait_handle = INVALID_HANDLE_VALUE; @@ -686,7 +694,7 @@ int uv_tcp_accept(uv_tcp_t* server, uv_tcp_t* client) { req->next_pending = NULL; req->accept_socket = INVALID_SOCKET; - if (!(server->flags & UV__HANDLE_CLOSING)) { + if (!(server->flags & UV_HANDLE_CLOSING)) { /* Check if we're in a middle of changing the number of pending accepts. */ if (!(server->flags & UV_HANDLE_TCP_ACCEPT_STATE_CHANGING)) { uv_tcp_queue_accept(server, req); @@ -724,13 +732,13 @@ int uv_tcp_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb, handle->alloc_cb = alloc_cb; INCREASE_ACTIVE_COUNT(loop, handle); - /* If reading was stopped and then started again, there could still be a */ - /* read request pending. */ + /* If reading was stopped and then started again, there could still be a read + * request pending. */ if (!(handle->flags & UV_HANDLE_READ_PENDING)) { if (handle->flags & UV_HANDLE_EMULATE_IOCP && - !handle->read_req.event_handle) { + handle->read_req.event_handle == NULL) { handle->read_req.event_handle = CreateEvent(NULL, 0, 0, NULL); - if (!handle->read_req.event_handle) { + if (handle->read_req.event_handle == NULL) { uv_fatal_error(GetLastError(), "CreateEvent"); } } @@ -740,6 +748,40 @@ int uv_tcp_read_start(uv_tcp_t* handle, uv_alloc_cb alloc_cb, return 0; } +static int uv__is_loopback(const struct sockaddr_storage* storage) { + const struct sockaddr_in* in4; + const struct sockaddr_in6* in6; + int i; + + if (storage->ss_family == AF_INET) { + in4 = (const struct sockaddr_in*) storage; + return in4->sin_addr.S_un.S_un_b.s_b1 == 127; + } + if (storage->ss_family == AF_INET6) { + in6 = (const struct sockaddr_in6*) storage; + for (i = 0; i < 7; ++i) { + if (in6->sin6_addr.u.Word[i] != 0) + return 0; + } + return in6->sin6_addr.u.Word[7] == htons(1); + } + return 0; +} + +// Check if Windows version is 10.0.16299 or later +static int uv__is_fast_loopback_fail_supported() { + OSVERSIONINFOW os_info; + if (!pRtlGetVersion) + return 0; + pRtlGetVersion(&os_info); + if (os_info.dwMajorVersion < 10) + return 0; + if (os_info.dwMajorVersion > 10) + return 1; + if (os_info.dwMinorVersion > 0) + return 1; + return os_info.dwBuildNumber >= 16299; +} static int uv_tcp_try_connect(uv_connect_t* req, uv_tcp_t* handle, @@ -747,11 +789,17 @@ static int uv_tcp_try_connect(uv_connect_t* req, unsigned int addrlen, uv_connect_cb cb) { uv_loop_t* loop = handle->loop; + TCP_INITIAL_RTO_PARAMETERS retransmit_ioctl; const struct sockaddr* bind_addr; + struct sockaddr_storage converted; BOOL success; DWORD bytes; int err; + err = uv__convert_to_localhost_if_unspecified(addr, &converted); + if (err) + return err; + if (handle->delayed_error) { return handle->delayed_error; } @@ -777,19 +825,37 @@ static int uv_tcp_try_connect(uv_connect_t* req, } } - uv_req_init(loop, (uv_req_t*) req); - req->type = UV_CONNECT; + /* This makes connect() fail instantly if the target port on the localhost + * is not reachable, instead of waiting for 2s. We do not care if this fails. + * This only works on Windows version 10.0.16299 and later. + */ + if (uv__is_fast_loopback_fail_supported() && uv__is_loopback(&converted)) { + memset(&retransmit_ioctl, 0, sizeof(retransmit_ioctl)); + retransmit_ioctl.Rtt = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS; + retransmit_ioctl.MaxSynRetransmissions = TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS; + WSAIoctl(handle->socket, + SIO_TCP_INITIAL_RTO, + &retransmit_ioctl, + sizeof(retransmit_ioctl), + NULL, + 0, + &bytes, + NULL, + NULL); + } + + UV_REQ_INIT(req, UV_CONNECT); req->handle = (uv_stream_t*) handle; req->cb = cb; memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped)); success = handle->tcp.conn.func_connectex(handle->socket, - addr, - addrlen, - NULL, - 0, - &bytes, - &req->u.io.overlapped); + (const struct sockaddr*) &converted, + addrlen, + NULL, + 0, + &bytes, + &req->u.io.overlapped); if (UV_SUCCEEDED_WITHOUT_IOCP(success)) { /* Process the req without IOCP. */ @@ -811,44 +877,24 @@ static int uv_tcp_try_connect(uv_connect_t* req, int uv_tcp_getsockname(const uv_tcp_t* handle, struct sockaddr* name, int* namelen) { - int result; - - if (handle->socket == INVALID_SOCKET) { - return UV_EINVAL; - } - - if (handle->delayed_error) { - return uv_translate_sys_error(handle->delayed_error); - } - result = getsockname(handle->socket, name, namelen); - if (result != 0) { - return uv_translate_sys_error(WSAGetLastError()); - } - - return 0; + return uv__getsockpeername((const uv_handle_t*) handle, + getsockname, + name, + namelen, + handle->delayed_error); } int uv_tcp_getpeername(const uv_tcp_t* handle, struct sockaddr* name, int* namelen) { - int result; - - if (handle->socket == INVALID_SOCKET) { - return UV_EINVAL; - } - - if (handle->delayed_error) { - return uv_translate_sys_error(handle->delayed_error); - } - - result = getpeername(handle->socket, name, namelen); - if (result != 0) { - return uv_translate_sys_error(WSAGetLastError()); - } - return 0; + return uv__getsockpeername((const uv_handle_t*) handle, + getpeername, + name, + namelen, + handle->delayed_error); } @@ -861,8 +907,7 @@ int uv_tcp_write(uv_loop_t* loop, int result; DWORD bytes; - uv_req_init(loop, (uv_req_t*) req); - req->type = UV_WRITE; + UV_REQ_INIT(req, UV_WRITE); req->handle = (uv_stream_t*) handle; req->cb = cb; @@ -870,7 +915,7 @@ int uv_tcp_write(uv_loop_t* loop, memset(&(req->u.io.overlapped), 0, sizeof(req->u.io.overlapped)); if (handle->flags & UV_HANDLE_EMULATE_IOCP) { req->event_handle = CreateEvent(NULL, 0, 0, NULL); - if (!req->event_handle) { + if (req->event_handle == NULL) { uv_fatal_error(GetLastError(), "CreateEvent"); } req->u.io.overlapped.hEvent = (HANDLE) ((ULONG_PTR) req->event_handle | 1); @@ -948,6 +993,7 @@ void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, uv_req_t* req) { DWORD bytes, flags, err; uv_buf_t buf; + int count; assert(handle->type == UV_TCP); @@ -965,8 +1011,7 @@ void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, err = GET_REQ_SOCK_ERROR(req); if (err == WSAECONNABORTED) { - /* - * Turn WSAECONNABORTED into UV_ECONNRESET to be consistent with Unix. + /* Turn WSAECONNABORTED into UV_ECONNRESET to be consistent with Unix. */ err = WSAECONNRESET; } @@ -1003,9 +1048,11 @@ void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, } /* Do nonblocking reads until the buffer is empty */ - while (handle->flags & UV_HANDLE_READING) { + count = 32; + while ((handle->flags & UV_HANDLE_READING) && (count-- > 0)) { + buf = uv_buf_init(NULL, 0); handle->alloc_cb((uv_handle_t*) handle, 65536, &buf); - if (buf.len == 0) { + if (buf.base == NULL || buf.len == 0) { handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &buf); break; } @@ -1045,8 +1092,8 @@ void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, DECREASE_ACTIVE_COUNT(loop, handle); if (err == WSAECONNABORTED) { - /* Turn WSAECONNABORTED into UV_ECONNRESET to be consistent with */ - /* Unix. */ + /* Turn WSAECONNABORTED into UV_ECONNRESET to be consistent with + * Unix. */ err = WSAECONNRESET; } @@ -1086,7 +1133,7 @@ void uv_process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle, UnregisterWait(req->wait_handle); req->wait_handle = INVALID_HANDLE_VALUE; } - if (req->event_handle) { + if (req->event_handle != NULL) { CloseHandle(req->event_handle); req->event_handle = NULL; } @@ -1118,10 +1165,9 @@ void uv_process_tcp_accept_req(uv_loop_t* loop, uv_tcp_t* handle, assert(handle->type == UV_TCP); - /* If handle->accepted_socket is not a valid socket, then */ - /* uv_queue_accept must have failed. This is a serious error. We stop */ - /* accepting connections and report this error to the connection */ - /* callback. */ + /* If handle->accepted_socket is not a valid socket, then uv_queue_accept + * must have failed. This is a serious error. We stop accepting connections + * and report this error to the connection callback. */ if (req->accept_socket == INVALID_SOCKET) { if (handle->flags & UV_HANDLE_LISTENING) { handle->flags &= ~UV_HANDLE_LISTENING; @@ -1146,9 +1192,9 @@ void uv_process_tcp_accept_req(uv_loop_t* loop, uv_tcp_t* handle, handle->stream.serv.connection_cb((uv_stream_t*)handle, 0); } } else { - /* Error related to accepted socket is ignored because the server */ - /* socket may still be healthy. If the server socket is broken */ - /* uv_queue_accept will detect it. */ + /* Error related to accepted socket is ignored because the server socket + * may still be healthy. If the server socket is broken uv_queue_accept + * will detect it. */ closesocket(req->accept_socket); req->accept_socket = INVALID_SOCKET; if (handle->flags & UV_HANDLE_LISTENING) { @@ -1170,11 +1216,14 @@ void uv_process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle, err = 0; if (REQ_SUCCESS(req)) { - if (setsockopt(handle->socket, - SOL_SOCKET, - SO_UPDATE_CONNECT_CONTEXT, - NULL, - 0) == 0) { + if (handle->flags & UV_HANDLE_CLOSING) { + /* use UV_ECANCELED for consistency with Unix */ + err = ERROR_OPERATION_ABORTED; + } else if (setsockopt(handle->socket, + SOL_SOCKET, + SO_UPDATE_CONNECT_CONTEXT, + NULL, + 0) == 0) { uv_connection_init((uv_stream_t*)handle); handle->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE; loop->active_tcp_streams++; @@ -1190,40 +1239,76 @@ void uv_process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle, } -int uv_tcp_import(uv_tcp_t* tcp, uv__ipc_socket_info_ex* socket_info_ex, - int tcp_connection) { +int uv__tcp_xfer_export(uv_tcp_t* handle, + int target_pid, + uv__ipc_socket_xfer_type_t* xfer_type, + uv__ipc_socket_xfer_info_t* xfer_info) { + if (handle->flags & UV_HANDLE_CONNECTION) { + *xfer_type = UV__IPC_SOCKET_XFER_TCP_CONNECTION; + } else { + *xfer_type = UV__IPC_SOCKET_XFER_TCP_SERVER; + /* We're about to share the socket with another process. Because this is a + * listening socket, we assume that the other process will be accepting + * connections on it. Thus, before sharing the socket with another process, + * we call listen here in the parent process. */ + if (!(handle->flags & UV_HANDLE_LISTENING)) { + if (!(handle->flags & UV_HANDLE_BOUND)) { + return ERROR_NOT_SUPPORTED; + } + if (handle->delayed_error == 0 && + listen(handle->socket, SOMAXCONN) == SOCKET_ERROR) { + handle->delayed_error = WSAGetLastError(); + } + } + } + + if (WSADuplicateSocketW(handle->socket, target_pid, &xfer_info->socket_info)) + return WSAGetLastError(); + xfer_info->delayed_error = handle->delayed_error; + + /* Mark the local copy of the handle as 'shared' so we behave in a way that's + * friendly to the process(es) that we share the socket with. */ + handle->flags |= UV_HANDLE_SHARED_TCP_SOCKET; + + return 0; +} + + +int uv__tcp_xfer_import(uv_tcp_t* tcp, + uv__ipc_socket_xfer_type_t xfer_type, + uv__ipc_socket_xfer_info_t* xfer_info) { int err; - SOCKET socket = WSASocketW(FROM_PROTOCOL_INFO, - FROM_PROTOCOL_INFO, - FROM_PROTOCOL_INFO, - &socket_info_ex->socket_info, - 0, - WSA_FLAG_OVERLAPPED); + SOCKET socket; + + assert(xfer_type == UV__IPC_SOCKET_XFER_TCP_SERVER || + xfer_type == UV__IPC_SOCKET_XFER_TCP_CONNECTION); + + socket = WSASocketW(FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + FROM_PROTOCOL_INFO, + &xfer_info->socket_info, + 0, + WSA_FLAG_OVERLAPPED); if (socket == INVALID_SOCKET) { return WSAGetLastError(); } - err = uv_tcp_set_socket(tcp->loop, - tcp, - socket, - socket_info_ex->socket_info.iAddressFamily, - 1); + err = uv_tcp_set_socket( + tcp->loop, tcp, socket, xfer_info->socket_info.iAddressFamily, 1); if (err) { closesocket(socket); return err; } - if (tcp_connection) { + tcp->delayed_error = xfer_info->delayed_error; + tcp->flags |= UV_HANDLE_BOUND | UV_HANDLE_SHARED_TCP_SOCKET; + + if (xfer_type == UV__IPC_SOCKET_XFER_TCP_CONNECTION) { uv_connection_init((uv_stream_t*)tcp); tcp->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE; } - tcp->flags |= UV_HANDLE_BOUND; - tcp->flags |= UV_HANDLE_SHARED_TCP_SOCKET; - - tcp->delayed_error = socket_info_ex->delayed_error; - tcp->loop->active_tcp_streams++; return 0; } @@ -1269,39 +1354,6 @@ int uv_tcp_keepalive(uv_tcp_t* handle, int enable, unsigned int delay) { } -int uv_tcp_duplicate_socket(uv_tcp_t* handle, int pid, - LPWSAPROTOCOL_INFOW protocol_info) { - if (!(handle->flags & UV_HANDLE_CONNECTION)) { - /* - * We're about to share the socket with another process. Because - * this is a listening socket, we assume that the other process will - * be accepting connections on it. So, before sharing the socket - * with another process, we call listen here in the parent process. - */ - - if (!(handle->flags & UV_HANDLE_LISTENING)) { - if (!(handle->flags & UV_HANDLE_BOUND)) { - return ERROR_INVALID_PARAMETER; - } - - if (!(handle->delayed_error)) { - if (listen(handle->socket, SOMAXCONN) == SOCKET_ERROR) { - handle->delayed_error = WSAGetLastError(); - } - } - } - } - - if (WSADuplicateSocketW(handle->socket, pid, protocol_info)) { - return WSAGetLastError(); - } - - handle->flags |= UV_HANDLE_SHARED_TCP_SOCKET; - - return 0; -} - - int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) { if (handle->flags & UV_HANDLE_CONNECTION) { return UV_EINVAL; @@ -1342,8 +1394,8 @@ static int uv_tcp_try_cancel_io(uv_tcp_t* tcp) { non_ifs_lsp = (tcp->flags & UV_HANDLE_IPV6) ? uv_tcp_non_ifs_lsp_ipv6 : uv_tcp_non_ifs_lsp_ipv4; - /* If there are non-ifs LSPs then try to obtain a base handle for the */ - /* socket. This will always fail on Windows XP/3k. */ + /* If there are non-ifs LSPs then try to obtain a base handle for the socket. + * This will always fail on Windows XP/3k. */ if (non_ifs_lsp) { DWORD bytes; if (WSAIoctl(socket, @@ -1375,38 +1427,37 @@ void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp) { int close_socket = 1; if (tcp->flags & UV_HANDLE_READ_PENDING) { - /* In order for winsock to do a graceful close there must not be any */ - /* any pending reads, or the socket must be shut down for writing */ + /* In order for winsock to do a graceful close there must not be any any + * pending reads, or the socket must be shut down for writing */ if (!(tcp->flags & UV_HANDLE_SHARED_TCP_SOCKET)) { /* Just do shutdown on non-shared sockets, which ensures graceful close. */ shutdown(tcp->socket, SD_SEND); } else if (uv_tcp_try_cancel_io(tcp) == 0) { - /* In case of a shared socket, we try to cancel all outstanding I/O, */ - /* If that works, don't close the socket yet - wait for the read req to */ - /* return and close the socket in uv_tcp_endgame. */ + /* In case of a shared socket, we try to cancel all outstanding I/O,. If + * that works, don't close the socket yet - wait for the read req to + * return and close the socket in uv_tcp_endgame. */ close_socket = 0; } else { - /* When cancelling isn't possible - which could happen when an LSP is */ - /* present on an old Windows version, we will have to close the socket */ - /* with a read pending. That is not nice because trailing sent bytes */ - /* may not make it to the other side. */ + /* When cancelling isn't possible - which could happen when an LSP is + * present on an old Windows version, we will have to close the socket + * with a read pending. That is not nice because trailing sent bytes may + * not make it to the other side. */ } } else if ((tcp->flags & UV_HANDLE_SHARED_TCP_SOCKET) && tcp->tcp.serv.accept_reqs != NULL) { - /* Under normal circumstances closesocket() will ensure that all pending */ - /* accept reqs are canceled. However, when the socket is shared the */ - /* presence of another reference to the socket in another process will */ - /* keep the accept reqs going, so we have to ensure that these are */ - /* canceled. */ + /* Under normal circumstances closesocket() will ensure that all pending + * accept reqs are canceled. However, when the socket is shared the + * presence of another reference to the socket in another process will keep + * the accept reqs going, so we have to ensure that these are canceled. */ if (uv_tcp_try_cancel_io(tcp) != 0) { - /* When cancellation is not possible, there is another option: we can */ - /* close the incoming sockets, which will also cancel the accept */ - /* operations. However this is not cool because we might inadvertently */ - /* close a socket that just accepted a new connection, which will */ - /* cause the connection to be aborted. */ + /* When cancellation is not possible, there is another option: we can + * close the incoming sockets, which will also cancel the accept + * operations. However this is not cool because we might inadvertently + * close a socket that just accepted a new connection, which will cause + * the connection to be aborted. */ unsigned int i; for (i = 0; i < uv_simultaneous_server_accepts; i++) { uv_tcp_accept_t* req = &tcp->tcp.serv.accept_reqs[i]; @@ -1448,6 +1499,8 @@ int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) { WSAPROTOCOL_INFOW protocol_info; int opt_len; int err; + struct sockaddr_storage saddr; + int saddr_len; /* Detect the address family of the socket. */ opt_len = (int) sizeof protocol_info; @@ -1468,6 +1521,19 @@ int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) { return uv_translate_sys_error(err); } + /* Support already active socket. */ + saddr_len = sizeof(saddr); + if (!uv_tcp_getsockname(handle, (struct sockaddr*) &saddr, &saddr_len)) { + /* Socket is already bound. */ + handle->flags |= UV_HANDLE_BOUND; + saddr_len = sizeof(saddr); + if (!uv_tcp_getpeername(handle, (struct sockaddr*) &saddr, &saddr_len)) { + /* Socket is already connected. */ + uv_connection_init((uv_stream_t*) handle); + handle->flags |= UV_HANDLE_READABLE | UV_HANDLE_WRITABLE; + } + } + return 0; } diff --git a/include/libuv/src/win/thread.c b/include/libuv/src/win/thread.c index 91684e938..89c53ada7 100644 --- a/include/libuv/src/win/thread.c +++ b/include/libuv/src/win/thread.c @@ -23,29 +23,15 @@ #include #include +#if defined(__MINGW64_VERSION_MAJOR) +/* MemoryBarrier expands to __mm_mfence in some cases (x86+sse2), which may + * require this header in some versions of mingw64. */ +#include +#endif + #include "uv.h" #include "internal.h" - -#define HAVE_CONDVAR_API() (pInitializeConditionVariable != NULL) - -static int uv_cond_fallback_init(uv_cond_t* cond); -static void uv_cond_fallback_destroy(uv_cond_t* cond); -static void uv_cond_fallback_signal(uv_cond_t* cond); -static void uv_cond_fallback_broadcast(uv_cond_t* cond); -static void uv_cond_fallback_wait(uv_cond_t* cond, uv_mutex_t* mutex); -static int uv_cond_fallback_timedwait(uv_cond_t* cond, - uv_mutex_t* mutex, uint64_t timeout); - -static int uv_cond_condvar_init(uv_cond_t* cond); -static void uv_cond_condvar_destroy(uv_cond_t* cond); -static void uv_cond_condvar_signal(uv_cond_t* cond); -static void uv_cond_condvar_broadcast(uv_cond_t* cond); -static void uv_cond_condvar_wait(uv_cond_t* cond, uv_mutex_t* mutex); -static int uv_cond_condvar_timedwait(uv_cond_t* cond, - uv_mutex_t* mutex, uint64_t timeout); - - static void uv__once_inner(uv_once_t* guard, void (*callback)(void)) { DWORD result; HANDLE existing_event, created_event; @@ -69,8 +55,8 @@ static void uv__once_inner(uv_once_t* guard, void (*callback)(void)) { guard->ran = 1; } else { - /* We lost the race. Destroy the event we created and wait for the */ - /* existing one to become signaled. */ + /* We lost the race. Destroy the event we created and wait for the existing + * one to become signaled. */ CloseHandle(created_event); result = WaitForSingleObject(existing_event, INFINITE); assert(result == WAIT_OBJECT_0); @@ -126,9 +112,34 @@ static UINT __stdcall uv__thread_start(void* arg) { int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) { + uv_thread_options_t params; + params.flags = UV_THREAD_NO_FLAGS; + return uv_thread_create_ex(tid, ¶ms, entry, arg); +} + +int uv_thread_create_ex(uv_thread_t* tid, + const uv_thread_options_t* params, + void (*entry)(void *arg), + void *arg) { struct thread_ctx* ctx; int err; HANDLE thread; + SYSTEM_INFO sysinfo; + size_t stack_size; + size_t pagesize; + + stack_size = + params->flags & UV_THREAD_HAS_STACK_SIZE ? params->stack_size : 0; + + if (stack_size != 0) { + GetNativeSystemInfo(&sysinfo); + pagesize = (size_t)sysinfo.dwPageSize; + /* Round up to the nearest page boundary. */ + stack_size = (stack_size + pagesize - 1) &~ (pagesize - 1); + + if ((unsigned)stack_size != stack_size) + return UV_EINVAL; + } ctx = uv__malloc(sizeof(*ctx)); if (ctx == NULL) @@ -138,9 +149,9 @@ int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) { ctx->arg = arg; /* Create the thread in suspended state so we have a chance to pass - * its own creation handle to it */ + * its own creation handle to it */ thread = (HANDLE) _beginthreadex(NULL, - 0, + (unsigned)stack_size, uv__thread_start, ctx, CREATE_SUSPENDED, @@ -182,6 +193,7 @@ int uv_thread_join(uv_thread_t *tid) { else { CloseHandle(*tid); *tid = 0; + MemoryBarrier(); /* For feature parity with pthread_join(). */ return 0; } } @@ -198,6 +210,11 @@ int uv_mutex_init(uv_mutex_t* mutex) { } +int uv_mutex_init_recursive(uv_mutex_t* mutex) { + return uv_mutex_init(mutex); +} + + void uv_mutex_destroy(uv_mutex_t* mutex) { DeleteCriticalSection(mutex); } @@ -371,220 +388,35 @@ int uv_sem_trywait(uv_sem_t* sem) { } -/* This condition variable implementation is based on the SetEvent solution - * (section 3.2) at http://www.cs.wustl.edu/~schmidt/win32-cv-1.html - * We could not use the SignalObjectAndWait solution (section 3.4) because - * it want the 2nd argument (type uv_mutex_t) of uv_cond_wait() and - * uv_cond_timedwait() to be HANDLEs, but we use CRITICAL_SECTIONs. - */ - -static int uv_cond_fallback_init(uv_cond_t* cond) { - int err; - - /* Initialize the count to 0. */ - cond->fallback.waiters_count = 0; - - InitializeCriticalSection(&cond->fallback.waiters_count_lock); - - /* Create an auto-reset event. */ - cond->fallback.signal_event = CreateEvent(NULL, /* no security */ - FALSE, /* auto-reset event */ - FALSE, /* non-signaled initially */ - NULL); /* unnamed */ - if (!cond->fallback.signal_event) { - err = GetLastError(); - goto error2; - } - - /* Create a manual-reset event. */ - cond->fallback.broadcast_event = CreateEvent(NULL, /* no security */ - TRUE, /* manual-reset */ - FALSE, /* non-signaled */ - NULL); /* unnamed */ - if (!cond->fallback.broadcast_event) { - err = GetLastError(); - goto error; - } - - return 0; - -error: - CloseHandle(cond->fallback.signal_event); -error2: - DeleteCriticalSection(&cond->fallback.waiters_count_lock); - return uv_translate_sys_error(err); -} - - -static int uv_cond_condvar_init(uv_cond_t* cond) { - pInitializeConditionVariable(&cond->cond_var); - return 0; -} - - int uv_cond_init(uv_cond_t* cond) { - uv__once_init(); - - if (HAVE_CONDVAR_API()) - return uv_cond_condvar_init(cond); - else - return uv_cond_fallback_init(cond); -} - - -static void uv_cond_fallback_destroy(uv_cond_t* cond) { - if (!CloseHandle(cond->fallback.broadcast_event)) - abort(); - if (!CloseHandle(cond->fallback.signal_event)) - abort(); - DeleteCriticalSection(&cond->fallback.waiters_count_lock); -} - - -static void uv_cond_condvar_destroy(uv_cond_t* cond) { - /* nothing to do */ + InitializeConditionVariable(&cond->cond_var); + return 0; } void uv_cond_destroy(uv_cond_t* cond) { - if (HAVE_CONDVAR_API()) - uv_cond_condvar_destroy(cond); - else - uv_cond_fallback_destroy(cond); -} - - -static void uv_cond_fallback_signal(uv_cond_t* cond) { - int have_waiters; - - /* Avoid race conditions. */ - EnterCriticalSection(&cond->fallback.waiters_count_lock); - have_waiters = cond->fallback.waiters_count > 0; - LeaveCriticalSection(&cond->fallback.waiters_count_lock); - - if (have_waiters) - SetEvent(cond->fallback.signal_event); -} - - -static void uv_cond_condvar_signal(uv_cond_t* cond) { - pWakeConditionVariable(&cond->cond_var); + /* nothing to do */ + (void) &cond; } void uv_cond_signal(uv_cond_t* cond) { - if (HAVE_CONDVAR_API()) - uv_cond_condvar_signal(cond); - else - uv_cond_fallback_signal(cond); -} - - -static void uv_cond_fallback_broadcast(uv_cond_t* cond) { - int have_waiters; - - /* Avoid race conditions. */ - EnterCriticalSection(&cond->fallback.waiters_count_lock); - have_waiters = cond->fallback.waiters_count > 0; - LeaveCriticalSection(&cond->fallback.waiters_count_lock); - - if (have_waiters) - SetEvent(cond->fallback.broadcast_event); -} - - -static void uv_cond_condvar_broadcast(uv_cond_t* cond) { - pWakeAllConditionVariable(&cond->cond_var); + WakeConditionVariable(&cond->cond_var); } void uv_cond_broadcast(uv_cond_t* cond) { - if (HAVE_CONDVAR_API()) - uv_cond_condvar_broadcast(cond); - else - uv_cond_fallback_broadcast(cond); -} - - -static int uv_cond_wait_helper(uv_cond_t* cond, uv_mutex_t* mutex, - DWORD dwMilliseconds) { - DWORD result; - int last_waiter; - HANDLE handles[2] = { - cond->fallback.signal_event, - cond->fallback.broadcast_event - }; - - /* Avoid race conditions. */ - EnterCriticalSection(&cond->fallback.waiters_count_lock); - cond->fallback.waiters_count++; - LeaveCriticalSection(&cond->fallback.waiters_count_lock); - - /* It's ok to release the here since Win32 manual-reset events */ - /* maintain state when used with . This avoids the "lost wakeup" */ - /* bug. */ - uv_mutex_unlock(mutex); - - /* Wait for either event to become signaled due to being */ - /* called or being called. */ - result = WaitForMultipleObjects(2, handles, FALSE, dwMilliseconds); - - EnterCriticalSection(&cond->fallback.waiters_count_lock); - cond->fallback.waiters_count--; - last_waiter = result == WAIT_OBJECT_0 + 1 - && cond->fallback.waiters_count == 0; - LeaveCriticalSection(&cond->fallback.waiters_count_lock); - - /* Some thread called . */ - if (last_waiter) { - /* We're the last waiter to be notified or to stop waiting, so reset the */ - /* the manual-reset event. */ - ResetEvent(cond->fallback.broadcast_event); - } - - /* Reacquire the . */ - uv_mutex_lock(mutex); - - if (result == WAIT_OBJECT_0 || result == WAIT_OBJECT_0 + 1) - return 0; - - if (result == WAIT_TIMEOUT) - return UV_ETIMEDOUT; - - abort(); - return -1; /* Satisfy the compiler. */ -} - - -static void uv_cond_fallback_wait(uv_cond_t* cond, uv_mutex_t* mutex) { - if (uv_cond_wait_helper(cond, mutex, INFINITE)) - abort(); -} - - -static void uv_cond_condvar_wait(uv_cond_t* cond, uv_mutex_t* mutex) { - if (!pSleepConditionVariableCS(&cond->cond_var, mutex, INFINITE)) - abort(); + WakeAllConditionVariable(&cond->cond_var); } void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) { - if (HAVE_CONDVAR_API()) - uv_cond_condvar_wait(cond, mutex); - else - uv_cond_fallback_wait(cond, mutex); -} - - -static int uv_cond_fallback_timedwait(uv_cond_t* cond, - uv_mutex_t* mutex, uint64_t timeout) { - return uv_cond_wait_helper(cond, mutex, (DWORD)(timeout / 1e6)); + if (!SleepConditionVariableCS(&cond->cond_var, mutex, INFINITE)) + abort(); } - -static int uv_cond_condvar_timedwait(uv_cond_t* cond, - uv_mutex_t* mutex, uint64_t timeout) { - if (pSleepConditionVariableCS(&cond->cond_var, mutex, (DWORD)(timeout / 1e6))) +int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) { + if (SleepConditionVariableCS(&cond->cond_var, mutex, (DWORD)(timeout / 1e6))) return 0; if (GetLastError() != ERROR_TIMEOUT) abort(); @@ -592,15 +424,6 @@ static int uv_cond_condvar_timedwait(uv_cond_t* cond, } -int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, - uint64_t timeout) { - if (HAVE_CONDVAR_API()) - return uv_cond_condvar_timedwait(cond, mutex, timeout); - else - return uv_cond_fallback_timedwait(cond, mutex, timeout); -} - - int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) { int err; diff --git a/include/libuv/src/win/tty.c b/include/libuv/src/win/tty.c index 9b9637784..1b9d4f853 100644 --- a/include/libuv/src/win/tty.c +++ b/include/libuv/src/win/tty.c @@ -25,7 +25,7 @@ #include #if defined(_MSC_VER) && _MSC_VER < 1600 -# include "stdint-msvc2008.h" +# include "uv/stdint-msvc2008.h" #else # include #endif @@ -40,22 +40,36 @@ #include "stream-inl.h" #include "req-inl.h" +#ifndef InterlockedOr +# define InterlockedOr _InterlockedOr +#endif #define UNICODE_REPLACEMENT_CHARACTER (0xfffd) -#define ANSI_NORMAL 0x00 -#define ANSI_ESCAPE_SEEN 0x02 -#define ANSI_CSI 0x04 -#define ANSI_ST_CONTROL 0x08 -#define ANSI_IGNORE 0x10 -#define ANSI_IN_ARG 0x20 -#define ANSI_IN_STRING 0x40 -#define ANSI_BACKSLASH_SEEN 0x80 +#define ANSI_NORMAL 0x0000 +#define ANSI_ESCAPE_SEEN 0x0002 +#define ANSI_CSI 0x0004 +#define ANSI_ST_CONTROL 0x0008 +#define ANSI_IGNORE 0x0010 +#define ANSI_IN_ARG 0x0020 +#define ANSI_IN_STRING 0x0040 +#define ANSI_BACKSLASH_SEEN 0x0080 +#define ANSI_EXTENSION 0x0100 +#define ANSI_DECSCUSR 0x0200 #define MAX_INPUT_BUFFER_LENGTH 8192 +#define MAX_CONSOLE_CHAR 8192 + +#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING +#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 +#endif +#define CURSOR_SIZE_SMALL 25 +#define CURSOR_SIZE_LARGE 100 -static void uv_tty_capture_initial_style(CONSOLE_SCREEN_BUFFER_INFO* info); +static void uv_tty_capture_initial_style( + CONSOLE_SCREEN_BUFFER_INFO* screen_buffer_info, + CONSOLE_CURSOR_INFO* cursor_info); static void uv_tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info); static int uv__cancel_read_console(uv_tty_t* handle); @@ -105,9 +119,33 @@ static int uv_tty_virtual_offset = -1; static int uv_tty_virtual_height = -1; static int uv_tty_virtual_width = -1; -static CRITICAL_SECTION uv_tty_output_lock; +/* The console window size + * We keep this separate from uv_tty_virtual_*. We use those values to only + * handle signalling SIGWINCH + */ -static HANDLE uv_tty_output_handle = INVALID_HANDLE_VALUE; +static HANDLE uv__tty_console_handle = INVALID_HANDLE_VALUE; +static int uv__tty_console_height = -1; +static int uv__tty_console_width = -1; +static HANDLE uv__tty_console_resized = INVALID_HANDLE_VALUE; +static uv_mutex_t uv__tty_console_resize_mutex; + +static DWORD WINAPI uv__tty_console_resize_message_loop_thread(void* param); +static void CALLBACK uv__tty_console_resize_event(HWINEVENTHOOK hWinEventHook, + DWORD event, + HWND hwnd, + LONG idObject, + LONG idChild, + DWORD dwEventThread, + DWORD dwmsEventTime); +static DWORD WINAPI uv__tty_console_resize_watcher_thread(void* param); +static void uv__tty_console_signal_resize(void); + +/* We use a semaphore rather than a mutex or critical section because in some + cases (uv__cancel_read_console) we need take the lock in the main thread and + release it in another thread. Using a semaphore ensures that in such + scenario the main thread will still block when trying to acquire the lock. */ +static uv_sem_t uv_tty_output_lock; static WORD uv_tty_default_text_attributes = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; @@ -118,16 +156,46 @@ static char uv_tty_default_fg_bright = 0; static char uv_tty_default_bg_bright = 0; static char uv_tty_default_inverse = 0; - -void uv_console_init() { - InitializeCriticalSection(&uv_tty_output_lock); +static CONSOLE_CURSOR_INFO uv_tty_default_cursor_info; + +/* Determine whether or not ANSI support is enabled. */ +static BOOL uv__need_check_vterm_state = TRUE; +static uv_tty_vtermstate_t uv__vterm_state = UV_TTY_UNSUPPORTED; +static void uv__determine_vterm_state(HANDLE handle); + +void uv_console_init(void) { + if (uv_sem_init(&uv_tty_output_lock, 1)) + abort(); + uv__tty_console_handle = CreateFileW(L"CONOUT$", + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_WRITE, + 0, + OPEN_EXISTING, + 0, + 0); + if (uv__tty_console_handle != INVALID_HANDLE_VALUE) { + CONSOLE_SCREEN_BUFFER_INFO sb_info; + QueueUserWorkItem(uv__tty_console_resize_message_loop_thread, + NULL, + WT_EXECUTELONGFUNCTION); + uv_mutex_init(&uv__tty_console_resize_mutex); + if (GetConsoleScreenBufferInfo(uv__tty_console_handle, &sb_info)) { + uv__tty_console_width = sb_info.dwSize.X; + uv__tty_console_height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1; + } + } } -int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int readable) { +int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int unused) { + BOOL readable; + DWORD NumberOfEvents; HANDLE handle; CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info; + CONSOLE_CURSOR_INFO cursor_info; + (void)unused; + uv__once_init(); handle = (HANDLE) uv__get_osfhandle(fd); if (handle == INVALID_HANDLE_VALUE) return UV_EBADF; @@ -150,27 +218,31 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int readable) { fd = -1; } + readable = GetNumberOfConsoleInputEvents(handle, &NumberOfEvents); if (!readable) { /* Obtain the screen buffer info with the output handle. */ if (!GetConsoleScreenBufferInfo(handle, &screen_buffer_info)) { return uv_translate_sys_error(GetLastError()); } - /* Obtain the the tty_output_lock because the virtual window state is */ - /* shared between all uv_tty_t handles. */ - EnterCriticalSection(&uv_tty_output_lock); + /* Obtain the cursor info with the output handle. */ + if (!GetConsoleCursorInfo(handle, &cursor_info)) { + return uv_translate_sys_error(GetLastError()); + } + + /* Obtain the tty_output_lock because the virtual window state is shared + * between all uv_tty_t handles. */ + uv_sem_wait(&uv_tty_output_lock); - /* Store the global tty output handle. This handle is used by TTY read */ - /* streams to update the virtual window when a CONSOLE_BUFFER_SIZE_EVENT */ - /* is received. */ - uv_tty_output_handle = handle; + if (uv__need_check_vterm_state) + uv__determine_vterm_state(handle); - /* Remember the original console text attributes. */ - uv_tty_capture_initial_style(&screen_buffer_info); + /* Remember the original console text attributes and cursor info. */ + uv_tty_capture_initial_style(&screen_buffer_info, &cursor_info); uv_tty_update_virtual_window(&screen_buffer_info); - LeaveCriticalSection(&uv_tty_output_lock); + uv_sem_post(&uv_tty_output_lock); } @@ -217,7 +289,9 @@ int uv_tty_init(uv_loop_t* loop, uv_tty_t* tty, uv_file fd, int readable) { /* Set the default console text attributes based on how the console was * configured when libuv started. */ -static void uv_tty_capture_initial_style(CONSOLE_SCREEN_BUFFER_INFO* info) { +static void uv_tty_capture_initial_style( + CONSOLE_SCREEN_BUFFER_INFO* screen_buffer_info, + CONSOLE_CURSOR_INFO* cursor_info) { static int style_captured = 0; /* Only do this once. @@ -226,7 +300,7 @@ static void uv_tty_capture_initial_style(CONSOLE_SCREEN_BUFFER_INFO* info) { return; /* Save raw win32 attributes. */ - uv_tty_default_text_attributes = info->wAttributes; + uv_tty_default_text_attributes = screen_buffer_info->wAttributes; /* Convert black text on black background to use white text. */ if (uv_tty_default_text_attributes == 0) @@ -266,6 +340,9 @@ static void uv_tty_capture_initial_style(CONSOLE_SCREEN_BUFFER_INFO* info) { if (uv_tty_default_text_attributes & COMMON_LVB_REVERSE_VIDEO) uv_tty_default_inverse = 1; + /* Save the cursor size and the cursor state. */ + uv_tty_default_cursor_info = *cursor_info; + style_captured = 1; } @@ -294,10 +371,8 @@ int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) { break; case UV_TTY_MODE_IO: return UV_ENOTSUP; - } - - if (!SetConsoleMode(tty->handle, flags)) { - return uv_translate_sys_error(GetLastError()); + default: + return UV_EINVAL; } /* If currently reading, stop, and restart reading. */ @@ -311,7 +386,17 @@ int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) { } } else { was_reading = 0; + alloc_cb = NULL; + read_cb = NULL; + } + + uv_sem_wait(&uv_tty_output_lock); + if (!SetConsoleMode(tty->handle, flags)) { + err = uv_translate_sys_error(GetLastError()); + uv_sem_post(&uv_tty_output_lock); + return err; } + uv_sem_post(&uv_tty_output_lock); /* Update flag. */ tty->flags &= ~UV_HANDLE_TTY_RAW; @@ -329,12 +414,6 @@ int uv_tty_set_mode(uv_tty_t* tty, uv_tty_mode_t mode) { } -int uv_is_tty(uv_file file) { - DWORD result; - return GetConsoleMode((HANDLE) _get_osfhandle(file), &result) != 0; -} - - int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) { CONSOLE_SCREEN_BUFFER_INFO info; @@ -342,9 +421,9 @@ int uv_tty_get_winsize(uv_tty_t* tty, int* width, int* height) { return uv_translate_sys_error(GetLastError()); } - EnterCriticalSection(&uv_tty_output_lock); + uv_sem_wait(&uv_tty_output_lock); uv_tty_update_virtual_window(&info); - LeaveCriticalSection(&uv_tty_output_lock); + uv_sem_post(&uv_tty_output_lock); *width = uv_tty_virtual_width; *height = uv_tty_virtual_height; @@ -413,6 +492,7 @@ static DWORD CALLBACK uv_tty_line_read_thread(void* data) { DWORD chars, read_chars; LONG status; COORD pos; + BOOL read_console_success; assert(data); @@ -430,23 +510,26 @@ static DWORD CALLBACK uv_tty_line_read_thread(void* data) { bytes = MAX_INPUT_BUFFER_LENGTH; } - /* At last, unicode! */ - /* One utf-16 codeunit never takes more than 3 utf-8 codeunits to encode */ + /* At last, unicode! One utf-16 codeunit never takes more than 3 utf-8 + * codeunits to encode. */ chars = bytes / 3; status = InterlockedExchange(&uv__read_console_status, IN_PROGRESS); if (status == TRAP_REQUESTED) { SET_REQ_SUCCESS(req); + InterlockedExchange(&uv__read_console_status, COMPLETED); req->u.io.overlapped.InternalHigh = 0; POST_COMPLETION_FOR_REQ(loop, req); return 0; } - if (ReadConsoleW(handle->handle, - (void*) utf16, - chars, - &read_chars, - NULL)) { + read_console_success = ReadConsoleW(handle->handle, + (void*) utf16, + chars, + &read_chars, + NULL); + + if (read_console_success) { read_bytes = WideCharToMultiByte(CP_UTF8, 0, utf16, @@ -461,33 +544,36 @@ static DWORD CALLBACK uv_tty_line_read_thread(void* data) { SET_REQ_ERROR(req, GetLastError()); } - InterlockedExchange(&uv__read_console_status, COMPLETED); + status = InterlockedExchange(&uv__read_console_status, COMPLETED); - /* If we canceled the read by sending a VK_RETURN event, restore the screen - state to undo the visual effect of the VK_RETURN*/ - if (InterlockedOr(&uv__restore_screen_state, 0)) { - HANDLE active_screen_buffer = CreateFileA("conout$", + if (status == TRAP_REQUESTED) { + /* If we canceled the read by sending a VK_RETURN event, restore the + screen state to undo the visual effect of the VK_RETURN */ + if (read_console_success && InterlockedOr(&uv__restore_screen_state, 0)) { + HANDLE active_screen_buffer; + active_screen_buffer = CreateFileA("conout$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (active_screen_buffer != INVALID_HANDLE_VALUE) { - pos = uv__saved_screen_state.dwCursorPosition; - - /* If the cursor was at the bottom line of the screen buffer, the - VK_RETURN would have caused the buffer contents to scroll up by - one line. The right position to reset the cursor to is therefore one - line higher */ - if (pos.Y == uv__saved_screen_state.dwSize.Y - 1) - pos.Y--; - - SetConsoleCursorPosition(active_screen_buffer, pos); - CloseHandle(active_screen_buffer); + if (active_screen_buffer != INVALID_HANDLE_VALUE) { + pos = uv__saved_screen_state.dwCursorPosition; + + /* If the cursor was at the bottom line of the screen buffer, the + VK_RETURN would have caused the buffer contents to scroll up by one + line. The right position to reset the cursor to is therefore one line + higher */ + if (pos.Y == uv__saved_screen_state.dwSize.Y - 1) + pos.Y--; + + SetConsoleCursorPosition(active_screen_buffer, pos); + CloseHandle(active_screen_buffer); + } } + uv_sem_post(&uv_tty_output_lock); } - POST_COMPLETION_FOR_REQ(loop, req); return 0; } @@ -504,8 +590,10 @@ static void uv_tty_queue_read_line(uv_loop_t* loop, uv_tty_t* handle) { req = &handle->read_req; memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped)); + handle->tty.rd.read_line_buffer = uv_buf_init(NULL, 0); handle->alloc_cb((uv_handle_t*) handle, 8192, &handle->tty.rd.read_line_buffer); - if (handle->tty.rd.read_line_buffer.len == 0) { + if (handle->tty.rd.read_line_buffer.base == NULL || + handle->tty.rd.read_line_buffer.len == 0) { handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &handle->tty.rd.read_line_buffer); @@ -559,10 +647,10 @@ static const char* get_vt100_fn_key(DWORD code, char shift, char ctrl, } switch (code) { - /* These mappings are the same as Cygwin's. Unmodified and alt-modified */ - /* keypad keys comply with linux console, modifiers comply with xterm */ - /* modifier usage. F1..f12 and shift-f1..f10 comply with linux console, */ - /* f6..f12 with and without modifiers comply with rxvt. */ + /* These mappings are the same as Cygwin's. Unmodified and alt-modified + * keypad keys comply with linux console, modifiers comply with xterm + * modifier usage. F1. f12 and shift-f1. f10 comply with linux console, f6. + * f12 with and without modifiers comply with rxvt. */ VK_CASE(VK_INSERT, "[2~", "[2;2~", "[2;5~", "[2;6~") VK_CASE(VK_END, "[4~", "[4;2~", "[4;5~", "[4;6~") VK_CASE(VK_DOWN, "[B", "[1;2B", "[1;5B", "[1;6B") @@ -645,8 +733,8 @@ void uv_process_tty_read_raw_req(uv_loop_t* loop, uv_tty_t* handle, goto out; } - /* Windows sends a lot of events that we're not interested in, so buf */ - /* will be allocated on demand, when there's actually something to emit. */ + /* Windows sends a lot of events that we're not interested in, so buf will be + * allocated on demand, when there's actually something to emit. */ buf = uv_null_buf_; buf_used = 0; @@ -667,39 +755,28 @@ void uv_process_tty_read_raw_req(uv_loop_t* loop, uv_tty_t* handle, } records_left--; - /* If the window was resized, recompute the virtual window size. This */ - /* will trigger a SIGWINCH signal if the window size changed in an */ - /* way that matters to libuv. */ + /* We might be not subscribed to EVENT_CONSOLE_LAYOUT or we might be + * running under some TTY emulator that does not send those events. */ if (handle->tty.rd.last_input_record.EventType == WINDOW_BUFFER_SIZE_EVENT) { - CONSOLE_SCREEN_BUFFER_INFO info; - - EnterCriticalSection(&uv_tty_output_lock); - - if (uv_tty_output_handle != INVALID_HANDLE_VALUE && - GetConsoleScreenBufferInfo(uv_tty_output_handle, &info)) { - uv_tty_update_virtual_window(&info); - } - - LeaveCriticalSection(&uv_tty_output_lock); - - continue; + uv__tty_console_signal_resize(); } - /* Ignore other events that are not key or resize events. */ + /* Ignore other events that are not key events. */ if (handle->tty.rd.last_input_record.EventType != KEY_EVENT) { continue; } - /* Ignore keyup events, unless the left alt key was held and a valid */ - /* unicode character was emitted. */ - if (!KEV.bKeyDown && !(((KEV.dwControlKeyState & LEFT_ALT_PRESSED) || - KEV.wVirtualKeyCode==VK_MENU) && KEV.uChar.UnicodeChar != 0)) { + /* Ignore keyup events, unless the left alt key was held and a valid + * unicode character was emitted. */ + if (!KEV.bKeyDown && + (KEV.wVirtualKeyCode != VK_MENU || + KEV.uChar.UnicodeChar == 0)) { continue; } - /* Ignore keypresses to numpad number keys if the left alt is held */ - /* because the user is composing a character, or windows simulating */ - /* this. */ + /* Ignore keypresses to numpad number keys if the left alt is held + * because the user is composing a character, or windows simulating this. + */ if ((KEV.dwControlKeyState & LEFT_ALT_PRESSED) && !(KEV.dwControlKeyState & ENHANCED_KEY) && (KEV.wVirtualKeyCode == VK_INSERT || @@ -736,8 +813,8 @@ void uv_process_tty_read_raw_req(uv_loop_t* loop, uv_tty_t* handle, continue; } - /* Prefix with \u033 if alt was held, but alt was not used as part */ - /* a compose sequence. */ + /* Prefix with \u033 if alt was held, but alt was not used as part a + * compose sequence. */ if ((KEV.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) && !(KEV.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) && KEV.bKeyDown) { @@ -750,8 +827,9 @@ void uv_process_tty_read_raw_req(uv_loop_t* loop, uv_tty_t* handle, if (KEV.uChar.UnicodeChar >= 0xDC00 && KEV.uChar.UnicodeChar < 0xE000) { /* UTF-16 surrogate pair */ - WCHAR utf16_buffer[2] = { handle->tty.rd.last_utf16_high_surrogate, - KEV.uChar.UnicodeChar}; + WCHAR utf16_buffer[2]; + utf16_buffer[0] = handle->tty.rd.last_utf16_high_surrogate; + utf16_buffer[1] = KEV.uChar.UnicodeChar; char_len = WideCharToMultiByte(CP_UTF8, 0, utf16_buffer, @@ -775,8 +853,8 @@ void uv_process_tty_read_raw_req(uv_loop_t* loop, uv_tty_t* handle, /* Whatever happened, the last character wasn't a high surrogate. */ handle->tty.rd.last_utf16_high_surrogate = 0; - /* If the utf16 character(s) couldn't be converted something must */ - /* be wrong. */ + /* If the utf16 character(s) couldn't be converted something must be + * wrong. */ if (!char_len) { handle->flags &= ~UV_HANDLE_READING; DECREASE_ACTIVE_COUNT(loop, handle); @@ -828,8 +906,9 @@ void uv_process_tty_read_raw_req(uv_loop_t* loop, uv_tty_t* handle, if (handle->tty.rd.last_key_offset < handle->tty.rd.last_key_len) { /* Allocate a buffer if needed */ if (buf_used == 0) { + buf = uv_buf_init(NULL, 0); handle->alloc_cb((uv_handle_t*) handle, 1024, &buf); - if (buf.len == 0) { + if (buf.base == NULL || buf.len == 0) { handle->read_cb((uv_stream_t*) handle, UV_ENOBUFS, &buf); goto out; } @@ -899,21 +978,15 @@ void uv_process_tty_read_line_req(uv_loop_t* loop, uv_tty_t* handle, handle->read_cb((uv_stream_t*) handle, uv_translate_sys_error(GET_REQ_ERROR(req)), &buf); - } else { - /* The read was cancelled, or whatever we don't care */ - handle->read_cb((uv_stream_t*) handle, 0, &buf); } - } else { - if (!(handle->flags & UV_HANDLE_CANCELLATION_PENDING)) { - /* Read successful */ - /* TODO: read unicode, convert to utf-8 */ + if (!(handle->flags & UV_HANDLE_CANCELLATION_PENDING) && + req->u.io.overlapped.InternalHigh != 0) { + /* Read successful. TODO: read unicode, convert to utf-8 */ DWORD bytes = req->u.io.overlapped.InternalHigh; handle->read_cb((uv_stream_t*) handle, bytes, &buf); - } else { - handle->flags &= ~UV_HANDLE_CANCELLATION_PENDING; - handle->read_cb((uv_stream_t*) handle, 0, &buf); } + handle->flags &= ~UV_HANDLE_CANCELLATION_PENDING; } /* Wait for more input events. */ @@ -931,9 +1004,9 @@ void uv_process_tty_read_req(uv_loop_t* loop, uv_tty_t* handle, assert(handle->type == UV_TTY); assert(handle->flags & UV_HANDLE_TTY_READABLE); - /* If the read_line_buffer member is zero, it must have been an raw read. */ - /* Otherwise it was a line-buffered read. */ - /* FIXME: This is quite obscure. Use a flag or something. */ + /* If the read_line_buffer member is zero, it must have been an raw read. + * Otherwise it was a line-buffered read. FIXME: This is quite obscure. Use a + * flag or something. */ if (handle->tty.rd.read_line_buffer.len == 0) { uv_process_tty_read_raw_req(loop, handle, req); } else { @@ -955,17 +1028,20 @@ int uv_tty_read_start(uv_tty_t* handle, uv_alloc_cb alloc_cb, handle->read_cb = read_cb; handle->alloc_cb = alloc_cb; - /* If reading was stopped and then started again, there could still be a */ - /* read request pending. */ + /* If reading was stopped and then started again, there could still be a read + * request pending. */ if (handle->flags & UV_HANDLE_READ_PENDING) { return 0; } - /* Maybe the user stopped reading half-way while processing key events. */ - /* Short-circuit if this could be the case. */ + /* Maybe the user stopped reading half-way while processing key events. + * Short-circuit if this could be the case. */ if (handle->tty.rd.last_key_len > 0) { SET_REQ_SUCCESS(&handle->read_req); uv_insert_pending_req(handle->loop, (uv_req_t*) &handle->read_req); + /* Make sure no attempt is made to insert it again until it's handled. */ + handle->flags |= UV_HANDLE_READ_PENDING; + handle->reqs_pending++; return 0; } @@ -986,9 +1062,10 @@ int uv_tty_read_stop(uv_tty_t* handle) { return 0; if (handle->flags & UV_HANDLE_TTY_RAW) { - /* Cancel raw read */ - /* Write some bullshit event to force the console wait to return. */ + /* Cancel raw read. Write some bullshit event to force the console wait to + * return. */ memset(&record, 0, sizeof record); + record.EventType = FOCUS_EVENT; if (!WriteConsoleInputW(handle->handle, &record, 1, &written)) { return GetLastError(); } @@ -1013,11 +1090,16 @@ static int uv__cancel_read_console(uv_tty_t* handle) { assert(!(handle->flags & UV_HANDLE_CANCELLATION_PENDING)); + /* Hold the output lock during the cancellation, to ensure that further + writes don't interfere with the screen state. It will be the ReadConsole + thread's responsibility to release the lock. */ + uv_sem_wait(&uv_tty_output_lock); status = InterlockedExchange(&uv__read_console_status, TRAP_REQUESTED); if (status != IN_PROGRESS) { /* Either we have managed to set a trap for the other thread before ReadConsole is called, or ReadConsole has returned because the user has pressed ENTER. In either case, there is nothing else to do. */ + uv_sem_post(&uv_tty_output_lock); return 0; } @@ -1056,9 +1138,6 @@ static int uv__cancel_read_console(uv_tty_t* handle) { static void uv_tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info) { - int old_virtual_width = uv_tty_virtual_width; - int old_virtual_height = uv_tty_virtual_height; - uv_tty_virtual_width = info->dwSize.X; uv_tty_virtual_height = info->srWindow.Bottom - info->srWindow.Top + 1; @@ -1067,8 +1146,8 @@ static void uv_tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info) { uv_tty_virtual_offset = info->dwCursorPosition.Y; } else if (uv_tty_virtual_offset < info->dwCursorPosition.Y - uv_tty_virtual_height + 1) { - /* If suddenly find the cursor outside of the virtual window, it must */ - /* have somehow scrolled. Update the virtual window offset. */ + /* If suddenly find the cursor outside of the virtual window, it must have + * somehow scrolled. Update the virtual window offset. */ uv_tty_virtual_offset = info->dwCursorPosition.Y - uv_tty_virtual_height + 1; } @@ -1078,14 +1157,6 @@ static void uv_tty_update_virtual_window(CONSOLE_SCREEN_BUFFER_INFO* info) { if (uv_tty_virtual_offset < 0) { uv_tty_virtual_offset = 0; } - - /* If the virtual window size changed, emit a SIGWINCH signal. Don't emit */ - /* if this was the first time the virtual window size was computed. */ - if (old_virtual_width != -1 && old_virtual_height != -1 && - (uv_tty_virtual_width != old_virtual_width || - uv_tty_virtual_height != old_virtual_height)) { - uv__signal_dispatch(SIGWINCH); - } } @@ -1180,7 +1251,7 @@ static int uv_tty_move_caret(uv_tty_t* handle, int x, unsigned char x_relative, static int uv_tty_reset(uv_tty_t* handle, DWORD* error) { const COORD origin = {0, 0}; const WORD char_attrs = uv_tty_default_text_attributes; - CONSOLE_SCREEN_BUFFER_INFO info; + CONSOLE_SCREEN_BUFFER_INFO screen_buffer_info; DWORD count, written; if (*error != ERROR_SUCCESS) { @@ -1201,12 +1272,12 @@ static int uv_tty_reset(uv_tty_t* handle, DWORD* error) { /* Clear the screen buffer. */ retry: - if (!GetConsoleScreenBufferInfo(handle->handle, &info)) { - *error = GetLastError(); - return -1; + if (!GetConsoleScreenBufferInfo(handle->handle, &screen_buffer_info)) { + *error = GetLastError(); + return -1; } - count = info.dwSize.X * info.dwSize.Y; + count = screen_buffer_info.dwSize.X * screen_buffer_info.dwSize.Y; if (!(FillConsoleOutputCharacterW(handle->handle, L'\x20', @@ -1229,7 +1300,13 @@ static int uv_tty_reset(uv_tty_t* handle, DWORD* error) { /* Move the virtual window up to the top. */ uv_tty_virtual_offset = 0; - uv_tty_update_virtual_window(&info); + uv_tty_update_virtual_window(&screen_buffer_info); + + /* Reset the cursor size and the cursor state. */ + if (!SetConsoleCursorInfo(handle->handle, &uv_tty_default_cursor_info)) { + *error = GetLastError(); + return -1; + } return 0; } @@ -1263,8 +1340,8 @@ static int uv_tty_clear(uv_tty_t* handle, int dir, char entire_screen, x2 = 0; x2r = 1; } else { - /* Clear to end of row. We pretend the console is 65536 characters wide, */ - /* uv_tty_make_real_coord will clip it to the actual console width. */ + /* Clear to end of row. We pretend the console is 65536 characters wide, + * uv_tty_make_real_coord will clip it to the actual console width. */ x2 = 0xffff; x2r = 0; } @@ -1568,13 +1645,38 @@ static int uv_tty_set_cursor_visibility(uv_tty_t* handle, return 0; } +static int uv_tty_set_cursor_shape(uv_tty_t* handle, int style, DWORD* error) { + CONSOLE_CURSOR_INFO cursor_info; + + if (!GetConsoleCursorInfo(handle->handle, &cursor_info)) { + *error = GetLastError(); + return -1; + } + + if (style == 0) { + cursor_info.dwSize = uv_tty_default_cursor_info.dwSize; + } else if (style <= 2) { + cursor_info.dwSize = CURSOR_SIZE_LARGE; + } else { + cursor_info.dwSize = CURSOR_SIZE_SMALL; + } + + if (!SetConsoleCursorInfo(handle->handle, &cursor_info)) { + *error = GetLastError(); + return -1; + } + + return 0; +} + + static int uv_tty_write_bufs(uv_tty_t* handle, const uv_buf_t bufs[], unsigned int nbufs, DWORD* error) { - /* We can only write 8k characters at a time. Windows can't handle */ - /* much more characters in a single console write anyway. */ - WCHAR utf16_buf[8192]; + /* We can only write 8k characters at a time. Windows can't handle much more + * characters in a single console write anyway. */ + WCHAR utf16_buf[MAX_CONSOLE_CHAR]; DWORD utf16_buf_used = 0; unsigned int i; @@ -1595,14 +1697,13 @@ static int uv_tty_write_bufs(uv_tty_t* handle, unsigned char utf8_bytes_left = handle->tty.wr.utf8_bytes_left; unsigned int utf8_codepoint = handle->tty.wr.utf8_codepoint; unsigned char previous_eol = handle->tty.wr.previous_eol; - unsigned char ansi_parser_state = handle->tty.wr.ansi_parser_state; + unsigned short ansi_parser_state = handle->tty.wr.ansi_parser_state; - /* Store the error here. If we encounter an error, stop trying to do i/o */ - /* but keep parsing the buffer so we leave the parser in a consistent */ - /* state. */ + /* Store the error here. If we encounter an error, stop trying to do i/o but + * keep parsing the buffer so we leave the parser in a consistent state. */ *error = ERROR_SUCCESS; - EnterCriticalSection(&uv_tty_output_lock); + uv_sem_wait(&uv_tty_output_lock); for (i = 0; i < nbufs; i++) { uv_buf_t buf = bufs[i]; @@ -1611,9 +1712,9 @@ static int uv_tty_write_bufs(uv_tty_t* handle, for (j = 0; j < buf.len; j++) { unsigned char c = buf.base[j]; - /* Run the character through the utf8 decoder We happily accept non */ - /* shortest form encodings and invalid code points - there's no real */ - /* harm that can be done. */ + /* Run the character through the utf8 decoder We happily accept non + * shortest form encodings and invalid code points - there's no real harm + * that can be done. */ if (utf8_bytes_left == 0) { /* Read utf-8 start byte */ DWORD first_zero_bit; @@ -1653,8 +1754,8 @@ static int uv_tty_write_bufs(uv_tty_t* handle, /* Start byte where continuation was expected. */ utf8_bytes_left = 0; utf8_codepoint = UNICODE_REPLACEMENT_CHARACTER; - /* Patch buf offset so this character will be parsed again as a */ - /* start byte. */ + /* Patch buf offset so this character will be parsed again as a start + * byte. */ j--; } @@ -1664,7 +1765,9 @@ static int uv_tty_write_bufs(uv_tty_t* handle, } /* Parse vt100/ansi escape codes */ - if (ansi_parser_state == ANSI_NORMAL) { + if (uv__vterm_state == UV_TTY_SUPPORTED) { + /* Pass through escape codes if conhost supports them. */ + } else if (ansi_parser_state == ANSI_NORMAL) { switch (utf8_codepoint) { case '\033': ansi_parser_state = ANSI_ESCAPE_SEEN; @@ -1687,8 +1790,8 @@ static int uv_tty_write_bufs(uv_tty_t* handle, case '_': case 'P': case ']': - /* Not supported, but we'll have to parse until we see a stop */ - /* code, e.g. ESC \ or BEL. */ + /* Not supported, but we'll have to parse until we see a stop code, + * e. g. ESC \ or BEL. */ ansi_parser_state = ANSI_ST_CONTROL; continue; @@ -1710,7 +1813,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle, ansi_parser_state = ANSI_NORMAL; continue; - case '8': + case '8': /* Restore the cursor position and text attributes */ FLUSH_TEXT(); uv_tty_restore_state(handle, 1, error); @@ -1728,120 +1831,193 @@ static int uv_tty_write_bufs(uv_tty_t* handle, } } + } else if (ansi_parser_state == ANSI_IGNORE) { + /* We're ignoring this command. Stop only on command character. */ + if (utf8_codepoint >= '@' && utf8_codepoint <= '~') { + ansi_parser_state = ANSI_NORMAL; + } + continue; + + } else if (ansi_parser_state == ANSI_DECSCUSR) { + /* So far we've the sequence `ESC [ arg space`, and we're waiting for + * the final command byte. */ + if (utf8_codepoint >= '@' && utf8_codepoint <= '~') { + /* Command byte */ + if (utf8_codepoint == 'q') { + /* Change the cursor shape */ + int style = handle->tty.wr.ansi_csi_argc + ? handle->tty.wr.ansi_csi_argv[0] : 1; + if (style >= 0 && style <= 6) { + FLUSH_TEXT(); + uv_tty_set_cursor_shape(handle, style, error); + } + } + + /* Sequence ended - go back to normal state. */ + ansi_parser_state = ANSI_NORMAL; + continue; + } + /* Unexpected character, but sequence hasn't ended yet. Ignore the rest + * of the sequence. */ + ansi_parser_state = ANSI_IGNORE; + } else if (ansi_parser_state & ANSI_CSI) { - if (!(ansi_parser_state & ANSI_IGNORE)) { - if (utf8_codepoint >= '0' && utf8_codepoint <= '9') { - /* Parsing a numerical argument */ - - if (!(ansi_parser_state & ANSI_IN_ARG)) { - /* We were not currently parsing a number */ - - /* Check for too many arguments */ - if (handle->tty.wr.ansi_csi_argc >= ARRAY_SIZE(handle->tty.wr.ansi_csi_argv)) { - ansi_parser_state |= ANSI_IGNORE; - continue; - } - - ansi_parser_state |= ANSI_IN_ARG; - handle->tty.wr.ansi_csi_argc++; - handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] = - (unsigned short) utf8_codepoint - '0'; + /* So far we've seen `ESC [`, and we may or may not have already parsed + * some of the arguments that follow. */ + + if (utf8_codepoint >= '0' && utf8_codepoint <= '9') { + /* Parse a numerical argument. */ + if (!(ansi_parser_state & ANSI_IN_ARG)) { + /* We were not currently parsing a number, add a new one. */ + /* Check for that there are too many arguments. */ + if (handle->tty.wr.ansi_csi_argc >= + ARRAY_SIZE(handle->tty.wr.ansi_csi_argv)) { + ansi_parser_state = ANSI_IGNORE; continue; - } else { - /* We were already parsing a number. Parse next digit. */ - uint32_t value = 10 * - handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1]; - - /* Check for overflow. */ - if (value > UINT16_MAX) { - ansi_parser_state |= ANSI_IGNORE; - continue; - } - - handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] = - (unsigned short) value + (utf8_codepoint - '0'); - continue; } + ansi_parser_state |= ANSI_IN_ARG; + handle->tty.wr.ansi_csi_argc++; + handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] = + (unsigned short) utf8_codepoint - '0'; + continue; + + } else { + /* We were already parsing a number. Parse next digit. */ + uint32_t value = 10 * + handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1]; - } else if (utf8_codepoint == ';') { - /* Denotes the end of an argument. */ - if (ansi_parser_state & ANSI_IN_ARG) { - ansi_parser_state &= ~ANSI_IN_ARG; + /* Check for overflow. */ + if (value > UINT16_MAX) { + ansi_parser_state = ANSI_IGNORE; continue; + } - } else { - /* If ANSI_IN_ARG is not set, add another argument and */ - /* default it to 0. */ - /* Check for too many arguments */ - if (handle->tty.wr.ansi_csi_argc >= ARRAY_SIZE(handle->tty.wr.ansi_csi_argv)) { - ansi_parser_state |= ANSI_IGNORE; - continue; - } - - handle->tty.wr.ansi_csi_argc++; - handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] = 0; + handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] = + (unsigned short) value + (utf8_codepoint - '0'); + continue; + } + + } else if (utf8_codepoint == ';') { + /* Denotes the end of an argument. */ + if (ansi_parser_state & ANSI_IN_ARG) { + ansi_parser_state &= ~ANSI_IN_ARG; + continue; + + } else { + /* If ANSI_IN_ARG is not set, add another argument and default + * it to 0. */ + + /* Check for too many arguments */ + if (handle->tty.wr.ansi_csi_argc >= + + ARRAY_SIZE(handle->tty.wr.ansi_csi_argv)) { + ansi_parser_state = ANSI_IGNORE; continue; } - } else if (utf8_codepoint == '?' && !(ansi_parser_state & ANSI_IN_ARG) && - handle->tty.wr.ansi_csi_argc == 0) { - /* Ignores '?' if it is the first character after CSI[ */ - /* This is an extension character from the VT100 codeset */ - /* that is supported and used by most ANSI terminals today. */ + handle->tty.wr.ansi_csi_argc++; + handle->tty.wr.ansi_csi_argv[handle->tty.wr.ansi_csi_argc - 1] = 0; continue; + } - } else if (utf8_codepoint >= '@' && utf8_codepoint <= '~' && - (handle->tty.wr.ansi_csi_argc > 0 || utf8_codepoint != '[')) { - int x, y, d; + } else if (utf8_codepoint == '?' && + !(ansi_parser_state & ANSI_IN_ARG) && + !(ansi_parser_state & ANSI_EXTENSION) && + handle->tty.wr.ansi_csi_argc == 0) { + /* Pass through '?' if it is the first character after CSI */ + /* This is an extension character from the VT100 codeset */ + /* that is supported and used by most ANSI terminals today. */ + ansi_parser_state |= ANSI_EXTENSION; + continue; + + } else if (utf8_codepoint == ' ' && + !(ansi_parser_state & ANSI_EXTENSION)) { + /* We expect a command byte to follow after this space. The only + * command that we current support is 'set cursor style'. */ + ansi_parser_state = ANSI_DECSCUSR; + continue; + + } else if (utf8_codepoint >= '@' && utf8_codepoint <= '~') { + /* Command byte */ + if (ansi_parser_state & ANSI_EXTENSION) { + /* Sequence is `ESC [ ? args command`. */ + switch (utf8_codepoint) { + case 'l': + /* Hide the cursor */ + if (handle->tty.wr.ansi_csi_argc == 1 && + handle->tty.wr.ansi_csi_argv[0] == 25) { + FLUSH_TEXT(); + uv_tty_set_cursor_visibility(handle, 0, error); + } + break; + + case 'h': + /* Show the cursor */ + if (handle->tty.wr.ansi_csi_argc == 1 && + handle->tty.wr.ansi_csi_argv[0] == 25) { + FLUSH_TEXT(); + uv_tty_set_cursor_visibility(handle, 1, error); + } + break; + } - /* Command byte */ + } else { + /* Sequence is `ESC [ args command`. */ + int x, y, d; switch (utf8_codepoint) { case 'A': /* cursor up */ FLUSH_TEXT(); - y = -(handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 1); + y = -(handle->tty.wr.ansi_csi_argc + ? handle->tty.wr.ansi_csi_argv[0] : 1); uv_tty_move_caret(handle, 0, 1, y, 1, error); break; case 'B': /* cursor down */ FLUSH_TEXT(); - y = handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 1; + y = handle->tty.wr.ansi_csi_argc + ? handle->tty.wr.ansi_csi_argv[0] : 1; uv_tty_move_caret(handle, 0, 1, y, 1, error); break; case 'C': /* cursor forward */ FLUSH_TEXT(); - x = handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 1; + x = handle->tty.wr.ansi_csi_argc + ? handle->tty.wr.ansi_csi_argv[0] : 1; uv_tty_move_caret(handle, x, 1, 0, 1, error); break; case 'D': /* cursor back */ FLUSH_TEXT(); - x = -(handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 1); + x = -(handle->tty.wr.ansi_csi_argc + ? handle->tty.wr.ansi_csi_argv[0] : 1); uv_tty_move_caret(handle, x, 1, 0, 1, error); break; case 'E': /* cursor next line */ FLUSH_TEXT(); - y = handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 1; + y = handle->tty.wr.ansi_csi_argc + ? handle->tty.wr.ansi_csi_argv[0] : 1; uv_tty_move_caret(handle, 0, 0, y, 1, error); break; case 'F': /* cursor previous line */ FLUSH_TEXT(); - y = -(handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 1); + y = -(handle->tty.wr.ansi_csi_argc + ? handle->tty.wr.ansi_csi_argv[0] : 1); uv_tty_move_caret(handle, 0, 0, y, 1, error); break; case 'G': /* cursor horizontal move absolute */ FLUSH_TEXT(); - x = (handle->tty.wr.ansi_csi_argc >= 1 && handle->tty.wr.ansi_csi_argv[0]) + x = (handle->tty.wr.ansi_csi_argc >= 1 && + handle->tty.wr.ansi_csi_argv[0]) ? handle->tty.wr.ansi_csi_argv[0] - 1 : 0; uv_tty_move_caret(handle, x, 0, 0, 1, error); break; @@ -1850,9 +2026,11 @@ static int uv_tty_write_bufs(uv_tty_t* handle, case 'f': /* cursor move absolute */ FLUSH_TEXT(); - y = (handle->tty.wr.ansi_csi_argc >= 1 && handle->tty.wr.ansi_csi_argv[0]) + y = (handle->tty.wr.ansi_csi_argc >= 1 && + handle->tty.wr.ansi_csi_argv[0]) ? handle->tty.wr.ansi_csi_argv[0] - 1 : 0; - x = (handle->tty.wr.ansi_csi_argc >= 2 && handle->tty.wr.ansi_csi_argv[1]) + x = (handle->tty.wr.ansi_csi_argc >= 2 && + handle->tty.wr.ansi_csi_argv[1]) ? handle->tty.wr.ansi_csi_argv[1] - 1 : 0; uv_tty_move_caret(handle, x, 0, y, 0, error); break; @@ -1860,7 +2038,8 @@ static int uv_tty_write_bufs(uv_tty_t* handle, case 'J': /* Erase screen */ FLUSH_TEXT(); - d = handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 0; + d = handle->tty.wr.ansi_csi_argc + ? handle->tty.wr.ansi_csi_argv[0] : 0; if (d >= 0 && d <= 2) { uv_tty_clear(handle, d, 1, error); } @@ -1869,7 +2048,8 @@ static int uv_tty_write_bufs(uv_tty_t* handle, case 'K': /* Erase line */ FLUSH_TEXT(); - d = handle->tty.wr.ansi_csi_argc ? handle->tty.wr.ansi_csi_argv[0] : 0; + d = handle->tty.wr.ansi_csi_argc + ? handle->tty.wr.ansi_csi_argv[0] : 0; if (d >= 0 && d <= 2) { uv_tty_clear(handle, d, 0, error); } @@ -1892,47 +2072,23 @@ static int uv_tty_write_bufs(uv_tty_t* handle, FLUSH_TEXT(); uv_tty_restore_state(handle, 0, error); break; - - case 'l': - /* Hide the cursor */ - if (handle->tty.wr.ansi_csi_argc == 1 && - handle->tty.wr.ansi_csi_argv[0] == 25) { - FLUSH_TEXT(); - uv_tty_set_cursor_visibility(handle, 0, error); - } - break; - - case 'h': - /* Show the cursor */ - if (handle->tty.wr.ansi_csi_argc == 1 && - handle->tty.wr.ansi_csi_argv[0] == 25) { - FLUSH_TEXT(); - uv_tty_set_cursor_visibility(handle, 1, error); - } - break; } + } - /* Sequence ended - go back to normal state. */ - ansi_parser_state = ANSI_NORMAL; - continue; + /* Sequence ended - go back to normal state. */ + ansi_parser_state = ANSI_NORMAL; + continue; - } else { - /* We don't support commands that use private mode characters or */ - /* intermediaries. Ignore the rest of the sequence. */ - ansi_parser_state |= ANSI_IGNORE; - continue; - } } else { - /* We're ignoring this command. Stop only on command character. */ - if (utf8_codepoint >= '@' && utf8_codepoint <= '~') { - ansi_parser_state = ANSI_NORMAL; - } + /* We don't support commands that use private mode characters or + * intermediaries. Ignore the rest of the sequence. */ + ansi_parser_state = ANSI_IGNORE; continue; } } else if (ansi_parser_state & ANSI_ST_CONTROL) { - /* Unsupported control code */ - /* Ignore everything until we see BEL or ESC \ */ + /* Unsupported control code. + * Ignore everything until we see `BEL` or `ESC \`. */ if (ansi_parser_state & ANSI_IN_STRING) { if (!(ansi_parser_state & ANSI_BACKSLASH_SEEN)) { if (utf8_codepoint == '"') { @@ -1966,13 +2122,6 @@ static int uv_tty_write_bufs(uv_tty_t* handle, abort(); } - /* We wouldn't mind emitting utf-16 surrogate pairs. Too bad, the */ - /* windows console doesn't really support UTF-16, so just emit the */ - /* replacement character. */ - if (utf8_codepoint > 0xffff) { - utf8_codepoint = UNICODE_REPLACEMENT_CHARACTER; - } - if (utf8_codepoint == 0x0a || utf8_codepoint == 0x0d) { /* EOL conversion - emit \r\n when we see \n. */ @@ -1982,10 +2131,10 @@ static int uv_tty_write_bufs(uv_tty_t* handle, utf16_buf[utf16_buf_used++] = L'\r'; utf16_buf[utf16_buf_used++] = L'\n'; } else if (utf8_codepoint == 0x0d && previous_eol == 0x0a) { - /* \n was followed by \r; do not print the \r, since */ - /* the source was either \r\n\r (so the second \r is */ - /* redundant) or was \n\r (so the \n was processed */ - /* by the last case and an \r automatically inserted). */ + /* \n was followed by \r; do not print the \r, since the source was + * either \r\n\r (so the second \r is redundant) or was \n\r (so the + * \n was processed by the last case and an \r automatically + * inserted). */ } else { /* \r without \n; print \r as-is. */ ENSURE_BUFFER_SPACE(1); @@ -1999,6 +2148,12 @@ static int uv_tty_write_bufs(uv_tty_t* handle, ENSURE_BUFFER_SPACE(1); utf16_buf[utf16_buf_used++] = (WCHAR) utf8_codepoint; previous_eol = 0; + } else { + ENSURE_BUFFER_SPACE(2); + utf8_codepoint -= 0x10000; + utf16_buf[utf16_buf_used++] = (WCHAR) (utf8_codepoint / 0x400 + 0xD800); + utf16_buf[utf16_buf_used++] = (WCHAR) (utf8_codepoint % 0x400 + 0xDC00); + previous_eol = 0; } } } @@ -2012,7 +2167,7 @@ static int uv_tty_write_bufs(uv_tty_t* handle, handle->tty.wr.previous_eol = previous_eol; handle->tty.wr.ansi_parser_state = ansi_parser_state; - LeaveCriticalSection(&uv_tty_output_lock); + uv_sem_post(&uv_tty_output_lock); if (*error == STATUS_SUCCESS) { return 0; @@ -2032,8 +2187,7 @@ int uv_tty_write(uv_loop_t* loop, uv_write_cb cb) { DWORD error; - uv_req_init(loop, (uv_req_t*) req); - req->type = UV_WRITE; + UV_REQ_INIT(req, UV_WRITE); req->handle = (uv_stream_t*) handle; req->cb = cb; @@ -2094,14 +2248,14 @@ void uv_process_tty_write_req(uv_loop_t* loop, uv_tty_t* handle, void uv_tty_close(uv_tty_t* handle) { assert(handle->u.fd == -1 || handle->u.fd > 2); + if (handle->flags & UV_HANDLE_READING) + uv_tty_read_stop(handle); + if (handle->u.fd == -1) CloseHandle(handle->handle); else close(handle->u.fd); - if (handle->flags & UV_HANDLE_READING) - uv_tty_read_stop(handle); - handle->u.fd = -1; handle->handle = INVALID_HANDLE_VALUE; handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); @@ -2121,7 +2275,7 @@ void uv_tty_endgame(uv_loop_t* loop, uv_tty_t* handle) { /* TTY shutdown is really just a no-op */ if (handle->stream.conn.shutdown_req->cb) { - if (handle->flags & UV__HANDLE_CLOSING) { + if (handle->flags & UV_HANDLE_CLOSING) { handle->stream.conn.shutdown_req->cb(handle->stream.conn.shutdown_req, UV_ECANCELED); } else { handle->stream.conn.shutdown_req->cb(handle->stream.conn.shutdown_req, 0); @@ -2134,10 +2288,10 @@ void uv_tty_endgame(uv_loop_t* loop, uv_tty_t* handle) { return; } - if (handle->flags & UV__HANDLE_CLOSING && + if (handle->flags & UV_HANDLE_CLOSING && handle->reqs_pending == 0) { - /* The wait handle used for raw reading should be unregistered when the */ - /* wait callback runs. */ + /* The wait handle used for raw reading should be unregistered when the + * wait callback runs. */ assert(!(handle->flags & UV_HANDLE_TTY_READABLE) || handle->tty.rd.read_raw_wait == NULL); @@ -2147,14 +2301,20 @@ void uv_tty_endgame(uv_loop_t* loop, uv_tty_t* handle) { } -/* TODO: remove me */ +/* + * uv_process_tty_accept_req() is a stub to keep DELEGATE_STREAM_REQ working + * TODO: find a way to remove it + */ void uv_process_tty_accept_req(uv_loop_t* loop, uv_tty_t* handle, uv_req_t* raw_req) { abort(); } -/* TODO: remove me */ +/* + * uv_process_tty_connect_req() is a stub to keep DELEGATE_STREAM_REQ working + * TODO: find a way to remove it + */ void uv_process_tty_connect_req(uv_loop_t* loop, uv_tty_t* handle, uv_connect_t* req) { abort(); @@ -2165,3 +2325,128 @@ int uv_tty_reset_mode(void) { /* Not necessary to do anything. */ return 0; } + +/* Determine whether or not this version of windows supports + * proper ANSI color codes. Should be supported as of windows + * 10 version 1511, build number 10.0.10586. + */ +static void uv__determine_vterm_state(HANDLE handle) { + DWORD dwMode = 0; + + uv__need_check_vterm_state = FALSE; + if (!GetConsoleMode(handle, &dwMode)) { + return; + } + + dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + if (!SetConsoleMode(handle, dwMode)) { + return; + } + + uv__vterm_state = UV_TTY_SUPPORTED; +} + +static DWORD WINAPI uv__tty_console_resize_message_loop_thread(void* param) { + NTSTATUS status; + ULONG_PTR conhost_pid; + MSG msg; + + if (pSetWinEventHook == NULL || pNtQueryInformationProcess == NULL) + return 0; + + status = pNtQueryInformationProcess(GetCurrentProcess(), + ProcessConsoleHostProcess, + &conhost_pid, + sizeof(conhost_pid), + NULL); + + if (!NT_SUCCESS(status)) { + /* We couldn't retrieve our console host process, probably because this + * is a 32-bit process running on 64-bit Windows. Fall back to receiving + * console events from the input stream only. */ + return 0; + } + + /* Ensure the PID is a multiple of 4, which is required by SetWinEventHook */ + conhost_pid &= ~(ULONG_PTR)0x3; + + uv__tty_console_resized = CreateEvent(NULL, TRUE, FALSE, NULL); + if (uv__tty_console_resized == NULL) + return 0; + if (QueueUserWorkItem(uv__tty_console_resize_watcher_thread, + NULL, + WT_EXECUTELONGFUNCTION) == 0) + return 0; + + if (!pSetWinEventHook(EVENT_CONSOLE_LAYOUT, + EVENT_CONSOLE_LAYOUT, + NULL, + uv__tty_console_resize_event, + (DWORD)conhost_pid, + 0, + WINEVENT_OUTOFCONTEXT)) + return 0; + + while (GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + return 0; +} + +static void CALLBACK uv__tty_console_resize_event(HWINEVENTHOOK hWinEventHook, + DWORD event, + HWND hwnd, + LONG idObject, + LONG idChild, + DWORD dwEventThread, + DWORD dwmsEventTime) { + SetEvent(uv__tty_console_resized); +} + +static DWORD WINAPI uv__tty_console_resize_watcher_thread(void* param) { + for (;;) { + /* Make sure to not overwhelm the system with resize events */ + Sleep(33); + WaitForSingleObject(uv__tty_console_resized, INFINITE); + uv__tty_console_signal_resize(); + ResetEvent(uv__tty_console_resized); + } + return 0; +} + +static void uv__tty_console_signal_resize(void) { + CONSOLE_SCREEN_BUFFER_INFO sb_info; + int width, height; + + if (!GetConsoleScreenBufferInfo(uv__tty_console_handle, &sb_info)) + return; + + width = sb_info.dwSize.X; + height = sb_info.srWindow.Bottom - sb_info.srWindow.Top + 1; + + uv_mutex_lock(&uv__tty_console_resize_mutex); + assert(uv__tty_console_width != -1 && uv__tty_console_height != -1); + if (width != uv__tty_console_width || height != uv__tty_console_height) { + uv__tty_console_width = width; + uv__tty_console_height = height; + uv_mutex_unlock(&uv__tty_console_resize_mutex); + uv__signal_dispatch(SIGWINCH); + } else { + uv_mutex_unlock(&uv__tty_console_resize_mutex); + } +} + +void uv_tty_set_vterm_state(uv_tty_vtermstate_t state) { + uv_sem_wait(&uv_tty_output_lock); + uv__need_check_vterm_state = FALSE; + uv__vterm_state = state; + uv_sem_post(&uv_tty_output_lock); +} + +int uv_tty_get_vterm_state(uv_tty_vtermstate_t* state) { + uv_sem_wait(&uv_tty_output_lock); + *state = uv__vterm_state; + uv_sem_post(&uv_tty_output_lock); + return 0; +} diff --git a/include/libuv/src/win/udp.c b/include/libuv/src/win/udp.c index 24792ec06..68ca728aa 100644 --- a/include/libuv/src/win/udp.c +++ b/include/libuv/src/win/udp.c @@ -36,22 +36,27 @@ const unsigned int uv_active_udp_streams_threshold = 0; /* A zero-size buffer for use by uv_udp_read */ static char uv_zero_[] = ""; - -int uv_udp_getsockname(const uv_udp_t* handle, +int uv_udp_getpeername(const uv_udp_t* handle, struct sockaddr* name, int* namelen) { - int result; - if (handle->socket == INVALID_SOCKET) { - return UV_EINVAL; - } + return uv__getsockpeername((const uv_handle_t*) handle, + getpeername, + name, + namelen, + 0); +} - result = getsockname(handle->socket, name, namelen); - if (result != 0) { - return uv_translate_sys_error(WSAGetLastError()); - } - return 0; +int uv_udp_getsockname(const uv_udp_t* handle, + struct sockaddr* name, + int* namelen) { + + return uv__getsockpeername((const uv_handle_t*) handle, + getsockname, + name, + namelen, + 0); } @@ -74,8 +79,8 @@ static int uv_udp_set_socket(uv_loop_t* loop, uv_udp_t* handle, SOCKET socket, return GetLastError(); } - /* Associate it with the I/O completion port. */ - /* Use uv_handle_t pointer as completion key. */ + /* Associate it with the I/O completion port. Use uv_handle_t pointer as + * completion key. */ if (CreateIoCompletionPort((HANDLE)socket, loop->iocp, (ULONG_PTR)socket, @@ -83,31 +88,28 @@ static int uv_udp_set_socket(uv_loop_t* loop, uv_udp_t* handle, SOCKET socket, return GetLastError(); } - if (pSetFileCompletionNotificationModes) { - /* All known Windows that support SetFileCompletionNotificationModes */ - /* have a bug that makes it impossible to use this function in */ - /* conjunction with datagram sockets. We can work around that but only */ - /* if the user is using the default UDP driver (AFD) and has no other */ - /* LSPs stacked on top. Here we check whether that is the case. */ - opt_len = (int) sizeof info; - if (getsockopt(socket, - SOL_SOCKET, - SO_PROTOCOL_INFOW, - (char*) &info, - &opt_len) == SOCKET_ERROR) { - return GetLastError(); - } + /* All known Windows that support SetFileCompletionNotificationModes have a + * bug that makes it impossible to use this function in conjunction with + * datagram sockets. We can work around that but only if the user is using + * the default UDP driver (AFD) and has no other. LSPs stacked on top. Here + * we check whether that is the case. */ + opt_len = (int) sizeof info; + if (getsockopt( + socket, SOL_SOCKET, SO_PROTOCOL_INFOW, (char*) &info, &opt_len) == + SOCKET_ERROR) { + return GetLastError(); + } - if (info.ProtocolChain.ChainLen == 1) { - if (pSetFileCompletionNotificationModes((HANDLE)socket, - FILE_SKIP_SET_EVENT_ON_HANDLE | - FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)) { - handle->flags |= UV_HANDLE_SYNC_BYPASS_IOCP; - handle->func_wsarecv = uv_wsarecv_workaround; - handle->func_wsarecvfrom = uv_wsarecvfrom_workaround; - } else if (GetLastError() != ERROR_INVALID_FUNCTION) { - return GetLastError(); - } + if (info.ProtocolChain.ChainLen == 1) { + if (SetFileCompletionNotificationModes( + (HANDLE) socket, + FILE_SKIP_SET_EVENT_ON_HANDLE | + FILE_SKIP_COMPLETION_PORT_ON_SUCCESS)) { + handle->flags |= UV_HANDLE_SYNC_BYPASS_IOCP; + handle->func_wsarecv = uv_wsarecv_workaround; + handle->func_wsarecvfrom = uv_wsarecvfrom_workaround; + } else if (GetLastError() != ERROR_INVALID_FUNCTION) { + return GetLastError(); } } @@ -123,17 +125,10 @@ static int uv_udp_set_socket(uv_loop_t* loop, uv_udp_t* handle, SOCKET socket, } -int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) { - int domain; - - /* Use the lower 8 bits for the domain */ - domain = flags & 0xFF; - if (domain != AF_INET && domain != AF_INET6 && domain != AF_UNSPEC) - return UV_EINVAL; - - if (flags & ~0xFF) - return UV_EINVAL; - +int uv__udp_init_ex(uv_loop_t* loop, + uv_udp_t* handle, + unsigned flags, + int domain) { uv__handle_init(loop, (uv_handle_t*) handle, UV_UDP); handle->socket = INVALID_SOCKET; handle->reqs_pending = 0; @@ -142,8 +137,7 @@ int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) { handle->func_wsarecvfrom = WSARecvFrom; handle->send_queue_size = 0; handle->send_queue_count = 0; - uv_req_init(loop, (uv_req_t*) &(handle->recv_req)); - handle->recv_req.type = UV_UDP_RECV; + UV_REQ_INIT(&handle->recv_req, UV_UDP_RECV); handle->recv_req.data = handle; /* If anything fails beyond this point we need to remove the handle from @@ -173,11 +167,6 @@ int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) { } -int uv_udp_init(uv_loop_t* loop, uv_udp_t* handle) { - return uv_udp_init_ex(loop, handle, AF_UNSPEC); -} - - void uv_udp_close(uv_loop_t* loop, uv_udp_t* handle) { uv_udp_recv_stop(handle); closesocket(handle->socket); @@ -192,7 +181,7 @@ void uv_udp_close(uv_loop_t* loop, uv_udp_t* handle) { void uv_udp_endgame(uv_loop_t* loop, uv_udp_t* handle) { - if (handle->flags & UV__HANDLE_CLOSING && + if (handle->flags & UV_HANDLE_CLOSING && handle->reqs_pending == 0) { assert(!(handle->flags & UV_HANDLE_CLOSED)); uv__handle_close(handle); @@ -200,6 +189,11 @@ void uv_udp_endgame(uv_loop_t* loop, uv_udp_t* handle) { } +int uv_udp_using_recvmmsg(const uv_udp_t* handle) { + return 0; +} + + static int uv_udp_maybe_bind(uv_udp_t* handle, const struct sockaddr* addr, unsigned int addrlen, @@ -246,12 +240,12 @@ static int uv_udp_maybe_bind(uv_udp_t* handle, handle->flags |= UV_HANDLE_IPV6; if (addr->sa_family == AF_INET6 && !(flags & UV_UDP_IPV6ONLY)) { - /* On windows IPV6ONLY is on by default. */ - /* If the user doesn't specify it libuv turns it off. */ + /* On windows IPV6ONLY is on by default. If the user doesn't specify it + * libuv turns it off. */ - /* TODO: how to handle errors? This may fail if there is no ipv4 stack */ - /* available, or when run on XP/2003 which have no support for dualstack */ - /* sockets. For now we're silently ignoring the error. */ + /* TODO: how to handle errors? This may fail if there is no ipv4 stack + * available, or when run on XP/2003 which have no support for dualstack + * sockets. For now we're silently ignoring the error. */ setsockopt(handle->socket, IPPROTO_IPV6, IPV6_V6ONLY, @@ -289,8 +283,9 @@ static void uv_udp_queue_recv(uv_loop_t* loop, uv_udp_t* handle) { if (loop->active_udp_streams < uv_active_udp_streams_threshold) { handle->flags &= ~UV_HANDLE_ZERO_READ; + handle->recv_buffer = uv_buf_init(NULL, 0); handle->alloc_cb((uv_handle_t*) handle, 65536, &handle->recv_buffer); - if (handle->recv_buffer.len == 0) { + if (handle->recv_buffer.base == NULL || handle->recv_buffer.len == 0) { handle->recv_cb(handle, UV_ENOBUFS, &handle->recv_buffer, NULL, 0); return; } @@ -369,7 +364,7 @@ int uv__udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, int err; if (handle->flags & UV_HANDLE_READING) { - return WSAEALREADY; + return UV_EALREADY; } err = uv_udp_maybe_bind(handle, @@ -377,7 +372,7 @@ int uv__udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, sizeof(uv_addr_ip4_any_), 0); if (err) - return err; + return uv_translate_sys_error(err); handle->flags |= UV_HANDLE_READING; INCREASE_ACTIVE_COUNT(loop, handle); @@ -386,8 +381,8 @@ int uv__udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, handle->recv_cb = recv_cb; handle->alloc_cb = alloc_cb; - /* If reading was stopped and then started again, there could still be a */ - /* recv request pending. */ + /* If reading was stopped and then started again, there could still be a recv + * request pending. */ if (!(handle->flags & UV_HANDLE_READ_PENDING)) uv_udp_queue_recv(loop, handle); @@ -416,8 +411,7 @@ static int uv__send(uv_udp_send_t* req, uv_loop_t* loop = handle->loop; DWORD result, bytes; - uv_req_init(loop, (uv_req_t*) req); - req->type = UV_UDP_SEND; + UV_REQ_INIT(req, UV_UDP_SEND); req->handle = handle; req->cb = cb; memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped)); @@ -468,19 +462,19 @@ void uv_process_udp_recv_req(uv_loop_t* loop, uv_udp_t* handle, if (!REQ_SUCCESS(req)) { DWORD err = GET_REQ_SOCK_ERROR(req); if (err == WSAEMSGSIZE) { - /* Not a real error, it just indicates that the received packet */ - /* was bigger than the receive buffer. */ + /* Not a real error, it just indicates that the received packet was + * bigger than the receive buffer. */ } else if (err == WSAECONNRESET || err == WSAENETRESET) { - /* A previous sendto operation failed; ignore this error. If */ - /* zero-reading we need to call WSARecv/WSARecvFrom _without_ the */ - /* MSG_PEEK flag to clear out the error queue. For nonzero reads, */ - /* immediately queue a new receive. */ + /* A previous sendto operation failed; ignore this error. If zero-reading + * we need to call WSARecv/WSARecvFrom _without_ the. MSG_PEEK flag to + * clear out the error queue. For nonzero reads, immediately queue a new + * receive. */ if (!(handle->flags & UV_HANDLE_ZERO_READ)) { goto done; } } else { - /* A real error occurred. Report the error to the user only if we're */ - /* currently reading. */ + /* A real error occurred. Report the error to the user only if we're + * currently reading. */ if (handle->flags & UV_HANDLE_READING) { uv_udp_recv_stop(handle); buf = (handle->flags & UV_HANDLE_ZERO_READ) ? @@ -504,10 +498,11 @@ void uv_process_udp_recv_req(uv_loop_t* loop, uv_udp_t* handle, struct sockaddr_storage from; int from_len; - /* Do a nonblocking receive */ - /* TODO: try to read multiple datagrams at once. FIONREAD maybe? */ + /* Do a nonblocking receive. + * TODO: try to read multiple datagrams at once. FIONREAD maybe? */ + buf = uv_buf_init(NULL, 0); handle->alloc_cb((uv_handle_t*) handle, 65536, &buf); - if (buf.len == 0) { + if (buf.base == NULL || buf.len == 0) { handle->recv_cb(handle, UV_ENOBUFS, &buf, NULL, 0); goto done; } @@ -700,6 +695,115 @@ int uv__udp_set_membership6(uv_udp_t* handle, } +static int uv__udp_set_source_membership4(uv_udp_t* handle, + const struct sockaddr_in* multicast_addr, + const char* interface_addr, + const struct sockaddr_in* source_addr, + uv_membership membership) { + struct ip_mreq_source mreq; + int optname; + int err; + + if (handle->flags & UV_HANDLE_IPV6) + return UV_EINVAL; + + /* If the socket is unbound, bind to inaddr_any. */ + err = uv_udp_maybe_bind(handle, + (const struct sockaddr*) &uv_addr_ip4_any_, + sizeof(uv_addr_ip4_any_), + UV_UDP_REUSEADDR); + if (err) + return uv_translate_sys_error(err); + + memset(&mreq, 0, sizeof(mreq)); + + if (interface_addr != NULL) { + err = uv_inet_pton(AF_INET, interface_addr, &mreq.imr_interface.s_addr); + if (err) + return err; + } else { + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + } + + mreq.imr_multiaddr.s_addr = multicast_addr->sin_addr.s_addr; + mreq.imr_sourceaddr.s_addr = source_addr->sin_addr.s_addr; + + if (membership == UV_JOIN_GROUP) + optname = IP_ADD_SOURCE_MEMBERSHIP; + else if (membership == UV_LEAVE_GROUP) + optname = IP_DROP_SOURCE_MEMBERSHIP; + else + return UV_EINVAL; + + if (setsockopt(handle->socket, + IPPROTO_IP, + optname, + (char*) &mreq, + sizeof(mreq)) == SOCKET_ERROR) { + return uv_translate_sys_error(WSAGetLastError()); + } + + return 0; +} + + +int uv__udp_set_source_membership6(uv_udp_t* handle, + const struct sockaddr_in6* multicast_addr, + const char* interface_addr, + const struct sockaddr_in6* source_addr, + uv_membership membership) { + struct group_source_req mreq; + struct sockaddr_in6 addr6; + int optname; + int err; + + STATIC_ASSERT(sizeof(mreq.gsr_group) >= sizeof(*multicast_addr)); + STATIC_ASSERT(sizeof(mreq.gsr_source) >= sizeof(*source_addr)); + + if ((handle->flags & UV_HANDLE_BOUND) && !(handle->flags & UV_HANDLE_IPV6)) + return UV_EINVAL; + + err = uv_udp_maybe_bind(handle, + (const struct sockaddr*) &uv_addr_ip6_any_, + sizeof(uv_addr_ip6_any_), + UV_UDP_REUSEADDR); + + if (err) + return uv_translate_sys_error(err); + + memset(&mreq, 0, sizeof(mreq)); + + if (interface_addr != NULL) { + err = uv_ip6_addr(interface_addr, 0, &addr6); + if (err) + return err; + mreq.gsr_interface = addr6.sin6_scope_id; + } else { + mreq.gsr_interface = 0; + } + + memcpy(&mreq.gsr_group, multicast_addr, sizeof(*multicast_addr)); + memcpy(&mreq.gsr_source, source_addr, sizeof(*source_addr)); + + if (membership == UV_JOIN_GROUP) + optname = MCAST_JOIN_SOURCE_GROUP; + else if (membership == UV_LEAVE_GROUP) + optname = MCAST_LEAVE_SOURCE_GROUP; + else + return UV_EINVAL; + + if (setsockopt(handle->socket, + IPPROTO_IPV6, + optname, + (char*) &mreq, + sizeof(mreq)) == SOCKET_ERROR) { + return uv_translate_sys_error(WSAGetLastError()); + } + + return 0; +} + + int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr, const char* interface_addr, @@ -716,6 +820,50 @@ int uv_udp_set_membership(uv_udp_t* handle, } +int uv_udp_set_source_membership(uv_udp_t* handle, + const char* multicast_addr, + const char* interface_addr, + const char* source_addr, + uv_membership membership) { + int err; + struct sockaddr_storage mcast_addr; + struct sockaddr_in* mcast_addr4; + struct sockaddr_in6* mcast_addr6; + struct sockaddr_storage src_addr; + struct sockaddr_in* src_addr4; + struct sockaddr_in6* src_addr6; + + mcast_addr4 = (struct sockaddr_in*)&mcast_addr; + mcast_addr6 = (struct sockaddr_in6*)&mcast_addr; + src_addr4 = (struct sockaddr_in*)&src_addr; + src_addr6 = (struct sockaddr_in6*)&src_addr; + + err = uv_ip4_addr(multicast_addr, 0, mcast_addr4); + if (err) { + err = uv_ip6_addr(multicast_addr, 0, mcast_addr6); + if (err) + return err; + err = uv_ip6_addr(source_addr, 0, src_addr6); + if (err) + return err; + return uv__udp_set_source_membership6(handle, + mcast_addr6, + interface_addr, + src_addr6, + membership); + } + + err = uv_ip4_addr(source_addr, 0, src_addr4); + if (err) + return err; + return uv__udp_set_source_membership4(handle, + mcast_addr4, + interface_addr, + src_addr4, + membership); +} + + int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr) { struct sockaddr_storage addr_st; struct sockaddr_in* addr4; @@ -741,7 +889,7 @@ int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr) return UV_EINVAL; } - if (!(handle->flags & UV_HANDLE_BOUND)) + if (handle->socket == INVALID_SOCKET) return UV_EBADF; if (addr_st.ss_family == AF_INET) { @@ -772,7 +920,7 @@ int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr) int uv_udp_set_broadcast(uv_udp_t* handle, int value) { BOOL optval = (BOOL) value; - if (!(handle->flags & UV_HANDLE_BOUND)) + if (handle->socket == INVALID_SOCKET) return UV_EBADF; if (setsockopt(handle->socket, @@ -787,6 +935,18 @@ int uv_udp_set_broadcast(uv_udp_t* handle, int value) { } +int uv__udp_is_bound(uv_udp_t* handle) { + struct sockaddr_storage addr; + int addrlen; + + addrlen = sizeof(addr); + if (uv_udp_getsockname(handle, (struct sockaddr*) &addr, &addrlen) != 0) + return 0; + + return addrlen > 0; +} + + int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) { WSAPROTOCOL_INFOW protocol_info; int opt_len; @@ -806,7 +966,16 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) { handle, sock, protocol_info.iAddressFamily); - return uv_translate_sys_error(err); + if (err) + return uv_translate_sys_error(err); + + if (uv__udp_is_bound(handle)) + handle->flags |= UV_HANDLE_BOUND; + + if (uv__udp_is_connected(handle)) + handle->flags |= UV_HANDLE_UDP_CONNECTED; + + return 0; } @@ -818,7 +987,7 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) { return UV_EINVAL; \ } \ \ - if (!(handle->flags & UV_HANDLE_BOUND)) \ + if (handle->socket == INVALID_SOCKET) \ return UV_EBADF; \ \ if (!(handle->flags & UV_HANDLE_IPV6)) { \ @@ -883,6 +1052,50 @@ int uv__udp_bind(uv_udp_t* handle, } +int uv__udp_connect(uv_udp_t* handle, + const struct sockaddr* addr, + unsigned int addrlen) { + const struct sockaddr* bind_addr; + int err; + + if (!(handle->flags & UV_HANDLE_BOUND)) { + if (addrlen == sizeof(uv_addr_ip4_any_)) + bind_addr = (const struct sockaddr*) &uv_addr_ip4_any_; + else if (addrlen == sizeof(uv_addr_ip6_any_)) + bind_addr = (const struct sockaddr*) &uv_addr_ip6_any_; + else + return UV_EINVAL; + + err = uv_udp_maybe_bind(handle, bind_addr, addrlen, 0); + if (err) + return uv_translate_sys_error(err); + } + + err = connect(handle->socket, addr, addrlen); + if (err) + return uv_translate_sys_error(WSAGetLastError()); + + handle->flags |= UV_HANDLE_UDP_CONNECTED; + + return 0; +} + + +int uv__udp_disconnect(uv_udp_t* handle) { + int err; + struct sockaddr addr; + + memset(&addr, 0, sizeof(addr)); + + err = connect(handle->socket, &addr, sizeof(addr)); + if (err) + return uv_translate_sys_error(WSAGetLastError()); + + handle->flags &= ~UV_HANDLE_UDP_CONNECTED; + return 0; +} + + /* This function is an egress point, i.e. it returns libuv errors rather than * system errors. */ @@ -897,13 +1110,13 @@ int uv__udp_send(uv_udp_send_t* req, int err; if (!(handle->flags & UV_HANDLE_BOUND)) { - if (addrlen == sizeof(uv_addr_ip4_any_)) { + if (addrlen == sizeof(uv_addr_ip4_any_)) bind_addr = (const struct sockaddr*) &uv_addr_ip4_any_; - } else if (addrlen == sizeof(uv_addr_ip6_any_)) { + else if (addrlen == sizeof(uv_addr_ip6_any_)) bind_addr = (const struct sockaddr*) &uv_addr_ip6_any_; - } else { - abort(); - } + else + return UV_EINVAL; + err = uv_udp_maybe_bind(handle, bind_addr, addrlen, 0); if (err) return uv_translate_sys_error(err); @@ -922,5 +1135,47 @@ int uv__udp_try_send(uv_udp_t* handle, unsigned int nbufs, const struct sockaddr* addr, unsigned int addrlen) { - return UV_ENOSYS; + DWORD bytes; + const struct sockaddr* bind_addr; + struct sockaddr_storage converted; + int err; + + assert(nbufs > 0); + + if (addr != NULL) { + err = uv__convert_to_localhost_if_unspecified(addr, &converted); + if (err) + return err; + } + + /* Already sending a message.*/ + if (handle->send_queue_count != 0) + return UV_EAGAIN; + + if (!(handle->flags & UV_HANDLE_BOUND)) { + if (addrlen == sizeof(uv_addr_ip4_any_)) + bind_addr = (const struct sockaddr*) &uv_addr_ip4_any_; + else if (addrlen == sizeof(uv_addr_ip6_any_)) + bind_addr = (const struct sockaddr*) &uv_addr_ip6_any_; + else + return UV_EINVAL; + err = uv_udp_maybe_bind(handle, bind_addr, addrlen, 0); + if (err) + return uv_translate_sys_error(err); + } + + err = WSASendTo(handle->socket, + (WSABUF*)bufs, + nbufs, + &bytes, + 0, + (const struct sockaddr*) &converted, + addrlen, + NULL, + NULL); + + if (err) + return uv_translate_sys_error(WSAGetLastError()); + + return bytes; } diff --git a/include/libuv/src/win/util.c b/include/libuv/src/win/util.c index 4cebad390..aad8f1a15 100644 --- a/include/libuv/src/win/util.c +++ b/include/libuv/src/win/util.c @@ -30,14 +30,16 @@ #include "uv.h" #include "internal.h" +/* clang-format off */ #include #include #include #include #include #include +/* clang-format on */ #include - +#include /* * Max title length; the only thing MSDN tells us about the maximum length @@ -54,23 +56,27 @@ /* The number of nanoseconds in one second. */ #define UV__NANOSEC 1000000000 +/* Max user name length, from iphlpapi.h */ +#ifndef UNLEN +# define UNLEN 256 +#endif + + +/* A RtlGenRandom() by any other name... */ +extern BOOLEAN NTAPI SystemFunction036(PVOID Buffer, ULONG BufferLength); /* Cached copy of the process title, plus a mutex guarding it. */ static char *process_title; static CRITICAL_SECTION process_title_lock; -/* Cached copy of the process id, written once. */ -static DWORD current_pid = 0; - - -/* Interval (in seconds) of the high-resolution clock. */ -static double hrtime_interval_ = 0; +/* Frequency of the high-resolution clock. */ +static uint64_t hrtime_frequency_ = 0; /* * One-time initialization code for functionality defined in util.c. */ -void uv__util_init() { +void uv__util_init(void) { LARGE_INTEGER perf_frequency; /* Initialize process title access mutex. */ @@ -80,9 +86,9 @@ void uv__util_init() { * and precompute its reciprocal. */ if (QueryPerformanceFrequency(&perf_frequency)) { - hrtime_interval_ = 1.0 / perf_frequency.QuadPart; + hrtime_frequency_ = perf_frequency.QuadPart; } else { - hrtime_interval_= 0; + uv_fatal_error(GetLastError(), "QueryPerformanceFrequency"); } } @@ -134,8 +140,8 @@ int uv_exepath(char* buffer, size_t* size_ptr) { uv__free(utf16_buffer); - /* utf8_len *does* include the terminating null at this point, but the */ - /* returned size shouldn't. */ + /* utf8_len *does* include the terminating null at this point, but the + * returned size shouldn't. */ *size_ptr = utf8_len - 1; return 0; @@ -147,27 +153,33 @@ int uv_exepath(char* buffer, size_t* size_ptr) { int uv_cwd(char* buffer, size_t* size) { DWORD utf16_len; - WCHAR utf16_buffer[MAX_PATH]; + WCHAR *utf16_buffer; int r; if (buffer == NULL || size == NULL) { return UV_EINVAL; } - utf16_len = GetCurrentDirectoryW(MAX_PATH, utf16_buffer); + utf16_len = GetCurrentDirectoryW(0, NULL); if (utf16_len == 0) { return uv_translate_sys_error(GetLastError()); - } else if (utf16_len > MAX_PATH) { - /* This should be impossible; however the CRT has a code path to deal */ - /* with this scenario, so I added a check anyway. */ - return UV_EIO; + } + utf16_buffer = uv__malloc(utf16_len * sizeof(WCHAR)); + if (utf16_buffer == NULL) { + return UV_ENOMEM; + } + + utf16_len = GetCurrentDirectoryW(utf16_len, utf16_buffer); + if (utf16_len == 0) { + uv__free(utf16_buffer); + return uv_translate_sys_error(GetLastError()); } /* utf16_len contains the length, *not* including the terminating null. */ utf16_buffer[utf16_len] = L'\0'; - /* The returned directory should not have a trailing slash, unless it */ - /* points at a drive root, like c:\. Remove it if needed.*/ + /* The returned directory should not have a trailing slash, unless it points + * at a drive root, like c:\. Remove it if needed. */ if (utf16_buffer[utf16_len - 1] == L'\\' && !(utf16_len == 3 && utf16_buffer[1] == L':')) { utf16_len--; @@ -184,8 +196,10 @@ int uv_cwd(char* buffer, size_t* size) { NULL, NULL); if (r == 0) { + uv__free(utf16_buffer); return uv_translate_sys_error(GetLastError()); } else if (r > (int) *size) { + uv__free(utf16_buffer); *size = r; return UV_ENOBUFS; } @@ -199,6 +213,8 @@ int uv_cwd(char* buffer, size_t* size) { *size > INT_MAX ? INT_MAX : (int) *size, NULL, NULL); + uv__free(utf16_buffer); + if (r == 0) { return uv_translate_sys_error(GetLastError()); } @@ -209,47 +225,65 @@ int uv_cwd(char* buffer, size_t* size) { int uv_chdir(const char* dir) { - WCHAR utf16_buffer[MAX_PATH]; - size_t utf16_len; + WCHAR *utf16_buffer; + size_t utf16_len, new_utf16_len; WCHAR drive_letter, env_var[4]; if (dir == NULL) { return UV_EINVAL; } + utf16_len = MultiByteToWideChar(CP_UTF8, + 0, + dir, + -1, + NULL, + 0); + if (utf16_len == 0) { + return uv_translate_sys_error(GetLastError()); + } + utf16_buffer = uv__malloc(utf16_len * sizeof(WCHAR)); + if (utf16_buffer == NULL) { + return UV_ENOMEM; + } + if (MultiByteToWideChar(CP_UTF8, 0, dir, -1, utf16_buffer, - MAX_PATH) == 0) { - DWORD error = GetLastError(); - /* The maximum length of the current working directory is 260 chars, */ - /* including terminating null. If it doesn't fit, the path name must be */ - /* too long. */ - if (error == ERROR_INSUFFICIENT_BUFFER) { - return UV_ENAMETOOLONG; - } else { - return uv_translate_sys_error(error); - } + utf16_len) == 0) { + uv__free(utf16_buffer); + return uv_translate_sys_error(GetLastError()); } if (!SetCurrentDirectoryW(utf16_buffer)) { + uv__free(utf16_buffer); return uv_translate_sys_error(GetLastError()); } - /* Windows stores the drive-local path in an "hidden" environment variable, */ - /* which has the form "=C:=C:\Windows". SetCurrentDirectory does not */ - /* update this, so we'll have to do it. */ - utf16_len = GetCurrentDirectoryW(MAX_PATH, utf16_buffer); + /* Windows stores the drive-local path in an "hidden" environment variable, + * which has the form "=C:=C:\Windows". SetCurrentDirectory does not update + * this, so we'll have to do it. */ + new_utf16_len = GetCurrentDirectoryW(utf16_len, utf16_buffer); + if (new_utf16_len > utf16_len ) { + uv__free(utf16_buffer); + utf16_buffer = uv__malloc(new_utf16_len * sizeof(WCHAR)); + if (utf16_buffer == NULL) { + /* When updating the environment variable fails, return UV_OK anyway. + * We did successfully change current working directory, only updating + * hidden env variable failed. */ + return 0; + } + new_utf16_len = GetCurrentDirectoryW(new_utf16_len, utf16_buffer); + } if (utf16_len == 0) { - return uv_translate_sys_error(GetLastError()); - } else if (utf16_len > MAX_PATH) { - return UV_EIO; + uv__free(utf16_buffer); + return 0; } - /* The returned directory should not have a trailing slash, unless it */ - /* points at a drive root, like c:\. Remove it if needed. */ + /* The returned directory should not have a trailing slash, unless it points + * at a drive root, like c:\. Remove it if needed. */ if (utf16_buffer[utf16_len - 1] == L'\\' && !(utf16_len == 3 && utf16_buffer[1] == L':')) { utf16_len--; @@ -257,8 +291,8 @@ int uv_chdir(const char* dir) { } if (utf16_len < 2 || utf16_buffer[1] != L':') { - /* Doesn't look like a drive letter could be there - probably an UNC */ - /* path. TODO: Need to handle win32 namespaces like \\?\C:\ ? */ + /* Doesn't look like a drive letter could be there - probably an UNC path. + * TODO: Need to handle win32 namespaces like \\?\C:\ ? */ drive_letter = 0; } else if (utf16_buffer[0] >= L'A' && utf16_buffer[0] <= L'Z') { drive_letter = utf16_buffer[0]; @@ -277,11 +311,10 @@ int uv_chdir(const char* dir) { env_var[2] = L':'; env_var[3] = L'\0'; - if (!SetEnvironmentVariableW(env_var, utf16_buffer)) { - return uv_translate_sys_error(GetLastError()); - } + SetEnvironmentVariableW(env_var, utf16_buffer); } + uv__free(utf16_buffer); return 0; } @@ -316,7 +349,17 @@ uint64_t uv_get_total_memory(void) { } -int uv_parent_pid() { +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + +uv_pid_t uv_os_getpid(void) { + return GetCurrentProcessId(); +} + + +uv_pid_t uv_os_getppid(void) { int parent_pid = -1; HANDLE handle; PROCESSENTRY32 pe; @@ -339,16 +382,12 @@ int uv_parent_pid() { } -int uv_current_pid() { - if (current_pid == 0) { - current_pid = GetCurrentProcessId(); - } - return current_pid; +char** uv_setup_args(int argc, char** argv) { + return argv; } -char** uv_setup_args(int argc, char** argv) { - return argv; +void uv__process_title_cleanup(void) { } @@ -401,7 +440,7 @@ int uv_set_process_title(const char* title) { } -static int uv__get_process_title() { +static int uv__get_process_title(void) { WCHAR title_w[MAX_TITLE_LENGTH]; if (!GetConsoleTitleW(title_w, sizeof(title_w) / sizeof(WCHAR))) { @@ -416,6 +455,11 @@ static int uv__get_process_title() { int uv_get_process_title(char* buffer, size_t size) { + size_t len; + + if (buffer == NULL || size == 0) + return UV_EINVAL; + uv__once_init(); EnterCriticalSection(&process_title_lock); @@ -429,7 +473,14 @@ int uv_get_process_title(char* buffer, size_t size) { } assert(process_title); - strncpy(buffer, process_title, size); + len = strlen(process_title) + 1; + + if (size < len) { + LeaveCriticalSection(&process_title_lock); + return UV_ENOBUFS; + } + + memcpy(buffer, process_title, len); LeaveCriticalSection(&process_title_lock); return 0; @@ -441,23 +492,25 @@ uint64_t uv_hrtime(void) { return uv__hrtime(UV__NANOSEC); } -uint64_t uv__hrtime(double scale) { +uint64_t uv__hrtime(unsigned int scale) { LARGE_INTEGER counter; + double scaled_freq; + double result; - /* If the performance interval is zero, there's no support. */ - if (hrtime_interval_ == 0) { - return 0; - } - + assert(hrtime_frequency_ != 0); + assert(scale != 0); if (!QueryPerformanceCounter(&counter)) { - return 0; + uv_fatal_error(GetLastError(), "QueryPerformanceCounter"); } + assert(counter.QuadPart != 0); /* Because we have no guarantee about the order of magnitude of the * performance counter interval, integer math could cause this computation * to overflow. Therefore we resort to floating point math. */ - return (uint64_t) ((double) counter.QuadPart * hrtime_interval_ * scale); + scaled_freq = (double) hrtime_frequency_ / scale; + result = (double) counter.QuadPart / scaled_freq; + return (uint64_t) result; } @@ -555,8 +608,8 @@ int uv_uptime(double* uptime) { BYTE* address = (BYTE*) object_type + object_type->DefinitionLength + counter_definition->CounterOffset; uint64_t value = *((uint64_t*) address); - *uptime = (double) (object_type->PerfTime.QuadPart - value) / - (double) object_type->PerfFreq.QuadPart; + *uptime = floor((double) (object_type->PerfTime.QuadPart - value) / + (double) object_type->PerfFreq.QuadPart); uv__free(malloced_buffer); return 0; } @@ -583,7 +636,7 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos_ptr, int* cpu_count_ptr) { SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION* sppi; DWORD sppi_size; SYSTEM_INFO system_info; - DWORD cpu_count, r, i; + DWORD cpu_count, i; NTSTATUS status; ULONG result_size; int err; @@ -638,39 +691,35 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos_ptr, int* cpu_count_ptr) { assert(len > 0 && len < ARRAY_SIZE(key_name)); - r = RegOpenKeyExW(HKEY_LOCAL_MACHINE, - key_name, - 0, - KEY_QUERY_VALUE, - &processor_key); - if (r != ERROR_SUCCESS) { - err = GetLastError(); - goto error; - } - - if (RegQueryValueExW(processor_key, - L"~MHz", - NULL, - NULL, - (BYTE*) &cpu_speed, - &cpu_speed_size) != ERROR_SUCCESS) { - err = GetLastError(); - RegCloseKey(processor_key); + err = RegOpenKeyExW(HKEY_LOCAL_MACHINE, + key_name, + 0, + KEY_QUERY_VALUE, + &processor_key); + if (err != ERROR_SUCCESS) { goto error; } - if (RegQueryValueExW(processor_key, - L"ProcessorNameString", - NULL, - NULL, - (BYTE*) &cpu_brand, - &cpu_brand_size) != ERROR_SUCCESS) { - err = GetLastError(); + err = RegQueryValueExW(processor_key, + L"~MHz", + NULL, + NULL, + (BYTE*)&cpu_speed, + &cpu_speed_size); + if (err != ERROR_SUCCESS) { RegCloseKey(processor_key); goto error; } + err = RegQueryValueExW(processor_key, + L"ProcessorNameString", + NULL, + NULL, + (BYTE*)&cpu_brand, + &cpu_brand_size); RegCloseKey(processor_key); + if (err != ERROR_SUCCESS) + goto error; cpu_info = &cpu_infos[i]; cpu_info->speed = cpu_speed; @@ -694,9 +743,11 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos_ptr, int* cpu_count_ptr) { return 0; error: - /* This is safe because the cpu_infos array is zeroed on allocation. */ - for (i = 0; i < cpu_count; i++) - uv__free(cpu_infos[i].model); + if (cpu_infos != NULL) { + /* This is safe because the cpu_infos array is zeroed on allocation. */ + for (i = 0; i < cpu_count; i++) + uv__free(cpu_infos[i].model); + } uv__free(cpu_infos); uv__free(sppi); @@ -705,17 +756,6 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos_ptr, int* cpu_count_ptr) { } -void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { - int i; - - for (i = 0; i < count; i++) { - uv__free(cpu_infos[i].model); - } - - uv__free(cpu_infos); -} - - static int is_windows_version_or_greater(DWORD os_major, DWORD os_minor, WORD service_pack_major, @@ -797,6 +837,9 @@ int uv_interface_addresses(uv_interface_address_t** addresses_ptr, int is_vista_or_greater; ULONG flags; + *addresses_ptr = NULL; + *count_ptr = 0; + is_vista_or_greater = is_windows_version_or_greater(6, 0, 0, 0); if (is_vista_or_greater) { flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | @@ -811,17 +854,17 @@ int uv_interface_addresses(uv_interface_address_t** addresses_ptr, } - /* Fetch the size of the adapters reported by windows, and then get the */ - /* list itself. */ + /* Fetch the size of the adapters reported by windows, and then get the list + * itself. */ win_address_buf_size = 0; win_address_buf = NULL; for (;;) { ULONG r; - /* If win_address_buf is 0, then GetAdaptersAddresses will fail with */ - /* ERROR_BUFFER_OVERFLOW, and the required buffer size will be stored in */ - /* win_address_buf_size. */ + /* If win_address_buf is 0, then GetAdaptersAddresses will fail with. + * ERROR_BUFFER_OVERFLOW, and the required buffer size will be stored in + * win_address_buf_size. */ r = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, @@ -835,8 +878,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses_ptr, switch (r) { case ERROR_BUFFER_OVERFLOW: - /* This happens when win_address_buf is NULL or too small to hold */ - /* all adapters. */ + /* This happens when win_address_buf is NULL or too small to hold all + * adapters. */ win_address_buf = uv__malloc(win_address_buf_size); if (win_address_buf == NULL) return UV_ENOMEM; @@ -870,15 +913,15 @@ int uv_interface_addresses(uv_interface_address_t** addresses_ptr, return UV_ENOBUFS; default: - /* Other (unspecified) errors can happen, but we don't have any */ - /* special meaning for them. */ + /* Other (unspecified) errors can happen, but we don't have any special + * meaning for them. */ assert(r != ERROR_SUCCESS); return uv_translate_sys_error(r); } } - /* Count the number of enabled interfaces and compute how much space is */ - /* needed to store their info. */ + /* Count the number of enabled interfaces and compute how much space is + * needed to store their info. */ count = 0; uv_address_buf_size = 0; @@ -888,9 +931,9 @@ int uv_interface_addresses(uv_interface_address_t** addresses_ptr, IP_ADAPTER_UNICAST_ADDRESS* unicast_address; int name_size; - /* Interfaces that are not 'up' should not be reported. Also skip */ - /* interfaces that have no associated unicast address, as to avoid */ - /* allocating space for the name for this interface. */ + /* Interfaces that are not 'up' should not be reported. Also skip + * interfaces that have no associated unicast address, as to avoid + * allocating space for the name for this interface. */ if (adapter->OperStatus != IfOperStatusUp || adapter->FirstUnicastAddress == NULL) continue; @@ -910,8 +953,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses_ptr, } uv_address_buf_size += name_size; - /* Count the number of addresses associated with this interface, and */ - /* compute the size. */ + /* Count the number of addresses associated with this interface, and + * compute the size. */ for (unicast_address = (IP_ADAPTER_UNICAST_ADDRESS*) adapter->FirstUnicastAddress; unicast_address != NULL; @@ -928,8 +971,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses_ptr, return UV_ENOMEM; } - /* Compute the start of the uv_interface_address_t array, and the place in */ - /* the buffer where the interface names will be stored. */ + /* Compute the start of the uv_interface_address_t array, and the place in + * the buffer where the interface names will be stored. */ uv_address = uv_address_buf; name_buf = (char*) (uv_address_buf + count); @@ -1062,6 +1105,7 @@ int uv_getrusage(uv_rusage_t *uv_rusage) { FILETIME createTime, exitTime, kernelTime, userTime; SYSTEMTIME kernelSystemTime, userSystemTime; PROCESS_MEMORY_COUNTERS memCounters; + IO_COUNTERS ioCounters; int ret; ret = GetProcessTimes(GetCurrentProcess(), &createTime, &exitTime, &kernelTime, &userTime); @@ -1086,6 +1130,11 @@ int uv_getrusage(uv_rusage_t *uv_rusage) { return uv_translate_sys_error(GetLastError()); } + ret = GetProcessIoCounters(GetCurrentProcess(), &ioCounters); + if (ret == 0) { + return uv_translate_sys_error(GetLastError()); + } + memset(uv_rusage, 0, sizeof(*uv_rusage)); uv_rusage->ru_utime.tv_sec = userSystemTime.wHour * 3600 + @@ -1101,59 +1150,26 @@ int uv_getrusage(uv_rusage_t *uv_rusage) { uv_rusage->ru_majflt = (uint64_t) memCounters.PageFaultCount; uv_rusage->ru_maxrss = (uint64_t) memCounters.PeakWorkingSetSize / 1024; + uv_rusage->ru_oublock = (uint64_t) ioCounters.WriteOperationCount; + uv_rusage->ru_inblock = (uint64_t) ioCounters.ReadOperationCount; + return 0; } int uv_os_homedir(char* buffer, size_t* size) { uv_passwd_t pwd; - wchar_t path[MAX_PATH]; - DWORD bufsize; size_t len; int r; - if (buffer == NULL || size == NULL || *size == 0) - return UV_EINVAL; - - /* Check if the USERPROFILE environment variable is set first */ - len = GetEnvironmentVariableW(L"USERPROFILE", path, MAX_PATH); - - if (len == 0) { - r = GetLastError(); - - /* Don't return an error if USERPROFILE was not found */ - if (r != ERROR_ENVVAR_NOT_FOUND) - return uv_translate_sys_error(r); - } else if (len > MAX_PATH) { - /* This should not be possible */ - return UV_EIO; - } else { - /* Check how much space we need */ - bufsize = WideCharToMultiByte(CP_UTF8, 0, path, -1, NULL, 0, NULL, NULL); - - if (bufsize == 0) { - return uv_translate_sys_error(GetLastError()); - } else if (bufsize > *size) { - *size = bufsize; - return UV_ENOBUFS; - } - - /* Convert to UTF-8 */ - bufsize = WideCharToMultiByte(CP_UTF8, - 0, - path, - -1, - buffer, - *size, - NULL, - NULL); - - if (bufsize == 0) - return uv_translate_sys_error(GetLastError()); + /* Check if the USERPROFILE environment variable is set first. The task of + performing input validation on buffer and size is taken care of by + uv_os_getenv(). */ + r = uv_os_getenv("USERPROFILE", buffer, size); - *size = bufsize - 1; - return 0; - } + /* Don't return an error if USERPROFILE was not found. */ + if (r != UV_ENOENT) + return r; /* USERPROFILE is not set, so call uv__getpwuid_r() */ r = uv__getpwuid_r(&pwd); @@ -1179,24 +1195,33 @@ int uv_os_homedir(char* buffer, size_t* size) { int uv_os_tmpdir(char* buffer, size_t* size) { - wchar_t path[MAX_PATH + 1]; + wchar_t *path; DWORD bufsize; size_t len; if (buffer == NULL || size == NULL || *size == 0) return UV_EINVAL; - len = GetTempPathW(MAX_PATH + 1, path); + len = 0; + len = GetTempPathW(0, NULL); + if (len == 0) { + return uv_translate_sys_error(GetLastError()); + } + /* Include space for terminating null char. */ + len += 1; + path = uv__malloc(len * sizeof(wchar_t)); + if (path == NULL) { + return UV_ENOMEM; + } + len = GetTempPathW(len, path); if (len == 0) { + uv__free(path); return uv_translate_sys_error(GetLastError()); - } else if (len > MAX_PATH + 1) { - /* This should not be possible */ - return UV_EIO; } - /* The returned directory should not have a trailing slash, unless it */ - /* points at a drive root, like c:\. Remove it if needed.*/ + /* The returned directory should not have a trailing slash, unless it points + * at a drive root, like c:\. Remove it if needed. */ if (path[len - 1] == L'\\' && !(len == 3 && path[1] == L':')) { len--; @@ -1207,8 +1232,10 @@ int uv_os_tmpdir(char* buffer, size_t* size) { bufsize = WideCharToMultiByte(CP_UTF8, 0, path, -1, NULL, 0, NULL, NULL); if (bufsize == 0) { + uv__free(path); return uv_translate_sys_error(GetLastError()); } else if (bufsize > *size) { + uv__free(path); *size = bufsize; return UV_ENOBUFS; } @@ -1222,6 +1249,7 @@ int uv_os_tmpdir(char* buffer, size_t* size) { *size, NULL, NULL); + uv__free(path); if (bufsize == 0) return uv_translate_sys_error(GetLastError()); @@ -1297,10 +1325,51 @@ int uv__convert_utf16_to_utf8(const WCHAR* utf16, int utf16len, char** utf8) { } +/* + * Converts a UTF-8 string into a UTF-16 one. The resulting string is + * null-terminated. + * + * If utf8 is null terminated, utf8len can be set to -1, otherwise it must + * be specified. + */ +int uv__convert_utf8_to_utf16(const char* utf8, int utf8len, WCHAR** utf16) { + int bufsize; + + if (utf8 == NULL) + return UV_EINVAL; + + /* Check how much space we need */ + bufsize = MultiByteToWideChar(CP_UTF8, 0, utf8, utf8len, NULL, 0); + + if (bufsize == 0) + return uv_translate_sys_error(GetLastError()); + + /* Allocate the destination buffer adding an extra byte for the terminating + * NULL. If utf8len is not -1 MultiByteToWideChar will not add it, so + * we do it ourselves always, just in case. */ + *utf16 = uv__malloc(sizeof(WCHAR) * (bufsize + 1)); + + if (*utf16 == NULL) + return UV_ENOMEM; + + /* Convert to UTF-16 */ + bufsize = MultiByteToWideChar(CP_UTF8, 0, utf8, utf8len, *utf16, bufsize); + + if (bufsize == 0) { + uv__free(*utf16); + *utf16 = NULL; + return uv_translate_sys_error(GetLastError()); + } + + (*utf16)[bufsize] = L'\0'; + return 0; +} + + int uv__getpwuid_r(uv_passwd_t* pwd) { HANDLE token; wchar_t username[UNLEN + 1]; - wchar_t path[MAX_PATH]; + wchar_t *path; DWORD bufsize; int r; @@ -1311,24 +1380,34 @@ int uv__getpwuid_r(uv_passwd_t* pwd) { if (OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &token) == 0) return uv_translate_sys_error(GetLastError()); - bufsize = sizeof(path); - if (!GetUserProfileDirectoryW(token, path, &bufsize)) { + bufsize = 0; + GetUserProfileDirectoryW(token, NULL, &bufsize); + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { r = GetLastError(); CloseHandle(token); + return uv_translate_sys_error(r); + } - /* This should not be possible */ - if (r == ERROR_INSUFFICIENT_BUFFER) - return UV_ENOMEM; + path = uv__malloc(bufsize * sizeof(wchar_t)); + if (path == NULL) { + CloseHandle(token); + return UV_ENOMEM; + } + if (!GetUserProfileDirectoryW(token, path, &bufsize)) { + r = GetLastError(); + CloseHandle(token); + uv__free(path); return uv_translate_sys_error(r); } CloseHandle(token); /* Get the username using GetUserNameW() */ - bufsize = sizeof(username); + bufsize = ARRAY_SIZE(username); if (!GetUserNameW(username, &bufsize)) { r = GetLastError(); + uv__free(path); /* This should not be possible */ if (r == ERROR_INSUFFICIENT_BUFFER) @@ -1339,6 +1418,7 @@ int uv__getpwuid_r(uv_passwd_t* pwd) { pwd->homedir = NULL; r = uv__convert_utf16_to_utf8(path, -1, &pwd->homedir); + uv__free(path); if (r != 0) return r; @@ -1362,3 +1442,536 @@ int uv__getpwuid_r(uv_passwd_t* pwd) { int uv_os_get_passwd(uv_passwd_t* pwd) { return uv__getpwuid_r(pwd); } + + +int uv_os_environ(uv_env_item_t** envitems, int* count) { + wchar_t* env; + wchar_t* penv; + int i, cnt; + uv_env_item_t* envitem; + + *envitems = NULL; + *count = 0; + + env = GetEnvironmentStringsW(); + if (env == NULL) + return 0; + + for (penv = env, i = 0; *penv != L'\0'; penv += wcslen(penv) + 1, i++); + + *envitems = uv__calloc(i, sizeof(**envitems)); + if (*envitems == NULL) { + FreeEnvironmentStringsW(env); + return UV_ENOMEM; + } + + penv = env; + cnt = 0; + + while (*penv != L'\0' && cnt < i) { + char* buf; + char* ptr; + + if (uv__convert_utf16_to_utf8(penv, -1, &buf) != 0) + goto fail; + + /* Using buf + 1 here because we know that `buf` has length at least 1, + * and some special environment variables on Windows start with a = sign. */ + ptr = strchr(buf + 1, '='); + if (ptr == NULL) { + uv__free(buf); + goto do_continue; + } + + *ptr = '\0'; + + envitem = &(*envitems)[cnt]; + envitem->name = buf; + envitem->value = ptr + 1; + + cnt++; + + do_continue: + penv += wcslen(penv) + 1; + } + + FreeEnvironmentStringsW(env); + + *count = cnt; + return 0; + +fail: + FreeEnvironmentStringsW(env); + + for (i = 0; i < cnt; i++) { + envitem = &(*envitems)[cnt]; + uv__free(envitem->name); + } + uv__free(*envitems); + + *envitems = NULL; + *count = 0; + return UV_ENOMEM; +} + + +int uv_os_getenv(const char* name, char* buffer, size_t* size) { + wchar_t fastvar[512]; + wchar_t* var; + DWORD varlen; + wchar_t* name_w; + DWORD bufsize; + size_t len; + int r; + + if (name == NULL || buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + + r = uv__convert_utf8_to_utf16(name, -1, &name_w); + + if (r != 0) + return r; + + var = fastvar; + varlen = ARRAY_SIZE(fastvar); + + for (;;) { + SetLastError(ERROR_SUCCESS); + len = GetEnvironmentVariableW(name_w, var, varlen); + + if (len < varlen) + break; + + /* Try repeatedly because we might have been preempted by another thread + * modifying the environment variable just as we're trying to read it. + */ + if (var != fastvar) + uv__free(var); + + varlen = 1 + len; + var = uv__malloc(varlen * sizeof(*var)); + + if (var == NULL) { + r = UV_ENOMEM; + goto fail; + } + } + + uv__free(name_w); + name_w = NULL; + + if (len == 0) { + r = GetLastError(); + if (r != ERROR_SUCCESS) { + r = uv_translate_sys_error(r); + goto fail; + } + } + + /* Check how much space we need */ + bufsize = WideCharToMultiByte(CP_UTF8, 0, var, -1, NULL, 0, NULL, NULL); + + if (bufsize == 0) { + r = uv_translate_sys_error(GetLastError()); + goto fail; + } else if (bufsize > *size) { + *size = bufsize; + r = UV_ENOBUFS; + goto fail; + } + + /* Convert to UTF-8 */ + bufsize = WideCharToMultiByte(CP_UTF8, + 0, + var, + -1, + buffer, + *size, + NULL, + NULL); + + if (bufsize == 0) { + r = uv_translate_sys_error(GetLastError()); + goto fail; + } + + *size = bufsize - 1; + r = 0; + +fail: + + if (name_w != NULL) + uv__free(name_w); + + if (var != fastvar) + uv__free(var); + + return r; +} + + +int uv_os_setenv(const char* name, const char* value) { + wchar_t* name_w; + wchar_t* value_w; + int r; + + if (name == NULL || value == NULL) + return UV_EINVAL; + + r = uv__convert_utf8_to_utf16(name, -1, &name_w); + + if (r != 0) + return r; + + r = uv__convert_utf8_to_utf16(value, -1, &value_w); + + if (r != 0) { + uv__free(name_w); + return r; + } + + r = SetEnvironmentVariableW(name_w, value_w); + uv__free(name_w); + uv__free(value_w); + + if (r == 0) + return uv_translate_sys_error(GetLastError()); + + return 0; +} + + +int uv_os_unsetenv(const char* name) { + wchar_t* name_w; + int r; + + if (name == NULL) + return UV_EINVAL; + + r = uv__convert_utf8_to_utf16(name, -1, &name_w); + + if (r != 0) + return r; + + r = SetEnvironmentVariableW(name_w, NULL); + uv__free(name_w); + + if (r == 0) + return uv_translate_sys_error(GetLastError()); + + return 0; +} + + +int uv_os_gethostname(char* buffer, size_t* size) { + char buf[UV_MAXHOSTNAMESIZE]; + size_t len; + + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + + uv__once_init(); /* Initialize winsock */ + + if (gethostname(buf, sizeof(buf)) != 0) + return uv_translate_sys_error(WSAGetLastError()); + + buf[sizeof(buf) - 1] = '\0'; /* Null terminate, just to be safe. */ + len = strlen(buf); + + if (len >= *size) { + *size = len + 1; + return UV_ENOBUFS; + } + + memcpy(buffer, buf, len + 1); + *size = len; + return 0; +} + + +static int uv__get_handle(uv_pid_t pid, int access, HANDLE* handle) { + int r; + + if (pid == 0) + *handle = GetCurrentProcess(); + else + *handle = OpenProcess(access, FALSE, pid); + + if (*handle == NULL) { + r = GetLastError(); + + if (r == ERROR_INVALID_PARAMETER) + return UV_ESRCH; + else + return uv_translate_sys_error(r); + } + + return 0; +} + + +int uv_os_getpriority(uv_pid_t pid, int* priority) { + HANDLE handle; + int r; + + if (priority == NULL) + return UV_EINVAL; + + r = uv__get_handle(pid, PROCESS_QUERY_LIMITED_INFORMATION, &handle); + + if (r != 0) + return r; + + r = GetPriorityClass(handle); + + if (r == 0) { + r = uv_translate_sys_error(GetLastError()); + } else { + /* Map Windows priority classes to Unix nice values. */ + if (r == REALTIME_PRIORITY_CLASS) + *priority = UV_PRIORITY_HIGHEST; + else if (r == HIGH_PRIORITY_CLASS) + *priority = UV_PRIORITY_HIGH; + else if (r == ABOVE_NORMAL_PRIORITY_CLASS) + *priority = UV_PRIORITY_ABOVE_NORMAL; + else if (r == NORMAL_PRIORITY_CLASS) + *priority = UV_PRIORITY_NORMAL; + else if (r == BELOW_NORMAL_PRIORITY_CLASS) + *priority = UV_PRIORITY_BELOW_NORMAL; + else /* IDLE_PRIORITY_CLASS */ + *priority = UV_PRIORITY_LOW; + + r = 0; + } + + CloseHandle(handle); + return r; +} + + +int uv_os_setpriority(uv_pid_t pid, int priority) { + HANDLE handle; + int priority_class; + int r; + + /* Map Unix nice values to Windows priority classes. */ + if (priority < UV_PRIORITY_HIGHEST || priority > UV_PRIORITY_LOW) + return UV_EINVAL; + else if (priority < UV_PRIORITY_HIGH) + priority_class = REALTIME_PRIORITY_CLASS; + else if (priority < UV_PRIORITY_ABOVE_NORMAL) + priority_class = HIGH_PRIORITY_CLASS; + else if (priority < UV_PRIORITY_NORMAL) + priority_class = ABOVE_NORMAL_PRIORITY_CLASS; + else if (priority < UV_PRIORITY_BELOW_NORMAL) + priority_class = NORMAL_PRIORITY_CLASS; + else if (priority < UV_PRIORITY_LOW) + priority_class = BELOW_NORMAL_PRIORITY_CLASS; + else + priority_class = IDLE_PRIORITY_CLASS; + + r = uv__get_handle(pid, PROCESS_SET_INFORMATION, &handle); + + if (r != 0) + return r; + + if (SetPriorityClass(handle, priority_class) == 0) + r = uv_translate_sys_error(GetLastError()); + + CloseHandle(handle); + return r; +} + + +int uv_os_uname(uv_utsname_t* buffer) { + /* Implementation loosely based on + https://github.com/gagern/gnulib/blob/master/lib/uname.c */ + OSVERSIONINFOW os_info; + SYSTEM_INFO system_info; + HKEY registry_key; + WCHAR product_name_w[256]; + DWORD product_name_w_size; + int version_size; + int processor_level; + int r; + + if (buffer == NULL) + return UV_EINVAL; + + uv__once_init(); + os_info.dwOSVersionInfoSize = sizeof(os_info); + os_info.szCSDVersion[0] = L'\0'; + + /* Try calling RtlGetVersion(), and fall back to the deprecated GetVersionEx() + if RtlGetVersion() is not available. */ + if (pRtlGetVersion) { + pRtlGetVersion(&os_info); + } else { + /* Silence GetVersionEx() deprecation warning. */ + #ifdef _MSC_VER + #pragma warning(suppress : 4996) + #endif + if (GetVersionExW(&os_info) == 0) { + r = uv_translate_sys_error(GetLastError()); + goto error; + } + } + + /* Populate the version field. */ + version_size = 0; + r = RegOpenKeyExW(HKEY_LOCAL_MACHINE, + L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", + 0, + KEY_QUERY_VALUE, + ®istry_key); + + if (r == ERROR_SUCCESS) { + product_name_w_size = sizeof(product_name_w); + r = RegGetValueW(registry_key, + NULL, + L"ProductName", + RRF_RT_REG_SZ, + NULL, + (PVOID) product_name_w, + &product_name_w_size); + RegCloseKey(registry_key); + + if (r == ERROR_SUCCESS) { + version_size = WideCharToMultiByte(CP_UTF8, + 0, + product_name_w, + -1, + buffer->version, + sizeof(buffer->version), + NULL, + NULL); + if (version_size == 0) { + r = uv_translate_sys_error(GetLastError()); + goto error; + } + } + } + + /* Append service pack information to the version if present. */ + if (os_info.szCSDVersion[0] != L'\0') { + if (version_size > 0) + buffer->version[version_size - 1] = ' '; + + if (WideCharToMultiByte(CP_UTF8, + 0, + os_info.szCSDVersion, + -1, + buffer->version + version_size, + sizeof(buffer->version) - version_size, + NULL, + NULL) == 0) { + r = uv_translate_sys_error(GetLastError()); + goto error; + } + } + + /* Populate the sysname field. */ +#ifdef __MINGW32__ + r = snprintf(buffer->sysname, + sizeof(buffer->sysname), + "MINGW32_NT-%u.%u", + (unsigned int) os_info.dwMajorVersion, + (unsigned int) os_info.dwMinorVersion); + assert((size_t)r < sizeof(buffer->sysname)); +#else + uv__strscpy(buffer->sysname, "Windows_NT", sizeof(buffer->sysname)); +#endif + + /* Populate the release field. */ + r = snprintf(buffer->release, + sizeof(buffer->release), + "%d.%d.%d", + (unsigned int) os_info.dwMajorVersion, + (unsigned int) os_info.dwMinorVersion, + (unsigned int) os_info.dwBuildNumber); + assert((size_t)r < sizeof(buffer->release)); + + /* Populate the machine field. */ + GetSystemInfo(&system_info); + + switch (system_info.wProcessorArchitecture) { + case PROCESSOR_ARCHITECTURE_AMD64: + uv__strscpy(buffer->machine, "x86_64", sizeof(buffer->machine)); + break; + case PROCESSOR_ARCHITECTURE_IA64: + uv__strscpy(buffer->machine, "ia64", sizeof(buffer->machine)); + break; + case PROCESSOR_ARCHITECTURE_INTEL: + uv__strscpy(buffer->machine, "i386", sizeof(buffer->machine)); + + if (system_info.wProcessorLevel > 3) { + processor_level = system_info.wProcessorLevel < 6 ? + system_info.wProcessorLevel : 6; + buffer->machine[1] = '0' + processor_level; + } + + break; + case PROCESSOR_ARCHITECTURE_IA32_ON_WIN64: + uv__strscpy(buffer->machine, "i686", sizeof(buffer->machine)); + break; + case PROCESSOR_ARCHITECTURE_MIPS: + uv__strscpy(buffer->machine, "mips", sizeof(buffer->machine)); + break; + case PROCESSOR_ARCHITECTURE_ALPHA: + case PROCESSOR_ARCHITECTURE_ALPHA64: + uv__strscpy(buffer->machine, "alpha", sizeof(buffer->machine)); + break; + case PROCESSOR_ARCHITECTURE_PPC: + uv__strscpy(buffer->machine, "powerpc", sizeof(buffer->machine)); + break; + case PROCESSOR_ARCHITECTURE_SHX: + uv__strscpy(buffer->machine, "sh", sizeof(buffer->machine)); + break; + case PROCESSOR_ARCHITECTURE_ARM: + uv__strscpy(buffer->machine, "arm", sizeof(buffer->machine)); + break; + default: + uv__strscpy(buffer->machine, "unknown", sizeof(buffer->machine)); + break; + } + + return 0; + +error: + buffer->sysname[0] = '\0'; + buffer->release[0] = '\0'; + buffer->version[0] = '\0'; + buffer->machine[0] = '\0'; + return r; +} + +int uv_gettimeofday(uv_timeval64_t* tv) { + /* Based on https://doxygen.postgresql.org/gettimeofday_8c_source.html */ + const uint64_t epoch = (uint64_t) 116444736000000000ULL; + FILETIME file_time; + ULARGE_INTEGER ularge; + + if (tv == NULL) + return UV_EINVAL; + + GetSystemTimeAsFileTime(&file_time); + ularge.LowPart = file_time.dwLowDateTime; + ularge.HighPart = file_time.dwHighDateTime; + tv->tv_sec = (int64_t) ((ularge.QuadPart - epoch) / 10000000L); + tv->tv_usec = (int32_t) (((ularge.QuadPart - epoch) % 10000000L) / 10); + return 0; +} + +int uv__random_rtlgenrandom(void* buf, size_t buflen) { + if (buflen == 0) + return 0; + + if (SystemFunction036(buf, buflen) == FALSE) + return UV_EIO; + + return 0; +} + +void uv_sleep(unsigned int msec) { + Sleep(msec); +} diff --git a/include/libuv/src/win/winapi.c b/include/libuv/src/win/winapi.c index 26bd06486..bb86ec8ce 100644 --- a/include/libuv/src/win/winapi.c +++ b/include/libuv/src/win/winapi.c @@ -26,6 +26,7 @@ /* Ntdll function pointers */ +sRtlGetVersion pRtlGetVersion; sRtlNtStatusToDosError pRtlNtStatusToDosError; sNtDeviceIoControlFile pNtDeviceIoControlFile; sNtQueryInformationFile pNtQueryInformationFile; @@ -33,24 +34,22 @@ sNtSetInformationFile pNtSetInformationFile; sNtQueryVolumeInformationFile pNtQueryVolumeInformationFile; sNtQueryDirectoryFile pNtQueryDirectoryFile; sNtQuerySystemInformation pNtQuerySystemInformation; - +sNtQueryInformationProcess pNtQueryInformationProcess; /* Kernel32 function pointers */ sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx; -sSetFileCompletionNotificationModes pSetFileCompletionNotificationModes; -sCreateSymbolicLinkW pCreateSymbolicLinkW; -sCancelIoEx pCancelIoEx; -sInitializeConditionVariable pInitializeConditionVariable; -sSleepConditionVariableCS pSleepConditionVariableCS; -sSleepConditionVariableSRW pSleepConditionVariableSRW; -sWakeAllConditionVariable pWakeAllConditionVariable; -sWakeConditionVariable pWakeConditionVariable; -sCancelSynchronousIo pCancelSynchronousIo; -sGetFinalPathNameByHandleW pGetFinalPathNameByHandleW; - - -void uv_winapi_init() { + +/* Powrprof.dll function pointer */ +sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotification; + +/* User32.dll function pointer */ +sSetWinEventHook pSetWinEventHook; + + +void uv_winapi_init(void) { HMODULE ntdll_module; + HMODULE powrprof_module; + HMODULE user32_module; HMODULE kernel32_module; ntdll_module = GetModuleHandleA("ntdll.dll"); @@ -58,6 +57,9 @@ void uv_winapi_init() { uv_fatal_error(GetLastError(), "GetModuleHandleA"); } + pRtlGetVersion = (sRtlGetVersion) GetProcAddress(ntdll_module, + "RtlGetVersion"); + pRtlNtStatusToDosError = (sRtlNtStatusToDosError) GetProcAddress( ntdll_module, "RtlNtStatusToDosError"); @@ -105,6 +107,13 @@ void uv_winapi_init() { uv_fatal_error(GetLastError(), "GetProcAddress"); } + pNtQueryInformationProcess = (sNtQueryInformationProcess) GetProcAddress( + ntdll_module, + "NtQueryInformationProcess"); + if (pNtQueryInformationProcess == NULL) { + uv_fatal_error(GetLastError(), "GetProcAddress"); + } + kernel32_module = GetModuleHandleA("kernel32.dll"); if (kernel32_module == NULL) { uv_fatal_error(GetLastError(), "GetModuleHandleA"); @@ -114,33 +123,15 @@ void uv_winapi_init() { kernel32_module, "GetQueuedCompletionStatusEx"); - pSetFileCompletionNotificationModes = (sSetFileCompletionNotificationModes) - GetProcAddress(kernel32_module, "SetFileCompletionNotificationModes"); - - pCreateSymbolicLinkW = (sCreateSymbolicLinkW) - GetProcAddress(kernel32_module, "CreateSymbolicLinkW"); - - pCancelIoEx = (sCancelIoEx) - GetProcAddress(kernel32_module, "CancelIoEx"); - - pInitializeConditionVariable = (sInitializeConditionVariable) - GetProcAddress(kernel32_module, "InitializeConditionVariable"); - - pSleepConditionVariableCS = (sSleepConditionVariableCS) - GetProcAddress(kernel32_module, "SleepConditionVariableCS"); - - pSleepConditionVariableSRW = (sSleepConditionVariableSRW) - GetProcAddress(kernel32_module, "SleepConditionVariableSRW"); - - pWakeAllConditionVariable = (sWakeAllConditionVariable) - GetProcAddress(kernel32_module, "WakeAllConditionVariable"); - - pWakeConditionVariable = (sWakeConditionVariable) - GetProcAddress(kernel32_module, "WakeConditionVariable"); - - pCancelSynchronousIo = (sCancelSynchronousIo) - GetProcAddress(kernel32_module, "CancelSynchronousIo"); + powrprof_module = LoadLibraryA("powrprof.dll"); + if (powrprof_module != NULL) { + pPowerRegisterSuspendResumeNotification = (sPowerRegisterSuspendResumeNotification) + GetProcAddress(powrprof_module, "PowerRegisterSuspendResumeNotification"); + } - pGetFinalPathNameByHandleW = (sGetFinalPathNameByHandleW) - GetProcAddress(kernel32_module, "GetFinalPathNameByHandleW"); + user32_module = LoadLibraryA("user32.dll"); + if (user32_module != NULL) { + pSetWinEventHook = (sSetWinEventHook) + GetProcAddress(user32_module, "SetWinEventHook"); + } } diff --git a/include/libuv/src/win/winapi.h b/include/libuv/src/win/winapi.h index 122198a6d..0b66b5634 100644 --- a/include/libuv/src/win/winapi.h +++ b/include/libuv/src/win/winapi.h @@ -4076,8 +4076,8 @@ # define STATUS_HASH_NOT_PRESENT ((NTSTATUS) 0xC000A101L) #endif -/* This is not the NTSTATUS_FROM_WIN32 that the DDK provides, because the */ -/* DDK got it wrong! */ +/* This is not the NTSTATUS_FROM_WIN32 that the DDK provides, because the DDK + * got it wrong! */ #ifdef NTSTATUS_FROM_WIN32 # undef NTSTATUS_FROM_WIN32 #endif @@ -4104,7 +4104,14 @@ # define JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE 0x00002000 #endif +#ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE +# define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE 0x00000002 +#endif + /* from winternl.h */ +#if !defined(__UNICODE_STRING_DEFINED) && defined(__MINGW32__) +#define __UNICODE_STRING_DEFINED +#endif typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; @@ -4145,7 +4152,11 @@ typedef const UNICODE_STRING *PCUNICODE_STRING; struct { UCHAR DataBuffer[1]; } GenericReparseBuffer; - } DUMMYUNIONNAME; + struct { + ULONG StringCount; + WCHAR StringList[1]; + } AppExecLinkReparseBuffer; + }; } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER; #endif @@ -4153,7 +4164,7 @@ typedef struct _IO_STATUS_BLOCK { union { NTSTATUS Status; PVOID Pointer; - } DUMMYUNIONNAME; + }; ULONG_PTR Information; } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; @@ -4429,6 +4440,10 @@ typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION { # define SystemProcessorPerformanceInformation 8 #endif +#ifndef ProcessConsoleHostProcess +# define ProcessConsoleHostProcess 49 +#endif + #ifndef FILE_DEVICE_FILE_SYSTEM # define FILE_DEVICE_FILE_SYSTEM 0x00000009 #endif @@ -4506,12 +4521,18 @@ typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION { #ifndef IO_REPARSE_TAG_SYMLINK # define IO_REPARSE_TAG_SYMLINK (0xA000000CL) #endif +#ifndef IO_REPARSE_TAG_APPEXECLINK +# define IO_REPARSE_TAG_APPEXECLINK (0x8000001BL) +#endif typedef VOID (NTAPI *PIO_APC_ROUTINE) (PVOID ApcContext, PIO_STATUS_BLOCK IoStatusBlock, ULONG Reserved); +typedef NTSTATUS (NTAPI *sRtlGetVersion) + (PRTL_OSVERSIONINFOW lpVersionInformation); + typedef ULONG (NTAPI *sRtlNtStatusToDosError) (NTSTATUS Status); @@ -4568,6 +4589,13 @@ typedef NTSTATUS (NTAPI *sNtQueryDirectoryFile) BOOLEAN RestartScan ); +typedef NTSTATUS (NTAPI *sNtQueryInformationProcess) + (HANDLE ProcessHandle, + UINT ProcessInformationClass, + PVOID ProcessInformation, + ULONG Length, + PULONG ReturnLength); + /* * Kernel32 headers */ @@ -4606,6 +4634,10 @@ typedef NTSTATUS (NTAPI *sNtQueryDirectoryFile) #endif /* from winerror.h */ +#ifndef ERROR_ELEVATION_REQUIRED +# define ERROR_ELEVATION_REQUIRED 740 +#endif + #ifndef ERROR_SYMLINK_NOT_SUPPORTED # define ERROR_SYMLINK_NOT_SUPPORTED 1464 #endif @@ -4642,49 +4674,73 @@ typedef BOOL (WINAPI *sGetQueuedCompletionStatusEx) DWORD dwMilliseconds, BOOL fAlertable); -typedef BOOL (WINAPI* sSetFileCompletionNotificationModes) - (HANDLE FileHandle, - UCHAR Flags); +/* from powerbase.h */ +#ifndef DEVICE_NOTIFY_CALLBACK +# define DEVICE_NOTIFY_CALLBACK 2 +#endif -typedef BOOLEAN (WINAPI* sCreateSymbolicLinkW) - (LPCWSTR lpSymlinkFileName, - LPCWSTR lpTargetFileName, - DWORD dwFlags); +#ifndef PBT_APMRESUMEAUTOMATIC +# define PBT_APMRESUMEAUTOMATIC 18 +#endif -typedef BOOL (WINAPI* sCancelIoEx) - (HANDLE hFile, - LPOVERLAPPED lpOverlapped); +#ifndef PBT_APMRESUMESUSPEND +# define PBT_APMRESUMESUSPEND 7 +#endif -typedef VOID (WINAPI* sInitializeConditionVariable) - (PCONDITION_VARIABLE ConditionVariable); +typedef ULONG CALLBACK _DEVICE_NOTIFY_CALLBACK_ROUTINE( + PVOID Context, + ULONG Type, + PVOID Setting +); +typedef _DEVICE_NOTIFY_CALLBACK_ROUTINE* _PDEVICE_NOTIFY_CALLBACK_ROUTINE; -typedef BOOL (WINAPI* sSleepConditionVariableCS) - (PCONDITION_VARIABLE ConditionVariable, - PCRITICAL_SECTION CriticalSection, - DWORD dwMilliseconds); +typedef struct _DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS { + _PDEVICE_NOTIFY_CALLBACK_ROUTINE Callback; + PVOID Context; +} _DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS, *_PDEVICE_NOTIFY_SUBSCRIBE_PARAMETERS; -typedef BOOL (WINAPI* sSleepConditionVariableSRW) - (PCONDITION_VARIABLE ConditionVariable, - PSRWLOCK SRWLock, - DWORD dwMilliseconds, - ULONG Flags); +typedef PVOID _HPOWERNOTIFY; +typedef _HPOWERNOTIFY *_PHPOWERNOTIFY; -typedef VOID (WINAPI* sWakeAllConditionVariable) - (PCONDITION_VARIABLE ConditionVariable); +typedef DWORD (WINAPI *sPowerRegisterSuspendResumeNotification) + (DWORD Flags, + HANDLE Recipient, + _PHPOWERNOTIFY RegistrationHandle); -typedef VOID (WINAPI* sWakeConditionVariable) - (PCONDITION_VARIABLE ConditionVariable); +/* from Winuser.h */ +typedef VOID (CALLBACK* WINEVENTPROC) + (HWINEVENTHOOK hWinEventHook, + DWORD event, + HWND hwnd, + LONG idObject, + LONG idChild, + DWORD idEventThread, + DWORD dwmsEventTime); -typedef BOOL (WINAPI* sCancelSynchronousIo) - (HANDLE hThread); +typedef HWINEVENTHOOK (WINAPI *sSetWinEventHook) + (UINT eventMin, + UINT eventMax, + HMODULE hmodWinEventProc, + WINEVENTPROC lpfnWinEventProc, + DWORD idProcess, + DWORD idThread, + UINT dwflags); -typedef DWORD (WINAPI* sGetFinalPathNameByHandleW) - (HANDLE hFile, - LPWSTR lpszFilePath, - DWORD cchFilePath, - DWORD dwFlags); +/* From mstcpip.h */ +typedef struct _TCP_INITIAL_RTO_PARAMETERS { + USHORT Rtt; + UCHAR MaxSynRetransmissions; +} TCP_INITIAL_RTO_PARAMETERS, *PTCP_INITIAL_RTO_PARAMETERS; + +#ifndef TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS +# define TCP_INITIAL_RTO_NO_SYN_RETRANSMISSIONS ((UCHAR) -2) +#endif +#ifndef SIO_TCP_INITIAL_RTO +# define SIO_TCP_INITIAL_RTO _WSAIOW(IOC_VENDOR,17) +#endif /* Ntdll function pointers */ +extern sRtlGetVersion pRtlGetVersion; extern sRtlNtStatusToDosError pRtlNtStatusToDosError; extern sNtDeviceIoControlFile pNtDeviceIoControlFile; extern sNtQueryInformationFile pNtQueryInformationFile; @@ -4692,19 +4748,15 @@ extern sNtSetInformationFile pNtSetInformationFile; extern sNtQueryVolumeInformationFile pNtQueryVolumeInformationFile; extern sNtQueryDirectoryFile pNtQueryDirectoryFile; extern sNtQuerySystemInformation pNtQuerySystemInformation; - +extern sNtQueryInformationProcess pNtQueryInformationProcess; /* Kernel32 function pointers */ extern sGetQueuedCompletionStatusEx pGetQueuedCompletionStatusEx; -extern sSetFileCompletionNotificationModes pSetFileCompletionNotificationModes; -extern sCreateSymbolicLinkW pCreateSymbolicLinkW; -extern sCancelIoEx pCancelIoEx; -extern sInitializeConditionVariable pInitializeConditionVariable; -extern sSleepConditionVariableCS pSleepConditionVariableCS; -extern sSleepConditionVariableSRW pSleepConditionVariableSRW; -extern sWakeAllConditionVariable pWakeAllConditionVariable; -extern sWakeConditionVariable pWakeConditionVariable; -extern sCancelSynchronousIo pCancelSynchronousIo; -extern sGetFinalPathNameByHandleW pGetFinalPathNameByHandleW; + +/* Powrprof.dll function pointer */ +extern sPowerRegisterSuspendResumeNotification pPowerRegisterSuspendResumeNotification; + +/* User32.dll function pointer */ +extern sSetWinEventHook pSetWinEventHook; #endif /* UV_WIN_WINAPI_H_ */ diff --git a/include/libuv/src/win/winsock.c b/include/libuv/src/win/winsock.c index d2e667e9f..4cf6e6b04 100644 --- a/include/libuv/src/win/winsock.c +++ b/include/libuv/src/win/winsock.c @@ -74,25 +74,14 @@ BOOL uv_get_connectex_function(SOCKET socket, LPFN_CONNECTEX* target) { } -static int error_means_no_support(DWORD error) { - return error == WSAEPROTONOSUPPORT || error == WSAESOCKTNOSUPPORT || - error == WSAEPFNOSUPPORT || error == WSAEAFNOSUPPORT; -} - -void uv_winsock_init() { +void uv_winsock_init(void) { WSADATA wsa_data; int errorno; SOCKET dummy; WSAPROTOCOL_INFOW protocol_info; int opt_len; - /* Initialize winsock */ - errorno = WSAStartup(MAKEWORD(2, 2), &wsa_data); - if (errorno != 0) { - uv_fatal_error(errorno, "WSAStartup"); - } - /* Set implicit binding address used by connectEx */ if (uv_ip4_addr("0.0.0.0", 0, &uv_addr_ip4_any_)) { abort(); @@ -102,50 +91,45 @@ void uv_winsock_init() { abort(); } - /* Detect non-IFS LSPs */ - dummy = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); + /* Skip initialization in safe mode without network support */ + if (1 == GetSystemMetrics(SM_CLEANBOOT)) return; + /* Initialize winsock */ + errorno = WSAStartup(MAKEWORD(2, 2), &wsa_data); + if (errorno != 0) { + uv_fatal_error(errorno, "WSAStartup"); + } + + /* Try to detect non-IFS LSPs */ + uv_tcp_non_ifs_lsp_ipv4 = 1; + dummy = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); if (dummy != INVALID_SOCKET) { opt_len = (int) sizeof protocol_info; if (getsockopt(dummy, SOL_SOCKET, SO_PROTOCOL_INFOW, (char*) &protocol_info, - &opt_len) == SOCKET_ERROR) - uv_fatal_error(WSAGetLastError(), "getsockopt"); - - if (!(protocol_info.dwServiceFlags1 & XP1_IFS_HANDLES)) - uv_tcp_non_ifs_lsp_ipv4 = 1; - - if (closesocket(dummy) == SOCKET_ERROR) - uv_fatal_error(WSAGetLastError(), "closesocket"); - - } else if (!error_means_no_support(WSAGetLastError())) { - /* Any error other than "socket type not supported" is fatal. */ - uv_fatal_error(WSAGetLastError(), "socket"); + &opt_len) == 0) { + if (protocol_info.dwServiceFlags1 & XP1_IFS_HANDLES) + uv_tcp_non_ifs_lsp_ipv4 = 0; + } + closesocket(dummy); } - /* Detect IPV6 support and non-IFS LSPs */ + /* Try to detect IPV6 support and non-IFS LSPs */ + uv_tcp_non_ifs_lsp_ipv6 = 1; dummy = socket(AF_INET6, SOCK_STREAM, IPPROTO_IP); - if (dummy != INVALID_SOCKET) { opt_len = (int) sizeof protocol_info; if (getsockopt(dummy, SOL_SOCKET, SO_PROTOCOL_INFOW, (char*) &protocol_info, - &opt_len) == SOCKET_ERROR) - uv_fatal_error(WSAGetLastError(), "getsockopt"); - - if (!(protocol_info.dwServiceFlags1 & XP1_IFS_HANDLES)) - uv_tcp_non_ifs_lsp_ipv6 = 1; - - if (closesocket(dummy) == SOCKET_ERROR) - uv_fatal_error(WSAGetLastError(), "closesocket"); - - } else if (!error_means_no_support(WSAGetLastError())) { - /* Any error other than "socket type not supported" is fatal. */ - uv_fatal_error(WSAGetLastError(), "socket"); + &opt_len) == 0) { + if (protocol_info.dwServiceFlags1 & XP1_IFS_HANDLES) + uv_tcp_non_ifs_lsp_ipv6 = 0; + } + closesocket(dummy); } } @@ -256,8 +240,8 @@ int uv_ntstatus_to_winsock_error(NTSTATUS status) { default: if ((status & (FACILITY_NTWIN32 << 16)) == (FACILITY_NTWIN32 << 16) && (status & (ERROR_SEVERITY_ERROR | ERROR_SEVERITY_WARNING))) { - /* It's a windows error that has been previously mapped to an */ - /* ntstatus code. */ + /* It's a windows error that has been previously mapped to an ntstatus + * code. */ return (DWORD) (status & 0xffff); } else { /* The default fallback for unmappable ntstatus codes. */ @@ -519,8 +503,8 @@ int WSAAPI uv_msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in, sizeof *info_out); if (overlapped == NULL) { - /* If this is a blocking operation, wait for the event to become */ - /* signaled, and then grab the real status from the io status block. */ + /* If this is a blocking operation, wait for the event to become signaled, + * and then grab the real status from the io status block. */ if (status == STATUS_PENDING) { DWORD r = WaitForSingleObject(event, INFINITE); @@ -559,3 +543,33 @@ int WSAAPI uv_msafd_poll(SOCKET socket, AFD_POLL_INFO* info_in, return SOCKET_ERROR; } } + +int uv__convert_to_localhost_if_unspecified(const struct sockaddr* addr, + struct sockaddr_storage* storage) { + struct sockaddr_in* dest4; + struct sockaddr_in6* dest6; + + if (addr == NULL) + return UV_EINVAL; + + switch (addr->sa_family) { + case AF_INET: + dest4 = (struct sockaddr_in*) storage; + memcpy(dest4, addr, sizeof(*dest4)); + if (dest4->sin_addr.s_addr == 0) + dest4->sin_addr.s_addr = htonl(INADDR_LOOPBACK); + return 0; + case AF_INET6: + dest6 = (struct sockaddr_in6*) storage; + memcpy(dest6, addr, sizeof(*dest6)); + if (memcmp(&dest6->sin6_addr, + &uv_addr_ip6_any_.sin6_addr, + sizeof(uv_addr_ip6_any_.sin6_addr)) == 0) { + struct in6_addr init_sin6_addr = IN6ADDR_LOOPBACK_INIT; + dest6->sin6_addr = init_sin6_addr; + } + return 0; + default: + return UV_EINVAL; + } +} diff --git a/include/libuv/src/win/winsock.h b/include/libuv/src/win/winsock.h index 7c007ab49..2af958870 100644 --- a/include/libuv/src/win/winsock.h +++ b/include/libuv/src/win/winsock.h @@ -54,6 +54,14 @@ # define SIO_BASE_HANDLE 0x48000022 #endif +#ifndef MCAST_JOIN_SOURCE_GROUP +# define MCAST_JOIN_SOURCE_GROUP 45 +#endif + +#ifndef MCAST_LEAVE_SOURCE_GROUP +# define MCAST_LEAVE_SOURCE_GROUP 46 +#endif + /* * TDI defines that are only in the DDK. * We only need receive flags so far. @@ -187,4 +195,7 @@ typedef struct _IP_ADAPTER_UNICAST_ADDRESS_LH { #endif +int uv__convert_to_localhost_if_unspecified(const struct sockaddr* addr, + struct sockaddr_storage* storage); + #endif /* UV_WIN_WINSOCK_H_ */ From 0195486652674686ad5d81980ae60b1fbd543e0f Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Tue, 17 Nov 2020 17:45:47 +0300 Subject: [PATCH 005/117] cleanup --- libs/uv/uv.c | 1 - 1 file changed, 1 deletion(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 3a19dac84..c26487f00 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -284,7 +284,6 @@ HL_PRIM void HL_NAME(timer_again_wrap)(uv_timer_t *t) { } DEFINE_PRIM(_VOID, timer_again_wrap, _HANDLE); -// TODO: Requires libuv 1.40 HL_PRIM int HL_NAME(timer_get_due_in_wrap)(uv_timer_t *t) { return (int)uv_timer_get_due_in(t); //TODO: change to uint64_t } From 92f4442a8a9894f4afa82661eb9b6f653b5d3640 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 23 Jul 2021 10:37:55 +0300 Subject: [PATCH 006/117] wip --- libs/uv/uv.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index c26487f00..a2fd72b6f 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -39,8 +39,8 @@ typedef struct { // Errors -// static int code_uv2hx( int code ) { -// switch(code) { +// static int errno_uv2hx( int uv_errno ) { +// switch(uv_errno) { // case 0: return 0; break; // case UV_E2BIG: return 1; break; // case UV_EACCES: return 2; break; @@ -124,6 +124,20 @@ typedef struct { // } // } +// static vdynamic * (*new_UVException)(int); + +// HL_PRIM void HL_NAME(exception_init)(vclosure *fn_exception) { +// printf("dfsfsdsfd\n"); +// // new_UVException = (vdynamic * (*)(int))fn_exception->fun; +// } + +// DEFINE_PRIM(_VOID, exception_init, _FUN(_DYN, _I32)); + +// static void hx_error(int uv_errno) { +// hl_throw(new_UVException(errno_uv2hx(uv_errno))); +// // hl_error("%s", hl_to_utf16(uv_err_name(uv_errno))); +// } + // HANDLE static events_data *init_hl_data( uv_handle_t *h ) { @@ -245,7 +259,11 @@ DEFINE_PRIM(_BOOL, stream_listen, _HANDLE _I32 _CALLB); // Timer HL_PRIM uv_timer_t *HL_NAME(timer_init_wrap)( uv_loop_t *loop ) { uv_timer_t *t = UV_ALLOC(uv_timer_t); - if( uv_timer_init(loop,t) < 0) { + int result = uv_timer_init(loop,t); + // const uchar *err = hl_to_utf16(uv_err_name(UV_EACCES)); + // hl_error("%s", err); + // hx_error(UV_ENOENT); + if(result < 0) { free(t); //TODO: throw error return NULL; From 1ecc397b313f48ac4915dfc627593ff1307060ab Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 23 Jul 2021 17:06:42 +0300 Subject: [PATCH 007/117] Handle --- libs/uv/uv.c | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index a2fd72b6f..c423cfc9f 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -124,19 +124,10 @@ typedef struct { // } // } -// static vdynamic * (*new_UVException)(int); - -// HL_PRIM void HL_NAME(exception_init)(vclosure *fn_exception) { -// printf("dfsfsdsfd\n"); -// // new_UVException = (vdynamic * (*)(int))fn_exception->fun; -// } - -// DEFINE_PRIM(_VOID, exception_init, _FUN(_DYN, _I32)); - -// static void hx_error(int uv_errno) { -// hl_throw(new_UVException(errno_uv2hx(uv_errno))); -// // hl_error("%s", hl_to_utf16(uv_err_name(uv_errno))); -// } +static void hx_error(int uv_errno) { + //TODO: throw hl.uv.UVException + hl_error("%s", hl_to_utf16(uv_err_name(uv_errno))); +} // HANDLE @@ -186,6 +177,12 @@ HL_PRIM void HL_NAME(close_handle)( uv_handle_t *h, vclosure *c ) { DEFINE_PRIM(_VOID, close_handle, _HANDLE _CALLB); +DEFINE_PRIM(_I32, is_active, _HANDLE); +DEFINE_PRIM(_I32, is_closing, _HANDLE); +DEFINE_PRIM(_VOID, ref, _HANDLE); +DEFINE_PRIM(_VOID, unref, _HANDLE); +DEFINE_PRIM(_I32, has_ref, _HANDLE); + // STREAM static void on_write( uv_write_t *wr, int status ) { @@ -260,12 +257,9 @@ DEFINE_PRIM(_BOOL, stream_listen, _HANDLE _I32 _CALLB); HL_PRIM uv_timer_t *HL_NAME(timer_init_wrap)( uv_loop_t *loop ) { uv_timer_t *t = UV_ALLOC(uv_timer_t); int result = uv_timer_init(loop,t); - // const uchar *err = hl_to_utf16(uv_err_name(UV_EACCES)); - // hl_error("%s", err); - // hx_error(UV_ENOENT); if(result < 0) { free(t); - //TODO: throw error + hx_error(result); return NULL; } init_hl_data((uv_handle_t*)t); @@ -280,24 +274,27 @@ static void on_timer_tick( uv_timer_t *t ) { // TODO: change `timeout` and `repeat` to uint64 HL_PRIM void HL_NAME(timer_start_wrap)(uv_timer_t *t, vclosure *c, int timeout, int repeat) { register_callb((uv_handle_t*)t,c,EVT_TIMER_TICK); - if(uv_timer_start(t,on_timer_tick, (uint64_t)timeout, (uint64_t)repeat) < 0) { + int result = uv_timer_start(t,on_timer_tick, (uint64_t)timeout, (uint64_t)repeat); + if(result < 0) { clear_callb((uv_handle_t*)t, EVT_TIMER_TICK); - //TODO: throw error + hx_error(result); } } DEFINE_PRIM(_VOID, timer_start_wrap, _HANDLE _FUN(_VOID,_NO_ARG) _I32 _I32); HL_PRIM void HL_NAME(timer_stop_wrap)(uv_timer_t *t) { clear_callb((uv_handle_t*)t, EVT_TIMER_TICK); - if(uv_timer_stop(t) < 0) { - //TODO: throw error + int result = uv_timer_stop(t); + if(result < 0) { + hx_error(result); } } DEFINE_PRIM(_VOID, timer_stop_wrap, _HANDLE); HL_PRIM void HL_NAME(timer_again_wrap)(uv_timer_t *t) { - if(uv_timer_again(t) < 0) { - //TODO: throw error + int result = uv_timer_again(t); + if(result < 0) { + hx_error(result); } } DEFINE_PRIM(_VOID, timer_again_wrap, _HANDLE); From 876913759e6782cb2ff26c7f9cd6ed7c34350997 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 23 Jul 2021 17:31:30 +0300 Subject: [PATCH 008/117] proper Null Access exceptions --- libs/uv/uv.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index c423cfc9f..6e9cb8e81 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -36,6 +36,11 @@ typedef struct { #define _HANDLE _ABSTRACT(uv_handle) #define _CALLB _FUN(_VOID,_NO_ARG) #define UV_ALLOC(t) ((t*)malloc(sizeof(t))) +#define UV_CHECK_NULL(handle,fail_return) \ + if( !handle ) { \ + hl_null_access(); \ + return fail_return; \ + } // Errors @@ -171,17 +176,47 @@ static void free_handle( void *h ) { } HL_PRIM void HL_NAME(close_handle)( uv_handle_t *h, vclosure *c ) { + UV_CHECK_NULL(h,); register_callb(h, c, EVT_CLOSE); free_handle(h); } DEFINE_PRIM(_VOID, close_handle, _HANDLE _CALLB); -DEFINE_PRIM(_I32, is_active, _HANDLE); -DEFINE_PRIM(_I32, is_closing, _HANDLE); -DEFINE_PRIM(_VOID, ref, _HANDLE); -DEFINE_PRIM(_VOID, unref, _HANDLE); -DEFINE_PRIM(_I32, has_ref, _HANDLE); +HL_PRIM bool HL_NAME(is_active_wrap)( uv_handle_t *h ) { + UV_CHECK_NULL(h,false); + return uv_is_active(h) != 0; +} + +DEFINE_PRIM(_BOOL, is_active_wrap, _HANDLE); + +HL_PRIM bool HL_NAME(is_closing_wrap)( uv_handle_t *h ) { + UV_CHECK_NULL(h,false); + return uv_is_closing(h) != 0; +} + +DEFINE_PRIM(_BOOL, is_closing_wrap, _HANDLE); + +HL_PRIM bool HL_NAME(has_ref_wrap)( uv_handle_t *h ) { + UV_CHECK_NULL(h,false); + return uv_has_ref(h) != 0; +} + +DEFINE_PRIM(_BOOL, has_ref_wrap, _HANDLE); + +HL_PRIM void HL_NAME(ref_wrap)( uv_handle_t *h ) { + UV_CHECK_NULL(h,); + uv_ref(h); +} + +DEFINE_PRIM(_VOID, ref_wrap, _HANDLE); + +HL_PRIM void HL_NAME(unref_wrap)( uv_handle_t *h ) { + UV_CHECK_NULL(h,); + uv_unref(h); +} + +DEFINE_PRIM(_VOID, unref_wrap, _HANDLE); // STREAM @@ -255,6 +290,7 @@ DEFINE_PRIM(_BOOL, stream_listen, _HANDLE _I32 _CALLB); // Timer HL_PRIM uv_timer_t *HL_NAME(timer_init_wrap)( uv_loop_t *loop ) { + UV_CHECK_NULL(loop,NULL); uv_timer_t *t = UV_ALLOC(uv_timer_t); int result = uv_timer_init(loop,t); if(result < 0) { @@ -273,9 +309,11 @@ static void on_timer_tick( uv_timer_t *t ) { // TODO: change `timeout` and `repeat` to uint64 HL_PRIM void HL_NAME(timer_start_wrap)(uv_timer_t *t, vclosure *c, int timeout, int repeat) { + UV_CHECK_NULL(t,); + UV_CHECK_NULL(c,); register_callb((uv_handle_t*)t,c,EVT_TIMER_TICK); int result = uv_timer_start(t,on_timer_tick, (uint64_t)timeout, (uint64_t)repeat); - if(result < 0) { + if( result < 0 ) { clear_callb((uv_handle_t*)t, EVT_TIMER_TICK); hx_error(result); } @@ -283,6 +321,7 @@ HL_PRIM void HL_NAME(timer_start_wrap)(uv_timer_t *t, vclosure *c, int timeout, DEFINE_PRIM(_VOID, timer_start_wrap, _HANDLE _FUN(_VOID,_NO_ARG) _I32 _I32); HL_PRIM void HL_NAME(timer_stop_wrap)(uv_timer_t *t) { + UV_CHECK_NULL(t,); clear_callb((uv_handle_t*)t, EVT_TIMER_TICK); int result = uv_timer_stop(t); if(result < 0) { @@ -292,6 +331,7 @@ HL_PRIM void HL_NAME(timer_stop_wrap)(uv_timer_t *t) { DEFINE_PRIM(_VOID, timer_stop_wrap, _HANDLE); HL_PRIM void HL_NAME(timer_again_wrap)(uv_timer_t *t) { + UV_CHECK_NULL(t,); int result = uv_timer_again(t); if(result < 0) { hx_error(result); @@ -300,17 +340,20 @@ HL_PRIM void HL_NAME(timer_again_wrap)(uv_timer_t *t) { DEFINE_PRIM(_VOID, timer_again_wrap, _HANDLE); HL_PRIM int HL_NAME(timer_get_due_in_wrap)(uv_timer_t *t) { + UV_CHECK_NULL(t,0); return (int)uv_timer_get_due_in(t); //TODO: change to uint64_t } DEFINE_PRIM(_I32, timer_get_due_in_wrap, _HANDLE); HL_PRIM int HL_NAME(timer_get_repeat_wrap)(uv_timer_t *t) { + UV_CHECK_NULL(t,0); return (int)uv_timer_get_repeat(t); //TODO: change to uint64_t } DEFINE_PRIM(_I32, timer_get_repeat_wrap, _HANDLE); //TODO: change `value` to uint64_t HL_PRIM int HL_NAME(timer_set_repeat_wrap)(uv_timer_t *t, int value) { + UV_CHECK_NULL(t,0); uv_timer_set_repeat(t, (uint64_t)value); return value; } From faaa8d51335e18323a55b1d5a6d30da12114b7c4 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 23 Jul 2021 18:39:56 +0300 Subject: [PATCH 009/117] UV_CHECK_ERROR --- libs/uv/uv.c | 40 +++++++++++++++------------------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 6e9cb8e81..9646d4b4a 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -42,6 +42,14 @@ typedef struct { return fail_return; \ } +#define UV_CHECK_ERROR(action,cleanup,fail_return) \ + int __result__ = action; \ + if(__result__ < 0) { \ + cleanup; \ + hx_error(__result__); \ + return fail_return; \ + } + // Errors // static int errno_uv2hx( int uv_errno ) { @@ -180,42 +188,36 @@ HL_PRIM void HL_NAME(close_handle)( uv_handle_t *h, vclosure *c ) { register_callb(h, c, EVT_CLOSE); free_handle(h); } - DEFINE_PRIM(_VOID, close_handle, _HANDLE _CALLB); HL_PRIM bool HL_NAME(is_active_wrap)( uv_handle_t *h ) { UV_CHECK_NULL(h,false); return uv_is_active(h) != 0; } - DEFINE_PRIM(_BOOL, is_active_wrap, _HANDLE); HL_PRIM bool HL_NAME(is_closing_wrap)( uv_handle_t *h ) { UV_CHECK_NULL(h,false); return uv_is_closing(h) != 0; } - DEFINE_PRIM(_BOOL, is_closing_wrap, _HANDLE); HL_PRIM bool HL_NAME(has_ref_wrap)( uv_handle_t *h ) { UV_CHECK_NULL(h,false); return uv_has_ref(h) != 0; } - DEFINE_PRIM(_BOOL, has_ref_wrap, _HANDLE); HL_PRIM void HL_NAME(ref_wrap)( uv_handle_t *h ) { UV_CHECK_NULL(h,); uv_ref(h); } - DEFINE_PRIM(_VOID, ref_wrap, _HANDLE); HL_PRIM void HL_NAME(unref_wrap)( uv_handle_t *h ) { UV_CHECK_NULL(h,); uv_unref(h); } - DEFINE_PRIM(_VOID, unref_wrap, _HANDLE); // STREAM @@ -292,12 +294,7 @@ DEFINE_PRIM(_BOOL, stream_listen, _HANDLE _I32 _CALLB); HL_PRIM uv_timer_t *HL_NAME(timer_init_wrap)( uv_loop_t *loop ) { UV_CHECK_NULL(loop,NULL); uv_timer_t *t = UV_ALLOC(uv_timer_t); - int result = uv_timer_init(loop,t); - if(result < 0) { - free(t); - hx_error(result); - return NULL; - } + UV_CHECK_ERROR(uv_timer_init(loop,t),free(t),NULL); init_hl_data((uv_handle_t*)t); return t; } @@ -312,30 +309,23 @@ HL_PRIM void HL_NAME(timer_start_wrap)(uv_timer_t *t, vclosure *c, int timeout, UV_CHECK_NULL(t,); UV_CHECK_NULL(c,); register_callb((uv_handle_t*)t,c,EVT_TIMER_TICK); - int result = uv_timer_start(t,on_timer_tick, (uint64_t)timeout, (uint64_t)repeat); - if( result < 0 ) { - clear_callb((uv_handle_t*)t, EVT_TIMER_TICK); - hx_error(result); - } + UV_CHECK_ERROR( + uv_timer_start(t,on_timer_tick, (uint64_t)timeout, (uint64_t)repeat), + clear_callb((uv_handle_t*)t, EVT_TIMER_TICK), + ); } DEFINE_PRIM(_VOID, timer_start_wrap, _HANDLE _FUN(_VOID,_NO_ARG) _I32 _I32); HL_PRIM void HL_NAME(timer_stop_wrap)(uv_timer_t *t) { UV_CHECK_NULL(t,); clear_callb((uv_handle_t*)t, EVT_TIMER_TICK); - int result = uv_timer_stop(t); - if(result < 0) { - hx_error(result); - } + UV_CHECK_ERROR(uv_timer_stop(t),,); } DEFINE_PRIM(_VOID, timer_stop_wrap, _HANDLE); HL_PRIM void HL_NAME(timer_again_wrap)(uv_timer_t *t) { UV_CHECK_NULL(t,); - int result = uv_timer_again(t); - if(result < 0) { - hx_error(result); - } + UV_CHECK_ERROR(uv_timer_again(t),,); } DEFINE_PRIM(_VOID, timer_again_wrap, _HANDLE); From 28f8b8f919169041ee5f7e56131a53a074cfc518 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 23 Jul 2021 21:41:53 +0300 Subject: [PATCH 010/117] Async --- libs/uv/uv.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 9646d4b4a..716fe59ed 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -349,6 +349,33 @@ HL_PRIM int HL_NAME(timer_set_repeat_wrap)(uv_timer_t *t, int value) { } DEFINE_PRIM(_I32, timer_set_repeat_wrap, _HANDLE _I32); +// Async + +static void on_async( uv_async_t *a ) { + events_data *ev = UV_DATA(a); + vclosure *c = ev ? ev->events[0] : NULL; + if( !c ) + hl_fatal("No callback in async handle"); + hl_call1(void, c, uv_async_t *, a); +} + +HL_PRIM uv_async_t *HL_NAME(async_init_wrap)( uv_loop_t *loop, vclosure *c ) { + UV_CHECK_NULL(loop,NULL); + UV_CHECK_NULL(c,NULL); + uv_async_t *a = UV_ALLOC(uv_async_t); + UV_CHECK_ERROR(uv_async_init(loop,a,on_async),free(a),NULL); + init_hl_data((uv_handle_t*)a); + register_callb((uv_handle_t*)a,c,0); + return a; +} +DEFINE_PRIM(_HANDLE, async_init_wrap, _LOOP _FUN(_VOID,_HANDLE)); + +HL_PRIM void HL_NAME(async_send_wrap)( uv_async_t *a ) { + UV_CHECK_NULL(a,); + UV_CHECK_ERROR(uv_async_send(a),,); +} +DEFINE_PRIM(_VOID, async_send_wrap, _HANDLE); + // TCP #define _TCP _HANDLE From 866ad06024c8083e592ffdd91d96568cf7b95fba Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Sat, 24 Jul 2021 10:05:13 +0300 Subject: [PATCH 011/117] idle --- libs/uv/uv.c | 54 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 716fe59ed..367b31386 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -21,8 +21,6 @@ typedef struct sockaddr uv_sockaddr; #define EVT_WRITE 0 // write_t #define EVT_CONNECT 0 // connect_t -#define EVT_TIMER_TICK 0 // timer - #define EVT_MAX 2 typedef struct { @@ -153,7 +151,8 @@ static events_data *init_hl_data( uv_handle_t *h ) { } static void register_callb( uv_handle_t *h, vclosure *c, int event_kind ) { - if( !h || !h->data ) return; + if( !h || !h->data ) + hl_fatal("Missing handle or handle data"); UV_DATA(h)->events[event_kind] = c; } @@ -300,25 +299,29 @@ HL_PRIM uv_timer_t *HL_NAME(timer_init_wrap)( uv_loop_t *loop ) { } DEFINE_PRIM(_HANDLE, timer_init_wrap, _LOOP); -static void on_timer_tick( uv_timer_t *t ) { - trigger_callb((uv_handle_t*)t, EVT_TIMER_TICK, NULL, 0, true); +static void on_timer( uv_timer_t *h ) { + events_data *ev = UV_DATA(h); + vclosure *c = ev ? ev->events[0] : NULL; + if( !c ) + hl_fatal("No callback in timer handle"); + hl_call0(void, c); } // TODO: change `timeout` and `repeat` to uint64 HL_PRIM void HL_NAME(timer_start_wrap)(uv_timer_t *t, vclosure *c, int timeout, int repeat) { UV_CHECK_NULL(t,); UV_CHECK_NULL(c,); - register_callb((uv_handle_t*)t,c,EVT_TIMER_TICK); + register_callb((uv_handle_t*)t,c,0); UV_CHECK_ERROR( - uv_timer_start(t,on_timer_tick, (uint64_t)timeout, (uint64_t)repeat), - clear_callb((uv_handle_t*)t, EVT_TIMER_TICK), + uv_timer_start(t,on_timer, (uint64_t)timeout, (uint64_t)repeat), + clear_callb((uv_handle_t*)t,0), ); } DEFINE_PRIM(_VOID, timer_start_wrap, _HANDLE _FUN(_VOID,_NO_ARG) _I32 _I32); HL_PRIM void HL_NAME(timer_stop_wrap)(uv_timer_t *t) { UV_CHECK_NULL(t,); - clear_callb((uv_handle_t*)t, EVT_TIMER_TICK); + clear_callb((uv_handle_t*)t,0); UV_CHECK_ERROR(uv_timer_stop(t),,); } DEFINE_PRIM(_VOID, timer_stop_wrap, _HANDLE); @@ -376,6 +379,39 @@ HL_PRIM void HL_NAME(async_send_wrap)( uv_async_t *a ) { } DEFINE_PRIM(_VOID, async_send_wrap, _HANDLE); +// Idle + +static void on_idle( uv_idle_t *h ) { + events_data *ev = UV_DATA(h); + vclosure *c = ev ? ev->events[0] : NULL; + if( !c ) + hl_fatal("No callback in idle handle"); + hl_call0(void, c); +} + +HL_PRIM uv_idle_t *HL_NAME(idle_init_wrap)( uv_loop_t *loop ) { + UV_CHECK_NULL(loop,NULL); + uv_idle_t *h = UV_ALLOC(uv_idle_t); + UV_CHECK_ERROR(uv_idle_init(loop,h),free(h),NULL); + init_hl_data((uv_handle_t*)h); + return h; +} +DEFINE_PRIM(_HANDLE, idle_init_wrap, _LOOP); + +HL_PRIM void HL_NAME(idle_start_wrap)( uv_idle_t *h, vclosure *c ) { + UV_CHECK_NULL(h,); + UV_CHECK_NULL(c,); + register_callb((uv_handle_t*)h,c,0); + uv_idle_start(h, on_idle); +} +DEFINE_PRIM(_VOID, idle_start_wrap, _HANDLE _FUN(_VOID,_NO_ARG)); + +HL_PRIM void HL_NAME(idle_stop_wrap)( uv_idle_t *h ) { + UV_CHECK_NULL(h,); + uv_idle_stop(h); +} +DEFINE_PRIM(_VOID, idle_stop_wrap, _HANDLE); + // TCP #define _TCP _HANDLE From e7dd89b45e43ef2de4d501090d600f39d6085897 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Sat, 24 Jul 2021 12:09:58 +0300 Subject: [PATCH 012/117] prepare --- libs/uv/uv.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 367b31386..23db2cf04 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -412,6 +412,39 @@ HL_PRIM void HL_NAME(idle_stop_wrap)( uv_idle_t *h ) { } DEFINE_PRIM(_VOID, idle_stop_wrap, _HANDLE); +// Prepare + +static void on_prepare( uv_prepare_t *h ) { + events_data *ev = UV_DATA(h); + vclosure *c = ev ? ev->events[0] : NULL; + if( !c ) + hl_fatal("No callback in prepare handle"); + hl_call0(void, c); +} + +HL_PRIM uv_prepare_t *HL_NAME(prepare_init_wrap)( uv_loop_t *loop ) { + UV_CHECK_NULL(loop,NULL); + uv_prepare_t *h = UV_ALLOC(uv_prepare_t); + UV_CHECK_ERROR(uv_prepare_init(loop,h),free(h),NULL); + init_hl_data((uv_handle_t*)h); + return h; +} +DEFINE_PRIM(_HANDLE, prepare_init_wrap, _LOOP); + +HL_PRIM void HL_NAME(prepare_start_wrap)( uv_prepare_t *h, vclosure *c ) { + UV_CHECK_NULL(h,); + UV_CHECK_NULL(c,); + register_callb((uv_handle_t*)h,c,0); + uv_prepare_start(h, on_prepare); +} +DEFINE_PRIM(_VOID, prepare_start_wrap, _HANDLE _FUN(_VOID,_NO_ARG)); + +HL_PRIM void HL_NAME(prepare_stop_wrap)( uv_prepare_t *h ) { + UV_CHECK_NULL(h,); + uv_prepare_stop(h); +} +DEFINE_PRIM(_VOID, prepare_stop_wrap, _HANDLE); + // TCP #define _TCP _HANDLE From 4ce89169d13a61cfa7e88052de7dbeec74b18934 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Sat, 24 Jul 2021 12:18:58 +0300 Subject: [PATCH 013/117] check --- libs/uv/uv.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 23db2cf04..b6cb08cb5 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -445,6 +445,39 @@ HL_PRIM void HL_NAME(prepare_stop_wrap)( uv_prepare_t *h ) { } DEFINE_PRIM(_VOID, prepare_stop_wrap, _HANDLE); +// Check + +static void on_check( uv_check_t *h ) { + events_data *ev = UV_DATA(h); + vclosure *c = ev ? ev->events[0] : NULL; + if( !c ) + hl_fatal("No callback in check handle"); + hl_call0(void, c); +} + +HL_PRIM uv_check_t *HL_NAME(check_init_wrap)( uv_loop_t *loop ) { + UV_CHECK_NULL(loop,NULL); + uv_check_t *h = UV_ALLOC(uv_check_t); + UV_CHECK_ERROR(uv_check_init(loop,h),free(h),NULL); + init_hl_data((uv_handle_t*)h); + return h; +} +DEFINE_PRIM(_HANDLE, check_init_wrap, _LOOP); + +HL_PRIM void HL_NAME(check_start_wrap)( uv_check_t *h, vclosure *c ) { + UV_CHECK_NULL(h,); + UV_CHECK_NULL(c,); + register_callb((uv_handle_t*)h,c,0); + uv_check_start(h, on_check); +} +DEFINE_PRIM(_VOID, check_start_wrap, _HANDLE _FUN(_VOID,_NO_ARG)); + +HL_PRIM void HL_NAME(check_stop_wrap)( uv_check_t *h ) { + UV_CHECK_NULL(h,); + uv_check_stop(h); +} +DEFINE_PRIM(_VOID, check_stop_wrap, _HANDLE); + // TCP #define _TCP _HANDLE From 74b7ef2d1127fb2c85c05bcc018fd0a633ab27dd Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Sat, 24 Jul 2021 16:00:06 +0300 Subject: [PATCH 014/117] signal --- libs/uv/uv.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index b6cb08cb5..35b3d6aa5 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -478,6 +478,92 @@ HL_PRIM void HL_NAME(check_stop_wrap)( uv_check_t *h ) { } DEFINE_PRIM(_VOID, check_stop_wrap, _HANDLE); +// Signal + +static int signum_hx2uv( int hx ) { + switch(hx) { + case -1: return SIGABRT; break; + case -2: return SIGFPE; break; + case -3: return SIGHUP; break; + case -4: return SIGILL; break; + case -5: return SIGINT; break; + case -6: return SIGKILL; break; + case -7: return SIGSEGV; break; + case -8: return SIGTERM; break; + case -9: return SIGWINCH; break; + default: return hx; break; + } +} + +static int signum_uv2hx( int uv ) { + switch(uv) { + case SIGABRT: return -1; break; + case SIGFPE: return -2; break; + case SIGHUP: return -3; break; + case SIGILL: return -4; break; + case SIGINT: return -5; break; + case SIGKILL: return -6; break; + case SIGSEGV: return -7; break; + case SIGTERM: return -8; break; + case SIGWINCH: return -9; break; + default: return uv; break; + } +} + +static void on_signal( uv_signal_t *h, int signum ) { + events_data *ev = UV_DATA(h); + vclosure *c = ev ? ev->events[0] : NULL; + if( !c ) + hl_fatal("No callback in signal handle"); + hl_call1(void, c, int, signum_uv2hx(signum)); +} + +static void on_signal_oneshot( uv_signal_t *h, int signum ) { + events_data *ev = UV_DATA(h); + vclosure *c = ev ? ev->events[0] : NULL; + if( !c ) + hl_fatal("No callback in signal handle"); + clear_callb((uv_handle_t *)h,0); + hl_call1(void, c, int, signum_uv2hx(signum)); +} + +HL_PRIM uv_signal_t *HL_NAME(signal_init_wrap)( uv_loop_t *loop ) { + UV_CHECK_NULL(loop,NULL); + uv_signal_t *h = UV_ALLOC(uv_signal_t); + UV_CHECK_ERROR(uv_signal_init(loop,h),free(h),NULL); + init_hl_data((uv_handle_t*)h); + return h; +} +DEFINE_PRIM(_HANDLE, signal_init_wrap, _LOOP); + +HL_PRIM void HL_NAME(signal_start_wrap)( uv_signal_t *h, int signum, vclosure *c ) { + UV_CHECK_NULL(h,); + UV_CHECK_NULL(c,); + register_callb((uv_handle_t*)h,c,0); + UV_CHECK_ERROR(uv_signal_start(h, on_signal, signum_hx2uv(signum)),clear_callb((uv_handle_t *)h,0),); +} +DEFINE_PRIM(_VOID, signal_start_wrap, _HANDLE _I32 _FUN(_VOID,_I32)); + +HL_PRIM void HL_NAME(signal_start_oneshot_wrap)( uv_signal_t *h, int signum, vclosure *c ) { + UV_CHECK_NULL(h,); + UV_CHECK_NULL(c,); + register_callb((uv_handle_t*)h,c,0); + UV_CHECK_ERROR(uv_signal_start_oneshot(h, on_signal_oneshot, signum_hx2uv(signum)),clear_callb((uv_handle_t *)h,0),); +} +DEFINE_PRIM(_VOID, signal_start_oneshot_wrap, _HANDLE _I32 _FUN(_VOID,_I32)); + +HL_PRIM void HL_NAME(signal_stop_wrap)( uv_signal_t *h ) { + UV_CHECK_NULL(h,); + UV_CHECK_ERROR(uv_signal_stop(h),,); +} +DEFINE_PRIM(_VOID, signal_stop_wrap, _HANDLE); + +HL_PRIM int HL_NAME(signal_get_sigNum_wrap)(uv_signal_t *h) { + UV_CHECK_NULL(h,0); + return signum_uv2hx(h->signum); +} +DEFINE_PRIM(_I32, signal_get_sigNum_wrap, _HANDLE); + // TCP #define _TCP _HANDLE From 4ade9ef4091a88a92b6e32651deb4e18dd993ac8 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Sun, 25 Jul 2021 11:44:35 +0300 Subject: [PATCH 015/117] update loop --- libs/uv/uv.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 35b3d6aa5..c63ab6ba4 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -652,9 +652,29 @@ HL_PRIM uv_loop_t *HL_NAME(loop_init_wrap)( ) { DEFINE_PRIM(_LOOP, loop_init_wrap, _NO_ARG); DEFINE_PRIM(_LOOP, default_loop, _NO_ARG); -DEFINE_PRIM(_I32, loop_close, _LOOP); -DEFINE_PRIM(_I32, run, _LOOP _I32); -DEFINE_PRIM(_I32, loop_alive, _LOOP); -DEFINE_PRIM(_VOID, stop, _LOOP); + +HL_PRIM void HL_NAME(loop_close_wrap)( uv_loop_t *loop ) { + UV_CHECK_NULL(loop,); + UV_CHECK_ERROR(uv_loop_close(loop),,); +} +DEFINE_PRIM(_VOID, loop_close_wrap, _LOOP); + +HL_PRIM bool HL_NAME(run_wrap)( uv_loop_t *loop, int mode ) { + UV_CHECK_NULL(loop,false); + return uv_run(loop, mode) != 0; +} +DEFINE_PRIM(_BOOL, run_wrap, _LOOP _I32); + +HL_PRIM bool HL_NAME(loop_alive_wrap)( uv_loop_t *loop ) { + UV_CHECK_NULL(loop,false); + return uv_loop_alive(loop) != 0; +} +DEFINE_PRIM(_BOOL, loop_alive_wrap, _LOOP); + +HL_PRIM void HL_NAME(stop_wrap)( uv_loop_t *loop ) { + UV_CHECK_NULL(loop,); + uv_stop(loop); +} +DEFINE_PRIM(_VOID, stop_wrap, _LOOP); DEFINE_PRIM(_BYTES, strerror, _I32); From 22a784b788865b98712a08cfc168fe411aa2cfc8 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Sun, 25 Jul 2021 20:35:28 +0300 Subject: [PATCH 016/117] stream --- libs/uv/uv.c | 335 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 198 insertions(+), 137 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index c63ab6ba4..f6701dfca 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -15,13 +15,12 @@ typedef struct sockaddr uv_sockaddr; #define EVT_CLOSE 1 -#define EVT_READ 0 // stream -#define EVT_LISTEN 2 // stream +#define EVT_STREAM_READ 0 +#define EVT_STREAM_LISTEN 2 -#define EVT_WRITE 0 // write_t #define EVT_CONNECT 0 // connect_t -#define EVT_MAX 2 +#define EVT_MAX 2 // !!!!!!!!!!!!!! typedef struct { vclosure *events[EVT_MAX + 1]; @@ -50,90 +49,90 @@ typedef struct { // Errors -// static int errno_uv2hx( int uv_errno ) { -// switch(uv_errno) { -// case 0: return 0; break; -// case UV_E2BIG: return 1; break; -// case UV_EACCES: return 2; break; -// case UV_EADDRINUSE: return 3; break; -// case UV_EADDRNOTAVAIL: return 4; break; -// case UV_EAFNOSUPPORT: return 5; break; -// case UV_EAGAIN: return 6; break; -// case UV_EAI_ADDRFAMILY: return 7; break; -// case UV_EAI_AGAIN: return 8; break; -// case UV_EAI_BADFLAGS: return 9; break; -// case UV_EAI_BADHINTS: return 10; break; -// case UV_EAI_CANCELED: return 11; break; -// case UV_EAI_FAIL: return 11; break; -// case UV_EAI_FAMILY: return 12; break; -// case UV_EAI_MEMORY: return 13; break; -// case UV_EAI_NODATA: return 14; break; -// case UV_EAI_NONAME: return 15; break; -// case UV_EAI_OVERFLOW: return 16; break; -// case UV_EAI_PROTOCOL: return 17; break; -// case UV_EAI_SERVICE: return 18; break; -// case UV_EAI_SOCKTYPE: return 19; break; -// case UV_EALREADY: return 20; break; -// case UV_EBADF: return 21; break; -// case UV_EBUSY: return 22; break; -// case UV_ECANCELED: return 23; break; -// case UV_ECHARSET: return 24; break; -// case UV_ECONNABORTED: return 25; break; -// case UV_ECONNREFUSED: return 26; break; -// case UV_ECONNRESET: return 27; break; -// case UV_EDESTADDRREQ: return 28; break; -// case UV_EEXIST: return 29; break; -// case UV_EFAULT: return 30; break; -// case UV_EFBIG: return 31; break; -// case UV_EHOSTUNREACH: return 32; break; -// case UV_EINTR: return 33; break; -// case UV_EINVAL: return 34; break; -// case UV_EIO: return 35; break; -// case UV_EISCONN: return 36; break; -// case UV_EISDIR: return 37; break; -// case UV_ELOOP: return 38; break; -// case UV_EMFILE: return 39; break; -// case UV_EMSGSIZE: return 40; break; -// case UV_ENAMETOOLONG: return 41; break; -// case UV_ENETDOWN: return 42; break; -// case UV_ENETUNREACH: return 43; break; -// case UV_ENFILE: return 44; break; -// case UV_ENOBUFS: return 45; break; -// case UV_ENODEV: return 46; break; -// case UV_ENOENT: return 47; break; -// case UV_ENOMEM: return 48; break; -// case UV_ENONET: return 49; break; -// case UV_ENOPROTOOPT: return 50; break; -// case UV_ENOSPC: return 51; break; -// case UV_ENOSYS: return 52; break; -// case UV_ENOTCONN: return 53; break; -// case UV_ENOTDIR: return 54; break; -// case UV_ENOTEMPTY: return 55; break; -// case UV_ENOTSOCK: return 56; break; -// case UV_ENOTSUP: return 57; break; -// case UV_EPERM: return 58; break; -// case UV_EPIPE: return 59; break; -// case UV_EPROTO: return 60; break; -// case UV_EPROTONOSUPPORT: return 61; break; -// case UV_EPROTOTYPE: return 62; break; -// case UV_ERANGE: return 63; break; -// case UV_EROFS: return 64; break; -// case UV_ESHUTDOWN: return 65; break; -// case UV_ESPIPE: return 66; break; -// case UV_ESRCH: return 67; break; -// case UV_ETIMEDOUT: return 68; break; -// case UV_ETXTBSY: return 69; break; -// case UV_EXDEV: return 70; break; -// case UV_UNKNOWN: return 71; break; -// case UV_EOF: return 72; break; -// case UV_ENXIO: return 73; break; -// case UV_EMLINK: return 74; break; -// case UV_EHOSTDOWN: return 75; break; -// case UV_EREMOTEIO: return 76; break; -// case UV_ENOTTY: return 77; break; -// default: return UV_UNKNOWN; -// } -// } +static int errno_uv2hx( int uv_errno ) { + switch(uv_errno) { + case 0: return 0; break; + case UV_E2BIG: return 1; break; + case UV_EACCES: return 2; break; + case UV_EADDRINUSE: return 3; break; + case UV_EADDRNOTAVAIL: return 4; break; + case UV_EAFNOSUPPORT: return 5; break; + case UV_EAGAIN: return 6; break; + case UV_EAI_ADDRFAMILY: return 7; break; + case UV_EAI_AGAIN: return 8; break; + case UV_EAI_BADFLAGS: return 9; break; + case UV_EAI_BADHINTS: return 10; break; + case UV_EAI_CANCELED: return 11; break; + case UV_EAI_FAIL: return 11; break; + case UV_EAI_FAMILY: return 12; break; + case UV_EAI_MEMORY: return 13; break; + case UV_EAI_NODATA: return 14; break; + case UV_EAI_NONAME: return 15; break; + case UV_EAI_OVERFLOW: return 16; break; + case UV_EAI_PROTOCOL: return 17; break; + case UV_EAI_SERVICE: return 18; break; + case UV_EAI_SOCKTYPE: return 19; break; + case UV_EALREADY: return 20; break; + case UV_EBADF: return 21; break; + case UV_EBUSY: return 22; break; + case UV_ECANCELED: return 23; break; + case UV_ECHARSET: return 24; break; + case UV_ECONNABORTED: return 25; break; + case UV_ECONNREFUSED: return 26; break; + case UV_ECONNRESET: return 27; break; + case UV_EDESTADDRREQ: return 28; break; + case UV_EEXIST: return 29; break; + case UV_EFAULT: return 30; break; + case UV_EFBIG: return 31; break; + case UV_EHOSTUNREACH: return 32; break; + case UV_EINTR: return 33; break; + case UV_EINVAL: return 34; break; + case UV_EIO: return 35; break; + case UV_EISCONN: return 36; break; + case UV_EISDIR: return 37; break; + case UV_ELOOP: return 38; break; + case UV_EMFILE: return 39; break; + case UV_EMSGSIZE: return 40; break; + case UV_ENAMETOOLONG: return 41; break; + case UV_ENETDOWN: return 42; break; + case UV_ENETUNREACH: return 43; break; + case UV_ENFILE: return 44; break; + case UV_ENOBUFS: return 45; break; + case UV_ENODEV: return 46; break; + case UV_ENOENT: return 47; break; + case UV_ENOMEM: return 48; break; + case UV_ENONET: return 49; break; + case UV_ENOPROTOOPT: return 50; break; + case UV_ENOSPC: return 51; break; + case UV_ENOSYS: return 52; break; + case UV_ENOTCONN: return 53; break; + case UV_ENOTDIR: return 54; break; + case UV_ENOTEMPTY: return 55; break; + case UV_ENOTSOCK: return 56; break; + case UV_ENOTSUP: return 57; break; + case UV_EPERM: return 58; break; + case UV_EPIPE: return 59; break; + case UV_EPROTO: return 60; break; + case UV_EPROTONOSUPPORT: return 61; break; + case UV_EPROTOTYPE: return 62; break; + case UV_ERANGE: return 63; break; + case UV_EROFS: return 64; break; + case UV_ESHUTDOWN: return 65; break; + case UV_ESPIPE: return 66; break; + case UV_ESRCH: return 67; break; + case UV_ETIMEDOUT: return 68; break; + case UV_ETXTBSY: return 69; break; + case UV_EXDEV: return 70; break; + case UV_UNKNOWN: return 71; break; + case UV_EOF: return 72; break; + case UV_ENXIO: return 73; break; + case UV_EMLINK: return 74; break; + case UV_EHOSTDOWN: return 75; break; + case UV_EREMOTEIO: return 76; break; + case UV_ENOTTY: return 77; break; + default: return 71; //UV_UNKNOWN; + } +} static void hx_error(int uv_errno) { //TODO: throw hl.uv.UVException @@ -179,7 +178,12 @@ static void on_close( uv_handle_t *h ) { } static void free_handle( void *h ) { - if( h ) uv_close((uv_handle_t*)h, on_close); + uv_close((uv_handle_t*)h, on_close); +} + +static void free_request( void *r ) { + clear_callb((uv_handle_t*)r,0); + free(r); } HL_PRIM void HL_NAME(close_handle)( uv_handle_t *h, vclosure *c ) { @@ -221,73 +225,130 @@ DEFINE_PRIM(_VOID, unref_wrap, _HANDLE); // STREAM -static void on_write( uv_write_t *wr, int status ) { - vdynamic b; - vdynamic *args = &b; - b.t = &hlt_bool; - b.v.b = status == 0; - trigger_callb((uv_handle_t*)wr,EVT_WRITE,&args,1,false); - on_close((uv_handle_t*)wr); +static void on_shutdown( uv_shutdown_t *r, int status ) { + events_data *ev = UV_DATA(r); + vclosure *c = ev ? ev->events[0] : NULL; + if( !c ) + hl_fatal("No callback in shutdown request"); + free_request(r); + hl_call1(void, c, int, errno_uv2hx(status)); } -HL_PRIM bool HL_NAME(stream_write)( uv_stream_t *s, vbyte *b, int size, vclosure *c ) { - uv_write_t *wr = UV_ALLOC(uv_write_t); - events_data *d = init_hl_data((uv_handle_t*)wr); - // keep a copy of the data - uv_buf_t buf; - d->write_data = malloc(size); - memcpy(d->write_data,b,size); - buf.base = d->write_data; - buf.len = size; - register_callb((uv_handle_t*)wr,c,EVT_WRITE); - if( uv_write(wr,s,&buf,1,on_write) < 0 ) { - on_close((uv_handle_t*)wr); - return false; - } - return true; +HL_PRIM void HL_NAME(shutdown_wrap)( uv_stream_t *h, vclosure *c ) { + UV_CHECK_NULL(h,); + UV_CHECK_NULL(c,); + uv_shutdown_t *req = UV_ALLOC(uv_shutdown_t); + register_callb((uv_handle_t*)req,c,0); + UV_CHECK_ERROR(uv_shutdown(req, h, on_shutdown),free_request(req),); +} +DEFINE_PRIM(_VOID, shutdown_wrap, _HANDLE _FUN(_VOID,_I32)); + +static void on_listen( uv_stream_t *h, int status ) { + events_data *ev = UV_DATA(h); + vclosure *c = ev ? ev->events[EVT_STREAM_LISTEN] : NULL; + if( !c ) + hl_fatal("No listen callback in stream handle"); + hl_call1(void, c, int, errno_uv2hx(status)); +} + +HL_PRIM void HL_NAME(listen_wrap)( uv_stream_t *h, int backlog, vclosure *c ) { + UV_CHECK_NULL(h,); + UV_CHECK_NULL(c,); + register_callb((uv_handle_t*)h,c,EVT_STREAM_LISTEN); + UV_CHECK_ERROR(uv_listen(h, backlog, on_listen),clear_callb((uv_handle_t*)h,EVT_STREAM_LISTEN),); +} +DEFINE_PRIM(_VOID, listen_wrap, _HANDLE _BYTES _I32 _FUN(_VOID,_BOOL)); + +HL_PRIM void HL_NAME(accept_wrap)( uv_stream_t *h, uv_stream_t *client ) { + UV_CHECK_NULL(h,); + UV_CHECK_NULL(client,); + UV_CHECK_ERROR(uv_accept(h, client),,); } +DEFINE_PRIM(_VOID, accept_wrap, _HANDLE _HANDLE); static void on_alloc( uv_handle_t* h, size_t size, uv_buf_t *buf ) { *buf = uv_buf_init(malloc(size), (int)size); } -static void on_read( uv_stream_t *s, ssize_t nread, const uv_buf_t *buf ) { - vdynamic bytes; - vdynamic len; - vdynamic *args[2]; - bytes.t = &hlt_bytes; - bytes.v.ptr = buf->base; - len.t = &hlt_i32; - len.v.i = (int)nread; - args[0] = &bytes; - args[1] = &len; - trigger_callb((uv_handle_t*)s,EVT_READ,args,2,true); +static void on_read( uv_stream_t *h, ssize_t nread, const uv_buf_t *buf ) { + events_data *ev = UV_DATA(h); + vclosure *c = ev ? ev->events[EVT_STREAM_READ] : NULL; + if( !c ) + hl_fatal("No listen callback in stream handle"); + if( nread < 0 ) { + hl_call3(void, c, int, errno_uv2hx(nread), vbyte *, NULL, int, 0); + } else { + hl_call3(void, c, int, 0, vbyte *, (vbyte *)buf->base, int, nread); + } free(buf->base); } -HL_PRIM bool HL_NAME(stream_read_start)( uv_stream_t *s, vclosure *c ) { - register_callb((uv_handle_t*)s,c,EVT_READ); - return uv_read_start(s,on_alloc,on_read) >= 0; +HL_PRIM void HL_NAME(read_start_wrap)( uv_stream_t *h, vclosure *c ) { + UV_CHECK_NULL(h,); + UV_CHECK_NULL(c,); + register_callb((uv_handle_t*)h,c,EVT_STREAM_READ); + UV_CHECK_ERROR(uv_read_start(h,on_alloc,on_read), clear_callb((uv_handle_t*)h,EVT_STREAM_READ),); } +DEFINE_PRIM(_VOID, read_start_wrap, _HANDLE _FUN(_VOID,_I32 _BYTES _I32)); -HL_PRIM void HL_NAME(stream_read_stop)( uv_stream_t *s ) { - uv_read_stop(s); - clear_callb((uv_handle_t*)s,EVT_READ); // clear callback +HL_PRIM void HL_NAME(read_stop_wrap)( uv_stream_t *h ) { + UV_CHECK_NULL(h,); + clear_callb((uv_handle_t*)h,EVT_STREAM_READ); + uv_read_stop(h); } +DEFINE_PRIM(_VOID, read_stop_wrap, _HANDLE); -static void on_listen( uv_stream_t *s, int status ) { - trigger_callb((uv_handle_t*)s, EVT_LISTEN, NULL, 0, true); +static void on_write( uv_write_t *r, int status ) { + events_data *ev = UV_DATA(r); + vclosure *c = ev ? ev->events[0] : NULL; + if( !c ) + hl_fatal("No callback in write request"); + hl_call1(void, c, int, status < 0 ? errno_uv2hx(status) : 0); + free_request(r); } -HL_PRIM bool HL_NAME(stream_listen)( uv_stream_t *s, int count, vclosure *c ) { - register_callb((uv_handle_t*)s,c,EVT_LISTEN); - return uv_listen(s,count,on_listen) >= 0; +HL_PRIM void HL_NAME(write_wrap)( uv_stream_t *h, vbyte *b, int length, vclosure *c ) { + UV_CHECK_NULL(h,); + UV_CHECK_NULL(b,); + UV_CHECK_NULL(c,); + uv_write_t *r = UV_ALLOC(uv_write_t); + events_data *d = init_hl_data((uv_handle_t*)r); + // keep a copy of the data + uv_buf_t buf; + d->write_data = malloc(length); + memcpy(d->write_data,b,length); + buf.base = d->write_data; + buf.len = length; + register_callb((uv_handle_t*)r,c,0); + UV_CHECK_ERROR(uv_write(r,h,&buf,1,on_write),free_request(r),); +} +DEFINE_PRIM(_VOID, write_wrap, _HANDLE _BYTES _I32 _FUN(_VOID,_I32)); + +HL_PRIM int HL_NAME(try_write_wrap)( uv_stream_t *h, vbyte *b, int length ) { + UV_CHECK_NULL(h,0); + UV_CHECK_NULL(b,0); + uv_buf_t buf = uv_buf_init((char *)b, length); + int result = uv_try_write(h,&buf,1); + if( result < 0 ) { + hx_error(result); + return 0; + } + return result; +} +DEFINE_PRIM(_VOID, try_write_wrap, _HANDLE _BYTES _I32); + +HL_PRIM bool HL_NAME(is_readable_wrap)( uv_stream_t *h ) { + UV_CHECK_NULL(h,false); + return uv_is_readable(h) != 0; +} +DEFINE_PRIM(_BOOL, is_readable_wrap, _HANDLE); + +HL_PRIM bool HL_NAME(is_writable_wrap)( uv_stream_t *h ) { + UV_CHECK_NULL(h,false); + return uv_is_writable(h) != 0; } +DEFINE_PRIM(_BOOL, is_writable_wrap, _HANDLE); -DEFINE_PRIM(_BOOL, stream_write, _HANDLE _BYTES _I32 _FUN(_VOID,_BOOL)); -DEFINE_PRIM(_BOOL, stream_read_start, _HANDLE _FUN(_VOID,_BYTES _I32)); -DEFINE_PRIM(_VOID, stream_read_stop, _HANDLE); -DEFINE_PRIM(_BOOL, stream_listen, _HANDLE _I32 _CALLB); // Timer HL_PRIM uv_timer_t *HL_NAME(timer_init_wrap)( uv_loop_t *loop ) { From 1ec55a90c274491ac67384fe6ce799db691107c4 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Wed, 28 Jul 2021 14:25:12 +0300 Subject: [PATCH 017/117] sockaddr --- libs/uv/uv.c | 107 ++++++++++++++++++++------------------------------- 1 file changed, 42 insertions(+), 65 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index f6701dfca..37a59fa0b 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -12,6 +12,9 @@ #endif typedef struct sockaddr uv_sockaddr; +typedef struct sockaddr_in uv_sockaddr_in; +typedef struct sockaddr_in6 uv_sockaddr_in6; +typedef struct sockaddr_storage uv_sockaddr_storage; #define EVT_CLOSE 1 @@ -31,6 +34,7 @@ typedef struct { #define _LOOP _ABSTRACT(uv_loop) #define _HANDLE _ABSTRACT(uv_handle) +#define _SOCKADDR _ABSTRACT(uv_sockaddr_storage) #define _CALLB _FUN(_VOID,_NO_ARG) #define UV_ALLOC(t) ((t*)malloc(sizeof(t))) #define UV_CHECK_NULL(handle,fail_return) \ @@ -186,12 +190,12 @@ static void free_request( void *r ) { free(r); } -HL_PRIM void HL_NAME(close_handle)( uv_handle_t *h, vclosure *c ) { +HL_PRIM void HL_NAME(close_wrap)( uv_handle_t *h, vclosure *c ) { UV_CHECK_NULL(h,); register_callb(h, c, EVT_CLOSE); free_handle(h); } -DEFINE_PRIM(_VOID, close_handle, _HANDLE _CALLB); +DEFINE_PRIM(_VOID, close_wrap, _HANDLE _CALLB); HL_PRIM bool HL_NAME(is_active_wrap)( uv_handle_t *h ) { UV_CHECK_NULL(h,false); @@ -349,7 +353,6 @@ HL_PRIM bool HL_NAME(is_writable_wrap)( uv_stream_t *h ) { } DEFINE_PRIM(_BOOL, is_writable_wrap, _HANDLE); - // Timer HL_PRIM uv_timer_t *HL_NAME(timer_init_wrap)( uv_loop_t *loop ) { UV_CHECK_NULL(loop,NULL); @@ -625,79 +628,53 @@ HL_PRIM int HL_NAME(signal_get_sigNum_wrap)(uv_signal_t *h) { } DEFINE_PRIM(_I32, signal_get_sigNum_wrap, _HANDLE); -// TCP - -#define _TCP _HANDLE +// Sockaddr -HL_PRIM uv_tcp_t *HL_NAME(tcp_init_wrap)( uv_loop_t *loop ) { - uv_tcp_t *t = UV_ALLOC(uv_tcp_t); - if( uv_tcp_init(loop,t) < 0 ) { - free(t); - return NULL; - } - init_hl_data((uv_handle_t*)t); - return t; +HL_PRIM uv_sockaddr_storage *HL_NAME(ip4_addr_wrap)( vstring *ip, int port ) { + UV_CHECK_NULL(ip,NULL); + uv_sockaddr_storage *addr = UV_ALLOC(uv_sockaddr_storage); + UV_CHECK_ERROR(uv_ip4_addr(hl_to_utf8(ip->bytes), port, (uv_sockaddr_in *)addr),free(addr),NULL); + return addr; } +DEFINE_PRIM(_SOCKADDR, ip4_addr_wrap, _STRING _I32); -static void on_connect( uv_connect_t *cnx, int status ) { - vdynamic b; - vdynamic *args = &b; - b.t = &hlt_bool; - b.v.b = status == 0; - trigger_callb((uv_handle_t*)cnx,EVT_CONNECT,&args,1,false); - on_close((uv_handle_t*)cnx); -} - -HL_PRIM uv_connect_t *HL_NAME(tcp_connect_wrap)( uv_tcp_t *t, int host, int port, vclosure *c ) { - uv_connect_t *cnx = UV_ALLOC(uv_connect_t); - struct sockaddr_in addr; - memset(&addr,0,sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons((unsigned short)port); - *(int*)&addr.sin_addr.s_addr = host; - if( !t || uv_tcp_connect(cnx,t,(uv_sockaddr *)&addr,on_connect) < 0 ) { - free(cnx); - return NULL; - } - memset(&addr,0,sizeof(addr)); - init_hl_data((uv_handle_t*)cnx); - register_callb((uv_handle_t*)cnx, c, EVT_CONNECT); - return cnx; +HL_PRIM uv_sockaddr_storage *HL_NAME(ip6_addr_wrap)( vstring *ip, int port ) { + UV_CHECK_NULL(ip,NULL); + uv_sockaddr_storage *addr = UV_ALLOC(uv_sockaddr_storage); + UV_CHECK_ERROR(uv_ip6_addr(hl_to_utf8(ip->bytes), port, (uv_sockaddr_in6 *)addr),free(addr),NULL); + return addr; } +DEFINE_PRIM(_SOCKADDR, ip6_addr_wrap, _STRING _I32); -HL_PRIM bool HL_NAME(tcp_bind_wrap)( uv_tcp_t *t, int host, int port ) { - struct sockaddr_in addr; - memset(&addr,0,sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_port = htons((unsigned short)port); - *(int*)&addr.sin_addr.s_addr = host; - return uv_tcp_bind(t,(uv_sockaddr *)&addr,0) >= 0; -} - - -HL_PRIM uv_tcp_t *HL_NAME(tcp_accept_wrap)( uv_tcp_t *t ) { - uv_tcp_t *client = UV_ALLOC(uv_tcp_t); - if( uv_tcp_init(t->loop, client) < 0 ) { - free(client); +HL_PRIM vdynamic *HL_NAME(get_port)( uv_sockaddr_storage *addr ) { + UV_CHECK_NULL(addr,NULL); + int port; + if( addr->ss_family == AF_INET ) { + port = ntohs(((uv_sockaddr_in *)addr)->sin_port); + } else if( addr->ss_family == AF_INET6 ) { + port = ntohs(((uv_sockaddr_in6 *)addr)->sin6_port); + } else { return NULL; } - if( uv_accept((uv_stream_t*)t,(uv_stream_t*)client) < 0 ) { - uv_close((uv_handle_t*)client, NULL); + return hl_make_dyn(&port, &hlt_i32); +} +DEFINE_PRIM(_NULL(_I32), get_port, _SOCKADDR); + +//How to return vstring instead of vbyte? +HL_PRIM vbyte *HL_NAME(ip_name_wrap)( uv_sockaddr_storage *addr ) { + UV_CHECK_NULL(addr,NULL); + vbyte *dst = hl_alloc_bytes(128); + if( addr->ss_family == AF_INET ) { + UV_CHECK_ERROR(uv_ip4_name((uv_sockaddr_in *)addr, (char *)dst, 128),,NULL); //free bytes? + } else if( addr->ss_family == AF_INET6 ) { + UV_CHECK_ERROR(uv_ip6_name((uv_sockaddr_in6 *)addr, (char *)dst, 128),,NULL); + } else { return NULL; } - init_hl_data((uv_handle_t*)client); - return client; -} - -HL_PRIM void HL_NAME(tcp_nodelay_wrap)( uv_tcp_t *t, bool enable ) { - uv_tcp_nodelay(t,enable?1:0); + return dst; } +DEFINE_PRIM(_BYTES, ip_name_wrap, _SOCKADDR); -DEFINE_PRIM(_TCP, tcp_init_wrap, _LOOP); -DEFINE_PRIM(_HANDLE, tcp_connect_wrap, _TCP _I32 _I32 _FUN(_VOID,_BOOL)); -DEFINE_PRIM(_BOOL, tcp_bind_wrap, _TCP _I32 _I32); -DEFINE_PRIM(_HANDLE, tcp_accept_wrap, _HANDLE); -DEFINE_PRIM(_VOID, tcp_nodelay_wrap, _TCP _BOOL); // loop From 639016282859ec80fddee72634d3cd8477bc914a Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Wed, 28 Jul 2021 16:37:38 +0300 Subject: [PATCH 018/117] tcp --- libs/uv/uv.c | 338 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 257 insertions(+), 81 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 37a59fa0b..c9e03dac6 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -55,89 +55,174 @@ typedef struct { static int errno_uv2hx( int uv_errno ) { switch(uv_errno) { - case 0: return 0; break; - case UV_E2BIG: return 1; break; - case UV_EACCES: return 2; break; - case UV_EADDRINUSE: return 3; break; - case UV_EADDRNOTAVAIL: return 4; break; - case UV_EAFNOSUPPORT: return 5; break; - case UV_EAGAIN: return 6; break; - case UV_EAI_ADDRFAMILY: return 7; break; - case UV_EAI_AGAIN: return 8; break; - case UV_EAI_BADFLAGS: return 9; break; - case UV_EAI_BADHINTS: return 10; break; - case UV_EAI_CANCELED: return 11; break; - case UV_EAI_FAIL: return 11; break; - case UV_EAI_FAMILY: return 12; break; - case UV_EAI_MEMORY: return 13; break; - case UV_EAI_NODATA: return 14; break; - case UV_EAI_NONAME: return 15; break; - case UV_EAI_OVERFLOW: return 16; break; - case UV_EAI_PROTOCOL: return 17; break; - case UV_EAI_SERVICE: return 18; break; - case UV_EAI_SOCKTYPE: return 19; break; - case UV_EALREADY: return 20; break; - case UV_EBADF: return 21; break; - case UV_EBUSY: return 22; break; - case UV_ECANCELED: return 23; break; - case UV_ECHARSET: return 24; break; - case UV_ECONNABORTED: return 25; break; - case UV_ECONNREFUSED: return 26; break; - case UV_ECONNRESET: return 27; break; - case UV_EDESTADDRREQ: return 28; break; - case UV_EEXIST: return 29; break; - case UV_EFAULT: return 30; break; - case UV_EFBIG: return 31; break; - case UV_EHOSTUNREACH: return 32; break; - case UV_EINTR: return 33; break; - case UV_EINVAL: return 34; break; - case UV_EIO: return 35; break; - case UV_EISCONN: return 36; break; - case UV_EISDIR: return 37; break; - case UV_ELOOP: return 38; break; - case UV_EMFILE: return 39; break; - case UV_EMSGSIZE: return 40; break; - case UV_ENAMETOOLONG: return 41; break; - case UV_ENETDOWN: return 42; break; - case UV_ENETUNREACH: return 43; break; - case UV_ENFILE: return 44; break; - case UV_ENOBUFS: return 45; break; - case UV_ENODEV: return 46; break; - case UV_ENOENT: return 47; break; - case UV_ENOMEM: return 48; break; - case UV_ENONET: return 49; break; - case UV_ENOPROTOOPT: return 50; break; - case UV_ENOSPC: return 51; break; - case UV_ENOSYS: return 52; break; - case UV_ENOTCONN: return 53; break; - case UV_ENOTDIR: return 54; break; - case UV_ENOTEMPTY: return 55; break; - case UV_ENOTSOCK: return 56; break; - case UV_ENOTSUP: return 57; break; - case UV_EPERM: return 58; break; - case UV_EPIPE: return 59; break; - case UV_EPROTO: return 60; break; - case UV_EPROTONOSUPPORT: return 61; break; - case UV_EPROTOTYPE: return 62; break; - case UV_ERANGE: return 63; break; - case UV_EROFS: return 64; break; - case UV_ESHUTDOWN: return 65; break; - case UV_ESPIPE: return 66; break; - case UV_ESRCH: return 67; break; - case UV_ETIMEDOUT: return 68; break; - case UV_ETXTBSY: return 69; break; - case UV_EXDEV: return 70; break; - case UV_UNKNOWN: return 71; break; - case UV_EOF: return 72; break; - case UV_ENXIO: return 73; break; - case UV_EMLINK: return 74; break; - case UV_EHOSTDOWN: return 75; break; - case UV_EREMOTEIO: return 76; break; - case UV_ENOTTY: return 77; break; - default: return 71; //UV_UNKNOWN; + case 0: return 0; + case UV_E2BIG: return 1; + case UV_EACCES: return 2; + case UV_EADDRINUSE: return 3; + case UV_EADDRNOTAVAIL: return 4; + case UV_EAFNOSUPPORT: return 5; + case UV_EAGAIN: return 6; + case UV_EAI_ADDRFAMILY: return 7; + case UV_EAI_AGAIN: return 8; + case UV_EAI_BADFLAGS: return 9; + case UV_EAI_BADHINTS: return 10; + case UV_EAI_CANCELED: return 11; + case UV_EAI_FAIL: return 12; + case UV_EAI_FAMILY: return 13; + case UV_EAI_MEMORY: return 14; + case UV_EAI_NODATA: return 15; + case UV_EAI_NONAME: return 16; + case UV_EAI_OVERFLOW: return 17; + case UV_EAI_PROTOCOL: return 18; + case UV_EAI_SERVICE: return 19; + case UV_EAI_SOCKTYPE: return 20; + case UV_EALREADY: return 21; + case UV_EBADF: return 22; + case UV_EBUSY: return 23; + case UV_ECANCELED: return 24; + case UV_ECHARSET: return 25; + case UV_ECONNABORTED: return 26; + case UV_ECONNREFUSED: return 27; + case UV_ECONNRESET: return 28; + case UV_EDESTADDRREQ: return 29; + case UV_EEXIST: return 30; + case UV_EFAULT: return 31; + case UV_EFBIG: return 32; + case UV_EHOSTUNREACH: return 33; + case UV_EINTR: return 34; + case UV_EINVAL: return 35; + case UV_EIO: return 36; + case UV_EISCONN: return 37; + case UV_EISDIR: return 38; + case UV_ELOOP: return 39; + case UV_EMFILE: return 40; + case UV_EMSGSIZE: return 41; + case UV_ENAMETOOLONG: return 42; + case UV_ENETDOWN: return 43; + case UV_ENETUNREACH: return 44; + case UV_ENFILE: return 45; + case UV_ENOBUFS: return 46; + case UV_ENODEV: return 47; + case UV_ENOENT: return 48; + case UV_ENOMEM: return 49; + case UV_ENONET: return 50; + case UV_ENOPROTOOPT: return 51; + case UV_ENOSPC: return 52; + case UV_ENOSYS: return 53; + case UV_ENOTCONN: return 54; + case UV_ENOTDIR: return 55; + case UV_ENOTEMPTY: return 56; + case UV_ENOTSOCK: return 57; + case UV_ENOTSUP: return 58; + case UV_EPERM: return 59; + case UV_EPIPE: return 60; + case UV_EPROTO: return 61; + case UV_EPROTONOSUPPORT: return 62; + case UV_EPROTOTYPE: return 63; + case UV_ERANGE: return 64; + case UV_EROFS: return 65; + case UV_ESHUTDOWN: return 66; + case UV_ESPIPE: return 67; + case UV_ESRCH: return 68; + case UV_ETIMEDOUT: return 69; + case UV_ETXTBSY: return 70; + case UV_EXDEV: return 71; + case UV_UNKNOWN: return 72; + case UV_EOF: return 73; + case UV_ENXIO: return 74; + case UV_EMLINK: return 75; + case UV_EHOSTDOWN: return 76; + case UV_EREMOTEIO: return 77; + case UV_ENOTTY: return 78; + default: return 72; } } +// static int errno_hx2uv( int hx_errno ) { +// switch(hx_errno) { +// case 0: return 0; +// case 1: return UV_E2BIG; +// case 2: return UV_EACCES; +// case 3: return UV_EADDRINUSE; +// case 4: return UV_EADDRNOTAVAIL; +// case 5: return UV_EAFNOSUPPORT; +// case 6: return UV_EAGAIN; +// case 7: return UV_EAI_ADDRFAMILY; +// case 8: return UV_EAI_AGAIN; +// case 9: return UV_EAI_BADFLAGS; +// case 10: return UV_EAI_BADHINTS; +// case 11: return UV_EAI_CANCELED; +// case 12: return UV_EAI_FAIL; +// case 13: return UV_EAI_FAMILY; +// case 14: return UV_EAI_MEMORY; +// case 15: return UV_EAI_NODATA; +// case 16: return UV_EAI_NONAME; +// case 17: return UV_EAI_OVERFLOW; +// case 18: return UV_EAI_PROTOCOL; +// case 19: return UV_EAI_SERVICE; +// case 20: return UV_EAI_SOCKTYPE; +// case 21: return UV_EALREADY; +// case 22: return UV_EBADF; +// case 23: return UV_EBUSY; +// case 24: return UV_ECANCELED; +// case 25: return UV_ECHARSET; +// case 26: return UV_ECONNABORTED; +// case 27: return UV_ECONNREFUSED; +// case 28: return UV_ECONNRESET; +// case 29: return UV_EDESTADDRREQ; +// case 30: return UV_EEXIST; +// case 31: return UV_EFAULT; +// case 32: return UV_EFBIG; +// case 33: return UV_EHOSTUNREACH; +// case 34: return UV_EINTR; +// case 35: return UV_EINVAL; +// case 36: return UV_EIO; +// case 37: return UV_EISCONN; +// case 38: return UV_EISDIR; +// case 39: return UV_ELOOP; +// case 40: return UV_EMFILE; +// case 41: return UV_EMSGSIZE; +// case 42: return UV_ENAMETOOLONG; +// case 43: return UV_ENETDOWN; +// case 44: return UV_ENETUNREACH; +// case 45: return UV_ENFILE; +// case 46: return UV_ENOBUFS; +// case 47: return UV_ENODEV; +// case 48: return UV_ENOENT; +// case 49: return UV_ENOMEM; +// case 50: return UV_ENONET; +// case 51: return UV_ENOPROTOOPT; +// case 52: return UV_ENOSPC; +// case 53: return UV_ENOSYS; +// case 54: return UV_ENOTCONN; +// case 55: return UV_ENOTDIR; +// case 56: return UV_ENOTEMPTY; +// case 57: return UV_ENOTSOCK; +// case 58: return UV_ENOTSUP; +// case 59: return UV_EPERM; +// case 60: return UV_EPIPE; +// case 61: return UV_EPROTO; +// case 62: return UV_EPROTONOSUPPORT; +// case 63: return UV_EPROTOTYPE; +// case 64: return UV_ERANGE; +// case 65: return UV_EROFS; +// case 66: return UV_ESHUTDOWN; +// case 67: return UV_ESPIPE; +// case 68: return UV_ESRCH; +// case 69: return UV_ETIMEDOUT; +// case 70: return UV_ETXTBSY; +// case 71: return UV_EXDEV; +// case 72: return UV_UNKNOWN; +// case 73: return UV_EOF; +// case 74: return UV_ENXIO; +// case 75: return UV_EMLINK; +// case 76: return UV_EHOSTDOWN; +// case 77: return UV_EREMOTEIO; +// case 78: return UV_ENOTTY; +// default: return UV_UNKNOWN; +// } +// } + static void hx_error(int uv_errno) { //TODO: throw hl.uv.UVException hl_error("%s", hl_to_utf16(uv_err_name(uv_errno))); @@ -261,7 +346,7 @@ HL_PRIM void HL_NAME(listen_wrap)( uv_stream_t *h, int backlog, vclosure *c ) { register_callb((uv_handle_t*)h,c,EVT_STREAM_LISTEN); UV_CHECK_ERROR(uv_listen(h, backlog, on_listen),clear_callb((uv_handle_t*)h,EVT_STREAM_LISTEN),); } -DEFINE_PRIM(_VOID, listen_wrap, _HANDLE _BYTES _I32 _FUN(_VOID,_BOOL)); +DEFINE_PRIM(_VOID, listen_wrap, _HANDLE _I32 _FUN(_VOID,_I32)); HL_PRIM void HL_NAME(accept_wrap)( uv_stream_t *h, uv_stream_t *client ) { UV_CHECK_NULL(h,); @@ -675,6 +760,97 @@ HL_PRIM vbyte *HL_NAME(ip_name_wrap)( uv_sockaddr_storage *addr ) { } DEFINE_PRIM(_BYTES, ip_name_wrap, _SOCKADDR); +// TCP + +HL_PRIM uv_tcp_t *HL_NAME(tcp_init_wrap)( uv_loop_t *loop, vdynamic *domain ) { + UV_CHECK_NULL(loop,NULL); + uv_tcp_t *h = UV_ALLOC(uv_tcp_t); + if( !domain ) { + UV_CHECK_ERROR(uv_tcp_init(loop,h),free(h),NULL); + } else { + int d = domain->v.i; + //convert `hl.uv.SockAddr.AddressFamily` values to native ones + switch( d ) { + case -1: d = AF_UNSPEC; break; + case -2: d = AF_INET; break; + case -3: d = AF_INET6; break; + } + UV_CHECK_ERROR(uv_tcp_init_ex(loop,h,d),free_handle(h),NULL); + } + init_hl_data((uv_handle_t*)h); + return h; +} +DEFINE_PRIM(_HANDLE, tcp_init_wrap, _LOOP _NULL(_I32)); + +HL_PRIM void HL_NAME(tcp_nodelay_wrap)( uv_tcp_t *h, bool enable ) { + UV_CHECK_NULL(h,); + UV_CHECK_ERROR(uv_tcp_nodelay(h,enable?1:0),,); +} +DEFINE_PRIM(_VOID, tcp_nodelay_wrap, _HANDLE _BOOL); + +HL_PRIM void HL_NAME(tcp_keepalive_wrap)( uv_tcp_t *h, bool enable, int delay ) { + UV_CHECK_NULL(h,); + UV_CHECK_ERROR(uv_tcp_keepalive(h,enable?1:0,delay),,); +} +DEFINE_PRIM(_VOID, tcp_keepalive_wrap, _HANDLE _BOOL _I32); + +HL_PRIM void HL_NAME(tcp_simultaneous_accepts_wrap)( uv_tcp_t *h, bool enable ) { + UV_CHECK_NULL(h,); + UV_CHECK_ERROR(uv_tcp_simultaneous_accepts(h,enable?1:0),,); +} +DEFINE_PRIM(_VOID, tcp_simultaneous_accepts_wrap, _HANDLE _BOOL); + +HL_PRIM void HL_NAME(tcp_bind_wrap)( uv_tcp_t *h, uv_sockaddr_storage *addr, vdynamic *ipv6_only ) { + UV_CHECK_NULL(h,); + UV_CHECK_NULL(addr,); + int flags = ipv6_only && ipv6_only->v.b ? UV_TCP_IPV6ONLY : 0; + UV_CHECK_ERROR(uv_tcp_bind(h,(uv_sockaddr *)addr,flags),,); +} +DEFINE_PRIM(_VOID, tcp_bind_wrap, _HANDLE _SOCKADDR _NULL(_BOOL)); + +HL_PRIM uv_sockaddr_storage *HL_NAME(tcp_getsockname_wrap)( uv_tcp_t *h ) { + UV_CHECK_NULL(h,NULL); + uv_sockaddr_storage *addr = UV_ALLOC(uv_sockaddr_storage); + int size = sizeof(uv_sockaddr_storage); + UV_CHECK_ERROR(uv_tcp_getsockname(h,(uv_sockaddr *)addr,&size),free(addr),NULL); + return addr; +} +DEFINE_PRIM(_SOCKADDR, tcp_getsockname_wrap, _HANDLE); + +HL_PRIM uv_sockaddr_storage *HL_NAME(tcp_getpeername_wrap)( uv_tcp_t *h ) { + UV_CHECK_NULL(h,NULL); + uv_sockaddr_storage *addr = UV_ALLOC(uv_sockaddr_storage); + int size = sizeof(uv_sockaddr_storage); + UV_CHECK_ERROR(uv_tcp_getpeername(h,(uv_sockaddr *)addr,&size),free(addr),NULL); + return addr; +} +DEFINE_PRIM(_SOCKADDR, tcp_getpeername_wrap, _HANDLE); + +static void on_connect( uv_connect_t *r, int status ) { + events_data *ev = UV_DATA(r); + vclosure *c = ev ? ev->events[0] : NULL; + if( !c ) + hl_fatal("No callback in tcp_connect request"); + free_request(r); + hl_call1(void, c, int, errno_uv2hx(status)); +} + +HL_PRIM void HL_NAME(tcp_connect_wrap)( uv_tcp_t *h, uv_sockaddr_storage *addr, vclosure *c ) { + UV_CHECK_NULL(h,); + UV_CHECK_NULL(addr,); + UV_CHECK_NULL(c,); + uv_connect_t *req = UV_ALLOC(uv_connect_t); + register_callb((uv_handle_t*)req,c,0); + UV_CHECK_ERROR(uv_tcp_connect(req, h,(uv_sockaddr *)addr,on_connect),free_request(req),); +} +DEFINE_PRIM(_VOID, tcp_connect_wrap, _HANDLE _SOCKADDR _FUN(_VOID,_I32)); + +HL_PRIM void HL_NAME(tcp_close_reset_wrap)( uv_tcp_t *h, vclosure *c ) { + UV_CHECK_NULL(h,); + register_callb((uv_handle_t *)h, c, EVT_CLOSE); + uv_tcp_close_reset(h, on_close); +} +DEFINE_PRIM(_VOID, tcp_close_reset_wrap, _HANDLE _CALLB); // loop From e9c40f1e727e0a9459f2223a51d96b6a06245c5c Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Wed, 28 Jul 2021 22:33:59 +0300 Subject: [PATCH 019/117] separate uv_handle_t and uv_req_t functions --- libs/uv/uv.c | 175 +++++++++++++++++++++++++++++---------------------- 1 file changed, 101 insertions(+), 74 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index c9e03dac6..8fc856bd2 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -228,9 +228,42 @@ static void hx_error(int uv_errno) { hl_error("%s", hl_to_utf16(uv_err_name(uv_errno))); } +// Request + +static events_data *req_init_hl_data( uv_req_t *r ) { + events_data *d = hl_gc_alloc_raw(sizeof(events_data)); + memset(d,0,sizeof(events_data)); + hl_add_root(&r->data); + r->data = d; + return d; +} + +static void req_register_callback( uv_req_t *r, vclosure *c, int event_kind ) { + if( !r ) + hl_fatal("Missing req"); + if( !r->data ) + hl_fatal("Missing req data"); + UV_DATA(r)->events[event_kind] = c; +} + +static void req_clear_callback( uv_req_t *r, int event_kind ) { + req_register_callback(r,NULL,event_kind); +} + +static void free_req( uv_req_t *r ) { + events_data *ev = UV_DATA(r); + if( ev ) { + req_clear_callback((uv_req_t *)r, 0); + free(ev->write_data); + hl_remove_root(&r->data); + r->data = NULL; + } + free(r); +} + // HANDLE -static events_data *init_hl_data( uv_handle_t *h ) { +static events_data *handle_init_hl_data( uv_handle_t *h ) { events_data *d = hl_gc_alloc_raw(sizeof(events_data)); memset(d,0,sizeof(events_data)); hl_add_root(&h->data); @@ -238,46 +271,42 @@ static events_data *init_hl_data( uv_handle_t *h ) { return d; } -static void register_callb( uv_handle_t *h, vclosure *c, int event_kind ) { - if( !h || !h->data ) - hl_fatal("Missing handle or handle data"); +static void handle_register_callback( uv_handle_t *h, vclosure *c, int event_kind ) { + if( !h ) + hl_fatal("Missing handle"); + if( !h->data ) + hl_fatal("Missing handle data"); UV_DATA(h)->events[event_kind] = c; } -static void clear_callb( uv_handle_t *h, int event_kind ) { - register_callb(h,NULL,event_kind); -} - -static void trigger_callb( uv_handle_t *h, int event_kind, vdynamic **args, int nargs, bool repeat ) { - events_data *ev = UV_DATA(h); - vclosure *c = ev ? ev->events[event_kind] : NULL; - if( !c ) return; - if( !repeat ) ev->events[event_kind] = NULL; - hl_dyn_call(c, args, nargs); +static void handle_clear_callback( uv_handle_t *h, int event_kind ) { + handle_register_callback(h,NULL,event_kind); } static void on_close( uv_handle_t *h ) { events_data *ev = UV_DATA(h); - if( !ev ) return; - trigger_callb(h, EVT_CLOSE, NULL, 0, false); - free(ev->write_data); - hl_remove_root(&h->data); - h->data = NULL; + if( ev ) { + vclosure *c = ev ? ev->events[EVT_CLOSE] : NULL; + if( c ) { + hl_call0(void, c); + handle_clear_callback(h, EVT_CLOSE); + } + free(ev->write_data); + hl_remove_root(&h->data); + h->data = NULL; + } free(h); } -static void free_handle( void *h ) { - uv_close((uv_handle_t*)h, on_close); -} - -static void free_request( void *r ) { - clear_callb((uv_handle_t*)r,0); - free(r); +static void free_handle( uv_handle_t *h ) { + printf("free_handle\n"); + uv_close(h, on_close); } HL_PRIM void HL_NAME(close_wrap)( uv_handle_t *h, vclosure *c ) { UV_CHECK_NULL(h,); - register_callb(h, c, EVT_CLOSE); + printf("close_wrap\n"); + handle_register_callback(h, c, EVT_CLOSE); free_handle(h); } DEFINE_PRIM(_VOID, close_wrap, _HANDLE _CALLB); @@ -319,16 +348,17 @@ static void on_shutdown( uv_shutdown_t *r, int status ) { vclosure *c = ev ? ev->events[0] : NULL; if( !c ) hl_fatal("No callback in shutdown request"); - free_request(r); hl_call1(void, c, int, errno_uv2hx(status)); + free_req((uv_req_t *)r); } HL_PRIM void HL_NAME(shutdown_wrap)( uv_stream_t *h, vclosure *c ) { UV_CHECK_NULL(h,); UV_CHECK_NULL(c,); - uv_shutdown_t *req = UV_ALLOC(uv_shutdown_t); - register_callb((uv_handle_t*)req,c,0); - UV_CHECK_ERROR(uv_shutdown(req, h, on_shutdown),free_request(req),); + uv_shutdown_t *r = UV_ALLOC(uv_shutdown_t); + req_init_hl_data((uv_req_t *)r); + req_register_callback((uv_req_t*)r,c,0); + UV_CHECK_ERROR(uv_shutdown(r, h, on_shutdown),free_req((uv_req_t *)r),); } DEFINE_PRIM(_VOID, shutdown_wrap, _HANDLE _FUN(_VOID,_I32)); @@ -343,8 +373,8 @@ static void on_listen( uv_stream_t *h, int status ) { HL_PRIM void HL_NAME(listen_wrap)( uv_stream_t *h, int backlog, vclosure *c ) { UV_CHECK_NULL(h,); UV_CHECK_NULL(c,); - register_callb((uv_handle_t*)h,c,EVT_STREAM_LISTEN); - UV_CHECK_ERROR(uv_listen(h, backlog, on_listen),clear_callb((uv_handle_t*)h,EVT_STREAM_LISTEN),); + handle_register_callback((uv_handle_t*)h,c,EVT_STREAM_LISTEN); + UV_CHECK_ERROR(uv_listen(h, backlog, on_listen),handle_clear_callback((uv_handle_t*)h,EVT_STREAM_LISTEN),); } DEFINE_PRIM(_VOID, listen_wrap, _HANDLE _I32 _FUN(_VOID,_I32)); @@ -375,14 +405,14 @@ static void on_read( uv_stream_t *h, ssize_t nread, const uv_buf_t *buf ) { HL_PRIM void HL_NAME(read_start_wrap)( uv_stream_t *h, vclosure *c ) { UV_CHECK_NULL(h,); UV_CHECK_NULL(c,); - register_callb((uv_handle_t*)h,c,EVT_STREAM_READ); - UV_CHECK_ERROR(uv_read_start(h,on_alloc,on_read), clear_callb((uv_handle_t*)h,EVT_STREAM_READ),); + handle_register_callback((uv_handle_t*)h,c,EVT_STREAM_READ); + UV_CHECK_ERROR(uv_read_start(h,on_alloc,on_read), handle_clear_callback((uv_handle_t*)h,EVT_STREAM_READ),); } DEFINE_PRIM(_VOID, read_start_wrap, _HANDLE _FUN(_VOID,_I32 _BYTES _I32)); HL_PRIM void HL_NAME(read_stop_wrap)( uv_stream_t *h ) { UV_CHECK_NULL(h,); - clear_callb((uv_handle_t*)h,EVT_STREAM_READ); + handle_clear_callback((uv_handle_t*)h,EVT_STREAM_READ); uv_read_stop(h); } DEFINE_PRIM(_VOID, read_stop_wrap, _HANDLE); @@ -393,7 +423,7 @@ static void on_write( uv_write_t *r, int status ) { if( !c ) hl_fatal("No callback in write request"); hl_call1(void, c, int, status < 0 ? errno_uv2hx(status) : 0); - free_request(r); + free_req((uv_req_t *)r); } HL_PRIM void HL_NAME(write_wrap)( uv_stream_t *h, vbyte *b, int length, vclosure *c ) { @@ -401,15 +431,15 @@ HL_PRIM void HL_NAME(write_wrap)( uv_stream_t *h, vbyte *b, int length, vclosure UV_CHECK_NULL(b,); UV_CHECK_NULL(c,); uv_write_t *r = UV_ALLOC(uv_write_t); - events_data *d = init_hl_data((uv_handle_t*)r); + events_data *d = req_init_hl_data((uv_req_t *)r); // keep a copy of the data uv_buf_t buf; d->write_data = malloc(length); memcpy(d->write_data,b,length); buf.base = d->write_data; buf.len = length; - register_callb((uv_handle_t*)r,c,0); - UV_CHECK_ERROR(uv_write(r,h,&buf,1,on_write),free_request(r),); + req_register_callback((uv_req_t *)r,c,0); + UV_CHECK_ERROR(uv_write(r,h,&buf,1,on_write),free_req((uv_req_t *)r),); } DEFINE_PRIM(_VOID, write_wrap, _HANDLE _BYTES _I32 _FUN(_VOID,_I32)); @@ -424,7 +454,7 @@ HL_PRIM int HL_NAME(try_write_wrap)( uv_stream_t *h, vbyte *b, int length ) { } return result; } -DEFINE_PRIM(_VOID, try_write_wrap, _HANDLE _BYTES _I32); +DEFINE_PRIM(_I32, try_write_wrap, _HANDLE _BYTES _I32); HL_PRIM bool HL_NAME(is_readable_wrap)( uv_stream_t *h ) { UV_CHECK_NULL(h,false); @@ -443,7 +473,7 @@ HL_PRIM uv_timer_t *HL_NAME(timer_init_wrap)( uv_loop_t *loop ) { UV_CHECK_NULL(loop,NULL); uv_timer_t *t = UV_ALLOC(uv_timer_t); UV_CHECK_ERROR(uv_timer_init(loop,t),free(t),NULL); - init_hl_data((uv_handle_t*)t); + handle_init_hl_data((uv_handle_t*)t); return t; } DEFINE_PRIM(_HANDLE, timer_init_wrap, _LOOP); @@ -460,17 +490,17 @@ static void on_timer( uv_timer_t *h ) { HL_PRIM void HL_NAME(timer_start_wrap)(uv_timer_t *t, vclosure *c, int timeout, int repeat) { UV_CHECK_NULL(t,); UV_CHECK_NULL(c,); - register_callb((uv_handle_t*)t,c,0); + handle_register_callback((uv_handle_t*)t,c,0); UV_CHECK_ERROR( uv_timer_start(t,on_timer, (uint64_t)timeout, (uint64_t)repeat), - clear_callb((uv_handle_t*)t,0), + handle_clear_callback((uv_handle_t*)t,0), ); } DEFINE_PRIM(_VOID, timer_start_wrap, _HANDLE _FUN(_VOID,_NO_ARG) _I32 _I32); HL_PRIM void HL_NAME(timer_stop_wrap)(uv_timer_t *t) { UV_CHECK_NULL(t,); - clear_callb((uv_handle_t*)t,0); + handle_clear_callback((uv_handle_t*)t,0); UV_CHECK_ERROR(uv_timer_stop(t),,); } DEFINE_PRIM(_VOID, timer_stop_wrap, _HANDLE); @@ -503,12 +533,12 @@ DEFINE_PRIM(_I32, timer_set_repeat_wrap, _HANDLE _I32); // Async -static void on_async( uv_async_t *a ) { - events_data *ev = UV_DATA(a); +static void on_async( uv_async_t *h ) { + events_data *ev = UV_DATA(h); vclosure *c = ev ? ev->events[0] : NULL; if( !c ) hl_fatal("No callback in async handle"); - hl_call1(void, c, uv_async_t *, a); + hl_call1(void, c, uv_async_t *, h); } HL_PRIM uv_async_t *HL_NAME(async_init_wrap)( uv_loop_t *loop, vclosure *c ) { @@ -516,8 +546,8 @@ HL_PRIM uv_async_t *HL_NAME(async_init_wrap)( uv_loop_t *loop, vclosure *c ) { UV_CHECK_NULL(c,NULL); uv_async_t *a = UV_ALLOC(uv_async_t); UV_CHECK_ERROR(uv_async_init(loop,a,on_async),free(a),NULL); - init_hl_data((uv_handle_t*)a); - register_callb((uv_handle_t*)a,c,0); + handle_init_hl_data((uv_handle_t*)a); + handle_register_callback((uv_handle_t*)a,c,0); return a; } DEFINE_PRIM(_HANDLE, async_init_wrap, _LOOP _FUN(_VOID,_HANDLE)); @@ -542,7 +572,7 @@ HL_PRIM uv_idle_t *HL_NAME(idle_init_wrap)( uv_loop_t *loop ) { UV_CHECK_NULL(loop,NULL); uv_idle_t *h = UV_ALLOC(uv_idle_t); UV_CHECK_ERROR(uv_idle_init(loop,h),free(h),NULL); - init_hl_data((uv_handle_t*)h); + handle_init_hl_data((uv_handle_t*)h); return h; } DEFINE_PRIM(_HANDLE, idle_init_wrap, _LOOP); @@ -550,7 +580,7 @@ DEFINE_PRIM(_HANDLE, idle_init_wrap, _LOOP); HL_PRIM void HL_NAME(idle_start_wrap)( uv_idle_t *h, vclosure *c ) { UV_CHECK_NULL(h,); UV_CHECK_NULL(c,); - register_callb((uv_handle_t*)h,c,0); + handle_register_callback((uv_handle_t*)h,c,0); uv_idle_start(h, on_idle); } DEFINE_PRIM(_VOID, idle_start_wrap, _HANDLE _FUN(_VOID,_NO_ARG)); @@ -575,7 +605,7 @@ HL_PRIM uv_prepare_t *HL_NAME(prepare_init_wrap)( uv_loop_t *loop ) { UV_CHECK_NULL(loop,NULL); uv_prepare_t *h = UV_ALLOC(uv_prepare_t); UV_CHECK_ERROR(uv_prepare_init(loop,h),free(h),NULL); - init_hl_data((uv_handle_t*)h); + handle_init_hl_data((uv_handle_t*)h); return h; } DEFINE_PRIM(_HANDLE, prepare_init_wrap, _LOOP); @@ -583,7 +613,7 @@ DEFINE_PRIM(_HANDLE, prepare_init_wrap, _LOOP); HL_PRIM void HL_NAME(prepare_start_wrap)( uv_prepare_t *h, vclosure *c ) { UV_CHECK_NULL(h,); UV_CHECK_NULL(c,); - register_callb((uv_handle_t*)h,c,0); + handle_register_callback((uv_handle_t*)h,c,0); uv_prepare_start(h, on_prepare); } DEFINE_PRIM(_VOID, prepare_start_wrap, _HANDLE _FUN(_VOID,_NO_ARG)); @@ -608,7 +638,7 @@ HL_PRIM uv_check_t *HL_NAME(check_init_wrap)( uv_loop_t *loop ) { UV_CHECK_NULL(loop,NULL); uv_check_t *h = UV_ALLOC(uv_check_t); UV_CHECK_ERROR(uv_check_init(loop,h),free(h),NULL); - init_hl_data((uv_handle_t*)h); + handle_init_hl_data((uv_handle_t*)h); return h; } DEFINE_PRIM(_HANDLE, check_init_wrap, _LOOP); @@ -616,7 +646,7 @@ DEFINE_PRIM(_HANDLE, check_init_wrap, _LOOP); HL_PRIM void HL_NAME(check_start_wrap)( uv_check_t *h, vclosure *c ) { UV_CHECK_NULL(h,); UV_CHECK_NULL(c,); - register_callb((uv_handle_t*)h,c,0); + handle_register_callback((uv_handle_t*)h,c,0); uv_check_start(h, on_check); } DEFINE_PRIM(_VOID, check_start_wrap, _HANDLE _FUN(_VOID,_NO_ARG)); @@ -672,7 +702,7 @@ static void on_signal_oneshot( uv_signal_t *h, int signum ) { vclosure *c = ev ? ev->events[0] : NULL; if( !c ) hl_fatal("No callback in signal handle"); - clear_callb((uv_handle_t *)h,0); + handle_clear_callback((uv_handle_t *)h,0); hl_call1(void, c, int, signum_uv2hx(signum)); } @@ -680,7 +710,7 @@ HL_PRIM uv_signal_t *HL_NAME(signal_init_wrap)( uv_loop_t *loop ) { UV_CHECK_NULL(loop,NULL); uv_signal_t *h = UV_ALLOC(uv_signal_t); UV_CHECK_ERROR(uv_signal_init(loop,h),free(h),NULL); - init_hl_data((uv_handle_t*)h); + handle_init_hl_data((uv_handle_t*)h); return h; } DEFINE_PRIM(_HANDLE, signal_init_wrap, _LOOP); @@ -688,16 +718,16 @@ DEFINE_PRIM(_HANDLE, signal_init_wrap, _LOOP); HL_PRIM void HL_NAME(signal_start_wrap)( uv_signal_t *h, int signum, vclosure *c ) { UV_CHECK_NULL(h,); UV_CHECK_NULL(c,); - register_callb((uv_handle_t*)h,c,0); - UV_CHECK_ERROR(uv_signal_start(h, on_signal, signum_hx2uv(signum)),clear_callb((uv_handle_t *)h,0),); + handle_register_callback((uv_handle_t*)h,c,0); + UV_CHECK_ERROR(uv_signal_start(h, on_signal, signum_hx2uv(signum)),handle_clear_callback((uv_handle_t *)h,0),); } DEFINE_PRIM(_VOID, signal_start_wrap, _HANDLE _I32 _FUN(_VOID,_I32)); HL_PRIM void HL_NAME(signal_start_oneshot_wrap)( uv_signal_t *h, int signum, vclosure *c ) { UV_CHECK_NULL(h,); UV_CHECK_NULL(c,); - register_callb((uv_handle_t*)h,c,0); - UV_CHECK_ERROR(uv_signal_start_oneshot(h, on_signal_oneshot, signum_hx2uv(signum)),clear_callb((uv_handle_t *)h,0),); + handle_register_callback((uv_handle_t*)h,c,0); + UV_CHECK_ERROR(uv_signal_start_oneshot(h, on_signal_oneshot, signum_hx2uv(signum)),handle_clear_callback((uv_handle_t *)h,0),); } DEFINE_PRIM(_VOID, signal_start_oneshot_wrap, _HANDLE _I32 _FUN(_VOID,_I32)); @@ -775,9 +805,9 @@ HL_PRIM uv_tcp_t *HL_NAME(tcp_init_wrap)( uv_loop_t *loop, vdynamic *domain ) { case -2: d = AF_INET; break; case -3: d = AF_INET6; break; } - UV_CHECK_ERROR(uv_tcp_init_ex(loop,h,d),free_handle(h),NULL); + UV_CHECK_ERROR(uv_tcp_init_ex(loop,h,d),free_handle((uv_handle_t *)h),NULL); } - init_hl_data((uv_handle_t*)h); + handle_init_hl_data((uv_handle_t*)h); return h; } DEFINE_PRIM(_HANDLE, tcp_init_wrap, _LOOP _NULL(_I32)); @@ -831,23 +861,24 @@ static void on_connect( uv_connect_t *r, int status ) { vclosure *c = ev ? ev->events[0] : NULL; if( !c ) hl_fatal("No callback in tcp_connect request"); - free_request(r); hl_call1(void, c, int, errno_uv2hx(status)); + free_req((uv_req_t *)r); } HL_PRIM void HL_NAME(tcp_connect_wrap)( uv_tcp_t *h, uv_sockaddr_storage *addr, vclosure *c ) { UV_CHECK_NULL(h,); UV_CHECK_NULL(addr,); UV_CHECK_NULL(c,); - uv_connect_t *req = UV_ALLOC(uv_connect_t); - register_callb((uv_handle_t*)req,c,0); - UV_CHECK_ERROR(uv_tcp_connect(req, h,(uv_sockaddr *)addr,on_connect),free_request(req),); + uv_connect_t *r = UV_ALLOC(uv_connect_t); + req_init_hl_data((uv_req_t *)r); + req_register_callback((uv_req_t *)r,c,0); + UV_CHECK_ERROR(uv_tcp_connect(r, h,(uv_sockaddr *)addr,on_connect),free_req((uv_req_t *)r),); } DEFINE_PRIM(_VOID, tcp_connect_wrap, _HANDLE _SOCKADDR _FUN(_VOID,_I32)); HL_PRIM void HL_NAME(tcp_close_reset_wrap)( uv_tcp_t *h, vclosure *c ) { UV_CHECK_NULL(h,); - register_callb((uv_handle_t *)h, c, EVT_CLOSE); + handle_register_callback((uv_handle_t *)h, c, EVT_CLOSE); uv_tcp_close_reset(h, on_close); } DEFINE_PRIM(_VOID, tcp_close_reset_wrap, _HANDLE _CALLB); @@ -856,11 +887,7 @@ DEFINE_PRIM(_VOID, tcp_close_reset_wrap, _HANDLE _CALLB); HL_PRIM uv_loop_t *HL_NAME(loop_init_wrap)( ) { uv_loop_t *loop = UV_ALLOC(uv_loop_t); - if( uv_loop_init(loop) < 0) { - free(loop); - //TODO: throw error - return NULL; - } + UV_CHECK_ERROR(uv_loop_init(loop),free(loop),NULL); return loop; } DEFINE_PRIM(_LOOP, loop_init_wrap, _NO_ARG); From e361f1ef91f8dfe77d2d81d6db6cc1a88a35f17d Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Wed, 28 Jul 2021 22:34:06 +0300 Subject: [PATCH 020/117] ignore display.hxml --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index ef2d81331..8348178c1 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,4 @@ node_modules /*.tgz args.txt /other/benchs/hlc +/display.hxml \ No newline at end of file From eeb49c4bbc911e2108b6535960f39babf59121dd Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Wed, 28 Jul 2021 22:34:16 +0300 Subject: [PATCH 021/117] update other/uvsample --- other/uvsample/Log.hx | 7 +++ other/uvsample/TcpSample.hx | 65 +++++++++++++++++++ other/uvsample/UVSample.hx | 122 +----------------------------------- 3 files changed, 73 insertions(+), 121 deletions(-) create mode 100644 other/uvsample/Log.hx create mode 100644 other/uvsample/TcpSample.hx diff --git a/other/uvsample/Log.hx b/other/uvsample/Log.hx new file mode 100644 index 000000000..c2926c5a5 --- /dev/null +++ b/other/uvsample/Log.hx @@ -0,0 +1,7 @@ +class Log { + static final T0 = haxe.Timer.stamp(); + + public static function print( msg : String ) { + Sys.println("["+Std.int((haxe.Timer.stamp() - T0) * 100)+"] "+msg); + } +} \ No newline at end of file diff --git a/other/uvsample/TcpSample.hx b/other/uvsample/TcpSample.hx new file mode 100644 index 000000000..fbac0c4b5 --- /dev/null +++ b/other/uvsample/TcpSample.hx @@ -0,0 +1,65 @@ +import haxe.io.Bytes; +import hl.uv.UVError; +import haxe.Timer; +import hl.uv.UVException; +import sys.thread.Thread; +import hl.uv.Tcp; +import hl.uv.SockAddr; + +class TcpSample { + static inline var PORT = 22001; + + public static function main() { + server(); + Timer.delay(client,100); + } + + static function handle(success:()->Void):(e:UVError)->Void { + return e -> switch e { + case UV_NOERR: success(); + case _: throw new UVException(e); + } + } + + static function server() { + inline function print(msg:String) { + Log.print('SERVER: $msg'); + } + var loop = Thread.current().events; + var server = Tcp.init(loop, INET); + server.bind(SockAddr.ipv4('0.0.0.0', PORT)); + server.listen(32, handle(() -> { + var client = Tcp.init(loop); + server.accept(client); + print('connection from ' + client.getSockName()); + client.readStart((e, data, bytesRead) -> handle(() -> { + print('incoming request: ' + data.toBytes(bytesRead).toString()); + client.write(data, bytesRead, handle(() -> { + client.shutdown(handle(() -> { + client.close(() -> print('client closed')); + })); + })); + })(e)); + })); + } + + static function client() { + inline function print(msg:String) { + Log.print('CLIENT: $msg'); + } + var loop = Thread.current().events; + var client = Tcp.init(loop, INET); + client.connect(SockAddr.ipv4('127.0.0.1', PORT), handle(() -> { + print('connected to ' + client.getPeerName()); + var data = Bytes.ofString('Hello, world!').getData(); + client.write(data.bytes, data.length, handle(() -> { + client.readStart((e, data, bytesRead) -> handle(() -> { + print('response from server: ' + data.toBytes(bytesRead).toString()); + client.shutdown(handle(() -> { + client.close(() -> print('connection closed')); + })); + })(e)); + })); + })); + } +} \ No newline at end of file diff --git a/other/uvsample/UVSample.hx b/other/uvsample/UVSample.hx index 9587177dc..131d9287b 100644 --- a/other/uvsample/UVSample.hx +++ b/other/uvsample/UVSample.hx @@ -1,128 +1,8 @@ import hl.uv.*; class UVSample { - - static var T0 = haxe.Timer.stamp(); - - static function log( msg : String ) { - Sys.println("["+Std.int((haxe.Timer.stamp() - T0) * 100)+"] "+msg); - } - static function main() { - var loop = Loop.getDefault(); - var tcp = new Tcp(loop); - - /* - tcp.connect(new sys.net.Host("google.com"), 80, function(b) { - - log("Connected=" + b); - var bytes = haxe.io.Bytes.ofString("GET / HTTP/1.0\r\nHost: www.google.com\r\n\r\n"); - tcp.write(bytes, function(b) trace("Sent=" + b)); - var buf = new haxe.io.BytesBuffer(); - tcp.readStart(function(bytes:hl.Bytes, len) { - if( len < 0 ) { - var str = buf.getBytes().toString(); - if( str.length > 1000 ) - str = str.substr(0, 500) + "\n...\n" + str.substr(str.length - 500); - log("#" + str + "#"); - tcp.readStop(); - return; - } - log("Read="+len); - var bytes = bytes.toBytes(len); - buf.addBytes(bytes, 0, len); - }); - - }); - */ - - var host = new sys.net.Host("localhost"); - var port = 6001; - - var totR = 0, totW = 0, totRB = 0; - - log("Starting server"); - tcp.bind(host, port); - tcp.listen(5, function() { - - log("Client connected"); - var s = tcp.accept(); - s.readStart(function(bytes) { - if( bytes == null ) { - s.close(); - return; - } - totR += bytes.length; - // write back - s.write(bytes, function(b) if( !b ) throw "Write failure"); - }); - - }); - - - function startClient() { - - var numbers = []; - var client = new Tcp(loop); - //log("Connecting..."); - client.connect(host, port, function(b) { - //log("Connected to server"); - - - function send() { - var b = haxe.io.Bytes.alloc(1); - var k = Std.random(255); - numbers.push(k); - totW++; - b.set(0, k); - client.write(b, function(b) if( !b ) log("Write failure")); - } - - function sendBatch() { - for( i in 0...1+Std.random(10) ) - send(); - } - sendBatch(); - - client.readStart(function(b) { - totRB += b.length; - for( i in 0...b.length ) { - var k = b.get(i); - if( !numbers.remove(k) ) - throw "!"; - } - if( numbers.length == 0 ) { - if( Std.random(10000) == 0 ) { - startClient(); - client.close(); - } else - sendBatch(); - } - }); - - }); - - } - - for( i in 0...4 ) - startClient(); - - - log("Enter Loop"); - - var K = 0; - var maxRead = 1000000; - while( loop.run(NoWait) != 0 ) { - if( K++ % 10000 == 0 ) log("Read=" + totR); - - if( totR > maxRead ) { - log("Total Read > " + maxRead); - break; - } - } - - log("Done"); - Sys.exit(0); + TcpSample.main(); } } \ No newline at end of file From 244b858f74dd8be943b16877a7863feeb62aac68 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Wed, 28 Jul 2021 22:47:53 +0300 Subject: [PATCH 022/117] cleanup --- libs/uv/uv.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 8fc856bd2..d04e61ccd 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -299,13 +299,11 @@ static void on_close( uv_handle_t *h ) { } static void free_handle( uv_handle_t *h ) { - printf("free_handle\n"); uv_close(h, on_close); } HL_PRIM void HL_NAME(close_wrap)( uv_handle_t *h, vclosure *c ) { UV_CHECK_NULL(h,); - printf("close_wrap\n"); handle_register_callback(h, c, EVT_CLOSE); free_handle(h); } From 0bf0df3c0388a180c6cc769dbc7cc844741eafba Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Wed, 28 Jul 2021 22:56:09 +0300 Subject: [PATCH 023/117] update TcpSample --- other/uvsample/TcpSample.hx | 58 +++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/other/uvsample/TcpSample.hx b/other/uvsample/TcpSample.hx index fbac0c4b5..6e6b09ab6 100644 --- a/other/uvsample/TcpSample.hx +++ b/other/uvsample/TcpSample.hx @@ -1,4 +1,5 @@ import haxe.io.Bytes; +import haxe.PosInfos; import hl.uv.UVError; import haxe.Timer; import hl.uv.UVException; @@ -14,16 +15,26 @@ class TcpSample { Timer.delay(client,100); } - static function handle(success:()->Void):(e:UVError)->Void { + static function handle(success:()->Void, ?p:PosInfos):(e:UVError)->Void { return e -> switch e { case UV_NOERR: success(); - case _: throw new UVException(e); + case _: throw new UVException(e, p.fileName + ':' + p.lineNumber + ': ' + e.toString()); } } + static function shutdownAndClose(tcp:Tcp, print:(msg:String)->Void, ?onClose:()->Void) { + tcp.shutdown(_ -> { + tcp.close(() -> { + print('connection closed'); + if(onClose != null) + onClose(); + }); + }); + } + static function server() { - inline function print(msg:String) { - Log.print('SERVER: $msg'); + function print(msg:String) { + Sys.println('SERVER: $msg'); } var loop = Thread.current().events; var server = Tcp.init(loop, INET); @@ -32,20 +43,26 @@ class TcpSample { var client = Tcp.init(loop); server.accept(client); print('connection from ' + client.getSockName()); - client.readStart((e, data, bytesRead) -> handle(() -> { - print('incoming request: ' + data.toBytes(bytesRead).toString()); - client.write(data, bytesRead, handle(() -> { - client.shutdown(handle(() -> { - client.close(() -> print('client closed')); + client.readStart((e, data, bytesRead) -> switch e { + case UV_NOERR: + print('incoming request: ' + data.toBytes(bytesRead).toString()); + client.write(data, bytesRead, handle(() -> { + shutdownAndClose(client, print, () -> { + server.close(() -> print('done')); + }); })); - })); - })(e)); + case UV_EOF: + print('client disconnected'); + client.close(() -> print('connection closed')); + case _: + throw new UVException(e); + }); })); } static function client() { - inline function print(msg:String) { - Log.print('CLIENT: $msg'); + function print(msg:String) { + Sys.println('CLIENT: $msg'); } var loop = Thread.current().events; var client = Tcp.init(loop, INET); @@ -53,12 +70,15 @@ class TcpSample { print('connected to ' + client.getPeerName()); var data = Bytes.ofString('Hello, world!').getData(); client.write(data.bytes, data.length, handle(() -> { - client.readStart((e, data, bytesRead) -> handle(() -> { - print('response from server: ' + data.toBytes(bytesRead).toString()); - client.shutdown(handle(() -> { - client.close(() -> print('connection closed')); - })); - })(e)); + client.readStart((e, data, bytesRead) -> switch e { + case UV_NOERR: + print('response from server: ' + data.toBytes(bytesRead).toString()); + case UV_EOF: + print('disconnected from server'); + shutdownAndClose(client, print); + case _: + throw new UVException(e); + }); })); })); } From 38ba17f850d33e26a704c518b9ed3d92a3986c7e Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Thu, 29 Jul 2021 10:50:10 +0300 Subject: [PATCH 024/117] handle subsequent `uv_close()` --- libs/uv/uv.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index d04e61ccd..18ee4c866 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -304,6 +304,10 @@ static void free_handle( uv_handle_t *h ) { HL_PRIM void HL_NAME(close_wrap)( uv_handle_t *h, vclosure *c ) { UV_CHECK_NULL(h,); + if( uv_is_closing(h) ) { + hx_error(UV_ECANCELED); + return; + } handle_register_callback(h, c, EVT_CLOSE); free_handle(h); } From e7425eda10e07de131b55871914dff3cdb66b43c Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Thu, 29 Jul 2021 11:07:56 +0300 Subject: [PATCH 025/117] Revert "handle subsequent `uv_close()`" This reverts commit 38ba17f850d33e26a704c518b9ed3d92a3986c7e. --- libs/uv/uv.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 18ee4c866..d04e61ccd 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -304,10 +304,6 @@ static void free_handle( uv_handle_t *h ) { HL_PRIM void HL_NAME(close_wrap)( uv_handle_t *h, vclosure *c ) { UV_CHECK_NULL(h,); - if( uv_is_closing(h) ) { - hx_error(UV_ECANCELED); - return; - } handle_register_callback(h, c, EVT_CLOSE); free_handle(h); } From 744dba697febe99ab592e3cd9264ae49dad6f672 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Thu, 29 Jul 2021 14:40:30 +0300 Subject: [PATCH 026/117] update errno_uv2hx --- libs/uv/uv.c | 131 ++++++++++----------------------------------------- 1 file changed, 24 insertions(+), 107 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index d04e61ccd..c396b2a62 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -114,115 +114,32 @@ static int errno_uv2hx( int uv_errno ) { case UV_ENOTEMPTY: return 56; case UV_ENOTSOCK: return 57; case UV_ENOTSUP: return 58; - case UV_EPERM: return 59; - case UV_EPIPE: return 60; - case UV_EPROTO: return 61; - case UV_EPROTONOSUPPORT: return 62; - case UV_EPROTOTYPE: return 63; - case UV_ERANGE: return 64; - case UV_EROFS: return 65; - case UV_ESHUTDOWN: return 66; - case UV_ESPIPE: return 67; - case UV_ESRCH: return 68; - case UV_ETIMEDOUT: return 69; - case UV_ETXTBSY: return 70; - case UV_EXDEV: return 71; - case UV_UNKNOWN: return 72; - case UV_EOF: return 73; - case UV_ENXIO: return 74; - case UV_EMLINK: return 75; - case UV_EHOSTDOWN: return 76; - case UV_EREMOTEIO: return 77; - case UV_ENOTTY: return 78; - default: return 72; + // case UV_EOVERFLOW: return 59; + case UV_EPERM: return 60; + case UV_EPIPE: return 61; + case UV_EPROTO: return 62; + case UV_EPROTONOSUPPORT: return 63; + case UV_EPROTOTYPE: return 64; + case UV_ERANGE: return 65; + case UV_EROFS: return 66; + case UV_ESHUTDOWN: return 67; + case UV_ESPIPE: return 68; + case UV_ESRCH: return 69; + case UV_ETIMEDOUT: return 70; + case UV_ETXTBSY: return 71; + case UV_EXDEV: return 72; + case UV_UNKNOWN: return 73; + case UV_EOF: return 74; + case UV_ENXIO: return 75; + case UV_EMLINK: return 76; + case UV_ENOTTY: return 77; + case UV_EFTYPE: return 78; + case UV_EILSEQ: return 79; + // case UV_ESOCKTNOSUPPORT: return 80; + default: return 73; //UV_UNKNOWN } } -// static int errno_hx2uv( int hx_errno ) { -// switch(hx_errno) { -// case 0: return 0; -// case 1: return UV_E2BIG; -// case 2: return UV_EACCES; -// case 3: return UV_EADDRINUSE; -// case 4: return UV_EADDRNOTAVAIL; -// case 5: return UV_EAFNOSUPPORT; -// case 6: return UV_EAGAIN; -// case 7: return UV_EAI_ADDRFAMILY; -// case 8: return UV_EAI_AGAIN; -// case 9: return UV_EAI_BADFLAGS; -// case 10: return UV_EAI_BADHINTS; -// case 11: return UV_EAI_CANCELED; -// case 12: return UV_EAI_FAIL; -// case 13: return UV_EAI_FAMILY; -// case 14: return UV_EAI_MEMORY; -// case 15: return UV_EAI_NODATA; -// case 16: return UV_EAI_NONAME; -// case 17: return UV_EAI_OVERFLOW; -// case 18: return UV_EAI_PROTOCOL; -// case 19: return UV_EAI_SERVICE; -// case 20: return UV_EAI_SOCKTYPE; -// case 21: return UV_EALREADY; -// case 22: return UV_EBADF; -// case 23: return UV_EBUSY; -// case 24: return UV_ECANCELED; -// case 25: return UV_ECHARSET; -// case 26: return UV_ECONNABORTED; -// case 27: return UV_ECONNREFUSED; -// case 28: return UV_ECONNRESET; -// case 29: return UV_EDESTADDRREQ; -// case 30: return UV_EEXIST; -// case 31: return UV_EFAULT; -// case 32: return UV_EFBIG; -// case 33: return UV_EHOSTUNREACH; -// case 34: return UV_EINTR; -// case 35: return UV_EINVAL; -// case 36: return UV_EIO; -// case 37: return UV_EISCONN; -// case 38: return UV_EISDIR; -// case 39: return UV_ELOOP; -// case 40: return UV_EMFILE; -// case 41: return UV_EMSGSIZE; -// case 42: return UV_ENAMETOOLONG; -// case 43: return UV_ENETDOWN; -// case 44: return UV_ENETUNREACH; -// case 45: return UV_ENFILE; -// case 46: return UV_ENOBUFS; -// case 47: return UV_ENODEV; -// case 48: return UV_ENOENT; -// case 49: return UV_ENOMEM; -// case 50: return UV_ENONET; -// case 51: return UV_ENOPROTOOPT; -// case 52: return UV_ENOSPC; -// case 53: return UV_ENOSYS; -// case 54: return UV_ENOTCONN; -// case 55: return UV_ENOTDIR; -// case 56: return UV_ENOTEMPTY; -// case 57: return UV_ENOTSOCK; -// case 58: return UV_ENOTSUP; -// case 59: return UV_EPERM; -// case 60: return UV_EPIPE; -// case 61: return UV_EPROTO; -// case 62: return UV_EPROTONOSUPPORT; -// case 63: return UV_EPROTOTYPE; -// case 64: return UV_ERANGE; -// case 65: return UV_EROFS; -// case 66: return UV_ESHUTDOWN; -// case 67: return UV_ESPIPE; -// case 68: return UV_ESRCH; -// case 69: return UV_ETIMEDOUT; -// case 70: return UV_ETXTBSY; -// case 71: return UV_EXDEV; -// case 72: return UV_UNKNOWN; -// case 73: return UV_EOF; -// case 74: return UV_ENXIO; -// case 75: return UV_EMLINK; -// case 76: return UV_EHOSTDOWN; -// case 77: return UV_EREMOTEIO; -// case 78: return UV_ENOTTY; -// default: return UV_UNKNOWN; -// } -// } - static void hx_error(int uv_errno) { //TODO: throw hl.uv.UVException hl_error("%s", hl_to_utf16(uv_err_name(uv_errno))); @@ -745,7 +662,7 @@ DEFINE_PRIM(_I32, signal_get_sigNum_wrap, _HANDLE); HL_PRIM uv_sockaddr_storage *HL_NAME(ip4_addr_wrap)( vstring *ip, int port ) { UV_CHECK_NULL(ip,NULL); - uv_sockaddr_storage *addr = UV_ALLOC(uv_sockaddr_storage); + uv_sockaddr_storage *addr = UV_ALLOC(uv_sockaddr_storage); //register in hl gc? UV_CHECK_ERROR(uv_ip4_addr(hl_to_utf8(ip->bytes), port, (uv_sockaddr_in *)addr),free(addr),NULL); return addr; } From 32667ee4554e4887695bbceada89953b699e73fc Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Thu, 29 Jul 2021 14:40:46 +0300 Subject: [PATCH 027/117] request --- libs/uv/uv.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index c396b2a62..5fc9db3e7 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -34,6 +34,7 @@ typedef struct { #define _LOOP _ABSTRACT(uv_loop) #define _HANDLE _ABSTRACT(uv_handle) +#define _REQUEST _ABSTRACT(uv_req) #define _SOCKADDR _ABSTRACT(uv_sockaddr_storage) #define _CALLB _FUN(_VOID,_NO_ARG) #define UV_ALLOC(t) ((t*)malloc(sizeof(t))) @@ -178,6 +179,13 @@ static void free_req( uv_req_t *r ) { free(r); } +HL_PRIM void HL_NAME(cancel_wrap)( uv_req_t *r ) { + UV_CHECK_NULL(r,); + UV_CHECK_ERROR(uv_cancel(r),,); + free_req(r); +} +DEFINE_PRIM(_VOID, cancel_wrap, _REQUEST); + // HANDLE static events_data *handle_init_hl_data( uv_handle_t *h ) { From 8e7cfd85a2a852b25d36581a560750d4c6202159 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Thu, 29 Jul 2021 18:07:30 +0300 Subject: [PATCH 028/117] pipe --- libs/uv/uv.c | 126 +++++++++++++++++++++++++++++++++-- other/uvsample/PipeSample.hx | 98 +++++++++++++++++++++++++++ other/uvsample/TcpSample.hx | 5 +- other/uvsample/UVSample.hx | 1 + 4 files changed, 221 insertions(+), 9 deletions(-) create mode 100644 other/uvsample/PipeSample.hx diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 5fc9db3e7..eb2658e66 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -370,15 +370,29 @@ HL_PRIM int HL_NAME(try_write_wrap)( uv_stream_t *h, vbyte *b, int length ) { UV_CHECK_NULL(h,0); UV_CHECK_NULL(b,0); uv_buf_t buf = uv_buf_init((char *)b, length); - int result = uv_try_write(h,&buf,1); - if( result < 0 ) { - hx_error(result); - return 0; - } - return result; + UV_CHECK_ERROR(uv_try_write(h,&buf,1),,0); + return __result__; } DEFINE_PRIM(_I32, try_write_wrap, _HANDLE _BYTES _I32); +HL_PRIM void HL_NAME(write2_wrap)( uv_stream_t *h, vbyte *b, int length, uv_stream_t *send_handle, vclosure *c ) { + UV_CHECK_NULL(h,); + UV_CHECK_NULL(b,); + UV_CHECK_NULL(send_handle,); + UV_CHECK_NULL(c,); + uv_write_t *r = UV_ALLOC(uv_write_t); + events_data *d = req_init_hl_data((uv_req_t *)r); + // keep a copy of the data + uv_buf_t buf; + d->write_data = malloc(length); + memcpy(d->write_data,b,length); + buf.base = d->write_data; + buf.len = length; + req_register_callback((uv_req_t *)r,c,0); + UV_CHECK_ERROR(uv_write2(r,h,&buf,1,send_handle,on_write),free_req((uv_req_t *)r),); +} +DEFINE_PRIM(_VOID, write2_wrap, _HANDLE _BYTES _I32 _HANDLE _FUN(_VOID,_I32)); + HL_PRIM bool HL_NAME(is_readable_wrap)( uv_stream_t *h ) { UV_CHECK_NULL(h,false); return uv_is_readable(h) != 0; @@ -783,7 +797,7 @@ static void on_connect( uv_connect_t *r, int status ) { events_data *ev = UV_DATA(r); vclosure *c = ev ? ev->events[0] : NULL; if( !c ) - hl_fatal("No callback in tcp_connect request"); + hl_fatal("No callback in connect request"); hl_call1(void, c, int, errno_uv2hx(status)); free_req((uv_req_t *)r); } @@ -806,6 +820,104 @@ HL_PRIM void HL_NAME(tcp_close_reset_wrap)( uv_tcp_t *h, vclosure *c ) { } DEFINE_PRIM(_VOID, tcp_close_reset_wrap, _HANDLE _CALLB); +// PIPE + +HL_PRIM uv_pipe_t *HL_NAME(pipe_init_wrap)( uv_loop_t *loop, vdynamic *ipc ) { + UV_CHECK_NULL(loop,NULL); + uv_pipe_t *h = UV_ALLOC(uv_pipe_t); + UV_CHECK_ERROR(uv_pipe_init(loop,h,(ipc&&ipc->v.b)),free(h),NULL); + handle_init_hl_data((uv_handle_t*)h); + return h; +} +DEFINE_PRIM(_HANDLE, pipe_init_wrap, _LOOP _NULL(_BOOL)); + +HL_PRIM void HL_NAME(pipe_bind_wrap)( uv_pipe_t *h, vstring *name ) { + UV_CHECK_NULL(h,); + UV_CHECK_NULL(name,); + UV_CHECK_ERROR(uv_pipe_bind(h,hl_to_utf8(name->bytes)),,); +} +DEFINE_PRIM(_VOID, pipe_bind_wrap, _HANDLE _STRING); + +HL_PRIM vbyte *HL_NAME(pipe_getsockname_wrap)( uv_pipe_t *h ) { + UV_CHECK_NULL(h,NULL); + size_t size = 256; + vbyte *name = NULL; + int result = UV_ENOBUFS; + while (result == UV_ENOBUFS) { + name = hl_alloc_bytes(size); //free previousely allocated name bytes? + result = uv_pipe_getsockname(h,(char *)name,&size); + } + UV_CHECK_ERROR(result,,NULL); //free bytes? + return name; +} +DEFINE_PRIM(_BYTES, pipe_getsockname_wrap, _HANDLE); + +HL_PRIM vbyte *HL_NAME(pipe_getpeername_wrap)( uv_pipe_t *h ) { + UV_CHECK_NULL(h,NULL); + size_t size = 256; + vbyte *name = NULL; + int result = UV_ENOBUFS; + while (result == UV_ENOBUFS) { + name = hl_alloc_bytes(size); //free previousely allocated name bytes? + result = uv_pipe_getpeername(h,(char *)name,&size); + } + UV_CHECK_ERROR(result,,NULL); //free bytes? + return name; +} +DEFINE_PRIM(_BYTES, pipe_getpeername_wrap, _HANDLE); + +HL_PRIM void HL_NAME(pipe_connect_wrap)( uv_pipe_t *h, vstring *name, vclosure *c ) { + UV_CHECK_NULL(h,); + UV_CHECK_NULL(name,); + UV_CHECK_NULL(c,); + uv_connect_t *r = UV_ALLOC(uv_connect_t); + req_init_hl_data((uv_req_t *)r); + req_register_callback((uv_req_t *)r,c,0); + uv_pipe_connect(r, h,hl_to_utf8(name->bytes),on_connect); +} +DEFINE_PRIM(_VOID, pipe_connect_wrap, _HANDLE _STRING _FUN(_VOID,_I32)); + +HL_PRIM void HL_NAME(pipe_pending_instances_wrap)( uv_pipe_t *h, int count ) { + UV_CHECK_NULL(h,); + uv_pipe_pending_instances(h, count); +} +DEFINE_PRIM(_VOID, pipe_pending_instances_wrap, _HANDLE _I32); + +HL_PRIM int HL_NAME(pipe_pending_count_wrap)( uv_pipe_t *h ) { + UV_CHECK_NULL(h,0); + UV_CHECK_ERROR(uv_pipe_pending_count(h),,0); + return __result__; +} +DEFINE_PRIM(_I32, pipe_pending_count_wrap, _HANDLE); + +HL_PRIM int HL_NAME(pipe_pending_type_wrap)( uv_pipe_t *h ) { + UV_CHECK_NULL(h,0); + UV_CHECK_ERROR(uv_pipe_pending_type(h),,0); + switch( __result__ ) { + case UV_ASYNC: return 1; + case UV_CHECK: return 2; + case UV_FS_EVENT: return 3; + case UV_FS_POLL: return 4; + case UV_HANDLE: return 5; + case UV_IDLE: return 6; + case UV_NAMED_PIPE: return 7; + case UV_POLL: return 8; + case UV_PREPARE: return 9; + case UV_PROCESS: return 10; + case UV_STREAM: return 11; + case UV_TCP: return 12; + case UV_TIMER: return 13; + case UV_TTY: return 14; + case UV_UDP: return 15; + case UV_SIGNAL: return 16; + case UV_FILE: return 17; + case UV_UNKNOWN_HANDLE: + default: + return 0; + } +} +DEFINE_PRIM(_I32, pipe_pending_type_wrap, _HANDLE); + // loop HL_PRIM uv_loop_t *HL_NAME(loop_init_wrap)( ) { diff --git a/other/uvsample/PipeSample.hx b/other/uvsample/PipeSample.hx new file mode 100644 index 000000000..ef4e8b247 --- /dev/null +++ b/other/uvsample/PipeSample.hx @@ -0,0 +1,98 @@ +import hl.uv.SockAddr; +import hl.uv.Tcp; +import haxe.io.Bytes; +import hl.uv.Pipe; +import hl.uv.UVException; +import hl.uv.UVError; +import haxe.PosInfos; +import sys.thread.Thread; +import haxe.Timer; + +class PipeSample { + static inline var NAME = 'testpipe'; + + static public function main() { + #if CLIENT + Log.print('Running PipeSample client...'); + client(); + #else + Log.print('Running PipeSample server...'); + Log.print('waiting for connections'); + server(); + #end + } + + static function handle(success:()->Void, ?p:PosInfos):(e:UVError)->Void { + return e -> switch e { + case UV_NOERR: success(); + case _: throw new UVException(e, p.fileName + ':' + p.lineNumber + ': ' + e.toString()); + } + } + + static function server() { + function print(msg:String) + Log.print('SERVER: $msg'); + var loop = Thread.current().events; + var server = Pipe.init(loop); + server.bind(NAME); + server.listen(32, handle(() -> { + var client = Pipe.init(loop, true); + server.accept(client); + print('connection from ' + client.getSockName()); + var tcp = Tcp.init(loop, INET); + client.readStart((e, data, bytesRead) -> switch e { + case UV_NOERR: + print('incoming request: ' + data.toBytes(bytesRead).toString()); + var addr = SockAddr.ipv4('93.184.216.34', 80); //http://example.com + tcp.connect(addr, handle(() -> { + print('tcp connected to ' + addr); + client.write2(data, bytesRead, tcp, handle(() -> print('tcp sent'))); + })); + case UV_EOF: + print('client disconnected'); + tcp.close(() -> { + print('tcp closed'); + client.close(() -> { + print('pipe connection closed'); + server.close(() -> print('done')); + }); + }); + case _: + throw new UVException(e); + }); + })); + } + + static function client() { + function print(msg:String) + Log.print('CLIENT: $msg'); + var loop = Thread.current().events; + var client = Pipe.init(loop, true); + client.connect(NAME, handle(() -> { + print('connected to ' + client.getPeerName()); + var data = Bytes.ofString('Hello, world!').getData(); + client.write(data.bytes, data.length, handle(() -> { + var tcp = Tcp.init(loop); + client.readStart((e, data, bytesRead) -> switch e { + case UV_NOERR: + while(client.pendingCount() > 0) { + switch client.pendingType() { + case UV_TCP: + client.accept(tcp); + print('Received tcp socket connected to ' + tcp.getPeerName()); + case _: + throw 'Received unexpected handler type'; + } + } + print('response from server: ' + data.toBytes(bytesRead).toString()); + client.close(() -> print('pipe connection closed')); + case UV_EOF: + print('disconnected from server'); + client.close(() -> print('pipe connection closed')); + case _: + throw new UVException(e); + }); + })); + })); + } +} \ No newline at end of file diff --git a/other/uvsample/TcpSample.hx b/other/uvsample/TcpSample.hx index 6e6b09ab6..3f7acf166 100644 --- a/other/uvsample/TcpSample.hx +++ b/other/uvsample/TcpSample.hx @@ -11,6 +11,7 @@ class TcpSample { static inline var PORT = 22001; public static function main() { + Log.print('Running TcpSample...'); server(); Timer.delay(client,100); } @@ -34,7 +35,7 @@ class TcpSample { static function server() { function print(msg:String) { - Sys.println('SERVER: $msg'); + Log.print('SERVER: $msg'); } var loop = Thread.current().events; var server = Tcp.init(loop, INET); @@ -62,7 +63,7 @@ class TcpSample { static function client() { function print(msg:String) { - Sys.println('CLIENT: $msg'); + Log.print('CLIENT: $msg'); } var loop = Thread.current().events; var client = Tcp.init(loop, INET); diff --git a/other/uvsample/UVSample.hx b/other/uvsample/UVSample.hx index 131d9287b..d8106abd2 100644 --- a/other/uvsample/UVSample.hx +++ b/other/uvsample/UVSample.hx @@ -3,6 +3,7 @@ import hl.uv.*; class UVSample { static function main() { TcpSample.main(); + // PipeSample.main(); } } \ No newline at end of file From f3acb427700e3b7c49741e1dfcf9d70efb429470 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 30 Jul 2021 16:49:25 +0300 Subject: [PATCH 029/117] process --- libs/uv/uv.c | 161 ++++++++++++++++++++++++++++++++ other/uvsample/ProcessSample.hx | 30 ++++++ other/uvsample/UVSample.hx | 1 + 3 files changed, 192 insertions(+) create mode 100644 other/uvsample/ProcessSample.hx diff --git a/libs/uv/uv.c b/libs/uv/uv.c index eb2658e66..6c058e9c4 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -918,6 +918,167 @@ HL_PRIM int HL_NAME(pipe_pending_type_wrap)( uv_pipe_t *h ) { } DEFINE_PRIM(_I32, pipe_pending_type_wrap, _HANDLE); +// Process + +static void on_process_exit(uv_process_t *h, int64_t exit_status, int term_signal) { + events_data *ev = UV_DATA(h); + vclosure *c = ev ? ev->events[0] : NULL; + if( c ) { + hl_call3(void, c, uv_process_t *, h, int64_t, exit_status, int, signum_uv2hx(term_signal)); + handle_clear_callback((uv_handle_t *)h, 0); + } +} + +typedef struct { + hl_type *t; + int index; + int fd; +} stdio_fd; + +typedef struct { + hl_type *t; + int index; + uv_pipe_t *pipe; + int permissions; + vdynamic *nonBlock; +} stdio_pipe; + +typedef struct { + hl_type *t; + int index; + uv_stream_t *stream; +} stdio_stream; + +HL_PRIM uv_process_t *HL_NAME(spawn_wrap)( uv_loop_t *loop, vstring *file, varray *args, + vclosure *on_exit, varray *stdio, varray *env, vstring *cwd, vdynamic *uid, + vdynamic *gid, vdynamic *detached, vdynamic *windowsVerbatimArguments, vdynamic *windowsHide, + vdynamic *windowsHideConsole, vdynamic *windowsHideGui ) { + UV_CHECK_NULL(loop,NULL); + UV_CHECK_NULL(file,NULL); + UV_CHECK_NULL(args,NULL); + + uv_process_t *h = UV_ALLOC(uv_process_t); + + uv_process_options_t options = {0}; + options.file = hl_to_utf8(file->bytes); + options.exit_cb = on_process_exit; + + options.args = malloc(sizeof(char *) * (args->size + 1)); + for (int i = 0; i < args->size; i++) + options.args[i] = hl_to_utf8(hl_aptr(args, vstring *)[i]->bytes); + options.args[args->size] = NULL; + + if( env ) { + options.env = malloc(sizeof(char *) * (env->size + 1)); + for (int i = 0; i < env->size; i++) + options.env[i] = hl_to_utf8(hl_aptr(env, vstring *)[i]->bytes); + options.env[env->size] = NULL; + } + if( stdio ) { + options.stdio_count = stdio->size; + options.stdio = malloc(sizeof(uv_stdio_container_t) * stdio->size); + for (int i = 0; i < stdio->size; i++) { + venum *io = hl_aptr(stdio, venum *)[i]; + if( !io ) { + options.stdio[i].flags = UV_IGNORE; + continue; + } + /* + On Haxe side: + enum ProcessStdio { + IGNORE; + INHERIT; + FD(fd:StdioFd); + PIPE(pipe:Pipe, permissions:StdioPipePermissions, ?nonBlock:Bool); + STREAM(stream:Stream); + } + */ + stdio_pipe *cfg; + switch( io->index ) { + case 0: + options.stdio[i].flags = UV_IGNORE; + break; + case 1: + options.stdio[i].flags = UV_INHERIT_FD; + options.stdio[i].data.fd = i; + break; + case 2: + options.stdio[i].flags = UV_INHERIT_FD; + options.stdio[i].data.fd = ((stdio_fd *)io)->fd; + break; + case 3: + cfg = (stdio_pipe *)io; + UV_CHECK_NULL(cfg->pipe,NULL); + options.stdio[i].flags = UV_CREATE_PIPE; + switch( cfg->permissions ) { + case 1: options.stdio[i].flags |= UV_READABLE_PIPE; break; + case 2: options.stdio[i].flags |= UV_WRITABLE_PIPE; break; + case 3: + default: options.stdio[i].flags |= UV_READABLE_PIPE | UV_WRITABLE_PIPE; break; + } + if( cfg->nonBlock && cfg->nonBlock->v.b ) + options.stdio[i].flags |= UV_OVERLAPPED_PIPE; + options.stdio[i].data.stream = (uv_stream_t *)cfg->pipe; + break; + case 4: + UV_CHECK_NULL(((stdio_stream *)io)->stream,NULL); + options.stdio[i].flags = UV_INHERIT_STREAM; + options.stdio[i].data.stream = ((stdio_stream *)io)->stream; + break; + default: + options.stdio[i].flags = UV_IGNORE; + break; + } + } + } + + if( cwd ) + options.cwd = hl_to_utf8(cwd->bytes); + if(uid) { + options.uid = uid->v.i; + options.flags |= UV_PROCESS_SETUID; + } + if(gid) { + options.gid = gid->v.i; + options.flags |= UV_PROCESS_SETGID; + } + if(detached && detached->v.b) + options.flags |= UV_PROCESS_DETACHED; + if(windowsVerbatimArguments && windowsVerbatimArguments->v.b) + options.flags |= UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS; + if(windowsHide && windowsHide->v.b) + options.flags |= UV_PROCESS_WINDOWS_HIDE; + if(windowsHideConsole && windowsHideConsole->v.b) + options.flags |= UV_PROCESS_WINDOWS_HIDE_CONSOLE; + if(windowsHideGui && windowsHideGui->v.b) + options.flags |= UV_PROCESS_WINDOWS_HIDE_GUI; + + UV_CHECK_ERROR(uv_spawn(loop,h,&options),free(h),NULL); // free options? + handle_init_hl_data((uv_handle_t*)h); + if( on_exit ) + handle_register_callback((uv_handle_t *)h, on_exit, 0); + return h; +} +DEFINE_PRIM(_HANDLE, spawn_wrap, _LOOP _STRING _ARR _FUN(_VOID, _HANDLE _I64 _I32) _ARR _ARR _STRING _NULL(_I32) _NULL(_I32) _NULL(_BOOL) _NULL(_BOOL) _NULL(_BOOL) _NULL(_BOOL) _NULL(_BOOL)); + +HL_PRIM int HL_NAME(process_pid)( uv_process_t *h ) { + return h->pid; +} +DEFINE_PRIM(_I32, process_pid, _HANDLE); + +DEFINE_PRIM(_VOID, disable_stdio_inheritance, _NO_ARG); + +HL_PRIM void HL_NAME(process_kill_wrap)( uv_process_t *h, int signum ) { + UV_CHECK_NULL(h,); + UV_CHECK_ERROR(uv_process_kill(h, signum_hx2uv(signum)),,); +} +DEFINE_PRIM(_VOID, process_kill_wrap, _HANDLE _I32); + +HL_PRIM void HL_NAME(kill_wrap)( int pid, int signum ) { + UV_CHECK_ERROR(uv_kill(pid, signum_hx2uv(signum)),,); +} +DEFINE_PRIM(_VOID, kill_wrap, _I32 _I32); + // loop HL_PRIM uv_loop_t *HL_NAME(loop_init_wrap)( ) { diff --git a/other/uvsample/ProcessSample.hx b/other/uvsample/ProcessSample.hx new file mode 100644 index 000000000..5341209ca --- /dev/null +++ b/other/uvsample/ProcessSample.hx @@ -0,0 +1,30 @@ +import hl.uv.Process; +import sys.thread.Thread; + +class ProcessSample { + public static function main() { + // Process.disableStdioInheritance(); + + var env = Sys.environment(); + env.set('DUMMY', '123'); + + var cmd = switch Sys.systemName() { + case 'Windows': 'dir'; + case _: 'ls'; + } + + var opt:ProcessOptions = { + cwd: '..', + stdio: [INHERIT, INHERIT, INHERIT], + env: env, + onExit: (p, exitCode, termSignal) -> { + Log.print('process finished: exitCode $exitCode, termSignal $termSignal'); + p.close(() -> Log.print('process closed')); + }, + } + var p = Process.spawn(Thread.current().events, cmd, [cmd], opt); + Log.print('pid ${p.pid}'); + // p.kill(SIGINT); + // Process.killPid(p.pid, SIGINT); + } +} \ No newline at end of file diff --git a/other/uvsample/UVSample.hx b/other/uvsample/UVSample.hx index d8106abd2..e6e96a40e 100644 --- a/other/uvsample/UVSample.hx +++ b/other/uvsample/UVSample.hx @@ -4,6 +4,7 @@ class UVSample { static function main() { TcpSample.main(); // PipeSample.main(); + // ProcessSample.main(); } } \ No newline at end of file From f1e5defb999b4d455350c6b47d7da6259181eef0 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Sat, 31 Jul 2021 15:02:47 +0300 Subject: [PATCH 030/117] udp --- libs/uv/uv.c | 232 ++++++++++++++++++++++++++++++++++-- other/uvsample/UVSample.hx | 1 + other/uvsample/UdpSample.hx | 55 +++++++++ src/hl.h | 6 +- 4 files changed, 285 insertions(+), 9 deletions(-) create mode 100644 other/uvsample/UdpSample.hx diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 6c058e9c4..fa2c519f5 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -682,6 +682,18 @@ DEFINE_PRIM(_I32, signal_get_sigNum_wrap, _HANDLE); // Sockaddr +/** + * Convert `hl.uv.SockAddr.AddressFamily` values to corresponding AF_* values + */ +static int address_family(int hx_address_family) { + switch( hx_address_family ) { + case -1: return AF_UNSPEC; + case -2: return AF_INET; + case -3: return AF_INET6; + default: return hx_address_family; + } +} + HL_PRIM uv_sockaddr_storage *HL_NAME(ip4_addr_wrap)( vstring *ip, int port ) { UV_CHECK_NULL(ip,NULL); uv_sockaddr_storage *addr = UV_ALLOC(uv_sockaddr_storage); //register in hl gc? @@ -735,13 +747,7 @@ HL_PRIM uv_tcp_t *HL_NAME(tcp_init_wrap)( uv_loop_t *loop, vdynamic *domain ) { if( !domain ) { UV_CHECK_ERROR(uv_tcp_init(loop,h),free(h),NULL); } else { - int d = domain->v.i; - //convert `hl.uv.SockAddr.AddressFamily` values to native ones - switch( d ) { - case -1: d = AF_UNSPEC; break; - case -2: d = AF_INET; break; - case -3: d = AF_INET6; break; - } + int d = address_family(domain->v.i); UV_CHECK_ERROR(uv_tcp_init_ex(loop,h,d),free_handle((uv_handle_t *)h),NULL); } handle_init_hl_data((uv_handle_t*)h); @@ -1079,6 +1085,218 @@ HL_PRIM void HL_NAME(kill_wrap)( int pid, int signum ) { } DEFINE_PRIM(_VOID, kill_wrap, _I32 _I32); +// UDP + +HL_PRIM uv_udp_t *HL_NAME(udp_init_wrap)( uv_loop_t *loop, vdynamic *domain, vdynamic *recvmmsg ) { + UV_CHECK_NULL(loop,NULL); + uv_udp_t *h = UV_ALLOC(uv_udp_t); + if( !domain && !recvmmsg ) { + UV_CHECK_ERROR(uv_udp_init(loop,h),free(h),NULL); + } else { + int flags = 0; + if( domain ) + flags |= address_family(domain->v.i); + if( recvmmsg && recvmmsg->v.b ) + flags |= UV_UDP_RECVMMSG; + UV_CHECK_ERROR(uv_udp_init_ex(loop,h,flags),free_handle((uv_handle_t *)h),NULL); + } + handle_init_hl_data((uv_handle_t*)h); + return h; +} +DEFINE_PRIM(_HANDLE, udp_init_wrap, _LOOP _NULL(_I32) _NULL(_BOOL)); + +HL_PRIM void HL_NAME(udp_bind_wrap)( uv_udp_t *h, uv_sockaddr_storage *addr, vdynamic *ipv6_only, vdynamic *reuse_addr ) { + UV_CHECK_NULL(h,); + UV_CHECK_NULL(addr,); + int flags = 0; + if( ipv6_only && ipv6_only->v.b ) + flags |= UV_UDP_IPV6ONLY; + if( reuse_addr && reuse_addr->v.b ) + flags |= UV_UDP_REUSEADDR; + UV_CHECK_ERROR(uv_udp_bind(h,(uv_sockaddr *)addr,flags),,); +} +DEFINE_PRIM(_VOID, udp_bind_wrap, _HANDLE _SOCKADDR _NULL(_BOOL) _NULL(_BOOL)); + +HL_PRIM void HL_NAME(udp_connect_wrap)( uv_udp_t *h, uv_sockaddr_storage *addr ) { + UV_CHECK_NULL(h,); + UV_CHECK_ERROR(uv_udp_connect(h,(uv_sockaddr *)addr),,); +} +DEFINE_PRIM(_VOID, udp_connect_wrap, _HANDLE _SOCKADDR _NULL(_BOOL)); + +HL_PRIM uv_sockaddr_storage *HL_NAME(udp_getsockname_wrap)( uv_udp_t *h ) { + UV_CHECK_NULL(h,NULL); + uv_sockaddr_storage *addr = UV_ALLOC(uv_sockaddr_storage); + int size = sizeof(uv_sockaddr_storage); + UV_CHECK_ERROR(uv_udp_getsockname(h,(uv_sockaddr *)addr,&size),free(addr),NULL); + return addr; +} +DEFINE_PRIM(_SOCKADDR, udp_getsockname_wrap, _HANDLE); + +HL_PRIM uv_sockaddr_storage *HL_NAME(udp_getpeername_wrap)( uv_udp_t *h ) { + UV_CHECK_NULL(h,NULL); + uv_sockaddr_storage *addr = UV_ALLOC(uv_sockaddr_storage); + int size = sizeof(uv_sockaddr_storage); + UV_CHECK_ERROR(uv_udp_getpeername(h,(uv_sockaddr *)addr,&size),free(addr),NULL); + return addr; +} +DEFINE_PRIM(_SOCKADDR, udp_getpeername_wrap, _HANDLE); + +static uv_membership udp_membership( int hx_membership ) { + switch( hx_membership ) { + case 0: return UV_LEAVE_GROUP; + case 1: return UV_JOIN_GROUP; + default: + hl_fatal("Unexpected UdpMembership value"); + return hx_membership; + } +} + +HL_PRIM void HL_NAME(udp_set_membership_wrap)( uv_udp_t *h, vstring *multicast_addr, vstring *interface_addr, int membership ) { + UV_CHECK_NULL(h,); + UV_CHECK_NULL(multicast_addr,); + UV_CHECK_NULL(interface_addr,); + char *m_addr = hl_to_utf8(multicast_addr->bytes); + char *i_addr = hl_to_utf8(interface_addr->bytes); + uv_membership m = udp_membership(membership); + UV_CHECK_ERROR(uv_udp_set_membership(h,m_addr,i_addr,m),,); +} +DEFINE_PRIM(_VOID, udp_set_membership_wrap, _HANDLE _STRING _STRING _STRING _I32); + +HL_PRIM void HL_NAME(udp_set_source_membership_wrap)( uv_udp_t *h, vstring *multicast_addr, vstring *interface_addr, vstring *source_addr, int membership ) { + UV_CHECK_NULL(h,); + UV_CHECK_NULL(multicast_addr,); + UV_CHECK_NULL(interface_addr,); + UV_CHECK_NULL(source_addr,); + char *m_addr = hl_to_utf8(multicast_addr->bytes); + char *i_addr = hl_to_utf8(interface_addr->bytes); + char *s_addr = hl_to_utf8(source_addr->bytes); + uv_membership m = udp_membership(membership); + UV_CHECK_ERROR(uv_udp_set_source_membership(h,m_addr,i_addr,s_addr,m),,); +} +DEFINE_PRIM(_VOID, udp_set_source_membership_wrap, _HANDLE _STRING _STRING _STRING _I32); + +HL_PRIM void HL_NAME(udp_set_multicast_loop_wrap)( uv_udp_t *h, bool on ) { + UV_CHECK_NULL(h,); + UV_CHECK_ERROR(uv_udp_set_multicast_loop(h,on),,); +} +DEFINE_PRIM(_VOID, udp_set_multicast_loop_wrap, _HANDLE _BOOL); + +HL_PRIM void HL_NAME(udp_set_multicast_ttl_wrap)( uv_udp_t *h, int ttl ) { + UV_CHECK_NULL(h,); + UV_CHECK_ERROR(uv_udp_set_multicast_ttl(h,ttl),,); +} +DEFINE_PRIM(_VOID, udp_set_multicast_ttl_wrap, _HANDLE _I32); + +HL_PRIM void HL_NAME(udp_set_multicast_interface_wrap)( uv_udp_t *h, vstring *interface_addr ) { + UV_CHECK_NULL(h,); + UV_CHECK_NULL(interface_addr,); + UV_CHECK_ERROR(uv_udp_set_multicast_interface(h,hl_to_utf8(interface_addr->bytes)),,); +} +DEFINE_PRIM(_VOID, udp_set_multicast_interface_wrap, _HANDLE _STRING); + +HL_PRIM void HL_NAME(udp_set_broadcast_wrap)( uv_udp_t *h, bool on ) { + UV_CHECK_NULL(h,); + UV_CHECK_ERROR(uv_udp_set_broadcast(h,on),,); +} +DEFINE_PRIM(_VOID, udp_set_broadcast_wrap, _HANDLE _BOOL); + +HL_PRIM void HL_NAME(udp_set_ttl_wrap)( uv_udp_t *h, int ttl ) { + UV_CHECK_NULL(h,); + UV_CHECK_ERROR(uv_udp_set_ttl(h,ttl),,); +} +DEFINE_PRIM(_VOID, udp_set_ttl_wrap, _HANDLE _I32); + +static void on_udp_send( uv_udp_send_t *r, int status ) { + events_data *ev = UV_DATA(r); + vclosure *c = ev ? ev->events[0] : NULL; + if( !c ) + hl_fatal("No callback in udp send request"); + hl_call1(void, c, int, status < 0 ? errno_uv2hx(status) : 0); + free_req((uv_req_t *)r); +} + +HL_PRIM void HL_NAME(udp_send_wrap)( uv_udp_t *h, vbyte *data, int length, uv_sockaddr_storage *addr, vclosure *c ) { + UV_CHECK_NULL(h,); + UV_CHECK_NULL(c,); + uv_udp_send_t *r = UV_ALLOC(uv_udp_send_t); + events_data *d = req_init_hl_data((uv_req_t *)r); + uv_buf_t buf; + d->write_data = malloc(length); + memcpy(d->write_data,data,length); + buf.base = d->write_data; + buf.len = length; + req_register_callback((uv_req_t *)r,c,0); + UV_CHECK_ERROR(uv_udp_send(r,h,&buf,1,(uv_sockaddr *)addr,on_udp_send),free_req((uv_req_t *)r),); +} +DEFINE_PRIM(_VOID, udp_send_wrap, _HANDLE _BYTES _I32 _SOCKADDR _FUN(_VOID,_I32)); + +HL_PRIM int HL_NAME(udp_try_send_wrap)( uv_udp_t *h, vbyte *data, int length, uv_sockaddr_storage *addr ) { + UV_CHECK_NULL(h,0); + uv_buf_t buf = uv_buf_init((char *)data, length); + UV_CHECK_ERROR(uv_udp_try_send(h,&buf,1,(uv_sockaddr *)addr),,0); + return __result__; +} +DEFINE_PRIM(_I32, udp_try_send_wrap, _HANDLE _BYTES _I32 _SOCKADDR); + +static void on_udp_recv( uv_udp_t *h, ssize_t nread, const uv_buf_t *buf, const uv_sockaddr *src_addr, unsigned flags ) { + events_data *ev = UV_DATA(h); + vclosure *c = ev ? ev->events[0] : NULL; + if( !c ) + hl_fatal("No recv callback in udp handle"); + + uv_sockaddr_storage *addr = NULL; + if( src_addr ) { + addr = UV_ALLOC(uv_sockaddr_storage); + memcpy(addr, src_addr, sizeof(uv_sockaddr_storage)); + } + + vdynamic *hx_flags = (vdynamic*)hl_alloc_dynobj(); + hl_dyn_seti(hx_flags, hl_hash_utf8("mmsgChunk"), &hlt_bool, 0 != (flags & UV_UDP_MMSG_CHUNK)); + hl_dyn_seti(hx_flags, hl_hash_utf8("mmsgFree"), &hlt_bool, 0 != (flags & UV_UDP_MMSG_FREE)); + hl_dyn_seti(hx_flags, hl_hash_utf8("partial"), &hlt_bool, 0 != (flags & UV_UDP_PARTIAL)); + + if( nread < 0 ) { + hl_call5(void, c, int, errno_uv2hx(nread), vbyte *, NULL, int, 0, uv_sockaddr_storage *, addr, vdynamic *, hx_flags); + } else { + hl_call5(void, c, int, 0, vbyte *, (vbyte *)buf->base, int, nread, uv_sockaddr_storage *, addr, vdynamic *, hx_flags); + } + if( (nread <= 0 && !addr) || (flags & UV_UDP_MMSG_FREE) ) + free(buf->base); +} + +HL_PRIM void HL_NAME(udp_recv_start_wrap)( uv_udp_t *h, vclosure *c ) { + UV_CHECK_NULL(h,); + UV_CHECK_NULL(c,); + handle_register_callback((uv_handle_t *)h,c,0); + UV_CHECK_ERROR(uv_udp_recv_start(h,on_alloc,on_udp_recv),handle_clear_callback((uv_handle_t*)h,0),); +} +DEFINE_PRIM(_VOID, udp_recv_start_wrap, _HANDLE _FUN(_VOID,_I32 _BYTES _I32 _SOCKADDR _DYN)); + +HL_PRIM bool HL_NAME(udp_using_recvmmsg_wrap)( uv_udp_t *h ) { + UV_CHECK_NULL(h,false); + UV_CHECK_ERROR(uv_udp_using_recvmmsg(h),,false); + return __result__ == 1; +} +DEFINE_PRIM(_BOOL, udp_using_recvmmsg_wrap, _HANDLE); + +HL_PRIM void HL_NAME(udp_recv_stop_wrap)( uv_udp_t *h ) { + UV_CHECK_NULL(h,); + UV_CHECK_ERROR(uv_udp_recv_stop(h),,); +} +DEFINE_PRIM(_VOID, udp_recv_stop_wrap, _HANDLE); + +HL_PRIM int HL_NAME(udp_get_send_queue_size_wrap)( uv_udp_t *h ) { + UV_CHECK_NULL(h,0); + return uv_udp_get_send_queue_size(h); +} +DEFINE_PRIM(_I32, udp_get_send_queue_size_wrap, _HANDLE); + +HL_PRIM int HL_NAME(udp_get_send_queue_count_wrap)( uv_udp_t *h ) { + UV_CHECK_NULL(h,0); + return uv_udp_get_send_queue_count(h); +} +DEFINE_PRIM(_I32, udp_get_send_queue_count_wrap, _HANDLE); + // loop HL_PRIM uv_loop_t *HL_NAME(loop_init_wrap)( ) { diff --git a/other/uvsample/UVSample.hx b/other/uvsample/UVSample.hx index e6e96a40e..a850a8031 100644 --- a/other/uvsample/UVSample.hx +++ b/other/uvsample/UVSample.hx @@ -3,6 +3,7 @@ import hl.uv.*; class UVSample { static function main() { TcpSample.main(); + // UdpSample.main(); // PipeSample.main(); // ProcessSample.main(); } diff --git a/other/uvsample/UdpSample.hx b/other/uvsample/UdpSample.hx new file mode 100644 index 000000000..6ff2eb69c --- /dev/null +++ b/other/uvsample/UdpSample.hx @@ -0,0 +1,55 @@ +import haxe.Timer; +import haxe.io.Bytes; +import hl.uv.UVException; +import hl.uv.SockAddr; +import hl.uv.Udp; +import sys.thread.Thread; + +class UdpSample { + static inline var PORT = 22002; + + public static function main() { + Log.print('Running UdpSample...'); + server(); + Timer.delay(client,100); + } + + static function server() { + function print(msg:String) { + Log.print('RECEIVER: $msg'); + } + var loop = Thread.current().events; + var udp = Udp.init(loop, INET,true); + udp.bind(SockAddr.ipv4('0.0.0.0', PORT)); + var cnt = 0; + udp.recvStart((e, data, bytesRead, addr, flags) -> switch e { + case UV_NOERR: + var msg = data.toBytes(bytesRead).toString(); + if(bytesRead > 0) { + print('Received message from $addr: $msg'); + } else { + print('recv callback invocation with addr = $addr'); + if(addr == null && !flags.mmsgChunk) + udp.close(() -> print('Done')); + } + print('...with flags $flags'); + case _: + throw new UVException(e); + }); + } + + static function client() { + function print(msg:String) { + Log.print('SENDER: $msg'); + } + var udp = Udp.init(Thread.current().events, INET); + var data = Bytes.ofString('Hello, UDP!'); + udp.send(data.getData(), data.length, SockAddr.ipv4('127.0.0.1', PORT), e -> switch e { + case UV_NOERR: + print('Message sent'); + udp.close(() -> print('Done')); + case _: + throw new UVException(e); + }); + } +} \ No newline at end of file diff --git a/src/hl.h b/src/hl.h index ee271ac23..e534c2ceb 100644 --- a/src/hl.h +++ b/src/hl.h @@ -657,7 +657,7 @@ HL_API vdynamic *hl_dyn_call_safe( vclosure *c, vdynamic **args, int nargs, bool so you are sure it's of the used typed. Otherwise use hl_dyn_call */ #define hl_call0(ret,cl) \ - (cl->hasValue ? ((ret(*)(vdynamic*))cl->fun)(cl->value) : ((ret(*)())cl->fun)()) + (cl->hasValue ? ((ret(*)(vdynamic*))cl->fun)(cl->value) : ((ret(*)())cl->fun)()) #define hl_call1(ret,cl,t,v) \ (cl->hasValue ? ((ret(*)(vdynamic*,t))cl->fun)(cl->value,v) : ((ret(*)(t))cl->fun)(v)) #define hl_call2(ret,cl,t1,v1,t2,v2) \ @@ -666,6 +666,8 @@ HL_API vdynamic *hl_dyn_call_safe( vclosure *c, vdynamic **args, int nargs, bool (cl->hasValue ? ((ret(*)(vdynamic*,t1,t2,t3))cl->fun)(cl->value,v1,v2,v3) : ((ret(*)(t1,t2,t3))cl->fun)(v1,v2,v3)) #define hl_call4(ret,cl,t1,v1,t2,v2,t3,v3,t4,v4) \ (cl->hasValue ? ((ret(*)(vdynamic*,t1,t2,t3,t4))cl->fun)(cl->value,v1,v2,v3,v4) : ((ret(*)(t1,t2,t3,t4))cl->fun)(v1,v2,v3,v4)) +#define hl_call5(ret,cl,t1,v1,t2,v2,t3,v3,t4,v4,t5,v5) \ + (cl->hasValue ? ((ret(*)(vdynamic*,t1,t2,t3,t4,t5))cl->fun)(cl->value,v1,v2,v3,v4,v5) : ((ret(*)(t1,t2,t3,t4,t5))cl->fun)(v1,v2,v3,v4,v5)) // ----------------------- THREADS -------------------------------------------------- @@ -900,7 +902,7 @@ typedef struct { HL_API hl_track_info hl_track; -#else +#else #define hl_is_tracking(_) false #define hl_track_call(a,b) From ebbaddb7105101a1f0001647fbeb06b3c5b2a253 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Sun, 1 Aug 2021 18:18:18 +0300 Subject: [PATCH 031/117] DNS --- libs/uv/uv.c | 166 +++++++++++++++++++++++++++++++++++- other/uvsample/DnsSample.hx | 31 +++++++ other/uvsample/UVSample.hx | 4 +- 3 files changed, 196 insertions(+), 5 deletions(-) create mode 100644 other/uvsample/DnsSample.hx diff --git a/libs/uv/uv.c b/libs/uv/uv.c index fa2c519f5..c4285512b 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -710,7 +710,7 @@ HL_PRIM uv_sockaddr_storage *HL_NAME(ip6_addr_wrap)( vstring *ip, int port ) { } DEFINE_PRIM(_SOCKADDR, ip6_addr_wrap, _STRING _I32); -HL_PRIM vdynamic *HL_NAME(get_port)( uv_sockaddr_storage *addr ) { +HL_PRIM vdynamic *HL_NAME(sockaddr_get_port)( uv_sockaddr_storage *addr ) { UV_CHECK_NULL(addr,NULL); int port; if( addr->ss_family == AF_INET ) { @@ -722,7 +722,15 @@ HL_PRIM vdynamic *HL_NAME(get_port)( uv_sockaddr_storage *addr ) { } return hl_make_dyn(&port, &hlt_i32); } -DEFINE_PRIM(_NULL(_I32), get_port, _SOCKADDR); +DEFINE_PRIM(_NULL(_I32), sockaddr_get_port, _SOCKADDR); + +HL_PRIM uv_sockaddr_storage *HL_NAME(sockaddr_cast_ptr)( vdynamic *ptr ) { + UV_CHECK_NULL(ptr,NULL); + if( ptr->t->kind != HABSTRACT || 0 != memcmp(ptr->t->abs_name,USTR("uv_sockaddr_storage"),38) ) + hl_error("Invalid usage of hl.uv.SockAddr.castPtr()"); + return (uv_sockaddr_storage *)ptr->v.ptr; +} +DEFINE_PRIM(_SOCKADDR, sockaddr_cast_ptr, _DYN); //How to return vstring instead of vbyte? HL_PRIM vbyte *HL_NAME(ip_name_wrap)( uv_sockaddr_storage *addr ) { @@ -1297,6 +1305,160 @@ HL_PRIM int HL_NAME(udp_get_send_queue_count_wrap)( uv_udp_t *h ) { } DEFINE_PRIM(_I32, udp_get_send_queue_count_wrap, _HANDLE); +// DNS + +// typedef struct { +// int family; +// int sockType; +// int protocol; +// uv_sockaddr_storage *addr; +// vstring *canonName; +// } hx_addrinfo + +static void on_getaddrinfo( uv_getaddrinfo_t *r, int status, struct addrinfo *res ) { + events_data *ev = UV_DATA(r); + vclosure *c = ev ? ev->events[0] : NULL; + if( !c ) + hl_fatal("No callback in getaddrinfo request"); + + int count = 0; + struct addrinfo *current = res; + while( current ) { + ++count; + current = current->ai_next; + } + + varray *addresses = hl_alloc_array(&hlt_dyn, count); + current = res; + int hfamily = hl_hash_utf8("family"); + int hsockType = hl_hash_utf8("sockType"); + int hprotocol = hl_hash_utf8("protocol"); + int haddr = hl_hash_utf8("addr"); + int hcanonName = hl_hash_utf8("canonName"); + int i = 0; + hl_type hlt_sockaddr = { HABSTRACT, {USTR("uv_sockaddr_storage")} }; + vdynamic *entry; + while( current ) { + entry = (vdynamic *)hl_alloc_dynobj(); + uv_sockaddr_storage *addr = UV_ALLOC(uv_sockaddr_storage); + memcpy(addr, current->ai_addr, current->ai_addrlen); + hl_dyn_setp(entry,haddr,&hlt_sockaddr,addr); + hl_dyn_seti(entry,hprotocol,&hlt_i32,current->ai_protocol); + if( current->ai_canonname ) { + vbyte *canonname = hl_copy_bytes((vbyte *)current->ai_canonname, strlen(current->ai_canonname)); + hl_dyn_setp(entry,hcanonName,&hlt_bytes,canonname); + } + int family; + switch( current->ai_family ) { + case PF_UNSPEC: family = -1; break; + case PF_INET: family = -2; break; + case PF_INET6: family = -3; break; + default: family = current->ai_family; break; + } + hl_dyn_seti(entry,hfamily,&hlt_i32,family); + int socktype; + switch( current->ai_socktype ) { + case SOCK_STREAM: socktype = -1; break; + case SOCK_DGRAM: socktype = -2; break; + case SOCK_RAW: socktype = -3; break; + default: socktype = current->ai_socktype; break; + } + hl_dyn_seti(entry,hsockType,&hlt_i32,socktype); + hl_aptr(addresses, vdynamic*)[i] = entry; + current = current->ai_next; + ++i; + } + freeaddrinfo(res); + + hl_call2(void,c,int,errno_uv2hx(status),varray *,addresses); + free_req((uv_req_t *)r); +} + +HL_PRIM void HL_NAME(getaddrinfo_wrap)( uv_loop_t *l, vstring *name, vstring *service, vdynamic *flags, + vdynamic *family, vdynamic *socktype, vdynamic *protocol, vclosure *c ) { + UV_CHECK_NULL(l,); + UV_CHECK_NULL(( name || service ),); + UV_CHECK_NULL(c,); + uv_getaddrinfo_t *r = UV_ALLOC(uv_getaddrinfo_t); + req_init_hl_data((uv_req_t *)r); + req_register_callback((uv_req_t *)r,c,0); + char *c_name = name ? hl_to_utf8(name->bytes) : NULL; + char *c_service = service ? hl_to_utf8(service->bytes) : NULL; + struct addrinfo hints = {0}; + if( family ) + switch( family->v.i ) { + case -1: hints.ai_family = PF_UNSPEC; break; + case -2: hints.ai_family = PF_INET; break; + case -3: hints.ai_family = PF_INET6; break; + default: hints.ai_family = family->v.i; break; + } + if( socktype ) + switch( socktype->v.i ) { + case -1: hints.ai_socktype = SOCK_STREAM; break; + case -2: hints.ai_socktype = SOCK_DGRAM; break; + case -3: hints.ai_socktype = SOCK_RAW; break; + default: hints.ai_socktype = socktype->v.i; break; + } + if( protocol ) + hints.ai_protocol = protocol->v.i; + if( flags ) { + if( hl_dyn_geti(flags,hl_hash_utf8("passive"),&hlt_bool) ) + hints.ai_flags |= AI_PASSIVE; + if( hl_dyn_geti(flags,hl_hash_utf8("canonName"),&hlt_bool) ) + hints.ai_flags |= AI_CANONNAME; + if( hl_dyn_geti(flags,hl_hash_utf8("numericHost"),&hlt_bool) ) + hints.ai_flags |= AI_NUMERICHOST; + if( hl_dyn_geti(flags,hl_hash_utf8("numericServ"),&hlt_bool) ) + hints.ai_flags |= AI_NUMERICSERV; + if( hl_dyn_geti(flags,hl_hash_utf8("v4Mapped"),&hlt_bool) ) + hints.ai_flags |= AI_V4MAPPED; + if( hl_dyn_geti(flags,hl_hash_utf8("all"),&hlt_bool) ) + hints.ai_flags |= AI_ALL; + if( hl_dyn_geti(flags,hl_hash_utf8("addrConfig"),&hlt_bool) ) + hints.ai_flags |= AI_ADDRCONFIG; + } + UV_CHECK_ERROR(uv_getaddrinfo(l,r,on_getaddrinfo,c_name,c_service,&hints),free_req((uv_req_t *)r),); +} +DEFINE_PRIM(_VOID, getaddrinfo_wrap, _LOOP _STRING _STRING _DYN _NULL(_I32) _NULL(_I32) _NULL(_I32) _FUN(_VOID,_I32 _ARR)); + +static void on_getnameinfo( uv_getnameinfo_t *r, int status, const char *hostname, const char *service ) { + events_data *ev = UV_DATA(r); + vclosure *c = ev ? ev->events[0] : NULL; + if( !c ) + hl_fatal("No callback in getaddrinfo request"); + vbyte * bhost = NULL; + if( hostname ) + bhost = hl_copy_bytes((const vbyte *)hostname, strlen(hostname)); + vbyte * bservice = NULL; + if( service ) + bservice = hl_copy_bytes((const vbyte *)service, strlen(service)); + hl_call3(void,c,int,errno_uv2hx(status),vbyte *,bhost,vbyte *,bservice); + free_req((uv_req_t *)r); +} + +HL_PRIM void HL_NAME(getnameinfo_wrap)( uv_loop_t *l, uv_sockaddr_storage *addr, vdynamic *namereqd, vdynamic *dgram, + vdynamic *nofqdn, vdynamic *numerichost, vdynamic *numericserv, vclosure *c ) { + UV_CHECK_NULL(l,); + UV_CHECK_NULL(addr,); + UV_CHECK_NULL(c,); + uv_getnameinfo_t *r = UV_ALLOC(uv_getnameinfo_t); + req_init_hl_data((uv_req_t *)r); + req_register_callback((uv_req_t *)r,c,0); + int flags = 0; + if( namereqd && namereqd->v.b ) + flags |= NI_NAMEREQD; + if( dgram && dgram->v.b ) + flags |= NI_DGRAM; + if( nofqdn && nofqdn->v.b ) + flags |= NI_NOFQDN; + if( numerichost && numerichost->v.b ) + flags |= NI_NUMERICHOST; + if( numericserv && numericserv->v.b ) + flags |= NI_NUMERICSERV; + UV_CHECK_ERROR(uv_getnameinfo(l,r,on_getnameinfo,(uv_sockaddr *)addr,flags),free_req((uv_req_t *)r),); +} +DEFINE_PRIM(_VOID, getnameinfo_wrap, _LOOP _SOCKADDR _NULL(_BOOL) _NULL(_BOOL) _NULL(_BOOL) _NULL(_BOOL) _NULL(_BOOL) _FUN(_VOID,_I32 _BYTES _BYTES)); + // loop HL_PRIM uv_loop_t *HL_NAME(loop_init_wrap)( ) { diff --git a/other/uvsample/DnsSample.hx b/other/uvsample/DnsSample.hx new file mode 100644 index 000000000..3ffdd9db9 --- /dev/null +++ b/other/uvsample/DnsSample.hx @@ -0,0 +1,31 @@ +import hl.uv.SockAddr; +import hl.uv.UVException; +import sys.thread.Thread; +import hl.uv.Dns; + +class DnsSample { + public static function main() { + var loop = Thread.current().events; + Dns.getAddrInfo(loop, 'haxe.org', 'http', + { flags: {canonName: true}, family: INET }, + (e, infos) -> switch e { + case UV_NOERR: + for(i in infos) { + Log.print('getAddrInfo: addr ${i.addr}, canonname ${i.canonName}'); + if(i.canonName != null) { + Dns.getNameInfo(loop, i.addr, { nameReqd: true }, + (e, name, service) -> switch e { + case UV_NOERR: + Log.print('getNameInfo: host $name, service $service'); + case _: + throw new UVException(e); + } + ); + } + } + case _: + throw new UVException(e); + } + ); + } +} \ No newline at end of file diff --git a/other/uvsample/UVSample.hx b/other/uvsample/UVSample.hx index a850a8031..9facd4cf1 100644 --- a/other/uvsample/UVSample.hx +++ b/other/uvsample/UVSample.hx @@ -1,11 +1,9 @@ -import hl.uv.*; - class UVSample { static function main() { TcpSample.main(); + // DnsSample.main(); // UdpSample.main(); // PipeSample.main(); // ProcessSample.main(); } - } \ No newline at end of file From 5d31304b24a7e0f7a7e43743981c605dcdb850a4 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Tue, 3 Aug 2021 13:06:01 +0300 Subject: [PATCH 032/117] [wip] File --- libs/uv/uv.c | 110 ++++++++++++++++++++++++++++++----- other/uvsample/FileSample.hx | 17 ++++++ other/uvsample/UVSample.hx | 3 +- 3 files changed, 116 insertions(+), 14 deletions(-) create mode 100644 other/uvsample/FileSample.hx diff --git a/libs/uv/uv.c b/libs/uv/uv.c index c4285512b..f0d9b46da 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -43,7 +43,6 @@ typedef struct { hl_null_access(); \ return fail_return; \ } - #define UV_CHECK_ERROR(action,cleanup,fail_return) \ int __result__ = action; \ if(__result__ < 0) { \ @@ -141,11 +140,17 @@ static int errno_uv2hx( int uv_errno ) { } } +static int hx_errno( int result ) { + return result < 0 ? errno_uv2hx(result) : 0; +} + static void hx_error(int uv_errno) { //TODO: throw hl.uv.UVException hl_error("%s", hl_to_utf16(uv_err_name(uv_errno))); } +DEFINE_PRIM(_BYTES, strerror, _I32); + // Request static events_data *req_init_hl_data( uv_req_t *r ) { @@ -855,12 +860,17 @@ DEFINE_PRIM(_VOID, pipe_bind_wrap, _HANDLE _STRING); HL_PRIM vbyte *HL_NAME(pipe_getsockname_wrap)( uv_pipe_t *h ) { UV_CHECK_NULL(h,NULL); size_t size = 256; - vbyte *name = NULL; + char *buf = NULL; int result = UV_ENOBUFS; while (result == UV_ENOBUFS) { - name = hl_alloc_bytes(size); //free previousely allocated name bytes? - result = uv_pipe_getsockname(h,(char *)name,&size); + if( buf ) + free(buf); + buf = malloc(size); + result = uv_pipe_getsockname(h,buf,&size); } + vbyte *name = hl_alloc_bytes(size); + memcpy(name,buf,size); + free(buf); UV_CHECK_ERROR(result,,NULL); //free bytes? return name; } @@ -1307,14 +1317,6 @@ DEFINE_PRIM(_I32, udp_get_send_queue_count_wrap, _HANDLE); // DNS -// typedef struct { -// int family; -// int sockType; -// int protocol; -// uv_sockaddr_storage *addr; -// vstring *canonName; -// } hx_addrinfo - static void on_getaddrinfo( uv_getaddrinfo_t *r, int status, struct addrinfo *res ) { events_data *ev = UV_DATA(r); vclosure *c = ev ? ev->events[0] : NULL; @@ -1494,4 +1496,86 @@ HL_PRIM void HL_NAME(stop_wrap)( uv_loop_t *loop ) { } DEFINE_PRIM(_VOID, stop_wrap, _LOOP); -DEFINE_PRIM(_BYTES, strerror, _I32); +// File system + +static void on_fs_open( uv_fs_t *r ) { + events_data *ev = UV_DATA(r); + vclosure *c = ev ? ev->events[0] : NULL; + if( !c ) + hl_fatal("No callback in fs_open request"); + + hl_call2(void, c, int, hx_errno(r->result), int, r->result); + free_req((uv_req_t *)r); +} + +HL_PRIM void HL_NAME(fs_open_wrap)( uv_loop_t *loop, vstring *path, varray *flags, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(flags,); + + int i_flags = 0; + int mode = 0; + for( int i=0; isize; i++ ){ + venum *flag = hl_aptr(flags, venum*)[i]; + if( !flag ) { + continue; + } + switch( flag->index ) { + case 0: i_flags |= UV_FS_O_APPEND; break; + case 1: + i_flags |= UV_FS_O_CREAT; + mode = ((struct {hl_type *t;int index;int mode;} *)flag)->mode; + break; + case 2: i_flags |= UV_FS_O_DIRECT; break; + case 3: i_flags |= UV_FS_O_DIRECTORY; break; + case 4: i_flags |= UV_FS_O_DSYNC; break; + case 5: i_flags |= UV_FS_O_EXCL; break; + case 6: i_flags |= UV_FS_O_EXLOCK; break; + case 7: i_flags |= UV_FS_O_FILEMAP; break; + case 8: i_flags |= UV_FS_O_NOATIME; break; + case 9: i_flags |= UV_FS_O_NOCTTY; break; + case 10: i_flags |= UV_FS_O_NOFOLLOW; break; + case 11: i_flags |= UV_FS_O_NONBLOCK; break; + case 12: i_flags |= UV_FS_O_RANDOM; break; + case 13: i_flags |= UV_FS_O_RDONLY; break; + case 14: i_flags |= UV_FS_O_RDWR; break; + case 15: i_flags |= UV_FS_O_SEQUENTIAL; break; + case 16: i_flags |= UV_FS_O_SHORT_LIVED; break; + case 17: i_flags |= UV_FS_O_SYMLINK; break; + case 18: i_flags |= UV_FS_O_SYNC; break; + case 19: i_flags |= UV_FS_O_TEMPORARY; break; + case 20: i_flags |= UV_FS_O_TRUNC; break; + case 21: i_flags |= UV_FS_O_WRONLY; break; + } + } + uv_fs_t *r = UV_ALLOC(uv_fs_t); + req_init_hl_data((uv_req_t *)r); + req_register_callback((uv_req_t *)r,c,0); + UV_CHECK_ERROR(uv_fs_open(loop,r,hl_to_utf8(path->bytes),i_flags,mode,on_fs_open),free_req((uv_req_t *)r),); +} +DEFINE_PRIM(_VOID, fs_open_wrap, _LOOP _STRING _ARR _FUN(_VOID, _I32 _I32)); + +HL_PRIM void HL_NAME(fs_close_wrap)( uv_loop_t *loop, uv_file file, vclosure *c ) { + UV_CHECK_NULL(loop,); + +} +DEFINE_PRIM(_VOID, fs_close_wrap, _LOOP _STRING _ARR _FUN(_VOID, _I32 _I32)); + +// Miscellaneous + +HL_PRIM vbyte *HL_NAME(os_tmpdir_wrap)() { + size_t size = 256; + char *buf = NULL; + int result = UV_ENOBUFS; + while (result == UV_ENOBUFS) { + if( buf ) + free(buf); + buf = malloc(size); + result = uv_os_tmpdir(buf,&size); + } + vbyte *path = hl_alloc_bytes(size); + memcpy(path,buf,size); + free(buf); + UV_CHECK_ERROR(result,,NULL); // free bytes? + return path; +} +DEFINE_PRIM(_BYTES, os_tmpdir_wrap, _NO_ARG); diff --git a/other/uvsample/FileSample.hx b/other/uvsample/FileSample.hx new file mode 100644 index 000000000..3b9fee224 --- /dev/null +++ b/other/uvsample/FileSample.hx @@ -0,0 +1,17 @@ +import hl.uv.Misc; +import hl.uv.UVException; +import sys.thread.Thread; +import hl.uv.File; + +class FileSample { + public static function main() { + var loop = Thread.current().events; + var dir = Misc.tmpDir(); + File.open(loop, '$dir/test', [O_CREAT(511), O_WRONLY], (e, file) -> switch e { + case UV_NOERR: + trace(file); + case _: + throw new UVException(e); + }); + } +} \ No newline at end of file diff --git a/other/uvsample/UVSample.hx b/other/uvsample/UVSample.hx index 9facd4cf1..d71482fc9 100644 --- a/other/uvsample/UVSample.hx +++ b/other/uvsample/UVSample.hx @@ -1,9 +1,10 @@ class UVSample { static function main() { - TcpSample.main(); + // TcpSample.main(); // DnsSample.main(); // UdpSample.main(); // PipeSample.main(); // ProcessSample.main(); + FileSample.main(); } } \ No newline at end of file From 2eeed4f8e97a3bff4e2606e649d6bff156c9336a Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Tue, 3 Aug 2021 13:29:44 +0300 Subject: [PATCH 033/117] UV_ALLOC_REQ --- libs/uv/uv.c | 165 +++++++++++++++++---------------------------------- 1 file changed, 53 insertions(+), 112 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index f0d9b46da..345ec97c5 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -38,6 +38,11 @@ typedef struct { #define _SOCKADDR _ABSTRACT(uv_sockaddr_storage) #define _CALLB _FUN(_VOID,_NO_ARG) #define UV_ALLOC(t) ((t*)malloc(sizeof(t))) +#define UV_ALLOC_REQ(t,r,c) \ + t *r = UV_ALLOC(t); \ + req_init_hl_data((uv_req_t *)r); \ + if( c ) \ + req_register_callback((uv_req_t *)r,c,0); #define UV_CHECK_NULL(handle,fail_return) \ if( !handle ) { \ hl_null_access(); \ @@ -50,6 +55,11 @@ typedef struct { hx_error(__result__); \ return fail_return; \ } +#define UV_GET_CLOSURE(c,holder,callback_idx,fail_msg) \ + events_data *__ev__ = UV_DATA(holder); \ + vclosure *c = __ev__ ? __ev__->events[callback_idx] : NULL; \ + if( !c ) \ + hl_fatal(fail_msg); // Errors @@ -272,10 +282,7 @@ DEFINE_PRIM(_VOID, unref_wrap, _HANDLE); // STREAM static void on_shutdown( uv_shutdown_t *r, int status ) { - events_data *ev = UV_DATA(r); - vclosure *c = ev ? ev->events[0] : NULL; - if( !c ) - hl_fatal("No callback in shutdown request"); + UV_GET_CLOSURE(c,r,0,"No callback in shutdown request"); hl_call1(void, c, int, errno_uv2hx(status)); free_req((uv_req_t *)r); } @@ -283,18 +290,13 @@ static void on_shutdown( uv_shutdown_t *r, int status ) { HL_PRIM void HL_NAME(shutdown_wrap)( uv_stream_t *h, vclosure *c ) { UV_CHECK_NULL(h,); UV_CHECK_NULL(c,); - uv_shutdown_t *r = UV_ALLOC(uv_shutdown_t); - req_init_hl_data((uv_req_t *)r); - req_register_callback((uv_req_t*)r,c,0); + UV_ALLOC_REQ(uv_shutdown_t,r,c) UV_CHECK_ERROR(uv_shutdown(r, h, on_shutdown),free_req((uv_req_t *)r),); } DEFINE_PRIM(_VOID, shutdown_wrap, _HANDLE _FUN(_VOID,_I32)); static void on_listen( uv_stream_t *h, int status ) { - events_data *ev = UV_DATA(h); - vclosure *c = ev ? ev->events[EVT_STREAM_LISTEN] : NULL; - if( !c ) - hl_fatal("No listen callback in stream handle"); + UV_GET_CLOSURE(c,h,EVT_STREAM_LISTEN,"No listen callback in stream handle"); hl_call1(void, c, int, errno_uv2hx(status)); } @@ -318,10 +320,7 @@ static void on_alloc( uv_handle_t* h, size_t size, uv_buf_t *buf ) { } static void on_read( uv_stream_t *h, ssize_t nread, const uv_buf_t *buf ) { - events_data *ev = UV_DATA(h); - vclosure *c = ev ? ev->events[EVT_STREAM_READ] : NULL; - if( !c ) - hl_fatal("No listen callback in stream handle"); + UV_GET_CLOSURE(c,h,EVT_STREAM_READ,"No listen callback in stream handle"); if( nread < 0 ) { hl_call3(void, c, int, errno_uv2hx(nread), vbyte *, NULL, int, 0); } else { @@ -346,10 +345,7 @@ HL_PRIM void HL_NAME(read_stop_wrap)( uv_stream_t *h ) { DEFINE_PRIM(_VOID, read_stop_wrap, _HANDLE); static void on_write( uv_write_t *r, int status ) { - events_data *ev = UV_DATA(r); - vclosure *c = ev ? ev->events[0] : NULL; - if( !c ) - hl_fatal("No callback in write request"); + UV_GET_CLOSURE(c,r,0,"No callback in write request"); hl_call1(void, c, int, status < 0 ? errno_uv2hx(status) : 0); free_req((uv_req_t *)r); } @@ -358,15 +354,14 @@ HL_PRIM void HL_NAME(write_wrap)( uv_stream_t *h, vbyte *b, int length, vclosure UV_CHECK_NULL(h,); UV_CHECK_NULL(b,); UV_CHECK_NULL(c,); - uv_write_t *r = UV_ALLOC(uv_write_t); - events_data *d = req_init_hl_data((uv_req_t *)r); + UV_ALLOC_REQ(uv_write_t,r,c); + events_data *d = UV_DATA(r); // keep a copy of the data uv_buf_t buf; d->write_data = malloc(length); memcpy(d->write_data,b,length); buf.base = d->write_data; buf.len = length; - req_register_callback((uv_req_t *)r,c,0); UV_CHECK_ERROR(uv_write(r,h,&buf,1,on_write),free_req((uv_req_t *)r),); } DEFINE_PRIM(_VOID, write_wrap, _HANDLE _BYTES _I32 _FUN(_VOID,_I32)); @@ -385,15 +380,14 @@ HL_PRIM void HL_NAME(write2_wrap)( uv_stream_t *h, vbyte *b, int length, uv_stre UV_CHECK_NULL(b,); UV_CHECK_NULL(send_handle,); UV_CHECK_NULL(c,); - uv_write_t *r = UV_ALLOC(uv_write_t); - events_data *d = req_init_hl_data((uv_req_t *)r); + UV_ALLOC_REQ(uv_write_t,r,c); + events_data *d = UV_DATA(r); // keep a copy of the data uv_buf_t buf; d->write_data = malloc(length); memcpy(d->write_data,b,length); buf.base = d->write_data; buf.len = length; - req_register_callback((uv_req_t *)r,c,0); UV_CHECK_ERROR(uv_write2(r,h,&buf,1,send_handle,on_write),free_req((uv_req_t *)r),); } DEFINE_PRIM(_VOID, write2_wrap, _HANDLE _BYTES _I32 _HANDLE _FUN(_VOID,_I32)); @@ -421,10 +415,7 @@ HL_PRIM uv_timer_t *HL_NAME(timer_init_wrap)( uv_loop_t *loop ) { DEFINE_PRIM(_HANDLE, timer_init_wrap, _LOOP); static void on_timer( uv_timer_t *h ) { - events_data *ev = UV_DATA(h); - vclosure *c = ev ? ev->events[0] : NULL; - if( !c ) - hl_fatal("No callback in timer handle"); + UV_GET_CLOSURE(c,h,0,"No callback in timer handle"); hl_call0(void, c); } @@ -476,10 +467,7 @@ DEFINE_PRIM(_I32, timer_set_repeat_wrap, _HANDLE _I32); // Async static void on_async( uv_async_t *h ) { - events_data *ev = UV_DATA(h); - vclosure *c = ev ? ev->events[0] : NULL; - if( !c ) - hl_fatal("No callback in async handle"); + UV_GET_CLOSURE(c,h,0,"No callback in async handle"); hl_call1(void, c, uv_async_t *, h); } @@ -503,10 +491,7 @@ DEFINE_PRIM(_VOID, async_send_wrap, _HANDLE); // Idle static void on_idle( uv_idle_t *h ) { - events_data *ev = UV_DATA(h); - vclosure *c = ev ? ev->events[0] : NULL; - if( !c ) - hl_fatal("No callback in idle handle"); + UV_GET_CLOSURE(c,h,0,"No callback in idle handle"); hl_call0(void, c); } @@ -536,10 +521,7 @@ DEFINE_PRIM(_VOID, idle_stop_wrap, _HANDLE); // Prepare static void on_prepare( uv_prepare_t *h ) { - events_data *ev = UV_DATA(h); - vclosure *c = ev ? ev->events[0] : NULL; - if( !c ) - hl_fatal("No callback in prepare handle"); + UV_GET_CLOSURE(c,h,0,"No callback in prepare handle"); hl_call0(void, c); } @@ -569,10 +551,7 @@ DEFINE_PRIM(_VOID, prepare_stop_wrap, _HANDLE); // Check static void on_check( uv_check_t *h ) { - events_data *ev = UV_DATA(h); - vclosure *c = ev ? ev->events[0] : NULL; - if( !c ) - hl_fatal("No callback in check handle"); + UV_GET_CLOSURE(c,h,0,"No callback in check handle"); hl_call0(void, c); } @@ -632,18 +611,12 @@ static int signum_uv2hx( int uv ) { } static void on_signal( uv_signal_t *h, int signum ) { - events_data *ev = UV_DATA(h); - vclosure *c = ev ? ev->events[0] : NULL; - if( !c ) - hl_fatal("No callback in signal handle"); + UV_GET_CLOSURE(c,h,0,"No callback in signal handle"); hl_call1(void, c, int, signum_uv2hx(signum)); } static void on_signal_oneshot( uv_signal_t *h, int signum ) { - events_data *ev = UV_DATA(h); - vclosure *c = ev ? ev->events[0] : NULL; - if( !c ) - hl_fatal("No callback in signal handle"); + UV_GET_CLOSURE(c,h,0,"No callback in signal handle"); handle_clear_callback((uv_handle_t *)h,0); hl_call1(void, c, int, signum_uv2hx(signum)); } @@ -813,10 +786,7 @@ HL_PRIM uv_sockaddr_storage *HL_NAME(tcp_getpeername_wrap)( uv_tcp_t *h ) { DEFINE_PRIM(_SOCKADDR, tcp_getpeername_wrap, _HANDLE); static void on_connect( uv_connect_t *r, int status ) { - events_data *ev = UV_DATA(r); - vclosure *c = ev ? ev->events[0] : NULL; - if( !c ) - hl_fatal("No callback in connect request"); + UV_GET_CLOSURE(c,r,0,"No callback in connect request"); hl_call1(void, c, int, errno_uv2hx(status)); free_req((uv_req_t *)r); } @@ -825,9 +795,7 @@ HL_PRIM void HL_NAME(tcp_connect_wrap)( uv_tcp_t *h, uv_sockaddr_storage *addr, UV_CHECK_NULL(h,); UV_CHECK_NULL(addr,); UV_CHECK_NULL(c,); - uv_connect_t *r = UV_ALLOC(uv_connect_t); - req_init_hl_data((uv_req_t *)r); - req_register_callback((uv_req_t *)r,c,0); + UV_ALLOC_REQ(uv_connect_t,r,c); UV_CHECK_ERROR(uv_tcp_connect(r, h,(uv_sockaddr *)addr,on_connect),free_req((uv_req_t *)r),); } DEFINE_PRIM(_VOID, tcp_connect_wrap, _HANDLE _SOCKADDR _FUN(_VOID,_I32)); @@ -879,12 +847,17 @@ DEFINE_PRIM(_BYTES, pipe_getsockname_wrap, _HANDLE); HL_PRIM vbyte *HL_NAME(pipe_getpeername_wrap)( uv_pipe_t *h ) { UV_CHECK_NULL(h,NULL); size_t size = 256; - vbyte *name = NULL; + char *buf = NULL; int result = UV_ENOBUFS; while (result == UV_ENOBUFS) { - name = hl_alloc_bytes(size); //free previousely allocated name bytes? - result = uv_pipe_getpeername(h,(char *)name,&size); + if( buf ) + free(buf); + buf = malloc(size); + result = uv_pipe_getpeername(h,buf,&size); } + vbyte *name = hl_alloc_bytes(size); + memcpy(name,buf,size); + free(buf); UV_CHECK_ERROR(result,,NULL); //free bytes? return name; } @@ -1007,30 +980,21 @@ HL_PRIM uv_process_t *HL_NAME(spawn_wrap)( uv_loop_t *loop, vstring *file, varra options.stdio[i].flags = UV_IGNORE; continue; } - /* - On Haxe side: - enum ProcessStdio { - IGNORE; - INHERIT; - FD(fd:StdioFd); - PIPE(pipe:Pipe, permissions:StdioPipePermissions, ?nonBlock:Bool); - STREAM(stream:Stream); - } - */ + // On Haxe side: enum ProcessStdio stdio_pipe *cfg; switch( io->index ) { - case 0: + case 0: // IGNORE options.stdio[i].flags = UV_IGNORE; break; - case 1: + case 1: // INHERIT options.stdio[i].flags = UV_INHERIT_FD; options.stdio[i].data.fd = i; break; - case 2: + case 2: // FD(fd:StdioFd) options.stdio[i].flags = UV_INHERIT_FD; options.stdio[i].data.fd = ((stdio_fd *)io)->fd; break; - case 3: + case 3: // PIPE cfg = (stdio_pipe *)io; UV_CHECK_NULL(cfg->pipe,NULL); options.stdio[i].flags = UV_CREATE_PIPE; @@ -1044,7 +1008,7 @@ HL_PRIM uv_process_t *HL_NAME(spawn_wrap)( uv_loop_t *loop, vstring *file, varra options.stdio[i].flags |= UV_OVERLAPPED_PIPE; options.stdio[i].data.stream = (uv_stream_t *)cfg->pipe; break; - case 4: + case 4: // STREAM UV_CHECK_NULL(((stdio_stream *)io)->stream,NULL); options.stdio[i].flags = UV_INHERIT_STREAM; options.stdio[i].data.stream = ((stdio_stream *)io)->stream; @@ -1225,10 +1189,7 @@ HL_PRIM void HL_NAME(udp_set_ttl_wrap)( uv_udp_t *h, int ttl ) { DEFINE_PRIM(_VOID, udp_set_ttl_wrap, _HANDLE _I32); static void on_udp_send( uv_udp_send_t *r, int status ) { - events_data *ev = UV_DATA(r); - vclosure *c = ev ? ev->events[0] : NULL; - if( !c ) - hl_fatal("No callback in udp send request"); + UV_GET_CLOSURE(c,r,0,"No callback in udp send request"); hl_call1(void, c, int, status < 0 ? errno_uv2hx(status) : 0); free_req((uv_req_t *)r); } @@ -1236,14 +1197,13 @@ static void on_udp_send( uv_udp_send_t *r, int status ) { HL_PRIM void HL_NAME(udp_send_wrap)( uv_udp_t *h, vbyte *data, int length, uv_sockaddr_storage *addr, vclosure *c ) { UV_CHECK_NULL(h,); UV_CHECK_NULL(c,); - uv_udp_send_t *r = UV_ALLOC(uv_udp_send_t); - events_data *d = req_init_hl_data((uv_req_t *)r); + UV_ALLOC_REQ(uv_udp_send_t,r,c); + events_data *d = UV_DATA(r); uv_buf_t buf; d->write_data = malloc(length); memcpy(d->write_data,data,length); buf.base = d->write_data; buf.len = length; - req_register_callback((uv_req_t *)r,c,0); UV_CHECK_ERROR(uv_udp_send(r,h,&buf,1,(uv_sockaddr *)addr,on_udp_send),free_req((uv_req_t *)r),); } DEFINE_PRIM(_VOID, udp_send_wrap, _HANDLE _BYTES _I32 _SOCKADDR _FUN(_VOID,_I32)); @@ -1257,10 +1217,7 @@ HL_PRIM int HL_NAME(udp_try_send_wrap)( uv_udp_t *h, vbyte *data, int length, uv DEFINE_PRIM(_I32, udp_try_send_wrap, _HANDLE _BYTES _I32 _SOCKADDR); static void on_udp_recv( uv_udp_t *h, ssize_t nread, const uv_buf_t *buf, const uv_sockaddr *src_addr, unsigned flags ) { - events_data *ev = UV_DATA(h); - vclosure *c = ev ? ev->events[0] : NULL; - if( !c ) - hl_fatal("No recv callback in udp handle"); + UV_GET_CLOSURE(c,h,0,"No recv callback in udp handle"); uv_sockaddr_storage *addr = NULL; if( src_addr ) { @@ -1318,10 +1275,7 @@ DEFINE_PRIM(_I32, udp_get_send_queue_count_wrap, _HANDLE); // DNS static void on_getaddrinfo( uv_getaddrinfo_t *r, int status, struct addrinfo *res ) { - events_data *ev = UV_DATA(r); - vclosure *c = ev ? ev->events[0] : NULL; - if( !c ) - hl_fatal("No callback in getaddrinfo request"); + UV_GET_CLOSURE(c,r,0,"No callback in getaddrinfo request"); int count = 0; struct addrinfo *current = res; @@ -1381,9 +1335,7 @@ HL_PRIM void HL_NAME(getaddrinfo_wrap)( uv_loop_t *l, vstring *name, vstring *se UV_CHECK_NULL(l,); UV_CHECK_NULL(( name || service ),); UV_CHECK_NULL(c,); - uv_getaddrinfo_t *r = UV_ALLOC(uv_getaddrinfo_t); - req_init_hl_data((uv_req_t *)r); - req_register_callback((uv_req_t *)r,c,0); + UV_ALLOC_REQ(uv_getaddrinfo_t,r,c); char *c_name = name ? hl_to_utf8(name->bytes) : NULL; char *c_service = service ? hl_to_utf8(service->bytes) : NULL; struct addrinfo hints = {0}; @@ -1424,10 +1376,7 @@ HL_PRIM void HL_NAME(getaddrinfo_wrap)( uv_loop_t *l, vstring *name, vstring *se DEFINE_PRIM(_VOID, getaddrinfo_wrap, _LOOP _STRING _STRING _DYN _NULL(_I32) _NULL(_I32) _NULL(_I32) _FUN(_VOID,_I32 _ARR)); static void on_getnameinfo( uv_getnameinfo_t *r, int status, const char *hostname, const char *service ) { - events_data *ev = UV_DATA(r); - vclosure *c = ev ? ev->events[0] : NULL; - if( !c ) - hl_fatal("No callback in getaddrinfo request"); + UV_GET_CLOSURE(c,r,0,"No callback in getnameinfo request"); vbyte * bhost = NULL; if( hostname ) bhost = hl_copy_bytes((const vbyte *)hostname, strlen(hostname)); @@ -1443,9 +1392,7 @@ HL_PRIM void HL_NAME(getnameinfo_wrap)( uv_loop_t *l, uv_sockaddr_storage *addr, UV_CHECK_NULL(l,); UV_CHECK_NULL(addr,); UV_CHECK_NULL(c,); - uv_getnameinfo_t *r = UV_ALLOC(uv_getnameinfo_t); - req_init_hl_data((uv_req_t *)r); - req_register_callback((uv_req_t *)r,c,0); + UV_ALLOC_REQ(uv_getnameinfo_t,r,c); int flags = 0; if( namereqd && namereqd->v.b ) flags |= NI_NAMEREQD; @@ -1499,11 +1446,7 @@ DEFINE_PRIM(_VOID, stop_wrap, _LOOP); // File system static void on_fs_open( uv_fs_t *r ) { - events_data *ev = UV_DATA(r); - vclosure *c = ev ? ev->events[0] : NULL; - if( !c ) - hl_fatal("No callback in fs_open request"); - + UV_GET_CLOSURE(c,r,0,"No callback in fs_open request"); hl_call2(void, c, int, hx_errno(r->result), int, r->result); free_req((uv_req_t *)r); } @@ -1547,16 +1490,14 @@ HL_PRIM void HL_NAME(fs_open_wrap)( uv_loop_t *loop, vstring *path, varray *flag case 21: i_flags |= UV_FS_O_WRONLY; break; } } - uv_fs_t *r = UV_ALLOC(uv_fs_t); - req_init_hl_data((uv_req_t *)r); - req_register_callback((uv_req_t *)r,c,0); + UV_ALLOC_REQ(uv_fs_t,r,c); UV_CHECK_ERROR(uv_fs_open(loop,r,hl_to_utf8(path->bytes),i_flags,mode,on_fs_open),free_req((uv_req_t *)r),); } DEFINE_PRIM(_VOID, fs_open_wrap, _LOOP _STRING _ARR _FUN(_VOID, _I32 _I32)); HL_PRIM void HL_NAME(fs_close_wrap)( uv_loop_t *loop, uv_file file, vclosure *c ) { UV_CHECK_NULL(loop,); - + UV_ALLOC_REQ(uv_fs_t,r,c); } DEFINE_PRIM(_VOID, fs_close_wrap, _LOOP _STRING _ARR _FUN(_VOID, _I32 _I32)); From 299af5d6e306a40fee874773401ae5b3bb56589d Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Tue, 3 Aug 2021 19:44:38 +0300 Subject: [PATCH 034/117] [dip] fs --- CMakeLists.txt | 1 + Makefile | 2 +- libs/uv/uv.c | 246 ++++++++++++++++++++++++++++++----- other/uvsample/FileSample.hx | 128 ++++++++++++++++-- src/std/num.c | 6 + 5 files changed, 339 insertions(+), 44 deletions(-) create mode 100644 src/std/num.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 01c00142e..88a3109ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -79,6 +79,7 @@ file(GLOB std_srcs src/std/ucs2.c src/std/thread.c src/std/process.c + src/std/num.c ) if(CMAKE_SYSTEM_NAME MATCHES "Darwin") diff --git a/Makefile b/Makefile index 50dfb4d27..28de81d19 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ RUNTIME = src/gc.o STD = src/std/array.o src/std/buffer.o src/std/bytes.o src/std/cast.o src/std/date.o src/std/error.o src/std/debug.o \ src/std/file.o src/std/fun.o src/std/maps.o src/std/math.o src/std/obj.o src/std/random.o src/std/regexp.o \ src/std/socket.o src/std/string.o src/std/sys.o src/std/types.o src/std/ucs2.o src/std/thread.o src/std/process.o \ - src/std/track.o + src/std/track.o src/std/num.o HL = src/code.o src/jit.o src/main.o src/module.o src/debugger.o src/profile.o diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 345ec97c5..ed145cbbc 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -27,17 +27,19 @@ typedef struct sockaddr_storage uv_sockaddr_storage; typedef struct { vclosure *events[EVT_MAX + 1]; - void *write_data; + void *data; } events_data; #define UV_DATA(h) ((events_data*)((h)->data)) -#define _LOOP _ABSTRACT(uv_loop) -#define _HANDLE _ABSTRACT(uv_handle) -#define _REQUEST _ABSTRACT(uv_req) -#define _SOCKADDR _ABSTRACT(uv_sockaddr_storage) -#define _CALLB _FUN(_VOID,_NO_ARG) -#define UV_ALLOC(t) ((t*)malloc(sizeof(t))) +#define _LOOP _ABSTRACT(uv_loop) +#define _HANDLE _ABSTRACT(uv_handle) +#define _REQUEST _ABSTRACT(uv_req) +#define _SOCKADDR _ABSTRACT(uv_sockaddr_storage) +#define _CALLB _FUN(_VOID,_NO_ARG) +#define _TIMESPEC _OBJ(_I64 _I64) +#define _STAT _OBJ(_I64 _I64 _I64 _I64 _I64 _I64 _I64 _I64 _I64 _I64 _I64 _I64 _TIMESPEC _TIMESPEC _TIMESPEC _TIMESPEC) +#define UV_ALLOC(t) ((t*)malloc(sizeof(t))) #define UV_ALLOC_REQ(t,r,c) \ t *r = UV_ALLOC(t); \ req_init_hl_data((uv_req_t *)r); \ @@ -60,6 +62,11 @@ typedef struct { vclosure *c = __ev__ ? __ev__->events[callback_idx] : NULL; \ if( !c ) \ hl_fatal(fail_msg); +#define UV_COPY_DATA(buf,holder,buffer,length) \ + events_data *__ev__ = UV_DATA(holder); \ + __ev__->data = malloc(length); \ + memcpy(__ev__->data,buffer,length); \ + uv_buf_t buf = uv_buf_init(__ev__->data, length); // Errors @@ -186,14 +193,29 @@ static void req_clear_callback( uv_req_t *r, int event_kind ) { static void free_req( uv_req_t *r ) { events_data *ev = UV_DATA(r); if( ev ) { - req_clear_callback((uv_req_t *)r, 0); - free(ev->write_data); - hl_remove_root(&r->data); + req_clear_callback(r, 0); + if( ev->data ) { + free(ev->data); + hl_remove_root(&r->data); + } r->data = NULL; } free(r); } +static void free_fs_req( uv_fs_t *r ) { + events_data *ev = UV_DATA(r); + if( ev ) { + req_clear_callback((uv_req_t *)r, 0); + if( ev->data ) { + free(ev->data); + hl_remove_root(&r->data); + } + r->data = NULL; + } + uv_fs_req_cleanup(r); +} + HL_PRIM void HL_NAME(cancel_wrap)( uv_req_t *r ) { UV_CHECK_NULL(r,); UV_CHECK_ERROR(uv_cancel(r),,); @@ -231,8 +253,10 @@ static void on_close( uv_handle_t *h ) { hl_call0(void, c); handle_clear_callback(h, EVT_CLOSE); } - free(ev->write_data); - hl_remove_root(&h->data); + if( ev->data ) { + free(ev->data); + hl_remove_root(&h->data); + } h->data = NULL; } free(h); @@ -346,7 +370,7 @@ DEFINE_PRIM(_VOID, read_stop_wrap, _HANDLE); static void on_write( uv_write_t *r, int status ) { UV_GET_CLOSURE(c,r,0,"No callback in write request"); - hl_call1(void, c, int, status < 0 ? errno_uv2hx(status) : 0); + hl_call1(void, c, int, hx_errno(status)); free_req((uv_req_t *)r); } @@ -355,13 +379,7 @@ HL_PRIM void HL_NAME(write_wrap)( uv_stream_t *h, vbyte *b, int length, vclosure UV_CHECK_NULL(b,); UV_CHECK_NULL(c,); UV_ALLOC_REQ(uv_write_t,r,c); - events_data *d = UV_DATA(r); - // keep a copy of the data - uv_buf_t buf; - d->write_data = malloc(length); - memcpy(d->write_data,b,length); - buf.base = d->write_data; - buf.len = length; + UV_COPY_DATA(buf,r,b,length); UV_CHECK_ERROR(uv_write(r,h,&buf,1,on_write),free_req((uv_req_t *)r),); } DEFINE_PRIM(_VOID, write_wrap, _HANDLE _BYTES _I32 _FUN(_VOID,_I32)); @@ -381,13 +399,7 @@ HL_PRIM void HL_NAME(write2_wrap)( uv_stream_t *h, vbyte *b, int length, uv_stre UV_CHECK_NULL(send_handle,); UV_CHECK_NULL(c,); UV_ALLOC_REQ(uv_write_t,r,c); - events_data *d = UV_DATA(r); - // keep a copy of the data - uv_buf_t buf; - d->write_data = malloc(length); - memcpy(d->write_data,b,length); - buf.base = d->write_data; - buf.len = length; + UV_COPY_DATA(buf,r,b,length); UV_CHECK_ERROR(uv_write2(r,h,&buf,1,send_handle,on_write),free_req((uv_req_t *)r),); } DEFINE_PRIM(_VOID, write2_wrap, _HANDLE _BYTES _I32 _HANDLE _FUN(_VOID,_I32)); @@ -921,7 +933,7 @@ static void on_process_exit(uv_process_t *h, int64_t exit_status, int term_signa events_data *ev = UV_DATA(h); vclosure *c = ev ? ev->events[0] : NULL; if( c ) { - hl_call3(void, c, uv_process_t *, h, int64_t, exit_status, int, signum_uv2hx(term_signal)); + hl_call3(void, c, uv_process_t *, h, int64, exit_status, int, signum_uv2hx(term_signal)); handle_clear_callback((uv_handle_t *)h, 0); } } @@ -1190,7 +1202,7 @@ DEFINE_PRIM(_VOID, udp_set_ttl_wrap, _HANDLE _I32); static void on_udp_send( uv_udp_send_t *r, int status ) { UV_GET_CLOSURE(c,r,0,"No callback in udp send request"); - hl_call1(void, c, int, status < 0 ? errno_uv2hx(status) : 0); + hl_call1(void, c, int, hx_errno(status)); free_req((uv_req_t *)r); } @@ -1200,9 +1212,9 @@ HL_PRIM void HL_NAME(udp_send_wrap)( uv_udp_t *h, vbyte *data, int length, uv_so UV_ALLOC_REQ(uv_udp_send_t,r,c); events_data *d = UV_DATA(r); uv_buf_t buf; - d->write_data = malloc(length); - memcpy(d->write_data,data,length); - buf.base = d->write_data; + d->data = malloc(length); + memcpy(d->data,data,length); + buf.base = d->data; buf.len = length; UV_CHECK_ERROR(uv_udp_send(r,h,&buf,1,(uv_sockaddr *)addr,on_udp_send),free_req((uv_req_t *)r),); } @@ -1454,6 +1466,7 @@ static void on_fs_open( uv_fs_t *r ) { HL_PRIM void HL_NAME(fs_open_wrap)( uv_loop_t *loop, vstring *path, varray *flags, vclosure *c ) { UV_CHECK_NULL(loop,); UV_CHECK_NULL(flags,); + UV_CHECK_NULL(c,); int i_flags = 0; int mode = 0; @@ -1493,13 +1506,176 @@ HL_PRIM void HL_NAME(fs_open_wrap)( uv_loop_t *loop, vstring *path, varray *flag UV_ALLOC_REQ(uv_fs_t,r,c); UV_CHECK_ERROR(uv_fs_open(loop,r,hl_to_utf8(path->bytes),i_flags,mode,on_fs_open),free_req((uv_req_t *)r),); } -DEFINE_PRIM(_VOID, fs_open_wrap, _LOOP _STRING _ARR _FUN(_VOID, _I32 _I32)); +DEFINE_PRIM(_VOID, fs_open_wrap, _LOOP _STRING _ARR _FUN(_VOID,_I32 _I32)); + +static void on_fs_common( uv_fs_t *r ) { + UV_GET_CLOSURE(c,r,0,"No callback in fs request"); + hl_call1(void,c,int,hx_errno(r->result)); + free_fs_req(r); +} + +HL_PRIM void HL_NAME(fs_close_wrap)( uv_file file, uv_loop_t *loop, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + UV_CHECK_ERROR(uv_fs_close(loop,r,file,on_fs_common),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_close_wrap, _I32 _LOOP _FUN(_VOID,_I32)); + +static void on_fs_write( uv_fs_t *r ) { + UV_GET_CLOSURE(c,r,0,"No callback in fs_write request"); + hl_call2(void, c, int, hx_errno(r->result),int64,r->result<0?0:r->result); + free_fs_req(r); +} + +HL_PRIM void HL_NAME(fs_write_wrap)( uv_file file, uv_loop_t *loop, vbyte *data, int length, int64 offset, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(data,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + UV_COPY_DATA(buf,r,data,length); + UV_CHECK_ERROR(uv_fs_write(loop,r,file,&buf,1,offset,on_fs_write),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_write_wrap, _I32 _LOOP _BYTES _I32 _I64 _FUN(_VOID,_I32 _I64)); + +static void on_fs_read( uv_fs_t *r ) { + UV_GET_CLOSURE(c,r,0,"No callback in fs_read request"); + hl_call2(void,c,int,hx_errno(r->result),int64,r->result<0?0:r->result); + free_fs_req(r); +} + +HL_PRIM void HL_NAME(fs_read_wrap)( uv_file file, uv_loop_t *loop, vbyte *buf, int length, int64 offset, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(buf,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + uv_buf_t b = uv_buf_init((char *)buf, length); + UV_CHECK_ERROR(uv_fs_read(loop,r,file,&b,1,offset,on_fs_read),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_read_wrap, _I32 _LOOP _BYTES _I32 _I64 _FUN(_VOID,_I32 _I64)); + +HL_PRIM void HL_NAME(fs_unlink_wrap)( uv_loop_t *loop, vstring *path, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(path,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + UV_CHECK_ERROR(uv_fs_unlink(loop,r,hl_to_utf8(path->bytes),on_fs_common),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_unlink_wrap, _LOOP _STRING _FUN(_VOID,_I32)); + +HL_PRIM void HL_NAME(fs_mkdir_wrap)( uv_loop_t *loop, vstring *path, int mode, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(path,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + UV_CHECK_ERROR(uv_fs_mkdir(loop,r,hl_to_utf8(path->bytes),mode,on_fs_common),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_mkdir_wrap, _LOOP _STRING _I32 _FUN(_VOID,_I32)); + +HL_PRIM void HL_NAME(fs_rmdir_wrap)( uv_loop_t *loop, vstring *path, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(path,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + UV_CHECK_ERROR(uv_fs_rmdir(loop,r,hl_to_utf8(path->bytes),on_fs_common),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_rmdir_wrap, _LOOP _STRING _FUN(_VOID,_I32)); + +static void on_fs_mkdtemp( uv_fs_t *r ) { + UV_GET_CLOSURE(c,r,0,"No callback in fs_mkdtemp request"); + hl_call2(void,c,int,hx_errno(r->result),vbyte *,(vbyte *)(r->result<0?NULL:r->path)); + free_fs_req(r); +} -HL_PRIM void HL_NAME(fs_close_wrap)( uv_loop_t *loop, uv_file file, vclosure *c ) { +HL_PRIM void HL_NAME(fs_mkdtemp_wrap)( uv_loop_t *loop, vstring *tpl, vclosure *c ) { UV_CHECK_NULL(loop,); + UV_CHECK_NULL(tpl,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + UV_CHECK_ERROR(uv_fs_mkdtemp(loop,r,hl_to_utf8(tpl->bytes),on_fs_mkdtemp),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_mkdtemp_wrap, _LOOP _STRING _FUN(_VOID,_I32 _BYTES)); + +static void on_fs_mkstemp( uv_fs_t *r ) { + UV_GET_CLOSURE(c,r,0,"No callback in fs_mkstemp request"); + hl_call3(void,c,int,hx_errno(r->result),int,r->result,vbyte *,(vbyte *)(r->result<0?NULL:r->path)); + free_fs_req(r); +} + +HL_PRIM void HL_NAME(fs_mkstemp_wrap)( uv_loop_t *loop, vstring *tpl, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(tpl,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + UV_CHECK_ERROR(uv_fs_mkstemp(loop,r,hl_to_utf8(tpl->bytes),on_fs_mkstemp),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_mkstemp_wrap, _LOOP _STRING _FUN(_VOID,_I32 _I32 _BYTES)); + +typedef struct { + hl_type *t; + int64 sec; + int64 nsec; +} hx_timespec; + +typedef struct { + hl_type *t; + int64 dev; //uint64 + int64 mode; //uint64 + int64 nlink; //uint64 + int64 uid; //uint64 + int64 gid; //uint64 + int64 rdev; //uint64 + int64 ino; //uint64 + int64 size; //uint64 + int64 blksize; //uint64 + int64 blocks; //uint64 + int64 flags; //uint64 + int64 gen; //uint64 + hx_timespec *atim; + hx_timespec *mtim; + hx_timespec *ctim; + hx_timespec *birthtim; +} hx_stat; + +static void on_fs_stat( uv_fs_t *r ) { + UV_GET_CLOSURE(c,r,0,"No callback in fs stat request"); + events_data *ev = UV_DATA(r); + hx_stat *stat = ev->data; + stat->dev = r->statbuf.st_dev; + stat->mode = r->statbuf.st_mode; + stat->nlink = r->statbuf.st_nlink; + stat->uid = r->statbuf.st_uid; + stat->gid = r->statbuf.st_gid; + stat->rdev = r->statbuf.st_rdev; + stat->ino = r->statbuf.st_ino; + stat->size = r->statbuf.st_size; + stat->blksize = r->statbuf.st_blksize; + stat->blocks = r->statbuf.st_blocks; + stat->flags = r->statbuf.st_flags; + stat->gen = r->statbuf.st_gen; + stat->atim->sec = r->statbuf.st_atim.tv_sec; + stat->atim->nsec = r->statbuf.st_atim.tv_nsec; + stat->mtim->sec = r->statbuf.st_mtim.tv_sec; + stat->mtim->nsec = r->statbuf.st_mtim.tv_nsec; + stat->ctim->sec = r->statbuf.st_ctim.tv_sec; + stat->ctim->nsec = r->statbuf.st_ctim.tv_nsec; + stat->birthtim->sec = r->statbuf.st_birthtim.tv_sec; + stat->birthtim->nsec = r->statbuf.st_birthtim.tv_nsec; + ev->data = NULL; + hl_call2(void,c,int,hx_errno(r->result),hx_stat *,(r->result<0?NULL:stat)); + free_fs_req(r); +} + +HL_PRIM void HL_NAME(fs_stat_wrap)( uv_loop_t *loop, vstring *path, hx_stat *stat, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(path,); + UV_CHECK_NULL(stat,); + UV_CHECK_NULL(c,); UV_ALLOC_REQ(uv_fs_t,r,c); + r->data = stat; + UV_CHECK_ERROR(uv_fs_stat(loop,r,hl_to_utf8(path->bytes),on_fs_stat),free_fs_req(r),); } -DEFINE_PRIM(_VOID, fs_close_wrap, _LOOP _STRING _ARR _FUN(_VOID, _I32 _I32)); +DEFINE_PRIM(_VOID, fs_stat_wrap, _LOOP _STRING _STAT _FUN(_VOID,_I32 _STAT)); // Miscellaneous diff --git a/other/uvsample/FileSample.hx b/other/uvsample/FileSample.hx index 3b9fee224..3d41189ac 100644 --- a/other/uvsample/FileSample.hx +++ b/other/uvsample/FileSample.hx @@ -1,17 +1,129 @@ +import haxe.Constraints.Function; +import haxe.xml.Access; +import hl.I64; +import haxe.io.Bytes; +import hl.uv.UVError; +import haxe.PosInfos; import hl.uv.Misc; import hl.uv.UVException; import sys.thread.Thread; import hl.uv.File; +typedef Actions = Array; + class FileSample { + static final loop = Thread.current().events; + public static function main() { - var loop = Thread.current().events; - var dir = Misc.tmpDir(); - File.open(loop, '$dir/test', [O_CREAT(511), O_WRONLY], (e, file) -> switch e { - case UV_NOERR: - trace(file); - case _: - throw new UVException(e); - }); + var actions:Actions = [ + createWriteReadUnlink, + mkdirRmdir, + mkdtempRmdir, + mkstempUnlink, + stat, + ]; + runNext(actions); + } + + static function handle(success:()->Void, ?p:PosInfos):(e:UVError)->Void { + return e -> switch e { + case UV_NOERR: success(); + case _: throw new UVException(e, p.fileName + ':' + p.lineNumber + ': ' + e.toString()); + } + } + + static function runNext(actions:Actions) { + var fn = actions.shift(); + if(fn != null) { + Log.print('-----------'); + fn(actions); + } + } + + static function createWriteReadUnlink(actions:Actions) { + var path = Misc.tmpDir() + '/test-file'; + Log.print('Creating $path for writing...'); + File.open(loop, path, [O_CREAT(511), O_WRONLY], (e, file) -> handle(() -> { + Log.print('Writing...'); + var data = Bytes.ofString('Hello, world!'); + file.write(loop, data.getData(), data.length, I64.ofInt(0), (e, bytesWritten) -> handle(() -> { + Log.print('$bytesWritten bytes written: $data'); + file.close(loop, handle(() -> { + Log.print('closed $path'); + readUnlink(path, actions); + })); + })(e)); + })(e)); + } + + static function readUnlink(path:String, actions:Actions) { + Log.print('Opening $path for reading...'); + File.open(loop, path, [O_RDONLY], (e, file) -> handle(() -> { + Log.print('Reading...'); + var buf = new hl.Bytes(1024); + file.read(loop, buf, 1024, I64.ofInt(0), (e, bytesRead) -> handle(() -> { + Log.print('$bytesRead bytes read: ' + buf.toBytes(bytesRead.toInt())); + file.close(loop, handle(() -> { + Log.print('closed $path'); + unlink(path, actions); + })); + })(e)); + })(e)); + } + + static function unlink(path:String, actions:Actions) { + Log.print('Unlinking $path...'); + File.unlink(loop, path, handle(() -> { + runNext(actions); + })); + } + + static function mkdirRmdir(actions:Actions) { + var path = Misc.tmpDir() + '/test-dir'; + Log.print('Creating directory $path...'); + File.mkdir(loop, path, 511, handle(() -> { + Log.print('Removing directory $path...'); + File.rmdir(loop, path, handle(() -> { + Log.print('Done'); + runNext(actions); + })); + })); + } + + static function mkdtempRmdir(actions:Actions) { + var tpl = Misc.tmpDir() + '/test-dir-XXXXXX'; + Log.print('Creating temp directory with tpl $tpl...'); + File.mkdtemp(loop, tpl, (e, path) -> handle(() -> { + Log.print('Removing directory $path...'); + File.rmdir(loop, path, handle(() -> { + Log.print('Done'); + runNext(actions); + })); + })(e)); + } + + static function mkstempUnlink(actions:Actions) { + var tpl = Misc.tmpDir() + '/test-file-XXXXXX'; + Log.print('Creating temp file with tpl $tpl...'); + File.mkstemp(loop, tpl, (e, file, path) -> handle(() -> { + Log.print('Closing $path...'); + file.close(loop, handle(() -> { + Log.print('Unlinking $path...'); + File.unlink(loop, path, handle(() -> { + Log.print('Done'); + runNext(actions); + })); + })); + })(e)); + } + + static function stat(actions:Actions) { + var path = Misc.tmpDir(); + Log.print('Stat on path...'); + File.stat(loop,path, (e, stat) -> handle(() -> { + Log.print('Got stat: $stat'); + Log.print('Done'); + runNext(actions); + })(e)); } } \ No newline at end of file diff --git a/src/std/num.c b/src/std/num.c new file mode 100644 index 000000000..375874e6a --- /dev/null +++ b/src/std/num.c @@ -0,0 +1,6 @@ +#include + +HL_PRIM int64 hl_num_i64_of_int( int i ) { + return i; +} +DEFINE_PRIM(_I64, num_i64_of_int, _I32); \ No newline at end of file From eb5401659397766a626b4ffbbbb9cb097f1ef51f Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Wed, 4 Aug 2021 13:11:59 +0300 Subject: [PATCH 035/117] fs_stat --- libs/uv/uv.c | 89 +++++++++++++++--------------------- other/uvsample/FileSample.hx | 5 +- other/uvsample/uvsample.hxml | 2 +- 3 files changed, 42 insertions(+), 54 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index ed145cbbc..e967d87da 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -68,6 +68,12 @@ typedef struct { memcpy(__ev__->data,buffer,length); \ uv_buf_t buf = uv_buf_init(__ev__->data, length); +static void dyn_set_i64( vdynamic *obj, int field, int64 value ) { + vdynamic *v = hl_alloc_dynamic(&hlt_i64); + v->v.i64 = value; + hl_dyn_setp(obj, field, &hlt_dyn, v); +} + // Errors static int errno_uv2hx( int uv_errno ) { @@ -1611,71 +1617,52 @@ HL_PRIM void HL_NAME(fs_mkstemp_wrap)( uv_loop_t *loop, vstring *tpl, vclosure * } DEFINE_PRIM(_VOID, fs_mkstemp_wrap, _LOOP _STRING _FUN(_VOID,_I32 _I32 _BYTES)); -typedef struct { - hl_type *t; - int64 sec; - int64 nsec; -} hx_timespec; - -typedef struct { - hl_type *t; - int64 dev; //uint64 - int64 mode; //uint64 - int64 nlink; //uint64 - int64 uid; //uint64 - int64 gid; //uint64 - int64 rdev; //uint64 - int64 ino; //uint64 - int64 size; //uint64 - int64 blksize; //uint64 - int64 blocks; //uint64 - int64 flags; //uint64 - int64 gen; //uint64 - hx_timespec *atim; - hx_timespec *mtim; - hx_timespec *ctim; - hx_timespec *birthtim; -} hx_stat; +static vdynamic *alloc_timespec_dyn( uv_timespec_t *spec ) { + vdynamic *obj = (vdynamic*)hl_alloc_dynobj(); + dyn_set_i64(obj, hl_hash_utf8("sec"), spec->tv_sec); + dyn_set_i64(obj, hl_hash_utf8("nsec"), spec->tv_nsec); + return obj; +} static void on_fs_stat( uv_fs_t *r ) { UV_GET_CLOSURE(c,r,0,"No callback in fs stat request"); events_data *ev = UV_DATA(r); - hx_stat *stat = ev->data; - stat->dev = r->statbuf.st_dev; - stat->mode = r->statbuf.st_mode; - stat->nlink = r->statbuf.st_nlink; - stat->uid = r->statbuf.st_uid; - stat->gid = r->statbuf.st_gid; - stat->rdev = r->statbuf.st_rdev; - stat->ino = r->statbuf.st_ino; - stat->size = r->statbuf.st_size; - stat->blksize = r->statbuf.st_blksize; - stat->blocks = r->statbuf.st_blocks; - stat->flags = r->statbuf.st_flags; - stat->gen = r->statbuf.st_gen; - stat->atim->sec = r->statbuf.st_atim.tv_sec; - stat->atim->nsec = r->statbuf.st_atim.tv_nsec; - stat->mtim->sec = r->statbuf.st_mtim.tv_sec; - stat->mtim->nsec = r->statbuf.st_mtim.tv_nsec; - stat->ctim->sec = r->statbuf.st_ctim.tv_sec; - stat->ctim->nsec = r->statbuf.st_ctim.tv_nsec; - stat->birthtim->sec = r->statbuf.st_birthtim.tv_sec; - stat->birthtim->nsec = r->statbuf.st_birthtim.tv_nsec; + if( r->result < 0 ) { + hl_call2(void,c,int,hx_errno(r->result),vdynamic *,NULL); + } else { + vdynamic *stat = (vdynamic*)hl_alloc_dynobj(); + vdynamic *mode = hl_alloc_dynamic(&hlt_i64); + mode->v.i64 = r->statbuf.st_mode; + dyn_set_i64(stat, hl_hash_utf8("dev"), r->statbuf.st_dev); + dyn_set_i64(stat, hl_hash_utf8("mode"), r->statbuf.st_mode); + dyn_set_i64(stat, hl_hash_utf8("nlink"), r->statbuf.st_nlink); + dyn_set_i64(stat, hl_hash_utf8("uid"), r->statbuf.st_uid); + dyn_set_i64(stat, hl_hash_utf8("gid"), r->statbuf.st_gid); + dyn_set_i64(stat, hl_hash_utf8("rdev"), r->statbuf.st_rdev); + dyn_set_i64(stat, hl_hash_utf8("ino"), r->statbuf.st_ino); + dyn_set_i64(stat, hl_hash_utf8("size"), r->statbuf.st_size); + dyn_set_i64(stat, hl_hash_utf8("blksize"), r->statbuf.st_blksize); + dyn_set_i64(stat, hl_hash_utf8("blocks"), r->statbuf.st_blocks); + dyn_set_i64(stat, hl_hash_utf8("flags"), r->statbuf.st_flags); + dyn_set_i64(stat, hl_hash_utf8("gen"), r->statbuf.st_gen); + hl_dyn_setp(stat, hl_hash_utf8("atim"), &hlt_dyn, alloc_timespec_dyn(&r->statbuf.st_atim)); + hl_dyn_setp(stat, hl_hash_utf8("mtim"), &hlt_dyn, alloc_timespec_dyn(&r->statbuf.st_mtim)); + hl_dyn_setp(stat, hl_hash_utf8("ctim"), &hlt_dyn, alloc_timespec_dyn(&r->statbuf.st_ctim)); + hl_dyn_setp(stat, hl_hash_utf8("birthtim"), &hlt_dyn, alloc_timespec_dyn(&r->statbuf.st_birthtim)); + hl_call2(void,c,int,0,vdynamic *,stat); + } ev->data = NULL; - hl_call2(void,c,int,hx_errno(r->result),hx_stat *,(r->result<0?NULL:stat)); free_fs_req(r); } -HL_PRIM void HL_NAME(fs_stat_wrap)( uv_loop_t *loop, vstring *path, hx_stat *stat, vclosure *c ) { +HL_PRIM void HL_NAME(fs_stat_wrap)( uv_loop_t *loop, vstring *path, vclosure *c ) { UV_CHECK_NULL(loop,); UV_CHECK_NULL(path,); - UV_CHECK_NULL(stat,); UV_CHECK_NULL(c,); UV_ALLOC_REQ(uv_fs_t,r,c); - r->data = stat; UV_CHECK_ERROR(uv_fs_stat(loop,r,hl_to_utf8(path->bytes),on_fs_stat),free_fs_req(r),); } -DEFINE_PRIM(_VOID, fs_stat_wrap, _LOOP _STRING _STAT _FUN(_VOID,_I32 _STAT)); +DEFINE_PRIM(_VOID, fs_stat_wrap, _LOOP _STRING _FUN(_VOID,_I32 _DYN)); // Miscellaneous diff --git a/other/uvsample/FileSample.hx b/other/uvsample/FileSample.hx index 3d41189ac..9cb913766 100644 --- a/other/uvsample/FileSample.hx +++ b/other/uvsample/FileSample.hx @@ -1,3 +1,4 @@ +import hl.uv.Loop; import haxe.Constraints.Function; import haxe.xml.Access; import hl.I64; @@ -119,8 +120,8 @@ class FileSample { static function stat(actions:Actions) { var path = Misc.tmpDir(); - Log.print('Stat on path...'); - File.stat(loop,path, (e, stat) -> handle(() -> { + Log.print('Stat on $path...'); + File.stat(loop, path, (e, stat) -> handle(() -> { Log.print('Got stat: $stat'); Log.print('Done'); runNext(actions); diff --git a/other/uvsample/uvsample.hxml b/other/uvsample/uvsample.hxml index 4d621c415..7be5e9af2 100644 --- a/other/uvsample/uvsample.hxml +++ b/other/uvsample/uvsample.hxml @@ -1,3 +1,3 @@ -hl uvsample.hl -main UVSample --dce no \ No newline at end of file +-dce full \ No newline at end of file From 405c234ea436e75d3988774b4f2516dea30cd631 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Wed, 4 Aug 2021 19:47:54 +0300 Subject: [PATCH 036/117] finished fs --- libs/uv/uv.c | 386 ++++++++++++++++++++++++++++++++--- other/uvsample/FileSample.hx | 288 +++++++++++++++++++++++--- src/hl.h | 11 + 3 files changed, 621 insertions(+), 64 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index e967d87da..cbaf0bec7 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -35,6 +35,7 @@ typedef struct { #define _LOOP _ABSTRACT(uv_loop) #define _HANDLE _ABSTRACT(uv_handle) #define _REQUEST _ABSTRACT(uv_req) +#define _DIR _ABSTRACT(uv_dir) #define _SOCKADDR _ABSTRACT(uv_sockaddr_storage) #define _CALLB _FUN(_VOID,_NO_ARG) #define _TIMESPEC _OBJ(_I64 _I64) @@ -74,6 +75,10 @@ static void dyn_set_i64( vdynamic *obj, int field, int64 value ) { hl_dyn_setp(obj, field, &hlt_dyn, v); } +static int bytes_geti32( vbyte *b, int pos ) { + return b[pos] | (b[pos + 1] << 8) | (b[pos + 2] << 16) | (b[pos + 3] << 24); +} + // Errors static int errno_uv2hx( int uv_errno ) { @@ -1520,6 +1525,24 @@ static void on_fs_common( uv_fs_t *r ) { free_fs_req(r); } +static void on_fs_bytes_handled( uv_fs_t *r ) { + UV_GET_CLOSURE(c,r,0,"No callback in fs request"); + hl_call2(void,c,int,hx_errno(r->result),int64,r->result<0?0:r->result); + free_fs_req(r); +} + +static void on_fs_path( uv_fs_t *r ) { + UV_GET_CLOSURE(c,r,0,"No callback in fs request"); + hl_call2(void,c,int,hx_errno(r->result),vbyte *,(vbyte *)(r->result<0?NULL:r->path)); + free_fs_req(r); +} + +static void on_fs_bytes( uv_fs_t *r ) { + UV_GET_CLOSURE(c,r,0,"No callback in fs request"); + hl_call2(void,c,int,hx_errno(r->result),vbyte *,(vbyte *)(r->result<0?NULL:r->ptr)); + free_fs_req(r); +} + HL_PRIM void HL_NAME(fs_close_wrap)( uv_file file, uv_loop_t *loop, vclosure *c ) { UV_CHECK_NULL(loop,); UV_CHECK_NULL(c,); @@ -1528,35 +1551,23 @@ HL_PRIM void HL_NAME(fs_close_wrap)( uv_file file, uv_loop_t *loop, vclosure *c } DEFINE_PRIM(_VOID, fs_close_wrap, _I32 _LOOP _FUN(_VOID,_I32)); -static void on_fs_write( uv_fs_t *r ) { - UV_GET_CLOSURE(c,r,0,"No callback in fs_write request"); - hl_call2(void, c, int, hx_errno(r->result),int64,r->result<0?0:r->result); - free_fs_req(r); -} - HL_PRIM void HL_NAME(fs_write_wrap)( uv_file file, uv_loop_t *loop, vbyte *data, int length, int64 offset, vclosure *c ) { UV_CHECK_NULL(loop,); UV_CHECK_NULL(data,); UV_CHECK_NULL(c,); UV_ALLOC_REQ(uv_fs_t,r,c); UV_COPY_DATA(buf,r,data,length); - UV_CHECK_ERROR(uv_fs_write(loop,r,file,&buf,1,offset,on_fs_write),free_fs_req(r),); + UV_CHECK_ERROR(uv_fs_write(loop,r,file,&buf,1,offset,on_fs_bytes_handled),free_fs_req(r),); } DEFINE_PRIM(_VOID, fs_write_wrap, _I32 _LOOP _BYTES _I32 _I64 _FUN(_VOID,_I32 _I64)); -static void on_fs_read( uv_fs_t *r ) { - UV_GET_CLOSURE(c,r,0,"No callback in fs_read request"); - hl_call2(void,c,int,hx_errno(r->result),int64,r->result<0?0:r->result); - free_fs_req(r); -} - HL_PRIM void HL_NAME(fs_read_wrap)( uv_file file, uv_loop_t *loop, vbyte *buf, int length, int64 offset, vclosure *c ) { UV_CHECK_NULL(loop,); UV_CHECK_NULL(buf,); UV_CHECK_NULL(c,); UV_ALLOC_REQ(uv_fs_t,r,c); uv_buf_t b = uv_buf_init((char *)buf, length); - UV_CHECK_ERROR(uv_fs_read(loop,r,file,&b,1,offset,on_fs_read),free_fs_req(r),); + UV_CHECK_ERROR(uv_fs_read(loop,r,file,&b,1,offset,on_fs_bytes_handled),free_fs_req(r),); } DEFINE_PRIM(_VOID, fs_read_wrap, _I32 _LOOP _BYTES _I32 _I64 _FUN(_VOID,_I32 _I64)); @@ -1578,6 +1589,24 @@ HL_PRIM void HL_NAME(fs_mkdir_wrap)( uv_loop_t *loop, vstring *path, int mode, v } DEFINE_PRIM(_VOID, fs_mkdir_wrap, _LOOP _STRING _I32 _FUN(_VOID,_I32)); +HL_PRIM void HL_NAME(fs_mkdtemp_wrap)( uv_loop_t *loop, vstring *tpl, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(tpl,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + UV_CHECK_ERROR(uv_fs_mkdtemp(loop,r,hl_to_utf8(tpl->bytes),on_fs_path),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_mkdtemp_wrap, _LOOP _STRING _FUN(_VOID,_I32 _BYTES)); + +HL_PRIM void HL_NAME(fs_mkstemp_wrap)( uv_loop_t *loop, vstring *tpl, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(tpl,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + UV_CHECK_ERROR(uv_fs_mkstemp(loop,r,hl_to_utf8(tpl->bytes),on_fs_path),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_mkstemp_wrap, _LOOP _STRING _FUN(_VOID,_I32 _I32 _BYTES)); + HL_PRIM void HL_NAME(fs_rmdir_wrap)( uv_loop_t *loop, vstring *path, vclosure *c ) { UV_CHECK_NULL(loop,); UV_CHECK_NULL(path,); @@ -1587,52 +1616,90 @@ HL_PRIM void HL_NAME(fs_rmdir_wrap)( uv_loop_t *loop, vstring *path, vclosure *c } DEFINE_PRIM(_VOID, fs_rmdir_wrap, _LOOP _STRING _FUN(_VOID,_I32)); -static void on_fs_mkdtemp( uv_fs_t *r ) { - UV_GET_CLOSURE(c,r,0,"No callback in fs_mkdtemp request"); - hl_call2(void,c,int,hx_errno(r->result),vbyte *,(vbyte *)(r->result<0?NULL:r->path)); +static void on_fs_opendir( uv_fs_t *r ) { + UV_GET_CLOSURE(c,r,0,"No callback in fs_opendir request"); + hl_call2(void,c,int,hx_errno(r->result),uv_dir_t *,(r->result<0?NULL:r->ptr)); free_fs_req(r); } -HL_PRIM void HL_NAME(fs_mkdtemp_wrap)( uv_loop_t *loop, vstring *tpl, vclosure *c ) { +HL_PRIM void HL_NAME(fs_opendir_wrap)( uv_loop_t *loop, vstring *path, vclosure *c ) { UV_CHECK_NULL(loop,); - UV_CHECK_NULL(tpl,); + UV_CHECK_NULL(path,); UV_CHECK_NULL(c,); UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_mkdtemp(loop,r,hl_to_utf8(tpl->bytes),on_fs_mkdtemp),free_fs_req(r),); + UV_CHECK_ERROR(uv_fs_opendir(loop,r,hl_to_utf8(path->bytes),on_fs_opendir),free_fs_req(r),); } -DEFINE_PRIM(_VOID, fs_mkdtemp_wrap, _LOOP _STRING _FUN(_VOID,_I32 _BYTES)); +DEFINE_PRIM(_VOID, fs_opendir_wrap, _LOOP _STRING _FUN(_VOID,_I32 _DIR)); + +HL_PRIM void HL_NAME(fs_closedir_wrap)( uv_dir_t *dir, uv_loop_t *loop, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(dir,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + UV_CHECK_ERROR(uv_fs_closedir(loop,r,dir,on_fs_common),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_closedir_wrap, _DIR _LOOP _FUN(_VOID,_I32)); + +static void on_fs_readdir( uv_fs_t *r ) { + UV_GET_CLOSURE(c,r,0,"No callback in fs_readdir request"); + if( r->result < 0 ) { + hl_call2(void,c,int,hx_errno(r->result),vdynamic *,NULL); + } else { + uv_dir_t *dir = r->ptr; + varray *entries = hl_alloc_array(&hlt_dyn, r->result); + int hash_type = hl_hash_utf8("type"); + int hash_name = hl_hash_utf8("name"); + for(int i = 0; i < r->result; i++) { + vdynamic *entry = (vdynamic*)hl_alloc_dynobj(); + + int entry_type = 0; + switch( dir->dirents[i].type ) { + case UV_DIRENT_UNKNOWN: entry_type = 1; break; + case UV_DIRENT_FILE: entry_type = 2; break; + case UV_DIRENT_DIR: entry_type = 3; break; + case UV_DIRENT_LINK: entry_type = 4; break; + case UV_DIRENT_FIFO: entry_type = 5; break; + case UV_DIRENT_SOCKET: entry_type = 6; break; + case UV_DIRENT_CHAR: entry_type = 7; break; + case UV_DIRENT_BLOCK: entry_type = 8; break; + } + hl_dyn_seti(entry, hash_type, &hlt_i32, entry_type); -static void on_fs_mkstemp( uv_fs_t *r ) { - UV_GET_CLOSURE(c,r,0,"No callback in fs_mkstemp request"); - hl_call3(void,c,int,hx_errno(r->result),int,r->result,vbyte *,(vbyte *)(r->result<0?NULL:r->path)); + const char *c_name = dir->dirents[i].name; + vbyte *name = hl_copy_bytes((const vbyte *)c_name, strlen(c_name)); + hl_dyn_setp(entry, hash_name, &hlt_bytes, name); + + hl_aptr(entries,vdynamic *)[i] = entry; + } + hl_call2(void,c,int,0,varray *,entries); + } free_fs_req(r); } -HL_PRIM void HL_NAME(fs_mkstemp_wrap)( uv_loop_t *loop, vstring *tpl, vclosure *c ) { +HL_PRIM void HL_NAME(fs_readdir_wrap)( uv_dir_t *dir, uv_loop_t *loop, int num_entries, vclosure *c ) { UV_CHECK_NULL(loop,); - UV_CHECK_NULL(tpl,); + UV_CHECK_NULL(dir,); UV_CHECK_NULL(c,); UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_mkstemp(loop,r,hl_to_utf8(tpl->bytes),on_fs_mkstemp),free_fs_req(r),); + dir->nentries = num_entries; + dir->dirents = malloc(sizeof(uv_dirent_t) * num_entries); + UV_CHECK_ERROR(uv_fs_readdir(loop,r,dir,on_fs_readdir),free_fs_req(r),); } -DEFINE_PRIM(_VOID, fs_mkstemp_wrap, _LOOP _STRING _FUN(_VOID,_I32 _I32 _BYTES)); +DEFINE_PRIM(_VOID, fs_readdir_wrap, _DIR _LOOP _I32 _FUN(_VOID,_I32 _ARR)); static vdynamic *alloc_timespec_dyn( uv_timespec_t *spec ) { vdynamic *obj = (vdynamic*)hl_alloc_dynobj(); - dyn_set_i64(obj, hl_hash_utf8("sec"), spec->tv_sec); - dyn_set_i64(obj, hl_hash_utf8("nsec"), spec->tv_nsec); + hl_dyn_seti(obj, hl_hash_utf8("sec"), &hlt_i32, spec->tv_sec); + hl_dyn_seti(obj, hl_hash_utf8("nsec"), &hlt_i32, spec->tv_nsec); return obj; } static void on_fs_stat( uv_fs_t *r ) { UV_GET_CLOSURE(c,r,0,"No callback in fs stat request"); - events_data *ev = UV_DATA(r); if( r->result < 0 ) { hl_call2(void,c,int,hx_errno(r->result),vdynamic *,NULL); } else { vdynamic *stat = (vdynamic*)hl_alloc_dynobj(); - vdynamic *mode = hl_alloc_dynamic(&hlt_i64); - mode->v.i64 = r->statbuf.st_mode; dyn_set_i64(stat, hl_hash_utf8("dev"), r->statbuf.st_dev); dyn_set_i64(stat, hl_hash_utf8("mode"), r->statbuf.st_mode); dyn_set_i64(stat, hl_hash_utf8("nlink"), r->statbuf.st_nlink); @@ -1651,7 +1718,6 @@ static void on_fs_stat( uv_fs_t *r ) { hl_dyn_setp(stat, hl_hash_utf8("birthtim"), &hlt_dyn, alloc_timespec_dyn(&r->statbuf.st_birthtim)); hl_call2(void,c,int,0,vdynamic *,stat); } - ev->data = NULL; free_fs_req(r); } @@ -1664,6 +1730,258 @@ HL_PRIM void HL_NAME(fs_stat_wrap)( uv_loop_t *loop, vstring *path, vclosure *c } DEFINE_PRIM(_VOID, fs_stat_wrap, _LOOP _STRING _FUN(_VOID,_I32 _DYN)); +HL_PRIM void HL_NAME(fs_fstat_wrap)( uv_file file, uv_loop_t *loop, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + UV_CHECK_ERROR(uv_fs_fstat(loop,r,file,on_fs_stat),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_fstat_wrap, _I32 _LOOP _FUN(_VOID,_I32 _DYN)); + +HL_PRIM void HL_NAME(fs_lstat_wrap)( uv_loop_t *loop, vstring *path, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(path,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + UV_CHECK_ERROR(uv_fs_lstat(loop,r,hl_to_utf8(path->bytes),on_fs_stat),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_lstat_wrap, _LOOP _STRING _FUN(_VOID,_I32 _DYN)); + +static void on_fs_statfs( uv_fs_t *r ) { + UV_GET_CLOSURE(c,r,0,"No callback in statfs request"); + events_data *ev = UV_DATA(r); + if( r->result < 0 ) { + hl_call2(void,c,int,hx_errno(r->result),vdynamic *,NULL); + } else { + uv_statfs_t *stat = r->ptr; + vdynamic *obj = (vdynamic *)hl_alloc_dynobj(); + dyn_set_i64(obj, hl_hash_utf8("type"), stat->f_type); + dyn_set_i64(obj, hl_hash_utf8("bsize"), stat->f_bsize); + dyn_set_i64(obj, hl_hash_utf8("blocks"), stat->f_blocks); + dyn_set_i64(obj, hl_hash_utf8("bfree"), stat->f_bfree); + dyn_set_i64(obj, hl_hash_utf8("bavail"), stat->f_bavail); + dyn_set_i64(obj, hl_hash_utf8("files"), stat->f_files); + dyn_set_i64(obj, hl_hash_utf8("ffree"), stat->f_ffree); + + varray *spare = hl_alloc_array(&hlt_i64, 4); + for (int i = 0; i < 4; i++) + hl_aptr(spare,int64)[i] = stat->f_spare[i]; + hl_dyn_setp(obj, hl_hash_utf8("spare"), &hlt_array, spare); + + hl_call2(void,c,int,0,vdynamic *,obj); + } + ev->data = NULL; + free_fs_req(r); +} + +HL_PRIM void HL_NAME(fs_statfs_wrap)( uv_loop_t *loop, vstring *path, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(path,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + UV_CHECK_ERROR(uv_fs_statfs(loop,r,hl_to_utf8(path->bytes),on_fs_statfs),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_statfs_wrap, _LOOP _STRING _FUN(_VOID,_I32 _DYN)); + +HL_PRIM void HL_NAME(fs_rename_wrap)( uv_loop_t *loop, vstring *path, vstring *new_path, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(path,); + UV_CHECK_NULL(new_path,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + UV_CHECK_ERROR(uv_fs_rename(loop,r,hl_to_utf8(path->bytes),hl_to_utf8(new_path->bytes),on_fs_common),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_rename_wrap, _LOOP _STRING _STRING _FUN(_VOID,_I32)); + +HL_PRIM void HL_NAME(fs_fsync_wrap)( uv_file file, uv_loop_t *loop, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + UV_CHECK_ERROR(uv_fs_fsync(loop,r,file,on_fs_common),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_fsync_wrap, _I32 _LOOP _FUN(_VOID,_I32)); + +HL_PRIM void HL_NAME(fs_fdatasync_wrap)( uv_file file, uv_loop_t *loop, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + UV_CHECK_ERROR(uv_fs_fdatasync(loop,r,file,on_fs_common),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_fdatasync_wrap, _I32 _LOOP _FUN(_VOID,_I32)); + +HL_PRIM void HL_NAME(fs_ftruncate_wrap)( uv_file file, uv_loop_t *loop, int64 offset, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + UV_CHECK_ERROR(uv_fs_ftruncate(loop,r,file,offset,on_fs_common),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_ftruncate_wrap, _I32 _LOOP _I64 _FUN(_VOID,_I32)); + +HL_PRIM void HL_NAME(fs_copyfile_wrap)( uv_loop_t *loop, vstring *path, vstring *new_path, varray_bytes *flags, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(path,); + UV_CHECK_NULL(new_path,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + int i_flags = 0; + if( flags ) { + for(int i = 0; i < flags->length; i++) { + switch( bytes_geti32(flags->bytes, i * 4) ) { + case 1: i_flags |= UV_FS_COPYFILE_EXCL; break; + case 2: i_flags |= UV_FS_COPYFILE_FICLONE; break; + case 3: i_flags |= UV_FS_COPYFILE_FICLONE_FORCE; break; + } + } + } + UV_CHECK_ERROR(uv_fs_copyfile(loop,r,hl_to_utf8(path->bytes),hl_to_utf8(new_path->bytes),i_flags,on_fs_common),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_copyfile_wrap, _LOOP _STRING _STRING _ARRBYTES _FUN(_VOID,_I32)); + +HL_PRIM void HL_NAME(fs_sendfile_wrap)( uv_file src, uv_loop_t *loop, uv_file dst, int64 in_offset, int64 length, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + UV_CHECK_ERROR(uv_fs_sendfile(loop,r,src,dst,in_offset,length,on_fs_bytes_handled),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_sendfile_wrap, _I32 _LOOP _I32 _I64 _I64 _FUN(_VOID,_I32 _I64)); + +HL_PRIM void HL_NAME(fs_access_wrap)( uv_loop_t *loop, vstring *path, varray_bytes *mode, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(path,); + UV_CHECK_NULL(mode,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + int i_mode = 0; + if( mode ) { + for(int i = 0; i < mode->length; i++) { + switch( bytes_geti32(mode->bytes, i * 4) ) { + case 0: i_mode |= F_OK; break; + case 1: i_mode |= R_OK; break; + case 2: i_mode |= W_OK; break; + case 3: i_mode |= X_OK; break; + } + } + } + UV_CHECK_ERROR(uv_fs_access(loop,r,hl_to_utf8(path->bytes),i_mode,on_fs_common),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_access_wrap, _LOOP _STRING _ARRBYTES _FUN(_VOID,_I32)); + +HL_PRIM void HL_NAME(fs_chmod_wrap)( uv_loop_t *loop, vstring *path, int mode, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(path,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + UV_CHECK_ERROR(uv_fs_chmod(loop,r,hl_to_utf8(path->bytes),mode,on_fs_common),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_chmod_wrap, _LOOP _STRING _I32 _FUN(_VOID,_I32)); + +HL_PRIM void HL_NAME(fs_fchmod_wrap)( uv_file file, uv_loop_t *loop, int mode, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + UV_CHECK_ERROR(uv_fs_fchmod(loop,r,file,mode,on_fs_common),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_fchmod_wrap, _I32 _LOOP _I32 _FUN(_VOID,_I32)); + +HL_PRIM void HL_NAME(fs_utime_wrap)( uv_loop_t *loop, vstring *path, double atime, double mtime, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(path,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + UV_CHECK_ERROR(uv_fs_utime(loop,r,hl_to_utf8(path->bytes),atime,mtime,on_fs_common),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_utime_wrap, _LOOP _STRING _F64 _F64 _FUN(_VOID,_I32)); + +HL_PRIM void HL_NAME(fs_futime_wrap)( uv_file file, uv_loop_t *loop, double atime, double mtime, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + UV_CHECK_ERROR(uv_fs_futime(loop,r,file,atime,mtime,on_fs_common),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_futime_wrap, _I32 _LOOP _F64 _F64 _FUN(_VOID,_I32)); + +HL_PRIM void HL_NAME(fs_lutime_wrap)( uv_loop_t *loop, vstring *path, double atime, double mtime, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(path,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + UV_CHECK_ERROR(uv_fs_lutime(loop,r,hl_to_utf8(path->bytes),atime,mtime,on_fs_common),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_lutime_wrap, _LOOP _STRING _F64 _F64 _FUN(_VOID,_I32)); + +HL_PRIM void HL_NAME(fs_link_wrap)( uv_loop_t *loop, vstring *path, vstring *link, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(path,); + UV_CHECK_NULL(link,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + UV_CHECK_ERROR(uv_fs_link(loop,r,hl_to_utf8(path->bytes),hl_to_utf8(link->bytes),on_fs_common),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_link_wrap, _LOOP _STRING _STRING _FUN(_VOID,_I32)); + +HL_PRIM void HL_NAME(fs_symlink_wrap)( uv_loop_t *loop, vstring *path, vstring *link, varray_bytes *flags, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(path,); + UV_CHECK_NULL(link,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + int i_flags = 0; + if( flags ) { + for(int i = 0; i < flags->length; i++) { + switch( bytes_geti32(flags->bytes, i * 4) ) { + case 0: i_flags |= UV_FS_SYMLINK_DIR; break; + case 1: i_flags |= UV_FS_SYMLINK_JUNCTION; break; + } + } + } + UV_CHECK_ERROR(uv_fs_symlink(loop,r,hl_to_utf8(path->bytes),hl_to_utf8(link->bytes),i_flags,on_fs_common),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_symlink_wrap, _LOOP _STRING _STRING _ARRBYTES _FUN(_VOID,_I32)); + +HL_PRIM void HL_NAME(fs_readlink_wrap)( uv_loop_t *loop, vstring *path, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(path,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + UV_CHECK_ERROR(uv_fs_readlink(loop,r,hl_to_utf8(path->bytes),on_fs_bytes),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_readlink_wrap, _LOOP _STRING _FUN(_VOID,_I32 _BYTES)); + +HL_PRIM void HL_NAME(fs_realpath_wrap)( uv_loop_t *loop, vstring *path, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(path,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + UV_CHECK_ERROR(uv_fs_realpath(loop,r,hl_to_utf8(path->bytes),on_fs_bytes),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_realpath_wrap, _LOOP _STRING _FUN(_VOID,_I32 _BYTES)); + +HL_PRIM void HL_NAME(fs_chown_wrap)( uv_loop_t *loop, vstring *path, int uid, int gid, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(path,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + UV_CHECK_ERROR(uv_fs_chown(loop,r,hl_to_utf8(path->bytes),uid,gid,on_fs_common),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_chown_wrap, _LOOP _STRING _I32 _I32 _FUN(_VOID,_I32)); + +HL_PRIM void HL_NAME(fs_fchown_wrap)( uv_file file, uv_loop_t *loop, int uid, int gid, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + UV_CHECK_ERROR(uv_fs_fchown(loop,r,file,uid,gid,on_fs_common),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_fchown_wrap, _I32 _LOOP _I32 _I32 _FUN(_VOID,_I32)); + +HL_PRIM void HL_NAME(fs_lchown_wrap)( uv_loop_t *loop, vstring *path, int uid, int gid, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(path,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_fs_t,r,c); + UV_CHECK_ERROR(uv_fs_lchown(loop,r,hl_to_utf8(path->bytes),uid,gid,on_fs_common),free_fs_req(r),); +} +DEFINE_PRIM(_VOID, fs_lchown_wrap, _LOOP _STRING _I32 _I32 _FUN(_VOID,_I32)); + // Miscellaneous HL_PRIM vbyte *HL_NAME(os_tmpdir_wrap)() { diff --git a/other/uvsample/FileSample.hx b/other/uvsample/FileSample.hx index 9cb913766..2e4fb036f 100644 --- a/other/uvsample/FileSample.hx +++ b/other/uvsample/FileSample.hx @@ -1,3 +1,5 @@ +import hl.uv.Dir; +import sys.io.Process; import hl.uv.Loop; import haxe.Constraints.Function; import haxe.xml.Access; @@ -10,20 +12,38 @@ import hl.uv.UVException; import sys.thread.Thread; import hl.uv.File; -typedef Actions = Array; +abstract Actions(Array) from Array { + public function next() { + var fn = this.shift(); + if(fn != null) { + Log.print('-----------'); + fn(this); + } + } +} class FileSample { static final loop = Thread.current().events; public static function main() { var actions:Actions = [ - createWriteReadUnlink, - mkdirRmdir, - mkdtempRmdir, - mkstempUnlink, - stat, + // createWriteSyncReadUnlink, + // mkdirRenameRmdir, + // mkdtempRmdir, + // mkstempUnlink, + // statFStat, + // statFs, + // truncate, + // copyFile, + // sendFile, // Throws EBADF and idk why + // access, + // chmod, + // utime, + // linkSymlinkReadLinkRealPath, + // chown, + openReadCloseDir, ]; - runNext(actions); + actions.next(); } static function handle(success:()->Void, ?p:PosInfos):(e:UVError)->Void { @@ -33,25 +53,53 @@ class FileSample { } } - static function runNext(actions:Actions) { - var fn = actions.shift(); - if(fn != null) { - Log.print('-----------'); - fn(actions); + static function createFile(path:String, content:Bytes, callback:()->Void) { + File.open(loop, path, [O_CREAT(420),O_TRUNC,O_WRONLY], (e, file) -> handle(() -> { + file.write(loop, content.getData(), content.length, I64.ofInt(0), (e, bytesWritten) -> handle(() -> { + file.close(loop, handle(callback)); + })(e)); + })(e)); + } + + static function readFile(path:String, callback:(data:Bytes)->Void) { + File.open(loop, path, [O_RDONLY], (e, file) -> handle(() -> { + var buf = new hl.Bytes(10240); + file.read(loop, buf, 10240, I64.ofInt(0), (e, bytesRead) -> handle(() -> { + file.close(loop, handle(() -> { + callback(buf.toBytes(bytesRead.toInt())); + })); + })(e)); + })(e)); + } + + static function deleteFiles(files:Array, callback:()->Void) { + var finished = 0; + for(path in files) { + File.unlink(loop, path, handle(() -> { + ++finished; + if(finished == files.length) + callback(); + })); } } - static function createWriteReadUnlink(actions:Actions) { + static function createWriteSyncReadUnlink(actions:Actions) { var path = Misc.tmpDir() + '/test-file'; Log.print('Creating $path for writing...'); - File.open(loop, path, [O_CREAT(511), O_WRONLY], (e, file) -> handle(() -> { + File.open(loop, path, [O_CREAT(420), O_WRONLY], (e, file) -> handle(() -> { Log.print('Writing...'); var data = Bytes.ofString('Hello, world!'); file.write(loop, data.getData(), data.length, I64.ofInt(0), (e, bytesWritten) -> handle(() -> { Log.print('$bytesWritten bytes written: $data'); - file.close(loop, handle(() -> { - Log.print('closed $path'); - readUnlink(path, actions); + Log.print('fsync...'); + file.fsync(loop, handle(() -> { + Log.print('fdatasync...'); + file.fdataSync(loop, handle(() -> { + file.close(loop, handle(() -> { + Log.print('closed $path'); + readUnlink(path, actions); + })); + })); })); })(e)); })(e)); @@ -75,18 +123,22 @@ class FileSample { static function unlink(path:String, actions:Actions) { Log.print('Unlinking $path...'); File.unlink(loop, path, handle(() -> { - runNext(actions); + actions.next(); })); } - static function mkdirRmdir(actions:Actions) { + static function mkdirRenameRmdir(actions:Actions) { var path = Misc.tmpDir() + '/test-dir'; + var newPath = Misc.tmpDir() + '/test-dir2'; Log.print('Creating directory $path...'); File.mkdir(loop, path, 511, handle(() -> { - Log.print('Removing directory $path...'); - File.rmdir(loop, path, handle(() -> { - Log.print('Done'); - runNext(actions); + Log.print('Renaming $path to $newPath...'); + File.rename(loop, path, newPath, handle(() -> { + Log.print('Removing directory $newPath...'); + File.rmdir(loop, newPath, handle(() -> { + Log.print('Done'); + actions.next(); + })); })); })); } @@ -98,7 +150,7 @@ class FileSample { Log.print('Removing directory $path...'); File.rmdir(loop, path, handle(() -> { Log.print('Done'); - runNext(actions); + actions.next(); })); })(e)); } @@ -112,19 +164,195 @@ class FileSample { Log.print('Unlinking $path...'); File.unlink(loop, path, handle(() -> { Log.print('Done'); - runNext(actions); + actions.next(); })); })); })(e)); } - static function stat(actions:Actions) { + static function statFStat(actions:Actions) { + var path = Misc.tmpDir() + '/test-file'; + Log.print('fstat on $path...'); + File.open(loop, path, [O_CREAT(420)], (e, file) -> handle(() -> { + file.fstat(loop, (e, fstat) -> handle(() -> { + Log.print('got fstat'); + file.close(loop, handle(() -> { + Log.print('stat on $path'); + File.stat(loop, path, (e, stat) -> handle(() -> { + Log.print('got stat: $stat'); + // TODO: jit error on I64 == I64 + // var ok = stat.dev == fstat.dev; + // && stat.mode == fstat.mode + // && stat.nlink == fstat.nlink + // && stat.uid == fstat.uid + // && stat.gid == fstat.gid + // && stat.rdev == fstat.rdev + // && stat.ino == fstat.ino + // && stat.size == fstat.size + // && stat.blksize == fstat.blksize + // && stat.blocks == fstat.blocks + // && stat.flags == fstat.flags + // && stat.gen == fstat.gen; + // Log.print('fstat equals stat: $ok'); + Log.print('Done'); + actions.next(); + })(e)); + })); + })(e)); + })(e)); + } + + static function statFs(actions:Actions) { + Log.print('statfs on .'); + File.statFs(loop, '.', (e, stat) -> handle(() -> { + Log.print('got statfs: $stat'); + Log.print('Done'); + actions.next(); + })(e)); + } + + static function truncate(actions:Actions) { + var path = Misc.tmpDir() + '/test-file'; + var content = '1234567890'; + Log.print('Writing content for truncation at $path: $content'); + createFile(path, Bytes.ofString(content), () -> { + File.open(loop, path, [O_WRONLY], (e, file) -> handle(() -> { + Log.print('truncating at 5...'); + file.ftruncate(loop, I64.ofInt(5), handle(() -> { + file.close(loop, handle(() -> { + readFile(path, data -> { + Log.print('Content after truncation (length=${data.length}): $data'); + Log.print('Done'); + actions.next(); + }); + })); + })); + })(e)); + }); + } + + static function copyFile(actions:Actions) { + var path = Misc.tmpDir() + '/test-file'; + var newPath = '$path-copy'; + createFile(path, Bytes.ofString('123'), () -> { + Log.print('Copy $path to $newPath'); + File.copyFile(loop, path, newPath, [EXCL], handle(() -> { + deleteFiles([path, newPath], () -> { + Log.print('Done'); + actions.next(); + }); + })); + }); + } + + static function sendFile(actions:Actions) { + var path = Misc.tmpDir() + '/test-file'; + var newPath = '$path-copy'; + createFile(path, Bytes.ofString('12345678'), () -> { + File.open(loop, path, [O_RDONLY], (e, src) -> handle(() -> { + File.open(loop, newPath, [O_CREAT(420), O_WRONLY], (e, dst) -> handle(() -> { + Log.print('sendFile from $path to $newPath...'); + src.sendFile(loop, dst, I64.ofInt(0), I64.ofInt(20), (e, outOffset) -> handle(() -> { + Log.print('sendfile stopped at $outOffset'); + src.close(loop, handle(() -> { + dst.close(loop, handle(() -> { + deleteFiles([path, newPath], () -> { + Log.print('Done'); + actions.next(); + }); + })); + })); + })(e)); + })(e)); + })(e)); + }); + } + + static function access(actions:Actions) { var path = Misc.tmpDir(); - Log.print('Stat on $path...'); - File.stat(loop, path, (e, stat) -> handle(() -> { - Log.print('Got stat: $stat'); + Log.print('Checking write permissions on $path...'); + File.access(loop, path, [W_OK], handle(() -> { Log.print('Done'); - runNext(actions); + actions.next(); + })); + } + + static function chmod(actions:Actions) { + var path = Misc.tmpDir() + '/test-file'; + createFile(path, Bytes.ofString('123'), () -> { + Log.print('chmod on $path...'); + File.chmod(loop, path, 420, handle(() -> { + Log.print('Done'); + actions.next(); + })); + }); + } + + static function utime(actions:Actions) { + var path = Misc.tmpDir() + '/test-file'; + createFile(path, Bytes.ofString('123'), () -> { + Log.print('utime on $path...'); + File.utime(loop, path, Date.now().getTime(), Date.now().getTime(), handle(() -> { + Log.print('Done'); + actions.next(); + })); + }); + } + + static function linkSymlinkReadLinkRealPath(actions:Actions) { + var path = Misc.tmpDir() + '/test-file'; + var newPath = Misc.tmpDir() + '/test-file-link'; + createFile(path, Bytes.ofString('123'), () -> { + Log.print('link $path to $newPath...'); + File.link(loop, path, newPath, handle(() -> { + deleteFiles([newPath], () -> { + Log.print('symlink $path to $newPath...'); + File.symlink(loop, path, newPath, [SYMLINK_JUNCTION], handle(() -> { + Log.print('readlink at $newPath...'); + File.readLink(loop, newPath, (e, target) -> handle(() -> { + Log.print('Link content: $target'); + File.readLink(loop, newPath, (e, real) -> handle(() -> { + Log.print('Real path of $newPath: $real'); + deleteFiles([path, newPath], () -> { + Log.print('Done'); + actions.next(); + }); + })(e)); + })(e)); + })); + }); + })); + }); + } + + static function chown(actions:Actions) { + if(Sys.systemName() == 'Windows') { + actions.next(); + return; + } + + var path = Misc.tmpDir() + '/test-file'; + createFile(path, Bytes.ofString(''), () -> { + Log.print('chown on $path...'); + File.chown(loop, path, -1, -1, handle(() -> { + Log.print('Done'); + actions.next(); + })); + }); + } + + static function openReadCloseDir(actions:Actions) { + var path = Misc.tmpDir(); + Log.print('Reading 3 entries from dir $path...'); + Dir.open(loop, path, (e, dir) -> handle(() -> { + dir.read(loop, 3, (e, entries) -> handle(() -> { + for(i in 0...entries.length) + Log.print('\t${entries[i]}'); + dir.close(loop, handle(() -> { + Log.print('Done'); + actions.next(); + })); + })(e)); })(e)); } } \ No newline at end of file diff --git a/src/hl.h b/src/hl.h index e534c2ceb..68245e885 100644 --- a/src/hl.h +++ b/src/hl.h @@ -785,6 +785,17 @@ typedef struct { int length; } vstring; +#undef _ARRBYTES +#define _ARRBYTES _OBJ(_I32 _BYTES _I32) + +// hl.types.ArrayBytes +typedef struct { + hl_type *t; + int length; + vbyte *bytes; + int size; +} varray_bytes; + #define DEFINE_PRIM(t,name,args) DEFINE_PRIM_WITH_NAME(t,name,args,name) #define _DEFINE_PRIM_WITH_NAME(t,name,args,realName) C_FUNCTION_BEGIN EXPORT void *hlp_##realName( const char **sign ) { *sign = _FUN(t,args); return (void*)(&HL_NAME(name)); } C_FUNCTION_END From ba5735811c6bd30e906bcf4e8be4bdaf86dc5db0 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Wed, 4 Aug 2021 19:53:00 +0300 Subject: [PATCH 037/117] fix mkstemp --- libs/uv/uv.c | 8 +++++++- other/uvsample/FileSample.hx | 26 +++++++++++++------------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index cbaf0bec7..d74ebe95a 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -1598,12 +1598,18 @@ HL_PRIM void HL_NAME(fs_mkdtemp_wrap)( uv_loop_t *loop, vstring *tpl, vclosure * } DEFINE_PRIM(_VOID, fs_mkdtemp_wrap, _LOOP _STRING _FUN(_VOID,_I32 _BYTES)); +static void on_fs_mkstemp( uv_fs_t *r ) { + UV_GET_CLOSURE(c,r,0,"No callback in fs_mkstemp request"); + hl_call3(void,c,int,hx_errno(r->result),int,r->result,vbyte *,(vbyte *)(r->result<0?NULL:r->path)); + free_fs_req(r); +} + HL_PRIM void HL_NAME(fs_mkstemp_wrap)( uv_loop_t *loop, vstring *tpl, vclosure *c ) { UV_CHECK_NULL(loop,); UV_CHECK_NULL(tpl,); UV_CHECK_NULL(c,); UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_mkstemp(loop,r,hl_to_utf8(tpl->bytes),on_fs_path),free_fs_req(r),); + UV_CHECK_ERROR(uv_fs_mkstemp(loop,r,hl_to_utf8(tpl->bytes),on_fs_mkstemp),free_fs_req(r),); } DEFINE_PRIM(_VOID, fs_mkstemp_wrap, _LOOP _STRING _FUN(_VOID,_I32 _I32 _BYTES)); diff --git a/other/uvsample/FileSample.hx b/other/uvsample/FileSample.hx index 2e4fb036f..5e76b6802 100644 --- a/other/uvsample/FileSample.hx +++ b/other/uvsample/FileSample.hx @@ -27,20 +27,20 @@ class FileSample { public static function main() { var actions:Actions = [ - // createWriteSyncReadUnlink, - // mkdirRenameRmdir, - // mkdtempRmdir, - // mkstempUnlink, - // statFStat, - // statFs, - // truncate, - // copyFile, + createWriteSyncReadUnlink, + mkdirRenameRmdir, + mkdtempRmdir, + mkstempUnlink, + statFStat, + statFs, + truncate, + copyFile, // sendFile, // Throws EBADF and idk why - // access, - // chmod, - // utime, - // linkSymlinkReadLinkRealPath, - // chown, + access, + chmod, + utime, + linkSymlinkReadLinkRealPath, + chown, openReadCloseDir, ]; actions.next(); From d7909e94025a1c88cbb87b8a46e032a4925db6ee Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Mon, 9 Aug 2021 21:56:01 +0300 Subject: [PATCH 038/117] custom exceptions (for hl.uv.UVException) --- libs/uv/uv.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index d74ebe95a..fc77226bb 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -172,9 +172,20 @@ static int hx_errno( int result ) { return result < 0 ? errno_uv2hx(result) : 0; } +static vclosure *c_exception; + +HL_PRIM void HL_NAME(init_exception)( vclosure *c ) { + c_exception = c; +} +DEFINE_PRIM(_VOID, init_exception, _FUN(_DYN, _I32)); + static void hx_error(int uv_errno) { - //TODO: throw hl.uv.UVException - hl_error("%s", hl_to_utf16(uv_err_name(uv_errno))); + if( c_exception ) { + vdynamic *exc = hl_call1(vdynamic *, c_exception, int, errno_uv2hx(uv_errno)); + hl_throw(exc); + } else { + hl_error("%s", hl_to_utf16(uv_err_name(uv_errno))); + } } DEFINE_PRIM(_BYTES, strerror, _I32); From 7009e1b6098fa52191596f058069658cc1f7b801 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Tue, 10 Aug 2021 07:33:36 +0300 Subject: [PATCH 039/117] better cleanups in FileSample --- other/uvsample/FileSample.hx | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/other/uvsample/FileSample.hx b/other/uvsample/FileSample.hx index 5e76b6802..d51a3200f 100644 --- a/other/uvsample/FileSample.hx +++ b/other/uvsample/FileSample.hx @@ -194,8 +194,10 @@ class FileSample { // && stat.flags == fstat.flags // && stat.gen == fstat.gen; // Log.print('fstat equals stat: $ok'); - Log.print('Done'); - actions.next(); + deleteFiles([path], () -> { + Log.print('Done'); + actions.next(); + }); })(e)); })); })(e)); @@ -222,8 +224,10 @@ class FileSample { file.close(loop, handle(() -> { readFile(path, data -> { Log.print('Content after truncation (length=${data.length}): $data'); - Log.print('Done'); - actions.next(); + deleteFiles([path], () -> { + Log.print('Done'); + actions.next(); + }); }); })); })); @@ -282,8 +286,10 @@ class FileSample { createFile(path, Bytes.ofString('123'), () -> { Log.print('chmod on $path...'); File.chmod(loop, path, 420, handle(() -> { - Log.print('Done'); - actions.next(); + deleteFiles([path], () -> { + Log.print('Done'); + actions.next(); + }); })); }); } @@ -293,8 +299,10 @@ class FileSample { createFile(path, Bytes.ofString('123'), () -> { Log.print('utime on $path...'); File.utime(loop, path, Date.now().getTime(), Date.now().getTime(), handle(() -> { - Log.print('Done'); - actions.next(); + deleteFiles([path], () -> { + Log.print('Done'); + actions.next(); + }); })); }); } @@ -335,8 +343,10 @@ class FileSample { createFile(path, Bytes.ofString(''), () -> { Log.print('chown on $path...'); File.chown(loop, path, -1, -1, handle(() -> { - Log.print('Done'); - actions.next(); + deleteFiles([path], () -> { + Log.print('Done'); + actions.next(); + }); })); }); } From 0a495cf084fc9470d2ef923420bc448c451fc355 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Tue, 10 Aug 2021 07:34:00 +0300 Subject: [PATCH 040/117] FS event --- libs/uv/uv.c | 53 +++++++++++++++++++++++++++++++++ other/uvsample/FsEventSample.hx | 25 ++++++++++++++++ other/uvsample/UVSample.hx | 3 +- 3 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 other/uvsample/FsEventSample.hx diff --git a/libs/uv/uv.c b/libs/uv/uv.c index fc77226bb..7697bef9e 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -1999,6 +1999,59 @@ HL_PRIM void HL_NAME(fs_lchown_wrap)( uv_loop_t *loop, vstring *path, int uid, i } DEFINE_PRIM(_VOID, fs_lchown_wrap, _LOOP _STRING _I32 _I32 _FUN(_VOID,_I32)); +// Fs event + +HL_PRIM uv_fs_event_t *HL_NAME(fs_event_init_wrap)( uv_loop_t *loop ) { + UV_CHECK_NULL(loop,NULL); + uv_fs_event_t *h = UV_ALLOC(uv_fs_event_t); + UV_CHECK_ERROR(uv_fs_event_init(loop,h),free(h),NULL); + handle_init_hl_data((uv_handle_t*)h); + return h; +} +DEFINE_PRIM(_HANDLE, fs_event_init_wrap, _LOOP); + +static void on_fs_event( uv_fs_event_t *h, const char *filename, int events, int status ) { + UV_GET_CLOSURE(c,h,0,"No callback in fs_event handle"); + if( status < 0 ) { + hl_call3(void,c,int,errno_uv2hx(status),vbyte *,NULL,varray *,NULL); + } else { + int size = (0 != (UV_RENAME & events)) + (0 != (UV_CHANGE & events)); + varray *a_events = hl_alloc_array(&hlt_i32, size); + int i = 0; + if( UV_RENAME & events ) + hl_aptr(a_events,int)[i++] = UV_RENAME; + if( UV_CHANGE & events ) + hl_aptr(a_events,int)[i++] = UV_CHANGE; + vbyte *path = hl_copy_bytes((const vbyte *)filename, strlen(filename)); + hl_call3(void,c,int,0,vbyte *,path,varray *,a_events); + } +} + +HL_PRIM void HL_NAME(fs_event_start_wrap)( uv_fs_event_t *h, vstring *path, varray_bytes *flags, vclosure *c ) { + UV_CHECK_NULL(h,); + UV_CHECK_NULL(path,); + UV_CHECK_NULL(c,); + handle_register_callback((uv_handle_t*)h,c,0); + unsigned int i_flags = 0; + if( flags ) { + for(int i = 0; i < flags->length; i++) { + switch( bytes_geti32(flags->bytes, i * 4) ) { + case 1: i_flags |= UV_FS_EVENT_WATCH_ENTRY; break; + case 2: i_flags |= UV_FS_EVENT_STAT; break; + case 3: i_flags |= UV_FS_EVENT_RECURSIVE; break; + } + } + } + UV_CHECK_ERROR(uv_fs_event_start(h,on_fs_event,hl_to_utf8(path->bytes),i_flags),handle_clear_callback((uv_handle_t *)h,0),); +} +DEFINE_PRIM(_VOID, fs_event_start_wrap, _HANDLE _STRING _ARRBYTES _FUN(_VOID,_I32 _BYTES _ARR)); + +HL_PRIM void HL_NAME(fs_event_stop_wrap)( uv_fs_event_t *h ) { + UV_CHECK_NULL(h,); + UV_CHECK_ERROR(uv_fs_event_stop(h),,); +} +DEFINE_PRIM(_VOID, fs_event_stop_wrap, _HANDLE); + // Miscellaneous HL_PRIM vbyte *HL_NAME(os_tmpdir_wrap)() { diff --git a/other/uvsample/FsEventSample.hx b/other/uvsample/FsEventSample.hx new file mode 100644 index 000000000..f643b26b0 --- /dev/null +++ b/other/uvsample/FsEventSample.hx @@ -0,0 +1,25 @@ +import sys.FileSystem; +import haxe.Timer; +import sys.io.File; +import hl.uv.Misc; +import hl.uv.FsEvent; +import hl.uv.UVException; +import sys.thread.Thread; + +class FsEventSample { + public static function main() { + var loop = Thread.current().events; + var event = FsEvent.init(loop); + var path = Misc.tmpDir() + '/test-file'; + File.saveContent(path, 'Hello, world'); + event.start(path, null, (e, path, events) -> switch e { + case UV_NOERR: + Log.print('FS events $events on $path'); + event.stop(); + event.close(); + case _: + throw new UVException(e); + }); + Timer.delay(FileSystem.deleteFile.bind(path), 50); + } +} \ No newline at end of file diff --git a/other/uvsample/UVSample.hx b/other/uvsample/UVSample.hx index d71482fc9..208acae1b 100644 --- a/other/uvsample/UVSample.hx +++ b/other/uvsample/UVSample.hx @@ -5,6 +5,7 @@ class UVSample { // UdpSample.main(); // PipeSample.main(); // ProcessSample.main(); - FileSample.main(); + // FileSample.main(); + FsEventSample.main(); } } \ No newline at end of file From d3d2049c213aa40a3dd46d109131b06b87ccf2f9 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Tue, 10 Aug 2021 07:59:32 +0300 Subject: [PATCH 041/117] fs poll --- libs/uv/uv.c | 73 +++++++++++++++++++++++++--------- other/uvsample/FsPollSample.hx | 28 +++++++++++++ other/uvsample/UVSample.hx | 3 +- 3 files changed, 84 insertions(+), 20 deletions(-) create mode 100644 other/uvsample/FsPollSample.hx diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 7697bef9e..2e11a2596 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -1704,36 +1704,40 @@ HL_PRIM void HL_NAME(fs_readdir_wrap)( uv_dir_t *dir, uv_loop_t *loop, int num_e } DEFINE_PRIM(_VOID, fs_readdir_wrap, _DIR _LOOP _I32 _FUN(_VOID,_I32 _ARR)); -static vdynamic *alloc_timespec_dyn( uv_timespec_t *spec ) { +static vdynamic *alloc_timespec_dyn( const uv_timespec_t *spec ) { vdynamic *obj = (vdynamic*)hl_alloc_dynobj(); hl_dyn_seti(obj, hl_hash_utf8("sec"), &hlt_i32, spec->tv_sec); hl_dyn_seti(obj, hl_hash_utf8("nsec"), &hlt_i32, spec->tv_nsec); return obj; } +static vdynamic *alloc_stat( const uv_stat_t *stat ) { + vdynamic *obj = (vdynamic*)hl_alloc_dynobj(); + dyn_set_i64(obj, hl_hash_utf8("dev"), stat->st_dev); + dyn_set_i64(obj, hl_hash_utf8("mode"), stat->st_mode); + dyn_set_i64(obj, hl_hash_utf8("nlink"), stat->st_nlink); + dyn_set_i64(obj, hl_hash_utf8("uid"), stat->st_uid); + dyn_set_i64(obj, hl_hash_utf8("gid"), stat->st_gid); + dyn_set_i64(obj, hl_hash_utf8("rdev"), stat->st_rdev); + dyn_set_i64(obj, hl_hash_utf8("ino"), stat->st_ino); + dyn_set_i64(obj, hl_hash_utf8("size"), stat->st_size); + dyn_set_i64(obj, hl_hash_utf8("blksize"), stat->st_blksize); + dyn_set_i64(obj, hl_hash_utf8("blocks"), stat->st_blocks); + dyn_set_i64(obj, hl_hash_utf8("flags"), stat->st_flags); + dyn_set_i64(obj, hl_hash_utf8("gen"), stat->st_gen); + hl_dyn_setp(obj, hl_hash_utf8("atim"), &hlt_dyn, alloc_timespec_dyn(&stat->st_atim)); + hl_dyn_setp(obj, hl_hash_utf8("mtim"), &hlt_dyn, alloc_timespec_dyn(&stat->st_mtim)); + hl_dyn_setp(obj, hl_hash_utf8("ctim"), &hlt_dyn, alloc_timespec_dyn(&stat->st_ctim)); + hl_dyn_setp(obj, hl_hash_utf8("birthtim"), &hlt_dyn, alloc_timespec_dyn(&stat->st_birthtim)); + return obj; +} + static void on_fs_stat( uv_fs_t *r ) { UV_GET_CLOSURE(c,r,0,"No callback in fs stat request"); if( r->result < 0 ) { hl_call2(void,c,int,hx_errno(r->result),vdynamic *,NULL); } else { - vdynamic *stat = (vdynamic*)hl_alloc_dynobj(); - dyn_set_i64(stat, hl_hash_utf8("dev"), r->statbuf.st_dev); - dyn_set_i64(stat, hl_hash_utf8("mode"), r->statbuf.st_mode); - dyn_set_i64(stat, hl_hash_utf8("nlink"), r->statbuf.st_nlink); - dyn_set_i64(stat, hl_hash_utf8("uid"), r->statbuf.st_uid); - dyn_set_i64(stat, hl_hash_utf8("gid"), r->statbuf.st_gid); - dyn_set_i64(stat, hl_hash_utf8("rdev"), r->statbuf.st_rdev); - dyn_set_i64(stat, hl_hash_utf8("ino"), r->statbuf.st_ino); - dyn_set_i64(stat, hl_hash_utf8("size"), r->statbuf.st_size); - dyn_set_i64(stat, hl_hash_utf8("blksize"), r->statbuf.st_blksize); - dyn_set_i64(stat, hl_hash_utf8("blocks"), r->statbuf.st_blocks); - dyn_set_i64(stat, hl_hash_utf8("flags"), r->statbuf.st_flags); - dyn_set_i64(stat, hl_hash_utf8("gen"), r->statbuf.st_gen); - hl_dyn_setp(stat, hl_hash_utf8("atim"), &hlt_dyn, alloc_timespec_dyn(&r->statbuf.st_atim)); - hl_dyn_setp(stat, hl_hash_utf8("mtim"), &hlt_dyn, alloc_timespec_dyn(&r->statbuf.st_mtim)); - hl_dyn_setp(stat, hl_hash_utf8("ctim"), &hlt_dyn, alloc_timespec_dyn(&r->statbuf.st_ctim)); - hl_dyn_setp(stat, hl_hash_utf8("birthtim"), &hlt_dyn, alloc_timespec_dyn(&r->statbuf.st_birthtim)); - hl_call2(void,c,int,0,vdynamic *,stat); + hl_call2(void,c,int,0,vdynamic *,alloc_stat(&r->statbuf)); } free_fs_req(r); } @@ -2052,6 +2056,37 @@ HL_PRIM void HL_NAME(fs_event_stop_wrap)( uv_fs_event_t *h ) { } DEFINE_PRIM(_VOID, fs_event_stop_wrap, _HANDLE); +// Fs poll + +HL_PRIM uv_fs_poll_t *HL_NAME(fs_poll_init_wrap)( uv_loop_t *loop ) { + UV_CHECK_NULL(loop,NULL); + uv_fs_poll_t *h = UV_ALLOC(uv_fs_poll_t); + UV_CHECK_ERROR(uv_fs_poll_init(loop,h),free(h),NULL); + handle_init_hl_data((uv_handle_t*)h); + return h; +} +DEFINE_PRIM(_HANDLE, fs_poll_init_wrap, _LOOP); + +static void on_fs_poll( uv_fs_poll_t *h, int status, const uv_stat_t *prev, const uv_stat_t *curr ) { + UV_GET_CLOSURE(c,h,0,"No callback in fs_poll handle"); + hl_call3(void,c,int,0,vdynamic *,(prev?alloc_stat(prev):NULL),vdynamic *,(curr?alloc_stat(curr):NULL)); +} + +HL_PRIM void HL_NAME(fs_poll_start_wrap)( uv_fs_poll_t *h, vstring *path, int interval, vclosure *c ) { + UV_CHECK_NULL(h,); + UV_CHECK_NULL(path,); + UV_CHECK_NULL(c,); + handle_register_callback((uv_handle_t*)h,c,0); + UV_CHECK_ERROR(uv_fs_poll_start(h,on_fs_poll,hl_to_utf8(path->bytes),interval),handle_clear_callback((uv_handle_t *)h,0),); +} +DEFINE_PRIM(_VOID, fs_poll_start_wrap, _HANDLE _STRING _I32 _FUN(_VOID,_I32 _DYN _DYN)); + +HL_PRIM void HL_NAME(fs_poll_stop_wrap)( uv_fs_poll_t *h ) { + UV_CHECK_NULL(h,); + UV_CHECK_ERROR(uv_fs_poll_stop(h),,); +} +DEFINE_PRIM(_VOID, fs_poll_stop_wrap, _HANDLE); + // Miscellaneous HL_PRIM vbyte *HL_NAME(os_tmpdir_wrap)() { diff --git a/other/uvsample/FsPollSample.hx b/other/uvsample/FsPollSample.hx new file mode 100644 index 000000000..3feeab2e2 --- /dev/null +++ b/other/uvsample/FsPollSample.hx @@ -0,0 +1,28 @@ +import sys.FileSystem; +import haxe.Timer; +import sys.io.File; +import hl.uv.Misc; +import hl.uv.FsPoll; +import hl.uv.UVException; +import sys.thread.Thread; + +class FsPollSample { + public static function main() { + var loop = Thread.current().events; + var poll = FsPoll.init(loop); + var path = Misc.tmpDir() + '/test-file'; + File.saveContent(path, 'Hello, world'); + poll.start(path, 1000, (e, previous, current) -> switch e { + case UV_NOERR: + Log.print('FS Poll at $path:'); + Log.print('\tprev: $previous'); + Log.print('\tcurr: $current'); + FileSystem.deleteFile(path); + poll.stop(); + poll.close(); + case _: + throw new UVException(e); + }); + Timer.delay(File.saveContent.bind(path, 'Bye, world'), 50); + } +} \ No newline at end of file diff --git a/other/uvsample/UVSample.hx b/other/uvsample/UVSample.hx index 208acae1b..0623e82d1 100644 --- a/other/uvsample/UVSample.hx +++ b/other/uvsample/UVSample.hx @@ -6,6 +6,7 @@ class UVSample { // PipeSample.main(); // ProcessSample.main(); // FileSample.main(); - FsEventSample.main(); + // FsEventSample.main(); + FsPollSample.main(); } } \ No newline at end of file From 3c215300b660e3fe192342805456b6401e57c1ce Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Tue, 10 Aug 2021 14:56:19 +0300 Subject: [PATCH 042/117] wip misc --- libs/uv/uv.c | 189 ++++++++++++++++++++++++++++++----- other/uvsample/MiscSample.hx | 24 +++++ other/uvsample/PipeSample.hx | 7 +- other/uvsample/UVSample.hx | 3 +- 4 files changed, 196 insertions(+), 27 deletions(-) create mode 100644 other/uvsample/MiscSample.hx diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 2e11a2596..b5b27d0a6 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -69,6 +69,8 @@ typedef struct { memcpy(__ev__->data,buffer,length); \ uv_buf_t buf = uv_buf_init(__ev__->data, length); +hl_type hlt_sockaddr = { HABSTRACT, {USTR("uv_sockaddr_storage")} }; + static void dyn_set_i64( vdynamic *obj, int field, int64 value ) { vdynamic *v = hl_alloc_dynamic(&hlt_i64); v->v.i64 = value; @@ -859,7 +861,7 @@ HL_PRIM void HL_NAME(pipe_bind_wrap)( uv_pipe_t *h, vstring *name ) { } DEFINE_PRIM(_VOID, pipe_bind_wrap, _HANDLE _STRING); -HL_PRIM vbyte *HL_NAME(pipe_getsockname_wrap)( uv_pipe_t *h ) { +static vbyte *pipe_getname( uv_pipe_t *h, int (*fn)(const uv_pipe_t *h, char *buf, size_t *size) ) { UV_CHECK_NULL(h,NULL); size_t size = 256; char *buf = NULL; @@ -868,7 +870,7 @@ HL_PRIM vbyte *HL_NAME(pipe_getsockname_wrap)( uv_pipe_t *h ) { if( buf ) free(buf); buf = malloc(size); - result = uv_pipe_getsockname(h,buf,&size); + result = fn(h,buf,&size); } vbyte *name = hl_alloc_bytes(size); memcpy(name,buf,size); @@ -876,24 +878,14 @@ HL_PRIM vbyte *HL_NAME(pipe_getsockname_wrap)( uv_pipe_t *h ) { UV_CHECK_ERROR(result,,NULL); //free bytes? return name; } + +HL_PRIM vbyte *HL_NAME(pipe_getsockname_wrap)( uv_pipe_t *h ) { + return pipe_getname(h, uv_pipe_getsockname); +} DEFINE_PRIM(_BYTES, pipe_getsockname_wrap, _HANDLE); HL_PRIM vbyte *HL_NAME(pipe_getpeername_wrap)( uv_pipe_t *h ) { - UV_CHECK_NULL(h,NULL); - size_t size = 256; - char *buf = NULL; - int result = UV_ENOBUFS; - while (result == UV_ENOBUFS) { - if( buf ) - free(buf); - buf = malloc(size); - result = uv_pipe_getpeername(h,buf,&size); - } - vbyte *name = hl_alloc_bytes(size); - memcpy(name,buf,size); - free(buf); - UV_CHECK_ERROR(result,,NULL); //free bytes? - return name; + return pipe_getname(h, uv_pipe_getpeername); } DEFINE_PRIM(_BYTES, pipe_getpeername_wrap, _HANDLE); @@ -1326,7 +1318,6 @@ static void on_getaddrinfo( uv_getaddrinfo_t *r, int status, struct addrinfo *re int haddr = hl_hash_utf8("addr"); int hcanonName = hl_hash_utf8("canonName"); int i = 0; - hl_type hlt_sockaddr = { HABSTRACT, {USTR("uv_sockaddr_storage")} }; vdynamic *entry; while( current ) { entry = (vdynamic *)hl_alloc_dynobj(); @@ -1706,12 +1697,12 @@ DEFINE_PRIM(_VOID, fs_readdir_wrap, _DIR _LOOP _I32 _FUN(_VOID,_I32 _ARR)); static vdynamic *alloc_timespec_dyn( const uv_timespec_t *spec ) { vdynamic *obj = (vdynamic*)hl_alloc_dynobj(); - hl_dyn_seti(obj, hl_hash_utf8("sec"), &hlt_i32, spec->tv_sec); - hl_dyn_seti(obj, hl_hash_utf8("nsec"), &hlt_i32, spec->tv_nsec); + hl_dyn_seti(obj, hl_hash_utf8("sec"), &hlt_i64, (int64)spec->tv_sec); + hl_dyn_seti(obj, hl_hash_utf8("nsec"), &hlt_i64, (int64)spec->tv_nsec); return obj; } -static vdynamic *alloc_stat( const uv_stat_t *stat ) { +static vdynamic *alloc_stat_dyn( const uv_stat_t *stat ) { vdynamic *obj = (vdynamic*)hl_alloc_dynobj(); dyn_set_i64(obj, hl_hash_utf8("dev"), stat->st_dev); dyn_set_i64(obj, hl_hash_utf8("mode"), stat->st_mode); @@ -1737,7 +1728,7 @@ static void on_fs_stat( uv_fs_t *r ) { if( r->result < 0 ) { hl_call2(void,c,int,hx_errno(r->result),vdynamic *,NULL); } else { - hl_call2(void,c,int,0,vdynamic *,alloc_stat(&r->statbuf)); + hl_call2(void,c,int,0,vdynamic *,alloc_stat_dyn(&r->statbuf)); } free_fs_req(r); } @@ -2069,7 +2060,7 @@ DEFINE_PRIM(_HANDLE, fs_poll_init_wrap, _LOOP); static void on_fs_poll( uv_fs_poll_t *h, int status, const uv_stat_t *prev, const uv_stat_t *curr ) { UV_GET_CLOSURE(c,h,0,"No callback in fs_poll handle"); - hl_call3(void,c,int,0,vdynamic *,(prev?alloc_stat(prev):NULL),vdynamic *,(curr?alloc_stat(curr):NULL)); + hl_call3(void,c,int,0,vdynamic *,(prev?alloc_stat_dyn(prev):NULL),vdynamic *,(curr?alloc_stat_dyn(curr):NULL)); } HL_PRIM void HL_NAME(fs_poll_start_wrap)( uv_fs_poll_t *h, vstring *path, int interval, vclosure *c ) { @@ -2089,7 +2080,131 @@ DEFINE_PRIM(_VOID, fs_poll_stop_wrap, _HANDLE); // Miscellaneous -HL_PRIM vbyte *HL_NAME(os_tmpdir_wrap)() { +HL_PRIM int64 HL_NAME(resident_set_memory_wrap)() { + size_t rss; + UV_CHECK_ERROR(uv_resident_set_memory(&rss),,0); + return rss; +} +DEFINE_PRIM(_I64, resident_set_memory_wrap, _NO_ARG); + +HL_PRIM double HL_NAME(uptime_wrap)() { + double uptime; + UV_CHECK_ERROR(uv_uptime(&uptime),,0); + return uptime; +} +DEFINE_PRIM(_F64, uptime_wrap, _NO_ARG); + +static vdynamic *alloc_timeval_dyn( const uv_timeval_t *tv ) { + vdynamic *obj = (vdynamic*)hl_alloc_dynobj(); + hl_dyn_seti(obj, hl_hash_utf8("sec"), &hlt_i64, (int64)tv->tv_sec); + hl_dyn_seti(obj, hl_hash_utf8("usec"), &hlt_i64, (int64)tv->tv_usec); + return obj; +} + +HL_PRIM vdynamic *HL_NAME(getrusage_wrap)() { + uv_rusage_t rusage = {0}; + UV_CHECK_ERROR(uv_getrusage(&rusage),,NULL); + vdynamic *obj = (vdynamic *)hl_alloc_dynobj(); + hl_dyn_setp(obj, hl_hash_utf8("utime"), &hlt_dyn, alloc_timeval_dyn(&rusage.ru_utime)); + hl_dyn_setp(obj, hl_hash_utf8("stime"), &hlt_dyn, alloc_timeval_dyn(&rusage.ru_stime)); + dyn_set_i64(obj, hl_hash_utf8("maxrss"), rusage.ru_maxrss); + dyn_set_i64(obj, hl_hash_utf8("ixrss"), rusage.ru_ixrss); + dyn_set_i64(obj, hl_hash_utf8("idrss"), rusage.ru_idrss); + dyn_set_i64(obj, hl_hash_utf8("isrss"), rusage.ru_isrss); + dyn_set_i64(obj, hl_hash_utf8("minflt"), rusage.ru_minflt); + dyn_set_i64(obj, hl_hash_utf8("majflt"), rusage.ru_majflt); + dyn_set_i64(obj, hl_hash_utf8("nswap"), rusage.ru_nswap); + dyn_set_i64(obj, hl_hash_utf8("inblock"), rusage.ru_inblock); + dyn_set_i64(obj, hl_hash_utf8("oublock"), rusage.ru_oublock); + dyn_set_i64(obj, hl_hash_utf8("msgsnd"), rusage.ru_msgsnd); + dyn_set_i64(obj, hl_hash_utf8("msgrcv"), rusage.ru_msgrcv); + dyn_set_i64(obj, hl_hash_utf8("nsignals"), rusage.ru_nsignals); + dyn_set_i64(obj, hl_hash_utf8("nvcsw"), rusage.ru_nvcsw); + dyn_set_i64(obj, hl_hash_utf8("nivcsw"), rusage.ru_nivcsw); + return obj; +} +DEFINE_PRIM(_DYN, getrusage_wrap, _NO_ARG); + +DEFINE_PRIM(_I32, os_getpid, _NO_ARG); +DEFINE_PRIM(_I32, os_getppid, _NO_ARG); + +HL_PRIM varray *HL_NAME(cpu_info_wrap)() { + uv_cpu_info_t *infos; + int count; + UV_CHECK_ERROR(uv_cpu_info(&infos, &count),,NULL); + int hash_user = hl_hash_utf8("user"); + int hash_nice = hl_hash_utf8("nice"); + int hash_sys = hl_hash_utf8("sys"); + int hash_idle = hl_hash_utf8("idle"); + int hash_irq = hl_hash_utf8("irq"); + int hash_model = hl_hash_utf8("model"); + int hash_speed = hl_hash_utf8("speed"); + int hash_cpuTimes = hl_hash_utf8("cpuTimes"); + varray *a = hl_alloc_array(&hlt_dyn, count); + for(int i = 0; i < count; i++) { + vdynamic *times = (vdynamic *)hl_alloc_dynobj(); + dyn_set_i64(times, hash_user, infos[i].cpu_times.user); + dyn_set_i64(times, hash_nice, infos[i].cpu_times.nice); + dyn_set_i64(times, hash_sys, infos[i].cpu_times.sys); + dyn_set_i64(times, hash_idle, infos[i].cpu_times.idle); + dyn_set_i64(times, hash_irq, infos[i].cpu_times.irq); + + vdynamic *info = (vdynamic *)hl_alloc_dynobj(); + hl_dyn_setp(info, hash_model, &hlt_bytes, hl_copy_bytes((vbyte *)infos[i].model, strlen(infos[i].model) + 1)); + hl_dyn_seti(info, hash_speed, &hlt_i32, infos[i].speed); + hl_dyn_setp(info, hash_cpuTimes, &hlt_dyn, times); + + hl_aptr(a,vdynamic *)[i] = info; + } + uv_free_cpu_info(infos, count); + return a; +} +DEFINE_PRIM(_ARR, cpu_info_wrap, _NO_ARG); + +HL_PRIM varray *HL_NAME(interface_addresses_wrap)() { + uv_interface_address_t *addresses; + int count; + UV_CHECK_ERROR(uv_interface_addresses(&addresses, &count),,NULL); + int hash_name = hl_hash_utf8("name"); + int hash_physAddr = hl_hash_utf8("physAddr"); + int hash_isInternal = hl_hash_utf8("isInternal"); + int hash_address = hl_hash_utf8("address"); + int hash_netmask = hl_hash_utf8("netmask"); + varray *a = hl_alloc_array(&hlt_dyn, count); + for(int i = 0; i < count; i++) { + vdynamic *info = (vdynamic *)hl_alloc_dynobj(); + + hl_dyn_setp(info, hash_name, &hlt_bytes, hl_copy_bytes((vbyte *)addresses[i].name, strlen(addresses[i].name))); + hl_dyn_setp(info, hash_physAddr, &hlt_bytes, hl_copy_bytes((vbyte *)addresses[i].phys_addr, 6)); + hl_dyn_seti(info, hash_isInternal, &hlt_bool, addresses[i].is_internal); + + uv_sockaddr_storage *addr = UV_ALLOC(uv_sockaddr_storage); + memcpy(addr, &addresses[i].address, sizeof(addresses[i].address)); + hl_dyn_setp(info, hash_address, &hlt_sockaddr, addr); + + uv_sockaddr_storage *mask = UV_ALLOC(uv_sockaddr_storage); + memcpy(mask, &addresses[i].netmask, sizeof(addresses[i].netmask)); + hl_dyn_setp(info, hash_netmask, &hlt_sockaddr, mask); + + hl_aptr(a,vdynamic *)[i] = info; + } + uv_free_interface_addresses(addresses, count); + return a; +} +DEFINE_PRIM(_ARR, interface_addresses_wrap, _NO_ARG); + +HL_PRIM varray *HL_NAME(loadavg_wrap)() { + double avg[3]; + uv_loadavg(avg); + varray *a = hl_alloc_array(&hlt_f64, 3); + hl_aptr(a,double)[0] = avg[0]; + hl_aptr(a,double)[1] = avg[1]; + hl_aptr(a,double)[2] = avg[2]; + return a; +} +DEFINE_PRIM(_ARR, loadavg_wrap, _NO_ARG); + +static vbyte *os_dir( int (*fn)(char *buf, size_t *size) ) { size_t size = 256; char *buf = NULL; int result = UV_ENOBUFS; @@ -2097,7 +2212,7 @@ HL_PRIM vbyte *HL_NAME(os_tmpdir_wrap)() { if( buf ) free(buf); buf = malloc(size); - result = uv_os_tmpdir(buf,&size); + result = fn(buf,&size); } vbyte *path = hl_alloc_bytes(size); memcpy(path,buf,size); @@ -2105,4 +2220,28 @@ HL_PRIM vbyte *HL_NAME(os_tmpdir_wrap)() { UV_CHECK_ERROR(result,,NULL); // free bytes? return path; } + +HL_PRIM vbyte *HL_NAME(os_homedir_wrap)() { + return os_dir(uv_os_homedir); +} +DEFINE_PRIM(_BYTES, os_homedir_wrap, _NO_ARG); + +HL_PRIM vbyte *HL_NAME(os_tmpdir_wrap)() { + return os_dir(uv_os_tmpdir); +} DEFINE_PRIM(_BYTES, os_tmpdir_wrap, _NO_ARG); + +HL_PRIM vdynamic *HL_NAME(os_getpasswd_wrap)() { + uv_passwd_t p; + UV_CHECK_ERROR(uv_os_get_passwd(&p),,NULL); + vdynamic *obj = (vdynamic *)hl_alloc_dynobj(); + hl_dyn_setp(obj, hl_hash_utf8("username"), &hlt_bytes, hl_copy_bytes((vbyte *)p.username, strlen(p.username))); + hl_dyn_setp(obj, hl_hash_utf8("homedir"), &hlt_bytes, hl_copy_bytes((vbyte *)p.homedir, strlen(p.homedir))); + dyn_set_i64(obj, hl_hash_utf8("uid"), p.uid); + dyn_set_i64(obj, hl_hash_utf8("gid"), p.gid); + if( p.shell ) + hl_dyn_setp(obj, hl_hash_utf8("shell"), &hlt_bytes, hl_copy_bytes((vbyte *)p.shell, strlen(p.shell))); + uv_os_free_passwd(&p); + return obj; +} +DEFINE_PRIM(_DYN, os_getpasswd_wrap, _NO_ARG); \ No newline at end of file diff --git a/other/uvsample/MiscSample.hx b/other/uvsample/MiscSample.hx new file mode 100644 index 000000000..35e14bda8 --- /dev/null +++ b/other/uvsample/MiscSample.hx @@ -0,0 +1,24 @@ +import hl.uv.Misc; + +class MiscSample { + public static function main() { + Log.print('Resident set memory: ' + Misc.residentSetMemory()); + Log.print('Uptime: ' + Misc.uptime()); + Log.print('RUsage: ' + Misc.getRUsage()); + Log.print('Pid: ' + Misc.getPid()); + Log.print('PPid: ' + Misc.getPPid()); + Log.print('Cpu infos:\n ' + Misc.cpuInfo().map(Std.string).join('\n ')); + Log.print('Inteface addresses:\n ' + Misc.interfaceAddresses().map(i -> { + Std.string({ + name:i.name, + physAddr:i.physAddr, + isInternal:i.isInternal, + address:i.address.name(), + netmask:i.netmask.name(), + }); + }).join('\n ')); + Log.print('Load avg: ' + Misc.loadAvg()); + Log.print('Home dir: ' + Misc.homeDir()); + Log.print('Passwd: ' + Misc.getPasswd()); + } +} \ No newline at end of file diff --git a/other/uvsample/PipeSample.hx b/other/uvsample/PipeSample.hx index ef4e8b247..f7de8e5cc 100644 --- a/other/uvsample/PipeSample.hx +++ b/other/uvsample/PipeSample.hx @@ -1,3 +1,5 @@ +import sys.FileSystem; +import hl.uv.Misc; import hl.uv.SockAddr; import hl.uv.Tcp; import haxe.io.Bytes; @@ -9,9 +11,10 @@ import sys.thread.Thread; import haxe.Timer; class PipeSample { - static inline var NAME = 'testpipe'; + static var NAME = 'testpipe'; static public function main() { + NAME = Misc.tmpDir() + '/' + NAME; #if CLIENT Log.print('Running PipeSample client...'); client(); @@ -30,6 +33,8 @@ class PipeSample { } static function server() { + if(FileSystem.exists(NAME)) + FileSystem.deleteFile(NAME); function print(msg:String) Log.print('SERVER: $msg'); var loop = Thread.current().events; diff --git a/other/uvsample/UVSample.hx b/other/uvsample/UVSample.hx index 0623e82d1..848746019 100644 --- a/other/uvsample/UVSample.hx +++ b/other/uvsample/UVSample.hx @@ -7,6 +7,7 @@ class UVSample { // ProcessSample.main(); // FileSample.main(); // FsEventSample.main(); - FsPollSample.main(); + // FsPollSample.main(); + MiscSample.main(); } } \ No newline at end of file From 75d70e6170fd78f45c7900419d0aed1b89e233b9 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Tue, 10 Aug 2021 16:37:24 +0300 Subject: [PATCH 043/117] misc --- libs/uv/uv.c | 66 +++++++++++++++++++++++++++++++++--- other/uvsample/MiscSample.hx | 44 ++++++++++++++++++------ 2 files changed, 96 insertions(+), 14 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index b5b27d0a6..4440a06ec 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -2204,7 +2204,7 @@ HL_PRIM varray *HL_NAME(loadavg_wrap)() { } DEFINE_PRIM(_ARR, loadavg_wrap, _NO_ARG); -static vbyte *os_dir( int (*fn)(char *buf, size_t *size) ) { +static vbyte *os_str( int (*fn)(char *buf, size_t *size) ) { size_t size = 256; char *buf = NULL; int result = UV_ENOBUFS; @@ -2222,12 +2222,12 @@ static vbyte *os_dir( int (*fn)(char *buf, size_t *size) ) { } HL_PRIM vbyte *HL_NAME(os_homedir_wrap)() { - return os_dir(uv_os_homedir); + return os_str(uv_os_homedir); } DEFINE_PRIM(_BYTES, os_homedir_wrap, _NO_ARG); HL_PRIM vbyte *HL_NAME(os_tmpdir_wrap)() { - return os_dir(uv_os_tmpdir); + return os_str(uv_os_tmpdir); } DEFINE_PRIM(_BYTES, os_tmpdir_wrap, _NO_ARG); @@ -2244,4 +2244,62 @@ HL_PRIM vdynamic *HL_NAME(os_getpasswd_wrap)() { uv_os_free_passwd(&p); return obj; } -DEFINE_PRIM(_DYN, os_getpasswd_wrap, _NO_ARG); \ No newline at end of file +DEFINE_PRIM(_DYN, os_getpasswd_wrap, _NO_ARG); + +DEFINE_PRIM(_I64, get_free_memory, _NO_ARG); +DEFINE_PRIM(_I64, get_total_memory, _NO_ARG); +DEFINE_PRIM(_I64, get_constrained_memory, _NO_ARG); +DEFINE_PRIM(_I64, hrtime, _NO_ARG); + +HL_PRIM vbyte *HL_NAME(os_gethostname_wrap)() { + return os_str(uv_os_gethostname); +} +DEFINE_PRIM(_BYTES, os_gethostname_wrap, _NO_ARG); + +HL_PRIM int HL_NAME(os_getpriority_wrap)( int pid ) { + int priority; + UV_CHECK_ERROR(uv_os_getpriority(pid,&priority),,0); + return priority; +} +DEFINE_PRIM(_I32, os_getpriority_wrap, _I32); + +HL_PRIM void HL_NAME(os_setpriority_wrap)( int pid, int priority ) { + UV_CHECK_ERROR(uv_os_setpriority(pid,priority),,); +} +DEFINE_PRIM(_VOID, os_setpriority_wrap, _I32 _I32); + +HL_PRIM vdynamic *HL_NAME(os_uname_wrap)() { + uv_utsname_t u; + UV_CHECK_ERROR(uv_os_uname(&u),,NULL); + vdynamic *obj = (vdynamic *)hl_alloc_dynobj(); + hl_dyn_setp(obj, hl_hash_utf8("machine"), &hlt_bytes, hl_copy_bytes((vbyte *)u.machine, strlen(u.machine))); + hl_dyn_setp(obj, hl_hash_utf8("release"), &hlt_bytes, hl_copy_bytes((vbyte *)u.release, strlen(u.release))); + hl_dyn_setp(obj, hl_hash_utf8("sysname"), &hlt_bytes, hl_copy_bytes((vbyte *)u.sysname, strlen(u.sysname))); + hl_dyn_setp(obj, hl_hash_utf8("version"), &hlt_bytes, hl_copy_bytes((vbyte *)u.version, strlen(u.version))); + return obj; +} +DEFINE_PRIM(_DYN, os_uname_wrap, _NO_ARG); + +HL_PRIM vdynamic *HL_NAME(gettimeofday_wrap)() { + uv_timeval64_t t; + UV_CHECK_ERROR(uv_gettimeofday(&t),,NULL); + vdynamic *obj = (vdynamic *)hl_alloc_dynobj(); + dyn_set_i64(obj, hl_hash_utf8("sec"), t.tv_sec); + dyn_set_i64(obj, hl_hash_utf8("usec"), t.tv_usec); + return obj; +} +DEFINE_PRIM(_DYN, gettimeofday_wrap, _NO_ARG); + +static void on_random( uv_random_t* r, int status, void* buf, size_t buflen ) { + UV_GET_CLOSURE(c,r,0,"No callback in random request"); + hl_call1(void,c,int,errno_uv2hx(status)); +} + +HL_PRIM void HL_NAME(random_wrap)( uv_loop_t *loop, vbyte *buf, int length, int flags, vclosure *c ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(buf,); + UV_CHECK_NULL(c,); + UV_ALLOC_REQ(uv_random_t,r,c); + UV_CHECK_ERROR(uv_random(loop,r,buf,length,flags,on_random),free_req((uv_req_t *)r),); +} +DEFINE_PRIM(_VOID, random_wrap, _LOOP _BYTES _I32 _I32 _FUN(_VOID,_I32)); diff --git a/other/uvsample/MiscSample.hx b/other/uvsample/MiscSample.hx index 35e14bda8..af3660bdd 100644 --- a/other/uvsample/MiscSample.hx +++ b/other/uvsample/MiscSample.hx @@ -1,14 +1,21 @@ +import hl.Bytes; +import hl.uv.UVException; +import sys.thread.Thread; import hl.uv.Misc; class MiscSample { + static function print(msg:String) { + Log.print('MiscSample: $msg'); + } + public static function main() { - Log.print('Resident set memory: ' + Misc.residentSetMemory()); - Log.print('Uptime: ' + Misc.uptime()); - Log.print('RUsage: ' + Misc.getRUsage()); - Log.print('Pid: ' + Misc.getPid()); - Log.print('PPid: ' + Misc.getPPid()); - Log.print('Cpu infos:\n ' + Misc.cpuInfo().map(Std.string).join('\n ')); - Log.print('Inteface addresses:\n ' + Misc.interfaceAddresses().map(i -> { + print('Resident set memory: ' + Misc.residentSetMemory()); + print('Uptime: ' + Misc.uptime()); + print('RUsage: ' + Misc.getRUsage()); + print('Pid: ' + Misc.getPid()); + print('PPid: ' + Misc.getPPid()); + print('Cpu infos:\n ' + Misc.cpuInfo().map(Std.string).join('\n ')); + print('Inteface addresses:\n ' + Misc.interfaceAddresses().map(i -> { Std.string({ name:i.name, physAddr:i.physAddr, @@ -17,8 +24,25 @@ class MiscSample { netmask:i.netmask.name(), }); }).join('\n ')); - Log.print('Load avg: ' + Misc.loadAvg()); - Log.print('Home dir: ' + Misc.homeDir()); - Log.print('Passwd: ' + Misc.getPasswd()); + print('Load avg: ' + Misc.loadAvg()); + print('Home dir: ' + Misc.homeDir()); + print('Passwd: ' + Misc.getPasswd()); + print('Free mem: ' + Misc.getFreeMemory()); + print('Total mem: ' + Misc.getTotalMemory()); + print('Constrained mem: ' + Misc.getConstrainedMemory()); + print('HR time: ' + Misc.hrTime()); + print('Host name: ' + Misc.getHostName()); + var myPid = Misc.getPid(); + Misc.setPriority(myPid, Misc.getPriority(myPid) + 1); + print('Priority: ' + Misc.getPriority(myPid)); + print('Uname: ' + Misc.uname()); + print('Time of day: ' + Misc.getTimeOfDay()); + var buf = new Bytes(20); + Misc.random(Thread.current().events, buf, 20, 0, e -> switch e { + case UV_NOERR: + print('Random bytes hex: ' + haxe.io.Bytes.ofData(new haxe.io.BytesData(buf, 20)).toHex()); + case _: + throw new UVException(e); + }); } } \ No newline at end of file From 1a16505776d0b8361d683c239d30f5067faa21b1 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Thu, 12 Aug 2021 21:49:31 +0300 Subject: [PATCH 044/117] threadpool --- libs/uv/uv.c | 28 +++++++++++++++++++++++++++- other/uvsample/ThreadPoolSample.hx | 16 ++++++++++++++++ other/uvsample/UVSample.hx | 3 ++- 3 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 other/uvsample/ThreadPoolSample.hx diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 4440a06ec..293fd2bcb 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -21,7 +21,8 @@ typedef struct sockaddr_storage uv_sockaddr_storage; #define EVT_STREAM_READ 0 #define EVT_STREAM_LISTEN 2 -#define EVT_CONNECT 0 // connect_t +#define THREAD_POOL_AFTER 0 +#define THREAD_POOL_WORK 2 #define EVT_MAX 2 // !!!!!!!!!!!!!! @@ -2303,3 +2304,28 @@ HL_PRIM void HL_NAME(random_wrap)( uv_loop_t *loop, vbyte *buf, int length, int UV_CHECK_ERROR(uv_random(loop,r,buf,length,flags,on_random),free_req((uv_req_t *)r),); } DEFINE_PRIM(_VOID, random_wrap, _LOOP _BYTES _I32 _I32 _FUN(_VOID,_I32)); + +// Thread pool + +static void on_work( uv_work_t *r ) { + UV_GET_CLOSURE(c,r,THREAD_POOL_WORK,"No work callback in queue_work request"); + hl_call0(void, c); +} + +static void on_after_work( uv_work_t *r, int status ) { + UV_GET_CLOSURE(c,r,THREAD_POOL_AFTER,"No after_work callback in queue_work request"); + hl_call1(void, c, int, errno_uv2hx(status)); + free_req((uv_req_t *) r); +} + +HL_PRIM void HL_NAME(queue_work_wrap)( uv_loop_t *loop, vclosure *c_work, vclosure *c_after ) { + UV_CHECK_NULL(loop,); + UV_CHECK_NULL(c_work,); + UV_CHECK_NULL(c_after,); + UV_ALLOC_REQ(uv_work_t,r,NULL); + req_register_callback((uv_req_t *)r,c_work,THREAD_POOL_WORK); + req_register_callback((uv_req_t *)r,c_after,THREAD_POOL_AFTER); + UV_CHECK_ERROR(uv_queue_work(loop,r,on_work,on_after_work),free_req((uv_req_t *)r),); +} +DEFINE_PRIM(_VOID, queue_work_wrap, _LOOP _FUN(_VOID,_NO_ARG) _FUN(_VOID,_I32)); + diff --git a/other/uvsample/ThreadPoolSample.hx b/other/uvsample/ThreadPoolSample.hx new file mode 100644 index 000000000..4c36bf136 --- /dev/null +++ b/other/uvsample/ThreadPoolSample.hx @@ -0,0 +1,16 @@ +import hl.uv.ThreadPool; +import hl.Bytes; +import hl.uv.UVException; +import sys.thread.Thread; +import hl.uv.Misc; + +class ThreadPoolSample { + public static function main() { + function print(msg:String) + Log.print('ThreadPoolSample: $msg'); + ThreadPool.queueWork(Thread.current().events, () -> print('message from pool'), e -> switch e { + case UV_NOERR: print('Done'); + case _: throw new UVException(e); + }); + } +} \ No newline at end of file diff --git a/other/uvsample/UVSample.hx b/other/uvsample/UVSample.hx index 848746019..c618fd04a 100644 --- a/other/uvsample/UVSample.hx +++ b/other/uvsample/UVSample.hx @@ -8,6 +8,7 @@ class UVSample { // FileSample.main(); // FsEventSample.main(); // FsPollSample.main(); - MiscSample.main(); + // MiscSample.main(); + ThreadPoolSample.main(); } } \ No newline at end of file From c0e66bc688c868fe66bae17761f7c1f3b0f8b4fd Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Thu, 12 Aug 2021 21:50:06 +0300 Subject: [PATCH 045/117] Revert "threadpool" This reverts commit 1a16505776d0b8361d683c239d30f5067faa21b1. --- libs/uv/uv.c | 28 +--------------------------- other/uvsample/ThreadPoolSample.hx | 16 ---------------- other/uvsample/UVSample.hx | 3 +-- 3 files changed, 2 insertions(+), 45 deletions(-) delete mode 100644 other/uvsample/ThreadPoolSample.hx diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 293fd2bcb..4440a06ec 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -21,8 +21,7 @@ typedef struct sockaddr_storage uv_sockaddr_storage; #define EVT_STREAM_READ 0 #define EVT_STREAM_LISTEN 2 -#define THREAD_POOL_AFTER 0 -#define THREAD_POOL_WORK 2 +#define EVT_CONNECT 0 // connect_t #define EVT_MAX 2 // !!!!!!!!!!!!!! @@ -2304,28 +2303,3 @@ HL_PRIM void HL_NAME(random_wrap)( uv_loop_t *loop, vbyte *buf, int length, int UV_CHECK_ERROR(uv_random(loop,r,buf,length,flags,on_random),free_req((uv_req_t *)r),); } DEFINE_PRIM(_VOID, random_wrap, _LOOP _BYTES _I32 _I32 _FUN(_VOID,_I32)); - -// Thread pool - -static void on_work( uv_work_t *r ) { - UV_GET_CLOSURE(c,r,THREAD_POOL_WORK,"No work callback in queue_work request"); - hl_call0(void, c); -} - -static void on_after_work( uv_work_t *r, int status ) { - UV_GET_CLOSURE(c,r,THREAD_POOL_AFTER,"No after_work callback in queue_work request"); - hl_call1(void, c, int, errno_uv2hx(status)); - free_req((uv_req_t *) r); -} - -HL_PRIM void HL_NAME(queue_work_wrap)( uv_loop_t *loop, vclosure *c_work, vclosure *c_after ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(c_work,); - UV_CHECK_NULL(c_after,); - UV_ALLOC_REQ(uv_work_t,r,NULL); - req_register_callback((uv_req_t *)r,c_work,THREAD_POOL_WORK); - req_register_callback((uv_req_t *)r,c_after,THREAD_POOL_AFTER); - UV_CHECK_ERROR(uv_queue_work(loop,r,on_work,on_after_work),free_req((uv_req_t *)r),); -} -DEFINE_PRIM(_VOID, queue_work_wrap, _LOOP _FUN(_VOID,_NO_ARG) _FUN(_VOID,_I32)); - diff --git a/other/uvsample/ThreadPoolSample.hx b/other/uvsample/ThreadPoolSample.hx deleted file mode 100644 index 4c36bf136..000000000 --- a/other/uvsample/ThreadPoolSample.hx +++ /dev/null @@ -1,16 +0,0 @@ -import hl.uv.ThreadPool; -import hl.Bytes; -import hl.uv.UVException; -import sys.thread.Thread; -import hl.uv.Misc; - -class ThreadPoolSample { - public static function main() { - function print(msg:String) - Log.print('ThreadPoolSample: $msg'); - ThreadPool.queueWork(Thread.current().events, () -> print('message from pool'), e -> switch e { - case UV_NOERR: print('Done'); - case _: throw new UVException(e); - }); - } -} \ No newline at end of file diff --git a/other/uvsample/UVSample.hx b/other/uvsample/UVSample.hx index c618fd04a..848746019 100644 --- a/other/uvsample/UVSample.hx +++ b/other/uvsample/UVSample.hx @@ -8,7 +8,6 @@ class UVSample { // FileSample.main(); // FsEventSample.main(); // FsPollSample.main(); - // MiscSample.main(); - ThreadPoolSample.main(); + MiscSample.main(); } } \ No newline at end of file From a9d7c39ab914166d54acf30062e171e9dd2d0118 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Thu, 12 Aug 2021 22:59:03 +0300 Subject: [PATCH 046/117] tty --- libs/uv/uv.c | 62 +++++++++++++++++++++++++++++++++++++ other/uvsample/TtySample.hx | 20 ++++++++++++ other/uvsample/UVSample.hx | 3 +- 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 other/uvsample/TtySample.hx diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 4440a06ec..e0032942b 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -2303,3 +2303,65 @@ HL_PRIM void HL_NAME(random_wrap)( uv_loop_t *loop, vbyte *buf, int length, int UV_CHECK_ERROR(uv_random(loop,r,buf,length,flags,on_random),free_req((uv_req_t *)r),); } DEFINE_PRIM(_VOID, random_wrap, _LOOP _BYTES _I32 _I32 _FUN(_VOID,_I32)); + +// Tty + +HL_PRIM uv_tty_t *HL_NAME(tty_init_wrap)( uv_loop_t *loop, int fd ) { + UV_CHECK_NULL(loop,NULL); + uv_tty_t *h = UV_ALLOC(uv_tty_t); + UV_CHECK_ERROR(uv_tty_init(loop,h,fd,0),free_handle((uv_handle_t *)h),NULL); + handle_init_hl_data((uv_handle_t *)h); + return h; +} +DEFINE_PRIM(_HANDLE, tty_init_wrap, _LOOP _I32); + +HL_PRIM void HL_NAME(tty_set_mode_wrap)( uv_tty_t *h, int mode ) { + UV_CHECK_NULL(h,); + uv_tty_mode_t uv_mode = UV_TTY_MODE_NORMAL; + switch( mode ) { + case 0: uv_mode = UV_TTY_MODE_NORMAL; break; + case 1: uv_mode = UV_TTY_MODE_RAW; break; + case 2: uv_mode = UV_TTY_MODE_IO; break; + } + UV_CHECK_ERROR(uv_tty_set_mode(h,uv_mode),,); +} +DEFINE_PRIM(_VOID, tty_set_mode_wrap, _HANDLE _I32); + +HL_PRIM void HL_NAME(tty_reset_mode_wrap)() { + UV_CHECK_ERROR(uv_tty_reset_mode(),,); +} +DEFINE_PRIM(_VOID, tty_reset_mode_wrap, _NO_ARG); + +HL_PRIM vdynamic *HL_NAME(tty_get_winsize_wrap)( uv_tty_t *h ) { + UV_CHECK_NULL(h,NULL); + int width; + int height; + UV_CHECK_ERROR(uv_tty_get_winsize(h,&width,&height),,NULL); + vdynamic *obj = (vdynamic *)hl_alloc_dynobj(); + hl_dyn_seti(obj, hl_hash_utf8("width"), &hlt_i32, width); + hl_dyn_seti(obj, hl_hash_utf8("height"), &hlt_i32, height); + return obj; +} +DEFINE_PRIM(_DYN, tty_get_winsize_wrap, _HANDLE); + +HL_PRIM void HL_NAME(tty_set_vterm_state_wrap)( int state ) { + uv_tty_vtermstate_t uv_state = UV_TTY_SUPPORTED; + switch( state ) { + case 0: uv_state = UV_TTY_SUPPORTED; break; + case 1: uv_state = UV_TTY_UNSUPPORTED; break; + } + uv_tty_set_vterm_state(uv_state); +} +DEFINE_PRIM(_VOID, tty_set_vterm_state_wrap, _I32); + +HL_PRIM int HL_NAME(tty_get_vterm_state_wrap)() { + uv_tty_vtermstate_t state; + UV_CHECK_ERROR(uv_tty_get_vterm_state(&state),,UV_TTY_SUPPORTED); + switch( state ) { + case UV_TTY_SUPPORTED: return 0; + case UV_TTY_UNSUPPORTED: + default: return 1; + } +} +DEFINE_PRIM(_I32, tty_get_vterm_state_wrap, _NO_ARG); + diff --git a/other/uvsample/TtySample.hx b/other/uvsample/TtySample.hx new file mode 100644 index 000000000..e59a05d80 --- /dev/null +++ b/other/uvsample/TtySample.hx @@ -0,0 +1,20 @@ +import hl.uv.File; +import hl.uv.Tty; +import hl.uv.UVException; +import sys.thread.Thread; + +class TtySample { + static function print(msg:String) { + Log.print('TtySample: $msg'); + } + + public static function main() { + print('opening tty...'); + var tty = Tty.init(Thread.current().events, File.stdout); + print('setting mode...'); + tty.setMode(TTY_MODE_NORMAL); + print('window size: ' + tty.getWinSize()); + Tty.resetMode(); + tty.close(() -> print('Done')); + } +} \ No newline at end of file diff --git a/other/uvsample/UVSample.hx b/other/uvsample/UVSample.hx index 848746019..7fc5f9d0a 100644 --- a/other/uvsample/UVSample.hx +++ b/other/uvsample/UVSample.hx @@ -8,6 +8,7 @@ class UVSample { // FileSample.main(); // FsEventSample.main(); // FsPollSample.main(); - MiscSample.main(); + // MiscSample.main(); + TtySample.main(); } } \ No newline at end of file From 68fa72c74ebe9f4e2d9a8d5128569af662ff112b Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Thu, 12 Aug 2021 23:12:15 +0300 Subject: [PATCH 047/117] update libuv to 1.42 --- include/libuv/AUTHORS | 37 +- include/libuv/ChangeLog | 236 +++++++++++- include/libuv/include/uv.h | 30 +- include/libuv/include/uv/errno.h | 12 + include/libuv/include/uv/tree.h | 2 +- include/libuv/include/uv/version.h | 2 +- include/libuv/src/idna.c | 49 ++- include/libuv/src/inet.c | 3 +- include/libuv/src/threadpool.c | 2 - include/libuv/src/timer.c | 1 + include/libuv/src/unix/async.c | 2 +- include/libuv/src/unix/atomic-ops.h | 6 +- include/libuv/src/unix/bsd-ifaddrs.c | 6 +- include/libuv/src/unix/core.c | 28 +- include/libuv/src/unix/darwin.c | 12 +- include/libuv/src/unix/epoll.c | 422 ++++++++++++++++++++ include/libuv/src/unix/freebsd.c | 15 +- include/libuv/src/unix/fs.c | 106 ++++- include/libuv/src/unix/fsevents.c | 23 +- include/libuv/src/unix/getaddrinfo.c | 3 - include/libuv/src/unix/ibmi.c | 48 ++- include/libuv/src/unix/internal.h | 30 +- include/libuv/src/unix/linux-core.c | 470 +++-------------------- include/libuv/src/unix/linux-inotify.c | 2 +- include/libuv/src/unix/linux-syscalls.c | 37 +- include/libuv/src/unix/linux-syscalls.h | 7 +- include/libuv/src/unix/os390-proctitle.c | 136 +++++++ include/libuv/src/unix/os390-syscalls.c | 57 +-- include/libuv/src/unix/os390-syscalls.h | 2 +- include/libuv/src/unix/os390.c | 136 +------ include/libuv/src/unix/pipe.c | 54 +++ include/libuv/src/unix/poll.c | 14 +- include/libuv/src/unix/process.c | 127 ++---- include/libuv/src/unix/proctitle.c | 4 +- include/libuv/src/unix/signal.c | 2 +- include/libuv/src/unix/stream.c | 219 +++++------ include/libuv/src/unix/sunos.c | 11 + include/libuv/src/unix/tcp.c | 53 ++- include/libuv/src/unix/thread.c | 12 +- include/libuv/src/unix/tty.c | 18 + include/libuv/src/unix/udp.c | 32 +- include/libuv/src/uv-common.c | 25 +- include/libuv/src/uv-common.h | 9 +- include/libuv/src/win/atomicops-inl.h | 8 +- include/libuv/src/win/error.c | 2 +- include/libuv/src/win/fs.c | 51 ++- include/libuv/src/win/internal.h | 4 +- include/libuv/src/win/pipe.c | 228 ++++++++++- include/libuv/src/win/poll.c | 3 +- include/libuv/src/win/process-stdio.c | 96 ----- include/libuv/src/win/process.c | 2 +- include/libuv/src/win/stream.c | 23 +- include/libuv/src/win/tcp.c | 260 +++++++++---- include/libuv/src/win/udp.c | 4 +- include/libuv/src/win/util.c | 17 +- 55 files changed, 2017 insertions(+), 1183 deletions(-) create mode 100644 include/libuv/src/unix/epoll.c create mode 100644 include/libuv/src/unix/os390-proctitle.c diff --git a/include/libuv/AUTHORS b/include/libuv/AUTHORS index e7c789cfd..741bcc708 100644 --- a/include/libuv/AUTHORS +++ b/include/libuv/AUTHORS @@ -114,7 +114,6 @@ Dylan Cali Austin Foxley Benjamin Saunders Geoffry Song -Rasmus Christian Pedersen William Light Oleg Efimov Lars Gierth @@ -123,7 +122,6 @@ Justin Venus Kristian Evensen Linus Mårtensson Navaneeth Kedaram Nambiathan -Yorkie StarWing thierry-FreeBSD Isaiah Norton @@ -212,7 +210,7 @@ guworks RossBencina Roger A. Light chenttuuvv -Richard Lau +Richard Lau ronkorving Corbin Simpson Zachary Hamm @@ -448,3 +446,36 @@ Aleksej Lebedev Nikolay Mitev Ulrik Strid Elad Lahav +Elad Nachmias +Darshan Sen +Simon Kadisch +Momtchil Momtchev +Ethel Weston <66453757+ethelweston@users.noreply.github.com> +Drew DeVault +Mark Klein +schamberg97 <50446906+schamberg97@users.noreply.github.com> +Bob Weinand +Issam E. Maghni +Juan Pablo Canepa +Shuowang (Wayne) Zhang +Ondřej Surý +Juan José Arboleda +Zhao Zhili +Brandon Cheng +Matvii Hodovaniuk +Hayden +yiyuaner +bbara +SeverinLeonhardt +Andy Fiddaman +Romain Roffé +Eagle Liang +Ricky Zhou +Simon Kissane +James M Snell +Ali Mohammad Pur +Erkhes N <71805796+rexes-ND@users.noreply.github.com> +Joshua M. Clulow +Guilherme Íscaro +Martin Storsjö +Claes Nästén diff --git a/include/libuv/ChangeLog b/include/libuv/ChangeLog index 8788d946b..4f2a4bc91 100644 --- a/include/libuv/ChangeLog +++ b/include/libuv/ChangeLog @@ -1,4 +1,238 @@ -2020.09.26, Version 1.40.0 (Stable) +2021.07.21, Version 1.42.0 (Stable) + +Changes since version 1.41.0: + +* doc: fix code highlighting (Darshan Sen) + +* test: move to ASSERT_NULL and ASSERT_NOT_NULL test macros (tjarlama) + +* zos: build in ascii code page (Shuowang (Wayne) Zhang) + +* zos: don't use nanosecond timestamp fields (Shuowang (Wayne) Zhang) + +* zos: introduce zoslib (Shuowang (Wayne) Zhang) + +* zos: use strnlen() from zoslib (Shuowang (Wayne) Zhang) + +* zos: use nanosleep() from zoslib (Shuowang (Wayne) Zhang) + +* zos: use __getargv() from zoslib to get exe path (Shuowang (Wayne) Zhang) + +* zos: treat __rfim_utok as binary (Shuowang (Wayne) Zhang) + +* zos: use execvpe() to set environ explictly (Shuowang (Wayne) Zhang) + +* zos: use custom proctitle implementation (Shuowang (Wayne) Zhang) + +* doc: add instructions for building on z/OS (Shuowang (Wayne) Zhang) + +* linux,udp: enable full ICMP error reporting (Ondřej Surý) + +* test: fix test-udp-send-unreachable (Ondřej Surý) + +* include: fix typo in documentation (Tobias Nießen) + +* chore: use for(;;) instead of while (Yash Ladha) + +* test: remove string + int warning on udp-pummel (Juan José Arboleda) + +* cmake: fix linker flags (Zhao Zhili) + +* test: fix stack-use-after-scope (Zhao Zhili) + +* unix: expose thread_stack_size() internally (Brandon Cheng) + +* darwin: use RLIMIT_STACK for fsevents pthread (Brandon Cheng) + +* darwin: abort on pthread_attr_init fail (Brandon Cheng) + +* benchmark: remove unreachable code (Matvii Hodovaniuk) + +* macos: fix memleaks in uv__get_cpu_speed (George Zhao) + +* Make Thread Sanitizer aware of file descriptor close in uv__close() (Ondřej + Surý) + +* darwin: fix iOS compilation and functionality (Hayden) + +* linux: work around copy_file_range() cephfs bug (Ben Noordhuis) + +* zos: implement uv_get_constrained_memory() (Shuowang (Wayne) Zhang) + +* zos: fix uv_get_free_memory() (Shuowang (Wayne) Zhang) + +* zos: use CVTRLSTG to get total memory accurately (Shuowang (Wayne) Zhang) + +* ibmi: Handle interface names longer than 10 chars (Kevin Adler) + +* docs: update read-the-docs version of sphinx (Jameson Nash) + +* unix: refactor uv_try_write (twosee) + +* linux-core: add proper divide by zero assert (yiyuaner) + +* misc: remove unnecessary _GNU_SOURCE macros (Darshan Sen) + +* test: log to stdout to conform TAP spec (bbara) + +* win,fs: fix C4090 warning with MSVC (SeverinLeonhardt) + +* build: some systems provide dlopen() in libc (Andy Fiddaman) + +* include: add EOVERFLOW status code mapping (Darshan Sen) + +* unix,fs: use uv__load_relaxed and uv__store_relaxed (Darshan Sen) + +* win: fix string encoding issue of uv_os_gethostname (Eagle Liang) + +* unix,process: add uv__write_errno helper function (Ricky Zhou) + +* Re-merge "unix,stream: clear read/write states on close/eof" (Jameson Nash) + +* unix,core: fix errno handling in uv__getpwuid_r (Darshan Sen) + +* errors: map ESOCKTNOSUPPORT errno (Ryan Liptak) + +* doc: uv_read_stop always succeeds (Simon Kissane) + +* inet: fix inconsistent return value of inet_ntop6 (twosee) + +* darwin: fix -Wsometimes-uninitialized warning (twosee) + +* stream: introduce uv_try_write2 function (twosee) + +* poll,win: UV_PRIORITIZED option should not assert (twosee) + +* src: DragonFlyBSD has mmsghdr struct too (David Carlier) + +* cleanup,win: Remove _WIN32 guards on threadpool (James M Snell) + +* freebsd: fix an incompatible pointer type warning (Darshan Sen) + +* core: Correct the conditionals for {cloexec,nonblock}_ioctl (Ali Mohammad + Pur) + +* win,tcp: make uv_close work more like unix (Jameson Nash) + +* doc: more accurate list of valid send_handle's (twosee) + +* win,tcp: translate system errors correctly (twosee) + +* unix: implement cpu_relax() on ppc64 (Ben Noordhuis) + +* docs: move list of project links under PR control (Jameson Nash) + +* test: wrong pointer arithmetic multiplier (Erkhes N) + +* doc: switch discussion forum to github (Jameson Nash) + +* idna: fix OOB read in punycode decoder (Ben Noordhuis) + +* build: make sure -fvisibility=hidden is set (Santiago Gimeno) + +* illumos: event ports to epoll (tjarlama) + +* illumos,tty: UV_TTY_MODE_IO waits for 4 bytes (Joshua M. Clulow) + +* doc: add vtjnash GPG ID (Jameson Nash) + +* linux: read CPU model information on ppc (Richard Lau) + +* darwin: fix uv_barrier race condition (Guilherme Íscaro) + +* unix,stream: fix loop hang after uv_shutdown (Jameson Nash) + +* doc,udp: note that suggested_size is 1 max-sized dgram (Ryan Liptak) + +* mingw: fix building for ARM/AArch64 (Martin Storsjö) + +* unix: strnlen is not available on Solaris 10 (Claes Nästén) + +* sunos: restore use of event ports (Andy Fiddaman) + +* sunos,cmake: use thread-safe errno (Andy Fiddaman) + + +2021.02.14, Version 1.41.0 (Stable), 1dff88e5161cba5c59276d2070d2e304e4dcb242 + +Changes since version 1.40.0: + +* mailmap: update contact information for richardlau (Richard Lau) + +* build: add asan checks (gengjiawen) + +* unix: report bind error in uv_tcp_connect() (Ben Noordhuis) + +* doc: uv_tcp_bind() never returns UV_EADDRINUSE (Ben Noordhuis) + +* test: fix pump and tcp_write_batch benchmarks (Santiago Gimeno) + +* doc: mark IBM i as Tier 2 support (Jesse Gorzinski) + +* doc,poll: add notes (repeated cb & cancel pending cb) (Elad Nachmias) + +* linux: fix -Wincompatible-pointer-types warning (Ben Noordhuis) + +* linux: fix -Wsign-compare warning (Ben Noordhuis) + +* android: add system call api guards (Ben Noordhuis) + +* unix,win: harmonize uv_read_start() error handling (Ben Noordhuis) + +* unix,win: more uv_read_start() argument validation (Ben Noordhuis) + +* build: turn on -fno-strict-aliasing (Ben Noordhuis) + +* stream: add uv_pipe and uv_socketpair to the API (Jameson Nash) + +* unix,win: initialize timer `timeout` field (Ben Noordhuis) + +* bsd-ifaddrs: improve comments (Darshan Sen) + +* test: remove unnecessary uv_fs_stat() calls (Ben Noordhuis) + +* fs: fix utime/futime timestamp rounding errors (Ben Noordhuis) + +* test: ensure reliable floating point comparison (Jameson Nash) + +* unix,fs: fix uv_fs_sendfile() (Santiago Gimeno) + +* unix: fix uv_fs_stat when using statx (Simon Kadisch) + +* linux,macos: fix uv_set_process_title regression (Momtchil Momtchev) + +* doc: clarify UDP errors and recvmmsg (Ethel Weston) + +* test-getaddrinfo: use example.invalid (Drew DeVault) + +* Revert "build: fix android autotools build" (Bernardo Ramos) + +* unix,fs: on DVS fs, statx returns EOPNOTSUPP (Mark Klein) + +* win, fs: mkdir really return UV_EINVAL for invalid names (Nicholas Vavilov) + +* tools: migrate tools/make_dist_html.py to python3 (Dominique Dumont) + +* unix: fix uv_uptime() on linux (schamberg97) + +* unix: check for partial copy_file_range support (Momtchil Momtchev) + +* win: bump minimum supported version to windows 8 (Ben Noordhuis) + +* poll,unix: ensure safety of rapid fd reuse (Bob Weinand) + +* test: fix some warnings (Issam E. Maghni) + +* unix: fix uv_uptime() regression (Santiago Gimeno) + +* doc: fix versionadded metadata (cjihrig) + +* test: fix 'incompatible pointer types' warnings (cjihrig) + +* unix: check for EXDEV in uv__fs_sendfile() (Darshan Sen) + + +2020.09.26, Version 1.40.0 (Stable), 4e69e333252693bd82d6338d6124f0416538dbfc Changes since version 1.39.0: diff --git a/include/libuv/include/uv.h b/include/libuv/include/uv.h index 2557961ee..77503bde9 100644 --- a/include/libuv/include/uv.h +++ b/include/libuv/include/uv.h @@ -126,6 +126,7 @@ extern "C" { XX(ENOTEMPTY, "directory not empty") \ XX(ENOTSOCK, "socket operation on non-socket") \ XX(ENOTSUP, "operation not supported on socket") \ + XX(EOVERFLOW, "value too large for defined data type") \ XX(EPERM, "operation not permitted") \ XX(EPIPE, "broken pipe") \ XX(EPROTO, "protocol error") \ @@ -148,6 +149,7 @@ extern "C" { XX(ENOTTY, "inappropriate ioctl for device") \ XX(EFTYPE, "inappropriate file type or format") \ XX(EILSEQ, "illegal byte sequence") \ + XX(ESOCKTNOSUPPORT, "socket type not supported") \ #define UV_HANDLE_TYPE_MAP(XX) \ XX(ASYNC, async) \ @@ -475,6 +477,12 @@ UV_EXTERN int uv_fileno(const uv_handle_t* handle, uv_os_fd_t* fd); UV_EXTERN uv_buf_t uv_buf_init(char* base, unsigned int len); +UV_EXTERN int uv_pipe(uv_file fds[2], int read_flags, int write_flags); +UV_EXTERN int uv_socketpair(int type, + int protocol, + uv_os_sock_t socket_vector[2], + int flags0, + int flags1); #define UV_STREAM_FIELDS \ /* number of bytes queued for writing */ \ @@ -520,6 +528,10 @@ UV_EXTERN int uv_write2(uv_write_t* req, UV_EXTERN int uv_try_write(uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs); +UV_EXTERN int uv_try_write2(uv_stream_t* handle, + const uv_buf_t bufs[], + unsigned int nbufs, + uv_stream_t* send_handle); /* uv_write_t is a subclass of uv_req_t. */ struct uv_write_s { @@ -620,7 +632,14 @@ enum uv_udp_flags { * in uv_udp_recv_cb, nread will always be 0 and addr will always be NULL. */ UV_UDP_MMSG_FREE = 16, - + /* + * Indicates if IP_RECVERR/IPV6_RECVERR will be set when binding the handle. + * This sets IP_RECVERR for IPv4 and IPV6_RECVERR for IPv6 UDP sockets on + * Linux. This stops the Linux kernel from suppressing some ICMP error + * messages and enables full ICMP error reporting for faster failover. + * This flag is no-op on platforms other than Linux. + */ + UV_UDP_LINUX_RECVERR = 32, /* * Indicates that recvmmsg should be used, if available. */ @@ -933,10 +952,13 @@ typedef enum { UV_WRITABLE_PIPE = 0x20, /* - * Open the child pipe handle in overlapped mode on Windows. - * On Unix it is silently ignored. + * When UV_CREATE_PIPE is specified, specifying UV_NONBLOCK_PIPE opens the + * handle in non-blocking mode in the child. This may cause loss of data, + * if the child is not designed to handle to encounter this mode, + * but can also be significantly more efficient. */ - UV_OVERLAPPED_PIPE = 0x40 + UV_NONBLOCK_PIPE = 0x40, + UV_OVERLAPPED_PIPE = 0x40 /* old name, for compatibility */ } uv_stdio_flags; typedef struct uv_stdio_container_s { diff --git a/include/libuv/include/uv/errno.h b/include/libuv/include/uv/errno.h index aadce9c14..71906b3f5 100644 --- a/include/libuv/include/uv/errno.h +++ b/include/libuv/include/uv/errno.h @@ -445,4 +445,16 @@ # define UV__EILSEQ (-4027) #endif +#if defined(EOVERFLOW) && !defined(_WIN32) +# define UV__EOVERFLOW UV__ERR(EOVERFLOW) +#else +# define UV__EOVERFLOW (-4026) +#endif + +#if defined(ESOCKTNOSUPPORT) && !defined(_WIN32) +# define UV__ESOCKTNOSUPPORT UV__ERR(ESOCKTNOSUPPORT) +#else +# define UV__ESOCKTNOSUPPORT (-4025) +#endif + #endif /* UV_ERRNO_H_ */ diff --git a/include/libuv/include/uv/tree.h b/include/libuv/include/uv/tree.h index f936416e3..2b28835fd 100644 --- a/include/libuv/include/uv/tree.h +++ b/include/libuv/include/uv/tree.h @@ -251,7 +251,7 @@ void name##_SPLAY_MINMAX(struct name *head, int __comp) \ SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL; \ __left = __right = &__node; \ \ - while (1) { \ + for (;;) { \ if (__comp < 0) { \ __tmp = SPLAY_LEFT((head)->sph_root, field); \ if (__tmp == NULL) \ diff --git a/include/libuv/include/uv/version.h b/include/libuv/include/uv/version.h index 5272008a3..d5ba36c3b 100644 --- a/include/libuv/include/uv/version.h +++ b/include/libuv/include/uv/version.h @@ -31,7 +31,7 @@ */ #define UV_VERSION_MAJOR 1 -#define UV_VERSION_MINOR 40 +#define UV_VERSION_MINOR 42 #define UV_VERSION_PATCH 0 #define UV_VERSION_IS_RELEASE 1 #define UV_VERSION_SUFFIX "" diff --git a/include/libuv/src/idna.c b/include/libuv/src/idna.c index 13ffac6be..b44cb16a1 100644 --- a/include/libuv/src/idna.c +++ b/include/libuv/src/idna.c @@ -19,6 +19,7 @@ #include "uv.h" #include "idna.h" +#include #include static unsigned uv__utf8_decode1_slow(const char** p, @@ -32,7 +33,7 @@ static unsigned uv__utf8_decode1_slow(const char** p, if (a > 0xF7) return -1; - switch (*p - pe) { + switch (pe - *p) { default: if (a > 0xEF) { min = 0x10000; @@ -62,6 +63,8 @@ static unsigned uv__utf8_decode1_slow(const char** p, a = 0; break; } + /* Fall through. */ + case 0: return -1; /* Invalid continuation byte. */ } @@ -88,6 +91,8 @@ static unsigned uv__utf8_decode1_slow(const char** p, unsigned uv__utf8_decode1(const char** p, const char* pe) { unsigned a; + assert(*p < pe); + a = (unsigned char) *(*p)++; if (a < 128) @@ -96,9 +101,6 @@ unsigned uv__utf8_decode1(const char** p, const char* pe) { return uv__utf8_decode1_slow(p, pe, a); } -#define foreach_codepoint(c, p, pe) \ - for (; (void) (*p <= pe && (c = uv__utf8_decode1(p, pe))), *p <= pe;) - static int uv__idna_toascii_label(const char* s, const char* se, char** d, char* de) { static const char alphabet[] = "abcdefghijklmnopqrstuvwxyz0123456789"; @@ -121,15 +123,22 @@ static int uv__idna_toascii_label(const char* s, const char* se, ss = s; todo = 0; - foreach_codepoint(c, &s, se) { + /* Note: after this loop we've visited all UTF-8 characters and know + * they're legal so we no longer need to check for decode errors. + */ + while (s < se) { + c = uv__utf8_decode1(&s, se); + + if (c == -1u) + return UV_EINVAL; + if (c < 128) h++; - else if (c == (unsigned) -1) - return UV_EINVAL; else todo++; } + /* Only write "xn--" when there are non-ASCII characters. */ if (todo > 0) { if (*d < de) *(*d)++ = 'x'; if (*d < de) *(*d)++ = 'n'; @@ -137,9 +146,13 @@ static int uv__idna_toascii_label(const char* s, const char* se, if (*d < de) *(*d)++ = '-'; } + /* Write ASCII characters. */ x = 0; s = ss; - foreach_codepoint(c, &s, se) { + while (s < se) { + c = uv__utf8_decode1(&s, se); + assert(c != -1u); + if (c > 127) continue; @@ -166,10 +179,15 @@ static int uv__idna_toascii_label(const char* s, const char* se, while (todo > 0) { m = -1; s = ss; - foreach_codepoint(c, &s, se) + + while (s < se) { + c = uv__utf8_decode1(&s, se); + assert(c != -1u); + if (c >= n) if (c < m) m = c; + } x = m - n; y = h + 1; @@ -181,7 +199,10 @@ static int uv__idna_toascii_label(const char* s, const char* se, n = m; s = ss; - foreach_codepoint(c, &s, se) { + while (s < se) { + c = uv__utf8_decode1(&s, se); + assert(c != -1u); + if (c < n) if (++delta == 0) return UV_E2BIG; /* Overflow. */ @@ -245,8 +266,6 @@ static int uv__idna_toascii_label(const char* s, const char* se, return 0; } -#undef foreach_codepoint - long uv__idna_toascii(const char* s, const char* se, char* d, char* de) { const char* si; const char* st; @@ -256,10 +275,14 @@ long uv__idna_toascii(const char* s, const char* se, char* d, char* de) { ds = d; - for (si = s; si < se; /* empty */) { + si = s; + while (si < se) { st = si; c = uv__utf8_decode1(&si, se); + if (c == -1u) + return UV_EINVAL; + if (c != '.') if (c != 0x3002) /* 。 */ if (c != 0xFF0E) /* . */ diff --git a/include/libuv/src/inet.c b/include/libuv/src/inet.c index 698ab232e..ddabf22fa 100644 --- a/include/libuv/src/inet.c +++ b/include/libuv/src/inet.c @@ -141,8 +141,9 @@ static int inet_ntop6(const unsigned char *src, char *dst, size_t size) { if (best.base != -1 && (best.base + best.len) == ARRAY_SIZE(words)) *tp++ = ':'; *tp++ = '\0'; - if (UV_E2BIG == uv__strscpy(dst, tmp, size)) + if ((size_t) (tp - tmp) > size) return UV_ENOSPC; + uv__strscpy(dst, tmp, size); return 0; } diff --git a/include/libuv/src/threadpool.c b/include/libuv/src/threadpool.c index 0998938f3..869ae95f5 100644 --- a/include/libuv/src/threadpool.c +++ b/include/libuv/src/threadpool.c @@ -161,7 +161,6 @@ static void post(QUEUE* q, enum uv__work_kind kind) { void uv__threadpool_cleanup(void) { -#ifndef _WIN32 unsigned int i; if (nthreads == 0) @@ -181,7 +180,6 @@ void uv__threadpool_cleanup(void) { threads = NULL; nthreads = 0; -#endif } diff --git a/include/libuv/src/timer.c b/include/libuv/src/timer.c index 1bea2a8bd..bc680e71a 100644 --- a/include/libuv/src/timer.c +++ b/include/libuv/src/timer.c @@ -58,6 +58,7 @@ static int timer_less_than(const struct heap_node* ha, int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle) { uv__handle_init(loop, (uv_handle_t*)handle, UV_TIMER); handle->timer_cb = NULL; + handle->timeout = 0; handle->repeat = 0; return 0; } diff --git a/include/libuv/src/unix/async.c b/include/libuv/src/unix/async.c index 5f58fb88d..e1805c323 100644 --- a/include/libuv/src/unix/async.c +++ b/include/libuv/src/unix/async.c @@ -214,7 +214,7 @@ static int uv__async_start(uv_loop_t* loop) { pipefd[0] = err; pipefd[1] = -1; #else - err = uv__make_pipe(pipefd, UV__F_NONBLOCK); + err = uv__make_pipe(pipefd, UV_NONBLOCK_PIPE); if (err < 0) return err; #endif diff --git a/include/libuv/src/unix/atomic-ops.h b/include/libuv/src/unix/atomic-ops.h index 347d19365..c48d05843 100644 --- a/include/libuv/src/unix/atomic-ops.h +++ b/include/libuv/src/unix/atomic-ops.h @@ -52,9 +52,11 @@ UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)) { UV_UNUSED(static void cpu_relax(void)) { #if defined(__i386__) || defined(__x86_64__) - __asm__ __volatile__ ("rep; nop"); /* a.k.a. PAUSE */ + __asm__ __volatile__ ("rep; nop" ::: "memory"); /* a.k.a. PAUSE */ #elif (defined(__arm__) && __ARM_ARCH >= 7) || defined(__aarch64__) - __asm__ volatile("yield"); + __asm__ __volatile__ ("yield" ::: "memory"); +#elif defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__) + __asm__ __volatile__ ("or 1,1,1; or 2,2,2" ::: "memory"); #endif } diff --git a/include/libuv/src/unix/bsd-ifaddrs.c b/include/libuv/src/unix/bsd-ifaddrs.c index 5223ab487..e48934bce 100644 --- a/include/libuv/src/unix/bsd-ifaddrs.c +++ b/include/libuv/src/unix/bsd-ifaddrs.c @@ -42,8 +42,8 @@ static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) { return 1; #if !defined(__CYGWIN__) && !defined(__MSYS__) /* - * If `exclude_type` is `UV__EXCLUDE_IFPHYS`, just see whether `sa_family` - * equals to `AF_LINK` or not. Otherwise, the result depends on the operation + * If `exclude_type` is `UV__EXCLUDE_IFPHYS`, return whether `sa_family` + * equals `AF_LINK`. Otherwise, the result depends on the operating * system with `AF_LINK` or `PF_INET`. */ if (exclude_type == UV__EXCLUDE_IFPHYS) @@ -53,7 +53,7 @@ static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) { defined(__HAIKU__) /* * On BSD getifaddrs returns information related to the raw underlying - * devices. We're not interested in this information. + * devices. We're not interested in this information. */ if (ent->ifa_addr->sa_family == AF_LINK) return 1; diff --git a/include/libuv/src/unix/core.c b/include/libuv/src/unix/core.c index 1597828c8..71e9c525c 100644 --- a/include/libuv/src/unix/core.c +++ b/include/libuv/src/unix/core.c @@ -88,6 +88,10 @@ extern char** environ; # define uv__accept4 accept4 #endif +#if defined(__linux__) && defined(__SANITIZE_THREAD__) && defined(__clang__) +# include +#endif + static int uv__run_pending(uv_loop_t* loop); /* Verify that uv_buf_t is ABI-compatible with struct iovec. */ @@ -539,7 +543,13 @@ int uv__close_nocancel(int fd) { return close$NOCANCEL$UNIX2003(fd); #endif #pragma GCC diagnostic pop -#elif defined(__linux__) +#elif defined(__linux__) && defined(__SANITIZE_THREAD__) && defined(__clang__) + long rc; + __sanitizer_syscall_pre_close(fd); + rc = syscall(SYS_close, fd); + __sanitizer_syscall_post_close(rc, fd); + return rc; +#elif defined(__linux__) && !defined(__SANITIZE_THREAD__) return syscall(SYS_close, fd); #else return close(fd); @@ -574,7 +584,7 @@ int uv__close(int fd) { return uv__close_nocheckstdio(fd); } - +#if UV__NONBLOCK_IS_IOCTL int uv__nonblock_ioctl(int fd, int set) { int r; @@ -589,7 +599,6 @@ int uv__nonblock_ioctl(int fd, int set) { } -#if !defined(__CYGWIN__) && !defined(__MSYS__) && !defined(__HAIKU__) int uv__cloexec_ioctl(int fd, int set) { int r; @@ -925,13 +934,12 @@ void uv__io_stop(uv_loop_t* loop, uv__io_t* w, unsigned int events) { if (w->pevents == 0) { QUEUE_REMOVE(&w->watcher_queue); QUEUE_INIT(&w->watcher_queue); + w->events = 0; - if (loop->watchers[w->fd] != NULL) { - assert(loop->watchers[w->fd] == w); + if (w == loop->watchers[w->fd]) { assert(loop->nfds > 0); loop->watchers[w->fd] = NULL; loop->nfds--; - w->events = 0; } } else if (QUEUE_EMPTY(&w->watcher_queue)) @@ -1175,7 +1183,9 @@ int uv__getpwuid_r(uv_passwd_t* pwd) { if (buf == NULL) return UV_ENOMEM; - r = getpwuid_r(uid, &pw, buf, bufsize, &result); + do + r = getpwuid_r(uid, &pw, buf, bufsize, &result); + while (r == EINTR); if (r != ERANGE) break; @@ -1185,7 +1195,7 @@ int uv__getpwuid_r(uv_passwd_t* pwd) { if (r != 0) { uv__free(buf); - return -r; + return UV__ERR(r); } if (result == NULL) { @@ -1571,7 +1581,7 @@ int uv__search_path(const char* prog, char* buf, size_t* buflen) { buf[*buflen] = '\0'; return 0; - } + } /* Case iii). Search PATH environment variable */ cloned_path = NULL; diff --git a/include/libuv/src/unix/darwin.c b/include/libuv/src/unix/darwin.c index d0ecd452d..a7be0dd2f 100644 --- a/include/libuv/src/unix/darwin.c +++ b/include/libuv/src/unix/darwin.c @@ -33,9 +33,7 @@ #include #include /* sysconf */ -#if !TARGET_OS_IPHONE #include "darwin-stub.h" -#endif static uv_once_t once = UV_ONCE_INIT; static uint64_t (*time_func)(void); @@ -223,10 +221,10 @@ static int uv__get_cpu_speed(uint64_t* speed) { err = UV_ENOENT; core_foundation_handle = dlopen("/System/Library/Frameworks/" "CoreFoundation.framework/" - "Versions/A/CoreFoundation", + "CoreFoundation", RTLD_LAZY | RTLD_LOCAL); iokit_handle = dlopen("/System/Library/Frameworks/IOKit.framework/" - "Versions/A/IOKit", + "IOKit", RTLD_LAZY | RTLD_LOCAL); if (core_foundation_handle == NULL || iokit_handle == NULL) @@ -304,6 +302,12 @@ static int uv__get_cpu_speed(uint64_t* speed) { pIOObjectRelease(it); err = 0; + + if (device_type_str != NULL) + pCFRelease(device_type_str); + if (clock_frequency_str != NULL) + pCFRelease(clock_frequency_str); + out: if (core_foundation_handle != NULL) dlclose(core_foundation_handle); diff --git a/include/libuv/src/unix/epoll.c b/include/libuv/src/unix/epoll.c new file mode 100644 index 000000000..97348e254 --- /dev/null +++ b/include/libuv/src/unix/epoll.c @@ -0,0 +1,422 @@ +/* Copyright libuv contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" +#include +#include + +int uv__epoll_init(uv_loop_t* loop) { + int fd; + fd = epoll_create1(O_CLOEXEC); + + /* epoll_create1() can fail either because it's not implemented (old kernel) + * or because it doesn't understand the O_CLOEXEC flag. + */ + if (fd == -1 && (errno == ENOSYS || errno == EINVAL)) { + fd = epoll_create(256); + + if (fd != -1) + uv__cloexec(fd, 1); + } + + loop->backend_fd = fd; + if (fd == -1) + return UV__ERR(errno); + + return 0; +} + + +void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { + struct epoll_event* events; + struct epoll_event dummy; + uintptr_t i; + uintptr_t nfds; + + assert(loop->watchers != NULL); + assert(fd >= 0); + + events = (struct epoll_event*) loop->watchers[loop->nwatchers]; + nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; + if (events != NULL) + /* Invalidate events with same file descriptor */ + for (i = 0; i < nfds; i++) + if (events[i].data.fd == fd) + events[i].data.fd = -1; + + /* Remove the file descriptor from the epoll. + * This avoids a problem where the same file description remains open + * in another process, causing repeated junk epoll events. + * + * We pass in a dummy epoll_event, to work around a bug in old kernels. + */ + if (loop->backend_fd >= 0) { + /* Work around a bug in kernels 3.10 to 3.19 where passing a struct that + * has the EPOLLWAKEUP flag set generates spurious audit syslog warnings. + */ + memset(&dummy, 0, sizeof(dummy)); + epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &dummy); + } +} + + +int uv__io_check_fd(uv_loop_t* loop, int fd) { + struct epoll_event e; + int rc; + + memset(&e, 0, sizeof(e)); + e.events = POLLIN; + e.data.fd = -1; + + rc = 0; + if (epoll_ctl(loop->backend_fd, EPOLL_CTL_ADD, fd, &e)) + if (errno != EEXIST) + rc = UV__ERR(errno); + + if (rc == 0) + if (epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &e)) + abort(); + + return rc; +} + + +void uv__io_poll(uv_loop_t* loop, int timeout) { + /* A bug in kernels < 2.6.37 makes timeouts larger than ~30 minutes + * effectively infinite on 32 bits architectures. To avoid blocking + * indefinitely, we cap the timeout and poll again if necessary. + * + * Note that "30 minutes" is a simplification because it depends on + * the value of CONFIG_HZ. The magic constant assumes CONFIG_HZ=1200, + * that being the largest value I have seen in the wild (and only once.) + */ + static const int max_safe_timeout = 1789569; + static int no_epoll_pwait_cached; + static int no_epoll_wait_cached; + int no_epoll_pwait; + int no_epoll_wait; + struct epoll_event events[1024]; + struct epoll_event* pe; + struct epoll_event e; + int real_timeout; + QUEUE* q; + uv__io_t* w; + sigset_t sigset; + uint64_t sigmask; + uint64_t base; + int have_signals; + int nevents; + int count; + int nfds; + int fd; + int op; + int i; + int user_timeout; + int reset_timeout; + + if (loop->nfds == 0) { + assert(QUEUE_EMPTY(&loop->watcher_queue)); + return; + } + + memset(&e, 0, sizeof(e)); + + while (!QUEUE_EMPTY(&loop->watcher_queue)) { + q = QUEUE_HEAD(&loop->watcher_queue); + QUEUE_REMOVE(q); + QUEUE_INIT(q); + + w = QUEUE_DATA(q, uv__io_t, watcher_queue); + assert(w->pevents != 0); + assert(w->fd >= 0); + assert(w->fd < (int) loop->nwatchers); + + e.events = w->pevents; + e.data.fd = w->fd; + + if (w->events == 0) + op = EPOLL_CTL_ADD; + else + op = EPOLL_CTL_MOD; + + /* XXX Future optimization: do EPOLL_CTL_MOD lazily if we stop watching + * events, skip the syscall and squelch the events after epoll_wait(). + */ + if (epoll_ctl(loop->backend_fd, op, w->fd, &e)) { + if (errno != EEXIST) + abort(); + + assert(op == EPOLL_CTL_ADD); + + /* We've reactivated a file descriptor that's been watched before. */ + if (epoll_ctl(loop->backend_fd, EPOLL_CTL_MOD, w->fd, &e)) + abort(); + } + + w->events = w->pevents; + } + + sigmask = 0; + if (loop->flags & UV_LOOP_BLOCK_SIGPROF) { + sigemptyset(&sigset); + sigaddset(&sigset, SIGPROF); + sigmask |= 1 << (SIGPROF - 1); + } + + assert(timeout >= -1); + base = loop->time; + count = 48; /* Benchmarks suggest this gives the best throughput. */ + real_timeout = timeout; + + if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { + reset_timeout = 1; + user_timeout = timeout; + timeout = 0; + } else { + reset_timeout = 0; + user_timeout = 0; + } + + /* You could argue there is a dependency between these two but + * ultimately we don't care about their ordering with respect + * to one another. Worst case, we make a few system calls that + * could have been avoided because another thread already knows + * they fail with ENOSYS. Hardly the end of the world. + */ + no_epoll_pwait = uv__load_relaxed(&no_epoll_pwait_cached); + no_epoll_wait = uv__load_relaxed(&no_epoll_wait_cached); + + for (;;) { + /* Only need to set the provider_entry_time if timeout != 0. The function + * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. + */ + if (timeout != 0) + uv__metrics_set_provider_entry_time(loop); + + /* See the comment for max_safe_timeout for an explanation of why + * this is necessary. Executive summary: kernel bug workaround. + */ + if (sizeof(int32_t) == sizeof(long) && timeout >= max_safe_timeout) + timeout = max_safe_timeout; + + if (sigmask != 0 && no_epoll_pwait != 0) + if (pthread_sigmask(SIG_BLOCK, &sigset, NULL)) + abort(); + + if (no_epoll_wait != 0 || (sigmask != 0 && no_epoll_pwait == 0)) { + nfds = epoll_pwait(loop->backend_fd, + events, + ARRAY_SIZE(events), + timeout, + &sigset); + if (nfds == -1 && errno == ENOSYS) { + uv__store_relaxed(&no_epoll_pwait_cached, 1); + no_epoll_pwait = 1; + } + } else { + nfds = epoll_wait(loop->backend_fd, + events, + ARRAY_SIZE(events), + timeout); + if (nfds == -1 && errno == ENOSYS) { + uv__store_relaxed(&no_epoll_wait_cached, 1); + no_epoll_wait = 1; + } + } + + if (sigmask != 0 && no_epoll_pwait != 0) + if (pthread_sigmask(SIG_UNBLOCK, &sigset, NULL)) + abort(); + + /* Update loop->time unconditionally. It's tempting to skip the update when + * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the + * operating system didn't reschedule our process while in the syscall. + */ + SAVE_ERRNO(uv__update_time(loop)); + + if (nfds == 0) { + assert(timeout != -1); + + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + + if (timeout == -1) + continue; + + if (timeout == 0) + return; + + /* We may have been inside the system call for longer than |timeout| + * milliseconds so we need to update the timestamp to avoid drift. + */ + goto update_timeout; + } + + if (nfds == -1) { + if (errno == ENOSYS) { + /* epoll_wait() or epoll_pwait() failed, try the other system call. */ + assert(no_epoll_wait == 0 || no_epoll_pwait == 0); + continue; + } + + if (errno != EINTR) + abort(); + + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + + if (timeout == -1) + continue; + + if (timeout == 0) + return; + + /* Interrupted by a signal. Update timeout and poll again. */ + goto update_timeout; + } + + have_signals = 0; + nevents = 0; + + { + /* Squelch a -Waddress-of-packed-member warning with gcc >= 9. */ + union { + struct epoll_event* events; + uv__io_t* watchers; + } x; + + x.events = events; + assert(loop->watchers != NULL); + loop->watchers[loop->nwatchers] = x.watchers; + loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds; + } + + for (i = 0; i < nfds; i++) { + pe = events + i; + fd = pe->data.fd; + + /* Skip invalidated events, see uv__platform_invalidate_fd */ + if (fd == -1) + continue; + + assert(fd >= 0); + assert((unsigned) fd < loop->nwatchers); + + w = loop->watchers[fd]; + + if (w == NULL) { + /* File descriptor that we've stopped watching, disarm it. + * + * Ignore all errors because we may be racing with another thread + * when the file descriptor is closed. + */ + epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, pe); + continue; + } + + /* Give users only events they're interested in. Prevents spurious + * callbacks when previous callback invocation in this loop has stopped + * the current watcher. Also, filters out events that users has not + * requested us to watch. + */ + pe->events &= w->pevents | POLLERR | POLLHUP; + + /* Work around an epoll quirk where it sometimes reports just the + * EPOLLERR or EPOLLHUP event. In order to force the event loop to + * move forward, we merge in the read/write events that the watcher + * is interested in; uv__read() and uv__write() will then deal with + * the error or hangup in the usual fashion. + * + * Note to self: happens when epoll reports EPOLLIN|EPOLLHUP, the user + * reads the available data, calls uv_read_stop(), then sometime later + * calls uv_read_start() again. By then, libuv has forgotten about the + * hangup and the kernel won't report EPOLLIN again because there's + * nothing left to read. If anything, libuv is to blame here. The + * current hack is just a quick bandaid; to properly fix it, libuv + * needs to remember the error/hangup event. We should get that for + * free when we switch over to edge-triggered I/O. + */ + if (pe->events == POLLERR || pe->events == POLLHUP) + pe->events |= + w->pevents & (POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI); + + if (pe->events != 0) { + /* Run signal watchers last. This also affects child process watchers + * because those are implemented in terms of signal watchers. + */ + if (w == &loop->signal_io_watcher) { + have_signals = 1; + } else { + uv__metrics_update_idle_time(loop); + w->cb(loop, w, pe->events); + } + + nevents++; + } + } + + if (reset_timeout != 0) { + timeout = user_timeout; + reset_timeout = 0; + } + + if (have_signals != 0) { + uv__metrics_update_idle_time(loop); + loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN); + } + + loop->watchers[loop->nwatchers] = NULL; + loop->watchers[loop->nwatchers + 1] = NULL; + + if (have_signals != 0) + return; /* Event loop should cycle now so don't poll again. */ + + if (nevents != 0) { + if (nfds == ARRAY_SIZE(events) && --count != 0) { + /* Poll for more events but don't block this time. */ + timeout = 0; + continue; + } + return; + } + + if (timeout == 0) + return; + + if (timeout == -1) + continue; + +update_timeout: + assert(timeout > 0); + + real_timeout -= (loop->time - base); + if (real_timeout <= 0) + return; + + timeout = real_timeout; + } +} + diff --git a/include/libuv/src/unix/freebsd.c b/include/libuv/src/unix/freebsd.c index fe795a0e7..170b897e2 100644 --- a/include/libuv/src/unix/freebsd.c +++ b/include/libuv/src/unix/freebsd.c @@ -265,8 +265,11 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { int uv__sendmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) { -#if __FreeBSD__ >= 11 - return sendmmsg(fd, mmsg, vlen, /* flags */ 0); +#if __FreeBSD__ >= 11 && !defined(__DragonFly__) + return sendmmsg(fd, + (struct mmsghdr*) mmsg, + vlen, + 0 /* flags */); #else return errno = ENOSYS, -1; #endif @@ -274,8 +277,12 @@ int uv__sendmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) { int uv__recvmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) { -#if __FreeBSD__ >= 11 - return recvmmsg(fd, mmsg, vlen, 0 /* flags */, NULL /* timeout */); +#if __FreeBSD__ >= 11 && !defined(__DragonFly__) + return recvmmsg(fd, + (struct mmsghdr*) mmsg, + vlen, + 0 /* flags */, + NULL /* timeout */); #else return errno = ENOSYS, -1; #endif diff --git a/include/libuv/src/unix/fs.c b/include/libuv/src/unix/fs.c index 556fd103c..eb17fb4a2 100644 --- a/include/libuv/src/unix/fs.c +++ b/include/libuv/src/unix/fs.c @@ -56,8 +56,13 @@ # define HAVE_PREADV 0 #endif +#if defined(__linux__) +# include "sys/utsname.h" +#endif + #if defined(__linux__) || defined(__sun) # include +# include #endif #if defined(__APPLE__) @@ -212,14 +217,30 @@ static ssize_t uv__fs_fdatasync(uv_fs_t* req) { UV_UNUSED(static struct timespec uv__fs_to_timespec(double time)) { struct timespec ts; ts.tv_sec = time; - ts.tv_nsec = (uint64_t)(time * 1000000) % 1000000 * 1000; + ts.tv_nsec = (time - ts.tv_sec) * 1e9; + + /* TODO(bnoordhuis) Remove this. utimesat() has nanosecond resolution but we + * stick to microsecond resolution for the sake of consistency with other + * platforms. I'm the original author of this compatibility hack but I'm + * less convinced it's useful nowadays. + */ + ts.tv_nsec -= ts.tv_nsec % 1000; + + if (ts.tv_nsec < 0) { + ts.tv_nsec += 1e9; + ts.tv_sec -= 1; + } return ts; } UV_UNUSED(static struct timeval uv__fs_to_timeval(double time)) { struct timeval tv; tv.tv_sec = time; - tv.tv_usec = (uint64_t)(time * 1000000) % 1000000; + tv.tv_usec = (time - tv.tv_sec) * 1e6; + if (tv.tv_usec < 0) { + tv.tv_usec += 1e6; + tv.tv_sec -= 1; + } return tv; } @@ -227,9 +248,6 @@ static ssize_t uv__fs_futime(uv_fs_t* req) { #if defined(__linux__) \ || defined(_AIX71) \ || defined(__HAIKU__) - /* utimesat() has nanosecond resolution but we stick to microseconds - * for the sake of consistency with other platforms. - */ struct timespec ts[2]; ts[0] = uv__fs_to_timespec(req->atime); ts[1] = uv__fs_to_timespec(req->mtime); @@ -887,6 +905,50 @@ static ssize_t uv__fs_sendfile_emul(uv_fs_t* req) { } +#ifdef __linux__ +static unsigned uv__kernel_version(void) { + static unsigned cached_version; + struct utsname u; + unsigned version; + unsigned major; + unsigned minor; + unsigned patch; + + version = uv__load_relaxed(&cached_version); + if (version != 0) + return version; + + if (-1 == uname(&u)) + return 0; + + if (3 != sscanf(u.release, "%u.%u.%u", &major, &minor, &patch)) + return 0; + + version = major * 65536 + minor * 256 + patch; + uv__store_relaxed(&cached_version, version); + + return version; +} + + +/* Pre-4.20 kernels have a bug where CephFS uses the RADOS copy-from command + * in copy_file_range() when it shouldn't. There is no workaround except to + * fall back to a regular copy. + */ +static int uv__is_buggy_cephfs(int fd) { + struct statfs s; + + if (-1 == fstatfs(fd, &s)) + return 0; + + if (s.f_type != /* CephFS */ 0xC36400) + return 0; + + return uv__kernel_version() < /* 4.20.0 */ 0x041400; +} +#endif /* __linux__ */ + + static ssize_t uv__fs_sendfile(uv_fs_t* req) { int in_fd; int out_fd; @@ -903,14 +965,25 @@ static ssize_t uv__fs_sendfile(uv_fs_t* req) { #ifdef __linux__ { - static int copy_file_range_support = 1; + static int no_copy_file_range_support; - if (copy_file_range_support) { - r = uv__fs_copy_file_range(in_fd, NULL, out_fd, &off, req->bufsml[0].len, 0); + if (uv__load_relaxed(&no_copy_file_range_support) == 0) { + r = uv__fs_copy_file_range(in_fd, &off, out_fd, NULL, req->bufsml[0].len, 0); if (r == -1 && errno == ENOSYS) { + /* ENOSYS - it will never work */ + errno = 0; + uv__store_relaxed(&no_copy_file_range_support, 1); + } else if (r == -1 && errno == EACCES && uv__is_buggy_cephfs(in_fd)) { + /* EACCES - pre-4.20 kernels have a bug where CephFS uses the RADOS + copy-from command when it shouldn't */ + errno = 0; + uv__store_relaxed(&no_copy_file_range_support, 1); + } else if (r == -1 && (errno == ENOTSUP || errno == EXDEV)) { + /* ENOTSUP - it could work on another file system type */ + /* EXDEV - it will not work when in_fd and out_fd are not on the same + mounted filesystem (pre Linux 5.3) */ errno = 0; - copy_file_range_support = 0; } else { goto ok; } @@ -1010,9 +1083,6 @@ static ssize_t uv__fs_utime(uv_fs_t* req) { || defined(_AIX71) \ || defined(__sun) \ || defined(__HAIKU__) - /* utimesat() has nanosecond resolution but we stick to microseconds - * for the sake of consistency with other platforms. - */ struct timespec ts[2]; ts[0] = uv__fs_to_timespec(req->atime); ts[1] = uv__fs_to_timespec(req->mtime); @@ -1220,7 +1290,7 @@ static ssize_t uv__fs_copyfile(uv_fs_t* req) { if (fstatfs(dstfd, &s) == -1) goto out; - if (s.f_type != /* CIFS */ 0xFF534D42u) + if ((unsigned) s.f_type != /* CIFS */ 0xFF534D42u) goto out; } @@ -1340,7 +1410,8 @@ static void uv__to_stat(struct stat* src, uv_stat_t* dst) { dst->st_birthtim.tv_nsec = src->st_ctimensec; dst->st_flags = 0; dst->st_gen = 0; -#elif !defined(_AIX) && ( \ +#elif !defined(_AIX) && \ + !defined(__MVS__) && ( \ defined(__DragonFly__) || \ defined(__FreeBSD__) || \ defined(__OpenBSD__) || \ @@ -1420,8 +1491,9 @@ static int uv__fs_statx(int fd, case -1: /* EPERM happens when a seccomp filter rejects the system call. * Has been observed with libseccomp < 2.3.3 and docker < 18.04. + * EOPNOTSUPP is used on DVS exported filesystems */ - if (errno != EINVAL && errno != EPERM && errno != ENOSYS) + if (errno != EINVAL && errno != EPERM && errno != ENOSYS && errno != EOPNOTSUPP) return -1; /* Fall through. */ default: @@ -1434,12 +1506,12 @@ static int uv__fs_statx(int fd, return UV_ENOSYS; } - buf->st_dev = 256 * statxbuf.stx_dev_major + statxbuf.stx_dev_minor; + buf->st_dev = makedev(statxbuf.stx_dev_major, statxbuf.stx_dev_minor); buf->st_mode = statxbuf.stx_mode; buf->st_nlink = statxbuf.stx_nlink; buf->st_uid = statxbuf.stx_uid; buf->st_gid = statxbuf.stx_gid; - buf->st_rdev = statxbuf.stx_rdev_major; + buf->st_rdev = makedev(statxbuf.stx_rdev_major, statxbuf.stx_rdev_minor); buf->st_ino = statxbuf.stx_ino; buf->st_size = statxbuf.stx_size; buf->st_blksize = statxbuf.stx_blksize; diff --git a/include/libuv/src/unix/fsevents.c b/include/libuv/src/unix/fsevents.c index a51f29b3f..bf4f1f6a5 100644 --- a/include/libuv/src/unix/fsevents.c +++ b/include/libuv/src/unix/fsevents.c @@ -595,8 +595,7 @@ static int uv__fsevents_global_init(void) { static int uv__fsevents_loop_init(uv_loop_t* loop) { CFRunLoopSourceContext ctx; uv__cf_loop_state_t* state; - pthread_attr_t attr_storage; - pthread_attr_t* attr; + pthread_attr_t attr; int err; if (loop->cf_state != NULL) @@ -641,25 +640,19 @@ static int uv__fsevents_loop_init(uv_loop_t* loop) { goto fail_signal_source_create; } - /* In the unlikely event that pthread_attr_init() fails, create the thread - * with the default stack size. We'll use a little more address space but - * that in itself is not a fatal error. - */ - attr = &attr_storage; - if (pthread_attr_init(attr)) - attr = NULL; + if (pthread_attr_init(&attr)) + abort(); - if (attr != NULL) - if (pthread_attr_setstacksize(attr, 4 * PTHREAD_STACK_MIN)) - abort(); + if (pthread_attr_setstacksize(&attr, uv__thread_stack_size())) + abort(); loop->cf_state = state; /* uv_thread_t is an alias for pthread_t. */ - err = UV__ERR(pthread_create(&loop->cf_thread, attr, uv__cf_loop_runner, loop)); + err = UV__ERR(pthread_create(&loop->cf_thread, &attr, uv__cf_loop_runner, loop)); - if (attr != NULL) - pthread_attr_destroy(attr); + if (pthread_attr_destroy(&attr)) + abort(); if (err) goto fail_thread_create; diff --git a/include/libuv/src/unix/getaddrinfo.c b/include/libuv/src/unix/getaddrinfo.c index d7ca7d1a4..77337ace9 100644 --- a/include/libuv/src/unix/getaddrinfo.c +++ b/include/libuv/src/unix/getaddrinfo.c @@ -21,9 +21,6 @@ /* Expose glibc-specific EAI_* error codes. Needs to be defined before we * include any headers. */ -#ifndef _GNU_SOURCE -# define _GNU_SOURCE -#endif #include "uv.h" #include "internal.h" diff --git a/include/libuv/src/unix/ibmi.c b/include/libuv/src/unix/ibmi.c index 96efc02ba..8c6ae6363 100644 --- a/include/libuv/src/unix/ibmi.c +++ b/include/libuv/src/unix/ibmi.c @@ -26,7 +26,6 @@ #include #include #include -#include #include #include @@ -166,7 +165,7 @@ static void iconv_a2e(const char* src, unsigned char dst[], size_t length) { srclen = strlen(src); if (srclen > length) - abort(); + srclen = length; for (i = 0; i < srclen; i++) dst[i] = a2e[src[i]]; /* padding the remaining part with spaces */ @@ -360,6 +359,10 @@ static int get_ibmi_physical_address(const char* line, char (*phys_addr)[6]) { if (rc != 0) return rc; + if (err.bytes_available > 0) { + return -1; + } + /* convert ebcdic loca_adapter_address to ascii first */ iconv_e2a(rcvr.loca_adapter_address, mac_addr, sizeof(rcvr.loca_adapter_address)); @@ -443,9 +446,42 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { } address->is_internal = cur->ifa_flags & IFF_LOOPBACK ? 1 : 0; if (!address->is_internal) { - int rc = get_ibmi_physical_address(address->name, &address->phys_addr); - if (rc != 0) - r = rc; + int rc = -1; + size_t name_len = strlen(address->name); + /* To get the associated MAC address, we must convert the address to a + * line description. Normally, the name field contains the line + * description name, but for VLANs it has the VLAN appended with a + * period. Since object names can also contain periods and numbers, there + * is no way to know if a returned name is for a VLAN or not. eg. + * *LIND ETH1.1 and *LIND ETH1, VLAN 1 both have the same name: ETH1.1 + * + * Instead, we apply the same heuristic used by some of the XPF ioctls: + * - names > 10 *must* contain a VLAN + * - assume names <= 10 do not contain a VLAN and try directly + * - if >10 or QDCRLIND returned an error, try to strip off a VLAN + * and try again + * - if we still get an error or couldn't find a period, leave the MAC as + * 00:00:00:00:00:00 + */ + if (name_len <= 10) { + /* Assume name does not contain a VLAN ID */ + rc = get_ibmi_physical_address(address->name, &address->phys_addr); + } + + if (name_len > 10 || rc != 0) { + /* The interface name must contain a VLAN ID suffix. Attempt to strip + * it off so we can get the line description to pass to QDCRLIND. + */ + char* temp_name = uv__strdup(address->name); + char* dot = strrchr(temp_name, '.'); + if (dot != NULL) { + *dot = '\0'; + if (strlen(temp_name) <= 10) { + rc = get_ibmi_physical_address(temp_name, &address->phys_addr); + } + } + uv__free(temp_name); + } } address++; @@ -498,4 +534,4 @@ int uv_get_process_title(char* buffer, size_t size) { } void uv__process_title_cleanup(void) { -} \ No newline at end of file +} diff --git a/include/libuv/src/unix/internal.h b/include/libuv/src/unix/internal.h index 570274ed6..12d4da936 100644 --- a/include/libuv/src/unix/internal.h +++ b/include/libuv/src/unix/internal.h @@ -62,6 +62,17 @@ # include #endif +/* + * Define common detection for active Thread Sanitizer + * - clang uses __has_feature(thread_sanitizer) + * - gcc-7+ uses __SANITIZE_THREAD__ + */ +#if defined(__has_feature) +# if __has_feature(thread_sanitizer) +# define __SANITIZE_THREAD__ 1 +# endif +#endif + #if defined(PATH_MAX) # define UV__PATH_MAX PATH_MAX #else @@ -165,9 +176,11 @@ struct uv__stream_queued_fds_s { defined(__NetBSD__) #define uv__cloexec uv__cloexec_ioctl #define uv__nonblock uv__nonblock_ioctl +#define UV__NONBLOCK_IS_IOCTL 1 #else #define uv__cloexec uv__cloexec_fcntl #define uv__nonblock uv__nonblock_fcntl +#define UV__NONBLOCK_IS_IOCTL 0 #endif /* On Linux, uv__nonblock_fcntl() and uv__nonblock_ioctl() do not commute @@ -246,6 +259,7 @@ int uv__signal_loop_fork(uv_loop_t* loop); /* platform specific */ uint64_t uv__hrtime(uv_clocktype_t type); int uv__kqueue_init(uv_loop_t* loop); +int uv__epoll_init(uv_loop_t* loop); int uv__platform_loop_init(uv_loop_t* loop); void uv__platform_loop_delete(uv_loop_t* loop); void uv__platform_invalidate_fd(uv_loop_t* loop, int fd); @@ -261,6 +275,7 @@ void uv__prepare_close(uv_prepare_t* handle); void uv__process_close(uv_process_t* handle); void uv__stream_close(uv_stream_t* handle); void uv__tcp_close(uv_tcp_t* handle); +size_t uv__thread_stack_size(void); void uv__udp_close(uv_udp_t* handle); void uv__udp_finish_close(uv_udp_t* handle); uv_handle_type uv__handle_type(int fd); @@ -282,12 +297,6 @@ int uv___stream_fd(const uv_stream_t* handle); #define uv__stream_fd(handle) ((handle)->io_watcher.fd) #endif /* defined(__APPLE__) */ -#ifdef O_NONBLOCK -# define UV__F_NONBLOCK O_NONBLOCK -#else -# define UV__F_NONBLOCK 1 -#endif - int uv__make_pipe(int fds[2], int flags); #if defined(__APPLE__) @@ -327,7 +336,8 @@ int uv__getsockpeername(const uv_handle_t* handle, #if defined(__linux__) || \ defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel__) + defined(__FreeBSD_kernel__) || \ + defined(__DragonFly__) #define HAVE_MMSG 1 struct uv__mmsghdr { struct msghdr msg_hdr; @@ -340,5 +350,11 @@ int uv__sendmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen); #define HAVE_MMSG 0 #endif +#if defined(__sun) +#if !defined(_POSIX_VERSION) || _POSIX_VERSION < 200809L +size_t strnlen(const char* s, size_t maxlen); +#endif +#endif + #endif /* UV_UNIX_INTERNAL_H_ */ diff --git a/include/libuv/src/unix/linux-core.c b/include/libuv/src/unix/linux-core.c index 4db2f0505..2716e2be6 100644 --- a/include/libuv/src/unix/linux-core.c +++ b/include/libuv/src/unix/linux-core.c @@ -82,29 +82,12 @@ static int read_times(FILE* statfile_fp, static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci); static uint64_t read_cpufreq(unsigned int cpunum); - int uv__platform_loop_init(uv_loop_t* loop) { - int fd; - fd = epoll_create1(O_CLOEXEC); - - /* epoll_create1() can fail either because it's not implemented (old kernel) - * or because it doesn't understand the O_CLOEXEC flag. - */ - if (fd == -1 && (errno == ENOSYS || errno == EINVAL)) { - fd = epoll_create(256); - - if (fd != -1) - uv__cloexec(fd, 1); - } - - loop->backend_fd = fd; + loop->inotify_fd = -1; loop->inotify_watchers = NULL; - if (fd == -1) - return UV__ERR(errno); - - return 0; + return uv__epoll_init(loop); } @@ -134,380 +117,6 @@ void uv__platform_loop_delete(uv_loop_t* loop) { } -void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { - struct epoll_event* events; - struct epoll_event dummy; - uintptr_t i; - uintptr_t nfds; - - assert(loop->watchers != NULL); - assert(fd >= 0); - - events = (struct epoll_event*) loop->watchers[loop->nwatchers]; - nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; - if (events != NULL) - /* Invalidate events with same file descriptor */ - for (i = 0; i < nfds; i++) - if (events[i].data.fd == fd) - events[i].data.fd = -1; - - /* Remove the file descriptor from the epoll. - * This avoids a problem where the same file description remains open - * in another process, causing repeated junk epoll events. - * - * We pass in a dummy epoll_event, to work around a bug in old kernels. - */ - if (loop->backend_fd >= 0) { - /* Work around a bug in kernels 3.10 to 3.19 where passing a struct that - * has the EPOLLWAKEUP flag set generates spurious audit syslog warnings. - */ - memset(&dummy, 0, sizeof(dummy)); - epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &dummy); - } -} - - -int uv__io_check_fd(uv_loop_t* loop, int fd) { - struct epoll_event e; - int rc; - - memset(&e, 0, sizeof(e)); - e.events = POLLIN; - e.data.fd = -1; - - rc = 0; - if (epoll_ctl(loop->backend_fd, EPOLL_CTL_ADD, fd, &e)) - if (errno != EEXIST) - rc = UV__ERR(errno); - - if (rc == 0) - if (epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, &e)) - abort(); - - return rc; -} - - -void uv__io_poll(uv_loop_t* loop, int timeout) { - /* A bug in kernels < 2.6.37 makes timeouts larger than ~30 minutes - * effectively infinite on 32 bits architectures. To avoid blocking - * indefinitely, we cap the timeout and poll again if necessary. - * - * Note that "30 minutes" is a simplification because it depends on - * the value of CONFIG_HZ. The magic constant assumes CONFIG_HZ=1200, - * that being the largest value I have seen in the wild (and only once.) - */ - static const int max_safe_timeout = 1789569; - static int no_epoll_pwait_cached; - static int no_epoll_wait_cached; - int no_epoll_pwait; - int no_epoll_wait; - struct epoll_event events[1024]; - struct epoll_event* pe; - struct epoll_event e; - int real_timeout; - QUEUE* q; - uv__io_t* w; - sigset_t sigset; - uint64_t sigmask; - uint64_t base; - int have_signals; - int nevents; - int count; - int nfds; - int fd; - int op; - int i; - int user_timeout; - int reset_timeout; - - if (loop->nfds == 0) { - assert(QUEUE_EMPTY(&loop->watcher_queue)); - return; - } - - memset(&e, 0, sizeof(e)); - - while (!QUEUE_EMPTY(&loop->watcher_queue)) { - q = QUEUE_HEAD(&loop->watcher_queue); - QUEUE_REMOVE(q); - QUEUE_INIT(q); - - w = QUEUE_DATA(q, uv__io_t, watcher_queue); - assert(w->pevents != 0); - assert(w->fd >= 0); - assert(w->fd < (int) loop->nwatchers); - - e.events = w->pevents; - e.data.fd = w->fd; - - if (w->events == 0) - op = EPOLL_CTL_ADD; - else - op = EPOLL_CTL_MOD; - - /* XXX Future optimization: do EPOLL_CTL_MOD lazily if we stop watching - * events, skip the syscall and squelch the events after epoll_wait(). - */ - if (epoll_ctl(loop->backend_fd, op, w->fd, &e)) { - if (errno != EEXIST) - abort(); - - assert(op == EPOLL_CTL_ADD); - - /* We've reactivated a file descriptor that's been watched before. */ - if (epoll_ctl(loop->backend_fd, EPOLL_CTL_MOD, w->fd, &e)) - abort(); - } - - w->events = w->pevents; - } - - sigmask = 0; - if (loop->flags & UV_LOOP_BLOCK_SIGPROF) { - sigemptyset(&sigset); - sigaddset(&sigset, SIGPROF); - sigmask |= 1 << (SIGPROF - 1); - } - - assert(timeout >= -1); - base = loop->time; - count = 48; /* Benchmarks suggest this gives the best throughput. */ - real_timeout = timeout; - - if (uv__get_internal_fields(loop)->flags & UV_METRICS_IDLE_TIME) { - reset_timeout = 1; - user_timeout = timeout; - timeout = 0; - } else { - reset_timeout = 0; - user_timeout = 0; - } - - /* You could argue there is a dependency between these two but - * ultimately we don't care about their ordering with respect - * to one another. Worst case, we make a few system calls that - * could have been avoided because another thread already knows - * they fail with ENOSYS. Hardly the end of the world. - */ - no_epoll_pwait = uv__load_relaxed(&no_epoll_pwait_cached); - no_epoll_wait = uv__load_relaxed(&no_epoll_wait_cached); - - for (;;) { - /* Only need to set the provider_entry_time if timeout != 0. The function - * will return early if the loop isn't configured with UV_METRICS_IDLE_TIME. - */ - if (timeout != 0) - uv__metrics_set_provider_entry_time(loop); - - /* See the comment for max_safe_timeout for an explanation of why - * this is necessary. Executive summary: kernel bug workaround. - */ - if (sizeof(int32_t) == sizeof(long) && timeout >= max_safe_timeout) - timeout = max_safe_timeout; - - if (sigmask != 0 && no_epoll_pwait != 0) - if (pthread_sigmask(SIG_BLOCK, &sigset, NULL)) - abort(); - - if (no_epoll_wait != 0 || (sigmask != 0 && no_epoll_pwait == 0)) { - nfds = epoll_pwait(loop->backend_fd, - events, - ARRAY_SIZE(events), - timeout, - &sigset); - if (nfds == -1 && errno == ENOSYS) { - uv__store_relaxed(&no_epoll_pwait_cached, 1); - no_epoll_pwait = 1; - } - } else { - nfds = epoll_wait(loop->backend_fd, - events, - ARRAY_SIZE(events), - timeout); - if (nfds == -1 && errno == ENOSYS) { - uv__store_relaxed(&no_epoll_wait_cached, 1); - no_epoll_wait = 1; - } - } - - if (sigmask != 0 && no_epoll_pwait != 0) - if (pthread_sigmask(SIG_UNBLOCK, &sigset, NULL)) - abort(); - - /* Update loop->time unconditionally. It's tempting to skip the update when - * timeout == 0 (i.e. non-blocking poll) but there is no guarantee that the - * operating system didn't reschedule our process while in the syscall. - */ - SAVE_ERRNO(uv__update_time(loop)); - - if (nfds == 0) { - assert(timeout != -1); - - if (reset_timeout != 0) { - timeout = user_timeout; - reset_timeout = 0; - } - - if (timeout == -1) - continue; - - if (timeout == 0) - return; - - /* We may have been inside the system call for longer than |timeout| - * milliseconds so we need to update the timestamp to avoid drift. - */ - goto update_timeout; - } - - if (nfds == -1) { - if (errno == ENOSYS) { - /* epoll_wait() or epoll_pwait() failed, try the other system call. */ - assert(no_epoll_wait == 0 || no_epoll_pwait == 0); - continue; - } - - if (errno != EINTR) - abort(); - - if (reset_timeout != 0) { - timeout = user_timeout; - reset_timeout = 0; - } - - if (timeout == -1) - continue; - - if (timeout == 0) - return; - - /* Interrupted by a signal. Update timeout and poll again. */ - goto update_timeout; - } - - have_signals = 0; - nevents = 0; - - { - /* Squelch a -Waddress-of-packed-member warning with gcc >= 9. */ - union { - struct epoll_event* events; - uv__io_t* watchers; - } x; - - x.events = events; - assert(loop->watchers != NULL); - loop->watchers[loop->nwatchers] = x.watchers; - loop->watchers[loop->nwatchers + 1] = (void*) (uintptr_t) nfds; - } - - for (i = 0; i < nfds; i++) { - pe = events + i; - fd = pe->data.fd; - - /* Skip invalidated events, see uv__platform_invalidate_fd */ - if (fd == -1) - continue; - - assert(fd >= 0); - assert((unsigned) fd < loop->nwatchers); - - w = loop->watchers[fd]; - - if (w == NULL) { - /* File descriptor that we've stopped watching, disarm it. - * - * Ignore all errors because we may be racing with another thread - * when the file descriptor is closed. - */ - epoll_ctl(loop->backend_fd, EPOLL_CTL_DEL, fd, pe); - continue; - } - - /* Give users only events they're interested in. Prevents spurious - * callbacks when previous callback invocation in this loop has stopped - * the current watcher. Also, filters out events that users has not - * requested us to watch. - */ - pe->events &= w->pevents | POLLERR | POLLHUP; - - /* Work around an epoll quirk where it sometimes reports just the - * EPOLLERR or EPOLLHUP event. In order to force the event loop to - * move forward, we merge in the read/write events that the watcher - * is interested in; uv__read() and uv__write() will then deal with - * the error or hangup in the usual fashion. - * - * Note to self: happens when epoll reports EPOLLIN|EPOLLHUP, the user - * reads the available data, calls uv_read_stop(), then sometime later - * calls uv_read_start() again. By then, libuv has forgotten about the - * hangup and the kernel won't report EPOLLIN again because there's - * nothing left to read. If anything, libuv is to blame here. The - * current hack is just a quick bandaid; to properly fix it, libuv - * needs to remember the error/hangup event. We should get that for - * free when we switch over to edge-triggered I/O. - */ - if (pe->events == POLLERR || pe->events == POLLHUP) - pe->events |= - w->pevents & (POLLIN | POLLOUT | UV__POLLRDHUP | UV__POLLPRI); - - if (pe->events != 0) { - /* Run signal watchers last. This also affects child process watchers - * because those are implemented in terms of signal watchers. - */ - if (w == &loop->signal_io_watcher) { - have_signals = 1; - } else { - uv__metrics_update_idle_time(loop); - w->cb(loop, w, pe->events); - } - - nevents++; - } - } - - if (reset_timeout != 0) { - timeout = user_timeout; - reset_timeout = 0; - } - - if (have_signals != 0) { - uv__metrics_update_idle_time(loop); - loop->signal_io_watcher.cb(loop, &loop->signal_io_watcher, POLLIN); - } - - loop->watchers[loop->nwatchers] = NULL; - loop->watchers[loop->nwatchers + 1] = NULL; - - if (have_signals != 0) - return; /* Event loop should cycle now so don't poll again. */ - - if (nevents != 0) { - if (nfds == ARRAY_SIZE(events) && --count != 0) { - /* Poll for more events but don't block this time. */ - timeout = 0; - continue; - } - return; - } - - if (timeout == 0) - return; - - if (timeout == -1) - continue; - -update_timeout: - assert(timeout > 0); - - real_timeout -= (loop->time - base); - if (real_timeout <= 0) - return; - - timeout = real_timeout; - } -} - uint64_t uv__hrtime(uv_clocktype_t type) { static clock_t fast_clock_id = -1; @@ -602,22 +211,53 @@ int uv_resident_set_memory(size_t* rss) { return UV_EINVAL; } +static int uv__slurp(const char* filename, char* buf, size_t len) { + ssize_t n; + int fd; + + assert(len > 0); + + fd = uv__open_cloexec(filename, O_RDONLY); + if (fd < 0) + return fd; + + do + n = read(fd, buf, len - 1); + while (n == -1 && errno == EINTR); + + if (uv__close_nocheckstdio(fd)) + abort(); + + if (n < 0) + return UV__ERR(errno); + + buf[n] = '\0'; + + return 0; +} int uv_uptime(double* uptime) { static volatile int no_clock_boottime; + char buf[128]; struct timespec now; int r; + /* Try /proc/uptime first, then fallback to clock_gettime(). */ + + if (0 == uv__slurp("/proc/uptime", buf, sizeof(buf))) + if (1 == sscanf(buf, "%lf", uptime)) + return 0; + /* Try CLOCK_BOOTTIME first, fall back to CLOCK_MONOTONIC if not available * (pre-2.6.39 kernels). CLOCK_MONOTONIC doesn't increase when the system * is suspended. */ if (no_clock_boottime) { - retry: r = clock_gettime(CLOCK_MONOTONIC, &now); + retry_clock_gettime: r = clock_gettime(CLOCK_MONOTONIC, &now); } else if ((r = clock_gettime(CLOCK_BOOTTIME, &now)) && errno == EINVAL) { no_clock_boottime = 1; - goto retry; + goto retry_clock_gettime; } if (r) @@ -709,14 +349,19 @@ static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci) { } -/* Also reads the CPU frequency on x86. The other architectures only have - * a BogoMIPS field, which may not be very accurate. +/* Also reads the CPU frequency on ppc and x86. The other architectures only + * have a BogoMIPS field, which may not be very accurate. * * Note: Simply returns on error, uv_cpu_info() takes care of the cleanup. */ static int read_models(unsigned int numcpus, uv_cpu_info_t* ci) { +#if defined(__PPC__) + static const char model_marker[] = "cpu\t\t: "; + static const char speed_marker[] = "clock\t\t: "; +#else static const char model_marker[] = "model name\t: "; static const char speed_marker[] = "cpu MHz\t\t: "; +#endif const char* inferred_model; unsigned int model_idx; unsigned int speed_idx; @@ -738,6 +383,7 @@ static int read_models(unsigned int numcpus, uv_cpu_info_t* ci) { #if defined(__arm__) || \ defined(__i386__) || \ defined(__mips__) || \ + defined(__PPC__) || \ defined(__x86_64__) fp = uv__open_file("/proc/cpuinfo"); if (fp == NULL) @@ -786,7 +432,7 @@ static int read_models(unsigned int numcpus, uv_cpu_info_t* ci) { } fclose(fp); -#endif /* __arm__ || __i386__ || __mips__ || __x86_64__ */ +#endif /* __arm__ || __i386__ || __mips__ || __PPC__ || __x86_64__ */ /* Now we want to make sure that all the models contain *something* because * it's not safe to leave them as null. Copy the last entry unless there @@ -824,9 +470,9 @@ static int read_times(FILE* statfile_fp, char buf[1024]; ticks = (unsigned int)sysconf(_SC_CLK_TCK); - multiplier = ((uint64_t)1000L / ticks); assert(ticks != (unsigned int) -1); assert(ticks != 0); + multiplier = ((uint64_t)1000L / ticks); rewind(statfile_fp); @@ -1025,32 +671,6 @@ void uv__set_process_title(const char* title) { } -static int uv__slurp(const char* filename, char* buf, size_t len) { - ssize_t n; - int fd; - - assert(len > 0); - - fd = uv__open_cloexec(filename, O_RDONLY); - if (fd < 0) - return fd; - - do - n = read(fd, buf, len - 1); - while (n == -1 && errno == EINTR); - - if (uv__close_nocheckstdio(fd)) - abort(); - - if (n < 0) - return UV__ERR(errno); - - buf[n] = '\0'; - - return 0; -} - - static uint64_t uv__read_proc_meminfo(const char* what) { uint64_t rc; char* p; diff --git a/include/libuv/src/unix/linux-inotify.c b/include/libuv/src/unix/linux-inotify.c index 42b601adb..c1bd260e1 100644 --- a/include/libuv/src/unix/linux-inotify.c +++ b/include/libuv/src/unix/linux-inotify.c @@ -178,7 +178,7 @@ static void uv__inotify_read(uv_loop_t* loop, /* needs to be large enough for sizeof(inotify_event) + strlen(path) */ char buf[4096]; - while (1) { + for (;;) { do size = read(loop->inotify_fd, buf, sizeof(buf)); while (size == -1 && errno == EINTR); diff --git a/include/libuv/src/unix/linux-syscalls.c b/include/libuv/src/unix/linux-syscalls.c index 44daaf12d..5071cd56d 100644 --- a/include/libuv/src/unix/linux-syscalls.c +++ b/include/libuv/src/unix/linux-syscalls.c @@ -194,37 +194,37 @@ int uv__recvmmsg(int fd, struct uv__mmsghdr* mmsg, unsigned int vlen) { ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset) { -#if defined(__NR_preadv) - return syscall(__NR_preadv, fd, iov, iovcnt, (long)offset, (long)(offset >> 32)); -#else +#if !defined(__NR_preadv) || defined(__ANDROID_API__) && __ANDROID_API__ < 24 return errno = ENOSYS, -1; +#else + return syscall(__NR_preadv, fd, iov, iovcnt, (long)offset, (long)(offset >> 32)); #endif } ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset) { -#if defined(__NR_pwritev) - return syscall(__NR_pwritev, fd, iov, iovcnt, (long)offset, (long)(offset >> 32)); -#else +#if !defined(__NR_pwritev) || defined(__ANDROID_API__) && __ANDROID_API__ < 24 return errno = ENOSYS, -1; +#else + return syscall(__NR_pwritev, fd, iov, iovcnt, (long)offset, (long)(offset >> 32)); #endif } int uv__dup3(int oldfd, int newfd, int flags) { -#if defined(__NR_dup3) - return syscall(__NR_dup3, oldfd, newfd, flags); -#else +#if !defined(__NR_dup3) || defined(__ANDROID_API__) && __ANDROID_API__ < 21 return errno = ENOSYS, -1; +#else + return syscall(__NR_dup3, oldfd, newfd, flags); #endif } ssize_t uv__fs_copy_file_range(int fd_in, - ssize_t* off_in, + off_t* off_in, int fd_out, - ssize_t* off_out, + off_t* off_out, size_t len, unsigned int flags) { @@ -247,21 +247,18 @@ int uv__statx(int dirfd, int flags, unsigned int mask, struct uv__statx* statxbuf) { - /* __NR_statx make Android box killed by SIGSYS. - * That looks like a seccomp2 sandbox filter rejecting the system call. - */ -#if defined(__NR_statx) && !defined(__ANDROID__) - return syscall(__NR_statx, dirfd, path, flags, mask, statxbuf); -#else +#if !defined(__NR_statx) || defined(__ANDROID_API__) && __ANDROID_API__ < 30 return errno = ENOSYS, -1; +#else + return syscall(__NR_statx, dirfd, path, flags, mask, statxbuf); #endif } ssize_t uv__getrandom(void* buf, size_t buflen, unsigned flags) { -#if defined(__NR_getrandom) - return syscall(__NR_getrandom, buf, buflen, flags); -#else +#if !defined(__NR_getrandom) || defined(__ANDROID_API__) && __ANDROID_API__ < 28 return errno = ENOSYS, -1; +#else + return syscall(__NR_getrandom, buf, buflen, flags); #endif } diff --git a/include/libuv/src/unix/linux-syscalls.h b/include/libuv/src/unix/linux-syscalls.h index 761ff32e2..b4d9082d4 100644 --- a/include/libuv/src/unix/linux-syscalls.h +++ b/include/libuv/src/unix/linux-syscalls.h @@ -22,9 +22,6 @@ #ifndef UV_LINUX_SYSCALL_H_ #define UV_LINUX_SYSCALL_H_ -#undef _GNU_SOURCE -#define _GNU_SOURCE - #include #include #include @@ -66,9 +63,9 @@ ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset) int uv__dup3(int oldfd, int newfd, int flags); ssize_t uv__fs_copy_file_range(int fd_in, - ssize_t* off_in, + off_t* off_in, int fd_out, - ssize_t* off_out, + off_t* off_out, size_t len, unsigned int flags); int uv__statx(int dirfd, diff --git a/include/libuv/src/unix/os390-proctitle.c b/include/libuv/src/unix/os390-proctitle.c new file mode 100644 index 000000000..ccda97c9a --- /dev/null +++ b/include/libuv/src/unix/os390-proctitle.c @@ -0,0 +1,136 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include +#include + +static uv_mutex_t process_title_mutex; +static uv_once_t process_title_mutex_once = UV_ONCE_INIT; +static char* process_title = NULL; +static void* args_mem = NULL; + + +static void init_process_title_mutex_once(void) { + uv_mutex_init(&process_title_mutex); +} + + +char** uv_setup_args(int argc, char** argv) { + char** new_argv; + size_t size; + char* s; + int i; + + if (argc <= 0) + return argv; + + /* Calculate how much memory we need for the argv strings. */ + size = 0; + for (i = 0; i < argc; i++) + size += strlen(argv[i]) + 1; + + /* Add space for the argv pointers. */ + size += (argc + 1) * sizeof(char*); + + new_argv = uv__malloc(size); + if (new_argv == NULL) + return argv; + + /* Copy over the strings and set up the pointer table. */ + s = (char*) &new_argv[argc + 1]; + for (i = 0; i < argc; i++) { + size = strlen(argv[i]) + 1; + memcpy(s, argv[i], size); + new_argv[i] = s; + s += size; + } + new_argv[i] = NULL; + + args_mem = new_argv; + process_title = uv__strdup(argv[0]); + + return new_argv; +} + + +int uv_set_process_title(const char* title) { + char* new_title; + + /* If uv_setup_args wasn't called or failed, we can't continue. */ + if (args_mem == NULL) + return UV_ENOBUFS; + + /* We cannot free this pointer when libuv shuts down, + * the process may still be using it. + */ + new_title = uv__strdup(title); + if (new_title == NULL) + return UV_ENOMEM; + + uv_once(&process_title_mutex_once, init_process_title_mutex_once); + uv_mutex_lock(&process_title_mutex); + + if (process_title != NULL) + uv__free(process_title); + + process_title = new_title; + + uv_mutex_unlock(&process_title_mutex); + + return 0; +} + + +int uv_get_process_title(char* buffer, size_t size) { + size_t len; + + if (buffer == NULL || size == 0) + return UV_EINVAL; + + /* If uv_setup_args wasn't called or failed, we can't continue. */ + if (args_mem == NULL || process_title == NULL) + return UV_ENOBUFS; + + uv_once(&process_title_mutex_once, init_process_title_mutex_once); + uv_mutex_lock(&process_title_mutex); + + len = strlen(process_title); + + if (size <= len) { + uv_mutex_unlock(&process_title_mutex); + return UV_ENOBUFS; + } + + strcpy(buffer, process_title); + + uv_mutex_unlock(&process_title_mutex); + + return 0; +} + + +void uv__process_title_cleanup(void) { + uv__free(args_mem); /* Keep valgrind happy. */ + args_mem = NULL; +} diff --git a/include/libuv/src/unix/os390-syscalls.c b/include/libuv/src/unix/os390-syscalls.c index 491e950c5..c19155339 100644 --- a/include/libuv/src/unix/os390-syscalls.c +++ b/include/libuv/src/unix/os390-syscalls.c @@ -27,12 +27,6 @@ #include #include -#define CW_INTRPT 1 -#define CW_CONDVAR 32 - -#pragma linkage(BPX4CTW, OS) -#pragma linkage(BPX1CTW, OS) - static QUEUE global_epoll_queue; static uv_mutex_t global_epoll_lock; static uv_once_t once = UV_ONCE_INIT; @@ -55,7 +49,7 @@ int scandir(const char* maindir, struct dirent*** namelist, if (!mdir) return -1; - while (1) { + for (;;) { dirent = readdir(mdir); if (!dirent) break; @@ -381,46 +375,6 @@ void epoll_queue_close(uv__os390_epoll* lst) { } -int nanosleep(const struct timespec* req, struct timespec* rem) { - unsigned nano; - unsigned seconds; - unsigned events; - unsigned secrem; - unsigned nanorem; - int rv; - int err; - int rsn; - - nano = (int)req->tv_nsec; - seconds = req->tv_sec; - events = CW_CONDVAR | CW_INTRPT; - secrem = 0; - nanorem = 0; - -#if defined(_LP64) - BPX4CTW(&seconds, &nano, &events, &secrem, &nanorem, &rv, &err, &rsn); -#else - BPX1CTW(&seconds, &nano, &events, &secrem, &nanorem, &rv, &err, &rsn); -#endif - - /* Don't clobber errno unless BPX1CTW/BPX4CTW errored. - * Don't leak EAGAIN, that just means the timeout expired. - */ - if (rv == -1) - if (err == EAGAIN) - rv = 0; - else - errno = err; - - if (rem != NULL && (rv == 0 || err == EINTR)) { - rem->tv_nsec = nanorem; - rem->tv_sec = secrem; - } - - return rv; -} - - char* mkdtemp(char* path) { static const char* tempchars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; @@ -550,15 +504,6 @@ ssize_t os390_readlink(const char* path, char* buf, size_t len) { } -size_t strnlen(const char* str, size_t maxlen) { - char* p = memchr(str, 0, maxlen); - if (p == NULL) - return maxlen; - else - return p - str; -} - - int sem_init(UV_PLATFORM_SEM_T* semid, int pshared, unsigned int value) { UNREACHABLE(); } diff --git a/include/libuv/src/unix/os390-syscalls.h b/include/libuv/src/unix/os390-syscalls.h index 86416bbc5..7d59b75e4 100644 --- a/include/libuv/src/unix/os390-syscalls.h +++ b/include/libuv/src/unix/os390-syscalls.h @@ -28,6 +28,7 @@ #include #include #include +#include "zos-base.h" #define EPOLL_CTL_ADD 1 #define EPOLL_CTL_DEL 2 @@ -57,7 +58,6 @@ int epoll_wait(uv__os390_epoll* ep, struct epoll_event *events, int maxevents, i int epoll_file_close(int fd); /* utility functions */ -int nanosleep(const struct timespec* req, struct timespec* rem); int scandir(const char* maindir, struct dirent*** namelist, int (*filter)(const struct dirent *), int (*compar)(const struct dirent **, diff --git a/include/libuv/src/unix/os390.c b/include/libuv/src/unix/os390.c index 3bb44266d..bf0448b51 100644 --- a/include/libuv/src/unix/os390.c +++ b/include/libuv/src/unix/os390.c @@ -28,6 +28,8 @@ #include #include #include +#include +#include "zos-base.h" #if defined(__clang__) #include "csrsic.h" #else @@ -61,12 +63,6 @@ /* Address of the rsm control and enumeration area. */ #define CVTRCEP_OFFSET 0x490 -/* - Number of frames currently available to system. - Excluded are frames backing perm storage, frames offline, and bad frames. -*/ -#define RCEPOOL_OFFSET 0x004 - /* Total number of frames currently on all available frame queues. */ #define RCEAFC_OFFSET 0x088 @@ -144,102 +140,8 @@ uint64_t uv__hrtime(uv_clocktype_t type) { } -/* - Get the exe path using the thread entry information - in the address space. -*/ -static int getexe(const int pid, char* buf, size_t len) { - struct { - int pid; - int thid[2]; - char accesspid; - char accessthid; - char asid[2]; - char loginname[8]; - char flag; - char len; - } Input_data; - - union { - struct { - char gthb[4]; - int pid; - int thid[2]; - char accesspid; - char accessthid[3]; - int lenused; - int offsetProcess; - int offsetConTTY; - int offsetPath; - int offsetCommand; - int offsetFileData; - int offsetThread; - } Output_data; - char buf[2048]; - } Output_buf; - - struct Output_path_type { - char gthe[4]; - short int len; - char path[1024]; - }; - - int Input_length; - int Output_length; - void* Input_address; - void* Output_address; - struct Output_path_type* Output_path; - int rv; - int rc; - int rsn; - - Input_length = PGTH_LEN; - Output_length = sizeof(Output_buf); - Output_address = &Output_buf; - Input_address = &Input_data; - memset(&Input_data, 0, sizeof Input_data); - Input_data.flag |= PGTHAPATH; - Input_data.pid = pid; - Input_data.accesspid = PGTH_CURRENT; - -#ifdef _LP64 - BPX4GTH(&Input_length, - &Input_address, - &Output_length, - &Output_address, - &rv, - &rc, - &rsn); -#else - BPX1GTH(&Input_length, - &Input_address, - &Output_length, - &Output_address, - &rv, - &rc, - &rsn); -#endif - - if (rv == -1) { - errno = rc; - return -1; - } - - /* Check highest byte to ensure data availability */ - assert(((Output_buf.Output_data.offsetPath >>24) & 0xFF) == 'A'); - - /* Get the offset from the lowest 3 bytes */ - Output_path = (struct Output_path_type*) ((char*) (&Output_buf) + - (Output_buf.Output_data.offsetPath & 0x00FFFFFF)); - - if (Output_path->len >= len) { - errno = ENOBUFS; - return -1; - } - - uv__strscpy(buf, Output_path->path, len); - - return 0; +static int getexe(char* buf, size_t len) { + return uv__strscpy(buf, __getargv()[0], len); } @@ -259,8 +161,7 @@ int uv_exepath(char* buffer, size_t* size) { if (buffer == NULL || size == NULL || *size == 0) return UV_EINVAL; - pid = getpid(); - res = getexe(pid, args, sizeof(args)); + res = getexe(args, sizeof(args)); if (res < 0) return UV_EINVAL; @@ -275,25 +176,25 @@ uint64_t uv_get_free_memory(void) { data_area_ptr rcep = {0}; cvt.assign = *(data_area_ptr_assign_type*)(CVT_PTR); rcep.assign = *(data_area_ptr_assign_type*)(cvt.deref + CVTRCEP_OFFSET); - freeram = *((uint64_t*)(rcep.deref + RCEAFC_OFFSET)) * 4; + freeram = (uint64_t)*((uint32_t*)(rcep.deref + RCEAFC_OFFSET)) * 4096; return freeram; } uint64_t uv_get_total_memory(void) { - uint64_t totalram; - - data_area_ptr cvt = {0}; - data_area_ptr rcep = {0}; - cvt.assign = *(data_area_ptr_assign_type*)(CVT_PTR); - rcep.assign = *(data_area_ptr_assign_type*)(cvt.deref + CVTRCEP_OFFSET); - totalram = *((uint64_t*)(rcep.deref + RCEPOOL_OFFSET)) * 4; - return totalram; + /* Use CVTRLSTG to get the size of actual real storage online at IPL in K. */ + return (uint64_t)((int)((char *__ptr32 *__ptr32 *)0)[4][214]) * 1024; } uint64_t uv_get_constrained_memory(void) { - return 0; /* Memory constraints are unknown. */ + struct rlimit rl; + + /* RLIMIT_MEMLIMIT return value is in megabytes rather than bytes. */ + if (getrlimit(RLIMIT_MEMLIMIT, &rl) == 0) + return rl.rlim_cur * 1024 * 1024; + + return 0; /* There is no memory limit set. */ } @@ -733,6 +634,10 @@ static int os390_message_queue_handler(uv__os390_epoll* ep) { /* Some event that we are not interested in. */ return 0; + /* `__rfim_utok` is treated as text when it should be treated as binary while + * running in ASCII mode, resulting in an unwanted autoconversion. + */ + __a2e_l(msg.__rfim_utok, sizeof(msg.__rfim_utok)); handle = *(uv_fs_event_t**)(msg.__rfim_utok); handle->cb(handle, uv__basename_r(handle->path), events, 0); return 1; @@ -959,9 +864,6 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { } } -void uv__set_process_title(const char* title) { - /* do nothing */ -} int uv__io_fork(uv_loop_t* loop) { /* diff --git a/include/libuv/src/unix/pipe.c b/include/libuv/src/unix/pipe.c index 040d57817..788e038e8 100644 --- a/include/libuv/src/unix/pipe.c +++ b/include/libuv/src/unix/pipe.c @@ -379,3 +379,57 @@ int uv_pipe_chmod(uv_pipe_t* handle, int mode) { return r != -1 ? 0 : UV__ERR(errno); } + + +int uv_pipe(uv_os_fd_t fds[2], int read_flags, int write_flags) { + uv_os_fd_t temp[2]; + int err; +#if defined(__FreeBSD__) || defined(__linux__) + int flags = O_CLOEXEC; + + if ((read_flags & UV_NONBLOCK_PIPE) && (write_flags & UV_NONBLOCK_PIPE)) + flags |= UV_FS_O_NONBLOCK; + + if (pipe2(temp, flags)) + return UV__ERR(errno); + + if (flags & UV_FS_O_NONBLOCK) { + fds[0] = temp[0]; + fds[1] = temp[1]; + return 0; + } +#else + if (pipe(temp)) + return UV__ERR(errno); + + if ((err = uv__cloexec(temp[0], 1))) + goto fail; + + if ((err = uv__cloexec(temp[1], 1))) + goto fail; +#endif + + if (read_flags & UV_NONBLOCK_PIPE) + if ((err = uv__nonblock(temp[0], 1))) + goto fail; + + if (write_flags & UV_NONBLOCK_PIPE) + if ((err = uv__nonblock(temp[1], 1))) + goto fail; + + fds[0] = temp[0]; + fds[1] = temp[1]; + return 0; + +fail: + uv__close(temp[0]); + uv__close(temp[1]); + return err; +} + + +int uv__make_pipe(int fds[2], int flags) { + return uv_pipe(fds, + flags & UV_NONBLOCK_PIPE, + flags & UV_NONBLOCK_PIPE); +} diff --git a/include/libuv/src/unix/poll.c b/include/libuv/src/unix/poll.c index 3d5022b22..7a12e2d14 100644 --- a/include/libuv/src/unix/poll.c +++ b/include/libuv/src/unix/poll.c @@ -79,9 +79,10 @@ int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd) { * Workaround for e.g. kqueue fds not supporting ioctls. */ err = uv__nonblock(fd, 1); +#if UV__NONBLOCK_IS_IOCTL if (err == UV_ENOTTY) - if (uv__nonblock == uv__nonblock_ioctl) - err = uv__nonblock_fcntl(fd, 1); + err = uv__nonblock_fcntl(fd, 1); +#endif if (err) return err; @@ -116,12 +117,21 @@ int uv_poll_stop(uv_poll_t* handle) { int uv_poll_start(uv_poll_t* handle, int pevents, uv_poll_cb poll_cb) { + uv__io_t** watchers; + uv__io_t* w; int events; assert((pevents & ~(UV_READABLE | UV_WRITABLE | UV_DISCONNECT | UV_PRIORITIZED)) == 0); assert(!uv__is_closing(handle)); + watchers = handle->loop->watchers; + w = &handle->io_watcher; + + if (uv__fd_exists(handle->loop, w->fd)) + if (watchers[w->fd] != w) + return UV_EEXIST; + uv__poll_stop(handle); if (pevents == 0) diff --git a/include/libuv/src/unix/process.c b/include/libuv/src/unix/process.c index b021aaeba..f4aebb049 100644 --- a/include/libuv/src/unix/process.c +++ b/include/libuv/src/unix/process.c @@ -44,6 +44,10 @@ extern char **environ; # include #endif +#if defined(__MVS__) +# include "zos-base.h" +#endif + static void uv__chld(uv_signal_t* handle, int signum) { uv_process_t* process; @@ -111,68 +115,6 @@ static void uv__chld(uv_signal_t* handle, int signum) { assert(QUEUE_EMPTY(&pending)); } - -static int uv__make_socketpair(int fds[2]) { -#if defined(__FreeBSD__) || defined(__linux__) - if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, fds)) - return UV__ERR(errno); - - return 0; -#else - int err; - - if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds)) - return UV__ERR(errno); - - err = uv__cloexec(fds[0], 1); - if (err == 0) - err = uv__cloexec(fds[1], 1); - - if (err != 0) { - uv__close(fds[0]); - uv__close(fds[1]); - return UV__ERR(errno); - } - - return 0; -#endif -} - - -int uv__make_pipe(int fds[2], int flags) { -#if defined(__FreeBSD__) || defined(__linux__) - if (pipe2(fds, flags | O_CLOEXEC)) - return UV__ERR(errno); - - return 0; -#else - if (pipe(fds)) - return UV__ERR(errno); - - if (uv__cloexec(fds[0], 1)) - goto fail; - - if (uv__cloexec(fds[1], 1)) - goto fail; - - if (flags & UV__F_NONBLOCK) { - if (uv__nonblock(fds[0], 1)) - goto fail; - - if (uv__nonblock(fds[1], 1)) - goto fail; - } - - return 0; - -fail: - uv__close(fds[0]); - uv__close(fds[1]); - return UV__ERR(errno); -#endif -} - - /* * Used for initializing stdio streams like options.stdin_stream. Returns * zero on success. See also the cleanup section in uv_spawn(). @@ -192,7 +134,7 @@ static int uv__process_init_stdio(uv_stdio_container_t* container, int fds[2]) { if (container->data.stream->type != UV_NAMED_PIPE) return UV_EINVAL; else - return uv__make_socketpair(fds); + return uv_socketpair(SOCK_STREAM, 0, fds, 0, 0); case UV_INHERIT_FD: case UV_INHERIT_STREAM: @@ -259,6 +201,12 @@ static void uv__write_int(int fd, int val) { } +static void uv__write_errno(int error_fd) { + uv__write_int(error_fd, UV__ERR(errno)); + _exit(127); +} + + #if !(defined(__APPLE__) && (TARGET_OS_TV || TARGET_OS_WATCH)) /* execvp is marked __WATCHOS_PROHIBITED __TVOS_PROHIBITED, so must be * avoided. Since this isn't called on those targets, the function @@ -287,10 +235,8 @@ static void uv__process_child_init(const uv_process_options_t* options, if (use_fd < 0 || use_fd >= fd) continue; pipes[fd][1] = fcntl(use_fd, F_DUPFD, stdio_count); - if (pipes[fd][1] == -1) { - uv__write_int(error_fd, UV__ERR(errno)); - _exit(127); - } + if (pipes[fd][1] == -1) + uv__write_errno(error_fd); } for (fd = 0; fd < stdio_count; fd++) { @@ -307,10 +253,8 @@ static void uv__process_child_init(const uv_process_options_t* options, use_fd = open("/dev/null", fd == 0 ? O_RDONLY : O_RDWR); close_fd = use_fd; - if (use_fd < 0) { - uv__write_int(error_fd, UV__ERR(errno)); - _exit(127); - } + if (use_fd < 0) + uv__write_errno(error_fd); } } @@ -319,10 +263,8 @@ static void uv__process_child_init(const uv_process_options_t* options, else fd = dup2(use_fd, fd); - if (fd == -1) { - uv__write_int(error_fd, UV__ERR(errno)); - _exit(127); - } + if (fd == -1) + uv__write_errno(error_fd); if (fd <= 2) uv__nonblock_fcntl(fd, 0); @@ -338,10 +280,8 @@ static void uv__process_child_init(const uv_process_options_t* options, uv__close(use_fd); } - if (options->cwd != NULL && chdir(options->cwd)) { - uv__write_int(error_fd, UV__ERR(errno)); - _exit(127); - } + if (options->cwd != NULL && chdir(options->cwd)) + uv__write_errno(error_fd); if (options->flags & (UV_PROCESS_SETUID | UV_PROCESS_SETGID)) { /* When dropping privileges from root, the `setgroups` call will @@ -354,15 +294,11 @@ static void uv__process_child_init(const uv_process_options_t* options, SAVE_ERRNO(setgroups(0, NULL)); } - if ((options->flags & UV_PROCESS_SETGID) && setgid(options->gid)) { - uv__write_int(error_fd, UV__ERR(errno)); - _exit(127); - } + if ((options->flags & UV_PROCESS_SETGID) && setgid(options->gid)) + uv__write_errno(error_fd); - if ((options->flags & UV_PROCESS_SETUID) && setuid(options->uid)) { - uv__write_int(error_fd, UV__ERR(errno)); - _exit(127); - } + if ((options->flags & UV_PROCESS_SETUID) && setuid(options->uid)) + uv__write_errno(error_fd); if (options->env != NULL) { environ = options->env; @@ -385,22 +321,23 @@ static void uv__process_child_init(const uv_process_options_t* options, if (SIG_ERR != signal(n, SIG_DFL)) continue; - uv__write_int(error_fd, UV__ERR(errno)); - _exit(127); + uv__write_errno(error_fd); } /* Reset signal mask. */ sigemptyset(&set); err = pthread_sigmask(SIG_SETMASK, &set, NULL); - if (err != 0) { - uv__write_int(error_fd, UV__ERR(err)); - _exit(127); - } + if (err != 0) + uv__write_errno(error_fd); +#ifdef __MVS__ + execvpe(options->file, options->args, environ); +#else execvp(options->file, options->args); - uv__write_int(error_fd, UV__ERR(errno)); - _exit(127); +#endif + + uv__write_errno(error_fd); } #endif diff --git a/include/libuv/src/unix/proctitle.c b/include/libuv/src/unix/proctitle.c index 9ffe5b629..9d1f00ddf 100644 --- a/include/libuv/src/unix/proctitle.c +++ b/include/libuv/src/unix/proctitle.c @@ -84,10 +84,7 @@ char** uv_setup_args(int argc, char** argv) { } new_argv[i] = NULL; - /* argv is not adjacent on z/os, we use just argv[0] on that platform. */ -#ifndef __MVS__ pt.cap = argv[i - 1] + size - argv[0]; -#endif args_mem = new_argv; process_title = pt; @@ -119,6 +116,7 @@ int uv_set_process_title(const char* title) { memcpy(pt->str, title, len); memset(pt->str + len, '\0', pt->cap - len); pt->len = len; + uv__set_process_title(pt->str); uv_mutex_unlock(&process_title_mutex); diff --git a/include/libuv/src/unix/signal.c b/include/libuv/src/unix/signal.c index f40a3e54e..1133c73a9 100644 --- a/include/libuv/src/unix/signal.c +++ b/include/libuv/src/unix/signal.c @@ -265,7 +265,7 @@ static int uv__signal_loop_once_init(uv_loop_t* loop) { if (loop->signal_pipefd[0] != -1) return 0; - err = uv__make_pipe(loop->signal_pipefd, UV__F_NONBLOCK); + err = uv__make_pipe(loop->signal_pipefd, UV_NONBLOCK_PIPE); if (err) return err; diff --git a/include/libuv/src/unix/stream.c b/include/libuv/src/unix/stream.c index 8327f9ccf..bc64fe8f4 100644 --- a/include/libuv/src/unix/stream.c +++ b/include/libuv/src/unix/stream.c @@ -164,7 +164,7 @@ static void uv__stream_osx_select(void* arg) { else max_fd = s->int_fd; - while (1) { + for (;;) { /* Terminate on semaphore */ if (uv_sem_trywait(&s->close_sem) == 0) break; @@ -195,7 +195,7 @@ static void uv__stream_osx_select(void* arg) { /* Empty socketpair's buffer in case of interruption */ if (FD_ISSET(s->int_fd, s->sread)) - while (1) { + for (;;) { r = read(s->int_fd, buf, sizeof(buf)); if (r == sizeof(buf)) @@ -799,33 +799,21 @@ static int uv__handle_fd(uv_handle_t* handle) { } } -static void uv__write(uv_stream_t* stream) { +static int uv__try_write(uv_stream_t* stream, + const uv_buf_t bufs[], + unsigned int nbufs, + uv_stream_t* send_handle) { struct iovec* iov; - QUEUE* q; - uv_write_t* req; int iovmax; int iovcnt; ssize_t n; - int err; - -start: - - assert(uv__stream_fd(stream) >= 0); - - if (QUEUE_EMPTY(&stream->write_queue)) - return; - - q = QUEUE_HEAD(&stream->write_queue); - req = QUEUE_DATA(q, uv_write_t, queue); - assert(req->handle == stream); /* * Cast to iovec. We had to have our own uv_buf_t instead of iovec * because Windows's WSABUF is not an iovec. */ - assert(sizeof(uv_buf_t) == sizeof(struct iovec)); - iov = (struct iovec*) &(req->bufs[req->write_index]); - iovcnt = req->nbufs - req->write_index; + iov = (struct iovec*) bufs; + iovcnt = nbufs; iovmax = uv__getiovmax(); @@ -837,8 +825,7 @@ static void uv__write(uv_stream_t* stream) { * Now do the actual writev. Note that we've been updating the pointers * inside the iov each time we write. So there is no need to offset it. */ - - if (req->send_handle) { + if (send_handle != NULL) { int fd_to_send; struct msghdr msg; struct cmsghdr *cmsg; @@ -847,12 +834,10 @@ static void uv__write(uv_stream_t* stream) { struct cmsghdr alias; } scratch; - if (uv__is_closing(req->send_handle)) { - err = UV_EBADF; - goto error; - } + if (uv__is_closing(send_handle)) + return UV_EBADF; - fd_to_send = uv__handle_fd((uv_handle_t*) req->send_handle); + fd_to_send = uv__handle_fd((uv_handle_t*) send_handle); memset(&scratch, 0, sizeof(scratch)); @@ -882,44 +867,68 @@ static void uv__write(uv_stream_t* stream) { do n = sendmsg(uv__stream_fd(stream), &msg, 0); while (n == -1 && RETRY_ON_WRITE_ERROR(errno)); - - /* Ensure the handle isn't sent again in case this is a partial write. */ - if (n >= 0) - req->send_handle = NULL; } else { do n = uv__writev(uv__stream_fd(stream), iov, iovcnt); while (n == -1 && RETRY_ON_WRITE_ERROR(errno)); } - if (n == -1 && !IS_TRANSIENT_WRITE_ERROR(errno, req->send_handle)) { - err = UV__ERR(errno); - goto error; - } + if (n >= 0) + return n; - if (n >= 0 && uv__write_req_update(stream, req, n)) { - uv__write_req_finish(req); - return; /* TODO(bnoordhuis) Start trying to write the next request. */ - } + if (IS_TRANSIENT_WRITE_ERROR(errno, send_handle)) + return UV_EAGAIN; - /* If this is a blocking stream, try again. */ - if (stream->flags & UV_HANDLE_BLOCKING_WRITES) - goto start; + return UV__ERR(errno); +} - /* We're not done. */ - uv__io_start(stream->loop, &stream->io_watcher, POLLOUT); +static void uv__write(uv_stream_t* stream) { + QUEUE* q; + uv_write_t* req; + ssize_t n; - /* Notify select() thread about state change */ - uv__stream_osx_interrupt_select(stream); + assert(uv__stream_fd(stream) >= 0); + + for (;;) { + if (QUEUE_EMPTY(&stream->write_queue)) + return; + + q = QUEUE_HEAD(&stream->write_queue); + req = QUEUE_DATA(q, uv_write_t, queue); + assert(req->handle == stream); + + n = uv__try_write(stream, + &(req->bufs[req->write_index]), + req->nbufs - req->write_index, + req->send_handle); + + /* Ensure the handle isn't sent again in case this is a partial write. */ + if (n >= 0) { + req->send_handle = NULL; + if (uv__write_req_update(stream, req, n)) { + uv__write_req_finish(req); + return; /* TODO(bnoordhuis) Start trying to write the next request. */ + } + } else if (n != UV_EAGAIN) + break; + + /* If this is a blocking stream, try again. */ + if (stream->flags & UV_HANDLE_BLOCKING_WRITES) + continue; + + /* We're not done. */ + uv__io_start(stream->loop, &stream->io_watcher, POLLOUT); - return; + /* Notify select() thread about state change */ + uv__stream_osx_interrupt_select(stream); + + return; + } -error: - req->error = err; + req->error = n; + // XXX(jwn): this must call uv__stream_flush_write_queue(stream, n) here, since we won't generate any more events uv__write_req_finish(req); uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT); - if (!uv__io_active(&stream->io_watcher, POLLIN)) - uv__handle_stop(stream); uv__stream_osx_interrupt_select(stream); } @@ -1001,9 +1010,9 @@ uv_handle_type uv__handle_type(int fd) { static void uv__stream_eof(uv_stream_t* stream, const uv_buf_t* buf) { stream->flags |= UV_HANDLE_READ_EOF; stream->flags &= ~UV_HANDLE_READING; + stream->flags &= ~UV_HANDLE_READABLE; uv__io_stop(stream->loop, &stream->io_watcher, POLLIN); - if (!uv__io_active(&stream->io_watcher, POLLOUT)) - uv__handle_stop(stream); + uv__handle_stop(stream); uv__stream_osx_interrupt_select(stream); stream->read_cb(stream, UV_EOF, buf); } @@ -1188,12 +1197,12 @@ static void uv__read(uv_stream_t* stream) { #endif } else { /* Error. User should call uv_close(). */ + stream->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); stream->read_cb(stream, UV__ERR(errno), &buf); if (stream->flags & UV_HANDLE_READING) { stream->flags &= ~UV_HANDLE_READING; uv__io_stop(stream->loop, &stream->io_watcher, POLLIN); - if (!uv__io_active(&stream->io_watcher, POLLOUT)) - uv__handle_stop(stream); + uv__handle_stop(stream); uv__stream_osx_interrupt_select(stream); } } @@ -1276,6 +1285,7 @@ int uv_shutdown(uv_shutdown_t* req, uv_stream_t* stream, uv_shutdown_cb cb) { req->cb = cb; stream->shutdown_req = req; stream->flags |= UV_HANDLE_SHUTTING; + stream->flags &= ~UV_HANDLE_WRITABLE; uv__io_start(stream->loop, &stream->io_watcher, POLLOUT); uv__stream_osx_interrupt_select(stream); @@ -1390,14 +1400,9 @@ static void uv__stream_connect(uv_stream_t* stream) { } -int uv_write2(uv_write_t* req, - uv_stream_t* stream, - const uv_buf_t bufs[], - unsigned int nbufs, - uv_stream_t* send_handle, - uv_write_cb cb) { - int empty_queue; - +static int uv__check_before_write(uv_stream_t* stream, + unsigned int nbufs, + uv_stream_t* send_handle) { assert(nbufs > 0); assert((stream->type == UV_TCP || stream->type == UV_NAMED_PIPE || @@ -1410,7 +1415,7 @@ int uv_write2(uv_write_t* req, if (!(stream->flags & UV_HANDLE_WRITABLE)) return UV_EPIPE; - if (send_handle) { + if (send_handle != NULL) { if (stream->type != UV_NAMED_PIPE || !((uv_pipe_t*)stream)->ipc) return UV_EINVAL; @@ -1430,6 +1435,22 @@ int uv_write2(uv_write_t* req, #endif } + return 0; +} + +int uv_write2(uv_write_t* req, + uv_stream_t* stream, + const uv_buf_t bufs[], + unsigned int nbufs, + uv_stream_t* send_handle, + uv_write_cb cb) { + int empty_queue; + int err; + + err = uv__check_before_write(stream, nbufs, send_handle); + if (err < 0) + return err; + /* It's legal for write_queue_size > 0 even when the write_queue is empty; * it means there are error-state requests in the write_completed_queue that * will touch up write_queue_size later, see also uv__write_req_finish(). @@ -1498,72 +1519,37 @@ int uv_write(uv_write_t* req, } -void uv_try_write_cb(uv_write_t* req, int status) { - /* Should not be called */ - abort(); -} - - int uv_try_write(uv_stream_t* stream, const uv_buf_t bufs[], unsigned int nbufs) { - int r; - int has_pollout; - size_t written; - size_t req_size; - uv_write_t req; + return uv_try_write2(stream, bufs, nbufs, NULL); +} + + +int uv_try_write2(uv_stream_t* stream, + const uv_buf_t bufs[], + unsigned int nbufs, + uv_stream_t* send_handle) { + int err; /* Connecting or already writing some data */ if (stream->connect_req != NULL || stream->write_queue_size != 0) return UV_EAGAIN; - has_pollout = uv__io_active(&stream->io_watcher, POLLOUT); + err = uv__check_before_write(stream, nbufs, NULL); + if (err < 0) + return err; - r = uv_write(&req, stream, bufs, nbufs, uv_try_write_cb); - if (r != 0) - return r; - - /* Remove not written bytes from write queue size */ - written = uv__count_bufs(bufs, nbufs); - if (req.bufs != NULL) - req_size = uv__write_req_size(&req); - else - req_size = 0; - written -= req_size; - stream->write_queue_size -= req_size; - - /* Unqueue request, regardless of immediateness */ - QUEUE_REMOVE(&req.queue); - uv__req_unregister(stream->loop, &req); - if (req.bufs != req.bufsml) - uv__free(req.bufs); - req.bufs = NULL; - - /* Do not poll for writable, if we wasn't before calling this */ - if (!has_pollout) { - uv__io_stop(stream->loop, &stream->io_watcher, POLLOUT); - uv__stream_osx_interrupt_select(stream); - } - - if (written == 0 && req_size != 0) - return req.error < 0 ? req.error : UV_EAGAIN; - else - return written; + return uv__try_write(stream, bufs, nbufs, send_handle); } -int uv_read_start(uv_stream_t* stream, - uv_alloc_cb alloc_cb, - uv_read_cb read_cb) { +int uv__read_start(uv_stream_t* stream, + uv_alloc_cb alloc_cb, + uv_read_cb read_cb) { assert(stream->type == UV_TCP || stream->type == UV_NAMED_PIPE || stream->type == UV_TTY); - if (stream->flags & UV_HANDLE_CLOSING) - return UV_EINVAL; - - if (!(stream->flags & UV_HANDLE_READABLE)) - return UV_ENOTCONN; - /* The UV_HANDLE_READING flag is irrelevant of the state of the tcp - it just * expresses the desired state of the user. */ @@ -1593,8 +1579,7 @@ int uv_read_stop(uv_stream_t* stream) { stream->flags &= ~UV_HANDLE_READING; uv__io_stop(stream->loop, &stream->io_watcher, POLLIN); - if (!uv__io_active(&stream->io_watcher, POLLOUT)) - uv__handle_stop(stream); + uv__handle_stop(stream); uv__stream_osx_interrupt_select(stream); stream->read_cb = NULL; diff --git a/include/libuv/src/unix/sunos.c b/include/libuv/src/unix/sunos.c index d511c18b8..2bf297e5d 100644 --- a/include/libuv/src/unix/sunos.c +++ b/include/libuv/src/unix/sunos.c @@ -865,3 +865,14 @@ void uv_free_interface_addresses(uv_interface_address_t* addresses, uv__free(addresses); } + + +#if !defined(_POSIX_VERSION) || _POSIX_VERSION < 200809L +size_t strnlen(const char* s, size_t maxlen) { + const char* end; + end = memchr(s, '\0', maxlen); + if (end == NULL) + return maxlen; + return end - s; +} +#endif diff --git a/include/libuv/src/unix/tcp.c b/include/libuv/src/unix/tcp.c index 18acd20df..bc0fb661f 100644 --- a/include/libuv/src/unix/tcp.c +++ b/include/libuv/src/unix/tcp.c @@ -214,14 +214,15 @@ int uv__tcp_connect(uv_connect_t* req, if (handle->connect_req != NULL) return UV_EALREADY; /* FIXME(bnoordhuis) UV_EINVAL or maybe UV_EBUSY. */ + if (handle->delayed_error != 0) + goto out; + err = maybe_new_socket(handle, addr->sa_family, UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); if (err) return err; - handle->delayed_error = 0; - do { errno = 0; r = connect(uv__stream_fd(handle), addr, addrlen); @@ -249,6 +250,8 @@ int uv__tcp_connect(uv_connect_t* req, return UV__ERR(errno); } +out: + uv__req_init(handle->loop, req, UV_CONNECT); req->cb = cb; req->handle = (uv_stream_t*) handle; @@ -459,3 +462,49 @@ int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) { void uv__tcp_close(uv_tcp_t* handle) { uv__stream_close((uv_stream_t*)handle); } + + +int uv_socketpair(int type, int protocol, uv_os_sock_t fds[2], int flags0, int flags1) { + uv_os_sock_t temp[2]; + int err; +#if defined(__FreeBSD__) || defined(__linux__) + int flags; + + flags = type | SOCK_CLOEXEC; + if ((flags0 & UV_NONBLOCK_PIPE) && (flags1 & UV_NONBLOCK_PIPE)) + flags |= SOCK_NONBLOCK; + + if (socketpair(AF_UNIX, flags, protocol, temp)) + return UV__ERR(errno); + + if (flags & UV_FS_O_NONBLOCK) { + fds[0] = temp[0]; + fds[1] = temp[1]; + return 0; + } +#else + if (socketpair(AF_UNIX, type, protocol, temp)) + return UV__ERR(errno); + + if ((err = uv__cloexec(temp[0], 1))) + goto fail; + if ((err = uv__cloexec(temp[1], 1))) + goto fail; +#endif + + if (flags0 & UV_NONBLOCK_PIPE) + if ((err = uv__nonblock(temp[0], 1))) + goto fail; + if (flags1 & UV_NONBLOCK_PIPE) + if ((err = uv__nonblock(temp[1], 1))) + goto fail; + + fds[0] = temp[0]; + fds[1] = temp[1]; + return 0; + +fail: + uv__close(temp[0]); + uv__close(temp[1]); + return err; +} diff --git a/include/libuv/src/unix/thread.c b/include/libuv/src/unix/thread.c index 1a85d1d4f..c46450cc6 100644 --- a/include/libuv/src/unix/thread.c +++ b/include/libuv/src/unix/thread.c @@ -107,8 +107,7 @@ int uv_barrier_wait(uv_barrier_t* barrier) { } last = (--b->out == 0); - if (!last) - uv_cond_signal(&b->cond); /* Not needed for last thread. */ + uv_cond_signal(&b->cond); uv_mutex_unlock(&b->mutex); return last; @@ -122,9 +121,10 @@ void uv_barrier_destroy(uv_barrier_t* barrier) { uv_mutex_lock(&b->mutex); assert(b->in == 0); - assert(b->out == 0); + while (b->out != 0) + uv_cond_wait(&b->cond, &b->mutex); - if (b->in != 0 || b->out != 0) + if (b->in != 0) abort(); uv_mutex_unlock(&b->mutex); @@ -168,7 +168,7 @@ void uv_barrier_destroy(uv_barrier_t* barrier) { * On Linux, threads created by musl have a much smaller stack than threads * created by glibc (80 vs. 2048 or 4096 kB.) Follow glibc for consistency. */ -static size_t thread_stack_size(void) { +size_t uv__thread_stack_size(void) { #if defined(__APPLE__) || defined(__linux__) struct rlimit lim; @@ -234,7 +234,7 @@ int uv_thread_create_ex(uv_thread_t* tid, attr = NULL; if (stack_size == 0) { - stack_size = thread_stack_size(); + stack_size = uv__thread_stack_size(); } else { pagesize = (size_t)getpagesize(); /* Round up to the nearest page boundary. */ diff --git a/include/libuv/src/unix/tty.c b/include/libuv/src/unix/tty.c index 6f60abaad..9442cf163 100644 --- a/include/libuv/src/unix/tty.c +++ b/include/libuv/src/unix/tty.c @@ -242,6 +242,24 @@ static void uv__tty_make_raw(struct termios* tio) { tio->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); tio->c_cflag &= ~(CSIZE | PARENB); tio->c_cflag |= CS8; + + /* + * By default, most software expects a pending read to block until at + * least one byte becomes available. As per termio(7I), this requires + * setting the MIN and TIME parameters appropriately. + * + * As a somewhat unfortunate artifact of history, the MIN and TIME slots + * in the control character array overlap with the EOF and EOL slots used + * for canonical mode processing. Because the EOF character needs to be + * the ASCII EOT value (aka Control-D), it has the byte value 4. When + * switching to raw mode, this is interpreted as a MIN value of 4; i.e., + * reads will block until at least four bytes have been input. + * + * Other platforms with a distinct MIN slot like Linux and FreeBSD appear + * to default to a MIN value of 1, so we'll force that value here: + */ + tio->c_cc[VMIN] = 1; + tio->c_cc[VTIME] = 0; #else cfmakeraw(tio); #endif /* #ifdef __sun */ diff --git a/include/libuv/src/unix/udp.c b/include/libuv/src/unix/udp.c index 7d699a167..49051c07c 100644 --- a/include/libuv/src/unix/udp.c +++ b/include/libuv/src/unix/udp.c @@ -32,8 +32,6 @@ #endif #include -#define UV__UDP_DGRAM_MAXSIZE (64 * 1024) - #if defined(IPV6_JOIN_GROUP) && !defined(IPV6_ADD_MEMBERSHIP) # define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP #endif @@ -504,6 +502,28 @@ static int uv__set_reuse(int fd) { return 0; } +/* + * The Linux kernel suppresses some ICMP error messages by default for UDP + * sockets. Setting IP_RECVERR/IPV6_RECVERR on the socket enables full ICMP + * error reporting, hopefully resulting in faster failover to working name + * servers. + */ +static int uv__set_recverr(int fd, sa_family_t ss_family) { +#if defined(__linux__) + int yes; + + yes = 1; + if (ss_family == AF_INET) { + if (setsockopt(fd, IPPROTO_IP, IP_RECVERR, &yes, sizeof(yes))) + return UV__ERR(errno); + } else if (ss_family == AF_INET6) { + if (setsockopt(fd, IPPROTO_IPV6, IPV6_RECVERR, &yes, sizeof(yes))) + return UV__ERR(errno); + } +#endif + return 0; +} + int uv__udp_bind(uv_udp_t* handle, const struct sockaddr* addr, @@ -514,7 +534,7 @@ int uv__udp_bind(uv_udp_t* handle, int fd; /* Check for bad flags. */ - if (flags & ~(UV_UDP_IPV6ONLY | UV_UDP_REUSEADDR)) + if (flags & ~(UV_UDP_IPV6ONLY | UV_UDP_REUSEADDR | UV_UDP_LINUX_RECVERR)) return UV_EINVAL; /* Cannot set IPv6-only mode on non-IPv6 socket. */ @@ -530,6 +550,12 @@ int uv__udp_bind(uv_udp_t* handle, handle->io_watcher.fd = fd; } + if (flags & UV_UDP_LINUX_RECVERR) { + err = uv__set_recverr(fd, addr->sa_family); + if (err) + return err; + } + if (flags & UV_UDP_REUSEADDR) { err = uv__set_reuse(fd); if (err) diff --git a/include/libuv/src/uv-common.c b/include/libuv/src/uv-common.c index 602e5f492..e81ed79b0 100644 --- a/include/libuv/src/uv-common.c +++ b/include/libuv/src/uv-common.c @@ -832,6 +832,25 @@ void uv_loop_delete(uv_loop_t* loop) { } +int uv_read_start(uv_stream_t* stream, + uv_alloc_cb alloc_cb, + uv_read_cb read_cb) { + if (stream == NULL || alloc_cb == NULL || read_cb == NULL) + return UV_EINVAL; + + if (stream->flags & UV_HANDLE_CLOSING) + return UV_EINVAL; + + if (stream->flags & UV_HANDLE_READING) + return UV_EALREADY; + + if (!(stream->flags & UV_HANDLE_READABLE)) + return UV_ENOTCONN; + + return uv__read_start(stream, alloc_cb, read_cb); +} + + void uv_os_free_environ(uv_env_item_t* envitems, int count) { int i; @@ -853,7 +872,11 @@ void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { } -#ifdef __GNUC__ /* Also covers __clang__ and __INTEL_COMPILER. */ +/* Also covers __clang__ and __INTEL_COMPILER. Disabled on Windows because + * threads have already been forcibly terminated by the operating system + * by the time destructors run, ergo, it's not safe to try to clean them up. + */ +#if defined(__GNUC__) && !defined(_WIN32) __attribute__((destructor)) #endif void uv_library_shutdown(void) { diff --git a/include/libuv/src/uv-common.h b/include/libuv/src/uv-common.h index e851291cc..8a190bf8f 100644 --- a/include/libuv/src/uv-common.h +++ b/include/libuv/src/uv-common.h @@ -68,6 +68,8 @@ extern int snprintf(char*, size_t, const char*, ...); #define uv__store_relaxed(p, v) do *p = v; while (0) #endif +#define UV__UDP_DGRAM_MAXSIZE (64 * 1024) + /* Handle flags. Some flags are specific to Windows or UNIX. */ enum { /* Used by all handles. */ @@ -106,8 +108,7 @@ enum { UV_HANDLE_TCP_KEEPALIVE = 0x02000000, UV_HANDLE_TCP_SINGLE_ACCEPT = 0x04000000, UV_HANDLE_TCP_ACCEPT_STATE_CHANGING = 0x08000000, - UV_HANDLE_TCP_SOCKET_CLOSED = 0x10000000, - UV_HANDLE_SHARED_TCP_SOCKET = 0x20000000, + UV_HANDLE_SHARED_TCP_SOCKET = 0x10000000, /* Only used by uv_udp_t handles. */ UV_HANDLE_UDP_PROCESSING = 0x01000000, @@ -136,6 +137,10 @@ int uv__loop_configure(uv_loop_t* loop, uv_loop_option option, va_list ap); void uv__loop_close(uv_loop_t* loop); +int uv__read_start(uv_stream_t* stream, + uv_alloc_cb alloc_cb, + uv_read_cb read_cb); + int uv__tcp_bind(uv_tcp_t* tcp, const struct sockaddr* addr, unsigned int addrlen, diff --git a/include/libuv/src/win/atomicops-inl.h b/include/libuv/src/win/atomicops-inl.h index 52713cf30..2f984c6db 100644 --- a/include/libuv/src/win/atomicops-inl.h +++ b/include/libuv/src/win/atomicops-inl.h @@ -39,10 +39,11 @@ static char INLINE uv__atomic_exchange_set(char volatile* target) { return _InterlockedOr8(target, 1); } -#else /* GCC */ +#else /* GCC, Clang in mingw mode */ -/* Mingw-32 version, hopefully this works for 64-bit gcc as well. */ static inline char uv__atomic_exchange_set(char volatile* target) { +#if defined(__i386__) || defined(__x86_64__) + /* Mingw-32 version, hopefully this works for 64-bit gcc as well. */ const char one = 1; char old_value; __asm__ __volatile__ ("lock xchgb %0, %1\n\t" @@ -50,6 +51,9 @@ static inline char uv__atomic_exchange_set(char volatile* target) { : "0"(one), "m"(*target) : "memory"); return old_value; +#else + return __sync_fetch_and_or(target, 1); +#endif } #endif diff --git a/include/libuv/src/win/error.c b/include/libuv/src/win/error.c index 3ec984c83..3a269da87 100644 --- a/include/libuv/src/win/error.c +++ b/include/libuv/src/win/error.c @@ -105,7 +105,6 @@ int uv_translate_sys_error(int sys_errno) { case ERROR_SYMLINK_NOT_SUPPORTED: return UV_EINVAL; case WSAEINVAL: return UV_EINVAL; case WSAEPFNOSUPPORT: return UV_EINVAL; - case WSAESOCKTNOSUPPORT: return UV_EINVAL; case ERROR_BEGINNING_OF_MEDIA: return UV_EIO; case ERROR_BUS_RESET: return UV_EIO; case ERROR_CRC: return UV_EIO; @@ -168,6 +167,7 @@ int uv_translate_sys_error(int sys_errno) { case ERROR_NOT_SAME_DEVICE: return UV_EXDEV; case ERROR_INVALID_FUNCTION: return UV_EISDIR; case ERROR_META_EXPANSION_TOO_LONG: return UV_E2BIG; + case WSAESOCKTNOSUPPORT: return UV_ESOCKTNOSUPPORT; default: return UV_UNKNOWN; } } diff --git a/include/libuv/src/win/fs.c b/include/libuv/src/win/fs.c index 8a801749d..674070400 100644 --- a/include/libuv/src/win/fs.c +++ b/include/libuv/src/win/fs.c @@ -92,30 +92,24 @@ return; \ } -#define MILLIONu (1000U * 1000U) -#define BILLIONu (1000U * 1000U * 1000U) +#define MILLION ((int64_t) 1000 * 1000) +#define BILLION ((int64_t) 1000 * 1000 * 1000) -#define FILETIME_TO_UINT(filetime) \ - (*((uint64_t*) &(filetime)) - (uint64_t) 116444736 * BILLIONu) - -#define FILETIME_TO_TIME_T(filetime) \ - (FILETIME_TO_UINT(filetime) / (10u * MILLIONu)) - -#define FILETIME_TO_TIME_NS(filetime, secs) \ - ((FILETIME_TO_UINT(filetime) - (secs * (uint64_t) 10 * MILLIONu)) * 100U) - -#define FILETIME_TO_TIMESPEC(ts, filetime) \ - do { \ - (ts).tv_sec = (long) FILETIME_TO_TIME_T(filetime); \ - (ts).tv_nsec = (long) FILETIME_TO_TIME_NS(filetime, (ts).tv_sec); \ - } while(0) +static void uv__filetime_to_timespec(uv_timespec_t *ts, int64_t filetime) { + filetime -= 116444736 * BILLION; + ts->tv_sec = (long) (filetime / (10 * MILLION)); + ts->tv_nsec = (long) ((filetime - ts->tv_sec * 10 * MILLION) * 100U); + if (ts->tv_nsec < 0) { + ts->tv_sec -= 1; + ts->tv_nsec += 1e9; + } +} #define TIME_T_TO_FILETIME(time, filetime_ptr) \ do { \ - uint64_t bigtime = ((uint64_t) ((time) * (uint64_t) 10 * MILLIONu)) + \ - (uint64_t) 116444736 * BILLIONu; \ - (filetime_ptr)->dwLowDateTime = bigtime & 0xFFFFFFFF; \ - (filetime_ptr)->dwHighDateTime = bigtime >> 32; \ + int64_t bigtime = ((time) * 10 * MILLION + 116444736 * BILLION); \ + (filetime_ptr)->dwLowDateTime = (uint64_t) bigtime & 0xFFFFFFFF; \ + (filetime_ptr)->dwHighDateTime = (uint64_t) bigtime >> 32; \ } while(0) #define IS_SLASH(c) ((c) == L'\\' || (c) == L'/') @@ -1224,7 +1218,8 @@ void fs__mkdir(uv_fs_t* req) { SET_REQ_RESULT(req, 0); } else { SET_REQ_WIN32_ERROR(req, GetLastError()); - if (req->sys_errno_ == ERROR_INVALID_NAME) + if (req->sys_errno_ == ERROR_INVALID_NAME || + req->sys_errno_ == ERROR_DIRECTORY) req->result = UV_EINVAL; } } @@ -1243,7 +1238,7 @@ void fs__mktemp(uv_fs_t* req, uv__fs_mktemp_func func) { uint64_t v; char* path; - path = req->path; + path = (char*)req->path; len = wcslen(req->file.pathw); ep = req->file.pathw + len; if (len < num_x || wcsncmp(ep - num_x, L"XXXXXX", num_x)) { @@ -1791,10 +1786,14 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf, statbuf->st_mode |= (_S_IREAD | _S_IWRITE) | ((_S_IREAD | _S_IWRITE) >> 3) | ((_S_IREAD | _S_IWRITE) >> 6); - FILETIME_TO_TIMESPEC(statbuf->st_atim, file_info.BasicInformation.LastAccessTime); - FILETIME_TO_TIMESPEC(statbuf->st_ctim, file_info.BasicInformation.ChangeTime); - FILETIME_TO_TIMESPEC(statbuf->st_mtim, file_info.BasicInformation.LastWriteTime); - FILETIME_TO_TIMESPEC(statbuf->st_birthtim, file_info.BasicInformation.CreationTime); + uv__filetime_to_timespec(&statbuf->st_atim, + file_info.BasicInformation.LastAccessTime.QuadPart); + uv__filetime_to_timespec(&statbuf->st_ctim, + file_info.BasicInformation.ChangeTime.QuadPart); + uv__filetime_to_timespec(&statbuf->st_mtim, + file_info.BasicInformation.LastWriteTime.QuadPart); + uv__filetime_to_timespec(&statbuf->st_birthtim, + file_info.BasicInformation.CreationTime.QuadPart); statbuf->st_ino = file_info.InternalInformation.IndexNumber.QuadPart; diff --git a/include/libuv/src/win/internal.h b/include/libuv/src/win/internal.h index b096255e4..b1b25b4c7 100644 --- a/include/libuv/src/win/internal.h +++ b/include/libuv/src/win/internal.h @@ -115,8 +115,8 @@ void uv_udp_endgame(uv_loop_t* loop, uv_udp_t* handle); /* * Pipes */ -int uv_stdio_pipe_server(uv_loop_t* loop, uv_pipe_t* handle, DWORD access, - char* name, size_t nameSize); +int uv__create_stdio_pipe_pair(uv_loop_t* loop, + uv_pipe_t* parent_pipe, HANDLE* child_pipe_ptr, unsigned int flags); int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb); int uv_pipe_accept(uv_pipe_t* server, uv_stream_t* client); diff --git a/include/libuv/src/win/pipe.c b/include/libuv/src/win/pipe.c index f81245ec6..88ba99bbc 100644 --- a/include/libuv/src/win/pipe.c +++ b/include/libuv/src/win/pipe.c @@ -202,17 +202,17 @@ static void close_pipe(uv_pipe_t* pipe) { } -int uv_stdio_pipe_server(uv_loop_t* loop, uv_pipe_t* handle, DWORD access, - char* name, size_t nameSize) { +static int uv__pipe_server( + HANDLE* pipeHandle_ptr, DWORD access, + char* name, size_t nameSize, char* random) { HANDLE pipeHandle; int err; - char* ptr = (char*)handle; for (;;) { - uv_unique_pipe_name(ptr, name, nameSize); + uv_unique_pipe_name(random, name, nameSize); pipeHandle = CreateNamedPipeA(name, - access | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE | WRITE_DAC, + access | FILE_FLAG_FIRST_PIPE_INSTANCE, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 65536, 65536, 0, NULL); @@ -226,26 +226,225 @@ int uv_stdio_pipe_server(uv_loop_t* loop, uv_pipe_t* handle, DWORD access, goto error; } - /* Pipe name collision. Increment the pointer and try again. */ - ptr++; + /* Pipe name collision. Increment the random number and try again. */ + random++; } - if (CreateIoCompletionPort(pipeHandle, + *pipeHandle_ptr = pipeHandle; + + return 0; + + error: + if (pipeHandle != INVALID_HANDLE_VALUE) + CloseHandle(pipeHandle); + + return err; +} + + +static int uv__create_pipe_pair( + HANDLE* server_pipe_ptr, HANDLE* client_pipe_ptr, + unsigned int server_flags, unsigned int client_flags, + int inherit_client, char* random) { + /* allowed flags are: UV_READABLE_PIPE | UV_WRITABLE_PIPE | UV_NONBLOCK_PIPE */ + char pipe_name[64]; + SECURITY_ATTRIBUTES sa; + DWORD server_access; + DWORD client_access; + HANDLE server_pipe; + HANDLE client_pipe; + int err; + + server_pipe = INVALID_HANDLE_VALUE; + client_pipe = INVALID_HANDLE_VALUE; + + server_access = 0; + if (server_flags & UV_READABLE_PIPE) + server_access |= PIPE_ACCESS_INBOUND; + if (server_flags & UV_WRITABLE_PIPE) + server_access |= PIPE_ACCESS_OUTBOUND; + if (server_flags & UV_NONBLOCK_PIPE) + server_access |= FILE_FLAG_OVERLAPPED; + server_access |= WRITE_DAC; + + client_access = 0; + if (client_flags & UV_READABLE_PIPE) + client_access |= GENERIC_READ; + else + client_access |= FILE_READ_ATTRIBUTES; + if (client_flags & UV_WRITABLE_PIPE) + client_access |= GENERIC_WRITE; + else + client_access |= FILE_WRITE_ATTRIBUTES; + client_access |= WRITE_DAC; + + /* Create server pipe handle. */ + err = uv__pipe_server(&server_pipe, + server_access, + pipe_name, + sizeof(pipe_name), + random); + if (err) + goto error; + + /* Create client pipe handle. */ + sa.nLength = sizeof sa; + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = inherit_client; + + client_pipe = CreateFileA(pipe_name, + client_access, + 0, + &sa, + OPEN_EXISTING, + (client_flags & UV_NONBLOCK_PIPE) ? FILE_FLAG_OVERLAPPED : 0, + NULL); + if (client_pipe == INVALID_HANDLE_VALUE) { + err = GetLastError(); + goto error; + } + +#ifndef NDEBUG + /* Validate that the pipe was opened in the right mode. */ + { + DWORD mode; + BOOL r; + r = GetNamedPipeHandleState(client_pipe, &mode, NULL, NULL, NULL, NULL, 0); + if (r == TRUE) { + assert(mode == (PIPE_READMODE_BYTE | PIPE_WAIT)); + } else { + fprintf(stderr, "libuv assertion failure: GetNamedPipeHandleState failed\n"); + } + } +#endif + + /* Do a blocking ConnectNamedPipe. This should not block because we have + * both ends of the pipe created. */ + if (!ConnectNamedPipe(server_pipe, NULL)) { + if (GetLastError() != ERROR_PIPE_CONNECTED) { + err = GetLastError(); + goto error; + } + } + + *client_pipe_ptr = client_pipe; + *server_pipe_ptr = server_pipe; + return 0; + + error: + if (server_pipe != INVALID_HANDLE_VALUE) + CloseHandle(server_pipe); + + if (client_pipe != INVALID_HANDLE_VALUE) + CloseHandle(client_pipe); + + return err; +} + + +int uv_pipe(uv_file fds[2], int read_flags, int write_flags) { + uv_file temp[2]; + int err; + HANDLE readh; + HANDLE writeh; + + /* Make the server side the inbound (read) end, */ + /* so that both ends will have FILE_READ_ATTRIBUTES permission. */ + /* TODO: better source of local randomness than &fds? */ + read_flags |= UV_READABLE_PIPE; + write_flags |= UV_WRITABLE_PIPE; + err = uv__create_pipe_pair(&readh, &writeh, read_flags, write_flags, 0, (char*) &fds[0]); + if (err != 0) + return err; + temp[0] = _open_osfhandle((intptr_t) readh, 0); + if (temp[0] == -1) { + if (errno == UV_EMFILE) + err = UV_EMFILE; + else + err = UV_UNKNOWN; + CloseHandle(readh); + CloseHandle(writeh); + return err; + } + temp[1] = _open_osfhandle((intptr_t) writeh, 0); + if (temp[1] == -1) { + if (errno == UV_EMFILE) + err = UV_EMFILE; + else + err = UV_UNKNOWN; + _close(temp[0]); + CloseHandle(writeh); + return err; + } + fds[0] = temp[0]; + fds[1] = temp[1]; + return 0; +} + + +int uv__create_stdio_pipe_pair(uv_loop_t* loop, + uv_pipe_t* parent_pipe, HANDLE* child_pipe_ptr, unsigned int flags) { + /* The parent_pipe is always the server_pipe and kept by libuv. + * The child_pipe is always the client_pipe and is passed to the child. + * The flags are specified with respect to their usage in the child. */ + HANDLE server_pipe; + HANDLE client_pipe; + unsigned int server_flags; + unsigned int client_flags; + int err; + + server_pipe = INVALID_HANDLE_VALUE; + client_pipe = INVALID_HANDLE_VALUE; + + server_flags = 0; + client_flags = 0; + if (flags & UV_READABLE_PIPE) { + /* The server needs inbound (read) access too, otherwise CreateNamedPipe() + * won't give us the FILE_READ_ATTRIBUTES permission. We need that to probe + * the state of the write buffer when we're trying to shutdown the pipe. */ + server_flags |= UV_READABLE_PIPE | UV_WRITABLE_PIPE; + client_flags |= UV_READABLE_PIPE; + } + if (flags & UV_WRITABLE_PIPE) { + server_flags |= UV_READABLE_PIPE; + client_flags |= UV_WRITABLE_PIPE; + } + server_flags |= UV_NONBLOCK_PIPE; + if (flags & UV_NONBLOCK_PIPE || parent_pipe->ipc) { + client_flags |= UV_NONBLOCK_PIPE; + } + + err = uv__create_pipe_pair(&server_pipe, &client_pipe, + server_flags, client_flags, 1, (char*) server_pipe); + if (err) + goto error; + + if (CreateIoCompletionPort(server_pipe, loop->iocp, - (ULONG_PTR)handle, + (ULONG_PTR) parent_pipe, 0) == NULL) { err = GetLastError(); goto error; } - uv_pipe_connection_init(handle); - handle->handle = pipeHandle; + uv_pipe_connection_init(parent_pipe); + parent_pipe->handle = server_pipe; + *child_pipe_ptr = client_pipe; + + /* The server end is now readable and/or writable. */ + if (flags & UV_READABLE_PIPE) + parent_pipe->flags |= UV_HANDLE_WRITABLE; + if (flags & UV_WRITABLE_PIPE) + parent_pipe->flags |= UV_HANDLE_READABLE; return 0; error: - if (pipeHandle != INVALID_HANDLE_VALUE) - CloseHandle(pipeHandle); + if (server_pipe != INVALID_HANDLE_VALUE) + CloseHandle(server_pipe); + + if (client_pipe != INVALID_HANDLE_VALUE) + CloseHandle(client_pipe); return err; } @@ -712,9 +911,8 @@ void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, handle->name = NULL; } - if (pipeHandle != INVALID_HANDLE_VALUE) { + if (pipeHandle != INVALID_HANDLE_VALUE) CloseHandle(pipeHandle); - } /* Make this req pending reporting an error. */ SET_REQ_ERROR(req, err); diff --git a/include/libuv/src/win/poll.c b/include/libuv/src/win/poll.c index 87858590c..9d3775960 100644 --- a/include/libuv/src/win/poll.c +++ b/include/libuv/src/win/poll.c @@ -488,7 +488,8 @@ static int uv__poll_set(uv_poll_t* handle, int events, uv_poll_cb cb) { assert(handle->type == UV_POLL); assert(!(handle->flags & UV_HANDLE_CLOSING)); - assert((events & ~(UV_READABLE | UV_WRITABLE | UV_DISCONNECT)) == 0); + assert((events & ~(UV_READABLE | UV_WRITABLE | UV_DISCONNECT | + UV_PRIORITIZED)) == 0); handle->events = events; handle->poll_cb = cb; diff --git a/include/libuv/src/win/process-stdio.c b/include/libuv/src/win/process-stdio.c index 355d61880..0db357237 100644 --- a/include/libuv/src/win/process-stdio.c +++ b/include/libuv/src/win/process-stdio.c @@ -95,102 +95,6 @@ void uv_disable_stdio_inheritance(void) { } -static int uv__create_stdio_pipe_pair(uv_loop_t* loop, - uv_pipe_t* server_pipe, HANDLE* child_pipe_ptr, unsigned int flags) { - char pipe_name[64]; - SECURITY_ATTRIBUTES sa; - DWORD server_access = 0; - DWORD client_access = 0; - HANDLE child_pipe = INVALID_HANDLE_VALUE; - int err; - int overlap; - - if (flags & UV_READABLE_PIPE) { - /* The server needs inbound access too, otherwise CreateNamedPipe() won't - * give us the FILE_READ_ATTRIBUTES permission. We need that to probe the - * state of the write buffer when we're trying to shutdown the pipe. */ - server_access |= PIPE_ACCESS_OUTBOUND | PIPE_ACCESS_INBOUND; - client_access |= GENERIC_READ | FILE_WRITE_ATTRIBUTES; - } - if (flags & UV_WRITABLE_PIPE) { - server_access |= PIPE_ACCESS_INBOUND; - client_access |= GENERIC_WRITE | FILE_READ_ATTRIBUTES; - } - - /* Create server pipe handle. */ - err = uv_stdio_pipe_server(loop, - server_pipe, - server_access, - pipe_name, - sizeof(pipe_name)); - if (err) - goto error; - - /* Create child pipe handle. */ - sa.nLength = sizeof sa; - sa.lpSecurityDescriptor = NULL; - sa.bInheritHandle = TRUE; - - overlap = server_pipe->ipc || (flags & UV_OVERLAPPED_PIPE); - child_pipe = CreateFileA(pipe_name, - client_access, - 0, - &sa, - OPEN_EXISTING, - overlap ? FILE_FLAG_OVERLAPPED : 0, - NULL); - if (child_pipe == INVALID_HANDLE_VALUE) { - err = GetLastError(); - goto error; - } - -#ifndef NDEBUG - /* Validate that the pipe was opened in the right mode. */ - { - DWORD mode; - BOOL r = GetNamedPipeHandleState(child_pipe, - &mode, - NULL, - NULL, - NULL, - NULL, - 0); - assert(r == TRUE); - assert(mode == (PIPE_READMODE_BYTE | PIPE_WAIT)); - } -#endif - - /* Do a blocking ConnectNamedPipe. This should not block because we have both - * ends of the pipe created. */ - if (!ConnectNamedPipe(server_pipe->handle, NULL)) { - if (GetLastError() != ERROR_PIPE_CONNECTED) { - err = GetLastError(); - goto error; - } - } - - /* The server end is now readable and/or writable. */ - if (flags & UV_READABLE_PIPE) - server_pipe->flags |= UV_HANDLE_WRITABLE; - if (flags & UV_WRITABLE_PIPE) - server_pipe->flags |= UV_HANDLE_READABLE; - - *child_pipe_ptr = child_pipe; - return 0; - - error: - if (server_pipe->handle != INVALID_HANDLE_VALUE) { - uv_pipe_cleanup(loop, server_pipe); - } - - if (child_pipe != INVALID_HANDLE_VALUE) { - CloseHandle(child_pipe); - } - - return err; -} - - static int uv__duplicate_handle(uv_loop_t* loop, HANDLE handle, HANDLE* dup) { HANDLE current_process; diff --git a/include/libuv/src/win/process.c b/include/libuv/src/win/process.c index 73543c6ed..4038fbfd2 100644 --- a/include/libuv/src/win/process.c +++ b/include/libuv/src/win/process.c @@ -642,7 +642,7 @@ int env_strncmp(const wchar_t* a, int na, const wchar_t* b) { assert(r==nb); B[nb] = L'\0'; - while (1) { + for (;;) { wchar_t AA = *A++; wchar_t BB = *B++; if (AA < BB) { diff --git a/include/libuv/src/win/stream.c b/include/libuv/src/win/stream.c index 46a0709a3..abf477f64 100644 --- a/include/libuv/src/win/stream.c +++ b/include/libuv/src/win/stream.c @@ -65,18 +65,11 @@ int uv_accept(uv_stream_t* server, uv_stream_t* client) { } -int uv_read_start(uv_stream_t* handle, uv_alloc_cb alloc_cb, - uv_read_cb read_cb) { +int uv__read_start(uv_stream_t* handle, + uv_alloc_cb alloc_cb, + uv_read_cb read_cb) { int err; - if (handle->flags & UV_HANDLE_READING) { - return UV_EALREADY; - } - - if (!(handle->flags & UV_HANDLE_READABLE)) { - return UV_ENOTCONN; - } - err = ERROR_INVALID_PARAMETER; switch (handle->type) { case UV_TCP: @@ -195,6 +188,16 @@ int uv_try_write(uv_stream_t* stream, } +int uv_try_write2(uv_stream_t* stream, + const uv_buf_t bufs[], + unsigned int nbufs, + uv_stream_t* send_handle) { + if (send_handle != NULL) + return UV_EAGAIN; + return uv_try_write(stream, bufs, nbufs); +} + + int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) { uv_loop_t* loop = handle->loop; diff --git a/include/libuv/src/win/tcp.c b/include/libuv/src/win/tcp.c index 0dcaa97df..cf2dbd85f 100644 --- a/include/libuv/src/win/tcp.c +++ b/include/libuv/src/win/tcp.c @@ -236,12 +236,7 @@ void uv_tcp_endgame(uv_loop_t* loop, uv_tcp_t* handle) { if (handle->flags & UV_HANDLE_CLOSING && handle->reqs_pending == 0) { assert(!(handle->flags & UV_HANDLE_CLOSED)); - - if (!(handle->flags & UV_HANDLE_TCP_SOCKET_CLOSED)) { - closesocket(handle->socket); - handle->socket = INVALID_SOCKET; - handle->flags |= UV_HANDLE_TCP_SOCKET_CLOSED; - } + assert(handle->socket == INVALID_SOCKET); if (!(handle->flags & UV_HANDLE_CONNECTION) && handle->tcp.serv.accept_reqs) { if (handle->flags & UV_HANDLE_EMULATE_IOCP) { @@ -599,6 +594,7 @@ int uv_tcp_listen(uv_tcp_t* handle, int backlog, uv_connection_cb cb) { } } + /* If this flag is set, we already made this listen call in xfer. */ if (!(handle->flags & UV_HANDLE_SHARED_TCP_SOCKET) && listen(handle->socket, backlog) == SOCKET_ERROR) { return WSAGetLastError(); @@ -769,7 +765,7 @@ static int uv__is_loopback(const struct sockaddr_storage* storage) { } // Check if Windows version is 10.0.16299 or later -static int uv__is_fast_loopback_fail_supported() { +static int uv__is_fast_loopback_fail_supported(void) { OSVERSIONINFOW os_info; if (!pRtlGetVersion) return 0; @@ -800,9 +796,8 @@ static int uv_tcp_try_connect(uv_connect_t* req, if (err) return err; - if (handle->delayed_error) { - return handle->delayed_error; - } + if (handle->delayed_error != 0) + goto out; if (!(handle->flags & UV_HANDLE_BOUND)) { if (addrlen == sizeof(uv_addr_ip4_any_)) { @@ -815,8 +810,8 @@ static int uv_tcp_try_connect(uv_connect_t* req, err = uv_tcp_try_bind(handle, bind_addr, addrlen, 0); if (err) return err; - if (handle->delayed_error) - return handle->delayed_error; + if (handle->delayed_error != 0) + goto out; } if (!handle->tcp.conn.func_connectex) { @@ -844,11 +839,21 @@ static int uv_tcp_try_connect(uv_connect_t* req, NULL); } +out: + UV_REQ_INIT(req, UV_CONNECT); req->handle = (uv_stream_t*) handle; req->cb = cb; memset(&req->u.io.overlapped, 0, sizeof(req->u.io.overlapped)); + if (handle->delayed_error != 0) { + /* Process the req without IOCP. */ + handle->reqs_pending++; + REGISTER_HANDLE_REQ(loop, handle, req); + uv_insert_pending_req(loop, (uv_req_t*)req); + return 0; + } + success = handle->tcp.conn.func_connectex(handle->socket, (const struct sockaddr*) &converted, addrlen, @@ -1015,6 +1020,7 @@ void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, */ err = WSAECONNRESET; } + handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); handle->read_cb((uv_stream_t*)handle, uv_translate_sys_error(err), @@ -1096,6 +1102,7 @@ void uv_process_tcp_read_req(uv_loop_t* loop, uv_tcp_t* handle, * Unix. */ err = WSAECONNRESET; } + handle->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); handle->read_cb((uv_stream_t*)handle, uv_translate_sys_error(err), @@ -1149,9 +1156,14 @@ void uv_process_tcp_write_req(uv_loop_t* loop, uv_tcp_t* handle, } handle->stream.conn.write_reqs_pending--; - if (handle->stream.conn.shutdown_req != NULL && - handle->stream.conn.write_reqs_pending == 0) { - uv_want_endgame(loop, (uv_handle_t*)handle); + if (handle->stream.conn.write_reqs_pending == 0) { + if (handle->flags & UV_HANDLE_CLOSING) { + closesocket(handle->socket); + handle->socket = INVALID_SOCKET; + } + if (handle->stream.conn.shutdown_req != NULL) { + uv_want_endgame(loop, (uv_handle_t*)handle); + } } DECREASE_PENDING_REQ_COUNT(handle); @@ -1215,7 +1227,14 @@ void uv_process_tcp_connect_req(uv_loop_t* loop, uv_tcp_t* handle, UNREGISTER_HANDLE_REQ(loop, handle, req); err = 0; - if (REQ_SUCCESS(req)) { + if (handle->delayed_error) { + /* To smooth over the differences between unixes errors that + * were reported synchronously on the first connect can be delayed + * until the next tick--which is now. + */ + err = handle->delayed_error; + handle->delayed_error = 0; + } else if (REQ_SUCCESS(req)) { if (handle->flags & UV_HANDLE_CLOSING) { /* use UV_ECANCELED for consistency with Unix */ err = ERROR_OPERATION_ABORTED; @@ -1320,7 +1339,7 @@ int uv_tcp_nodelay(uv_tcp_t* handle, int enable) { if (handle->socket != INVALID_SOCKET) { err = uv__tcp_nodelay(handle, handle->socket, enable); if (err) - return err; + return uv_translate_sys_error(err); } if (enable) { @@ -1339,7 +1358,7 @@ int uv_tcp_keepalive(uv_tcp_t* handle, int enable, unsigned int delay) { if (handle->socket != INVALID_SOCKET) { err = uv__tcp_keepalive(handle, handle->socket, enable, delay); if (err) - return err; + return uv_translate_sys_error(err); } if (enable) { @@ -1386,9 +1405,24 @@ int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) { } -static int uv_tcp_try_cancel_io(uv_tcp_t* tcp) { - SOCKET socket = tcp->socket; +static void uv_tcp_try_cancel_reqs(uv_tcp_t* tcp) { + SOCKET socket; int non_ifs_lsp; + int reading; + int writing; + + socket = tcp->socket; + reading = tcp->flags & UV_HANDLE_READING; + writing = tcp->stream.conn.write_reqs_pending > 0; + if (!reading && !writing) + return; + + /* TODO: in libuv v2, keep explicit track of write_reqs, so we can cancel + * them each explicitly with CancelIoEx (like unix). */ + if (reading) + CancelIoEx((HANDLE) socket, &tcp->read_req.u.io.overlapped); + if (writing) + CancelIo((HANDLE) socket); /* Check if we have any non-IFS LSPs stacked on top of TCP */ non_ifs_lsp = (tcp->flags & UV_HANDLE_IPV6) ? uv_tcp_non_ifs_lsp_ipv6 : @@ -1408,71 +1442,41 @@ static int uv_tcp_try_cancel_io(uv_tcp_t* tcp) { NULL, NULL) != 0) { /* Failed. We can't do CancelIo. */ - return -1; + return; } } assert(socket != 0 && socket != INVALID_SOCKET); - if (!CancelIo((HANDLE) socket)) { - return GetLastError(); + if (socket != tcp->socket) { + if (reading) + CancelIoEx((HANDLE) socket, &tcp->read_req.u.io.overlapped); + if (writing) + CancelIo((HANDLE) socket); } - - /* It worked. */ - return 0; } void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp) { - int close_socket = 1; - - if (tcp->flags & UV_HANDLE_READ_PENDING) { - /* In order for winsock to do a graceful close there must not be any any - * pending reads, or the socket must be shut down for writing */ - if (!(tcp->flags & UV_HANDLE_SHARED_TCP_SOCKET)) { - /* Just do shutdown on non-shared sockets, which ensures graceful close. */ - shutdown(tcp->socket, SD_SEND); - - } else if (uv_tcp_try_cancel_io(tcp) == 0) { - /* In case of a shared socket, we try to cancel all outstanding I/O,. If - * that works, don't close the socket yet - wait for the read req to - * return and close the socket in uv_tcp_endgame. */ - close_socket = 0; - - } else { - /* When cancelling isn't possible - which could happen when an LSP is - * present on an old Windows version, we will have to close the socket - * with a read pending. That is not nice because trailing sent bytes may - * not make it to the other side. */ + if (tcp->flags & UV_HANDLE_CONNECTION) { + uv_tcp_try_cancel_reqs(tcp); + if (tcp->flags & UV_HANDLE_READING) { + uv_read_stop((uv_stream_t*) tcp); } - - } else if ((tcp->flags & UV_HANDLE_SHARED_TCP_SOCKET) && - tcp->tcp.serv.accept_reqs != NULL) { - /* Under normal circumstances closesocket() will ensure that all pending - * accept reqs are canceled. However, when the socket is shared the - * presence of another reference to the socket in another process will keep - * the accept reqs going, so we have to ensure that these are canceled. */ - if (uv_tcp_try_cancel_io(tcp) != 0) { - /* When cancellation is not possible, there is another option: we can - * close the incoming sockets, which will also cancel the accept - * operations. However this is not cool because we might inadvertently - * close a socket that just accepted a new connection, which will cause - * the connection to be aborted. */ + } else { + if (tcp->tcp.serv.accept_reqs != NULL) { + /* First close the incoming sockets to cancel the accept operations before + * we free their resources. */ unsigned int i; for (i = 0; i < uv_simultaneous_server_accepts; i++) { uv_tcp_accept_t* req = &tcp->tcp.serv.accept_reqs[i]; - if (req->accept_socket != INVALID_SOCKET && - !HasOverlappedIoCompleted(&req->u.io.overlapped)) { + if (req->accept_socket != INVALID_SOCKET) { closesocket(req->accept_socket); req->accept_socket = INVALID_SOCKET; } } } - } - - if (tcp->flags & UV_HANDLE_READING) { - tcp->flags &= ~UV_HANDLE_READING; - DECREASE_ACTIVE_COUNT(loop, tcp); + assert(!(tcp->flags & UV_HANDLE_READING)); } if (tcp->flags & UV_HANDLE_LISTENING) { @@ -1480,10 +1484,15 @@ void uv_tcp_close(uv_loop_t* loop, uv_tcp_t* tcp) { DECREASE_ACTIVE_COUNT(loop, tcp); } - if (close_socket) { + /* If any overlapped req failed to cancel, calling `closesocket` now would + * cause Win32 to send an RST packet. Try to avoid that for writes, if + * possibly applicable, by waiting to process the completion notifications + * first (which typically should be cancellations). There's not much we can + * do about canceled reads, which also will generate an RST packet. */ + if (!(tcp->flags & UV_HANDLE_CONNECTION) || + tcp->stream.conn.write_reqs_pending == 0) { closesocket(tcp->socket); tcp->socket = INVALID_SOCKET; - tcp->flags |= UV_HANDLE_TCP_SOCKET_CLOSED; } tcp->flags &= ~(UV_HANDLE_READABLE | UV_HANDLE_WRITABLE); @@ -1571,3 +1580,118 @@ int uv__tcp_connect(uv_connect_t* req, return 0; } + +#ifndef WSA_FLAG_NO_HANDLE_INHERIT +/* Added in Windows 7 SP1. Specify this to avoid race conditions, */ +/* but also manually clear the inherit flag in case this failed. */ +#define WSA_FLAG_NO_HANDLE_INHERIT 0x80 +#endif + +int uv_socketpair(int type, int protocol, uv_os_sock_t fds[2], int flags0, int flags1) { + SOCKET server = INVALID_SOCKET; + SOCKET client0 = INVALID_SOCKET; + SOCKET client1 = INVALID_SOCKET; + SOCKADDR_IN name; + LPFN_ACCEPTEX func_acceptex; + WSAOVERLAPPED overlap; + char accept_buffer[sizeof(struct sockaddr_storage) * 2 + 32]; + int namelen; + int err; + DWORD bytes; + DWORD flags; + DWORD client0_flags = WSA_FLAG_NO_HANDLE_INHERIT; + DWORD client1_flags = WSA_FLAG_NO_HANDLE_INHERIT; + + if (flags0 & UV_NONBLOCK_PIPE) + client0_flags |= WSA_FLAG_OVERLAPPED; + if (flags1 & UV_NONBLOCK_PIPE) + client1_flags |= WSA_FLAG_OVERLAPPED; + + server = WSASocketW(AF_INET, type, protocol, NULL, 0, + WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT); + if (server == INVALID_SOCKET) + goto wsaerror; + if (!SetHandleInformation((HANDLE) server, HANDLE_FLAG_INHERIT, 0)) + goto error; + name.sin_family = AF_INET; + name.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + name.sin_port = 0; + if (bind(server, (SOCKADDR*) &name, sizeof(name)) != 0) + goto wsaerror; + if (listen(server, 1) != 0) + goto wsaerror; + namelen = sizeof(name); + if (getsockname(server, (SOCKADDR*) &name, &namelen) != 0) + goto wsaerror; + client0 = WSASocketW(AF_INET, type, protocol, NULL, 0, client0_flags); + if (client0 == INVALID_SOCKET) + goto wsaerror; + if (!SetHandleInformation((HANDLE) client0, HANDLE_FLAG_INHERIT, 0)) + goto error; + if (connect(client0, (SOCKADDR*) &name, sizeof(name)) != 0) + goto wsaerror; + client1 = WSASocketW(AF_INET, type, protocol, NULL, 0, client1_flags); + if (client1 == INVALID_SOCKET) + goto wsaerror; + if (!SetHandleInformation((HANDLE) client1, HANDLE_FLAG_INHERIT, 0)) + goto error; + if (!uv_get_acceptex_function(server, &func_acceptex)) { + err = WSAEAFNOSUPPORT; + goto cleanup; + } + memset(&overlap, 0, sizeof(overlap)); + if (!func_acceptex(server, + client1, + accept_buffer, + 0, + sizeof(struct sockaddr_storage), + sizeof(struct sockaddr_storage), + &bytes, + &overlap)) { + err = WSAGetLastError(); + if (err == ERROR_IO_PENDING) { + /* Result should complete immediately, since we already called connect, + * but emperically, we sometimes have to poll the kernel a couple times + * until it notices that. */ + while (!WSAGetOverlappedResult(client1, &overlap, &bytes, FALSE, &flags)) { + err = WSAGetLastError(); + if (err != WSA_IO_INCOMPLETE) + goto cleanup; + SwitchToThread(); + } + } + else { + goto cleanup; + } + } + if (setsockopt(client1, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, + (char*) &server, sizeof(server)) != 0) { + goto wsaerror; + } + + closesocket(server); + + fds[0] = client0; + fds[1] = client1; + + return 0; + + wsaerror: + err = WSAGetLastError(); + goto cleanup; + + error: + err = GetLastError(); + goto cleanup; + + cleanup: + if (server != INVALID_SOCKET) + closesocket(server); + if (client0 != INVALID_SOCKET) + closesocket(client0); + if (client1 != INVALID_SOCKET) + closesocket(client1); + + assert(err); + return uv_translate_sys_error(err); +} diff --git a/include/libuv/src/win/udp.c b/include/libuv/src/win/udp.c index 68ca728aa..3043f2da8 100644 --- a/include/libuv/src/win/udp.c +++ b/include/libuv/src/win/udp.c @@ -284,7 +284,7 @@ static void uv_udp_queue_recv(uv_loop_t* loop, uv_udp_t* handle) { handle->flags &= ~UV_HANDLE_ZERO_READ; handle->recv_buffer = uv_buf_init(NULL, 0); - handle->alloc_cb((uv_handle_t*) handle, 65536, &handle->recv_buffer); + handle->alloc_cb((uv_handle_t*) handle, UV__UDP_DGRAM_MAXSIZE, &handle->recv_buffer); if (handle->recv_buffer.base == NULL || handle->recv_buffer.len == 0) { handle->recv_cb(handle, UV_ENOBUFS, &handle->recv_buffer, NULL, 0); return; @@ -501,7 +501,7 @@ void uv_process_udp_recv_req(uv_loop_t* loop, uv_udp_t* handle, /* Do a nonblocking receive. * TODO: try to read multiple datagrams at once. FIONREAD maybe? */ buf = uv_buf_init(NULL, 0); - handle->alloc_cb((uv_handle_t*) handle, 65536, &buf); + handle->alloc_cb((uv_handle_t*) handle, UV__UDP_DGRAM_MAXSIZE, &buf); if (buf.base == NULL || buf.len == 0) { handle->recv_cb(handle, UV_ENOBUFS, &buf, NULL, 0); goto done; diff --git a/include/libuv/src/win/util.c b/include/libuv/src/win/util.c index aad8f1a15..88602c7ee 100644 --- a/include/libuv/src/win/util.c +++ b/include/libuv/src/win/util.c @@ -1664,26 +1664,33 @@ int uv_os_unsetenv(const char* name) { int uv_os_gethostname(char* buffer, size_t* size) { - char buf[UV_MAXHOSTNAMESIZE]; + WCHAR buf[UV_MAXHOSTNAMESIZE]; size_t len; + char* utf8_str; + int convert_result; if (buffer == NULL || size == NULL || *size == 0) return UV_EINVAL; uv__once_init(); /* Initialize winsock */ - if (gethostname(buf, sizeof(buf)) != 0) + if (GetHostNameW(buf, UV_MAXHOSTNAMESIZE) != 0) return uv_translate_sys_error(WSAGetLastError()); - buf[sizeof(buf) - 1] = '\0'; /* Null terminate, just to be safe. */ - len = strlen(buf); + convert_result = uv__convert_utf16_to_utf8(buf, -1, &utf8_str); + if (convert_result != 0) + return convert_result; + + len = strlen(utf8_str); if (len >= *size) { *size = len + 1; + uv__free(utf8_str); return UV_ENOBUFS; } - memcpy(buffer, buf, len + 1); + memcpy(buffer, utf8_str, len + 1); + uv__free(utf8_str); *size = len; return 0; } From 9c521bfe1eafed167fbf671614779422db93d39c Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Thu, 12 Aug 2021 23:18:27 +0300 Subject: [PATCH 048/117] fix FileSample --- other/uvsample/FileSample.hx | 22 +++++++++++----------- other/uvsample/FsEventSample.hx | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/other/uvsample/FileSample.hx b/other/uvsample/FileSample.hx index d51a3200f..d8774df99 100644 --- a/other/uvsample/FileSample.hx +++ b/other/uvsample/FileSample.hx @@ -53,12 +53,12 @@ class FileSample { } } - static function createFile(path:String, content:Bytes, callback:()->Void) { + static function createFile(path:String, content:Bytes, callback:()->Void, ?pos:PosInfos) { File.open(loop, path, [O_CREAT(420),O_TRUNC,O_WRONLY], (e, file) -> handle(() -> { file.write(loop, content.getData(), content.length, I64.ofInt(0), (e, bytesWritten) -> handle(() -> { - file.close(loop, handle(callback)); - })(e)); - })(e)); + file.close(loop, handle(callback, pos)); + }, pos)(e)); + }, pos)(e)); } static function readFile(path:String, callback:(data:Bytes)->Void) { @@ -214,7 +214,7 @@ class FileSample { } static function truncate(actions:Actions) { - var path = Misc.tmpDir() + '/test-file'; + var path = Misc.tmpDir() + '/test-file-truncate'; var content = '1234567890'; Log.print('Writing content for truncation at $path: $content'); createFile(path, Bytes.ofString(content), () -> { @@ -236,7 +236,7 @@ class FileSample { } static function copyFile(actions:Actions) { - var path = Misc.tmpDir() + '/test-file'; + var path = Misc.tmpDir() + '/test-file-copy'; var newPath = '$path-copy'; createFile(path, Bytes.ofString('123'), () -> { Log.print('Copy $path to $newPath'); @@ -250,7 +250,7 @@ class FileSample { } static function sendFile(actions:Actions) { - var path = Misc.tmpDir() + '/test-file'; + var path = Misc.tmpDir() + '/test-file-send'; var newPath = '$path-copy'; createFile(path, Bytes.ofString('12345678'), () -> { File.open(loop, path, [O_RDONLY], (e, src) -> handle(() -> { @@ -282,7 +282,7 @@ class FileSample { } static function chmod(actions:Actions) { - var path = Misc.tmpDir() + '/test-file'; + var path = Misc.tmpDir() + '/test-file-chmod'; createFile(path, Bytes.ofString('123'), () -> { Log.print('chmod on $path...'); File.chmod(loop, path, 420, handle(() -> { @@ -295,7 +295,7 @@ class FileSample { } static function utime(actions:Actions) { - var path = Misc.tmpDir() + '/test-file'; + var path = Misc.tmpDir() + '/test-file-utime'; createFile(path, Bytes.ofString('123'), () -> { Log.print('utime on $path...'); File.utime(loop, path, Date.now().getTime(), Date.now().getTime(), handle(() -> { @@ -308,7 +308,7 @@ class FileSample { } static function linkSymlinkReadLinkRealPath(actions:Actions) { - var path = Misc.tmpDir() + '/test-file'; + var path = Misc.tmpDir() + '/test-file-l'; var newPath = Misc.tmpDir() + '/test-file-link'; createFile(path, Bytes.ofString('123'), () -> { Log.print('link $path to $newPath...'); @@ -339,7 +339,7 @@ class FileSample { return; } - var path = Misc.tmpDir() + '/test-file'; + var path = Misc.tmpDir() + '/test-file-chown'; createFile(path, Bytes.ofString(''), () -> { Log.print('chown on $path...'); File.chown(loop, path, -1, -1, handle(() -> { diff --git a/other/uvsample/FsEventSample.hx b/other/uvsample/FsEventSample.hx index f643b26b0..36dc0162e 100644 --- a/other/uvsample/FsEventSample.hx +++ b/other/uvsample/FsEventSample.hx @@ -10,7 +10,7 @@ class FsEventSample { public static function main() { var loop = Thread.current().events; var event = FsEvent.init(loop); - var path = Misc.tmpDir() + '/test-file'; + var path = Misc.tmpDir() + '/test-file-fsevent'; File.saveContent(path, 'Hello, world'); event.start(path, null, (e, path, events) -> switch e { case UV_NOERR: From 5b51a733b6847bddf527291f95cf37e2fe35cf04 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Thu, 12 Aug 2021 23:50:05 +0300 Subject: [PATCH 049/117] version --- libs/uv/uv.c | 39 +++++++++++++++++++++++++++++++++ other/uvsample/UVSample.hx | 3 ++- other/uvsample/VersionSample.hx | 19 ++++++++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 other/uvsample/VersionSample.hx diff --git a/libs/uv/uv.c b/libs/uv/uv.c index e0032942b..5519f5ccc 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -2365,3 +2365,42 @@ HL_PRIM int HL_NAME(tty_get_vterm_state_wrap)() { } DEFINE_PRIM(_I32, tty_get_vterm_state_wrap, _NO_ARG); +// version + +HL_PRIM vbyte *HL_NAME(version_string_wrap)() { + const char *v = uv_version_string(); + return hl_copy_bytes((vbyte *)v, strlen(v)); +} +DEFINE_PRIM(_BYTES, version_string_wrap, _NO_ARG); + +HL_PRIM int HL_NAME(version_major)() { + return UV_VERSION_MAJOR; +} +DEFINE_PRIM(_I32, version_major, _NO_ARG); + +HL_PRIM int HL_NAME(version_minor)() { + return UV_VERSION_MINOR; +} +DEFINE_PRIM(_I32, version_minor, _NO_ARG); + +HL_PRIM int HL_NAME(version_patch)() { + return UV_VERSION_PATCH; +} +DEFINE_PRIM(_I32, version_patch, _NO_ARG); + +HL_PRIM bool HL_NAME(version_is_release)() { + return UV_VERSION_IS_RELEASE; +} +DEFINE_PRIM(_BOOL, version_is_release, _NO_ARG); + +HL_PRIM vbyte *HL_NAME(version_suffix)() { + return (vbyte *)UV_VERSION_SUFFIX; +} +DEFINE_PRIM(_BYTES, version_suffix, _NO_ARG); + +HL_PRIM int HL_NAME(version_hex)() { + return UV_VERSION_HEX; +} +DEFINE_PRIM(_I32, version_hex, _NO_ARG); + + diff --git a/other/uvsample/UVSample.hx b/other/uvsample/UVSample.hx index 7fc5f9d0a..02f3ef038 100644 --- a/other/uvsample/UVSample.hx +++ b/other/uvsample/UVSample.hx @@ -9,6 +9,7 @@ class UVSample { // FsEventSample.main(); // FsPollSample.main(); // MiscSample.main(); - TtySample.main(); + // TtySample.main(); + VersionSample.main(); } } \ No newline at end of file diff --git a/other/uvsample/VersionSample.hx b/other/uvsample/VersionSample.hx new file mode 100644 index 000000000..0a4aaca0e --- /dev/null +++ b/other/uvsample/VersionSample.hx @@ -0,0 +1,19 @@ +import hl.uv.Version; +import hl.uv.UVException; +import sys.thread.Thread; + +class VersionSample { + static function print(msg:String) { + Log.print('VersionSample: $msg'); + } + + public static function main() { + print('string ' + Version.string()); + print('major ' + Version.major); + print('minor ' + Version.minor); + print('patch ' + Version.patch); + print('isRelease ' + Version.isRelease); + print('suffix "${Version.suffix}"'); + print('hex ' + Version.hex); + } +} \ No newline at end of file From bc22d4e7c347808aa97be21ca1830a13e5b8e774 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 13 Aug 2021 12:56:27 +0300 Subject: [PATCH 050/117] [ci][linux] manually build libuv --- libs/uv/uv.c | 4 ++-- other/azure-pipelines/build-linux.yml | 12 ++++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 5519f5ccc..ad0111d45 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -144,7 +144,7 @@ static int errno_uv2hx( int uv_errno ) { case UV_ENOTEMPTY: return 56; case UV_ENOTSOCK: return 57; case UV_ENOTSUP: return 58; - // case UV_EOVERFLOW: return 59; + case UV_EOVERFLOW: return 59; case UV_EPERM: return 60; case UV_EPIPE: return 61; case UV_EPROTO: return 62; @@ -165,7 +165,7 @@ static int errno_uv2hx( int uv_errno ) { case UV_ENOTTY: return 77; case UV_EFTYPE: return 78; case UV_EILSEQ: return 79; - // case UV_ESOCKTNOSUPPORT: return 80; + case UV_ESOCKTNOSUPPORT: return 80; default: return 73; //UV_UNKNOWN } } diff --git a/other/azure-pipelines/build-linux.yml b/other/azure-pipelines/build-linux.yml index 2f969b6d9..df27c21b8 100644 --- a/other/azure-pipelines/build-linux.yml +++ b/other/azure-pipelines/build-linux.yml @@ -42,7 +42,6 @@ jobs: libalut-dev \ libmbedtls-dev \ libturbojpeg0-dev \ - libuv1-dev \ libopenal-dev \ neko \ curl \ @@ -65,12 +64,21 @@ jobs: libalut-dev:${{ parameters.arch }} \ libmbedtls-dev:${{ parameters.arch }} \ libturbojpeg0-dev:i386 \ - libuv1-dev:${{ parameters.arch }} \ libopenal-dev:${{ parameters.arch }} \ neko \ curl \ ca-certificates displayName: Install dependencies + - script: | + curl -fsSL -o "libuv-v1.42.0.tar.gz" --retry 3 https://dist.libuv.org/dist/v1.42.0/libuv-v1.42.0.tar.gz + tar -xf libuv-v1.42.0.tar.gz + cd libuv-v1.42.0.tar.gz + sh autogen.sh + ./configure + make + make check + make install + displayName: Install libuv - template: install-haxe-snapshot.yml parameters: platform: linux64 From c3f9bca5558f89a3f28ecd5cb4d6d07b14f2367b Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 13 Aug 2021 14:27:17 +0300 Subject: [PATCH 051/117] sudo --- other/azure-pipelines/build-linux.yml | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/other/azure-pipelines/build-linux.yml b/other/azure-pipelines/build-linux.yml index df27c21b8..d753c5fb8 100644 --- a/other/azure-pipelines/build-linux.yml +++ b/other/azure-pipelines/build-linux.yml @@ -68,17 +68,15 @@ jobs: neko \ curl \ ca-certificates + curl -fsSL -o "libuv-v1.42.0.tar.gz" --retry 3 https://dist.libuv.org/dist/v1.42.0/libuv-v1.42.0.tar.gz + tar -xf libuv-v1.42.0.tar.gz + cd libuv-v1.42.0.tar.gz + sh autogen.sh + ./configure + make + make check + sudo make install displayName: Install dependencies - - script: | - curl -fsSL -o "libuv-v1.42.0.tar.gz" --retry 3 https://dist.libuv.org/dist/v1.42.0/libuv-v1.42.0.tar.gz - tar -xf libuv-v1.42.0.tar.gz - cd libuv-v1.42.0.tar.gz - sh autogen.sh - ./configure - make - make check - make install - displayName: Install libuv - template: install-haxe-snapshot.yml parameters: platform: linux64 From 417d557bf8e073b3ff4ce1ab1f7de4e1f5f4b47d Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 13 Aug 2021 14:27:44 +0300 Subject: [PATCH 052/117] sudo --- other/azure-pipelines/build-linux.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/other/azure-pipelines/build-linux.yml b/other/azure-pipelines/build-linux.yml index d753c5fb8..8e10ea9cd 100644 --- a/other/azure-pipelines/build-linux.yml +++ b/other/azure-pipelines/build-linux.yml @@ -46,6 +46,14 @@ jobs: neko \ curl \ ca-certificates + curl -fsSL -o "libuv-v1.42.0.tar.gz" --retry 3 https://dist.libuv.org/dist/v1.42.0/libuv-v1.42.0.tar.gz + tar -xf libuv-v1.42.0.tar.gz + cd libuv-v1.42.0.tar.gz + sh autogen.sh + ./configure + make + make check + sudo make install displayName: Install dependencies - ${{ if not(eq(parameters.arch, '')) }}: - script: | From 7d3ae3d8f2371432f5822c99529bbbfdcdc56ba6 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 13 Aug 2021 16:26:13 +0300 Subject: [PATCH 053/117] ls --- other/azure-pipelines/build-linux.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/other/azure-pipelines/build-linux.yml b/other/azure-pipelines/build-linux.yml index 8e10ea9cd..c205e6492 100644 --- a/other/azure-pipelines/build-linux.yml +++ b/other/azure-pipelines/build-linux.yml @@ -48,6 +48,7 @@ jobs: ca-certificates curl -fsSL -o "libuv-v1.42.0.tar.gz" --retry 3 https://dist.libuv.org/dist/v1.42.0/libuv-v1.42.0.tar.gz tar -xf libuv-v1.42.0.tar.gz + ls -la ./ cd libuv-v1.42.0.tar.gz sh autogen.sh ./configure @@ -78,6 +79,7 @@ jobs: ca-certificates curl -fsSL -o "libuv-v1.42.0.tar.gz" --retry 3 https://dist.libuv.org/dist/v1.42.0/libuv-v1.42.0.tar.gz tar -xf libuv-v1.42.0.tar.gz + ls -la ./ cd libuv-v1.42.0.tar.gz sh autogen.sh ./configure From d5aa49cd181a3e7467e58ab106a73eb8710454a2 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 13 Aug 2021 16:28:50 +0300 Subject: [PATCH 054/117] fix cd --- other/azure-pipelines/build-linux.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/other/azure-pipelines/build-linux.yml b/other/azure-pipelines/build-linux.yml index c205e6492..bf4016805 100644 --- a/other/azure-pipelines/build-linux.yml +++ b/other/azure-pipelines/build-linux.yml @@ -48,8 +48,7 @@ jobs: ca-certificates curl -fsSL -o "libuv-v1.42.0.tar.gz" --retry 3 https://dist.libuv.org/dist/v1.42.0/libuv-v1.42.0.tar.gz tar -xf libuv-v1.42.0.tar.gz - ls -la ./ - cd libuv-v1.42.0.tar.gz + cd libuv-v1.42.0 sh autogen.sh ./configure make @@ -79,8 +78,7 @@ jobs: ca-certificates curl -fsSL -o "libuv-v1.42.0.tar.gz" --retry 3 https://dist.libuv.org/dist/v1.42.0/libuv-v1.42.0.tar.gz tar -xf libuv-v1.42.0.tar.gz - ls -la ./ - cd libuv-v1.42.0.tar.gz + cd libuv-v1.42.0 sh autogen.sh ./configure make From b732fc8cfd84f910ba6d066893c41353eea66815 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 13 Aug 2021 17:04:41 +0300 Subject: [PATCH 055/117] autotools --- other/azure-pipelines/build-linux.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/other/azure-pipelines/build-linux.yml b/other/azure-pipelines/build-linux.yml index bf4016805..131023cf0 100644 --- a/other/azure-pipelines/build-linux.yml +++ b/other/azure-pipelines/build-linux.yml @@ -34,6 +34,7 @@ jobs: cmake \ make \ gcc \ + autotools-dev \ libz-dev \ zlib1g-dev \ libpng-dev \ @@ -64,6 +65,7 @@ jobs: cmake \ make \ gcc-multilib \ + autotools-dev \ libz-dev:${{ parameters.arch }} \ zlib1g-dev:${{ parameters.arch }} \ libpng-dev:${{ parameters.arch }} \ From 7d997cdd11601cedbc3245eb1253e8dca3fa3c13 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Tue, 17 Aug 2021 22:08:49 +0300 Subject: [PATCH 056/117] added libuv docs --- include/libuv/LICENSE-docs | 396 +++++++++ include/libuv/docs/Makefile | 183 +++++ include/libuv/docs/code/cgi/main.c | 81 ++ include/libuv/docs/code/cgi/tick.c | 13 + include/libuv/docs/code/detach/main.c | 31 + include/libuv/docs/code/dns/main.c | 80 ++ include/libuv/docs/code/helloworld/main.c | 15 + include/libuv/docs/code/idle-basic/main.c | 24 + include/libuv/docs/code/idle-compute/main.c | 43 + include/libuv/docs/code/interfaces/main.c | 33 + include/libuv/docs/code/locks/main.c | 57 ++ .../docs/code/multi-echo-server/hammer.js | 20 + .../libuv/docs/code/multi-echo-server/main.c | 114 +++ .../docs/code/multi-echo-server/worker.c | 88 ++ include/libuv/docs/code/onchange/main.c | 44 + .../libuv/docs/code/pipe-echo-server/main.c | 94 +++ include/libuv/docs/code/plugin/hello.c | 5 + include/libuv/docs/code/plugin/main.c | 39 + include/libuv/docs/code/plugin/plugin.h | 7 + include/libuv/docs/code/proc-streams/main.c | 49 ++ include/libuv/docs/code/proc-streams/test.c | 8 + include/libuv/docs/code/progress/main.c | 47 ++ include/libuv/docs/code/queue-cancel/main.c | 59 ++ include/libuv/docs/code/queue-work/main.c | 44 + include/libuv/docs/code/ref-timer/main.c | 29 + include/libuv/docs/code/signal/main.c | 66 ++ include/libuv/docs/code/spawn/main.c | 36 + .../libuv/docs/code/tcp-echo-server/main.c | 87 ++ include/libuv/docs/code/thread-create/main.c | 36 + include/libuv/docs/code/tty-gravity/main.c | 48 ++ include/libuv/docs/code/tty/main.c | 29 + include/libuv/docs/code/udp-dhcp/main.c | 127 +++ include/libuv/docs/code/uvcat/main.c | 63 ++ include/libuv/docs/code/uvstop/main.c | 33 + include/libuv/docs/code/uvtee/main.c | 80 ++ include/libuv/docs/code/uvwget/main.c | 166 ++++ include/libuv/docs/make.bat | 243 ++++++ include/libuv/docs/requirements.txt | 42 + include/libuv/docs/src/api.rst | 36 + include/libuv/docs/src/async.rst | 65 ++ include/libuv/docs/src/check.rst | 54 ++ include/libuv/docs/src/conf.py | 348 ++++++++ include/libuv/docs/src/design.rst | 140 ++++ include/libuv/docs/src/dll.rst | 44 + include/libuv/docs/src/dns.rst | 108 +++ include/libuv/docs/src/errors.rst | 385 +++++++++ include/libuv/docs/src/fs.rst | 702 ++++++++++++++++ include/libuv/docs/src/fs_event.rst | 132 +++ include/libuv/docs/src/fs_poll.rst | 77 ++ include/libuv/docs/src/guide.rst | 22 + include/libuv/docs/src/guide/about.rst | 22 + include/libuv/docs/src/guide/basics.rst | 221 +++++ include/libuv/docs/src/guide/eventloops.rst | 50 ++ include/libuv/docs/src/guide/filesystem.rst | 339 ++++++++ include/libuv/docs/src/guide/introduction.rst | 75 ++ include/libuv/docs/src/guide/networking.rst | 257 ++++++ include/libuv/docs/src/guide/processes.rst | 421 ++++++++++ include/libuv/docs/src/guide/threads.rst | 397 +++++++++ include/libuv/docs/src/guide/utilities.rst | 450 +++++++++++ include/libuv/docs/src/handle.rst | 283 +++++++ include/libuv/docs/src/idle.rst | 62 ++ include/libuv/docs/src/index.rst | 62 ++ include/libuv/docs/src/loop.rst | 245 ++++++ include/libuv/docs/src/metrics.rst | 27 + include/libuv/docs/src/migration_010_100.rst | 244 ++++++ include/libuv/docs/src/misc.rst | 755 ++++++++++++++++++ include/libuv/docs/src/pipe.rst | 138 ++++ include/libuv/docs/src/poll.rst | 148 ++++ include/libuv/docs/src/prepare.rst | 54 ++ include/libuv/docs/src/process.rst | 251 ++++++ include/libuv/docs/src/request.rst | 117 +++ include/libuv/docs/src/signal.rst | 101 +++ .../libuv/docs/src/sphinx-plugins/manpage.py | 45 ++ .../libuv/docs/src/static/architecture.png | Bin 0 -> 206767 bytes .../src/static/diagrams.key/Data/st0-311.jpg | Bin 0 -> 14413 bytes .../src/static/diagrams.key/Data/st1-475.jpg | Bin 0 -> 8284 bytes .../docs/src/static/diagrams.key/Index.zip | Bin 0 -> 71160 bytes .../Metadata/BuildVersionHistory.plist | 8 + .../diagrams.key/Metadata/DocumentIdentifier | 1 + .../diagrams.key/Metadata/Properties.plist | Bin 0 -> 340 bytes .../src/static/diagrams.key/preview-micro.jpg | Bin 0 -> 1425 bytes .../src/static/diagrams.key/preview-web.jpg | Bin 0 -> 8106 bytes .../docs/src/static/diagrams.key/preview.jpg | Bin 0 -> 107456 bytes include/libuv/docs/src/static/favicon.ico | Bin 0 -> 15086 bytes include/libuv/docs/src/static/logo.png | Bin 0 -> 33545 bytes .../libuv/docs/src/static/loop_iteration.png | Bin 0 -> 80528 bytes include/libuv/docs/src/stream.rst | 258 ++++++ include/libuv/docs/src/tcp.rst | 146 ++++ include/libuv/docs/src/threading.rst | 197 +++++ include/libuv/docs/src/threadpool.rst | 69 ++ include/libuv/docs/src/timer.rst | 88 ++ include/libuv/docs/src/tty.rst | 140 ++++ include/libuv/docs/src/udp.rst | 450 +++++++++++ include/libuv/docs/src/upgrading.rst | 11 + include/libuv/docs/src/version.rst | 60 ++ 95 files changed, 10964 insertions(+) create mode 100644 include/libuv/LICENSE-docs create mode 100644 include/libuv/docs/Makefile create mode 100644 include/libuv/docs/code/cgi/main.c create mode 100644 include/libuv/docs/code/cgi/tick.c create mode 100644 include/libuv/docs/code/detach/main.c create mode 100644 include/libuv/docs/code/dns/main.c create mode 100644 include/libuv/docs/code/helloworld/main.c create mode 100644 include/libuv/docs/code/idle-basic/main.c create mode 100644 include/libuv/docs/code/idle-compute/main.c create mode 100644 include/libuv/docs/code/interfaces/main.c create mode 100644 include/libuv/docs/code/locks/main.c create mode 100644 include/libuv/docs/code/multi-echo-server/hammer.js create mode 100644 include/libuv/docs/code/multi-echo-server/main.c create mode 100644 include/libuv/docs/code/multi-echo-server/worker.c create mode 100644 include/libuv/docs/code/onchange/main.c create mode 100644 include/libuv/docs/code/pipe-echo-server/main.c create mode 100644 include/libuv/docs/code/plugin/hello.c create mode 100644 include/libuv/docs/code/plugin/main.c create mode 100644 include/libuv/docs/code/plugin/plugin.h create mode 100644 include/libuv/docs/code/proc-streams/main.c create mode 100644 include/libuv/docs/code/proc-streams/test.c create mode 100644 include/libuv/docs/code/progress/main.c create mode 100644 include/libuv/docs/code/queue-cancel/main.c create mode 100644 include/libuv/docs/code/queue-work/main.c create mode 100644 include/libuv/docs/code/ref-timer/main.c create mode 100644 include/libuv/docs/code/signal/main.c create mode 100644 include/libuv/docs/code/spawn/main.c create mode 100644 include/libuv/docs/code/tcp-echo-server/main.c create mode 100644 include/libuv/docs/code/thread-create/main.c create mode 100644 include/libuv/docs/code/tty-gravity/main.c create mode 100644 include/libuv/docs/code/tty/main.c create mode 100644 include/libuv/docs/code/udp-dhcp/main.c create mode 100644 include/libuv/docs/code/uvcat/main.c create mode 100644 include/libuv/docs/code/uvstop/main.c create mode 100644 include/libuv/docs/code/uvtee/main.c create mode 100644 include/libuv/docs/code/uvwget/main.c create mode 100644 include/libuv/docs/make.bat create mode 100644 include/libuv/docs/requirements.txt create mode 100644 include/libuv/docs/src/api.rst create mode 100644 include/libuv/docs/src/async.rst create mode 100644 include/libuv/docs/src/check.rst create mode 100644 include/libuv/docs/src/conf.py create mode 100644 include/libuv/docs/src/design.rst create mode 100644 include/libuv/docs/src/dll.rst create mode 100644 include/libuv/docs/src/dns.rst create mode 100644 include/libuv/docs/src/errors.rst create mode 100644 include/libuv/docs/src/fs.rst create mode 100644 include/libuv/docs/src/fs_event.rst create mode 100644 include/libuv/docs/src/fs_poll.rst create mode 100644 include/libuv/docs/src/guide.rst create mode 100644 include/libuv/docs/src/guide/about.rst create mode 100644 include/libuv/docs/src/guide/basics.rst create mode 100644 include/libuv/docs/src/guide/eventloops.rst create mode 100644 include/libuv/docs/src/guide/filesystem.rst create mode 100644 include/libuv/docs/src/guide/introduction.rst create mode 100644 include/libuv/docs/src/guide/networking.rst create mode 100644 include/libuv/docs/src/guide/processes.rst create mode 100644 include/libuv/docs/src/guide/threads.rst create mode 100644 include/libuv/docs/src/guide/utilities.rst create mode 100644 include/libuv/docs/src/handle.rst create mode 100644 include/libuv/docs/src/idle.rst create mode 100644 include/libuv/docs/src/index.rst create mode 100644 include/libuv/docs/src/loop.rst create mode 100644 include/libuv/docs/src/metrics.rst create mode 100644 include/libuv/docs/src/migration_010_100.rst create mode 100644 include/libuv/docs/src/misc.rst create mode 100644 include/libuv/docs/src/pipe.rst create mode 100644 include/libuv/docs/src/poll.rst create mode 100644 include/libuv/docs/src/prepare.rst create mode 100644 include/libuv/docs/src/process.rst create mode 100644 include/libuv/docs/src/request.rst create mode 100644 include/libuv/docs/src/signal.rst create mode 100644 include/libuv/docs/src/sphinx-plugins/manpage.py create mode 100644 include/libuv/docs/src/static/architecture.png create mode 100644 include/libuv/docs/src/static/diagrams.key/Data/st0-311.jpg create mode 100644 include/libuv/docs/src/static/diagrams.key/Data/st1-475.jpg create mode 100644 include/libuv/docs/src/static/diagrams.key/Index.zip create mode 100644 include/libuv/docs/src/static/diagrams.key/Metadata/BuildVersionHistory.plist create mode 100644 include/libuv/docs/src/static/diagrams.key/Metadata/DocumentIdentifier create mode 100644 include/libuv/docs/src/static/diagrams.key/Metadata/Properties.plist create mode 100644 include/libuv/docs/src/static/diagrams.key/preview-micro.jpg create mode 100644 include/libuv/docs/src/static/diagrams.key/preview-web.jpg create mode 100644 include/libuv/docs/src/static/diagrams.key/preview.jpg create mode 100644 include/libuv/docs/src/static/favicon.ico create mode 100644 include/libuv/docs/src/static/logo.png create mode 100644 include/libuv/docs/src/static/loop_iteration.png create mode 100644 include/libuv/docs/src/stream.rst create mode 100644 include/libuv/docs/src/tcp.rst create mode 100644 include/libuv/docs/src/threading.rst create mode 100644 include/libuv/docs/src/threadpool.rst create mode 100644 include/libuv/docs/src/timer.rst create mode 100644 include/libuv/docs/src/tty.rst create mode 100644 include/libuv/docs/src/udp.rst create mode 100644 include/libuv/docs/src/upgrading.rst create mode 100644 include/libuv/docs/src/version.rst diff --git a/include/libuv/LICENSE-docs b/include/libuv/LICENSE-docs new file mode 100644 index 000000000..53883b1c7 --- /dev/null +++ b/include/libuv/LICENSE-docs @@ -0,0 +1,396 @@ +Attribution 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More_considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution 4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution 4.0 International Public License ("Public License"). To the +extent this Public License may be interpreted as a contract, You are +granted the Licensed Rights in consideration of Your acceptance of +these terms and conditions, and the Licensor grants You such rights in +consideration of benefits the Licensor receives from making the +Licensed Material available under these terms and conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + d. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + e. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + f. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + g. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + h. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + i. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + j. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + k. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + 4. If You Share Adapted Material You produce, the Adapter's + License You apply must not prevent recipients of the Adapted + Material from complying with this Public License. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material; and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. + diff --git a/include/libuv/docs/Makefile b/include/libuv/docs/Makefile new file mode 100644 index 000000000..d23c6948e --- /dev/null +++ b/include/libuv/docs/Makefile @@ -0,0 +1,183 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +SPHINXAUTOBUILD = sphinx-autobuild +PAPER = +BUILDDIR = build +SRCDIR = src + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SRCDIR) +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SRCDIR) + +.PHONY: help clean html livehtml dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " livehtml to make standalone HTML files and live reload them" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +livehtml: html + $(SPHINXAUTOBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/libuv.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/libuv.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/libuv" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/libuv" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/include/libuv/docs/code/cgi/main.c b/include/libuv/docs/code/cgi/main.c new file mode 100644 index 000000000..d2e34265a --- /dev/null +++ b/include/libuv/docs/code/cgi/main.c @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#include + +uv_loop_t *loop; +uv_process_t child_req; +uv_process_options_t options; + +void cleanup_handles(uv_process_t *req, int64_t exit_status, int term_signal) { + fprintf(stderr, "Process exited with status %" PRId64 ", signal %d\n", exit_status, term_signal); + uv_close((uv_handle_t*) req->data, NULL); + uv_close((uv_handle_t*) req, NULL); +} + +void invoke_cgi_script(uv_tcp_t *client) { + size_t size = 500; + char path[size]; + uv_exepath(path, &size); + strcpy(path + (strlen(path) - strlen("cgi")), "tick"); + + char* args[2]; + args[0] = path; + args[1] = NULL; + + /* ... finding the executable path and setting up arguments ... */ + + options.stdio_count = 3; + uv_stdio_container_t child_stdio[3]; + child_stdio[0].flags = UV_IGNORE; + child_stdio[1].flags = UV_INHERIT_STREAM; + child_stdio[1].data.stream = (uv_stream_t*) client; + child_stdio[2].flags = UV_IGNORE; + options.stdio = child_stdio; + + options.exit_cb = cleanup_handles; + options.file = args[0]; + options.args = args; + + // Set this so we can close the socket after the child process exits. + child_req.data = (void*) client; + int r; + if ((r = uv_spawn(loop, &child_req, &options))) { + fprintf(stderr, "%s\n", uv_strerror(r)); + return; + } +} + +void on_new_connection(uv_stream_t *server, int status) { + if (status == -1) { + // error! + return; + } + + uv_tcp_t *client = (uv_tcp_t*) malloc(sizeof(uv_tcp_t)); + uv_tcp_init(loop, client); + if (uv_accept(server, (uv_stream_t*) client) == 0) { + invoke_cgi_script(client); + } + else { + uv_close((uv_handle_t*) client, NULL); + } +} + +int main() { + loop = uv_default_loop(); + + uv_tcp_t server; + uv_tcp_init(loop, &server); + + struct sockaddr_in bind_addr; + uv_ip4_addr("0.0.0.0", 7000, &bind_addr); + uv_tcp_bind(&server, (const struct sockaddr *)&bind_addr, 0); + int r = uv_listen((uv_stream_t*) &server, 128, on_new_connection); + if (r) { + fprintf(stderr, "Listen error %s\n", uv_err_name(r)); + return 1; + } + return uv_run(loop, UV_RUN_DEFAULT); +} diff --git a/include/libuv/docs/code/cgi/tick.c b/include/libuv/docs/code/cgi/tick.c new file mode 100644 index 000000000..0b498edf5 --- /dev/null +++ b/include/libuv/docs/code/cgi/tick.c @@ -0,0 +1,13 @@ +#include +#include + +int main() { + int i; + for (i = 0; i < 10; i++) { + printf("tick\n"); + fflush(stdout); + sleep(1); + } + printf("BOOM!\n"); + return 0; +} diff --git a/include/libuv/docs/code/detach/main.c b/include/libuv/docs/code/detach/main.c new file mode 100644 index 000000000..3c88fff4e --- /dev/null +++ b/include/libuv/docs/code/detach/main.c @@ -0,0 +1,31 @@ +#include + +#include + +uv_loop_t *loop; +uv_process_t child_req; +uv_process_options_t options; + +int main() { + loop = uv_default_loop(); + + char* args[3]; + args[0] = "sleep"; + args[1] = "100"; + args[2] = NULL; + + options.exit_cb = NULL; + options.file = "sleep"; + options.args = args; + options.flags = UV_PROCESS_DETACHED; + + int r; + if ((r = uv_spawn(loop, &child_req, &options))) { + fprintf(stderr, "%s\n", uv_strerror(r)); + return 1; + } + fprintf(stderr, "Launched sleep with PID %d\n", child_req.pid); + uv_unref((uv_handle_t*) &child_req); + + return uv_run(loop, UV_RUN_DEFAULT); +} diff --git a/include/libuv/docs/code/dns/main.c b/include/libuv/docs/code/dns/main.c new file mode 100644 index 000000000..2d63f1aa1 --- /dev/null +++ b/include/libuv/docs/code/dns/main.c @@ -0,0 +1,80 @@ +#include +#include +#include +#include + +uv_loop_t *loop; + +void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { + buf->base = malloc(suggested_size); + buf->len = suggested_size; +} + +void on_read(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) { + if (nread < 0) { + if (nread != UV_EOF) + fprintf(stderr, "Read error %s\n", uv_err_name(nread)); + uv_close((uv_handle_t*) client, NULL); + free(buf->base); + free(client); + return; + } + + char *data = (char*) malloc(sizeof(char) * (nread+1)); + data[nread] = '\0'; + strncpy(data, buf->base, nread); + + fprintf(stderr, "%s", data); + free(data); + free(buf->base); +} + +void on_connect(uv_connect_t *req, int status) { + if (status < 0) { + fprintf(stderr, "connect failed error %s\n", uv_err_name(status)); + free(req); + return; + } + + uv_read_start((uv_stream_t*) req->handle, alloc_buffer, on_read); + free(req); +} + +void on_resolved(uv_getaddrinfo_t *resolver, int status, struct addrinfo *res) { + if (status < 0) { + fprintf(stderr, "getaddrinfo callback error %s\n", uv_err_name(status)); + return; + } + + char addr[17] = {'\0'}; + uv_ip4_name((struct sockaddr_in*) res->ai_addr, addr, 16); + fprintf(stderr, "%s\n", addr); + + uv_connect_t *connect_req = (uv_connect_t*) malloc(sizeof(uv_connect_t)); + uv_tcp_t *socket = (uv_tcp_t*) malloc(sizeof(uv_tcp_t)); + uv_tcp_init(loop, socket); + + uv_tcp_connect(connect_req, socket, (const struct sockaddr*) res->ai_addr, on_connect); + + uv_freeaddrinfo(res); +} + +int main() { + loop = uv_default_loop(); + + struct addrinfo hints; + hints.ai_family = PF_INET; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + hints.ai_flags = 0; + + uv_getaddrinfo_t resolver; + fprintf(stderr, "irc.libera.chat is... "); + int r = uv_getaddrinfo(loop, &resolver, on_resolved, "irc.libera.chat", "6667", &hints); + + if (r) { + fprintf(stderr, "getaddrinfo call error %s\n", uv_err_name(r)); + return 1; + } + return uv_run(loop, UV_RUN_DEFAULT); +} diff --git a/include/libuv/docs/code/helloworld/main.c b/include/libuv/docs/code/helloworld/main.c new file mode 100644 index 000000000..a31bf88a3 --- /dev/null +++ b/include/libuv/docs/code/helloworld/main.c @@ -0,0 +1,15 @@ +#include +#include +#include + +int main() { + uv_loop_t *loop = malloc(sizeof(uv_loop_t)); + uv_loop_init(loop); + + printf("Now quitting.\n"); + uv_run(loop, UV_RUN_DEFAULT); + + uv_loop_close(loop); + free(loop); + return 0; +} diff --git a/include/libuv/docs/code/idle-basic/main.c b/include/libuv/docs/code/idle-basic/main.c new file mode 100644 index 000000000..77ba31cf5 --- /dev/null +++ b/include/libuv/docs/code/idle-basic/main.c @@ -0,0 +1,24 @@ +#include +#include + +int64_t counter = 0; + +void wait_for_a_while(uv_idle_t* handle) { + counter++; + + if (counter >= 10e6) + uv_idle_stop(handle); +} + +int main() { + uv_idle_t idler; + + uv_idle_init(uv_default_loop(), &idler); + uv_idle_start(&idler, wait_for_a_while); + + printf("Idling...\n"); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + uv_loop_close(uv_default_loop()); + return 0; +} diff --git a/include/libuv/docs/code/idle-compute/main.c b/include/libuv/docs/code/idle-compute/main.c new file mode 100644 index 000000000..ff44b6946 --- /dev/null +++ b/include/libuv/docs/code/idle-compute/main.c @@ -0,0 +1,43 @@ +#include + +#include + +uv_loop_t *loop; +uv_fs_t stdin_watcher; +uv_idle_t idler; +char buffer[1024]; + +void crunch_away(uv_idle_t* handle) { + // Compute extra-terrestrial life + // fold proteins + // computer another digit of PI + // or similar + fprintf(stderr, "Computing PI...\n"); + // just to avoid overwhelming your terminal emulator + uv_idle_stop(handle); +} + +void on_type(uv_fs_t *req) { + if (stdin_watcher.result > 0) { + buffer[stdin_watcher.result] = '\0'; + printf("Typed %s\n", buffer); + + uv_buf_t buf = uv_buf_init(buffer, 1024); + uv_fs_read(loop, &stdin_watcher, 0, &buf, 1, -1, on_type); + uv_idle_start(&idler, crunch_away); + } + else if (stdin_watcher.result < 0) { + fprintf(stderr, "error opening file: %s\n", uv_strerror(req->result)); + } +} + +int main() { + loop = uv_default_loop(); + + uv_idle_init(loop, &idler); + + uv_buf_t buf = uv_buf_init(buffer, 1024); + uv_fs_read(loop, &stdin_watcher, 0, &buf, 1, -1, on_type); + uv_idle_start(&idler, crunch_away); + return uv_run(loop, UV_RUN_DEFAULT); +} diff --git a/include/libuv/docs/code/interfaces/main.c b/include/libuv/docs/code/interfaces/main.c new file mode 100644 index 000000000..cac12c266 --- /dev/null +++ b/include/libuv/docs/code/interfaces/main.c @@ -0,0 +1,33 @@ +#include +#include + +int main() { + char buf[512]; + uv_interface_address_t *info; + int count, i; + + uv_interface_addresses(&info, &count); + i = count; + + printf("Number of interfaces: %d\n", count); + while (i--) { + uv_interface_address_t interface = info[i]; + + printf("Name: %s\n", interface.name); + printf("Internal? %s\n", interface.is_internal ? "Yes" : "No"); + + if (interface.address.address4.sin_family == AF_INET) { + uv_ip4_name(&interface.address.address4, buf, sizeof(buf)); + printf("IPv4 address: %s\n", buf); + } + else if (interface.address.address4.sin_family == AF_INET6) { + uv_ip6_name(&interface.address.address6, buf, sizeof(buf)); + printf("IPv6 address: %s\n", buf); + } + + printf("\n"); + } + + uv_free_interface_addresses(info, count); + return 0; +} diff --git a/include/libuv/docs/code/locks/main.c b/include/libuv/docs/code/locks/main.c new file mode 100644 index 000000000..2b1f8ca7c --- /dev/null +++ b/include/libuv/docs/code/locks/main.c @@ -0,0 +1,57 @@ +#include +#include + +uv_barrier_t blocker; +uv_rwlock_t numlock; +int shared_num; + +void reader(void *n) +{ + int num = *(int *)n; + int i; + for (i = 0; i < 20; i++) { + uv_rwlock_rdlock(&numlock); + printf("Reader %d: acquired lock\n", num); + printf("Reader %d: shared num = %d\n", num, shared_num); + uv_rwlock_rdunlock(&numlock); + printf("Reader %d: released lock\n", num); + } + uv_barrier_wait(&blocker); +} + +void writer(void *n) +{ + int num = *(int *)n; + int i; + for (i = 0; i < 20; i++) { + uv_rwlock_wrlock(&numlock); + printf("Writer %d: acquired lock\n", num); + shared_num++; + printf("Writer %d: incremented shared num = %d\n", num, shared_num); + uv_rwlock_wrunlock(&numlock); + printf("Writer %d: released lock\n", num); + } + uv_barrier_wait(&blocker); +} + +int main() +{ + uv_barrier_init(&blocker, 4); + + shared_num = 0; + uv_rwlock_init(&numlock); + + uv_thread_t threads[3]; + + int thread_nums[] = {1, 2, 1}; + uv_thread_create(&threads[0], reader, &thread_nums[0]); + uv_thread_create(&threads[1], reader, &thread_nums[1]); + + uv_thread_create(&threads[2], writer, &thread_nums[2]); + + uv_barrier_wait(&blocker); + uv_barrier_destroy(&blocker); + + uv_rwlock_destroy(&numlock); + return 0; +} diff --git a/include/libuv/docs/code/multi-echo-server/hammer.js b/include/libuv/docs/code/multi-echo-server/hammer.js new file mode 100644 index 000000000..5df345b78 --- /dev/null +++ b/include/libuv/docs/code/multi-echo-server/hammer.js @@ -0,0 +1,20 @@ +var net = require('net'); + +var PHRASE = "hello world"; +var write = function(socket) { + socket.write(PHRASE, 'utf8'); +} + +for (var i = 0; i < 1000; i++) { +(function() { + var socket = net.connect(7000, 'localhost', function() { + socket.on('data', function(reply) { + if (reply.toString().indexOf(PHRASE) != 0) + console.error("Problem! '" + reply + "'" + " '" + PHRASE + "'"); + else + write(socket); + }); + write(socket); + }); +})(); +} diff --git a/include/libuv/docs/code/multi-echo-server/main.c b/include/libuv/docs/code/multi-echo-server/main.c new file mode 100644 index 000000000..b938a7dab --- /dev/null +++ b/include/libuv/docs/code/multi-echo-server/main.c @@ -0,0 +1,114 @@ +#include +#include +#include +#include +#include + +uv_loop_t *loop; + +struct child_worker { + uv_process_t req; + uv_process_options_t options; + uv_pipe_t pipe; +} *workers; + +int round_robin_counter; +int child_worker_count; + +uv_buf_t dummy_buf; +char worker_path[500]; + +void close_process_handle(uv_process_t *req, int64_t exit_status, int term_signal) { + fprintf(stderr, "Process exited with status %" PRId64 ", signal %d\n", exit_status, term_signal); + uv_close((uv_handle_t*) req, NULL); +} + +void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { + buf->base = malloc(suggested_size); + buf->len = suggested_size; +} + +void on_new_connection(uv_stream_t *server, int status) { + if (status == -1) { + // error! + return; + } + + uv_tcp_t *client = (uv_tcp_t*) malloc(sizeof(uv_tcp_t)); + uv_tcp_init(loop, client); + if (uv_accept(server, (uv_stream_t*) client) == 0) { + uv_write_t *write_req = (uv_write_t*) malloc(sizeof(uv_write_t)); + dummy_buf = uv_buf_init("a", 1); + struct child_worker *worker = &workers[round_robin_counter]; + uv_write2(write_req, (uv_stream_t*) &worker->pipe, &dummy_buf, 1, (uv_stream_t*) client, NULL); + round_robin_counter = (round_robin_counter + 1) % child_worker_count; + } + else { + uv_close((uv_handle_t*) client, NULL); + } +} + +void setup_workers() { + size_t path_size = 500; + uv_exepath(worker_path, &path_size); + strcpy(worker_path + (strlen(worker_path) - strlen("multi-echo-server")), "worker"); + fprintf(stderr, "Worker path: %s\n", worker_path); + + char* args[2]; + args[0] = worker_path; + args[1] = NULL; + + round_robin_counter = 0; + + // ... + + // launch same number of workers as number of CPUs + uv_cpu_info_t *info; + int cpu_count; + uv_cpu_info(&info, &cpu_count); + uv_free_cpu_info(info, cpu_count); + + child_worker_count = cpu_count; + + workers = calloc(cpu_count, sizeof(struct child_worker)); + while (cpu_count--) { + struct child_worker *worker = &workers[cpu_count]; + uv_pipe_init(loop, &worker->pipe, 1); + + uv_stdio_container_t child_stdio[3]; + child_stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; + child_stdio[0].data.stream = (uv_stream_t*) &worker->pipe; + child_stdio[1].flags = UV_IGNORE; + child_stdio[2].flags = UV_INHERIT_FD; + child_stdio[2].data.fd = 2; + + worker->options.stdio = child_stdio; + worker->options.stdio_count = 3; + + worker->options.exit_cb = close_process_handle; + worker->options.file = args[0]; + worker->options.args = args; + + uv_spawn(loop, &worker->req, &worker->options); + fprintf(stderr, "Started worker %d\n", worker->req.pid); + } +} + +int main() { + loop = uv_default_loop(); + + setup_workers(); + + uv_tcp_t server; + uv_tcp_init(loop, &server); + + struct sockaddr_in bind_addr; + uv_ip4_addr("0.0.0.0", 7000, &bind_addr); + uv_tcp_bind(&server, (const struct sockaddr *)&bind_addr, 0); + int r; + if ((r = uv_listen((uv_stream_t*) &server, 128, on_new_connection))) { + fprintf(stderr, "Listen error %s\n", uv_err_name(r)); + return 2; + } + return uv_run(loop, UV_RUN_DEFAULT); +} diff --git a/include/libuv/docs/code/multi-echo-server/worker.c b/include/libuv/docs/code/multi-echo-server/worker.c new file mode 100644 index 000000000..1c4657598 --- /dev/null +++ b/include/libuv/docs/code/multi-echo-server/worker.c @@ -0,0 +1,88 @@ +#include +#include +#include +#include +#include +#include + +uv_loop_t *loop; +uv_pipe_t queue; + +typedef struct { + uv_write_t req; + uv_buf_t buf; +} write_req_t; + +void free_write_req(uv_write_t *req) { + write_req_t *wr = (write_req_t*) req; + free(wr->buf.base); + free(wr); +} + +void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { + buf->base = malloc(suggested_size); + buf->len = suggested_size; +} + +void echo_write(uv_write_t *req, int status) { + if (status) { + fprintf(stderr, "Write error %s\n", uv_err_name(status)); + } + free_write_req(req); +} + +void echo_read(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) { + if (nread > 0) { + write_req_t *req = (write_req_t*) malloc(sizeof(write_req_t)); + req->buf = uv_buf_init(buf->base, nread); + uv_write((uv_write_t*) req, client, &req->buf, 1, echo_write); + return; + } + + if (nread < 0) { + if (nread != UV_EOF) + fprintf(stderr, "Read error %s\n", uv_err_name(nread)); + uv_close((uv_handle_t*) client, NULL); + } + + free(buf->base); +} + +void on_new_connection(uv_stream_t *q, ssize_t nread, const uv_buf_t *buf) { + if (nread < 0) { + if (nread != UV_EOF) + fprintf(stderr, "Read error %s\n", uv_err_name(nread)); + uv_close((uv_handle_t*) q, NULL); + return; + } + + uv_pipe_t *pipe = (uv_pipe_t*) q; + if (!uv_pipe_pending_count(pipe)) { + fprintf(stderr, "No pending count\n"); + return; + } + + uv_handle_type pending = uv_pipe_pending_type(pipe); + assert(pending == UV_TCP); + + uv_tcp_t *client = (uv_tcp_t*) malloc(sizeof(uv_tcp_t)); + uv_tcp_init(loop, client); + if (uv_accept(q, (uv_stream_t*) client) == 0) { + uv_os_fd_t fd; + uv_fileno((const uv_handle_t*) client, &fd); + fprintf(stderr, "Worker %d: Accepted fd %d\n", getpid(), fd); + uv_read_start((uv_stream_t*) client, alloc_buffer, echo_read); + } + else { + uv_close((uv_handle_t*) client, NULL); + } +} + +int main() { + loop = uv_default_loop(); + + uv_pipe_init(loop, &queue, 1 /* ipc */); + uv_pipe_open(&queue, 0); + uv_read_start((uv_stream_t*)&queue, alloc_buffer, on_new_connection); + return uv_run(loop, UV_RUN_DEFAULT); +} diff --git a/include/libuv/docs/code/onchange/main.c b/include/libuv/docs/code/onchange/main.c new file mode 100644 index 000000000..40bdaa52a --- /dev/null +++ b/include/libuv/docs/code/onchange/main.c @@ -0,0 +1,44 @@ +#include +#include + +#include + +uv_loop_t *loop; +const char *command; + +void run_command(uv_fs_event_t *handle, const char *filename, int events, int status) { + char path[1024]; + size_t size = 1023; + // Does not handle error if path is longer than 1023. + uv_fs_event_getpath(handle, path, &size); + path[size] = '\0'; + + fprintf(stderr, "Change detected in %s: ", path); + if (events & UV_RENAME) + fprintf(stderr, "renamed"); + if (events & UV_CHANGE) + fprintf(stderr, "changed"); + + fprintf(stderr, " %s\n", filename ? filename : ""); + system(command); +} + +int main(int argc, char **argv) { + if (argc <= 2) { + fprintf(stderr, "Usage: %s [file2 ...]\n", argv[0]); + return 1; + } + + loop = uv_default_loop(); + command = argv[1]; + + while (argc-- > 2) { + fprintf(stderr, "Adding watch on %s\n", argv[argc]); + uv_fs_event_t *fs_event_req = malloc(sizeof(uv_fs_event_t)); + uv_fs_event_init(loop, fs_event_req); + // The recursive flag watches subdirectories too. + uv_fs_event_start(fs_event_req, run_command, argv[argc], UV_FS_EVENT_RECURSIVE); + } + + return uv_run(loop, UV_RUN_DEFAULT); +} diff --git a/include/libuv/docs/code/pipe-echo-server/main.c b/include/libuv/docs/code/pipe-echo-server/main.c new file mode 100644 index 000000000..4f28fd03e --- /dev/null +++ b/include/libuv/docs/code/pipe-echo-server/main.c @@ -0,0 +1,94 @@ +#include +#include +#include +#include + +#ifdef _WIN32 +#define PIPENAME "\\\\?\\pipe\\echo.sock" +#else +#define PIPENAME "/tmp/echo.sock" +#endif + +uv_loop_t *loop; + +typedef struct { + uv_write_t req; + uv_buf_t buf; +} write_req_t; + +void free_write_req(uv_write_t *req) { + write_req_t *wr = (write_req_t*) req; + free(wr->buf.base); + free(wr); +} + +void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { + buf->base = malloc(suggested_size); + buf->len = suggested_size; +} + +void echo_write(uv_write_t *req, int status) { + if (status < 0) { + fprintf(stderr, "Write error %s\n", uv_err_name(status)); + } + free_write_req(req); +} + +void echo_read(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) { + if (nread > 0) { + write_req_t *req = (write_req_t*) malloc(sizeof(write_req_t)); + req->buf = uv_buf_init(buf->base, nread); + uv_write((uv_write_t*) req, client, &req->buf, 1, echo_write); + return; + } + + if (nread < 0) { + if (nread != UV_EOF) + fprintf(stderr, "Read error %s\n", uv_err_name(nread)); + uv_close((uv_handle_t*) client, NULL); + } + + free(buf->base); +} + +void on_new_connection(uv_stream_t *server, int status) { + if (status == -1) { + // error! + return; + } + + uv_pipe_t *client = (uv_pipe_t*) malloc(sizeof(uv_pipe_t)); + uv_pipe_init(loop, client, 0); + if (uv_accept(server, (uv_stream_t*) client) == 0) { + uv_read_start((uv_stream_t*) client, alloc_buffer, echo_read); + } + else { + uv_close((uv_handle_t*) client, NULL); + } +} + +void remove_sock(int sig) { + uv_fs_t req; + uv_fs_unlink(loop, &req, PIPENAME, NULL); + exit(0); +} + +int main() { + loop = uv_default_loop(); + + uv_pipe_t server; + uv_pipe_init(loop, &server, 0); + + signal(SIGINT, remove_sock); + + int r; + if ((r = uv_pipe_bind(&server, PIPENAME))) { + fprintf(stderr, "Bind error %s\n", uv_err_name(r)); + return 1; + } + if ((r = uv_listen((uv_stream_t*) &server, 128, on_new_connection))) { + fprintf(stderr, "Listen error %s\n", uv_err_name(r)); + return 2; + } + return uv_run(loop, UV_RUN_DEFAULT); +} diff --git a/include/libuv/docs/code/plugin/hello.c b/include/libuv/docs/code/plugin/hello.c new file mode 100644 index 000000000..7b2861d7d --- /dev/null +++ b/include/libuv/docs/code/plugin/hello.c @@ -0,0 +1,5 @@ +#include "plugin.h" + +void initialize() { + mfp_register("Hello World!"); +} diff --git a/include/libuv/docs/code/plugin/main.c b/include/libuv/docs/code/plugin/main.c new file mode 100644 index 000000000..06e581e63 --- /dev/null +++ b/include/libuv/docs/code/plugin/main.c @@ -0,0 +1,39 @@ +#include +#include +#include + +#include + +#include "plugin.h" + +typedef void (*init_plugin_function)(); + +void mfp_register(const char *name) { + fprintf(stderr, "Registered plugin \"%s\"\n", name); +} + +int main(int argc, char **argv) { + if (argc == 1) { + fprintf(stderr, "Usage: %s [plugin1] [plugin2] ...\n", argv[0]); + return 0; + } + + uv_lib_t *lib = (uv_lib_t*) malloc(sizeof(uv_lib_t)); + while (--argc) { + fprintf(stderr, "Loading %s\n", argv[argc]); + if (uv_dlopen(argv[argc], lib)) { + fprintf(stderr, "Error: %s\n", uv_dlerror(lib)); + continue; + } + + init_plugin_function init_plugin; + if (uv_dlsym(lib, "initialize", (void **) &init_plugin)) { + fprintf(stderr, "dlsym error: %s\n", uv_dlerror(lib)); + continue; + } + + init_plugin(); + } + + return 0; +} diff --git a/include/libuv/docs/code/plugin/plugin.h b/include/libuv/docs/code/plugin/plugin.h new file mode 100644 index 000000000..21f194e67 --- /dev/null +++ b/include/libuv/docs/code/plugin/plugin.h @@ -0,0 +1,7 @@ +#ifndef UVBOOK_PLUGIN_SYSTEM +#define UVBOOK_PLUGIN_SYSTEM + +// Plugin authors should use this to register their plugins with mfp. +void mfp_register(const char *name); + +#endif diff --git a/include/libuv/docs/code/proc-streams/main.c b/include/libuv/docs/code/proc-streams/main.c new file mode 100644 index 000000000..b8a65212e --- /dev/null +++ b/include/libuv/docs/code/proc-streams/main.c @@ -0,0 +1,49 @@ +#include +#include +#include + +#include + +uv_loop_t *loop; +uv_process_t child_req; +uv_process_options_t options; + +void on_exit(uv_process_t *req, int64_t exit_status, int term_signal) { + fprintf(stderr, "Process exited with status %" PRId64 ", signal %d\n", exit_status, term_signal); + uv_close((uv_handle_t*) req, NULL); +} + +int main() { + loop = uv_default_loop(); + + size_t size = 500; + char path[size]; + uv_exepath(path, &size); + strcpy(path + (strlen(path) - strlen("proc-streams")), "test"); + + char* args[2]; + args[0] = path; + args[1] = NULL; + + /* ... */ + + options.stdio_count = 3; + uv_stdio_container_t child_stdio[3]; + child_stdio[0].flags = UV_IGNORE; + child_stdio[1].flags = UV_IGNORE; + child_stdio[2].flags = UV_INHERIT_FD; + child_stdio[2].data.fd = 2; + options.stdio = child_stdio; + + options.exit_cb = on_exit; + options.file = args[0]; + options.args = args; + + int r; + if ((r = uv_spawn(loop, &child_req, &options))) { + fprintf(stderr, "%s\n", uv_strerror(r)); + return 1; + } + + return uv_run(loop, UV_RUN_DEFAULT); +} diff --git a/include/libuv/docs/code/proc-streams/test.c b/include/libuv/docs/code/proc-streams/test.c new file mode 100644 index 000000000..7c45c1fdc --- /dev/null +++ b/include/libuv/docs/code/proc-streams/test.c @@ -0,0 +1,8 @@ +#include + +int main() +{ + fprintf(stderr, "This is stderr\n"); + printf("This is stdout\n"); + return 0; +} diff --git a/include/libuv/docs/code/progress/main.c b/include/libuv/docs/code/progress/main.c new file mode 100644 index 000000000..5af01f143 --- /dev/null +++ b/include/libuv/docs/code/progress/main.c @@ -0,0 +1,47 @@ +#include +#include +#include + +#include + +uv_loop_t *loop; +uv_async_t async; + +double percentage; + +void fake_download(uv_work_t *req) { + int size = *((int*) req->data); + int downloaded = 0; + while (downloaded < size) { + percentage = downloaded*100.0/size; + async.data = (void*) &percentage; + uv_async_send(&async); + + sleep(1); + downloaded += (200+random())%1000; // can only download max 1000bytes/sec, + // but at least a 200; + } +} + +void after(uv_work_t *req, int status) { + fprintf(stderr, "Download complete\n"); + uv_close((uv_handle_t*) &async, NULL); +} + +void print_progress(uv_async_t *handle) { + double percentage = *((double*) handle->data); + fprintf(stderr, "Downloaded %.2f%%\n", percentage); +} + +int main() { + loop = uv_default_loop(); + + uv_work_t req; + int size = 10240; + req.data = (void*) &size; + + uv_async_init(loop, &async, print_progress); + uv_queue_work(loop, &req, fake_download, after); + + return uv_run(loop, UV_RUN_DEFAULT); +} diff --git a/include/libuv/docs/code/queue-cancel/main.c b/include/libuv/docs/code/queue-cancel/main.c new file mode 100644 index 000000000..3f7836cbf --- /dev/null +++ b/include/libuv/docs/code/queue-cancel/main.c @@ -0,0 +1,59 @@ +#include +#include +#include + +#include + +#define FIB_UNTIL 25 +uv_loop_t *loop; +uv_work_t fib_reqs[FIB_UNTIL]; + +long fib_(long t) { + if (t == 0 || t == 1) + return 1; + else + return fib_(t-1) + fib_(t-2); +} + +void fib(uv_work_t *req) { + int n = *(int *) req->data; + if (random() % 2) + sleep(1); + else + sleep(3); + long fib = fib_(n); + fprintf(stderr, "%dth fibonacci is %lu\n", n, fib); +} + +void after_fib(uv_work_t *req, int status) { + if (status == UV_ECANCELED) + fprintf(stderr, "Calculation of %d cancelled.\n", *(int *) req->data); +} + +void signal_handler(uv_signal_t *req, int signum) +{ + printf("Signal received!\n"); + int i; + for (i = 0; i < FIB_UNTIL; i++) { + uv_cancel((uv_req_t*) &fib_reqs[i]); + } + uv_signal_stop(req); +} + +int main() { + loop = uv_default_loop(); + + int data[FIB_UNTIL]; + int i; + for (i = 0; i < FIB_UNTIL; i++) { + data[i] = i; + fib_reqs[i].data = (void *) &data[i]; + uv_queue_work(loop, &fib_reqs[i], fib, after_fib); + } + + uv_signal_t sig; + uv_signal_init(loop, &sig); + uv_signal_start(&sig, signal_handler, SIGINT); + + return uv_run(loop, UV_RUN_DEFAULT); +} diff --git a/include/libuv/docs/code/queue-work/main.c b/include/libuv/docs/code/queue-work/main.c new file mode 100644 index 000000000..55675ea02 --- /dev/null +++ b/include/libuv/docs/code/queue-work/main.c @@ -0,0 +1,44 @@ +#include +#include +#include + +#include + +#define FIB_UNTIL 25 +uv_loop_t *loop; + +long fib_(long t) { + if (t == 0 || t == 1) + return 1; + else + return fib_(t-1) + fib_(t-2); +} + +void fib(uv_work_t *req) { + int n = *(int *) req->data; + if (random() % 2) + sleep(1); + else + sleep(3); + long fib = fib_(n); + fprintf(stderr, "%dth fibonacci is %lu\n", n, fib); +} + +void after_fib(uv_work_t *req, int status) { + fprintf(stderr, "Done calculating %dth fibonacci\n", *(int *) req->data); +} + +int main() { + loop = uv_default_loop(); + + int data[FIB_UNTIL]; + uv_work_t req[FIB_UNTIL]; + int i; + for (i = 0; i < FIB_UNTIL; i++) { + data[i] = i; + req[i].data = (void *) &data[i]; + uv_queue_work(loop, &req[i], fib, after_fib); + } + + return uv_run(loop, UV_RUN_DEFAULT); +} diff --git a/include/libuv/docs/code/ref-timer/main.c b/include/libuv/docs/code/ref-timer/main.c new file mode 100644 index 000000000..ad7c8295f --- /dev/null +++ b/include/libuv/docs/code/ref-timer/main.c @@ -0,0 +1,29 @@ +#include + +#include + +uv_loop_t *loop; +uv_timer_t gc_req; +uv_timer_t fake_job_req; + +void gc(uv_timer_t *handle) { + fprintf(stderr, "Freeing unused objects\n"); +} + +void fake_job(uv_timer_t *handle) { + fprintf(stdout, "Fake job done\n"); +} + +int main() { + loop = uv_default_loop(); + + uv_timer_init(loop, &gc_req); + uv_unref((uv_handle_t*) &gc_req); + + uv_timer_start(&gc_req, gc, 0, 2000); + + // could actually be a TCP download or something + uv_timer_init(loop, &fake_job_req); + uv_timer_start(&fake_job_req, fake_job, 9000, 0); + return uv_run(loop, UV_RUN_DEFAULT); +} diff --git a/include/libuv/docs/code/signal/main.c b/include/libuv/docs/code/signal/main.c new file mode 100644 index 000000000..1b982c5a7 --- /dev/null +++ b/include/libuv/docs/code/signal/main.c @@ -0,0 +1,66 @@ +#include +#include +#include +#include + +uv_loop_t* create_loop() +{ + uv_loop_t *loop = malloc(sizeof(uv_loop_t)); + if (loop) { + uv_loop_init(loop); + } + return loop; +} + +void signal_handler(uv_signal_t *handle, int signum) +{ + printf("Signal received: %d\n", signum); + uv_signal_stop(handle); +} + +// two signal handlers in one loop +void thread1_worker(void *userp) +{ + uv_loop_t *loop1 = create_loop(); + + uv_signal_t sig1a, sig1b; + uv_signal_init(loop1, &sig1a); + uv_signal_start(&sig1a, signal_handler, SIGUSR1); + + uv_signal_init(loop1, &sig1b); + uv_signal_start(&sig1b, signal_handler, SIGUSR1); + + uv_run(loop1, UV_RUN_DEFAULT); +} + +// two signal handlers, each in its own loop +void thread2_worker(void *userp) +{ + uv_loop_t *loop2 = create_loop(); + uv_loop_t *loop3 = create_loop(); + + uv_signal_t sig2; + uv_signal_init(loop2, &sig2); + uv_signal_start(&sig2, signal_handler, SIGUSR1); + + uv_signal_t sig3; + uv_signal_init(loop3, &sig3); + uv_signal_start(&sig3, signal_handler, SIGUSR1); + + while (uv_run(loop2, UV_RUN_NOWAIT) || uv_run(loop3, UV_RUN_NOWAIT)) { + } +} + +int main() +{ + printf("PID %d\n", getpid()); + + uv_thread_t thread1, thread2; + + uv_thread_create(&thread1, thread1_worker, 0); + uv_thread_create(&thread2, thread2_worker, 0); + + uv_thread_join(&thread1); + uv_thread_join(&thread2); + return 0; +} diff --git a/include/libuv/docs/code/spawn/main.c b/include/libuv/docs/code/spawn/main.c new file mode 100644 index 000000000..dedfe00c0 --- /dev/null +++ b/include/libuv/docs/code/spawn/main.c @@ -0,0 +1,36 @@ +#include +#include + +#include + +uv_loop_t *loop; +uv_process_t child_req; +uv_process_options_t options; + +void on_exit(uv_process_t *req, int64_t exit_status, int term_signal) { + fprintf(stderr, "Process exited with status %" PRId64 ", signal %d\n", exit_status, term_signal); + uv_close((uv_handle_t*) req, NULL); +} + +int main() { + loop = uv_default_loop(); + + char* args[3]; + args[0] = "mkdir"; + args[1] = "test-dir"; + args[2] = NULL; + + options.exit_cb = on_exit; + options.file = "mkdir"; + options.args = args; + + int r; + if ((r = uv_spawn(loop, &child_req, &options))) { + fprintf(stderr, "%s\n", uv_strerror(r)); + return 1; + } else { + fprintf(stderr, "Launched process with ID %d\n", child_req.pid); + } + + return uv_run(loop, UV_RUN_DEFAULT); +} diff --git a/include/libuv/docs/code/tcp-echo-server/main.c b/include/libuv/docs/code/tcp-echo-server/main.c new file mode 100644 index 000000000..5d7b49937 --- /dev/null +++ b/include/libuv/docs/code/tcp-echo-server/main.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include + +#define DEFAULT_PORT 7000 +#define DEFAULT_BACKLOG 128 + +uv_loop_t *loop; +struct sockaddr_in addr; + +typedef struct { + uv_write_t req; + uv_buf_t buf; +} write_req_t; + +void free_write_req(uv_write_t *req) { + write_req_t *wr = (write_req_t*) req; + free(wr->buf.base); + free(wr); +} + +void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { + buf->base = (char*) malloc(suggested_size); + buf->len = suggested_size; +} + +void on_close(uv_handle_t* handle) { + free(handle); +} + +void echo_write(uv_write_t *req, int status) { + if (status) { + fprintf(stderr, "Write error %s\n", uv_strerror(status)); + } + free_write_req(req); +} + +void echo_read(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) { + if (nread > 0) { + write_req_t *req = (write_req_t*) malloc(sizeof(write_req_t)); + req->buf = uv_buf_init(buf->base, nread); + uv_write((uv_write_t*) req, client, &req->buf, 1, echo_write); + return; + } + if (nread < 0) { + if (nread != UV_EOF) + fprintf(stderr, "Read error %s\n", uv_err_name(nread)); + uv_close((uv_handle_t*) client, on_close); + } + + free(buf->base); +} + +void on_new_connection(uv_stream_t *server, int status) { + if (status < 0) { + fprintf(stderr, "New connection error %s\n", uv_strerror(status)); + // error! + return; + } + + uv_tcp_t *client = (uv_tcp_t*) malloc(sizeof(uv_tcp_t)); + uv_tcp_init(loop, client); + if (uv_accept(server, (uv_stream_t*) client) == 0) { + uv_read_start((uv_stream_t*) client, alloc_buffer, echo_read); + } + else { + uv_close((uv_handle_t*) client, on_close); + } +} + +int main() { + loop = uv_default_loop(); + + uv_tcp_t server; + uv_tcp_init(loop, &server); + + uv_ip4_addr("0.0.0.0", DEFAULT_PORT, &addr); + + uv_tcp_bind(&server, (const struct sockaddr*)&addr, 0); + int r = uv_listen((uv_stream_t*) &server, DEFAULT_BACKLOG, on_new_connection); + if (r) { + fprintf(stderr, "Listen error %s\n", uv_strerror(r)); + return 1; + } + return uv_run(loop, UV_RUN_DEFAULT); +} diff --git a/include/libuv/docs/code/thread-create/main.c b/include/libuv/docs/code/thread-create/main.c new file mode 100644 index 000000000..70224c1e2 --- /dev/null +++ b/include/libuv/docs/code/thread-create/main.c @@ -0,0 +1,36 @@ +#include +#include + +#include + +void hare(void *arg) { + int tracklen = *((int *) arg); + while (tracklen) { + tracklen--; + sleep(1); + fprintf(stderr, "Hare ran another step\n"); + } + fprintf(stderr, "Hare done running!\n"); +} + +void tortoise(void *arg) { + int tracklen = *((int *) arg); + while (tracklen) { + tracklen--; + fprintf(stderr, "Tortoise ran another step\n"); + sleep(3); + } + fprintf(stderr, "Tortoise done running!\n"); +} + +int main() { + int tracklen = 10; + uv_thread_t hare_id; + uv_thread_t tortoise_id; + uv_thread_create(&hare_id, hare, &tracklen); + uv_thread_create(&tortoise_id, tortoise, &tracklen); + + uv_thread_join(&hare_id); + uv_thread_join(&tortoise_id); + return 0; +} diff --git a/include/libuv/docs/code/tty-gravity/main.c b/include/libuv/docs/code/tty-gravity/main.c new file mode 100644 index 000000000..0a8d6b29f --- /dev/null +++ b/include/libuv/docs/code/tty-gravity/main.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include + +uv_loop_t *loop; +uv_tty_t tty; +uv_timer_t tick; +uv_write_t write_req; +int width, height; +int pos = 0; +char *message = " Hello TTY "; + +void update(uv_timer_t *req) { + char data[500]; + + uv_buf_t buf; + buf.base = data; + buf.len = sprintf(data, "\033[2J\033[H\033[%dB\033[%luC\033[42;37m%s", + pos, + (unsigned long) (width-strlen(message))/2, + message); + uv_write(&write_req, (uv_stream_t*) &tty, &buf, 1, NULL); + + pos++; + if (pos > height) { + uv_tty_reset_mode(); + uv_timer_stop(&tick); + } +} + +int main() { + loop = uv_default_loop(); + + uv_tty_init(loop, &tty, STDOUT_FILENO, 0); + uv_tty_set_mode(&tty, 0); + + if (uv_tty_get_winsize(&tty, &width, &height)) { + fprintf(stderr, "Could not get TTY information\n"); + uv_tty_reset_mode(); + return 1; + } + + fprintf(stderr, "Width %d, height %d\n", width, height); + uv_timer_init(loop, &tick); + uv_timer_start(&tick, update, 200, 200); + return uv_run(loop, UV_RUN_DEFAULT); +} diff --git a/include/libuv/docs/code/tty/main.c b/include/libuv/docs/code/tty/main.c new file mode 100644 index 000000000..d44ec62ce --- /dev/null +++ b/include/libuv/docs/code/tty/main.c @@ -0,0 +1,29 @@ +#include +#include +#include +#include + +uv_loop_t *loop; +uv_tty_t tty; +int main() { + loop = uv_default_loop(); + + uv_tty_init(loop, &tty, STDOUT_FILENO, 0); + uv_tty_set_mode(&tty, UV_TTY_MODE_NORMAL); + + if (uv_guess_handle(1) == UV_TTY) { + uv_write_t req; + uv_buf_t buf; + buf.base = "\033[41;37m"; + buf.len = strlen(buf.base); + uv_write(&req, (uv_stream_t*) &tty, &buf, 1, NULL); + } + + uv_write_t req; + uv_buf_t buf; + buf.base = "Hello TTY\n"; + buf.len = strlen(buf.base); + uv_write(&req, (uv_stream_t*) &tty, &buf, 1, NULL); + uv_tty_reset_mode(); + return uv_run(loop, UV_RUN_DEFAULT); +} diff --git a/include/libuv/docs/code/udp-dhcp/main.c b/include/libuv/docs/code/udp-dhcp/main.c new file mode 100644 index 000000000..fc2ca0c8a --- /dev/null +++ b/include/libuv/docs/code/udp-dhcp/main.c @@ -0,0 +1,127 @@ +#include +#include +#include +#include + +#include + +uv_loop_t *loop; +uv_udp_t send_socket; +uv_udp_t recv_socket; + +void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { + buf->base = malloc(suggested_size); + buf->len = suggested_size; +} + +void on_read(uv_udp_t *req, ssize_t nread, const uv_buf_t *buf, const struct sockaddr *addr, unsigned flags) { + if (nread < 0) { + fprintf(stderr, "Read error %s\n", uv_err_name(nread)); + uv_close((uv_handle_t*) req, NULL); + free(buf->base); + return; + } + + char sender[17] = { 0 }; + uv_ip4_name((const struct sockaddr_in*) addr, sender, 16); + fprintf(stderr, "Recv from %s\n", sender); + + // ... DHCP specific code + unsigned int *as_integer = (unsigned int*)buf->base; + unsigned int ipbin = ntohl(as_integer[4]); + unsigned char ip[4] = {0}; + int i; + for (i = 0; i < 4; i++) + ip[i] = (ipbin >> i*8) & 0xff; + fprintf(stderr, "Offered IP %d.%d.%d.%d\n", ip[3], ip[2], ip[1], ip[0]); + + free(buf->base); + uv_udp_recv_stop(req); +} + +uv_buf_t make_discover_msg() { + uv_buf_t buffer; + alloc_buffer(NULL, 256, &buffer); + memset(buffer.base, 0, buffer.len); + + // BOOTREQUEST + buffer.base[0] = 0x1; + // HTYPE ethernet + buffer.base[1] = 0x1; + // HLEN + buffer.base[2] = 0x6; + // HOPS + buffer.base[3] = 0x0; + // XID 4 bytes + buffer.base[4] = (unsigned int) random(); + // SECS + buffer.base[8] = 0x0; + // FLAGS + buffer.base[10] = 0x80; + // CIADDR 12-15 is all zeros + // YIADDR 16-19 is all zeros + // SIADDR 20-23 is all zeros + // GIADDR 24-27 is all zeros + // CHADDR 28-43 is the MAC address, use your own + buffer.base[28] = 0xe4; + buffer.base[29] = 0xce; + buffer.base[30] = 0x8f; + buffer.base[31] = 0x13; + buffer.base[32] = 0xf6; + buffer.base[33] = 0xd4; + // SNAME 64 bytes zero + // FILE 128 bytes zero + // OPTIONS + // - magic cookie + buffer.base[236] = 99; + buffer.base[237] = 130; + buffer.base[238] = 83; + buffer.base[239] = 99; + + // DHCP Message type + buffer.base[240] = 53; + buffer.base[241] = 1; + buffer.base[242] = 1; // DHCPDISCOVER + + // DHCP Parameter request list + buffer.base[243] = 55; + buffer.base[244] = 4; + buffer.base[245] = 1; + buffer.base[246] = 3; + buffer.base[247] = 15; + buffer.base[248] = 6; + + return buffer; +} + +void on_send(uv_udp_send_t *req, int status) { + if (status) { + fprintf(stderr, "Send error %s\n", uv_strerror(status)); + return; + } +} + +int main() { + loop = uv_default_loop(); + + uv_udp_init(loop, &recv_socket); + struct sockaddr_in recv_addr; + uv_ip4_addr("0.0.0.0", 68, &recv_addr); + uv_udp_bind(&recv_socket, (const struct sockaddr *)&recv_addr, UV_UDP_REUSEADDR); + uv_udp_recv_start(&recv_socket, alloc_buffer, on_read); + + uv_udp_init(loop, &send_socket); + struct sockaddr_in broadcast_addr; + uv_ip4_addr("0.0.0.0", 0, &broadcast_addr); + uv_udp_bind(&send_socket, (const struct sockaddr *)&broadcast_addr, 0); + uv_udp_set_broadcast(&send_socket, 1); + + uv_udp_send_t send_req; + uv_buf_t discover_msg = make_discover_msg(); + + struct sockaddr_in send_addr; + uv_ip4_addr("255.255.255.255", 67, &send_addr); + uv_udp_send(&send_req, &send_socket, &discover_msg, 1, (const struct sockaddr *)&send_addr, on_send); + + return uv_run(loop, UV_RUN_DEFAULT); +} diff --git a/include/libuv/docs/code/uvcat/main.c b/include/libuv/docs/code/uvcat/main.c new file mode 100644 index 000000000..b03b09449 --- /dev/null +++ b/include/libuv/docs/code/uvcat/main.c @@ -0,0 +1,63 @@ +#include +#include +#include +#include +#include + +void on_read(uv_fs_t *req); + +uv_fs_t open_req; +uv_fs_t read_req; +uv_fs_t write_req; + +static char buffer[1024]; + +static uv_buf_t iov; + +void on_write(uv_fs_t *req) { + if (req->result < 0) { + fprintf(stderr, "Write error: %s\n", uv_strerror((int)req->result)); + } + else { + uv_fs_read(uv_default_loop(), &read_req, open_req.result, &iov, 1, -1, on_read); + } +} + +void on_read(uv_fs_t *req) { + if (req->result < 0) { + fprintf(stderr, "Read error: %s\n", uv_strerror(req->result)); + } + else if (req->result == 0) { + uv_fs_t close_req; + // synchronous + uv_fs_close(uv_default_loop(), &close_req, open_req.result, NULL); + } + else if (req->result > 0) { + iov.len = req->result; + uv_fs_write(uv_default_loop(), &write_req, 1, &iov, 1, -1, on_write); + } +} + +void on_open(uv_fs_t *req) { + // The request passed to the callback is the same as the one the call setup + // function was passed. + assert(req == &open_req); + if (req->result >= 0) { + iov = uv_buf_init(buffer, sizeof(buffer)); + uv_fs_read(uv_default_loop(), &read_req, req->result, + &iov, 1, -1, on_read); + } + else { + fprintf(stderr, "error opening file: %s\n", uv_strerror((int)req->result)); + } +} + +int main(int argc, char **argv) { + uv_fs_open(uv_default_loop(), &open_req, argv[1], O_RDONLY, 0, on_open); + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + uv_fs_req_cleanup(&open_req); + uv_fs_req_cleanup(&read_req); + uv_fs_req_cleanup(&write_req); + return 0; +} diff --git a/include/libuv/docs/code/uvstop/main.c b/include/libuv/docs/code/uvstop/main.c new file mode 100644 index 000000000..7aa53b761 --- /dev/null +++ b/include/libuv/docs/code/uvstop/main.c @@ -0,0 +1,33 @@ +#include +#include + +int64_t counter = 0; + +void idle_cb(uv_idle_t *handle) { + printf("Idle callback\n"); + counter++; + + if (counter >= 5) { + uv_stop(uv_default_loop()); + printf("uv_stop() called\n"); + } +} + +void prep_cb(uv_prepare_t *handle) { + printf("Prep callback\n"); +} + +int main() { + uv_idle_t idler; + uv_prepare_t prep; + + uv_idle_init(uv_default_loop(), &idler); + uv_idle_start(&idler, idle_cb); + + uv_prepare_init(uv_default_loop(), &prep); + uv_prepare_start(&prep, prep_cb); + + uv_run(uv_default_loop(), UV_RUN_DEFAULT); + + return 0; +} diff --git a/include/libuv/docs/code/uvtee/main.c b/include/libuv/docs/code/uvtee/main.c new file mode 100644 index 000000000..6216c2eb4 --- /dev/null +++ b/include/libuv/docs/code/uvtee/main.c @@ -0,0 +1,80 @@ +#include +#include +#include +#include +#include + +#include + +typedef struct { + uv_write_t req; + uv_buf_t buf; +} write_req_t; + +uv_loop_t *loop; +uv_pipe_t stdin_pipe; +uv_pipe_t stdout_pipe; +uv_pipe_t file_pipe; + +void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { + *buf = uv_buf_init((char*) malloc(suggested_size), suggested_size); +} + +void free_write_req(uv_write_t *req) { + write_req_t *wr = (write_req_t*) req; + free(wr->buf.base); + free(wr); +} + +void on_stdout_write(uv_write_t *req, int status) { + free_write_req(req); +} + +void on_file_write(uv_write_t *req, int status) { + free_write_req(req); +} + +void write_data(uv_stream_t *dest, size_t size, uv_buf_t buf, uv_write_cb cb) { + write_req_t *req = (write_req_t*) malloc(sizeof(write_req_t)); + req->buf = uv_buf_init((char*) malloc(size), size); + memcpy(req->buf.base, buf.base, size); + uv_write((uv_write_t*) req, (uv_stream_t*)dest, &req->buf, 1, cb); +} + +void read_stdin(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) { + if (nread < 0){ + if (nread == UV_EOF){ + // end of file + uv_close((uv_handle_t *)&stdin_pipe, NULL); + uv_close((uv_handle_t *)&stdout_pipe, NULL); + uv_close((uv_handle_t *)&file_pipe, NULL); + } + } else if (nread > 0) { + write_data((uv_stream_t *)&stdout_pipe, nread, *buf, on_stdout_write); + write_data((uv_stream_t *)&file_pipe, nread, *buf, on_file_write); + } + + // OK to free buffer as write_data copies it. + if (buf->base) + free(buf->base); +} + +int main(int argc, char **argv) { + loop = uv_default_loop(); + + uv_pipe_init(loop, &stdin_pipe, 0); + uv_pipe_open(&stdin_pipe, 0); + + uv_pipe_init(loop, &stdout_pipe, 0); + uv_pipe_open(&stdout_pipe, 1); + + uv_fs_t file_req; + int fd = uv_fs_open(loop, &file_req, argv[1], O_CREAT | O_RDWR, 0644, NULL); + uv_pipe_init(loop, &file_pipe, 0); + uv_pipe_open(&file_pipe, fd); + + uv_read_start((uv_stream_t*)&stdin_pipe, alloc_buffer, read_stdin); + + uv_run(loop, UV_RUN_DEFAULT); + return 0; +} diff --git a/include/libuv/docs/code/uvwget/main.c b/include/libuv/docs/code/uvwget/main.c new file mode 100644 index 000000000..40186241b --- /dev/null +++ b/include/libuv/docs/code/uvwget/main.c @@ -0,0 +1,166 @@ +#include +#include +#include +#include +#include + +uv_loop_t *loop; +CURLM *curl_handle; +uv_timer_t timeout; + +typedef struct curl_context_s { + uv_poll_t poll_handle; + curl_socket_t sockfd; +} curl_context_t; + +curl_context_t *create_curl_context(curl_socket_t sockfd) { + curl_context_t *context; + + context = (curl_context_t*) malloc(sizeof *context); + + context->sockfd = sockfd; + + int r = uv_poll_init_socket(loop, &context->poll_handle, sockfd); + assert(r == 0); + context->poll_handle.data = context; + + return context; +} + +void curl_close_cb(uv_handle_t *handle) { + curl_context_t *context = (curl_context_t*) handle->data; + free(context); +} + +void destroy_curl_context(curl_context_t *context) { + uv_close((uv_handle_t*) &context->poll_handle, curl_close_cb); +} + + +void add_download(const char *url, int num) { + char filename[50]; + sprintf(filename, "%d.download", num); + FILE *file; + + file = fopen(filename, "w"); + if (file == NULL) { + fprintf(stderr, "Error opening %s\n", filename); + return; + } + + CURL *handle = curl_easy_init(); + curl_easy_setopt(handle, CURLOPT_WRITEDATA, file); + curl_easy_setopt(handle, CURLOPT_URL, url); + curl_multi_add_handle(curl_handle, handle); + fprintf(stderr, "Added download %s -> %s\n", url, filename); +} + +void check_multi_info(void) { + char *done_url; + CURLMsg *message; + int pending; + + while ((message = curl_multi_info_read(curl_handle, &pending))) { + switch (message->msg) { + case CURLMSG_DONE: + curl_easy_getinfo(message->easy_handle, CURLINFO_EFFECTIVE_URL, + &done_url); + printf("%s DONE\n", done_url); + + curl_multi_remove_handle(curl_handle, message->easy_handle); + curl_easy_cleanup(message->easy_handle); + break; + + default: + fprintf(stderr, "CURLMSG default\n"); + abort(); + } + } +} + +void curl_perform(uv_poll_t *req, int status, int events) { + uv_timer_stop(&timeout); + int running_handles; + int flags = 0; + if (status < 0) flags = CURL_CSELECT_ERR; + if (!status && events & UV_READABLE) flags |= CURL_CSELECT_IN; + if (!status && events & UV_WRITABLE) flags |= CURL_CSELECT_OUT; + + curl_context_t *context; + + context = (curl_context_t*)req; + + curl_multi_socket_action(curl_handle, context->sockfd, flags, &running_handles); + check_multi_info(); +} + +void on_timeout(uv_timer_t *req) { + int running_handles; + curl_multi_socket_action(curl_handle, CURL_SOCKET_TIMEOUT, 0, &running_handles); + check_multi_info(); +} + +void start_timeout(CURLM *multi, long timeout_ms, void *userp) { + if (timeout_ms <= 0) + timeout_ms = 1; /* 0 means directly call socket_action, but we'll do it in a bit */ + uv_timer_start(&timeout, on_timeout, timeout_ms, 0); +} + +int handle_socket(CURL *easy, curl_socket_t s, int action, void *userp, void *socketp) { + curl_context_t *curl_context; + if (action == CURL_POLL_IN || action == CURL_POLL_OUT) { + if (socketp) { + curl_context = (curl_context_t*) socketp; + } + else { + curl_context = create_curl_context(s); + curl_multi_assign(curl_handle, s, (void *) curl_context); + } + } + + switch (action) { + case CURL_POLL_IN: + uv_poll_start(&curl_context->poll_handle, UV_READABLE, curl_perform); + break; + case CURL_POLL_OUT: + uv_poll_start(&curl_context->poll_handle, UV_WRITABLE, curl_perform); + break; + case CURL_POLL_REMOVE: + if (socketp) { + uv_poll_stop(&((curl_context_t*)socketp)->poll_handle); + destroy_curl_context((curl_context_t*) socketp); + curl_multi_assign(curl_handle, s, NULL); + } + break; + default: + abort(); + } + + return 0; +} + +int main(int argc, char **argv) { + loop = uv_default_loop(); + + if (argc <= 1) + return 0; + + if (curl_global_init(CURL_GLOBAL_ALL)) { + fprintf(stderr, "Could not init cURL\n"); + return 1; + } + + uv_timer_init(loop, &timeout); + + curl_handle = curl_multi_init(); + curl_multi_setopt(curl_handle, CURLMOPT_SOCKETFUNCTION, handle_socket); + curl_multi_setopt(curl_handle, CURLMOPT_TIMERFUNCTION, start_timeout); + + while (argc-- > 1) { + add_download(argv[argc], argc); + } + + uv_run(loop, UV_RUN_DEFAULT); + curl_multi_cleanup(curl_handle); + return 0; +} diff --git a/include/libuv/docs/make.bat b/include/libuv/docs/make.bat new file mode 100644 index 000000000..aa7089ab5 --- /dev/null +++ b/include/libuv/docs/make.bat @@ -0,0 +1,243 @@ +@ECHO OFF + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set BUILDDIR=build +set SRCDIR=src +set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% %SRCDIR% +set I18NSPHINXOPTS=%SPHINXOPTS% %SRCDIR% +if NOT "%PAPER%" == "" ( + set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% + set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% +) + +if "%1" == "" goto help + +if "%1" == "help" ( + :help + echo.Please use `make ^` where ^ is one of + echo. html to make standalone HTML files + echo. dirhtml to make HTML files named index.html in directories + echo. singlehtml to make a single large HTML file + echo. pickle to make pickle files + echo. json to make JSON files + echo. htmlhelp to make HTML files and a HTML help project + echo. qthelp to make HTML files and a qthelp project + echo. devhelp to make HTML files and a Devhelp project + echo. epub to make an epub + echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter + echo. text to make text files + echo. man to make manual pages + echo. texinfo to make Texinfo files + echo. gettext to make PO message catalogs + echo. changes to make an overview over all changed/added/deprecated items + echo. xml to make Docutils-native XML files + echo. pseudoxml to make pseudoxml-XML files for display purposes + echo. linkcheck to check all external links for integrity + echo. doctest to run all doctests embedded in the documentation if enabled + goto end +) + +if "%1" == "clean" ( + for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i + del /q /s %BUILDDIR%\* + goto end +) + + +%SPHINXBUILD% 2> nul +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "html" ( + %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/html. + goto end +) + +if "%1" == "dirhtml" ( + %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. + goto end +) + +if "%1" == "singlehtml" ( + %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. + goto end +) + +if "%1" == "pickle" ( + %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the pickle files. + goto end +) + +if "%1" == "json" ( + %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can process the JSON files. + goto end +) + +if "%1" == "htmlhelp" ( + %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run HTML Help Workshop with the ^ +.hhp project file in %BUILDDIR%/htmlhelp. + goto end +) + +if "%1" == "qthelp" ( + %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; now you can run "qcollectiongenerator" with the ^ +.qhcp project file in %BUILDDIR%/qthelp, like this: + echo.^> qcollectiongenerator %BUILDDIR%\qthelp\libuv.qhcp + echo.To view the help file: + echo.^> assistant -collectionFile %BUILDDIR%\qthelp\libuv.ghc + goto end +) + +if "%1" == "devhelp" ( + %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. + goto end +) + +if "%1" == "epub" ( + %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The epub file is in %BUILDDIR%/epub. + goto end +) + +if "%1" == "latex" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + if errorlevel 1 exit /b 1 + echo. + echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdf" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf + cd %BUILDDIR%/.. + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "latexpdfja" ( + %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex + cd %BUILDDIR%/latex + make all-pdf-ja + cd %BUILDDIR%/.. + echo. + echo.Build finished; the PDF files are in %BUILDDIR%/latex. + goto end +) + +if "%1" == "text" ( + %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The text files are in %BUILDDIR%/text. + goto end +) + +if "%1" == "man" ( + %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The manual pages are in %BUILDDIR%/man. + goto end +) + +if "%1" == "texinfo" ( + %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. + goto end +) + +if "%1" == "gettext" ( + %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The message catalogs are in %BUILDDIR%/locale. + goto end +) + +if "%1" == "changes" ( + %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes + if errorlevel 1 exit /b 1 + echo. + echo.The overview file is in %BUILDDIR%/changes. + goto end +) + +if "%1" == "linkcheck" ( + %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck + if errorlevel 1 exit /b 1 + echo. + echo.Link check complete; look for any errors in the above output ^ +or in %BUILDDIR%/linkcheck/output.txt. + goto end +) + +if "%1" == "doctest" ( + %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest + if errorlevel 1 exit /b 1 + echo. + echo.Testing of doctests in the sources finished, look at the ^ +results in %BUILDDIR%/doctest/output.txt. + goto end +) + +if "%1" == "xml" ( + %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The XML files are in %BUILDDIR%/xml. + goto end +) + +if "%1" == "pseudoxml" ( + %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml + if errorlevel 1 exit /b 1 + echo. + echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. + goto end +) + +:end diff --git a/include/libuv/docs/requirements.txt b/include/libuv/docs/requirements.txt new file mode 100644 index 000000000..8386e0178 --- /dev/null +++ b/include/libuv/docs/requirements.txt @@ -0,0 +1,42 @@ +# primary +Sphinx==3.5.4 + +# dependencies +alabaster==0.7.12 +appdirs==1.4.3 +Babel==2.9.0 +CacheControl==0.12.6 +certifi==2019.11.28 +chardet==3.0.4 +colorama==0.4.3 +contextlib2==0.6.0 +distlib==0.3.0 +distro==1.4.0 +docutils==0.16 +html5lib==1.0.1 +idna==2.8 +imagesize==1.2.0 +ipaddr==2.2.0 +Jinja2==2.11.3 +lockfile==0.12.2 +MarkupSafe==1.1.1 +msgpack==0.6.2 +packaging==20.3 +pep517==0.8.2 +progress==1.5 +Pygments==2.8.1 +pyparsing==2.4.6 +pytoml==0.1.21 +pytz==2021.1 +requests==2.22.0 +retrying==1.3.3 +six==1.14.0 +snowballstemmer==2.1.0 +sphinxcontrib-applehelp==1.0.2 +sphinxcontrib-devhelp==1.0.2 +sphinxcontrib-htmlhelp==1.0.3 +sphinxcontrib-jsmath==1.0.1 +sphinxcontrib-qthelp==1.0.3 +sphinxcontrib-serializinghtml==1.1.4 +urllib3==1.25.8 +webencodings==0.5.1 diff --git a/include/libuv/docs/src/api.rst b/include/libuv/docs/src/api.rst new file mode 100644 index 000000000..c8e837dd1 --- /dev/null +++ b/include/libuv/docs/src/api.rst @@ -0,0 +1,36 @@ +.. _api: + +API documentation +================= + +.. toctree:: + :maxdepth: 1 + + errors + version + loop + handle + request + timer + prepare + check + idle + async + poll + signal + process + stream + tcp + pipe + tty + udp + fs_event + fs_poll + fs + threadpool + dns + dll + threading + misc + metrics + diff --git a/include/libuv/docs/src/async.rst b/include/libuv/docs/src/async.rst new file mode 100644 index 000000000..029c051cf --- /dev/null +++ b/include/libuv/docs/src/async.rst @@ -0,0 +1,65 @@ + +.. _async: + +:c:type:`uv_async_t` --- Async handle +===================================== + +Async handles allow the user to "wakeup" the event loop and get a callback +called from another thread. + + +Data types +---------- + +.. c:type:: uv_async_t + + Async handle type. + +.. c:type:: void (*uv_async_cb)(uv_async_t* handle) + + Type definition for callback passed to :c:func:`uv_async_init`. + + +Public members +^^^^^^^^^^^^^^ + +N/A + +.. seealso:: The :c:type:`uv_handle_t` members also apply. + + +API +--- + +.. c:function:: int uv_async_init(uv_loop_t* loop, uv_async_t* async, uv_async_cb async_cb) + + Initialize the handle. A NULL callback is allowed. + + :returns: 0 on success, or an error code < 0 on failure. + + .. note:: + Unlike other handle initialization functions, it immediately starts the handle. + +.. c:function:: int uv_async_send(uv_async_t* async) + + Wake up the event loop and call the async handle's callback. + + :returns: 0 on success, or an error code < 0 on failure. + + .. note:: + It's safe to call this function from any thread. The callback will be called on the + loop thread. + + .. note:: + :c:func:`uv_async_send` is `async-signal-safe `_. + It's safe to call this function from a signal handler. + + .. warning:: + libuv will coalesce calls to :c:func:`uv_async_send`, that is, not every call to it will + yield an execution of the callback. For example: if :c:func:`uv_async_send` is called 5 + times in a row before the callback is called, the callback will only be called once. If + :c:func:`uv_async_send` is called again after the callback was called, it will be called + again. + +.. seealso:: + The :c:type:`uv_handle_t` API functions also apply. diff --git a/include/libuv/docs/src/check.rst b/include/libuv/docs/src/check.rst new file mode 100644 index 000000000..33aab5516 --- /dev/null +++ b/include/libuv/docs/src/check.rst @@ -0,0 +1,54 @@ + +.. _check: + +:c:type:`uv_check_t` --- Check handle +===================================== + +Check handles will run the given callback once per loop iteration, right +after polling for i/o. + + +Data types +---------- + +.. c:type:: uv_check_t + + Check handle type. + +.. c:type:: void (*uv_check_cb)(uv_check_t* handle) + + Type definition for callback passed to :c:func:`uv_check_start`. + + +Public members +^^^^^^^^^^^^^^ + +N/A + +.. seealso:: The :c:type:`uv_handle_t` members also apply. + + +API +--- + +.. c:function:: int uv_check_init(uv_loop_t* loop, uv_check_t* check) + + Initialize the handle. This function always succeeds. + + :returns: 0 + +.. c:function:: int uv_check_start(uv_check_t* check, uv_check_cb cb) + + Start the handle with the given callback. This function always succeeds, + except when `cb` is `NULL`. + + :returns: 0 on success, or `UV_EINVAL` when `cb == NULL`. + +.. c:function:: int uv_check_stop(uv_check_t* check) + + Stop the handle, the callback will no longer be called. + This function always succeeds. + + :returns: 0 + +.. seealso:: The :c:type:`uv_handle_t` API functions also apply. diff --git a/include/libuv/docs/src/conf.py b/include/libuv/docs/src/conf.py new file mode 100644 index 000000000..f6f43253d --- /dev/null +++ b/include/libuv/docs/src/conf.py @@ -0,0 +1,348 @@ +# -*- coding: utf-8 -*- +# +# libuv documentation documentation build configuration file, created by +# sphinx-quickstart on Sun Jul 27 11:47:51 2014. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import os +import re +import sys + + +def get_libuv_version(): + with open('../../include/uv/version.h') as f: + data = f.read() + try: + m = re.search(r"""^#define UV_VERSION_MAJOR (\d+)$""", data, re.MULTILINE) + major = int(m.group(1)) + m = re.search(r"""^#define UV_VERSION_MINOR (\d+)$""", data, re.MULTILINE) + minor = int(m.group(1)) + m = re.search(r"""^#define UV_VERSION_PATCH (\d+)$""", data, re.MULTILINE) + patch = int(m.group(1)) + m = re.search(r"""^#define UV_VERSION_IS_RELEASE (\d)$""", data, re.MULTILINE) + is_release = int(m.group(1)) + m = re.search(r"""^#define UV_VERSION_SUFFIX \"(\w*)\"$""", data, re.MULTILINE) + suffix = m.group(1) + return '%d.%d.%d%s' % (major, minor, patch, '-%s' % suffix if not is_release else '') + except Exception: + return 'unknown' + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.insert(0, os.path.abspath('sphinx-plugins')) + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = ['manpage'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'libuv API documentation' +copyright = u'2014-present, libuv contributors' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = get_libuv_version() +# The full version, including alpha/beta/rc tags. +release = version + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = [] + +# The reST default role (used for this markup: `text`) to use for all +# documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'nature' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +html_title = 'libuv documentation' + +# A shorter title for the navigation bar. Default is the same as html_title. +html_short_title = 'libuv %s documentation' % version + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +html_logo = 'static/logo.png' + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +html_favicon = 'static/favicon.ico' + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['static'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'libuv' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + ('index', 'libuv.tex', u'libuv documentation', + u'libuv contributors', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'libuv', u'libuv documentation', + [u'libuv contributors'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'libuv', u'libuv documentation', + u'libuv contributors', 'libuv', 'Cross-platform asynchronous I/O', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False + + +# -- Options for Epub output ---------------------------------------------- + +# Bibliographic Dublin Core info. +epub_title = u'libuv documentation' +epub_author = u'libuv contributors' +epub_publisher = u'libuv contributors' +epub_copyright = u'2014-present, libuv contributors' + +# The basename for the epub file. It defaults to the project name. +epub_basename = u'libuv' + +# The HTML theme for the epub output. Since the default themes are not optimized +# for small screen space, using the same theme for HTML and epub output is +# usually not wise. This defaults to 'epub', a theme designed to save visual +# space. +#epub_theme = 'epub' + +# The language of the text. It defaults to the language option +# or en if the language is not set. +#epub_language = '' + +# The scheme of the identifier. Typical schemes are ISBN or URL. +#epub_scheme = '' + +# The unique identifier of the text. This can be a ISBN number +# or the project homepage. +#epub_identifier = '' + +# A unique identification for the text. +#epub_uid = '' + +# A tuple containing the cover image and cover page html template filenames. +#epub_cover = () + +# A sequence of (type, uri, title) tuples for the guide element of content.opf. +#epub_guide = () + +# HTML files that should be inserted before the pages created by sphinx. +# The format is a list of tuples containing the path and title. +#epub_pre_files = [] + +# HTML files shat should be inserted after the pages created by sphinx. +# The format is a list of tuples containing the path and title. +#epub_post_files = [] + +# A list of files that should not be packed into the epub file. +epub_exclude_files = ['search.html'] + +# The depth of the table of contents in toc.ncx. +#epub_tocdepth = 3 + +# Allow duplicate toc entries. +#epub_tocdup = True + +# Choose between 'default' and 'includehidden'. +#epub_tocscope = 'default' + +# Fix unsupported image types using the PIL. +#epub_fix_images = False + +# Scale large images. +#epub_max_image_width = 0 + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#epub_show_urls = 'inline' + +# If false, no index is generated. +#epub_use_index = True diff --git a/include/libuv/docs/src/design.rst b/include/libuv/docs/src/design.rst new file mode 100644 index 000000000..a23e33a21 --- /dev/null +++ b/include/libuv/docs/src/design.rst @@ -0,0 +1,140 @@ + +.. _design: + +Design overview +=============== + +libuv is cross-platform support library which was originally written for `Node.js`_. It's designed +around the event-driven asynchronous I/O model. + +.. _Node.js: https://nodejs.org + +The library provides much more than a simple abstraction over different I/O polling mechanisms: +'handles' and 'streams' provide a high level abstraction for sockets and other entities; +cross-platform file I/O and threading functionality is also provided, amongst other things. + +Here is a diagram illustrating the different parts that compose libuv and what subsystem they +relate to: + +.. image:: static/architecture.png + :scale: 75% + :align: center + + +Handles and requests +^^^^^^^^^^^^^^^^^^^^ + +libuv provides users with 2 abstractions to work with, in combination with the event loop: +handles and requests. + +Handles represent long-lived objects capable of performing certain operations while active. Some examples: + +- A prepare handle gets its callback called once every loop iteration when active. +- A TCP server handle that gets its connection callback called every time there is a new connection. + +Requests represent (typically) short-lived operations. These operations can be performed over a +handle: write requests are used to write data on a handle; or standalone: getaddrinfo requests +don't need a handle they run directly on the loop. + + +The I/O loop +^^^^^^^^^^^^ + +The I/O (or event) loop is the central part of libuv. It establishes the content for all I/O +operations, and it's meant to be tied to a single thread. One can run multiple event loops +as long as each runs in a different thread. The libuv event loop (or any other API involving +the loop or handles, for that matter) **is not thread-safe** except where stated otherwise. + +The event loop follows the rather usual single threaded asynchronous I/O approach: all (network) +I/O is performed on non-blocking sockets which are polled using the best mechanism available +on the given platform: epoll on Linux, kqueue on OSX and other BSDs, event ports on SunOS and IOCP +on Windows. As part of a loop iteration the loop will block waiting for I/O activity on sockets +which have been added to the poller and callbacks will be fired indicating socket conditions +(readable, writable hangup) so handles can read, write or perform the desired I/O operation. + +In order to better understand how the event loop operates, the following diagram illustrates all +stages of a loop iteration: + +.. image:: static/loop_iteration.png + :scale: 75% + :align: center + + +#. The loop concept of 'now' is updated. The event loop caches the current time at the start of + the event loop tick in order to reduce the number of time-related system calls. + +#. If the loop is *alive* an iteration is started, otherwise the loop will exit immediately. So, + when is a loop considered to be *alive*? If a loop has active and ref'd handles, active + requests or closing handles it's considered to be *alive*. + +#. Due timers are run. All active timers scheduled for a time before the loop's concept of *now* + get their callbacks called. + +#. Pending callbacks are called. All I/O callbacks are called right after polling for I/O, for the + most part. There are cases, however, in which calling such a callback is deferred for the next + loop iteration. If the previous iteration deferred any I/O callback it will be run at this point. + +#. Idle handle callbacks are called. Despite the unfortunate name, idle handles are run on every + loop iteration, if they are active. + +#. Prepare handle callbacks are called. Prepare handles get their callbacks called right before + the loop will block for I/O. + +#. Poll timeout is calculated. Before blocking for I/O the loop calculates for how long it should + block. These are the rules when calculating the timeout: + + * If the loop was run with the ``UV_RUN_NOWAIT`` flag, the timeout is 0. + * If the loop is going to be stopped (:c:func:`uv_stop` was called), the timeout is 0. + * If there are no active handles or requests, the timeout is 0. + * If there are any idle handles active, the timeout is 0. + * If there are any handles pending to be closed, the timeout is 0. + * If none of the above cases matches, the timeout of the closest timer is taken, or + if there are no active timers, infinity. + +#. The loop blocks for I/O. At this point the loop will block for I/O for the duration calculated + in the previous step. All I/O related handles that were monitoring a given file descriptor + for a read or write operation get their callbacks called at this point. + +#. Check handle callbacks are called. Check handles get their callbacks called right after the + loop has blocked for I/O. Check handles are essentially the counterpart of prepare handles. + +#. Close callbacks are called. If a handle was closed by calling :c:func:`uv_close` it will + get the close callback called. + +#. Special case in case the loop was run with ``UV_RUN_ONCE``, as it implies forward progress. + It's possible that no I/O callbacks were fired after blocking for I/O, but some time has passed + so there might be timers which are due, those timers get their callbacks called. + +#. Iteration ends. If the loop was run with ``UV_RUN_NOWAIT`` or ``UV_RUN_ONCE`` modes the + iteration ends and :c:func:`uv_run` will return. If the loop was run with ``UV_RUN_DEFAULT`` + it will continue from the start if it's still *alive*, otherwise it will also end. + + +.. important:: + libuv uses a thread pool to make asynchronous file I/O operations possible, but + network I/O is **always** performed in a single thread, each loop's thread. + +.. note:: + While the polling mechanism is different, libuv makes the execution model consistent + across Unix systems and Windows. + + +File I/O +^^^^^^^^ + +Unlike network I/O, there are no platform-specific file I/O primitives libuv could rely on, +so the current approach is to run blocking file I/O operations in a thread pool. + +For a thorough explanation of the cross-platform file I/O landscape, checkout +`this post `_. + +libuv currently uses a global thread pool on which all loops can queue work. 3 types of +operations are currently run on this pool: + + * File system operations + * DNS functions (getaddrinfo and getnameinfo) + * User specified code via :c:func:`uv_queue_work` + +.. warning:: + See the :c:ref:`threadpool` section for more details, but keep in mind the thread pool size + is quite limited. diff --git a/include/libuv/docs/src/dll.rst b/include/libuv/docs/src/dll.rst new file mode 100644 index 000000000..fb13f9081 --- /dev/null +++ b/include/libuv/docs/src/dll.rst @@ -0,0 +1,44 @@ + +.. _dll: + +Shared library handling +======================= + +libuv provides cross platform utilities for loading shared libraries and +retrieving symbols from them, using the following API. + + +Data types +---------- + +.. c:type:: uv_lib_t + + Shared library data type. + + +Public members +^^^^^^^^^^^^^^ + +N/A + + +API +--- + +.. c:function:: int uv_dlopen(const char* filename, uv_lib_t* lib) + + Opens a shared library. The filename is in utf-8. Returns 0 on success and + -1 on error. Call :c:func:`uv_dlerror` to get the error message. + +.. c:function:: void uv_dlclose(uv_lib_t* lib) + + Close the shared library. + +.. c:function:: int uv_dlsym(uv_lib_t* lib, const char* name, void** ptr) + + Retrieves a data pointer from a dynamic library. It is legal for a symbol + to map to NULL. Returns 0 on success and -1 if the symbol was not found. + +.. c:function:: const char* uv_dlerror(const uv_lib_t* lib) + + Returns the last uv_dlopen() or uv_dlsym() error message. diff --git a/include/libuv/docs/src/dns.rst b/include/libuv/docs/src/dns.rst new file mode 100644 index 000000000..1d8815809 --- /dev/null +++ b/include/libuv/docs/src/dns.rst @@ -0,0 +1,108 @@ + +.. _dns: + +DNS utility functions +===================== + +libuv provides asynchronous variants of `getaddrinfo` and `getnameinfo`. + + +Data types +---------- + +.. c:type:: uv_getaddrinfo_t + + `getaddrinfo` request type. + +.. c:type:: void (*uv_getaddrinfo_cb)(uv_getaddrinfo_t* req, int status, struct addrinfo* res) + + Callback which will be called with the getaddrinfo request result once + complete. In case it was cancelled, `status` will have a value of + ``UV_ECANCELED``. + +.. c:type:: uv_getnameinfo_t + + `getnameinfo` request type. + +.. c:type:: void (*uv_getnameinfo_cb)(uv_getnameinfo_t* req, int status, const char* hostname, const char* service) + + Callback which will be called with the getnameinfo request result once + complete. In case it was cancelled, `status` will have a value of + ``UV_ECANCELED``. + + +Public members +^^^^^^^^^^^^^^ + +.. c:member:: uv_loop_t* uv_getaddrinfo_t.loop + + Loop that started this getaddrinfo request and where completion will be + reported. Readonly. + +.. c:member:: struct addrinfo* uv_getaddrinfo_t.addrinfo + + Pointer to a `struct addrinfo` containing the result. Must be freed by the user + with :c:func:`uv_freeaddrinfo`. + + .. versionchanged:: 1.3.0 the field is declared as public. + +.. c:member:: uv_loop_t* uv_getnameinfo_t.loop + + Loop that started this getnameinfo request and where completion will be + reported. Readonly. + +.. c:member:: char[NI_MAXHOST] uv_getnameinfo_t.host + + Char array containing the resulting host. It's null terminated. + + .. versionchanged:: 1.3.0 the field is declared as public. + +.. c:member:: char[NI_MAXSERV] uv_getnameinfo_t.service + + Char array containing the resulting service. It's null terminated. + + .. versionchanged:: 1.3.0 the field is declared as public. + +.. seealso:: The :c:type:`uv_req_t` members also apply. + + +API +--- + +.. c:function:: int uv_getaddrinfo(uv_loop_t* loop, uv_getaddrinfo_t* req, uv_getaddrinfo_cb getaddrinfo_cb, const char* node, const char* service, const struct addrinfo* hints) + + Asynchronous :man:`getaddrinfo(3)`. + + Either node or service may be NULL but not both. + + `hints` is a pointer to a struct addrinfo with additional address type + constraints, or NULL. Consult `man -s 3 getaddrinfo` for more details. + + Returns 0 on success or an error code < 0 on failure. If successful, the + callback will get called sometime in the future with the lookup result, + which is either: + + * status == 0, the res argument points to a valid `struct addrinfo`, or + * status < 0, the res argument is NULL. See the UV_EAI_* constants. + + Call :c:func:`uv_freeaddrinfo` to free the addrinfo structure. + + .. versionchanged:: 1.3.0 the callback parameter is now allowed to be NULL, + in which case the request will run **synchronously**. + +.. c:function:: void uv_freeaddrinfo(struct addrinfo* ai) + + Free the struct addrinfo. Passing NULL is allowed and is a no-op. + +.. c:function:: int uv_getnameinfo(uv_loop_t* loop, uv_getnameinfo_t* req, uv_getnameinfo_cb getnameinfo_cb, const struct sockaddr* addr, int flags) + + Asynchronous :man:`getnameinfo(3)`. + + Returns 0 on success or an error code < 0 on failure. If successful, the + callback will get called sometime in the future with the lookup result. + Consult `man -s 3 getnameinfo` for more details. + + .. versionchanged:: 1.3.0 the callback parameter is now allowed to be NULL, + in which case the request will run **synchronously**. + +.. seealso:: The :c:type:`uv_req_t` API functions also apply. diff --git a/include/libuv/docs/src/errors.rst b/include/libuv/docs/src/errors.rst new file mode 100644 index 000000000..c7240f354 --- /dev/null +++ b/include/libuv/docs/src/errors.rst @@ -0,0 +1,385 @@ + +.. _errors: + +Error handling +============== + +In libuv errors are negative numbered constants. As a rule of thumb, whenever +there is a status parameter, or an API functions returns an integer, a negative +number will imply an error. + +When a function which takes a callback returns an error, the callback will never +be called. + +.. note:: + Implementation detail: on Unix error codes are the negated `errno` (or `-errno`), while on + Windows they are defined by libuv to arbitrary negative numbers. + + +Error constants +--------------- + +.. c:macro:: UV_E2BIG + + argument list too long + +.. c:macro:: UV_EACCES + + permission denied + +.. c:macro:: UV_EADDRINUSE + + address already in use + +.. c:macro:: UV_EADDRNOTAVAIL + + address not available + +.. c:macro:: UV_EAFNOSUPPORT + + address family not supported + +.. c:macro:: UV_EAGAIN + + resource temporarily unavailable + +.. c:macro:: UV_EAI_ADDRFAMILY + + address family not supported + +.. c:macro:: UV_EAI_AGAIN + + temporary failure + +.. c:macro:: UV_EAI_BADFLAGS + + bad ai_flags value + +.. c:macro:: UV_EAI_BADHINTS + + invalid value for hints + +.. c:macro:: UV_EAI_CANCELED + + request canceled + +.. c:macro:: UV_EAI_FAIL + + permanent failure + +.. c:macro:: UV_EAI_FAMILY + + ai_family not supported + +.. c:macro:: UV_EAI_MEMORY + + out of memory + +.. c:macro:: UV_EAI_NODATA + + no address + +.. c:macro:: UV_EAI_NONAME + + unknown node or service + +.. c:macro:: UV_EAI_OVERFLOW + + argument buffer overflow + +.. c:macro:: UV_EAI_PROTOCOL + + resolved protocol is unknown + +.. c:macro:: UV_EAI_SERVICE + + service not available for socket type + +.. c:macro:: UV_EAI_SOCKTYPE + + socket type not supported + +.. c:macro:: UV_EALREADY + + connection already in progress + +.. c:macro:: UV_EBADF + + bad file descriptor + +.. c:macro:: UV_EBUSY + + resource busy or locked + +.. c:macro:: UV_ECANCELED + + operation canceled + +.. c:macro:: UV_ECHARSET + + invalid Unicode character + +.. c:macro:: UV_ECONNABORTED + + software caused connection abort + +.. c:macro:: UV_ECONNREFUSED + + connection refused + +.. c:macro:: UV_ECONNRESET + + connection reset by peer + +.. c:macro:: UV_EDESTADDRREQ + + destination address required + +.. c:macro:: UV_EEXIST + + file already exists + +.. c:macro:: UV_EFAULT + + bad address in system call argument + +.. c:macro:: UV_EFBIG + + file too large + +.. c:macro:: UV_EHOSTUNREACH + + host is unreachable + +.. c:macro:: UV_EINTR + + interrupted system call + +.. c:macro:: UV_EINVAL + + invalid argument + +.. c:macro:: UV_EIO + + i/o error + +.. c:macro:: UV_EISCONN + + socket is already connected + +.. c:macro:: UV_EISDIR + + illegal operation on a directory + +.. c:macro:: UV_ELOOP + + too many symbolic links encountered + +.. c:macro:: UV_EMFILE + + too many open files + +.. c:macro:: UV_EMSGSIZE + + message too long + +.. c:macro:: UV_ENAMETOOLONG + + name too long + +.. c:macro:: UV_ENETDOWN + + network is down + +.. c:macro:: UV_ENETUNREACH + + network is unreachable + +.. c:macro:: UV_ENFILE + + file table overflow + +.. c:macro:: UV_ENOBUFS + + no buffer space available + +.. c:macro:: UV_ENODEV + + no such device + +.. c:macro:: UV_ENOENT + + no such file or directory + +.. c:macro:: UV_ENOMEM + + not enough memory + +.. c:macro:: UV_ENONET + + machine is not on the network + +.. c:macro:: UV_ENOPROTOOPT + + protocol not available + +.. c:macro:: UV_ENOSPC + + no space left on device + +.. c:macro:: UV_ENOSYS + + function not implemented + +.. c:macro:: UV_ENOTCONN + + socket is not connected + +.. c:macro:: UV_ENOTDIR + + not a directory + +.. c:macro:: UV_ENOTEMPTY + + directory not empty + +.. c:macro:: UV_ENOTSOCK + + socket operation on non-socket + +.. c:macro:: UV_ENOTSUP + + operation not supported on socket + +.. c:macro:: UV_EOVERFLOW + + value too large for defined data type + +.. c:macro:: UV_EPERM + + operation not permitted + +.. c:macro:: UV_EPIPE + + broken pipe + +.. c:macro:: UV_EPROTO + + protocol error + +.. c:macro:: UV_EPROTONOSUPPORT + + protocol not supported + +.. c:macro:: UV_EPROTOTYPE + + protocol wrong type for socket + +.. c:macro:: UV_ERANGE + + result too large + +.. c:macro:: UV_EROFS + + read-only file system + +.. c:macro:: UV_ESHUTDOWN + + cannot send after transport endpoint shutdown + +.. c:macro:: UV_ESPIPE + + invalid seek + +.. c:macro:: UV_ESRCH + + no such process + +.. c:macro:: UV_ETIMEDOUT + + connection timed out + +.. c:macro:: UV_ETXTBSY + + text file is busy + +.. c:macro:: UV_EXDEV + + cross-device link not permitted + +.. c:macro:: UV_UNKNOWN + + unknown error + +.. c:macro:: UV_EOF + + end of file + +.. c:macro:: UV_ENXIO + + no such device or address + +.. c:macro:: UV_EMLINK + + too many links + +.. c:macro:: UV_ENOTTY + + inappropriate ioctl for device + +.. c:macro:: UV_EFTYPE + + inappropriate file type or format + +.. c:macro:: UV_EILSEQ + + illegal byte sequence + +.. c:macro:: UV_ESOCKTNOSUPPORT + + socket type not supported + + +API +--- + +.. c:macro:: UV_ERRNO_MAP(iter_macro) + + Macro that expands to a series of invocations of `iter_macro` for + each of the error constants above. `iter_macro` is invoked with two + arguments: the name of the error constant without the `UV_` prefix, + and the error message string literal. + +.. c:function:: const char* uv_strerror(int err) + + Returns the error message for the given error code. Leaks a few bytes + of memory when you call it with an unknown error code. + +.. c:function:: char* uv_strerror_r(int err, char* buf, size_t buflen) + + Returns the error message for the given error code. The zero-terminated + message is stored in the user-supplied buffer `buf` of at most `buflen` bytes. + + .. versionadded:: 1.22.0 + +.. c:function:: const char* uv_err_name(int err) + + Returns the error name for the given error code. Leaks a few bytes + of memory when you call it with an unknown error code. + +.. c:function:: char* uv_err_name_r(int err, char* buf, size_t buflen) + + Returns the error name for the given error code. The zero-terminated + name is stored in the user-supplied buffer `buf` of at most `buflen` bytes. + + .. versionadded:: 1.22.0 + +.. c:function:: int uv_translate_sys_error(int sys_errno) + + Returns the libuv error code equivalent to the given platform dependent error + code: POSIX error codes on Unix (the ones stored in `errno`), and Win32 error + codes on Windows (those returned by `GetLastError()` or `WSAGetLastError()`). + + If `sys_errno` is already a libuv error, it is simply returned. + + .. versionchanged:: 1.10.0 function declared public. diff --git a/include/libuv/docs/src/fs.rst b/include/libuv/docs/src/fs.rst new file mode 100644 index 000000000..0bf2abed5 --- /dev/null +++ b/include/libuv/docs/src/fs.rst @@ -0,0 +1,702 @@ + +.. _fs: + +File system operations +====================== + +libuv provides a wide variety of cross-platform sync and async file system +operations. All functions defined in this document take a callback, which is +allowed to be NULL. If the callback is NULL the request is completed synchronously, +otherwise it will be performed asynchronously. + +All file operations are run on the threadpool. See :ref:`threadpool` for information +on the threadpool size. + +.. note:: + On Windows `uv_fs_*` functions use utf-8 encoding. + +Data types +---------- + +.. c:type:: uv_fs_t + + File system request type. + +.. c:type:: uv_timespec_t + + Portable equivalent of ``struct timespec``. + + :: + + typedef struct { + long tv_sec; + long tv_nsec; + } uv_timespec_t; + +.. c:type:: uv_stat_t + + Portable equivalent of ``struct stat``. + + :: + + typedef struct { + uint64_t st_dev; + uint64_t st_mode; + uint64_t st_nlink; + uint64_t st_uid; + uint64_t st_gid; + uint64_t st_rdev; + uint64_t st_ino; + uint64_t st_size; + uint64_t st_blksize; + uint64_t st_blocks; + uint64_t st_flags; + uint64_t st_gen; + uv_timespec_t st_atim; + uv_timespec_t st_mtim; + uv_timespec_t st_ctim; + uv_timespec_t st_birthtim; + } uv_stat_t; + +.. c:enum:: uv_fs_type + + File system request type. + + :: + + typedef enum { + UV_FS_UNKNOWN = -1, + UV_FS_CUSTOM, + UV_FS_OPEN, + UV_FS_CLOSE, + UV_FS_READ, + UV_FS_WRITE, + UV_FS_SENDFILE, + UV_FS_STAT, + UV_FS_LSTAT, + UV_FS_FSTAT, + UV_FS_FTRUNCATE, + UV_FS_UTIME, + UV_FS_FUTIME, + UV_FS_ACCESS, + UV_FS_CHMOD, + UV_FS_FCHMOD, + UV_FS_FSYNC, + UV_FS_FDATASYNC, + UV_FS_UNLINK, + UV_FS_RMDIR, + UV_FS_MKDIR, + UV_FS_MKDTEMP, + UV_FS_RENAME, + UV_FS_SCANDIR, + UV_FS_LINK, + UV_FS_SYMLINK, + UV_FS_READLINK, + UV_FS_CHOWN, + UV_FS_FCHOWN, + UV_FS_REALPATH, + UV_FS_COPYFILE, + UV_FS_LCHOWN, + UV_FS_OPENDIR, + UV_FS_READDIR, + UV_FS_CLOSEDIR, + UV_FS_MKSTEMP, + UV_FS_LUTIME + } uv_fs_type; + +.. c:type:: uv_statfs_t + + Reduced cross platform equivalent of ``struct statfs``. + Used in :c:func:`uv_fs_statfs`. + + :: + + typedef struct uv_statfs_s { + uint64_t f_type; + uint64_t f_bsize; + uint64_t f_blocks; + uint64_t f_bfree; + uint64_t f_bavail; + uint64_t f_files; + uint64_t f_ffree; + uint64_t f_spare[4]; + } uv_statfs_t; + +.. c:enum:: uv_dirent_t + + Cross platform (reduced) equivalent of ``struct dirent``. + Used in :c:func:`uv_fs_scandir_next`. + + :: + + typedef enum { + UV_DIRENT_UNKNOWN, + UV_DIRENT_FILE, + UV_DIRENT_DIR, + UV_DIRENT_LINK, + UV_DIRENT_FIFO, + UV_DIRENT_SOCKET, + UV_DIRENT_CHAR, + UV_DIRENT_BLOCK + } uv_dirent_type_t; + + typedef struct uv_dirent_s { + const char* name; + uv_dirent_type_t type; + } uv_dirent_t; + +.. c:type:: uv_dir_t + + Data type used for streaming directory iteration. + Used by :c:func:`uv_fs_opendir()`, :c:func:`uv_fs_readdir()`, and + :c:func:`uv_fs_closedir()`. `dirents` represents a user provided array of + `uv_dirent_t`s used to hold results. `nentries` is the user provided maximum + array size of `dirents`. + + :: + + typedef struct uv_dir_s { + uv_dirent_t* dirents; + size_t nentries; + } uv_dir_t; + + +Public members +^^^^^^^^^^^^^^ + +.. c:member:: uv_loop_t* uv_fs_t.loop + + Loop that started this request and where completion will be reported. + Readonly. + +.. c:member:: uv_fs_type uv_fs_t.fs_type + + FS request type. + +.. c:member:: const char* uv_fs_t.path + + Path affecting the request. + +.. c:member:: ssize_t uv_fs_t.result + + Result of the request. < 0 means error, success otherwise. On requests such + as :c:func:`uv_fs_read` or :c:func:`uv_fs_write` it indicates the amount of + data that was read or written, respectively. + +.. c:member:: uv_stat_t uv_fs_t.statbuf + + Stores the result of :c:func:`uv_fs_stat` and other stat requests. + +.. c:member:: void* uv_fs_t.ptr + + Stores the result of :c:func:`uv_fs_readlink` and + :c:func:`uv_fs_realpath` and serves as an alias to `statbuf`. + +.. seealso:: The :c:type:`uv_req_t` members also apply. + + +API +--- + +.. c:function:: void uv_fs_req_cleanup(uv_fs_t* req) + + Cleanup request. Must be called after a request is finished to deallocate + any memory libuv might have allocated. + +.. c:function:: int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) + + Equivalent to :man:`close(2)`. + +.. c:function:: int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, uv_fs_cb cb) + + Equivalent to :man:`open(2)`. + + .. note:: + On Windows libuv uses `CreateFileW` and thus the file is always opened + in binary mode. Because of this the O_BINARY and O_TEXT flags are not + supported. + +.. c:function:: int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, uv_fs_cb cb) + + Equivalent to :man:`preadv(2)`. + + .. warning:: + On Windows, under non-MSVC environments (e.g. when GCC or Clang is used + to build libuv), files opened using ``UV_FS_O_FILEMAP`` may cause a fatal + crash if the memory mapped read operation fails. + +.. c:function:: int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) + + Equivalent to :man:`unlink(2)`. + +.. c:function:: int uv_fs_write(uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, uv_fs_cb cb) + + Equivalent to :man:`pwritev(2)`. + + .. warning:: + On Windows, under non-MSVC environments (e.g. when GCC or Clang is used + to build libuv), files opened using ``UV_FS_O_FILEMAP`` may cause a fatal + crash if the memory mapped write operation fails. + +.. c:function:: int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb) + + Equivalent to :man:`mkdir(2)`. + + .. note:: + `mode` is currently not implemented on Windows. + +.. c:function:: int uv_fs_mkdtemp(uv_loop_t* loop, uv_fs_t* req, const char* tpl, uv_fs_cb cb) + + Equivalent to :man:`mkdtemp(3)`. The result can be found as a null terminated string at `req->path`. + +.. c:function:: int uv_fs_mkstemp(uv_loop_t* loop, uv_fs_t* req, const char* tpl, uv_fs_cb cb) + + Equivalent to :man:`mkstemp(3)`. The created file path can be found as a null terminated string at `req->path`. + The file descriptor can be found as an integer at `req->result`. + + .. versionadded:: 1.34.0 + +.. c:function:: int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) + + Equivalent to :man:`rmdir(2)`. + +.. c:function:: int uv_fs_opendir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) + + Opens `path` as a directory stream. On success, a `uv_dir_t` is allocated + and returned via `req->ptr`. This memory is not freed by + `uv_fs_req_cleanup()`, although `req->ptr` is set to `NULL`. The allocated + memory must be freed by calling `uv_fs_closedir()`. On failure, no memory + is allocated. + + The contents of the directory can be iterated over by passing the resulting + `uv_dir_t` to `uv_fs_readdir()`. + + .. versionadded:: 1.28.0 + +.. c:function:: int uv_fs_closedir(uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, uv_fs_cb cb) + + Closes the directory stream represented by `dir` and frees the memory + allocated by `uv_fs_opendir()`. + + .. versionadded:: 1.28.0 + +.. c:function:: int uv_fs_readdir(uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, uv_fs_cb cb) + + Iterates over the directory stream, `dir`, returned by a successful + `uv_fs_opendir()` call. Prior to invoking `uv_fs_readdir()`, the caller + must set `dir->dirents` and `dir->nentries`, representing the array of + :c:type:`uv_dirent_t` elements used to hold the read directory entries and + its size. + + On success, the result is an integer >= 0 representing the number of entries + read from the stream. + + .. versionadded:: 1.28.0 + + .. warning:: + `uv_fs_readdir()` is not thread safe. + + .. note:: + This function does not return the "." and ".." entries. + + .. note:: + On success this function allocates memory that must be freed using + `uv_fs_req_cleanup()`. `uv_fs_req_cleanup()` must be called before + closing the directory with `uv_fs_closedir()`. + +.. c:function:: int uv_fs_scandir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, uv_fs_cb cb) +.. c:function:: int uv_fs_scandir_next(uv_fs_t* req, uv_dirent_t* ent) + + Equivalent to :man:`scandir(3)`, with a slightly different API. Once the callback + for the request is called, the user can use :c:func:`uv_fs_scandir_next` to + get `ent` populated with the next directory entry data. When there are no + more entries ``UV_EOF`` will be returned. + + .. note:: + Unlike `scandir(3)`, this function does not return the "." and ".." entries. + + .. note:: + On Linux, getting the type of an entry is only supported by some file systems (btrfs, ext2, + ext3 and ext4 at the time of this writing), check the :man:`getdents(2)` man page. + +.. c:function:: int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) +.. c:function:: int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) +.. c:function:: int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) + + Equivalent to :man:`stat(2)`, :man:`fstat(2)` and :man:`lstat(2)` respectively. + +.. c:function:: int uv_fs_statfs(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) + + Equivalent to :man:`statfs(2)`. On success, a `uv_statfs_t` is allocated + and returned via `req->ptr`. This memory is freed by `uv_fs_req_cleanup()`. + + .. note:: + Any fields in the resulting `uv_statfs_t` that are not supported by the + underlying operating system are set to zero. + + .. versionadded:: 1.31.0 + +.. c:function:: int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb) + + Equivalent to :man:`rename(2)`. + +.. c:function:: int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) + + Equivalent to :man:`fsync(2)`. + + .. note:: + For AIX, `uv_fs_fsync` returns `UV_EBADF` on file descriptors referencing + non regular files. + +.. c:function:: int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) + + Equivalent to :man:`fdatasync(2)`. + +.. c:function:: int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file file, int64_t offset, uv_fs_cb cb) + + Equivalent to :man:`ftruncate(2)`. + +.. c:function:: int uv_fs_copyfile(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, uv_fs_cb cb) + + Copies a file from `path` to `new_path`. Supported `flags` are described below. + + - `UV_FS_COPYFILE_EXCL`: If present, `uv_fs_copyfile()` will fail with + `UV_EEXIST` if the destination path already exists. The default behavior + is to overwrite the destination if it exists. + - `UV_FS_COPYFILE_FICLONE`: If present, `uv_fs_copyfile()` will attempt to + create a copy-on-write reflink. If the underlying platform does not + support copy-on-write, or an error occurs while attempting to use + copy-on-write, a fallback copy mechanism based on + :c:func:`uv_fs_sendfile()` is used. + - `UV_FS_COPYFILE_FICLONE_FORCE`: If present, `uv_fs_copyfile()` will + attempt to create a copy-on-write reflink. If the underlying platform does + not support copy-on-write, or an error occurs while attempting to use + copy-on-write, then an error is returned. + + .. warning:: + If the destination path is created, but an error occurs while copying + the data, then the destination path is removed. There is a brief window + of time between closing and removing the file where another process + could access the file. + + .. versionadded:: 1.14.0 + + .. versionchanged:: 1.20.0 `UV_FS_COPYFILE_FICLONE` and + `UV_FS_COPYFILE_FICLONE_FORCE` are supported. + + .. versionchanged:: 1.33.0 If an error occurs while using + `UV_FS_COPYFILE_FICLONE_FORCE`, that error is returned. Previously, + all errors were mapped to `UV_ENOTSUP`. + +.. c:function:: int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file out_fd, uv_file in_fd, int64_t in_offset, size_t length, uv_fs_cb cb) + + Limited equivalent to :man:`sendfile(2)`. + +.. c:function:: int uv_fs_access(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb) + + Equivalent to :man:`access(2)` on Unix. Windows uses ``GetFileAttributesW()``. + +.. c:function:: int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb) +.. c:function:: int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file file, int mode, uv_fs_cb cb) + + Equivalent to :man:`chmod(2)` and :man:`fchmod(2)` respectively. + +.. c:function:: int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, uv_fs_cb cb) +.. c:function:: int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime, double mtime, uv_fs_cb cb) +.. c:function:: int uv_fs_lutime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, uv_fs_cb cb) + + Equivalent to :man:`utime(2)`, :man:`futimes(3)` and :man:`lutimes(3)` respectively. + + .. note:: + z/OS: `uv_fs_lutime()` is not implemented for z/OS. It can still be called but will return + ``UV_ENOSYS``. + + .. note:: + AIX: `uv_fs_futime()` and `uv_fs_lutime()` functions only work for AIX 7.1 and newer. + They can still be called on older versions but will return ``UV_ENOSYS``. + + .. versionchanged:: 1.10.0 sub-second precission is supported on Windows + +.. c:function:: int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb) + + Equivalent to :man:`link(2)`. + +.. c:function:: int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, uv_fs_cb cb) + + Equivalent to :man:`symlink(2)`. + + .. note:: + On Windows the `flags` parameter can be specified to control how the symlink will + be created: + + * ``UV_FS_SYMLINK_DIR``: indicates that `path` points to a directory. + + * ``UV_FS_SYMLINK_JUNCTION``: request that the symlink is created + using junction points. + +.. c:function:: int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) + + Equivalent to :man:`readlink(2)`. + The resulting string is stored in `req->ptr`. + +.. c:function:: int uv_fs_realpath(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb) + + Equivalent to :man:`realpath(3)` on Unix. Windows uses `GetFinalPathNameByHandle `_. + The resulting string is stored in `req->ptr`. + + .. warning:: + This function has certain platform-specific caveats that were discovered when used in Node. + + * macOS and other BSDs: this function will fail with UV_ELOOP if more than 32 symlinks are + found while resolving the given path. This limit is hardcoded and cannot be sidestepped. + * Windows: while this function works in the common case, there are a number of corner cases + where it doesn't: + + - Paths in ramdisk volumes created by tools which sidestep the Volume Manager (such as ImDisk) + cannot be resolved. + - Inconsistent casing when using drive letters. + - Resolved path bypasses subst'd drives. + + While this function can still be used, it's not recommended if scenarios such as the + above need to be supported. + + The background story and some more details on these issues can be checked + `here `_. + + .. note:: + This function is not implemented on Windows XP and Windows Server 2003. + On these systems, UV_ENOSYS is returned. + + .. versionadded:: 1.8.0 + +.. c:function:: int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb) +.. c:function:: int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb) +.. c:function:: int uv_fs_lchown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb) + + Equivalent to :man:`chown(2)`, :man:`fchown(2)` and :man:`lchown(2)` respectively. + + .. note:: + These functions are not implemented on Windows. + + .. versionchanged:: 1.21.0 implemented uv_fs_lchown + +.. 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:: int uv_fs_get_system_error(const uv_fs_t* req) + + Returns the platform specific error code - `GetLastError()` value on Windows + and `-(req->result)` on other platforms. + + .. versionadded:: 1.38.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 +---------------- + +.. c:function:: uv_os_fd_t uv_get_osfhandle(int fd) + + For a file descriptor in the C runtime, get the OS-dependent handle. + On UNIX, returns the ``fd`` intact. On Windows, this calls `_get_osfhandle `_. + Note that the return value is still owned by the C runtime, + any attempts to close it or to use it after closing the fd may lead to malfunction. + + .. versionadded:: 1.12.0 + +.. c:function:: int uv_open_osfhandle(uv_os_fd_t os_fd) + + For a OS-dependent handle, get the file descriptor in the C runtime. + On UNIX, returns the ``os_fd`` intact. On Windows, this calls `_open_osfhandle `_. + Note that this consumes the argument, any attempts to close it or to use it + after closing the return value may lead to malfunction. + + .. versionadded:: 1.23.0 + +File open constants +------------------- + +.. c:macro:: UV_FS_O_APPEND + + The file is opened in append mode. Before each write, the file offset is + positioned at the end of the file. + +.. c:macro:: UV_FS_O_CREAT + + The file is created if it does not already exist. + +.. c:macro:: UV_FS_O_DIRECT + + File I/O is done directly to and from user-space buffers, which must be + aligned. Buffer size and address should be a multiple of the physical sector + size of the block device. + + .. note:: + `UV_FS_O_DIRECT` is supported on Linux, and on Windows via + `FILE_FLAG_NO_BUFFERING `_. + `UV_FS_O_DIRECT` is not supported on macOS. + +.. c:macro:: UV_FS_O_DIRECTORY + + If the path is not a directory, fail the open. + + .. note:: + `UV_FS_O_DIRECTORY` is not supported on Windows. + +.. c:macro:: UV_FS_O_DSYNC + + The file is opened for synchronous I/O. Write operations will complete once + all data and a minimum of metadata are flushed to disk. + + .. note:: + `UV_FS_O_DSYNC` is supported on Windows via + `FILE_FLAG_WRITE_THROUGH `_. + +.. c:macro:: UV_FS_O_EXCL + + If the `O_CREAT` flag is set and the file already exists, fail the open. + + .. note:: + In general, the behavior of `O_EXCL` is undefined if it is used without + `O_CREAT`. There is one exception: on Linux 2.6 and later, `O_EXCL` can + be used without `O_CREAT` if pathname refers to a block device. If the + block device is in use by the system (e.g., mounted), the open will fail + with the error `EBUSY`. + +.. c:macro:: UV_FS_O_EXLOCK + + Atomically obtain an exclusive lock. + + .. note:: + `UV_FS_O_EXLOCK` is only supported on macOS and Windows. + + .. versionchanged:: 1.17.0 support is added for Windows. + +.. c:macro:: UV_FS_O_FILEMAP + + Use a memory file mapping to access the file. When using this flag, the + file cannot be open multiple times concurrently. + + .. note:: + `UV_FS_O_FILEMAP` is only supported on Windows. + +.. c:macro:: UV_FS_O_NOATIME + + Do not update the file access time when the file is read. + + .. note:: + `UV_FS_O_NOATIME` is not supported on Windows. + +.. c:macro:: UV_FS_O_NOCTTY + + If the path identifies a terminal device, opening the path will not cause + that terminal to become the controlling terminal for the process (if the + process does not already have one). + + .. note:: + `UV_FS_O_NOCTTY` is not supported on Windows. + +.. c:macro:: UV_FS_O_NOFOLLOW + + If the path is a symbolic link, fail the open. + + .. note:: + `UV_FS_O_NOFOLLOW` is not supported on Windows. + +.. c:macro:: UV_FS_O_NONBLOCK + + Open the file in nonblocking mode if possible. + + .. note:: + `UV_FS_O_NONBLOCK` is not supported on Windows. + +.. c:macro:: UV_FS_O_RANDOM + + Access is intended to be random. The system can use this as a hint to + optimize file caching. + + .. note:: + `UV_FS_O_RANDOM` is only supported on Windows via + `FILE_FLAG_RANDOM_ACCESS `_. + +.. c:macro:: UV_FS_O_RDONLY + + Open the file for read-only access. + +.. c:macro:: UV_FS_O_RDWR + + Open the file for read-write access. + +.. c:macro:: UV_FS_O_SEQUENTIAL + + Access is intended to be sequential from beginning to end. The system can + use this as a hint to optimize file caching. + + .. note:: + `UV_FS_O_SEQUENTIAL` is only supported on Windows via + `FILE_FLAG_SEQUENTIAL_SCAN `_. + +.. c:macro:: UV_FS_O_SHORT_LIVED + + The file is temporary and should not be flushed to disk if possible. + + .. note:: + `UV_FS_O_SHORT_LIVED` is only supported on Windows via + `FILE_ATTRIBUTE_TEMPORARY `_. + +.. c:macro:: UV_FS_O_SYMLINK + + Open the symbolic link itself rather than the resource it points to. + +.. c:macro:: UV_FS_O_SYNC + + The file is opened for synchronous I/O. Write operations will complete once + all data and all metadata are flushed to disk. + + .. note:: + `UV_FS_O_SYNC` is supported on Windows via + `FILE_FLAG_WRITE_THROUGH `_. + +.. c:macro:: UV_FS_O_TEMPORARY + + The file is temporary and should not be flushed to disk if possible. + + .. note:: + `UV_FS_O_TEMPORARY` is only supported on Windows via + `FILE_ATTRIBUTE_TEMPORARY `_. + +.. c:macro:: UV_FS_O_TRUNC + + If the file exists and is a regular file, and the file is opened + successfully for write access, its length shall be truncated to zero. + +.. c:macro:: UV_FS_O_WRONLY + + Open the file for write-only access. diff --git a/include/libuv/docs/src/fs_event.rst b/include/libuv/docs/src/fs_event.rst new file mode 100644 index 000000000..e28ec625e --- /dev/null +++ b/include/libuv/docs/src/fs_event.rst @@ -0,0 +1,132 @@ + +.. _fs_event: + +:c:type:`uv_fs_event_t` --- FS Event handle +=========================================== + +FS Event handles allow the user to monitor a given path for changes, for example, +if the file was renamed or there was a generic change in it. This handle uses +the best backend for the job on each platform. + +.. note:: + For AIX, the non default IBM bos.ahafs package has to be installed. + The AIX Event Infrastructure file system (ahafs) has some limitations: + + - ahafs tracks monitoring per process and is not thread safe. A separate process + must be spawned for each monitor for the same event. + - Events for file modification (writing to a file) are not received if only the + containing folder is watched. + + See documentation_ for more details. + + The z/OS file system events monitoring infrastructure does not notify of file + creation/deletion within a directory that is being monitored. + See the `IBM Knowledge centre`_ for more details. + + .. _documentation: https://developer.ibm.com/articles/au-aix_event_infrastructure/ + .. _`IBM Knowledge centre`: https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.2.0/com.ibm.zos.v2r1.bpxb100/ioc.htm + + + + +Data types +---------- + +.. c:type:: uv_fs_event_t + + FS Event handle type. + +.. c:type:: void (*uv_fs_event_cb)(uv_fs_event_t* handle, const char* filename, int events, int status) + + Callback passed to :c:func:`uv_fs_event_start` which will be called repeatedly + after the handle is started. If the handle was started with a directory the + `filename` parameter will be a relative path to a file contained in the directory. + The `events` parameter is an ORed mask of :c:type:`uv_fs_event` elements. + +.. c:type:: uv_fs_event + + Event types that :c:type:`uv_fs_event_t` handles monitor. + + :: + + enum uv_fs_event { + UV_RENAME = 1, + UV_CHANGE = 2 + }; + +.. c:type:: uv_fs_event_flags + + Flags that can be passed to :c:func:`uv_fs_event_start` to control its + behavior. + + :: + + enum uv_fs_event_flags { + /* + * By default, if the fs event watcher is given a directory name, we will + * watch for all events in that directory. This flags overrides this behavior + * and makes fs_event report only changes to the directory entry itself. This + * flag does not affect individual files watched. + * This flag is currently not implemented yet on any backend. + */ + UV_FS_EVENT_WATCH_ENTRY = 1, + /* + * By default uv_fs_event will try to use a kernel interface such as inotify + * or kqueue to detect events. This may not work on remote file systems such + * as NFS mounts. This flag makes fs_event fall back to calling stat() on a + * regular interval. + * This flag is currently not implemented yet on any backend. + */ + UV_FS_EVENT_STAT = 2, + /* + * By default, event watcher, when watching directory, is not registering + * (is ignoring) changes in its subdirectories. + * This flag will override this behaviour on platforms that support it. + */ + UV_FS_EVENT_RECURSIVE = 4 + }; + + +Public members +^^^^^^^^^^^^^^ + +N/A + +.. seealso:: The :c:type:`uv_handle_t` members also apply. + + +API +--- + +.. c:function:: int uv_fs_event_init(uv_loop_t* loop, uv_fs_event_t* handle) + + Initialize the handle. + +.. c:function:: int uv_fs_event_start(uv_fs_event_t* handle, uv_fs_event_cb cb, const char* path, unsigned int flags) + + Start the handle with the given callback, which will watch the specified + `path` for changes. `flags` can be an ORed mask of :c:type:`uv_fs_event_flags`. + + .. note:: Currently the only supported flag is ``UV_FS_EVENT_RECURSIVE`` and + only on OSX and Windows. + +.. c:function:: int uv_fs_event_stop(uv_fs_event_t* handle) + + Stop the handle, the callback will no longer be called. + +.. c:function:: int uv_fs_event_getpath(uv_fs_event_t* handle, char* buffer, size_t* size) + + Get the path being monitored by the handle. The buffer must be preallocated + by the user. Returns 0 on success or an error code < 0 in case of failure. + On success, `buffer` will contain the path and `size` its length. If the buffer + is not big enough `UV_ENOBUFS` will be returned and `size` will be set to + the required size, including the null terminator. + + .. versionchanged:: 1.3.0 the returned length no longer includes the terminating null byte, + and the buffer is not null terminated. + + .. versionchanged:: 1.9.0 the returned length includes the terminating null + byte on `UV_ENOBUFS`, and the buffer is null terminated + on success. + +.. seealso:: The :c:type:`uv_handle_t` API functions also apply. diff --git a/include/libuv/docs/src/fs_poll.rst b/include/libuv/docs/src/fs_poll.rst new file mode 100644 index 000000000..2912bad93 --- /dev/null +++ b/include/libuv/docs/src/fs_poll.rst @@ -0,0 +1,77 @@ + +.. _fs_poll: + +:c:type:`uv_fs_poll_t` --- FS Poll handle +========================================= + +FS Poll handles allow the user to monitor a given path for changes. Unlike +:c:type:`uv_fs_event_t`, fs poll handles use `stat` to detect when a file has +changed so they can work on file systems where fs event handles can't. + + +Data types +---------- + +.. c:type:: uv_fs_poll_t + + FS Poll handle type. + +.. c:type:: void (*uv_fs_poll_cb)(uv_fs_poll_t* handle, int status, const uv_stat_t* prev, const uv_stat_t* curr) + + Callback passed to :c:func:`uv_fs_poll_start` which will be called repeatedly + after the handle is started, when any change happens to the monitored path. + + The callback is invoked with `status < 0` if `path` does not exist + or is inaccessible. The watcher is *not* stopped but your callback is + not called again until something changes (e.g. when the file is created + or the error reason changes). + + When `status == 0`, the callback receives pointers to the old and new + :c:type:`uv_stat_t` structs. They are valid for the duration of the + callback only. + + +Public members +^^^^^^^^^^^^^^ + +N/A + +.. seealso:: The :c:type:`uv_handle_t` members also apply. + + +API +--- + +.. c:function:: int uv_fs_poll_init(uv_loop_t* loop, uv_fs_poll_t* handle) + + Initialize the handle. + +.. c:function:: int uv_fs_poll_start(uv_fs_poll_t* handle, uv_fs_poll_cb poll_cb, const char* path, unsigned int interval) + + Check the file at `path` for changes every `interval` milliseconds. + + .. note:: + For maximum portability, use multi-second intervals. Sub-second intervals will not detect + all changes on many file systems. + +.. c:function:: int uv_fs_poll_stop(uv_fs_poll_t* handle) + + Stop the handle, the callback will no longer be called. + +.. c:function:: int uv_fs_poll_getpath(uv_fs_poll_t* handle, char* buffer, size_t* size) + + Get the path being monitored by the handle. The buffer must be preallocated + by the user. Returns 0 on success or an error code < 0 in case of failure. + On success, `buffer` will contain the path and `size` its length. If the buffer + is not big enough `UV_ENOBUFS` will be returned and `size` will be set to + the required size. + + .. versionchanged:: 1.3.0 the returned length no longer includes the terminating null byte, + and the buffer is not null terminated. + + .. versionchanged:: 1.9.0 the returned length includes the terminating null + byte on `UV_ENOBUFS`, and the buffer is null terminated + on success. + + +.. seealso:: The :c:type:`uv_handle_t` API functions also apply. diff --git a/include/libuv/docs/src/guide.rst b/include/libuv/docs/src/guide.rst new file mode 100644 index 000000000..126e08082 --- /dev/null +++ b/include/libuv/docs/src/guide.rst @@ -0,0 +1,22 @@ +.. _guide: + +User guide +========== + +.. warning:: + The contents of this guide have been recently incorporated into the libuv documentation + and it hasn't gone through thorough review yet. If you spot a mistake please file an + issue, or better yet, open a pull request! + +.. toctree:: + :maxdepth: 2 + + guide/introduction + guide/basics + guide/filesystem + guide/networking + guide/threads + guide/processes + guide/eventloops + guide/utilities + guide/about diff --git a/include/libuv/docs/src/guide/about.rst b/include/libuv/docs/src/guide/about.rst new file mode 100644 index 000000000..731d1a47d --- /dev/null +++ b/include/libuv/docs/src/guide/about.rst @@ -0,0 +1,22 @@ +About +===== + +`Nikhil Marathe `_ started writing this book one +afternoon (June 16, 2012) when he didn't feel like programming. He had recently +been stung by the lack of good documentation on libuv while working on +`node-taglib `_. Although reference +documentation was present, there were no comprehensive tutorials. This book is +the output of that need and tries to be accurate. That said, the book may have +mistakes. Pull requests are encouraged. + +Nikhil is indebted to Marc Lehmann's comprehensive `man page +`_ about libev which +describes much of the semantics of the two libraries. + +This book was made using `Sphinx `_ and `vim +`_. + +.. note:: + In 2017 the libuv project incorporated the Nikhil's work into the official + documentation and it's maintained there henceforth. + diff --git a/include/libuv/docs/src/guide/basics.rst b/include/libuv/docs/src/guide/basics.rst new file mode 100644 index 000000000..e55a20cfd --- /dev/null +++ b/include/libuv/docs/src/guide/basics.rst @@ -0,0 +1,221 @@ +Basics of libuv +=============== + +libuv enforces an **asynchronous**, **event-driven** style of programming. Its +core job is to provide an event loop and callback based notifications of I/O +and other activities. libuv offers core utilities like timers, non-blocking +networking support, asynchronous file system access, child processes and more. + +Event loops +----------- + +In event-driven programming, an application expresses interest in certain events +and respond to them when they occur. The responsibility of gathering events +from the operating system or monitoring other sources of events is handled by +libuv, and the user can register callbacks to be invoked when an event occurs. +The event-loop usually keeps running *forever*. In pseudocode: + +.. code-block:: python + + while there are still events to process: + e = get the next event + if there is a callback associated with e: + call the callback + +Some examples of events are: + +* File is ready for writing +* A socket has data ready to be read +* A timer has timed out + +This event loop is encapsulated by ``uv_run()`` -- the end-all function when using +libuv. + +The most common activity of systems programs is to deal with input and output, +rather than a lot of number-crunching. The problem with using conventional +input/output functions (``read``, ``fprintf``, etc.) is that they are +**blocking**. The actual write to a hard disk or reading from a network, takes +a disproportionately long time compared to the speed of the processor. The +functions don't return until the task is done, so that your program is doing +nothing. For programs which require high performance this is a major roadblock +as other activities and other I/O operations are kept waiting. + +One of the standard solutions is to use threads. Each blocking I/O operation is +started in a separate thread (or in a thread pool). When the blocking function +gets invoked in the thread, the processor can schedule another thread to run, +which actually needs the CPU. + +The approach followed by libuv uses another style, which is the **asynchronous, +non-blocking** style. Most modern operating systems provide event notification +subsystems. For example, a normal ``read`` call on a socket would block until +the sender actually sent something. Instead, the application can request the +operating system to watch the socket and put an event notification in the +queue. The application can inspect the events at its convenience (perhaps doing +some number crunching before to use the processor to the maximum) and grab the +data. It is **asynchronous** because the application expressed interest at one +point, then used the data at another point (in time and space). It is +**non-blocking** because the application process was free to do other tasks. +This fits in well with libuv's event-loop approach, since the operating system +events can be treated as just another libuv event. The non-blocking ensures +that other events can continue to be handled as fast as they come in [#]_. + +.. NOTE:: + + How the I/O is run in the background is not of our concern, but due to the + way our computer hardware works, with the thread as the basic unit of the + processor, libuv and OSes will usually run background/worker threads and/or + polling to perform tasks in a non-blocking manner. + +Bert Belder, one of the libuv core developers has a small video explaining the +architecture of libuv and its background. If you have no prior experience with +either libuv or libev, it is a quick, useful watch. + +libuv's event loop is explained in more detail in the `documentation +`_. + +.. raw:: html + + + +Hello World +----------- + +With the basics out of the way, let's write our first libuv program. It does +nothing, except start a loop which will exit immediately. + +.. rubric:: helloworld/main.c +.. literalinclude:: ../../code/helloworld/main.c + :language: c + :linenos: + +This program quits immediately because it has no events to process. A libuv +event loop has to be told to watch out for events using the various API +functions. + +Starting with libuv v1.0, users should allocate the memory for the loops before +initializing it with ``uv_loop_init(uv_loop_t *)``. This allows you to plug in +custom memory management. Remember to de-initialize the loop using +``uv_loop_close(uv_loop_t *)`` and then delete the storage. The examples never +close loops since the program quits after the loop ends and the system will +reclaim memory. Production grade projects, especially long running systems +programs, should take care to release correctly. + +Default loop +++++++++++++ + +A default loop is provided by libuv and can be accessed using +``uv_default_loop()``. You should use this loop if you only want a single +loop. + +.. note:: + + node.js uses the default loop as its main loop. If you are writing bindings + you should be aware of this. + +.. _libuv-error-handling: + +Error handling +-------------- + +Initialization functions or synchronous functions which may fail return a negative number on error. Async functions that may fail will pass a status parameter to their callbacks. The error messages are defined as ``UV_E*`` `constants`_. + +.. _constants: http://docs.libuv.org/en/v1.x/errors.html#error-constants + +You can use the ``uv_strerror(int)`` and ``uv_err_name(int)`` functions +to get a ``const char *`` describing the error or the error name respectively. + +I/O read callbacks (such as for files and sockets) are passed a parameter ``nread``. If ``nread`` is less than 0, there was an error (UV_EOF is the end of file error, which you may want to handle differently). + +Handles and Requests +-------------------- + +libuv works by the user expressing interest in particular events. This is +usually done by creating a **handle** to an I/O device, timer or process. +Handles are opaque structs named as ``uv_TYPE_t`` where type signifies what the +handle is used for. + +.. rubric:: libuv watchers +.. code-block:: c + + /* Handle types. */ + typedef struct uv_loop_s uv_loop_t; + typedef struct uv_handle_s uv_handle_t; + typedef struct uv_dir_s uv_dir_t; + typedef struct uv_stream_s uv_stream_t; + typedef struct uv_tcp_s uv_tcp_t; + typedef struct uv_udp_s uv_udp_t; + typedef struct uv_pipe_s uv_pipe_t; + typedef struct uv_tty_s uv_tty_t; + typedef struct uv_poll_s uv_poll_t; + typedef struct uv_timer_s uv_timer_t; + typedef struct uv_prepare_s uv_prepare_t; + typedef struct uv_check_s uv_check_t; + typedef struct uv_idle_s uv_idle_t; + typedef struct uv_async_s uv_async_t; + typedef struct uv_process_s uv_process_t; + typedef struct uv_fs_event_s uv_fs_event_t; + typedef struct uv_fs_poll_s uv_fs_poll_t; + typedef struct uv_signal_s uv_signal_t; + + /* Request types. */ + typedef struct uv_req_s uv_req_t; + typedef struct uv_getaddrinfo_s uv_getaddrinfo_t; + typedef struct uv_getnameinfo_s uv_getnameinfo_t; + typedef struct uv_shutdown_s uv_shutdown_t; + typedef struct uv_write_s uv_write_t; + typedef struct uv_connect_s uv_connect_t; + typedef struct uv_udp_send_s uv_udp_send_t; + typedef struct uv_fs_s uv_fs_t; + typedef struct uv_work_s uv_work_t; + + +Handles represent long-lived objects. Async operations on such handles are +identified using **requests**. A request is short-lived (usually used across +only one callback) and usually indicates one I/O operation on a handle. +Requests are used to preserve context between the initiation and the callback +of individual actions. For example, an UDP socket is represented by +a ``uv_udp_t``, while individual writes to the socket use a ``uv_udp_send_t`` +structure that is passed to the callback after the write is done. + +Handles are setup by a corresponding:: + + uv_TYPE_init(uv_loop_t *, uv_TYPE_t *) + +function. + +Callbacks are functions which are called by libuv whenever an event the watcher +is interested in has taken place. Application specific logic will usually be +implemented in the callback. For example, an IO watcher's callback will receive +the data read from a file, a timer callback will be triggered on timeout and so +on. + +Idling +++++++ + +Here is an example of using an idle handle. The callback is called once on +every turn of the event loop. A use case for idle handles is discussed in +:doc:`utilities`. Let us use an idle watcher to look at the watcher life cycle +and see how ``uv_run()`` will now block because a watcher is present. The idle +watcher is stopped when the count is reached and ``uv_run()`` exits since no +event watchers are active. + +.. rubric:: idle-basic/main.c +.. literalinclude:: ../../code/idle-basic/main.c + :language: c + :emphasize-lines: 6,10,14-17 + +Storing context ++++++++++++++++ + +In callback based programming style you'll often want to pass some 'context' -- +application specific information -- between the call site and the callback. All +handles and requests have a ``void* data`` member which you can set to the +context and cast back in the callback. This is a common pattern used throughout +the C library ecosystem. In addition ``uv_loop_t`` also has a similar data +member. + +---- + +.. [#] Depending on the capacity of the hardware of course. diff --git a/include/libuv/docs/src/guide/eventloops.rst b/include/libuv/docs/src/guide/eventloops.rst new file mode 100644 index 000000000..12244ff6b --- /dev/null +++ b/include/libuv/docs/src/guide/eventloops.rst @@ -0,0 +1,50 @@ +Advanced event loops +==================== + +libuv provides considerable user control over event loops, and you can achieve +interesting results by juggling multiple loops. You can also embed libuv's +event loop into another event loop based library -- imagine a Qt based UI, and +Qt's event loop driving a libuv backend which does intensive system level +tasks. + +Stopping an event loop +~~~~~~~~~~~~~~~~~~~~~~ + +``uv_stop()`` can be used to stop an event loop. The earliest the loop will +stop running is *on the next iteration*, possibly later. This means that events +that are ready to be processed in this iteration of the loop will still be +processed, so ``uv_stop()`` can't be used as a kill switch. When ``uv_stop()`` +is called, the loop **won't** block for i/o on this iteration. The semantics of +these things can be a bit difficult to understand, so let's look at +``uv_run()`` where all the control flow occurs. + +.. rubric:: src/unix/core.c - uv_run +.. literalinclude:: ../../../src/unix/core.c + :language: c + :linenos: + :lines: 304-324 + :emphasize-lines: 10,19,21 + +``stop_flag`` is set by ``uv_stop()``. Now all libuv callbacks are invoked +within the event loop, which is why invoking ``uv_stop()`` in them will still +lead to this iteration of the loop occurring. First libuv updates timers, then +runs pending timer, idle and prepare callbacks, and invokes any pending I/O +callbacks. If you were to call ``uv_stop()`` in any of them, ``stop_flag`` +would be set. This causes ``uv_backend_timeout()`` to return ``0``, which is +why the loop does not block on I/O. If on the other hand, you called +``uv_stop()`` in one of the check handlers, I/O has already finished and is not +affected. + +``uv_stop()`` is useful to shutdown a loop when a result has been computed or +there is an error, without having to ensure that all handlers are stopped one +by one. + +Here is a simple example that stops the loop and demonstrates how the current +iteration of the loop still takes places. + +.. rubric:: uvstop/main.c +.. literalinclude:: ../../code/uvstop/main.c + :language: c + :linenos: + :emphasize-lines: 11 + diff --git a/include/libuv/docs/src/guide/filesystem.rst b/include/libuv/docs/src/guide/filesystem.rst new file mode 100644 index 000000000..2d5f6cb92 --- /dev/null +++ b/include/libuv/docs/src/guide/filesystem.rst @@ -0,0 +1,339 @@ +Filesystem +========== + +Simple filesystem read/write is achieved using the ``uv_fs_*`` functions and the +``uv_fs_t`` struct. + +.. note:: + + The libuv filesystem operations are different from :doc:`socket operations + `. Socket operations use the non-blocking operations provided + by the operating system. Filesystem operations use blocking functions + internally, but invoke these functions in a `thread pool`_ and notify + watchers registered with the event loop when application interaction is + required. + +.. _thread pool: http://docs.libuv.org/en/v1.x/threadpool.html#thread-pool-work-scheduling + +All filesystem functions have two forms - *synchronous* and *asynchronous*. + +The *synchronous* forms automatically get called (and **block**) if the +callback is null. The return value of functions is a :ref:`libuv error code +`. This is usually only useful for synchronous calls. +The *asynchronous* form is called when a callback is passed and the return +value is 0. + +Reading/Writing files +--------------------- + +A file descriptor is obtained using + +.. code-block:: c + + int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, uv_fs_cb cb) + +``flags`` and ``mode`` are standard +`Unix flags `_. +libuv takes care of converting to the appropriate Windows flags. + +File descriptors are closed using + +.. code-block:: c + + int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb) + + +Filesystem operation callbacks have the signature: + +.. code-block:: c + + void callback(uv_fs_t* req); + +Let's see a simple implementation of ``cat``. We start with registering +a callback for when the file is opened: + +.. rubric:: uvcat/main.c - opening a file +.. literalinclude:: ../../code/uvcat/main.c + :language: c + :linenos: + :lines: 41-53 + :emphasize-lines: 4, 6-7 + +The ``result`` field of a ``uv_fs_t`` is the file descriptor in case of the +``uv_fs_open`` callback. If the file is successfully opened, we start reading it. + +.. rubric:: uvcat/main.c - read callback +.. literalinclude:: ../../code/uvcat/main.c + :language: c + :linenos: + :lines: 26-40 + :emphasize-lines: 2,8,12 + +In the case of a read call, you should pass an *initialized* buffer which will +be filled with data before the read callback is triggered. The ``uv_fs_*`` +operations map almost directly to certain POSIX functions, so EOF is indicated +in this case by ``result`` being 0. In the case of streams or pipes, the +``UV_EOF`` constant would have been passed as a status instead. + +Here you see a common pattern when writing asynchronous programs. The +``uv_fs_close()`` call is performed synchronously. *Usually tasks which are +one-off, or are done as part of the startup or shutdown stage are performed +synchronously, since we are interested in fast I/O when the program is going +about its primary task and dealing with multiple I/O sources*. For solo tasks +the performance difference usually is negligible and may lead to simpler code. + +Filesystem writing is similarly simple using ``uv_fs_write()``. *Your callback +will be triggered after the write is complete*. In our case the callback +simply drives the next read. Thus read and write proceed in lockstep via +callbacks. + +.. rubric:: uvcat/main.c - write callback +.. literalinclude:: ../../code/uvcat/main.c + :language: c + :linenos: + :lines: 16-24 + :emphasize-lines: 6 + +.. warning:: + + Due to the way filesystems and disk drives are configured for performance, + a write that 'succeeds' may not be committed to disk yet. + +We set the dominos rolling in ``main()``: + +.. rubric:: uvcat/main.c +.. literalinclude:: ../../code/uvcat/main.c + :language: c + :linenos: + :lines: 55- + :emphasize-lines: 2 + +.. warning:: + + The ``uv_fs_req_cleanup()`` function must always be called on filesystem + requests to free internal memory allocations in libuv. + +Filesystem operations +--------------------- + +All the standard filesystem operations like ``unlink``, ``rmdir``, ``stat`` are +supported asynchronously and have intuitive argument order. They follow the +same patterns as the read/write/open calls, returning the result in the +``uv_fs_t.result`` field. The full list: + +.. rubric:: Filesystem operations +.. code-block:: c + + int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb); + int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, uv_fs_cb cb); + int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, uv_fs_cb cb); + int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); + int uv_fs_write(uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, uv_fs_cb cb); + int uv_fs_copyfile(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, uv_fs_cb cb); + int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb); + int uv_fs_mkdtemp(uv_loop_t* loop, uv_fs_t* req, const char* tpl, uv_fs_cb cb); + int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); + int uv_fs_scandir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, uv_fs_cb cb); + int uv_fs_scandir_next(uv_fs_t* req, uv_dirent_t* ent); + int uv_fs_opendir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); + int uv_fs_readdir(uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, uv_fs_cb cb); + int uv_fs_closedir(uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, uv_fs_cb cb); + int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); + int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb); + int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb); + int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb); + int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb); + int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file file, int64_t offset, uv_fs_cb cb); + int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file out_fd, uv_file in_fd, int64_t in_offset, size_t length, uv_fs_cb cb); + int uv_fs_access(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb); + int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb); + int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, uv_fs_cb cb); + int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime, double mtime, uv_fs_cb cb); + int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); + int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb); + int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, uv_fs_cb cb); + int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); + int uv_fs_realpath(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb); + int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file file, int mode, uv_fs_cb cb); + int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb); + int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb); + int uv_fs_lchown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb); + + +.. _buffers-and-streams: + +Buffers and Streams +------------------- + +The basic I/O handle in libuv is the stream (``uv_stream_t``). TCP sockets, UDP +sockets, and pipes for file I/O and IPC are all treated as stream subclasses. + +Streams are initialized using custom functions for each subclass, then operated +upon using + +.. code-block:: c + + int uv_read_start(uv_stream_t*, uv_alloc_cb alloc_cb, uv_read_cb read_cb); + int uv_read_stop(uv_stream_t*); + int uv_write(uv_write_t* req, uv_stream_t* handle, + const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb); + +The stream based functions are simpler to use than the filesystem ones and +libuv will automatically keep reading from a stream when ``uv_read_start()`` is +called once, until ``uv_read_stop()`` is called. + +The discrete unit of data is the buffer -- ``uv_buf_t``. This is simply +a collection of a pointer to bytes (``uv_buf_t.base``) and the length +(``uv_buf_t.len``). The ``uv_buf_t`` is lightweight and passed around by value. +What does require management is the actual bytes, which have to be allocated +and freed by the application. + +.. ERROR:: + + THIS PROGRAM DOES NOT ALWAYS WORK, NEED SOMETHING BETTER** + +To demonstrate streams we will need to use ``uv_pipe_t``. This allows streaming +local files [#]_. Here is a simple tee utility using libuv. Doing all operations +asynchronously shows the power of evented I/O. The two writes won't block each +other, but we have to be careful to copy over the buffer data to ensure we don't +free a buffer until it has been written. + +The program is to be executed as:: + + ./uvtee + +We start off opening pipes on the files we require. libuv pipes to a file are +opened as bidirectional by default. + +.. rubric:: uvtee/main.c - read on pipes +.. literalinclude:: ../../code/uvtee/main.c + :language: c + :linenos: + :lines: 61-80 + :emphasize-lines: 4,5,15 + +The third argument of ``uv_pipe_init()`` should be set to 1 for IPC using named +pipes. This is covered in :doc:`processes`. The ``uv_pipe_open()`` call +associates the pipe with the file descriptor, in this case ``0`` (standard +input). + +We start monitoring ``stdin``. The ``alloc_buffer`` callback is invoked as new +buffers are required to hold incoming data. ``read_stdin`` will be called with +these buffers. + +.. rubric:: uvtee/main.c - reading buffers +.. literalinclude:: ../../code/uvtee/main.c + :language: c + :linenos: + :lines: 19-22,44-60 + +The standard ``malloc`` is sufficient here, but you can use any memory allocation +scheme. For example, node.js uses its own slab allocator which associates +buffers with V8 objects. + +The read callback ``nread`` parameter is less than 0 on any error. This error +might be EOF, in which case we close all the streams, using the generic close +function ``uv_close()`` which deals with the handle based on its internal type. +Otherwise ``nread`` is a non-negative number and we can attempt to write that +many bytes to the output streams. Finally remember that buffer allocation and +deallocation is application responsibility, so we free the data. + +The allocation callback may return a buffer with length zero if it fails to +allocate memory. In this case, the read callback is invoked with error +UV_ENOBUFS. libuv will continue to attempt to read the stream though, so you +must explicitly call ``uv_close()`` if you want to stop when allocation fails. + +The read callback may be called with ``nread = 0``, indicating that at this +point there is nothing to be read. Most applications will just ignore this. + +.. rubric:: uvtee/main.c - Write to pipe +.. literalinclude:: ../../code/uvtee/main.c + :language: c + :linenos: + :lines: 9-13,23-42 + +``write_data()`` makes a copy of the buffer obtained from read. This buffer +does not get passed through to the write callback trigged on write completion. To +get around this we wrap a write request and a buffer in ``write_req_t`` and +unwrap it in the callbacks. We make a copy so we can free the two buffers from +the two calls to ``write_data`` independently of each other. While acceptable +for a demo program like this, you'll probably want smarter memory management, +like reference counted buffers or a pool of buffers in any major application. + +.. WARNING:: + + If your program is meant to be used with other programs it may knowingly or + unknowingly be writing to a pipe. This makes it susceptible to `aborting on + receiving a SIGPIPE`_. It is a good idea to insert:: + + signal(SIGPIPE, SIG_IGN) + + in the initialization stages of your application. + +.. _aborting on receiving a SIGPIPE: http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#The_special_problem_of_SIGPIPE + +File change events +------------------ + +All modern operating systems provide APIs to put watches on individual files or +directories and be informed when the files are modified. libuv wraps common +file change notification libraries [#fsnotify]_. This is one of the more +inconsistent parts of libuv. File change notification systems are themselves +extremely varied across platforms so getting everything working everywhere is +difficult. To demonstrate, I'm going to build a simple utility which runs +a command whenever any of the watched files change:: + + ./onchange [file2] ... + +The file change notification is started using ``uv_fs_event_init()``: + +.. rubric:: onchange/main.c - The setup +.. literalinclude:: ../../code/onchange/main.c + :language: c + :linenos: + :lines: 26- + :emphasize-lines: 15 + +The third argument is the actual file or directory to monitor. The last +argument, ``flags``, can be: + +.. code-block:: c + + /* + * Flags to be passed to uv_fs_event_start(). + */ + enum uv_fs_event_flags { + UV_FS_EVENT_WATCH_ENTRY = 1, + UV_FS_EVENT_STAT = 2, + UV_FS_EVENT_RECURSIVE = 4 + }; + +``UV_FS_EVENT_WATCH_ENTRY`` and ``UV_FS_EVENT_STAT`` don't do anything (yet). +``UV_FS_EVENT_RECURSIVE`` will start watching subdirectories as well on +supported platforms. + +The callback will receive the following arguments: + + #. ``uv_fs_event_t *handle`` - The handle. The ``path`` field of the handle + is the file on which the watch was set. + #. ``const char *filename`` - If a directory is being monitored, this is the + file which was changed. Only non-``null`` on Linux and Windows. May be ``null`` + even on those platforms. + #. ``int flags`` - one of ``UV_RENAME`` or ``UV_CHANGE``, or a bitwise OR of + both. + #. ``int status`` - Currently 0. + +In our example we simply print the arguments and run the command using +``system()``. + +.. rubric:: onchange/main.c - file change notification callback +.. literalinclude:: ../../code/onchange/main.c + :language: c + :linenos: + :lines: 9-24 + +---- + +.. [#fsnotify] inotify on Linux, FSEvents on Darwin, kqueue on BSDs, + ReadDirectoryChangesW on Windows, event ports on Solaris, unsupported on Cygwin +.. [#] see :ref:`pipes` diff --git a/include/libuv/docs/src/guide/introduction.rst b/include/libuv/docs/src/guide/introduction.rst new file mode 100644 index 000000000..0656e4d85 --- /dev/null +++ b/include/libuv/docs/src/guide/introduction.rst @@ -0,0 +1,75 @@ +Introduction +============ + +This 'book' is a small set of tutorials about using libuv_ as +a high performance evented I/O library which offers the same API on Windows and Unix. + +It is meant to cover the main areas of libuv, but is not a comprehensive +reference discussing every function and data structure. The `official libuv +documentation`_ may be consulted for full details. + +.. _official libuv documentation: http://docs.libuv.org/en/v1.x/ + +This book is still a work in progress, so sections may be incomplete, but +I hope you will enjoy it as it grows. + +Who this book is for +-------------------- + +If you are reading this book, you are either: + +1) a systems programmer, creating low-level programs such as daemons or network + services and clients. You have found that the event loop approach is well + suited for your application and decided to use libuv. + +2) a node.js module writer, who wants to wrap platform APIs + written in C or C++ with a set of (a)synchronous APIs that are exposed to + JavaScript. You will use libuv purely in the context of node.js. For + this you will require some other resources as the book does not cover parts + specific to v8/node.js. + +This book assumes that you are comfortable with the C programming language. + +Background +---------- + +The node.js_ project began in 2009 as a JavaScript environment decoupled +from the browser. Using Google's V8_ and Marc Lehmann's libev_, node.js +combined a model of I/O -- evented -- with a language that was well suited to +the style of programming; due to the way it had been shaped by browsers. As +node.js grew in popularity, it was important to make it work on Windows, but +libev ran only on Unix. The Windows equivalent of kernel event notification +mechanisms like kqueue or (e)poll is IOCP. libuv was an abstraction around libev +or IOCP depending on the platform, providing users an API based on libev. +In the node-v0.9.0 version of libuv `libev was removed`_. + +Since then libuv has continued to mature and become a high quality standalone +library for system programming. Users outside of node.js include Mozilla's +Rust_ programming language, and a variety_ of language bindings. + +This book and the code is based on libuv version `v1.3.0`_. + +Code +---- + +All the code from this book is included as part of the source of the book on +Github. `Clone`_/`Download`_ the book, then build libuv:: + + cd libuv + ./autogen.sh + ./configure + make + +There is no need to ``make install``. To build the examples run ``make`` in the +``code/`` directory. + +.. _Clone: https://github.com/nikhilm/uvbook +.. _Download: https://github.com/nikhilm/uvbook/downloads +.. _v1.3.0: https://github.com/libuv/libuv/tags +.. _V8: https://v8.dev +.. _libev: http://software.schmorp.de/pkg/libev.html +.. _libuv: https://github.com/libuv/libuv +.. _node.js: https://www.nodejs.org +.. _libev was removed: https://github.com/joyent/libuv/issues/485 +.. _Rust: https://www.rust-lang.org +.. _variety: https://github.com/libuv/libuv/blob/v1.x/LINKS.md diff --git a/include/libuv/docs/src/guide/networking.rst b/include/libuv/docs/src/guide/networking.rst new file mode 100644 index 000000000..dcb564313 --- /dev/null +++ b/include/libuv/docs/src/guide/networking.rst @@ -0,0 +1,257 @@ +Networking +========== + +Networking in libuv is not much different from directly using the BSD socket +interface, some things are easier, all are non-blocking, but the concepts stay +the same. In addition libuv offers utility functions to abstract the annoying, +repetitive and low-level tasks like setting up sockets using the BSD socket +structures, DNS lookup, and tweaking various socket parameters. + +The ``uv_tcp_t`` and ``uv_udp_t`` structures are used for network I/O. + +.. NOTE:: + + The code samples in this chapter exist to show certain libuv APIs. They are + not examples of good quality code. They leak memory and don't always close + connections properly. + +TCP +--- + +TCP is a connection oriented, stream protocol and is therefore based on the +libuv streams infrastructure. + +Server +++++++ + +Server sockets proceed by: + +1. ``uv_tcp_init`` the TCP handle. +2. ``uv_tcp_bind`` it. +3. Call ``uv_listen`` on the handle to have a callback invoked whenever a new + connection is established by a client. +4. Use ``uv_accept`` to accept the connection. +5. Use :ref:`stream operations ` to communicate with the + client. + +Here is a simple echo server + +.. rubric:: tcp-echo-server/main.c - The listen socket +.. literalinclude:: ../../code/tcp-echo-server/main.c + :language: c + :linenos: + :lines: 68- + :emphasize-lines: 4-5,7-10 + +You can see the utility function ``uv_ip4_addr`` being used to convert from +a human readable IP address, port pair to the sockaddr_in structure required by +the BSD socket APIs. The reverse can be obtained using ``uv_ip4_name``. + +.. NOTE:: + + There are ``uv_ip6_*`` analogues for the ip4 functions. + +Most of the setup functions are synchronous since they are CPU-bound. +``uv_listen`` is where we return to libuv's callback style. The second +arguments is the backlog queue -- the maximum length of queued connections. + +When a connection is initiated by clients, the callback is required to set up +a handle for the client socket and associate the handle using ``uv_accept``. +In this case we also establish interest in reading from this stream. + +.. rubric:: tcp-echo-server/main.c - Accepting the client +.. literalinclude:: ../../code/tcp-echo-server/main.c + :language: c + :linenos: + :lines: 51-66 + :emphasize-lines: 9-10 + +The remaining set of functions is very similar to the streams example and can +be found in the code. Just remember to call ``uv_close`` when the socket isn't +required. This can be done even in the ``uv_listen`` callback if you are not +interested in accepting the connection. + +Client +++++++ + +Where you do bind/listen/accept on the server, on the client side it's simply +a matter of calling ``uv_tcp_connect``. The same ``uv_connect_cb`` style +callback of ``uv_listen`` is used by ``uv_tcp_connect``. Try:: + + uv_tcp_t* socket = (uv_tcp_t*)malloc(sizeof(uv_tcp_t)); + uv_tcp_init(loop, socket); + + uv_connect_t* connect = (uv_connect_t*)malloc(sizeof(uv_connect_t)); + + struct sockaddr_in dest; + uv_ip4_addr("127.0.0.1", 80, &dest); + + uv_tcp_connect(connect, socket, (const struct sockaddr*)&dest, on_connect); + +where ``on_connect`` will be called after the connection is established. The +callback receives the ``uv_connect_t`` struct, which has a member ``.handle`` +pointing to the socket. + +UDP +--- + +The `User Datagram Protocol`_ offers connectionless, unreliable network +communication. Hence libuv doesn't offer a stream. Instead libuv provides +non-blocking UDP support via the `uv_udp_t` handle (for receiving) and +`uv_udp_send_t` request (for sending) and related functions. That said, the +actual API for reading/writing is very similar to normal stream reads. To look +at how UDP can be used, the example shows the first stage of obtaining an IP +address from a `DHCP`_ server -- DHCP Discover. + +.. note:: + + You will have to run `udp-dhcp` as **root** since it uses well known port + numbers below 1024. + +.. rubric:: udp-dhcp/main.c - Setup and send UDP packets +.. literalinclude:: ../../code/udp-dhcp/main.c + :language: c + :linenos: + :lines: 7-11,104- + :emphasize-lines: 8,10-11,17-18,21 + +.. note:: + + The IP address ``0.0.0.0`` is used to bind to all interfaces. The IP + address ``255.255.255.255`` is a broadcast address meaning that packets + will be sent to all interfaces on the subnet. port ``0`` means that the OS + randomly assigns a port. + +First we setup the receiving socket to bind on all interfaces on port 68 (DHCP +client) and start a read on it. This will read back responses from any DHCP +server that replies. We use the UV_UDP_REUSEADDR flag to play nice with any +other system DHCP clients that are running on this computer on the same port. +Then we setup a similar send socket and use ``uv_udp_send`` to send +a *broadcast message* on port 67 (DHCP server). + +It is **necessary** to set the broadcast flag, otherwise you will get an +``EACCES`` error [#]_. The exact message being sent is not relevant to this +book and you can study the code if you are interested. As usual the read and +write callbacks will receive a status code of < 0 if something went wrong. + +Since UDP sockets are not connected to a particular peer, the read callback +receives an extra parameter about the sender of the packet. + +``nread`` may be zero if there is no more data to be read. If ``addr`` is NULL, +it indicates there is nothing to read (the callback shouldn't do anything), if +not NULL, it indicates that an empty datagram was received from the host at +``addr``. The ``flags`` parameter may be ``UV_UDP_PARTIAL`` if the buffer +provided by your allocator was not large enough to hold the data. *In this case +the OS will discard the data that could not fit* (That's UDP for you!). + +.. rubric:: udp-dhcp/main.c - Reading packets +.. literalinclude:: ../../code/udp-dhcp/main.c + :language: c + :linenos: + :lines: 17-40 + :emphasize-lines: 1,23 + +UDP Options ++++++++++++ + +Time-to-live +~~~~~~~~~~~~ + +The TTL of packets sent on the socket can be changed using ``uv_udp_set_ttl``. + +IPv6 stack only +~~~~~~~~~~~~~~~ + +IPv6 sockets can be used for both IPv4 and IPv6 communication. If you want to +restrict the socket to IPv6 only, pass the ``UV_UDP_IPV6ONLY`` flag to +``uv_udp_bind`` [#]_. + +Multicast +~~~~~~~~~ + +A socket can (un)subscribe to a multicast group using: + +.. code::block:: c + + int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr, const char* interface_addr, uv_membership membership); + +where ``membership`` is ``UV_JOIN_GROUP`` or ``UV_LEAVE_GROUP``. + +The concepts of multicasting are nicely explained in `this guide`_. + +.. _this guide: https://www.tldp.org/HOWTO/Multicast-HOWTO-2.html + +Local loopback of multicast packets is enabled by default [#]_, use +``uv_udp_set_multicast_loop`` to switch it off. + +The packet time-to-live for multicast packets can be changed using +``uv_udp_set_multicast_ttl``. + +Querying DNS +------------ + +libuv provides asynchronous DNS resolution. For this it provides its own +``getaddrinfo`` replacement [#]_. In the callback you can +perform normal socket operations on the retrieved addresses. Let's connect to +Libera.chat to see an example of DNS resolution. + +.. rubric:: dns/main.c +.. literalinclude:: ../../code/dns/main.c + :language: c + :linenos: + :lines: 61- + :emphasize-lines: 12 + +If ``uv_getaddrinfo`` returns non-zero, something went wrong in the setup and +your callback won't be invoked at all. All arguments can be freed immediately +after ``uv_getaddrinfo`` returns. The `hostname`, `servname` and `hints` +structures are documented in `the getaddrinfo man page `_. The +callback can be ``NULL`` in which case the function will run synchronously. + +In the resolver callback, you can pick any IP from the linked list of ``struct +addrinfo(s)``. This also demonstrates ``uv_tcp_connect``. It is necessary to +call ``uv_freeaddrinfo`` in the callback. + +.. rubric:: dns/main.c +.. literalinclude:: ../../code/dns/main.c + :language: c + :linenos: + :lines: 42-60 + :emphasize-lines: 8,16 + +libuv also provides the inverse `uv_getnameinfo`_. + +.. _uv_getnameinfo: http://docs.libuv.org/en/v1.x/dns.html#c.uv_getnameinfo + +Network interfaces +------------------ + +Information about the system's network interfaces can be obtained through libuv +using ``uv_interface_addresses``. This simple program just prints out all the +interface details so you get an idea of the fields that are available. This is +useful to allow your service to bind to IP addresses when it starts. + +.. rubric:: interfaces/main.c +.. literalinclude:: ../../code/interfaces/main.c + :language: c + :linenos: + :emphasize-lines: 9,17 + +``is_internal`` is true for loopback interfaces. Note that if a physical +interface has multiple IPv4/IPv6 addresses, the name will be reported multiple +times, with each address being reported once. + +.. _c-ares: https://c-ares.haxx.se +.. _getaddrinfo: https://man7.org/linux/man-pages/man3/getaddrinfo.3.html + +.. _User Datagram Protocol: https://en.wikipedia.org/wiki/User_Datagram_Protocol +.. _DHCP: https://tools.ietf.org/html/rfc2131 + +---- + +.. [#] https://beej.us/guide/bgnet/html/#broadcast-packetshello-world +.. [#] on Windows only supported on Windows Vista and later. +.. [#] https://www.tldp.org/HOWTO/Multicast-HOWTO-6.html#ss6.1 +.. [#] libuv use the system ``getaddrinfo`` in the libuv threadpool. libuv + v0.8.0 and earlier also included c-ares_ as an alternative, but this has been + removed in v0.9.0. diff --git a/include/libuv/docs/src/guide/processes.rst b/include/libuv/docs/src/guide/processes.rst new file mode 100644 index 000000000..c1278f17f --- /dev/null +++ b/include/libuv/docs/src/guide/processes.rst @@ -0,0 +1,421 @@ +Processes +========= + +libuv offers considerable child process management, abstracting the platform +differences and allowing communication with the child process using streams or +named pipes. + +A common idiom in Unix is for every process to do one thing and do it well. In +such a case, a process often uses multiple child processes to achieve tasks +(similar to using pipes in shells). A multi-process model with messages +may also be easier to reason about compared to one with threads and shared +memory. + +A common refrain against event-based programs is that they cannot take +advantage of multiple cores in modern computers. In a multi-threaded program +the kernel can perform scheduling and assign different threads to different +cores, improving performance. But an event loop has only one thread. The +workaround can be to launch multiple processes instead, with each process +running an event loop, and each process getting assigned to a separate CPU +core. + +Spawning child processes +------------------------ + +The simplest case is when you simply want to launch a process and know when it +exits. This is achieved using ``uv_spawn``. + +.. rubric:: spawn/main.c +.. literalinclude:: ../../code/spawn/main.c + :language: c + :linenos: + :lines: 6-8,15- + :emphasize-lines: 11,13-17 + +.. NOTE:: + + ``options`` is implicitly initialized with zeros since it is a global + variable. If you change ``options`` to a local variable, remember to + initialize it to null out all unused fields:: + + uv_process_options_t options = {0}; + +The ``uv_process_t`` struct only acts as the handle, all options are set via +``uv_process_options_t``. To simply launch a process, you need to set only the +``file`` and ``args`` fields. ``file`` is the program to execute. Since +``uv_spawn`` uses :man:`execvp(3)` internally, there is no need to supply the full +path. Finally as per underlying conventions, **the arguments array has to be +one larger than the number of arguments, with the last element being NULL**. + +After the call to ``uv_spawn``, ``uv_process_t.pid`` will contain the process +ID of the child process. + +The exit callback will be invoked with the *exit status* and the type of *signal* +which caused the exit. + +.. rubric:: spawn/main.c +.. literalinclude:: ../../code/spawn/main.c + :language: c + :linenos: + :lines: 9-12 + :emphasize-lines: 3 + +It is **required** to close the process watcher after the process exits. + +Changing process parameters +--------------------------- + +Before the child process is launched you can control the execution environment +using fields in ``uv_process_options_t``. + +Change execution directory +++++++++++++++++++++++++++ + +Set ``uv_process_options_t.cwd`` to the corresponding directory. + +Set environment variables ++++++++++++++++++++++++++ + +``uv_process_options_t.env`` is a null-terminated array of strings, each of the +form ``VAR=VALUE`` used to set up the environment variables for the process. Set +this to ``NULL`` to inherit the environment from the parent (this) process. + +Option flags +++++++++++++ + +Setting ``uv_process_options_t.flags`` to a bitwise OR of the following flags, +modifies the child process behaviour: + +* ``UV_PROCESS_SETUID`` - sets the child's execution user ID to ``uv_process_options_t.uid``. +* ``UV_PROCESS_SETGID`` - sets the child's execution group ID to ``uv_process_options_t.gid``. + +Changing the UID/GID is only supported on Unix, ``uv_spawn`` will fail on +Windows with ``UV_ENOTSUP``. + +* ``UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS`` - No quoting or escaping of + ``uv_process_options_t.args`` is done on Windows. Ignored on Unix. +* ``UV_PROCESS_DETACHED`` - Starts the child process in a new session, which + will keep running after the parent process exits. See example below. + +Detaching processes +------------------- + +Passing the flag ``UV_PROCESS_DETACHED`` can be used to launch daemons, or +child processes which are independent of the parent so that the parent exiting +does not affect it. + +.. rubric:: detach/main.c +.. literalinclude:: ../../code/detach/main.c + :language: c + :linenos: + :lines: 9-30 + :emphasize-lines: 12,19 + +Just remember that the handle is still monitoring the child, so your program +won't exit. Use ``uv_unref()`` if you want to be more *fire-and-forget*. + +Sending signals to processes +---------------------------- + +libuv wraps the standard ``kill(2)`` system call on Unix and implements one +with similar semantics on Windows, with *one caveat*: all of ``SIGTERM``, +``SIGINT`` and ``SIGKILL``, lead to termination of the process. The signature +of ``uv_kill`` is:: + + uv_err_t uv_kill(int pid, int signum); + +For processes started using libuv, you may use ``uv_process_kill`` instead, +which accepts the ``uv_process_t`` watcher as the first argument, rather than +the pid. In this case, **remember to call** ``uv_close`` on the watcher. + +Signals +------- + +libuv provides wrappers around Unix signals with `some Windows support +`_ as well. + +Use ``uv_signal_init()`` to initialize +a handle and associate it with a loop. To listen for particular signals on +that handler, use ``uv_signal_start()`` with the handler function. Each handler +can only be associated with one signal number, with subsequent calls to +``uv_signal_start()`` overwriting earlier associations. Use ``uv_signal_stop()`` to +stop watching. Here is a small example demonstrating the various possibilities: + +.. rubric:: signal/main.c +.. literalinclude:: ../../code/signal/main.c + :language: c + :linenos: + :emphasize-lines: 17-18,27-28 + +.. NOTE:: + + ``uv_run(loop, UV_RUN_NOWAIT)`` is similar to ``uv_run(loop, UV_RUN_ONCE)`` + in that it will process only one event. UV_RUN_ONCE blocks if there are no + pending events, while UV_RUN_NOWAIT will return immediately. We use NOWAIT + so that one of the loops isn't starved because the other one has no pending + activity. + +Send ``SIGUSR1`` to the process, and you'll find the handler being invoked +4 times, one for each ``uv_signal_t``. The handler just stops each handle, +so that the program exits. This sort of dispatch to all handlers is very +useful. A server using multiple event loops could ensure that all data was +safely saved before termination, simply by every loop adding a watcher for +``SIGINT``. + +Child Process I/O +----------------- + +A normal, newly spawned process has its own set of file descriptors, with 0, +1 and 2 being ``stdin``, ``stdout`` and ``stderr`` respectively. Sometimes you +may want to share file descriptors with the child. For example, perhaps your +applications launches a sub-command and you want any errors to go in the log +file, but ignore ``stdout``. For this you'd like to have ``stderr`` of the +child be the same as the stderr of the parent. In this case, libuv supports +*inheriting* file descriptors. In this sample, we invoke the test program, +which is: + +.. rubric:: proc-streams/test.c +.. literalinclude:: ../../code/proc-streams/test.c + :language: c + +The actual program ``proc-streams`` runs this while sharing only ``stderr``. +The file descriptors of the child process are set using the ``stdio`` field in +``uv_process_options_t``. First set the ``stdio_count`` field to the number of +file descriptors being set. ``uv_process_options_t.stdio`` is an array of +``uv_stdio_container_t``, which is: + +.. code-block:: c + + typedef struct uv_stdio_container_s { + uv_stdio_flags flags; + + union { + uv_stream_t* stream; + int fd; + } data; + } uv_stdio_container_t; + +where flags can have several values. Use ``UV_IGNORE`` if it isn't going to be +used. If the first three ``stdio`` fields are marked as ``UV_IGNORE`` they'll +redirect to ``/dev/null``. + +Since we want to pass on an existing descriptor, we'll use ``UV_INHERIT_FD``. +Then we set the ``fd`` to ``stderr``. + +.. rubric:: proc-streams/main.c +.. literalinclude:: ../../code/proc-streams/main.c + :language: c + :linenos: + :lines: 15-17,27- + :emphasize-lines: 6,10,11,12 + +If you run ``proc-stream`` you'll see that only the line "This is stderr" will +be displayed. Try marking ``stdout`` as being inherited and see the output. + +It is dead simple to apply this redirection to streams. By setting ``flags`` +to ``UV_INHERIT_STREAM`` and setting ``data.stream`` to the stream in the +parent process, the child process can treat that stream as standard I/O. This +can be used to implement something like CGI_. + +.. _CGI: https://en.wikipedia.org/wiki/Common_Gateway_Interface + +A sample CGI script/executable is: + +.. rubric:: cgi/tick.c +.. literalinclude:: ../../code/cgi/tick.c + :language: c + +The CGI server combines the concepts from this chapter and :doc:`networking` so +that every client is sent ten ticks after which that connection is closed. + +.. rubric:: cgi/main.c +.. literalinclude:: ../../code/cgi/main.c + :language: c + :linenos: + :lines: 49-63 + :emphasize-lines: 10 + +Here we simply accept the TCP connection and pass on the socket (*stream*) to +``invoke_cgi_script``. + +.. rubric:: cgi/main.c +.. literalinclude:: ../../code/cgi/main.c + :language: c + :linenos: + :lines: 16, 25-45 + :emphasize-lines: 8-9,18,20 + +The ``stdout`` of the CGI script is set to the socket so that whatever our tick +script prints, gets sent to the client. By using processes, we can offload the +read/write buffering to the operating system, so in terms of convenience this +is great. Just be warned that creating processes is a costly task. + +.. _pipes: + +Parent-child IPC +---------------- + +A parent and child can have one or two way communication over a pipe created by +settings ``uv_stdio_container_t.flags`` to a bit-wise combination of +``UV_CREATE_PIPE`` and ``UV_READABLE_PIPE`` or ``UV_WRITABLE_PIPE``. The +read/write flag is from the perspective of the child process. In this case, +the ``uv_stream_t* stream`` field must be set to point to an initialized, +unopened ``uv_pipe_t`` instance. + +New stdio Pipes ++++++++++++++++ + +The ``uv_pipe_t`` structure represents more than just `pipe(7)`_ (or ``|``), +but supports any streaming file-like objects. On Windows, the only object of +that description is the `Named Pipe`_. On Unix, this could be any of `Unix +Domain Socket`_, or derived from `mkfifo(1)`_, or it could actually be a +`pipe(7)`_. When ``uv_spawn`` initializes a ``uv_pipe_t`` due to the +`UV_CREATE_PIPE` flag, it opts for creating a `socketpair(2)`_. + +This is intended for the purpose of allowing multiple libuv processes to +communicate with IPC. This is discussed below. + +.. _pipe(7): https://man7.org/linux/man-pages/man7/pipe.7.html +.. _mkfifo(1): https://man7.org/linux/man-pages/man1/mkfifo.1.html +.. _socketpair(2): https://man7.org/linux/man-pages/man2/socketpair.2.html +.. _Unix Domain Socket: https://man7.org/linux/man-pages/man7/unix.7.html +.. _Named Pipe: https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipes + + +Arbitrary process IPC ++++++++++++++++++++++ + +Since domain sockets [#]_ can have a well known name and a location in the +file-system they can be used for IPC between unrelated processes. The D-BUS_ +system used by open source desktop environments uses domain sockets for event +notification. Various applications can then react when a contact comes online +or new hardware is detected. The MySQL server also runs a domain socket on +which clients can interact with it. + +.. _D-BUS: https://www.freedesktop.org/wiki/Software/dbus + +When using domain sockets, a client-server pattern is usually followed with the +creator/owner of the socket acting as the server. After the initial setup, +messaging is no different from TCP, so we'll re-use the echo server example. + +.. rubric:: pipe-echo-server/main.c +.. literalinclude:: ../../code/pipe-echo-server/main.c + :language: c + :linenos: + :lines: 70- + :emphasize-lines: 5,10,14 + +We name the socket ``echo.sock`` which means it will be created in the local +directory. This socket now behaves no different from TCP sockets as far as +the stream API is concerned. You can test this server using `socat`_:: + + $ socat - /path/to/socket + +A client which wants to connect to a domain socket will use:: + + void uv_pipe_connect(uv_connect_t *req, uv_pipe_t *handle, const char *name, uv_connect_cb cb); + +where ``name`` will be ``echo.sock`` or similar. On Unix systems, ``name`` must +point to a valid file (e.g. ``/tmp/echo.sock``). On Windows, ``name`` follows a +``\\?\pipe\echo.sock`` format. + +.. _socat: http://www.dest-unreach.org/socat/ + +Sending file descriptors over pipes ++++++++++++++++++++++++++++++++++++ + +The cool thing about domain sockets is that file descriptors can be exchanged +between processes by sending them over a domain socket. This allows processes +to hand off their I/O to other processes. Applications include load-balancing +servers, worker processes and other ways to make optimum use of CPU. libuv only +supports sending **TCP sockets or other pipes** over pipes for now. + +To demonstrate, we will look at a echo server implementation that hands of +clients to worker processes in a round-robin fashion. This program is a bit +involved, and while only snippets are included in the book, it is recommended +to read the full code to really understand it. + +The worker process is quite simple, since the file-descriptor is handed over to +it by the master. + +.. rubric:: multi-echo-server/worker.c +.. literalinclude:: ../../code/multi-echo-server/worker.c + :language: c + :linenos: + :lines: 7-9,81- + :emphasize-lines: 6-8 + +``queue`` is the pipe connected to the master process on the other end, along +which new file descriptors get sent. It is important to set the ``ipc`` +argument of ``uv_pipe_init`` to 1 to indicate this pipe will be used for +inter-process communication! Since the master will write the file handle to the +standard input of the worker, we connect the pipe to ``stdin`` using +``uv_pipe_open``. + +.. rubric:: multi-echo-server/worker.c +.. literalinclude:: ../../code/multi-echo-server/worker.c + :language: c + :linenos: + :lines: 51-79 + :emphasize-lines: 10,15,20 + +First we call ``uv_pipe_pending_count()`` to ensure that a handle is available +to read out. If your program could deal with different types of handles, +``uv_pipe_pending_type()`` can be used to determine the type. +Although ``accept`` seems odd in this code, it actually makes sense. What +``accept`` traditionally does is get a file descriptor (the client) from +another file descriptor (The listening socket). Which is exactly what we do +here. Fetch the file descriptor (``client``) from ``queue``. From this point +the worker does standard echo server stuff. + +Turning now to the master, let's take a look at how the workers are launched to +allow load balancing. + +.. rubric:: multi-echo-server/main.c +.. literalinclude:: ../../code/multi-echo-server/main.c + :language: c + :linenos: + :lines: 9-13 + +The ``child_worker`` structure wraps the process, and the pipe between the +master and the individual process. + +.. rubric:: multi-echo-server/main.c +.. literalinclude:: ../../code/multi-echo-server/main.c + :language: c + :linenos: + :lines: 51,61-95 + :emphasize-lines: 17,20-21 + +In setting up the workers, we use the nifty libuv function ``uv_cpu_info`` to +get the number of CPUs so we can launch an equal number of workers. Again it is +important to initialize the pipe acting as the IPC channel with the third +argument as 1. We then indicate that the child process' ``stdin`` is to be +a readable pipe (from the point of view of the child). Everything is +straightforward till here. The workers are launched and waiting for file +descriptors to be written to their standard input. + +It is in ``on_new_connection`` (the TCP infrastructure is initialized in +``main()``), that we accept the client socket and pass it along to the next +worker in the round-robin. + +.. rubric:: multi-echo-server/main.c +.. literalinclude:: ../../code/multi-echo-server/main.c + :language: c + :linenos: + :lines: 31-49 + :emphasize-lines: 9,12-13 + +The ``uv_write2`` call handles all the abstraction and it is simply a matter of +passing in the handle (``client``) as the right argument. With this our +multi-process echo server is operational. + +Thanks to Kyle for `pointing out`_ that ``uv_write2()`` requires a non-empty +buffer even when sending handles. + +.. _pointing out: https://github.com/nikhilm/uvbook/issues/56 + +---- + +.. [#] In this section domain sockets stands in for named pipes on Windows as + well. diff --git a/include/libuv/docs/src/guide/threads.rst b/include/libuv/docs/src/guide/threads.rst new file mode 100644 index 000000000..3990e4428 --- /dev/null +++ b/include/libuv/docs/src/guide/threads.rst @@ -0,0 +1,397 @@ +Threads +======= + +Wait a minute? Why are we on threads? Aren't event loops supposed to be **the +way** to do *web-scale programming*? Well... no. Threads are still the medium in +which processors do their jobs. Threads are therefore mighty useful sometimes, even +though you might have to wade through various synchronization primitives. + +Threads are used internally to fake the asynchronous nature of all of the system +calls. libuv also uses threads to allow you, the application, to perform a task +asynchronously that is actually blocking, by spawning a thread and collecting +the result when it is done. + +Today there are two predominant thread libraries: the Windows threads +implementation and POSIX's :man:`pthreads(7)`. libuv's thread API is analogous to +the pthreads API and often has similar semantics. + +A notable aspect of libuv's thread facilities is that it is a self contained +section within libuv. Whereas other features intimately depend on the event +loop and callback principles, threads are complete agnostic, they block as +required, signal errors directly via return values, and, as shown in the +:ref:`first example `, don't even require a running +event loop. + +libuv's thread API is also very limited since the semantics and syntax of +threads are different on all platforms, with different levels of completeness. + +This chapter makes the following assumption: **There is only one event loop, +running in one thread (the main thread)**. No other thread interacts +with the event loop (except using ``uv_async_send``). + +Core thread operations +---------------------- + +There isn't much here, you just start a thread using ``uv_thread_create()`` and +wait for it to close using ``uv_thread_join()``. + +.. _thread-create-example: + +.. rubric:: thread-create/main.c +.. literalinclude:: ../../code/thread-create/main.c + :language: c + :linenos: + :lines: 26-36 + :emphasize-lines: 3-7 + +.. tip:: + + ``uv_thread_t`` is just an alias for ``pthread_t`` on Unix, but this is an + implementation detail, avoid depending on it to always be true. + +The second parameter is the function which will serve as the entry point for +the thread, the last parameter is a ``void *`` argument which can be used to pass +custom parameters to the thread. The function ``hare`` will now run in a separate +thread, scheduled pre-emptively by the operating system: + +.. rubric:: thread-create/main.c +.. literalinclude:: ../../code/thread-create/main.c + :language: c + :linenos: + :lines: 6-14 + :emphasize-lines: 2 + +Unlike ``pthread_join()`` which allows the target thread to pass back a value to +the calling thread using a second parameter, ``uv_thread_join()`` does not. To +send values use :ref:`inter-thread-communication`. + +Synchronization Primitives +-------------------------- + +This section is purposely spartan. This book is not about threads, so I only +catalogue any surprises in the libuv APIs here. For the rest you can look at +the :man:`pthreads(7)` man pages. + +Mutexes +~~~~~~~ + +The mutex functions are a **direct** map to the pthread equivalents. + +.. rubric:: libuv mutex functions +.. code-block:: c + + int uv_mutex_init(uv_mutex_t* handle); + int uv_mutex_init_recursive(uv_mutex_t* handle); + void uv_mutex_destroy(uv_mutex_t* handle); + void uv_mutex_lock(uv_mutex_t* handle); + int uv_mutex_trylock(uv_mutex_t* handle); + void uv_mutex_unlock(uv_mutex_t* handle); + +The ``uv_mutex_init()``, ``uv_mutex_init_recursive()`` and ``uv_mutex_trylock()`` +functions will return 0 on success, and an error code otherwise. + +If `libuv` has been compiled with debugging enabled, ``uv_mutex_destroy()``, +``uv_mutex_lock()`` and ``uv_mutex_unlock()`` will ``abort()`` on error. +Similarly ``uv_mutex_trylock()`` will abort if the error is anything *other +than* ``EAGAIN`` or ``EBUSY``. + +Recursive mutexes are supported, but you should not rely on them. Also, they +should not be used with ``uv_cond_t`` variables. + +The default BSD mutex implementation will raise an error if a thread which has +locked a mutex attempts to lock it again. For example, a construct like:: + + uv_mutex_init(a_mutex); + uv_mutex_lock(a_mutex); + uv_thread_create(thread_id, entry, (void *)a_mutex); + uv_mutex_lock(a_mutex); + // more things here + +can be used to wait until another thread initializes some stuff and then +unlocks ``a_mutex`` but will lead to your program crashing if in debug mode, or +return an error in the second call to ``uv_mutex_lock()``. + +.. note:: + + Mutexes on Windows are always recursive. + +Locks +~~~~~ + +Read-write locks are a more granular access mechanism. Two readers can access +shared memory at the same time. A writer may not acquire the lock when it is +held by a reader. A reader or writer may not acquire a lock when a writer is +holding it. Read-write locks are frequently used in databases. Here is a toy +example. + +.. rubric:: locks/main.c - simple rwlocks +.. literalinclude:: ../../code/locks/main.c + :language: c + :linenos: + :emphasize-lines: 13,16,27,31,42,55 + +Run this and observe how the readers will sometimes overlap. In case of +multiple writers, schedulers will usually give them higher priority, so if you +add two writers, you'll see that both writers tend to finish first before the +readers get a chance again. + +We also use barriers in the above example so that the main thread can wait for +all readers and writers to indicate they have ended. + +Others +~~~~~~ + +libuv also supports semaphores_, `condition variables`_ and barriers_ with APIs +very similar to their pthread counterparts. + +.. _semaphores: https://en.wikipedia.org/wiki/Semaphore_(programming) +.. _condition variables: https://en.wikipedia.org/wiki/Monitor_(synchronization)#Condition_variables_2 +.. _barriers: https://en.wikipedia.org/wiki/Barrier_(computer_science) + +In addition, libuv provides a convenience function ``uv_once()``. Multiple +threads can attempt to call ``uv_once()`` with a given guard and a function +pointer, **only the first one will win, the function will be called once and +only once**:: + + /* Initialize guard */ + static uv_once_t once_only = UV_ONCE_INIT; + + int i = 0; + + void increment() { + i++; + } + + void thread1() { + /* ... work */ + uv_once(once_only, increment); + } + + void thread2() { + /* ... work */ + uv_once(once_only, increment); + } + + int main() { + /* ... spawn threads */ + } + +After all threads are done, ``i == 1``. + +.. _libuv-work-queue: + +libuv v0.11.11 onwards also added a ``uv_key_t`` struct and api_ for +thread-local storage. + +.. _api: http://docs.libuv.org/en/v1.x/threading.html#thread-local-storage + +libuv work queue +---------------- + +``uv_queue_work()`` is a convenience function that allows an application to run +a task in a separate thread, and have a callback that is triggered when the +task is done. A seemingly simple function, what makes ``uv_queue_work()`` +tempting is that it allows potentially any third-party libraries to be used +with the event-loop paradigm. When you use event loops, it is *imperative to +make sure that no function which runs periodically in the loop thread blocks +when performing I/O or is a serious CPU hog*, because this means that the loop +slows down and events are not being handled at full capacity. + +However, a lot of existing code out there features blocking functions (for example +a routine which performs I/O under the hood) to be used with threads if you +want responsiveness (the classic 'one thread per client' server model), and +getting them to play with an event loop library generally involves rolling your +own system of running the task in a separate thread. libuv just provides +a convenient abstraction for this. + +Here is a simple example inspired by `node.js is cancer`_. We are going to +calculate fibonacci numbers, sleeping a bit along the way, but run it in +a separate thread so that the blocking and CPU bound task does not prevent the +event loop from performing other activities. + +.. rubric:: queue-work/main.c - lazy fibonacci +.. literalinclude:: ../../code/queue-work/main.c + :language: c + :linenos: + :lines: 17-29 + +The actual task function is simple, nothing to show that it is going to be +run in a separate thread. The ``uv_work_t`` structure is the clue. You can pass +arbitrary data through it using the ``void* data`` field and use it to +communicate to and from the thread. But be sure you are using proper locks if +you are changing things while both threads may be running. + +The trigger is ``uv_queue_work``: + +.. rubric:: queue-work/main.c +.. literalinclude:: ../../code/queue-work/main.c + :language: c + :linenos: + :lines: 31-44 + :emphasize-lines: 10 + +The thread function will be launched in a separate thread, passed the +``uv_work_t`` structure and once the function returns, the *after* function +will be called on the thread the event loop is running in. It will be passed +the same structure. + +For writing wrappers to blocking libraries, a common :ref:`pattern ` +is to use a baton to exchange data. + +Since libuv version `0.9.4` an additional function, ``uv_cancel()``, is +available. This allows you to cancel tasks on the libuv work queue. Only tasks +that *are yet to be started* can be cancelled. If a task has *already started +executing, or it has finished executing*, ``uv_cancel()`` **will fail**. + +``uv_cancel()`` is useful to cleanup pending tasks if the user requests +termination. For example, a music player may queue up multiple directories to +be scanned for audio files. If the user terminates the program, it should quit +quickly and not wait until all pending requests are run. + +Let's modify the fibonacci example to demonstrate ``uv_cancel()``. We first set +up a signal handler for termination. + +.. rubric:: queue-cancel/main.c +.. literalinclude:: ../../code/queue-cancel/main.c + :language: c + :linenos: + :lines: 43- + +When the user triggers the signal by pressing ``Ctrl+C`` we send +``uv_cancel()`` to all the workers. ``uv_cancel()`` will return ``0`` for those that are already executing or finished. + +.. rubric:: queue-cancel/main.c +.. literalinclude:: ../../code/queue-cancel/main.c + :language: c + :linenos: + :lines: 33-41 + :emphasize-lines: 6 + +For tasks that do get cancelled successfully, the *after* function is called +with ``status`` set to ``UV_ECANCELED``. + +.. rubric:: queue-cancel/main.c +.. literalinclude:: ../../code/queue-cancel/main.c + :language: c + :linenos: + :lines: 28-31 + :emphasize-lines: 2 + +``uv_cancel()`` can also be used with ``uv_fs_t`` and ``uv_getaddrinfo_t`` +requests. For the filesystem family of functions, ``uv_fs_t.errorno`` will be +set to ``UV_ECANCELED``. + +.. TIP:: + + A well designed program would have a way to terminate long running workers + that have already started executing. Such a worker could periodically check + for a variable that only the main process sets to signal termination. + +.. _inter-thread-communication: + +Inter-thread communication +-------------------------- + +Sometimes you want various threads to actually send each other messages *while* +they are running. For example you might be running some long duration task in +a separate thread (perhaps using ``uv_queue_work``) but want to notify progress +to the main thread. This is a simple example of having a download manager +informing the user of the status of running downloads. + +.. rubric:: progress/main.c +.. literalinclude:: ../../code/progress/main.c + :language: c + :linenos: + :lines: 7-8,35- + :emphasize-lines: 2,11 + +The async thread communication works *on loops* so although any thread can be +the message sender, only threads with libuv loops can be receivers (or rather +the loop is the receiver). libuv will invoke the callback (``print_progress``) +with the async watcher whenever it receives a message. + +.. warning:: + + It is important to realize that since the message send is *async*, the callback + may be invoked immediately after ``uv_async_send`` is called in another + thread, or it may be invoked after some time. libuv may also combine + multiple calls to ``uv_async_send`` and invoke your callback only once. The + only guarantee that libuv makes is -- The callback function is called *at + least once* after the call to ``uv_async_send``. If you have no pending + calls to ``uv_async_send``, the callback won't be called. If you make two + or more calls, and libuv hasn't had a chance to run the callback yet, it + *may* invoke your callback *only once* for the multiple invocations of + ``uv_async_send``. Your callback will never be called twice for just one + event. + +.. rubric:: progress/main.c +.. literalinclude:: ../../code/progress/main.c + :language: c + :linenos: + :lines: 10-24 + :emphasize-lines: 7-8 + +In the download function, we modify the progress indicator and queue the message +for delivery with ``uv_async_send``. Remember: ``uv_async_send`` is also +non-blocking and will return immediately. + +.. rubric:: progress/main.c +.. literalinclude:: ../../code/progress/main.c + :language: c + :linenos: + :lines: 31-34 + +The callback is a standard libuv pattern, extracting the data from the watcher. + +Finally it is important to remember to clean up the watcher. + +.. rubric:: progress/main.c +.. literalinclude:: ../../code/progress/main.c + :language: c + :linenos: + :lines: 26-29 + :emphasize-lines: 3 + +After this example, which showed the abuse of the ``data`` field, bnoordhuis_ +pointed out that using the ``data`` field is not thread safe, and +``uv_async_send()`` is actually only meant to wake up the event loop. Use +a mutex or rwlock to ensure accesses are performed in the right order. + +.. note:: + + mutexes and rwlocks **DO NOT** work inside a signal handler, whereas + ``uv_async_send`` does. + +One use case where ``uv_async_send`` is required is when interoperating with +libraries that require thread affinity for their functionality. For example in +node.js, a v8 engine instance, contexts and its objects are bound to the thread +that the v8 instance was started in. Interacting with v8 data structures from +another thread can lead to undefined results. Now consider some node.js module +which binds a third party library. It may go something like this: + +1. In node, the third party library is set up with a JavaScript callback to be + invoked for more information:: + + var lib = require('lib'); + lib.on_progress(function() { + console.log("Progress"); + }); + + lib.do(); + + // do other stuff + +2. ``lib.do`` is supposed to be non-blocking but the third party lib is + blocking, so the binding uses ``uv_queue_work``. + +3. The actual work being done in a separate thread wants to invoke the progress + callback, but cannot directly call into v8 to interact with JavaScript. So + it uses ``uv_async_send``. + +4. The async callback, invoked in the main loop thread, which is the v8 thread, + then interacts with v8 to invoke the JavaScript callback. + +---- + +.. _node.js is cancer: http://widgetsandshit.com/teddziuba/2011/10/node-js-is-cancer.html +.. _bnoordhuis: https://github.com/bnoordhuis diff --git a/include/libuv/docs/src/guide/utilities.rst b/include/libuv/docs/src/guide/utilities.rst new file mode 100644 index 000000000..4657b1b0b --- /dev/null +++ b/include/libuv/docs/src/guide/utilities.rst @@ -0,0 +1,450 @@ +Utilities +========= + +This chapter catalogues tools and techniques which are useful for common tasks. +The `libev man page`_ already covers some patterns which can be adopted to +libuv through simple API changes. It also covers parts of the libuv API that +don't require entire chapters dedicated to them. + +Timers +------ + +Timers invoke the callback after a certain time has elapsed since the timer was +started. libuv timers can also be set to invoke at regular intervals instead of +just once. + +Simple use is to init a watcher and start it with a ``timeout``, and optional ``repeat``. +Timers can be stopped at any time. + +.. code-block:: c + + uv_timer_t timer_req; + + uv_timer_init(loop, &timer_req); + uv_timer_start(&timer_req, callback, 5000, 2000); + +will start a repeating timer, which first starts 5 seconds (the ``timeout``) after the execution +of ``uv_timer_start``, then repeats every 2 seconds (the ``repeat``). Use: + +.. code-block:: c + + uv_timer_stop(&timer_req); + +to stop the timer. This can be used safely from within the callback as well. + +The repeat interval can be modified at any time with:: + + uv_timer_set_repeat(uv_timer_t *timer, int64_t repeat); + +which will take effect **when possible**. If this function is called from +a timer callback, it means: + +* If the timer was non-repeating, the timer has already been stopped. Use + ``uv_timer_start`` again. +* If the timer is repeating, the next timeout has already been scheduled, so + the old repeat interval will be used once more before the timer switches to + the new interval. + +The utility function:: + + int uv_timer_again(uv_timer_t *) + +applies **only to repeating timers** and is equivalent to stopping the timer +and then starting it with both initial ``timeout`` and ``repeat`` set to the +old ``repeat`` value. If the timer hasn't been started it fails (error code +``UV_EINVAL``) and returns -1. + +An actual timer example is in the :ref:`reference count section +`. + +.. _reference-count: + +Event loop reference count +-------------------------- + +The event loop only runs as long as there are active handles. This system +works by having every handle increase the reference count of the event loop +when it is started and decreasing the reference count when stopped. It is also +possible to manually change the reference count of handles using:: + + void uv_ref(uv_handle_t*); + void uv_unref(uv_handle_t*); + +These functions can be used to allow a loop to exit even when a watcher is +active or to use custom objects to keep the loop alive. + +The latter can be used with interval timers. You might have a garbage collector +which runs every X seconds, or your network service might send a heartbeat to +others periodically, but you don't want to have to stop them along all clean +exit paths or error scenarios. Or you want the program to exit when all your +other watchers are done. In that case just unref the timer immediately after +creation so that if it is the only watcher running then ``uv_run`` will still +exit. + +This is also used in node.js where some libuv methods are being bubbled up to +the JS API. A ``uv_handle_t`` (the superclass of all watchers) is created per +JS object and can be ref/unrefed. + +.. rubric:: ref-timer/main.c +.. literalinclude:: ../../code/ref-timer/main.c + :language: c + :linenos: + :lines: 5-8, 17- + :emphasize-lines: 9 + +We initialize the garbage collector timer, then immediately ``unref`` it. +Observe how after 9 seconds, when the fake job is done, the program +automatically exits, even though the garbage collector is still running. + +Idler pattern +------------- + +The callbacks of idle handles are invoked once per event loop. The idle +callback can be used to perform some very low priority activity. For example, +you could dispatch a summary of the daily application performance to the +developers for analysis during periods of idleness, or use the application's +CPU time to perform SETI calculations :) An idle watcher is also useful in +a GUI application. Say you are using an event loop for a file download. If the +TCP socket is still being established and no other events are present your +event loop will pause (**block**), which means your progress bar will freeze +and the user will face an unresponsive application. In such a case queue up and +idle watcher to keep the UI operational. + +.. rubric:: idle-compute/main.c +.. literalinclude:: ../../code/idle-compute/main.c + :language: c + :linenos: + :lines: 5-9, 34- + :emphasize-lines: 13 + +Here we initialize the idle watcher and queue it up along with the actual +events we are interested in. ``crunch_away`` will now be called repeatedly +until the user types something and presses Return. Then it will be interrupted +for a brief amount as the loop deals with the input data, after which it will +keep calling the idle callback again. + +.. rubric:: idle-compute/main.c +.. literalinclude:: ../../code/idle-compute/main.c + :language: c + :linenos: + :lines: 10-19 + +.. _baton: + +Passing data to worker thread +----------------------------- + +When using ``uv_queue_work`` you'll usually need to pass complex data through +to the worker thread. The solution is to use a ``struct`` and set +``uv_work_t.data`` to point to it. A slight variation is to have the +``uv_work_t`` itself as the first member of this struct (called a baton [#]_). +This allows cleaning up the work request and all the data in one free call. + +.. code-block:: c + :linenos: + :emphasize-lines: 2 + + struct ftp_baton { + uv_work_t req; + char *host; + int port; + char *username; + char *password; + } + +.. code-block:: c + :linenos: + :emphasize-lines: 2 + + ftp_baton *baton = (ftp_baton*) malloc(sizeof(ftp_baton)); + baton->req.data = (void*) baton; + baton->host = strdup("my.webhost.com"); + baton->port = 21; + // ... + + uv_queue_work(loop, &baton->req, ftp_session, ftp_cleanup); + +Here we create the baton and queue the task. + +Now the task function can extract the data it needs: + +.. code-block:: c + :linenos: + :emphasize-lines: 2, 12 + + void ftp_session(uv_work_t *req) { + ftp_baton *baton = (ftp_baton*) req->data; + + fprintf(stderr, "Connecting to %s\n", baton->host); + } + + void ftp_cleanup(uv_work_t *req) { + ftp_baton *baton = (ftp_baton*) req->data; + + free(baton->host); + // ... + free(baton); + } + +We then free the baton which also frees the watcher. + +External I/O with polling +------------------------- + +Usually third-party libraries will handle their own I/O, and keep track of +their sockets and other files internally. In this case it isn't possible to use +the standard stream I/O operations, but the library can still be integrated +into the libuv event loop. All that is required is that the library allow you +to access the underlying file descriptors and provide functions that process +tasks in small increments as decided by your application. Some libraries though +will not allow such access, providing only a standard blocking function which +will perform the entire I/O transaction and only then return. It is unwise to +use these in the event loop thread, use the :ref:`threadpool` instead. Of +course, this will also mean losing granular control on the library. + +The ``uv_poll`` section of libuv simply watches file descriptors using the +operating system notification mechanism. In some sense, all the I/O operations +that libuv implements itself are also backed by ``uv_poll`` like code. Whenever +the OS notices a change of state in file descriptors being polled, libuv will +invoke the associated callback. + +Here we will walk through a simple download manager that will use libcurl_ to +download files. Rather than give all control to libcurl, we'll instead be +using the libuv event loop, and use the non-blocking, async multi_ interface to +progress with the download whenever libuv notifies of I/O readiness. + +.. _libcurl: https://curl.haxx.se/libcurl/ +.. _multi: https://curl.haxx.se/libcurl/c/libcurl-multi.html + +.. rubric:: uvwget/main.c - The setup +.. literalinclude:: ../../code/uvwget/main.c + :language: c + :linenos: + :lines: 1-9,140- + :emphasize-lines: 7,21,24-25 + +The way each library is integrated with libuv will vary. In the case of +libcurl, we can register two callbacks. The socket callback ``handle_socket`` +is invoked whenever the state of a socket changes and we have to start polling +it. ``start_timeout`` is called by libcurl to notify us of the next timeout +interval, after which we should drive libcurl forward regardless of I/O status. +This is so that libcurl can handle errors or do whatever else is required to +get the download moving. + +Our downloader is to be invoked as:: + + $ ./uvwget [url1] [url2] ... + +So we add each argument as an URL + +.. rubric:: uvwget/main.c - Adding urls +.. literalinclude:: ../../code/uvwget/main.c + :language: c + :linenos: + :lines: 39-56 + :emphasize-lines: 13-14 + +We let libcurl directly write the data to a file, but much more is possible if +you so desire. + +``start_timeout`` will be called immediately the first time by libcurl, so +things are set in motion. This simply starts a libuv `timer <#timers>`_ which +drives ``curl_multi_socket_action`` with ``CURL_SOCKET_TIMEOUT`` whenever it +times out. ``curl_multi_socket_action`` is what drives libcurl, and what we +call whenever sockets change state. But before we go into that, we need to poll +on sockets whenever ``handle_socket`` is called. + +.. rubric:: uvwget/main.c - Setting up polling +.. literalinclude:: ../../code/uvwget/main.c + :language: c + :linenos: + :lines: 102-140 + :emphasize-lines: 9,11,15,21,24 + +We are interested in the socket fd ``s``, and the ``action``. For every socket +we create a ``uv_poll_t`` handle if it doesn't exist, and associate it with the +socket using ``curl_multi_assign``. This way ``socketp`` points to it whenever +the callback is invoked. + +In the case that the download is done or fails, libcurl requests removal of the +poll. So we stop and free the poll handle. + +Depending on what events libcurl wishes to watch for, we start polling with +``UV_READABLE`` or ``UV_WRITABLE``. Now libuv will invoke the poll callback +whenever the socket is ready for reading or writing. Calling ``uv_poll_start`` +multiple times on the same handle is acceptable, it will just update the events +mask with the new value. ``curl_perform`` is the crux of this program. + +.. rubric:: uvwget/main.c - Driving libcurl. +.. literalinclude:: ../../code/uvwget/main.c + :language: c + :linenos: + :lines: 81-95 + :emphasize-lines: 2,6-7,12 + +The first thing we do is to stop the timer, since there has been some progress +in the interval. Then depending on what event triggered the callback, we set +the correct flags. Then we call ``curl_multi_socket_action`` with the socket +that progressed and the flags informing about what events happened. At this +point libcurl does all of its internal tasks in small increments, and will +attempt to return as fast as possible, which is exactly what an evented program +wants in its main thread. libcurl keeps queueing messages into its own queue +about transfer progress. In our case we are only interested in transfers that +are completed. So we extract these messages, and clean up handles whose +transfers are done. + +.. rubric:: uvwget/main.c - Reading transfer status. +.. literalinclude:: ../../code/uvwget/main.c + :language: c + :linenos: + :lines: 58-79 + :emphasize-lines: 6,9-10,13-14 + +Check & Prepare watchers +------------------------ + +TODO + +Loading libraries +----------------- + +libuv provides a cross platform API to dynamically load `shared libraries`_. +This can be used to implement your own plugin/extension/module system and is +used by node.js to implement ``require()`` support for bindings. The usage is +quite simple as long as your library exports the right symbols. Be careful with +sanity and security checks when loading third party code, otherwise your +program will behave unpredictably. This example implements a very simple +plugin system which does nothing except print the name of the plugin. + +Let us first look at the interface provided to plugin authors. + +.. rubric:: plugin/plugin.h +.. literalinclude:: ../../code/plugin/plugin.h + :language: c + :linenos: + +You can similarly add more functions that plugin authors can use to do useful +things in your application [#]_. A sample plugin using this API is: + +.. rubric:: plugin/hello.c +.. literalinclude:: ../../code/plugin/hello.c + :language: c + :linenos: + +Our interface defines that all plugins should have an ``initialize`` function +which will be called by the application. This plugin is compiled as a shared +library and can be loaded by running our application:: + + $ ./plugin libhello.dylib + Loading libhello.dylib + Registered plugin "Hello World!" + +.. NOTE:: + + The shared library filename will be different depending on platforms. On + Linux it is ``libhello.so``. + +This is done by using ``uv_dlopen`` to first load the shared library +``libhello.dylib``. Then we get access to the ``initialize`` function using +``uv_dlsym`` and invoke it. + +.. rubric:: plugin/main.c +.. literalinclude:: ../../code/plugin/main.c + :language: c + :linenos: + :lines: 7- + :emphasize-lines: 15, 18, 24 + +``uv_dlopen`` expects a path to the shared library and sets the opaque +``uv_lib_t`` pointer. It returns 0 on success, -1 on error. Use ``uv_dlerror`` +to get the error message. + +``uv_dlsym`` stores a pointer to the symbol in the second argument in the third +argument. ``init_plugin_function`` is a function pointer to the sort of +function we are looking for in the application's plugins. + +.. _shared libraries: https://en.wikipedia.org/wiki/Shared_library#Shared_libraries + +TTY +--- + +Text terminals have supported basic formatting for a long time, with a `pretty +standardised`_ command set. This formatting is often used by programs to +improve the readability of terminal output. For example ``grep --colour``. +libuv provides the ``uv_tty_t`` abstraction (a stream) and related functions to +implement the ANSI escape codes across all platforms. By this I mean that libuv +converts ANSI codes to the Windows equivalent, and provides functions to get +terminal information. + +.. _pretty standardised: https://en.wikipedia.org/wiki/ANSI_escape_sequences + +The first thing to do is to initialize a ``uv_tty_t`` with the file descriptor +it reads/writes from. This is achieved with:: + + int uv_tty_init(uv_loop_t*, uv_tty_t*, uv_file fd, int unused) + +The ``unused`` parameter is now auto-detected and ignored. It previously needed +to be set to use ``uv_read_start()`` on the stream. + +It is then best to use ``uv_tty_set_mode`` to set the mode to *normal* +which enables most TTY formatting, flow-control and other settings. Other_ modes +are also available. + +.. _Other: http://docs.libuv.org/en/v1.x/tty.html#c.uv_tty_mode_t + +Remember to call ``uv_tty_reset_mode`` when your program exits to restore the +state of the terminal. Just good manners. Another set of good manners is to be +aware of redirection. If the user redirects the output of your command to +a file, control sequences should not be written as they impede readability and +``grep``. To check if the file descriptor is indeed a TTY, call +``uv_guess_handle`` with the file descriptor and compare the return value with +``UV_TTY``. + +Here is a simple example which prints white text on a red background: + +.. rubric:: tty/main.c +.. literalinclude:: ../../code/tty/main.c + :language: c + :linenos: + :emphasize-lines: 11-12,14,17,27 + +The final TTY helper is ``uv_tty_get_winsize()`` which is used to get the +width and height of the terminal and returns ``0`` on success. Here is a small +program which does some animation using the function and character position +escape codes. + +.. rubric:: tty-gravity/main.c +.. literalinclude:: ../../code/tty-gravity/main.c + :language: c + :linenos: + :emphasize-lines: 19,25,38 + +The escape codes are: + +====== ======================= +Code Meaning +====== ======================= +*2* J Clear part of the screen, 2 is entire screen +H Moves cursor to certain position, default top-left +*n* B Moves cursor down by n lines +*n* C Moves cursor right by n columns +m Obeys string of display settings, in this case green background (40+2), white text (30+7) +====== ======================= + +As you can see this is very useful to produce nicely formatted output, or even +console based arcade games if that tickles your fancy. For fancier control you +can try `ncurses`_. + +.. _ncurses: https://www.gnu.org/software/ncurses/ncurses.html + +.. versionchanged:: 1.23.1: the `readable` parameter is now unused and ignored. + The appropriate value will now be auto-detected from the kernel. + +---- + +.. [#] I was first introduced to the term baton in this context, in Konstantin + Käfer's excellent slides on writing node.js bindings -- + https://kkaefer.com/node-cpp-modules/#baton +.. [#] mfp is My Fancy Plugin + +.. _libev man page: http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#COMMON_OR_USEFUL_IDIOMS_OR_BOTH diff --git a/include/libuv/docs/src/handle.rst b/include/libuv/docs/src/handle.rst new file mode 100644 index 000000000..0edb7d7ad --- /dev/null +++ b/include/libuv/docs/src/handle.rst @@ -0,0 +1,283 @@ + +.. _handle: + +:c:type:`uv_handle_t` --- Base handle +===================================== + +`uv_handle_t` is the base type for all libuv handle types. + +Structures are aligned so that any libuv handle can be cast to `uv_handle_t`. +All API functions defined here work with any handle type. + +Libuv handles are not movable. Pointers to handle structures passed to +functions must remain valid for the duration of the requested operation. Take +care when using stack allocated handles. + +Data types +---------- + +.. c:type:: uv_handle_t + + The base libuv handle type. + +.. c:enum:: uv_handle_type + + The kind of the libuv handle. + + :: + + typedef enum { + UV_UNKNOWN_HANDLE = 0, + UV_ASYNC, + UV_CHECK, + UV_FS_EVENT, + UV_FS_POLL, + UV_HANDLE, + UV_IDLE, + UV_NAMED_PIPE, + UV_POLL, + UV_PREPARE, + UV_PROCESS, + UV_STREAM, + UV_TCP, + UV_TIMER, + UV_TTY, + UV_UDP, + UV_SIGNAL, + UV_FILE, + UV_HANDLE_TYPE_MAX + } uv_handle_type; + +.. c:type:: uv_any_handle + + Union of all handle types. + +.. c:type:: void (*uv_alloc_cb)(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) + + Type definition for callback passed to :c:func:`uv_read_start` and + :c:func:`uv_udp_recv_start`. The user must allocate memory and fill the supplied + :c:type:`uv_buf_t` structure. If NULL is assigned as the buffer's base or 0 as its length, + a ``UV_ENOBUFS`` error will be triggered in the :c:type:`uv_udp_recv_cb` or the + :c:type:`uv_read_cb` callback. + + Each buffer is used only once and the user is responsible for freeing it in the + :c:type:`uv_udp_recv_cb` or the :c:type:`uv_read_cb` callback. + + A suggested size (65536 at the moment in most cases) is provided, but it's just an indication, + not related in any way to the pending data to be read. The user is free to allocate the amount + of memory they decide. + + As an example, applications with custom allocation schemes such as using freelists, allocation + pools or slab based allocators may decide to use a different size which matches the memory + chunks they already have. + + Example: + + :: + + static void my_alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { + buf->base = malloc(suggested_size); + buf->len = suggested_size; + } + +.. c:type:: void (*uv_close_cb)(uv_handle_t* handle) + + Type definition for callback passed to :c:func:`uv_close`. + + +Public members +^^^^^^^^^^^^^^ + +.. c:member:: uv_loop_t* uv_handle_t.loop + + Pointer to the :c:type:`uv_loop_t` the handle is running on. Readonly. + +.. c:member:: uv_handle_type uv_handle_t.type + + The :c:type:`uv_handle_type`, indicating the type of the underlying handle. Readonly. + +.. c:member:: void* uv_handle_t.data + + Space for user-defined arbitrary data. libuv does not use this field. + + +API +--- + +.. c:macro:: UV_HANDLE_TYPE_MAP(iter_macro) + + Macro that expands to a series of invocations of `iter_macro` for + each of the handle types. `iter_macro` is invoked with two + arguments: the name of the `uv_handle_type` element without the + `UV_` prefix, and the name of the corresponding structure type + without the `uv_` prefix and `_t` suffix. + +.. c:function:: int uv_is_active(const uv_handle_t* handle) + + Returns non-zero if the handle is active, zero if it's inactive. What + "active" means depends on the type of handle: + + - A uv_async_t handle is always active and cannot be deactivated, except + by closing it with uv_close(). + + - A uv_pipe_t, uv_tcp_t, uv_udp_t, etc. handle - basically any handle that + deals with i/o - is active when it is doing something that involves i/o, + like reading, writing, connecting, accepting new connections, etc. + + - A uv_check_t, uv_idle_t, uv_timer_t, etc. handle is active when it has + been started with a call to uv_check_start(), uv_idle_start(), etc. + + Rule of thumb: if a handle of type `uv_foo_t` has a `uv_foo_start()` + function, then it's active from the moment that function is called. + Likewise, `uv_foo_stop()` deactivates the handle again. + +.. c:function:: int uv_is_closing(const uv_handle_t* handle) + + Returns non-zero if the handle is closing or closed, zero otherwise. + + .. note:: + This function should only be used between the initialization of the handle and the + arrival of the close callback. + +.. c:function:: void uv_close(uv_handle_t* handle, uv_close_cb close_cb) + + Request handle to be closed. `close_cb` will be called asynchronously after + this call. This MUST be called on each handle before memory is released. + Moreover, the memory can only be released in `close_cb` or after it has + returned. + + Handles that wrap file descriptors are closed immediately but + `close_cb` will still be deferred to the next iteration of the event loop. + It gives you a chance to free up any resources associated with the handle. + + In-progress requests, like uv_connect_t or uv_write_t, are cancelled and + have their callbacks called asynchronously with status=UV_ECANCELED. + +.. c:function:: void uv_ref(uv_handle_t* handle) + + Reference the given handle. References are idempotent, that is, if a handle + is already referenced calling this function again will have no effect. + + See :ref:`refcount`. + +.. c:function:: void uv_unref(uv_handle_t* handle) + + Un-reference the given handle. References are idempotent, that is, if a handle + is not referenced calling this function again will have no effect. + + See :ref:`refcount`. + +.. c:function:: int uv_has_ref(const uv_handle_t* handle) + + Returns non-zero if the handle referenced, zero otherwise. + + See :ref:`refcount`. + +.. c:function:: size_t uv_handle_size(uv_handle_type type) + + Returns the size of the given handle type. Useful for FFI binding writers + who don't want to know the structure layout. + + +Miscellaneous API functions +--------------------------- + +The following API functions take a :c:type:`uv_handle_t` argument but they work +just for some handle types. + +.. c:function:: int uv_send_buffer_size(uv_handle_t* handle, int* value) + + Gets or sets the size of the send buffer that the operating + system uses for the socket. + + If `*value` == 0, then it will set `*value` to the current send buffer size. + If `*value` > 0 then it will use `*value` to set the new send buffer size. + + On success, zero is returned. On error, a negative result is + returned. + + This function works for TCP, pipe and UDP handles on Unix and for TCP and + UDP handles on Windows. + + .. note:: + Linux will set double the size and return double the size of the original set value. + +.. c:function:: int uv_recv_buffer_size(uv_handle_t* handle, int* value) + + Gets or sets the size of the receive buffer that the operating + system uses for the socket. + + If `*value` == 0, then it will set `*value` to the current receive buffer size. + If `*value` > 0 then it will use `*value` to set the new receive buffer size. + + On success, zero is returned. On error, a negative result is + returned. + + This function works for TCP, pipe and UDP handles on Unix and for TCP and + UDP handles on Windows. + + .. note:: + Linux will set double the size and return double the size of the original set value. + +.. c:function:: int uv_fileno(const uv_handle_t* handle, uv_os_fd_t* fd) + + Gets the platform dependent file descriptor equivalent. + + The following handles are supported: TCP, pipes, TTY, UDP and poll. Passing + any other handle type will fail with `UV_EINVAL`. + + If a handle doesn't have an attached file descriptor yet or the handle + itself has been closed, this function will return `UV_EBADF`. + + .. warning:: + 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: + +Reference counting +------------------ + +The libuv event loop (if run in the default mode) will run until there are no +active `and` referenced handles left. The user can force the loop to exit early +by unreferencing handles which are active, for example by calling :c:func:`uv_unref` +after calling :c:func:`uv_timer_start`. + +A handle can be referenced or unreferenced, the refcounting scheme doesn't use +a counter, so both operations are idempotent. + +All handles are referenced when active by default, see :c:func:`uv_is_active` +for a more detailed explanation on what being `active` involves. diff --git a/include/libuv/docs/src/idle.rst b/include/libuv/docs/src/idle.rst new file mode 100644 index 000000000..b7a0507b0 --- /dev/null +++ b/include/libuv/docs/src/idle.rst @@ -0,0 +1,62 @@ + +.. _idle: + +:c:type:`uv_idle_t` --- Idle handle +=================================== + +Idle handles will run the given callback once per loop iteration, right +before the :c:type:`uv_prepare_t` handles. + +.. note:: + The notable difference with prepare handles is that when there are active idle handles, + the loop will perform a zero timeout poll instead of blocking for i/o. + +.. warning:: + Despite the name, idle handles will get their callbacks called on every loop iteration, + not when the loop is actually "idle". + + +Data types +---------- + +.. c:type:: uv_idle_t + + Idle handle type. + +.. c:type:: void (*uv_idle_cb)(uv_idle_t* handle) + + Type definition for callback passed to :c:func:`uv_idle_start`. + + +Public members +^^^^^^^^^^^^^^ + +N/A + +.. seealso:: The :c:type:`uv_handle_t` members also apply. + + +API +--- + +.. c:function:: int uv_idle_init(uv_loop_t* loop, uv_idle_t* idle) + + Initialize the handle. This function always succeeds. + + :returns: 0 + +.. c:function:: int uv_idle_start(uv_idle_t* idle, uv_idle_cb cb) + + Start the handle with the given callback. This function always succeeds, + except when `cb` is `NULL`. + + :returns: 0 on success, or `UV_EINVAL` when `cb == NULL`. + +.. c:function:: int uv_idle_stop(uv_idle_t* idle) + + Stop the handle, the callback will no longer be called. + This function always succeeds. + + :returns: 0 + +.. seealso:: The :c:type:`uv_handle_t` API functions also apply. diff --git a/include/libuv/docs/src/index.rst b/include/libuv/docs/src/index.rst new file mode 100644 index 000000000..4b5d4d2c5 --- /dev/null +++ b/include/libuv/docs/src/index.rst @@ -0,0 +1,62 @@ + +Welcome to the libuv documentation +================================== + +Overview +-------- + +libuv is a multi-platform support library with a focus on asynchronous I/O. It +was primarily developed for use by `Node.js`_, but it's also used by `Luvit`_, +`Julia`_, `pyuv`_, and `others`_. + +.. note:: + In case you find errors in this documentation you can help by sending + `pull requests `_! + +.. _Node.js: https://nodejs.org +.. _Luvit: https://luvit.io +.. _Julia: https://julialang.org +.. _pyuv: https://github.com/saghul/pyuv +.. _others: https://github.com/libuv/libuv/blob/v1.x/LINKS.md + + +Features +-------- + +* Full-featured event loop backed by epoll, kqueue, IOCP, event ports. +* Asynchronous TCP and UDP sockets +* Asynchronous DNS resolution +* Asynchronous file and file system operations +* File system events +* ANSI escape code controlled TTY +* IPC with socket sharing, using Unix domain sockets or named pipes (Windows) +* Child processes +* Thread pool +* Signal handling +* High resolution clock +* Threading and synchronization primitives + + +Documentation +------------- + +.. toctree:: + :maxdepth: 1 + + design + api + guide + upgrading + + +Downloads +--------- + +libuv can be downloaded from `here `_. + + +Installation +------------ + +Installation instructions can be found in `the README `_. + diff --git a/include/libuv/docs/src/loop.rst b/include/libuv/docs/src/loop.rst new file mode 100644 index 000000000..0f5ddfb3c --- /dev/null +++ b/include/libuv/docs/src/loop.rst @@ -0,0 +1,245 @@ + +.. _loop: + +:c:type:`uv_loop_t` --- Event loop +================================== + +The event loop is the central part of libuv's functionality. It takes care +of polling for i/o and scheduling callbacks to be run based on different sources +of events. + + +Data types +---------- + +.. c:type:: uv_loop_t + + Loop data type. + +.. c:enum:: uv_run_mode + + Mode used to run the loop with :c:func:`uv_run`. + + :: + + typedef enum { + UV_RUN_DEFAULT = 0, + UV_RUN_ONCE, + UV_RUN_NOWAIT + } uv_run_mode; + +.. c:type:: void (*uv_walk_cb)(uv_handle_t* handle, void* arg) + + Type definition for callback passed to :c:func:`uv_walk`. + + +Public members +^^^^^^^^^^^^^^ + +.. c:member:: void* uv_loop_t.data + + Space for user-defined arbitrary data. libuv does not use and does not + touch this field. + + +API +--- + +.. c:function:: int uv_loop_init(uv_loop_t* loop) + + Initializes the given `uv_loop_t` structure. + +.. c:function:: int uv_loop_configure(uv_loop_t* loop, uv_loop_option option, ...) + + .. versionadded:: 1.0.2 + + Set additional loop options. You should normally call this before the + first call to :c:func:`uv_run` unless mentioned otherwise. + + Returns 0 on success or a UV_E* error code on failure. Be prepared to + handle UV_ENOSYS; it means the loop option is not supported by the platform. + + Supported options: + + - UV_LOOP_BLOCK_SIGNAL: Block a signal when polling for new events. The + second argument to :c:func:`uv_loop_configure` is the signal number. + + This operation is currently only implemented for SIGPROF signals, + to suppress unnecessary wakeups when using a sampling profiler. + Requesting other signals will fail with UV_EINVAL. + + - UV_METRICS_IDLE_TIME: Accumulate the amount of idle time the event loop + spends in the event provider. + + This option is necessary to use :c:func:`uv_metrics_idle_time`. + + .. versionchanged:: 1.39.0 added the UV_METRICS_IDLE_TIME option. + +.. c:function:: int uv_loop_close(uv_loop_t* loop) + + Releases all internal loop resources. Call this function only when the loop + has finished executing and all open handles and requests have been closed, + or it will return UV_EBUSY. After this function returns, the user can free + the memory allocated for the loop. + +.. c:function:: uv_loop_t* uv_default_loop(void) + + Returns the initialized default loop. It may return NULL in case of + allocation failure. + + This function is just a convenient way for having a global loop throughout + an application, the default loop is in no way different than the ones + initialized with :c:func:`uv_loop_init`. As such, the default loop can (and + should) be closed with :c:func:`uv_loop_close` so the resources associated + with it are freed. + + .. warning:: + This function is not thread safe. + +.. c:function:: int uv_run(uv_loop_t* loop, uv_run_mode mode) + + This function runs the event loop. It will act differently depending on the + specified mode: + + - UV_RUN_DEFAULT: Runs the event loop until there are no more active and + referenced handles or requests. Returns non-zero if :c:func:`uv_stop` + was called and there are still active handles or requests. Returns + zero in all other cases. + - UV_RUN_ONCE: Poll for i/o once. Note that this function blocks if + there are no pending callbacks. Returns zero when done (no active handles + or requests left), or non-zero if more callbacks are expected (meaning + you should run the event loop again sometime in the future). + - UV_RUN_NOWAIT: Poll for i/o once but don't block if there are no + pending callbacks. Returns zero if done (no active handles + or requests left), or non-zero if more callbacks are expected (meaning + you should run the event loop again sometime in the future). + + :c:func:`uv_run` is not reentrant. It must not be called from a callback. + +.. c:function:: int uv_loop_alive(const uv_loop_t* loop) + + Returns non-zero if there are referenced active handles, active + requests or closing handles in the loop. + +.. c:function:: void uv_stop(uv_loop_t* loop) + + Stop the event loop, causing :c:func:`uv_run` to end as soon as + possible. This will happen not sooner than the next loop iteration. + If this function was called before blocking for i/o, the loop won't block + for i/o on this iteration. + +.. c:function:: size_t uv_loop_size(void) + + Returns the size of the `uv_loop_t` structure. Useful for FFI binding + writers who don't want to know the structure layout. + +.. c:function:: int uv_backend_fd(const uv_loop_t* loop) + + Get backend file descriptor. Only kqueue, epoll and event ports are + supported. + + This can be used in conjunction with `uv_run(loop, UV_RUN_NOWAIT)` to + poll in one thread and run the event loop's callbacks in another see + test/test-embed.c for an example. + + .. note:: + Embedding a kqueue fd in another kqueue pollset doesn't work on all platforms. It's not + an error to add the fd but it never generates events. + +.. c:function:: int uv_backend_timeout(const uv_loop_t* loop) + + Get the poll timeout. The return value is in milliseconds, or -1 for no + timeout. + +.. c:function:: uint64_t uv_now(const uv_loop_t* loop) + + Return the current timestamp in milliseconds. The timestamp is cached at + the start of the event loop tick, see :c:func:`uv_update_time` for details + and rationale. + + The timestamp increases monotonically from some arbitrary point in time. + Don't make assumptions about the starting point, you will only get + disappointed. + + .. note:: + Use :c:func:`uv_hrtime` if you need sub-millisecond granularity. + +.. c:function:: void uv_update_time(uv_loop_t* loop) + + Update the event loop's concept of "now". Libuv caches the current time + at the start of the event loop tick in order to reduce the number of + time-related system calls. + + You won't normally need to call this function unless you have callbacks + that block the event loop for longer periods of time, where "longer" is + somewhat subjective but probably on the order of a millisecond or more. + +.. c:function:: void uv_walk(uv_loop_t* loop, uv_walk_cb walk_cb, void* arg) + + Walk the list of handles: `walk_cb` will be executed with the given `arg`. + +.. c:function:: int uv_loop_fork(uv_loop_t* loop) + + .. versionadded:: 1.12.0 + + Reinitialize any kernel state necessary in the child process after + a :man:`fork(2)` system call. + + Previously started watchers will continue to be started in the + child process. + + It is necessary to explicitly call this function on every event + loop created in the parent process that you plan to continue to + use in the child, including the default loop (even if you don't + continue to use it in the parent). This function must be called + before calling :c:func:`uv_run` or any other API function using + the loop in the child. Failure to do so will result in undefined + behaviour, possibly including duplicate events delivered to both + parent and child or aborting the child process. + + When possible, it is preferred to create a new loop in the child + process instead of reusing a loop created in the parent. New loops + created in the child process after the fork should not use this + function. + + This function is not implemented on Windows, where it returns ``UV_ENOSYS``. + + .. caution:: + + This function is experimental. It may contain bugs, and is subject to + change or removal. API and ABI stability is not guaranteed. + + .. note:: + + On Mac OS X, if directory FS event handles were in use in the + parent process *for any event loop*, the child process will no + longer be able to use the most efficient FSEvent + implementation. Instead, uses of directory FS event handles in + the child will fall back to the same implementation used for + files and on other kqueue-based systems. + + .. caution:: + + On AIX and SunOS, FS event handles that were already started in + the parent process at the time of forking will *not* deliver + events in the child process; they must be closed and restarted. + On all other platforms, they will continue to work normally + without any further intervention. + + .. caution:: + + 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/include/libuv/docs/src/metrics.rst b/include/libuv/docs/src/metrics.rst new file mode 100644 index 000000000..696c620d1 --- /dev/null +++ b/include/libuv/docs/src/metrics.rst @@ -0,0 +1,27 @@ + +.. _metrics: + +Metrics operations +====================== + +libuv provides a metrics API to track the amount of time the event loop has +spent idle in the kernel's event provider. + +API +--- + +.. c:function:: uint64_t uv_metrics_idle_time(uv_loop_t* loop) + + Retrieve the amount of time the event loop has been idle in the kernel's + event provider (e.g. ``epoll_wait``). The call is thread safe. + + The return value is the accumulated time spent idle in the kernel's event + provider starting from when the :c:type:`uv_loop_t` was configured to + collect the idle time. + + .. note:: + The event loop will not begin accumulating the event provider's idle + time until calling :c:type:`uv_loop_configure` with + :c:type:`UV_METRICS_IDLE_TIME`. + + .. versionadded:: 1.39.0 diff --git a/include/libuv/docs/src/migration_010_100.rst b/include/libuv/docs/src/migration_010_100.rst new file mode 100644 index 000000000..bb6ac1a80 --- /dev/null +++ b/include/libuv/docs/src/migration_010_100.rst @@ -0,0 +1,244 @@ + +.. _migration_010_100: + +libuv 0.10 -> 1.0.0 migration guide +=================================== + +Some APIs changed quite a bit throughout the 1.0.0 development process. Here +is a migration guide for the most significant changes that happened after 0.10 +was released. + + +Loop initialization and closing +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In libuv 0.10 (and previous versions), loops were created with `uv_loop_new`, which +allocated memory for a new loop and initialized it; and destroyed with `uv_loop_delete`, +which destroyed the loop and freed the memory. Starting with 1.0, those are deprecated +and the user is responsible for allocating the memory and then initializing the loop. + +libuv 0.10 + +:: + + uv_loop_t* loop = uv_loop_new(); + ... + uv_loop_delete(loop); + +libuv 1.0 + +:: + + uv_loop_t* loop = malloc(sizeof *loop); + uv_loop_init(loop); + ... + uv_loop_close(loop); + free(loop); + +.. note:: + Error handling was omitted for brevity. Check the documentation for :c:func:`uv_loop_init` + and :c:func:`uv_loop_close`. + + +Error handling +~~~~~~~~~~~~~~ + +Error handling had a major overhaul in libuv 1.0. In general, functions and status parameters +would get 0 for success and -1 for failure on libuv 0.10, and the user had to use `uv_last_error` +to fetch the error code, which was a positive number. + +In 1.0, functions and status parameters contain the actual error code, which is 0 for success, or +a negative number in case of error. + +libuv 0.10 + +:: + + ... assume 'server' is a TCP server which is already listening + r = uv_listen((uv_stream_t*) server, 511, NULL); + if (r == -1) { + uv_err_t err = uv_last_error(uv_default_loop()); + /* err.code contains UV_EADDRINUSE */ + } + +libuv 1.0 + +:: + + ... assume 'server' is a TCP server which is already listening + r = uv_listen((uv_stream_t*) server, 511, NULL); + if (r < 0) { + /* r contains UV_EADDRINUSE */ + } + + +Threadpool changes +~~~~~~~~~~~~~~~~~~ + +In libuv 0.10 Unix used a threadpool which defaulted to 4 threads, while Windows used the +`QueueUserWorkItem` API, which uses a Windows internal threadpool, which defaults to 512 +threads per process. + +In 1.0, we unified both implementations, so Windows now uses the same implementation Unix +does. The threadpool size can be set by exporting the ``UV_THREADPOOL_SIZE`` environment +variable. See :c:ref:`threadpool`. + + +Allocation callback API change +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In libuv 0.10 the callback had to return a filled :c:type:`uv_buf_t` by value: + +:: + + uv_buf_t alloc_cb(uv_handle_t* handle, size_t size) { + return uv_buf_init(malloc(size), size); + } + +In libuv 1.0 a pointer to a buffer is passed to the callback, which the user +needs to fill: + +:: + + void alloc_cb(uv_handle_t* handle, size_t size, uv_buf_t* buf) { + buf->base = malloc(size); + buf->len = size; + } + + +Unification of IPv4 / IPv6 APIs +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +libuv 1.0 unified the IPv4 and IPv6 APIS. There is no longer a `uv_tcp_bind` and `uv_tcp_bind6` +duality, there is only :c:func:`uv_tcp_bind` now. + +IPv4 functions took ``struct sockaddr_in`` structures by value, and IPv6 functions took +``struct sockaddr_in6``. Now functions take a ``struct sockaddr*`` (note it's a pointer). +It can be stack allocated. + +libuv 0.10 + +:: + + struct sockaddr_in addr = uv_ip4_addr("0.0.0.0", 1234); + ... + uv_tcp_bind(&server, addr) + +libuv 1.0 + +:: + + struct sockaddr_in addr; + uv_ip4_addr("0.0.0.0", 1234, &addr) + ... + uv_tcp_bind(&server, (const struct sockaddr*) &addr, 0); + +The IPv4 and IPv6 struct creating functions (:c:func:`uv_ip4_addr` and :c:func:`uv_ip6_addr`) +have also changed, make sure you check the documentation. + +..note:: + This change applies to all functions that made a distinction between IPv4 and IPv6 + addresses. + + +Streams / UDP data receive callback API change +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The streams and UDP data receive callbacks now get a pointer to a :c:type:`uv_buf_t` buffer, +not a structure by value. + +libuv 0.10 + +:: + + void on_read(uv_stream_t* handle, + ssize_t nread, + uv_buf_t buf) { + ... + } + + void recv_cb(uv_udp_t* handle, + ssize_t nread, + uv_buf_t buf, + struct sockaddr* addr, + unsigned flags) { + ... + } + +libuv 1.0 + +:: + + void on_read(uv_stream_t* handle, + ssize_t nread, + const uv_buf_t* buf) { + ... + } + + void recv_cb(uv_udp_t* handle, + ssize_t nread, + const uv_buf_t* buf, + const struct sockaddr* addr, + unsigned flags) { + ... + } + + +Receiving handles over pipes API change +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In libuv 0.10 (and earlier versions) the `uv_read2_start` function was used to start reading +data on a pipe, which could also result in the reception of handles over it. The callback +for such function looked like this: + +:: + + void on_read(uv_pipe_t* pipe, + ssize_t nread, + uv_buf_t buf, + uv_handle_type pending) { + ... + } + +In libuv 1.0, `uv_read2_start` was removed, and the user needs to check if there are pending +handles using :c:func:`uv_pipe_pending_count` and :c:func:`uv_pipe_pending_type` while in +the read callback: + +:: + + void on_read(uv_stream_t* handle, + ssize_t nread, + const uv_buf_t* buf) { + ... + while (uv_pipe_pending_count((uv_pipe_t*) handle) != 0) { + pending = uv_pipe_pending_type((uv_pipe_t*) handle); + ... + } + ... + } + + +Extracting the file descriptor out of a handle +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +While it wasn't supported by the API, users often accessed the libuv internals in +order to get access to the file descriptor of a TCP handle, for example. + +:: + + fd = handle->io_watcher.fd; + +This is now properly exposed through the :c:func:`uv_fileno` function. + + +uv_fs_readdir rename and API change +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`uv_fs_readdir` returned a list of strings in the `req->ptr` field upon completion in +libuv 0.10. In 1.0, this function got renamed to :c:func:`uv_fs_scandir`, since it's +actually implemented using ``scandir(3)``. + +In addition, instead of allocating a full list strings, the user is able to get one +result at a time by using the :c:func:`uv_fs_scandir_next` function. This function +does not need to make a roundtrip to the threadpool, because libuv will keep the +list of *dents* returned by ``scandir(3)`` around. diff --git a/include/libuv/docs/src/misc.rst b/include/libuv/docs/src/misc.rst new file mode 100644 index 000000000..9a8595e57 --- /dev/null +++ b/include/libuv/docs/src/misc.rst @@ -0,0 +1,755 @@ + +.. _misc: + +Miscellaneous utilities +======================= + +This section contains miscellaneous functions that don't really belong in any +other section. + + +Data types +---------- + +.. c:type:: uv_buf_t + + Buffer data type. + + .. c:member:: char* uv_buf_t.base + + Pointer to the base of the buffer. + + .. c:member:: size_t uv_buf_t.len + + Total bytes in the buffer. + + .. note:: + On Windows this field is ULONG. + +.. c:type:: void* (*uv_malloc_func)(size_t size) + + Replacement function for :man:`malloc(3)`. + See :c:func:`uv_replace_allocator`. + +.. c:type:: void* (*uv_realloc_func)(void* ptr, size_t size) + + Replacement function for :man:`realloc(3)`. + See :c:func:`uv_replace_allocator`. + +.. c:type:: void* (*uv_calloc_func)(size_t count, size_t size) + + Replacement function for :man:`calloc(3)`. + See :c:func:`uv_replace_allocator`. + +.. c:type:: void (*uv_free_func)(void* ptr) + + Replacement function for :man:`free(3)`. + See :c:func:`uv_replace_allocator`. + +.. c:type:: void (*uv_random_cb)(uv_random_t* req, int status, void* buf, size_t buflen) + + Callback passed to :c:func:`uv_random`. `status` is non-zero in case of + error. The `buf` pointer is the same pointer that was passed to + :c:func:`uv_random`. + +.. c:type:: uv_file + + Cross platform representation of a file handle. + +.. c:type:: uv_os_sock_t + + Cross platform representation of a socket handle. + +.. c:type:: uv_os_fd_t + + Abstract representation of a file descriptor. On Unix systems this is a + `typedef` of `int` and on Windows a `HANDLE`. + +.. c:type:: uv_pid_t + + Cross platform representation of a `pid_t`. + + .. versionadded:: 1.16.0 + +.. c:type:: uv_timeval_t + + Data type for storing times. + + :: + + typedef struct { + long tv_sec; + long tv_usec; + } uv_timeval_t; + +.. c:type:: uv_timeval64_t + + Alternative data type for storing times. + + :: + + typedef struct { + int64_t tv_sec; + int32_t tv_usec; + } uv_timeval64_t; + +.. c:type:: uv_rusage_t + + Data type for resource usage results. + + :: + + typedef struct { + uv_timeval_t ru_utime; /* user CPU time used */ + uv_timeval_t ru_stime; /* system CPU time used */ + uint64_t ru_maxrss; /* maximum resident set size */ + uint64_t ru_ixrss; /* integral shared memory size (X) */ + uint64_t ru_idrss; /* integral unshared data size (X) */ + uint64_t ru_isrss; /* integral unshared stack size (X) */ + uint64_t ru_minflt; /* page reclaims (soft page faults) (X) */ + uint64_t ru_majflt; /* page faults (hard page faults) */ + uint64_t ru_nswap; /* swaps (X) */ + uint64_t ru_inblock; /* block input operations */ + uint64_t ru_oublock; /* block output operations */ + uint64_t ru_msgsnd; /* IPC messages sent (X) */ + uint64_t ru_msgrcv; /* IPC messages received (X) */ + uint64_t ru_nsignals; /* signals received (X) */ + uint64_t ru_nvcsw; /* voluntary context switches (X) */ + uint64_t ru_nivcsw; /* involuntary context switches (X) */ + } uv_rusage_t; + + Members marked with `(X)` are unsupported on Windows. + See :man:`getrusage(2)` for supported fields on Unix + +.. c:type:: uv_cpu_info_t + + Data type for CPU information. + + :: + + typedef struct uv_cpu_info_s { + char* model; + int speed; + struct uv_cpu_times_s { + uint64_t user; /* milliseconds */ + uint64_t nice; /* milliseconds */ + uint64_t sys; /* milliseconds */ + uint64_t idle; /* milliseconds */ + uint64_t irq; /* milliseconds */ + } cpu_times; + } uv_cpu_info_t; + +.. c:type:: uv_interface_address_t + + Data type for interface addresses. + + :: + + typedef struct uv_interface_address_s { + char* name; + char phys_addr[6]; + int is_internal; + union { + struct sockaddr_in address4; + struct sockaddr_in6 address6; + } address; + union { + struct sockaddr_in netmask4; + struct sockaddr_in6 netmask6; + } netmask; + } uv_interface_address_t; + +.. c:type:: uv_passwd_t + + Data type for password file information. + + :: + + typedef struct uv_passwd_s { + char* username; + long uid; + long gid; + char* shell; + char* homedir; + } uv_passwd_t; + +.. c:type:: uv_utsname_t + + Data type for operating system name and version information. + + :: + + typedef struct uv_utsname_s { + char sysname[256]; + char release[256]; + char version[256]; + char machine[256]; + } uv_utsname_t; + +.. c:type:: uv_env_item_t + + Data type for environment variable storage. + + :: + + typedef struct uv_env_item_s { + char* name; + char* value; + } uv_env_item_t; + +.. c:type:: uv_random_t + + Random data request type. + +API +--- + +.. c:function:: uv_handle_type uv_guess_handle(uv_file file) + + Used to detect what type of stream should be used with a given file + descriptor. Usually this will be used during initialization to guess the + type of the stdio streams. + + For :man:`isatty(3)` equivalent functionality use this function and test + for ``UV_TTY``. + +.. c:function:: int uv_replace_allocator(uv_malloc_func malloc_func, uv_realloc_func realloc_func, uv_calloc_func calloc_func, uv_free_func free_func) + + .. versionadded:: 1.6.0 + + Override the use of the standard library's :man:`malloc(3)`, + :man:`calloc(3)`, :man:`realloc(3)`, :man:`free(3)`, memory allocation + functions. + + This function must be called before any other libuv function is called or + after all resources have been freed and thus libuv doesn't reference + any allocated memory chunk. + + On success, it returns 0, if any of the function pointers is NULL it + returns UV_EINVAL. + + .. warning:: There is no protection against changing the allocator multiple + times. If the user changes it they are responsible for making + sure the allocator is changed while no memory was allocated with + the previous allocator, or that they are compatible. + + .. warning:: Allocator must be thread-safe. + +.. c:function:: void uv_library_shutdown(void); + + .. versionadded:: 1.38.0 + + Release any global state that libuv is holding onto. Libuv will normally + do so automatically when it is unloaded but it can be instructed to perform + cleanup manually. + + .. warning:: Only call :c:func:`uv_library_shutdown()` once. + + .. warning:: Don't call :c:func:`uv_library_shutdown()` when there are + still event loops or I/O requests active. + + .. warning:: Don't call libuv functions after calling + :c:func:`uv_library_shutdown()`. + +.. c:function:: uv_buf_t uv_buf_init(char* base, unsigned int len) + + Constructor for :c:type:`uv_buf_t`. + + Due to platform differences the user cannot rely on the ordering of the + `base` and `len` members of the uv_buf_t struct. The user is responsible for + freeing `base` after the uv_buf_t is done. Return struct passed by value. + +.. c:function:: char** uv_setup_args(int argc, char** argv) + + Store the program arguments. Required for getting / setting the process title + or the executable path. Libuv may take ownership of the memory that `argv` + points to. This function should be called exactly once, at program start-up. + + Example: + + :: + + argv = uv_setup_args(argc, argv); /* May return a copy of argv. */ + + +.. c:function:: int uv_get_process_title(char* buffer, size_t size) + + Gets the title of the current process. You *must* call `uv_setup_args` + before calling this function on Unix and AIX systems. If `uv_setup_args` + has not been called on systems that require it, then `UV_ENOBUFS` is + returned. If `buffer` is `NULL` or `size` is zero, `UV_EINVAL` is returned. + If `size` cannot accommodate the process title and terminating `nul` + character, the function returns `UV_ENOBUFS`. + + .. note:: + On BSD systems, `uv_setup_args` is needed for getting the initial process + title. The process title returned will be an empty string until either + `uv_setup_args` or `uv_set_process_title` is called. + + .. versionchanged:: 1.18.1 now thread-safe on all supported platforms. + + .. versionchanged:: 1.39.0 now returns an error if `uv_setup_args` is needed + but hasn't been called. + +.. c:function:: int uv_set_process_title(const char* title) + + Sets the current process title. You *must* call `uv_setup_args` before + calling this function on Unix and AIX systems. If `uv_setup_args` has not + been called on systems that require it, then `UV_ENOBUFS` is returned. On + platforms with a fixed size buffer for the process title the contents of + `title` will be copied to the buffer and truncated if larger than the + available space. Other platforms will return `UV_ENOMEM` if they cannot + allocate enough space to duplicate the contents of `title`. + + .. versionchanged:: 1.18.1 now thread-safe on all supported platforms. + + .. versionchanged:: 1.39.0 now returns an error if `uv_setup_args` is needed + but hasn't been called. + +.. c:function:: int uv_resident_set_memory(size_t* rss) + + Gets the resident set size (RSS) for the current process. + +.. c:function:: int uv_uptime(double* uptime) + + Gets the current system uptime. + +.. c:function:: int uv_getrusage(uv_rusage_t* rusage) + + Gets the resource usage measures for the current process. + + .. note:: + On Windows not all fields are set, the unsupported fields are filled with zeroes. + See :c:type:`uv_rusage_t` for more details. + +.. c:function:: uv_pid_t uv_os_getpid(void) + + Returns the current process ID. + + .. versionadded:: 1.18.0 + +.. c:function:: uv_pid_t uv_os_getppid(void) + + Returns the parent process ID. + + .. versionadded:: 1.16.0 + +.. c:function:: int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) + + Gets information about the CPUs on the system. The `cpu_infos` array will + have `count` elements and needs to be freed with :c:func:`uv_free_cpu_info`. + +.. c:function:: void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) + + Frees the `cpu_infos` array previously allocated with :c:func:`uv_cpu_info`. + +.. c:function:: int uv_interface_addresses(uv_interface_address_t** addresses, int* count) + + Gets address information about the network interfaces on the system. An + array of `count` elements is allocated and returned in `addresses`. It must + be freed by the user, calling :c:func:`uv_free_interface_addresses`. + +.. c:function:: void uv_free_interface_addresses(uv_interface_address_t* addresses, int count) + + Free an array of :c:type:`uv_interface_address_t` which was returned by + :c:func:`uv_interface_addresses`. + +.. c:function:: void uv_loadavg(double avg[3]) + + Gets the load average. See: ``_ + + .. note:: + Returns [0,0,0] on Windows (i.e., it's not implemented). + +.. c:function:: int uv_ip4_addr(const char* ip, int port, struct sockaddr_in* addr) + + Convert a string containing an IPv4 addresses to a binary structure. + +.. c:function:: int uv_ip6_addr(const char* ip, int port, struct sockaddr_in6* addr) + + Convert a string containing an IPv6 addresses to a binary structure. + +.. c:function:: int uv_ip4_name(const struct sockaddr_in* src, char* dst, size_t size) + + Convert a binary structure containing an IPv4 address to a string. + +.. c:function:: int uv_ip6_name(const struct sockaddr_in6* src, char* dst, size_t size) + + Convert a binary structure containing an IPv6 address to a string. + +.. c:function:: int uv_inet_ntop(int af, const void* src, char* dst, size_t size) +.. c:function:: int uv_inet_pton(int af, const char* src, void* dst) + + Cross-platform IPv6-capable implementation of :man:`inet_ntop(3)` + and :man:`inet_pton(3)`. On success they return 0. In case of error + the target `dst` pointer is unmodified. + +.. c:macro:: UV_IF_NAMESIZE + + Maximum IPv6 interface identifier name length. Defined as + `IFNAMSIZ` on Unix and `IF_NAMESIZE` on Linux and Windows. + + .. versionadded:: 1.16.0 + +.. c:function:: int uv_if_indextoname(unsigned int ifindex, char* buffer, size_t* size) + + IPv6-capable implementation of :man:`if_indextoname(3)`. When called, + `*size` indicates the length of the `buffer`, which is used to store the + result. + On success, zero is returned, `buffer` contains the interface name, and + `*size` represents the string length of the `buffer`, excluding the NUL + terminator byte from `*size`. On error, a negative result is + returned. If `buffer` is not large enough to hold the result, + `UV_ENOBUFS` is returned, and `*size` represents the necessary size in + bytes, including the NUL terminator byte into the `*size`. + + On Unix, the returned interface name can be used directly as an + interface identifier in scoped IPv6 addresses, e.g. + `fe80::abc:def1:2345%en0`. + + On Windows, the returned interface cannot be used as an interface + identifier, as Windows uses numerical interface identifiers, e.g. + `fe80::abc:def1:2345%5`. + + To get an interface identifier in a cross-platform compatible way, + use `uv_if_indextoiid()`. + + Example: + + :: + + char ifname[UV_IF_NAMESIZE]; + size_t size = sizeof(ifname); + uv_if_indextoname(sin6->sin6_scope_id, ifname, &size); + + .. versionadded:: 1.16.0 + +.. c:function:: int uv_if_indextoiid(unsigned int ifindex, char* buffer, size_t* size) + + Retrieves a network interface identifier suitable for use in an IPv6 scoped + address. On Windows, returns the numeric `ifindex` as a string. On all other + platforms, `uv_if_indextoname()` is called. The result is written to + `buffer`, with `*size` indicating the length of `buffer`. If `buffer` is not + large enough to hold the result, then `UV_ENOBUFS` is returned, and `*size` + represents the size, including the NUL byte, required to hold the + result. + + See `uv_if_indextoname` for further details. + + .. versionadded:: 1.16.0 + +.. c:function:: int uv_exepath(char* buffer, size_t* size) + + Gets the executable path. You *must* call `uv_setup_args` before calling + this function. + +.. c:function:: int uv_cwd(char* buffer, size_t* size) + + Gets the current working directory, and stores it in `buffer`. If the + current working directory is too large to fit in `buffer`, this function + returns `UV_ENOBUFS`, and sets `size` to the required length, including the + null terminator. + + .. versionchanged:: 1.1.0 + + On Unix the path no longer ends in a slash. + + .. versionchanged:: 1.9.0 the returned length includes the terminating null + byte on `UV_ENOBUFS`, and the buffer is null terminated + on success. + + +.. c:function:: int uv_chdir(const char* dir) + + Changes the current working directory. + +.. c:function:: int uv_os_homedir(char* buffer, size_t* size) + + Gets the current user's home directory. On Windows, `uv_os_homedir()` first + checks the `USERPROFILE` environment variable using + `GetEnvironmentVariableW()`. If `USERPROFILE` is not set, + `GetUserProfileDirectoryW()` is called. On all other operating systems, + `uv_os_homedir()` first checks the `HOME` environment variable using + :man:`getenv(3)`. If `HOME` is not set, :man:`getpwuid_r(3)` is called. The + user's home directory is stored in `buffer`. When `uv_os_homedir()` is + called, `size` indicates the maximum size of `buffer`. On success `size` is set + to the string length of `buffer`. On `UV_ENOBUFS` failure `size` is set to the + required length for `buffer`, including the null byte. + + .. warning:: + `uv_os_homedir()` is not thread safe. + + .. versionadded:: 1.6.0 + +.. c:function:: int uv_os_tmpdir(char* buffer, size_t* size) + + Gets the temp directory. On Windows, `uv_os_tmpdir()` uses `GetTempPathW()`. + On all other operating systems, `uv_os_tmpdir()` uses the first environment + variable found in the ordered list `TMPDIR`, `TMP`, `TEMP`, and `TEMPDIR`. + If none of these are found, the path `"/tmp"` is used, or, on Android, + `"/data/local/tmp"` is used. The temp directory is stored in `buffer`. When + `uv_os_tmpdir()` is called, `size` indicates the maximum size of `buffer`. + On success `size` is set to the string length of `buffer` (which does not + include the terminating null). On `UV_ENOBUFS` failure `size` is set to the + required length for `buffer`, including the null byte. + + .. warning:: + `uv_os_tmpdir()` is not thread safe. + + .. versionadded:: 1.9.0 + +.. c:function:: int uv_os_get_passwd(uv_passwd_t* pwd) + + Gets a subset of the password file entry for the current effective uid (not + the real uid). The populated data includes the username, euid, gid, shell, + and home directory. On non-Windows systems, all data comes from + :man:`getpwuid_r(3)`. On Windows, uid and gid are set to -1 and have no + meaning, and shell is `NULL`. After successfully calling this function, the + memory allocated to `pwd` needs to be freed with + :c:func:`uv_os_free_passwd`. + + .. versionadded:: 1.9.0 + +.. c:function:: void uv_os_free_passwd(uv_passwd_t* pwd) + + Frees the `pwd` memory previously allocated with :c:func:`uv_os_get_passwd`. + + .. versionadded:: 1.9.0 + +.. c:function:: uint64_t uv_get_free_memory(void) + + Gets the amount of free memory available in the system, as reported by the kernel (in bytes). + +.. c:function:: uint64_t uv_get_total_memory(void) + + Gets the total amount of physical memory in the system (in bytes). + +.. c:function:: uint64_t uv_get_constrained_memory(void) + + Gets the amount of memory available to the process (in bytes) based on + limits imposed by the OS. If there is no such constraint, or the constraint + is unknown, `0` is returned. Note that it is not unusual for this value to + be less than or greater than :c:func:`uv_get_total_memory`. + + .. note:: + This function currently only returns a non-zero value on Linux, based + on cgroups if it is present, and on z/OS based on RLIMIT_MEMLIMIT. + + .. versionadded:: 1.29.0 + +.. c:function:: uint64_t uv_hrtime(void) + + Returns the current high-resolution real time. This is expressed in + nanoseconds. It is relative to an arbitrary time in the past. It is not + related to the time of day and therefore not subject to clock drift. The + primary use is for measuring performance between intervals. + + .. note:: + Not every platform can support nanosecond resolution; however, this value will always + be in nanoseconds. + +.. c:function:: void uv_print_all_handles(uv_loop_t* loop, FILE* stream) + + Prints all handles associated with the given `loop` to the given `stream`. + + Example: + + :: + + uv_print_all_handles(uv_default_loop(), stderr); + /* + [--I] signal 0x1a25ea8 + [-AI] async 0x1a25cf0 + [R--] idle 0x1a7a8c8 + */ + + The format is `[flags] handle-type handle-address`. For `flags`: + + - `R` is printed for a handle that is referenced + - `A` is printed for a handle that is active + - `I` is printed for a handle that is internal + + .. warning:: + This function is meant for ad hoc debugging, there is no API/ABI + stability guarantees. + + .. versionadded:: 1.8.0 + +.. c:function:: void uv_print_active_handles(uv_loop_t* loop, FILE* stream) + + This is the same as :c:func:`uv_print_all_handles` except only active handles + are printed. + + .. warning:: + This function is meant for ad hoc debugging, there is no API/ABI + stability guarantees. + + .. versionadded:: 1.8.0 + +.. c:function:: int uv_os_environ(uv_env_item_t** envitems, int* count) + + Retrieves all environment variables. This function will allocate memory + which must be freed by calling :c:func:`uv_os_free_environ`. + + .. warning:: + This function is not thread safe. + + .. versionadded:: 1.31.0 + +.. c:function:: void uv_os_free_environ(uv_env_item_t* envitems, int count); + + Frees the memory allocated for the environment variables by + :c:func:`uv_os_environ`. + + .. versionadded:: 1.31.0 + +.. c:function:: int uv_os_getenv(const char* name, char* buffer, size_t* size) + + Retrieves the environment variable specified by `name`, copies its value + into `buffer`, and sets `size` to the string length of the value. When + calling this function, `size` must be set to the amount of storage available + in `buffer`, including the null terminator. If the environment variable + exceeds the storage available in `buffer`, `UV_ENOBUFS` is returned, and + `size` is set to the amount of storage required to hold the value. If no + matching environment variable exists, `UV_ENOENT` is returned. + + .. warning:: + This function is not thread safe. + + .. versionadded:: 1.12.0 + +.. c:function:: int uv_os_setenv(const char* name, const char* value) + + Creates or updates the environment variable specified by `name` with + `value`. + + .. warning:: + This function is not thread safe. + + .. versionadded:: 1.12.0 + +.. c:function:: int uv_os_unsetenv(const char* name) + + Deletes the environment variable specified by `name`. If no such environment + variable exists, this function returns successfully. + + .. warning:: + This function is not thread safe. + + .. versionadded:: 1.12.0 + +.. c:function:: int uv_os_gethostname(char* buffer, size_t* size) + + Returns the hostname as a null-terminated string in `buffer`, and sets + `size` to the string length of the hostname. When calling this function, + `size` must be set to the amount of storage available in `buffer`, including + the null terminator. If the hostname exceeds the storage available in + `buffer`, `UV_ENOBUFS` is returned, and `size` is set to the amount of + storage required to hold the value. + + .. versionadded:: 1.12.0 + + .. versionchanged:: 1.26.0 `UV_MAXHOSTNAMESIZE` is available and represents + the maximum `buffer` size required to store a + hostname and terminating `nul` character. + +.. c:function:: int uv_os_getpriority(uv_pid_t pid, int* priority) + + Retrieves the scheduling priority of the process specified by `pid`. The + returned value of `priority` is between -20 (high priority) and 19 (low + priority). + + .. note:: + On Windows, the returned priority will equal one of the `UV_PRIORITY` + constants. + + .. versionadded:: 1.23.0 + +.. c:function:: int uv_os_setpriority(uv_pid_t pid, int priority) + + Sets the scheduling priority of the process specified by `pid`. The + `priority` value range is between -20 (high priority) and 19 (low priority). + The constants `UV_PRIORITY_LOW`, `UV_PRIORITY_BELOW_NORMAL`, + `UV_PRIORITY_NORMAL`, `UV_PRIORITY_ABOVE_NORMAL`, `UV_PRIORITY_HIGH`, and + `UV_PRIORITY_HIGHEST` are also provided for convenience. + + .. note:: + On Windows, this function utilizes `SetPriorityClass()`. The `priority` + argument is mapped to a Windows priority class. When retrieving the + process priority, the result will equal one of the `UV_PRIORITY` + constants, and not necessarily the exact value of `priority`. + + .. note:: + On Windows, setting `PRIORITY_HIGHEST` will only work for elevated user, + for others it will be silently reduced to `PRIORITY_HIGH`. + + .. note:: + On IBM i PASE, the highest process priority is -10. The constant + `UV_PRIORITY_HIGHEST` is -10, `UV_PRIORITY_HIGH` is -7, + `UV_PRIORITY_ABOVE_NORMAL` is -4, `UV_PRIORITY_NORMAL` is 0, + `UV_PRIORITY_BELOW_NORMAL` is 15 and `UV_PRIORITY_LOW` is 39. + + .. note:: + On IBM i PASE, you are not allowed to change your priority unless you + have the \*JOBCTL special authority (even to lower it). + + .. versionadded:: 1.23.0 + +.. c:function:: int uv_os_uname(uv_utsname_t* buffer) + + Retrieves system information in `buffer`. The populated data includes the + operating system name, release, version, and machine. On non-Windows + systems, `uv_os_uname()` is a thin wrapper around :man:`uname(2)`. Returns + zero on success, and a non-zero error value otherwise. + + .. versionadded:: 1.25.0 + +.. c:function:: int uv_gettimeofday(uv_timeval64_t* tv) + + Cross-platform implementation of :man:`gettimeofday(2)`. The timezone + argument to `gettimeofday()` is not supported, as it is considered obsolete. + + .. versionadded:: 1.28.0 + +.. c:function:: int uv_random(uv_loop_t* loop, uv_random_t* req, void* buf, size_t buflen, unsigned int flags, uv_random_cb cb) + + Fill `buf` with exactly `buflen` cryptographically strong random bytes + acquired from the system CSPRNG. `flags` is reserved for future extension + and must currently be 0. + + Short reads are not possible. When less than `buflen` random bytes are + available, a non-zero error value is returned or passed to the callback. + + The synchronous version may block indefinitely when not enough entropy + is available. The asynchronous version may not ever finish when the system + is low on entropy. + + Sources of entropy: + + - Windows: `RtlGenRandom _`. + - Linux, Android: :man:`getrandom(2)` if available, or :man:`urandom(4)` + after reading from `/dev/random` once, or the `KERN_RANDOM` + :man:`sysctl(2)`. + - FreeBSD: `getrandom(2) _`, + or `/dev/urandom` after reading from `/dev/random` once. + - NetBSD: `KERN_ARND` `sysctl(3) _` + - macOS, OpenBSD: `getentropy(2) _` + if available, or `/dev/urandom` after reading from `/dev/random` once. + - AIX: `/dev/random`. + - IBM i: `/dev/urandom`. + - Other UNIX: `/dev/urandom` after reading from `/dev/random` once. + + :returns: 0 on success, or an error code < 0 on failure. The contents of + `buf` is undefined after an error. + + .. note:: + When using the synchronous version, both `loop` and `req` parameters + are not used and can be set to `NULL`. + + .. versionadded:: 1.33.0 + +.. c:function:: void uv_sleep(unsigned int msec) + + Causes the calling thread to sleep for `msec` milliseconds. + + .. versionadded:: 1.34.0 diff --git a/include/libuv/docs/src/pipe.rst b/include/libuv/docs/src/pipe.rst new file mode 100644 index 000000000..5fa83b80d --- /dev/null +++ b/include/libuv/docs/src/pipe.rst @@ -0,0 +1,138 @@ + +.. _pipe: + +:c:type:`uv_pipe_t` --- Pipe handle +=================================== + +Pipe handles provide an abstraction over streaming files on Unix (including +local domain sockets, pipes, and FIFOs) and named pipes on Windows. + +:c:type:`uv_pipe_t` is a 'subclass' of :c:type:`uv_stream_t`. + + +Data types +---------- + +.. c:type:: uv_pipe_t + + Pipe handle type. + + +Public members +^^^^^^^^^^^^^^ + +.. c:member:: int uv_pipe_t.ipc + + Whether this pipe is suitable for handle passing between processes. + Only a connected pipe that will be passing the handles should have this flag + set, not the listening pipe that uv_accept is called on. + +.. seealso:: The :c:type:`uv_stream_t` members also apply. + + +API +--- + +.. c:function:: int uv_pipe_init(uv_loop_t* loop, uv_pipe_t* handle, int ipc) + + Initialize a pipe handle. The `ipc` argument is a boolean to indicate if + this pipe will be used for handle passing between processes (which may + change the bytes on the wire). Only a connected pipe that will be + passing the handles should have this flag set, not the listening pipe + that uv_accept is called on. + +.. c:function:: int uv_pipe_open(uv_pipe_t* handle, uv_file file) + + Open an existing file descriptor or HANDLE as a pipe. + + .. versionchanged:: 1.2.1 the file descriptor is set to non-blocking mode. + + .. note:: + The passed file descriptor or HANDLE is not checked for its type, but + it's required that it represents a valid pipe. + +.. c:function:: int uv_pipe_bind(uv_pipe_t* handle, const char* name) + + Bind the pipe to a file path (Unix) or a name (Windows). + + .. note:: + Paths on Unix get truncated to ``sizeof(sockaddr_un.sun_path)`` bytes, typically between + 92 and 108 bytes. + +.. c:function:: void uv_pipe_connect(uv_connect_t* req, uv_pipe_t* handle, const char* name, uv_connect_cb cb) + + Connect to the Unix domain socket or the named pipe. + + .. note:: + Paths on Unix get truncated to ``sizeof(sockaddr_un.sun_path)`` bytes, typically between + 92 and 108 bytes. + +.. c:function:: int uv_pipe_getsockname(const uv_pipe_t* handle, char* buffer, size_t* size) + + Get the name of the Unix domain socket or the named pipe. + + A preallocated buffer must be provided. The size parameter holds the length + of the buffer and it's set to the number of bytes written to the buffer on + output. If the buffer is not big enough ``UV_ENOBUFS`` will be returned and + len will contain the required size. + + .. versionchanged:: 1.3.0 the returned length no longer includes the terminating null byte, + and the buffer is not null terminated. + +.. c:function:: int uv_pipe_getpeername(const uv_pipe_t* handle, char* buffer, size_t* size) + + Get the name of the Unix domain socket or the named pipe to which the handle + is connected. + + A preallocated buffer must be provided. The size parameter holds the length + of the buffer and it's set to the number of bytes written to the buffer on + output. If the buffer is not big enough ``UV_ENOBUFS`` will be returned and + len will contain the required size. + + .. versionadded:: 1.3.0 + +.. c:function:: void uv_pipe_pending_instances(uv_pipe_t* handle, int count) + + Set the number of pending pipe instance handles when the pipe server is + waiting for connections. + + .. note:: + This setting applies to Windows only. + +.. c:function:: int uv_pipe_pending_count(uv_pipe_t* handle) +.. c:function:: uv_handle_type uv_pipe_pending_type(uv_pipe_t* handle) + + Used to receive handles over IPC pipes. + + First - call :c:func:`uv_pipe_pending_count`, if it's > 0 then initialize + a handle of the given `type`, returned by :c:func:`uv_pipe_pending_type` + and call ``uv_accept(pipe, handle)``. + +.. seealso:: The :c:type:`uv_stream_t` API functions also apply. + +.. c:function:: int uv_pipe_chmod(uv_pipe_t* handle, int flags) + + Alters pipe permissions, allowing it to be accessed from processes run by + different users. Makes the pipe writable or readable by all users. Mode can + be ``UV_WRITABLE``, ``UV_READABLE`` or ``UV_WRITABLE | UV_READABLE``. This + function is blocking. + + .. versionadded:: 1.16.0 + +.. c:function:: int uv_pipe(uv_file fds[2], int read_flags, int write_flags) + + Create a pair of connected pipe handles. + Data may be written to `fds[1]` and read from `fds[0]`. + The resulting handles can be passed to `uv_pipe_open`, used with `uv_spawn`, + or for any other purpose. + + Valid values for `flags` are: + + - UV_NONBLOCK_PIPE: Opens the specified socket handle for `OVERLAPPED` + or `FIONBIO`/`O_NONBLOCK` I/O usage. + This is recommended for handles that will be used by libuv, + and not usually recommended otherwise. + + Equivalent to :man:`pipe(2)` with the `O_CLOEXEC` flag set. + + .. versionadded:: 1.41.0 diff --git a/include/libuv/docs/src/poll.rst b/include/libuv/docs/src/poll.rst new file mode 100644 index 000000000..93a101ec6 --- /dev/null +++ b/include/libuv/docs/src/poll.rst @@ -0,0 +1,148 @@ + +.. _poll: + +:c:type:`uv_poll_t` --- Poll handle +=================================== + +Poll handles are used to watch file descriptors for readability, +writability and disconnection similar to the purpose of :man:`poll(2)`. + +The purpose of poll handles is to enable integrating external libraries that +rely on the event loop to signal it about the socket status changes, like +c-ares or libssh2. Using uv_poll_t for any other purpose is not recommended; +:c:type:`uv_tcp_t`, :c:type:`uv_udp_t`, etc. provide an implementation that is faster and +more scalable than what can be achieved with :c:type:`uv_poll_t`, especially on +Windows. + +It is possible that poll handles occasionally signal that a file descriptor is +readable or writable even when it isn't. The user should therefore always +be prepared to handle EAGAIN or equivalent when it attempts to read from or +write to the fd. + +It is not okay to have multiple active poll handles for the same socket, this +can cause libuv to busyloop or otherwise malfunction. + +The user should not close a file descriptor while it is being polled by an +active poll handle. This can cause the handle to report an error, +but it might also start polling another socket. However the fd can be safely +closed immediately after a call to :c:func:`uv_poll_stop` or :c:func:`uv_close`. + +.. note:: + On windows only sockets can be polled with poll handles. On Unix any file + descriptor that would be accepted by :man:`poll(2)` can be used. + +.. note:: + On AIX, watching for disconnection is not supported. + +Data types +---------- + +.. c:type:: uv_poll_t + + Poll handle type. + +.. c:type:: void (*uv_poll_cb)(uv_poll_t* handle, int status, int events) + + Type definition for callback passed to :c:func:`uv_poll_start`. + +.. c:type:: uv_poll_event + + Poll event types + + :: + + enum uv_poll_event { + UV_READABLE = 1, + UV_WRITABLE = 2, + UV_DISCONNECT = 4, + UV_PRIORITIZED = 8 + }; + + +Public members +^^^^^^^^^^^^^^ + +N/A + +.. seealso:: The :c:type:`uv_handle_t` members also apply. + + +API +--- + +.. c:function:: int uv_poll_init(uv_loop_t* loop, uv_poll_t* handle, int fd) + + Initialize the handle using a file descriptor. + + .. versionchanged:: 1.2.2 the file descriptor is set to non-blocking mode. + +.. c:function:: int uv_poll_init_socket(uv_loop_t* loop, uv_poll_t* handle, uv_os_sock_t socket) + + Initialize the handle using a socket descriptor. On Unix this is identical + to :c:func:`uv_poll_init`. On windows it takes a SOCKET handle. + + .. versionchanged:: 1.2.2 the socket is set to non-blocking mode. + +.. c:function:: int uv_poll_start(uv_poll_t* handle, int events, uv_poll_cb cb) + + Starts polling the file descriptor. `events` is a bitmask made up of + `UV_READABLE`, `UV_WRITABLE`, `UV_PRIORITIZED` and `UV_DISCONNECT`. As soon + as an event is detected the callback will be called with `status` set to 0, + and the detected events set on the `events` field. + + The `UV_PRIORITIZED` event is used to watch for sysfs interrupts or TCP + out-of-band messages. + + The `UV_DISCONNECT` event is optional in the sense that it may not be + reported and the user is free to ignore it, but it can help optimize the + shutdown path because an extra read or write call might be avoided. + + If an error happens while polling, `status` will be < 0 and corresponds + with one of the `UV_E*` error codes (see :ref:`errors`). The user should + not close the socket while the handle is active. If the user does that + anyway, the callback *may* be called reporting an error status, but this is + **not** guaranteed. + + .. note:: + Calling :c:func:`uv_poll_start` on a handle that is already active is + fine. Doing so will update the events mask that is being watched for. + + .. note:: + Though `UV_DISCONNECT` can be set, it is unsupported on AIX and as such + will not be set on the `events` field in the callback. + + .. note:: + If one of the events `UV_READABLE` or `UV_WRITABLE` are set, the + callback will be called again, as long as the given fd/socket remains + readable or writable accordingly. Particularly in each of the following + scenarios: + + * The callback has been called because the socket became + readable/writable and the callback did not conduct a read/write on + this socket at all. + * The callback committed a read on the socket, and has not read all the + available data (when `UV_READABLE` is set). + * The callback committed a write on the socket, but it remained + writable afterwards (when `UV_WRITABLE` is set). + * The socket has already became readable/writable before calling + :c:func:`uv_poll_start` on a poll handle associated with this socket, + and since then the state of the socket did not changed. + + In all of the above listed scenarios, the socket remains readable or + writable and hence the callback will be called again (depending on the + events set in the bitmask). This behaviour is known as level + triggering. + + .. versionchanged:: 1.9.0 Added the `UV_DISCONNECT` event. + .. versionchanged:: 1.14.0 Added the `UV_PRIORITIZED` event. + +.. c:function:: int uv_poll_stop(uv_poll_t* poll) + + Stop polling the file descriptor, the callback will no longer be called. + + .. note:: + Calling :c:func:`uv_poll_stop` is effective immediately: any pending + callback is also canceled, even if the socket state change notification + was already pending. + +.. seealso:: The :c:type:`uv_handle_t` API functions also apply. diff --git a/include/libuv/docs/src/prepare.rst b/include/libuv/docs/src/prepare.rst new file mode 100644 index 000000000..5e0d24766 --- /dev/null +++ b/include/libuv/docs/src/prepare.rst @@ -0,0 +1,54 @@ + +.. _prepare: + +:c:type:`uv_prepare_t` --- Prepare handle +========================================= + +Prepare handles will run the given callback once per loop iteration, right +before polling for i/o. + + +Data types +---------- + +.. c:type:: uv_prepare_t + + Prepare handle type. + +.. c:type:: void (*uv_prepare_cb)(uv_prepare_t* handle) + + Type definition for callback passed to :c:func:`uv_prepare_start`. + + +Public members +^^^^^^^^^^^^^^ + +N/A + +.. seealso:: The :c:type:`uv_handle_t` members also apply. + + +API +--- + +.. c:function:: int uv_prepare_init(uv_loop_t* loop, uv_prepare_t* prepare) + + Initialize the handle. This function always succeeds. + + :returns: 0 + +.. c:function:: int uv_prepare_start(uv_prepare_t* prepare, uv_prepare_cb cb) + + Start the handle with the given callback. This function always succeeds, + except when `cb` is `NULL`. + + :returns: 0 on success, or `UV_EINVAL` when `cb == NULL`. + +.. c:function:: int uv_prepare_stop(uv_prepare_t* prepare) + + Stop the handle, the callback will no longer be called. + This function always succeeds. + + :returns: 0 + +.. seealso:: The :c:type:`uv_handle_t` API functions also apply. diff --git a/include/libuv/docs/src/process.rst b/include/libuv/docs/src/process.rst new file mode 100644 index 000000000..ea6c4b9ad --- /dev/null +++ b/include/libuv/docs/src/process.rst @@ -0,0 +1,251 @@ + +.. _process: + +:c:type:`uv_process_t` --- Process handle +========================================= + +Process handles will spawn a new process and allow the user to control it and +establish communication channels with it using streams. + + +Data types +---------- + +.. c:type:: uv_process_t + + Process handle type. + +.. c:type:: uv_process_options_t + + Options for spawning the process (passed to :c:func:`uv_spawn`. + + :: + + typedef struct uv_process_options_s { + uv_exit_cb exit_cb; + const char* file; + char** args; + char** env; + const char* cwd; + unsigned int flags; + int stdio_count; + uv_stdio_container_t* stdio; + uv_uid_t uid; + uv_gid_t gid; + } uv_process_options_t; + +.. c:type:: void (*uv_exit_cb)(uv_process_t*, int64_t exit_status, int term_signal) + + Type definition for callback passed in :c:type:`uv_process_options_t` which + will indicate the exit status and the signal that caused the process to + terminate, if any. + +.. c:type:: uv_process_flags + + Flags to be set on the flags field of :c:type:`uv_process_options_t`. + + :: + + enum uv_process_flags { + /* + * Set the child process' user id. + */ + UV_PROCESS_SETUID = (1 << 0), + /* + * Set the child process' group id. + */ + UV_PROCESS_SETGID = (1 << 1), + /* + * Do not wrap any arguments in quotes, or perform any other escaping, when + * converting the argument list into a command line string. This option is + * only meaningful on Windows systems. On Unix it is silently ignored. + */ + UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS = (1 << 2), + /* + * Spawn the child process in a detached state - this will make it a process + * group leader, and will effectively enable the child to keep running after + * the parent exits. Note that the child process will still keep the + * parent's event loop alive unless the parent process calls uv_unref() on + * the child's process handle. + */ + UV_PROCESS_DETACHED = (1 << 3), + /* + * Hide the subprocess window that would normally be created. This option is + * only meaningful on Windows systems. On Unix it is silently ignored. + */ + UV_PROCESS_WINDOWS_HIDE = (1 << 4), + /* + * Hide the subprocess console window that would normally be created. This + * option is only meaningful on Windows systems. On Unix it is silently + * ignored. + */ + UV_PROCESS_WINDOWS_HIDE_CONSOLE = (1 << 5), + /* + * Hide the subprocess GUI window that would normally be created. This + * option is only meaningful on Windows systems. On Unix it is silently + * ignored. + */ + UV_PROCESS_WINDOWS_HIDE_GUI = (1 << 6) + }; + +.. c:type:: uv_stdio_container_t + + Container for each stdio handle or fd passed to a child process. + + :: + + typedef struct uv_stdio_container_s { + uv_stdio_flags flags; + union { + uv_stream_t* stream; + int fd; + } data; + } uv_stdio_container_t; + +.. c:enum:: uv_stdio_flags + + Flags specifying how a stdio should be transmitted to the child process. + + :: + + typedef enum { + UV_IGNORE = 0x00, + UV_CREATE_PIPE = 0x01, + UV_INHERIT_FD = 0x02, + UV_INHERIT_STREAM = 0x04, + /* + * When UV_CREATE_PIPE is specified, UV_READABLE_PIPE and UV_WRITABLE_PIPE + * determine the direction of flow, from the child process' perspective. Both + * flags may be specified to create a duplex data stream. + */ + UV_READABLE_PIPE = 0x10, + UV_WRITABLE_PIPE = 0x20, + /* + * When UV_CREATE_PIPE is specified, specifying UV_NONBLOCK_PIPE opens the + * handle in non-blocking mode in the child. This may cause loss of data, + * if the child is not designed to handle to encounter this mode, + * but can also be significantly more efficient. + */ + UV_NONBLOCK_PIPE = 0x40 + } uv_stdio_flags; + + +Public members +^^^^^^^^^^^^^^ + +.. c:member:: int uv_process_t.pid + + The PID of the spawned process. It's set after calling :c:func:`uv_spawn`. + +.. note:: + The :c:type:`uv_handle_t` members also apply. + +.. c:member:: uv_exit_cb uv_process_options_t.exit_cb + + Callback called after the process exits. + +.. c:member:: const char* uv_process_options_t.file + + Path pointing to the program to be executed. + +.. c:member:: char** uv_process_options_t.args + + Command line arguments. args[0] should be the path to the program. On + Windows this uses `CreateProcess` which concatenates the arguments into a + string this can cause some strange errors. See the + ``UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS`` flag on :c:type:`uv_process_flags`. + +.. c:member:: char** uv_process_options_t.env + + Environment for the new process. If NULL the parents environment is used. + +.. c:member:: const char* uv_process_options_t.cwd + + Current working directory for the subprocess. + +.. c:member:: unsigned int uv_process_options_t.flags + + Various flags that control how :c:func:`uv_spawn` behaves. See + :c:type:`uv_process_flags`. + +.. c:member:: int uv_process_options_t.stdio_count +.. c:member:: uv_stdio_container_t* uv_process_options_t.stdio + + The `stdio` field points to an array of :c:type:`uv_stdio_container_t` + structs that describe the file descriptors that will be made available to + the child process. The convention is that stdio[0] points to stdin, + fd 1 is used for stdout, and fd 2 is stderr. + + .. note:: + On Windows file descriptors greater than 2 are available to the child process only if + the child processes uses the MSVCRT runtime. + +.. c:member:: uv_uid_t uv_process_options_t.uid +.. c:member:: uv_gid_t uv_process_options_t.gid + + Libuv can change the child process' user/group id. This happens only when + the appropriate bits are set in the flags fields. + + .. note:: + This is not supported on Windows, :c:func:`uv_spawn` will fail and set the error + to ``UV_ENOTSUP``. + +.. c:member:: uv_stdio_flags uv_stdio_container_t.flags + + Flags specifying how the stdio container should be passed to the child. + +.. c:member:: union @0 uv_stdio_container_t.data + + Union containing either the `stream` or `fd` to be passed on to the child + process. + + +API +--- + +.. c:function:: void uv_disable_stdio_inheritance(void) + + Disables inheritance for file descriptors / handles that this process + inherited from its parent. The effect is that child processes spawned by + this process don't accidentally inherit these handles. + + It is recommended to call this function as early in your program as possible, + before the inherited file descriptors can be closed or duplicated. + + .. note:: + This function works on a best-effort basis: there is no guarantee that libuv can discover + all file descriptors that were inherited. In general it does a better job on Windows than + it does on Unix. + +.. c:function:: int uv_spawn(uv_loop_t* loop, uv_process_t* handle, const uv_process_options_t* options) + + Initializes the process handle and starts the process. If the process is + successfully spawned, this function will return 0. Otherwise, the + negative error code corresponding to the reason it couldn't spawn is + returned. + + Possible reasons for failing to spawn would include (but not be limited to) + the file to execute not existing, not having permissions to use the setuid or + setgid specified, or not having enough memory to allocate for the new + process. + + .. versionchanged:: 1.24.0 Added `UV_PROCESS_WINDOWS_HIDE_CONSOLE` and + `UV_PROCESS_WINDOWS_HIDE_GUI` flags. + +.. c:function:: int uv_process_kill(uv_process_t* handle, int signum) + + Sends the specified signal to the given process handle. Check the documentation + on :c:ref:`signal` for signal support, specially on Windows. + +.. c:function:: int uv_kill(int pid, int signum) + + 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/include/libuv/docs/src/request.rst b/include/libuv/docs/src/request.rst new file mode 100644 index 000000000..a0414431b --- /dev/null +++ b/include/libuv/docs/src/request.rst @@ -0,0 +1,117 @@ + +.. _request: + +:c:type:`uv_req_t` --- Base request +=================================== + +`uv_req_t` is the base type for all libuv request types. + +Structures are aligned so that any libuv request can be cast to `uv_req_t`. +All API functions defined here work with any request type. + + +Data types +---------- + +.. c:type:: uv_req_t + + The base libuv request structure. + +.. c:type:: uv_any_req + + Union of all request types. + + +Public members +^^^^^^^^^^^^^^ + +.. c:member:: void* uv_req_t.data + + Space for user-defined arbitrary data. libuv does not use this field. + +.. c:member:: uv_req_type uv_req_t.type + + Indicated the type of request. Readonly. + + :: + + typedef enum { + UV_UNKNOWN_REQ = 0, + UV_REQ, + UV_CONNECT, + UV_WRITE, + UV_SHUTDOWN, + UV_UDP_SEND, + UV_FS, + UV_WORK, + UV_GETADDRINFO, + UV_GETNAMEINFO, + UV_REQ_TYPE_MAX, + } uv_req_type; + + +API +--- + +.. c:macro:: UV_REQ_TYPE_MAP(iter_macro) + + Macro that expands to a series of invocations of `iter_macro` for + each of the request types. `iter_macro` is invoked with two + arguments: the name of the `uv_req_type` element without the `UV_` + prefix, and the name of the corresponding structure type without the + `uv_` prefix and `_t` suffix. + +.. c:function:: int uv_cancel(uv_req_t* req) + + Cancel a pending request. Fails if the request is executing or has finished + executing. + + Returns 0 on success, or an error code < 0 on failure. + + Only cancellation of :c:type:`uv_fs_t`, :c:type:`uv_getaddrinfo_t`, + :c:type:`uv_getnameinfo_t`, :c:type:`uv_random_t` and :c:type:`uv_work_t` + requests is currently supported. + + Cancelled requests have their callbacks invoked some time in the future. + It's **not** safe to free the memory associated with the request until the + callback is called. + + Here is how cancellation is reported to the callback: + + * A :c:type:`uv_fs_t` request has its req->result field set to `UV_ECANCELED`. + + * A :c:type:`uv_work_t`, :c:type:`uv_getaddrinfo_t`, + :c:type:`uv_getnameinfo_t` or :c:type:`uv_random_t` request has its + callback invoked with status == `UV_ECANCELED`. + +.. c:function:: size_t uv_req_size(uv_req_type type) + + 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/include/libuv/docs/src/signal.rst b/include/libuv/docs/src/signal.rst new file mode 100644 index 000000000..eeadb95b0 --- /dev/null +++ b/include/libuv/docs/src/signal.rst @@ -0,0 +1,101 @@ + +.. _signal: + +:c:type:`uv_signal_t` --- Signal handle +======================================= + +Signal handles implement Unix style signal handling on a per-event loop bases. + +Windows notes +------------- + +Reception of some signals is emulated: + +* SIGINT is normally delivered when the user presses CTRL+C. However, like + on Unix, it is not generated when terminal raw mode is enabled. + +* SIGBREAK is delivered when the user pressed CTRL + BREAK. + +* SIGHUP is generated when the user closes the console window. On SIGHUP the + program is given approximately 10 seconds to perform cleanup. After that + Windows will unconditionally terminate it. + +* SIGWINCH is raised whenever libuv detects that the console has been + resized. When a libuv app is running under a console emulator, or when a + 32-bit libuv app is running on 64-bit system, SIGWINCH will be emulated. In + such cases SIGWINCH signals may not always be delivered in a timely manner. + For a writable :c:type:`uv_tty_t` handle libuv will only detect size changes + when the cursor is moved. When a readable :c:type:`uv_tty_t` handle is used, + resizing of the console buffer will be detected only if the handle is in raw + mode and is being read. + +* Watchers for other signals can be successfully created, but these signals + are never received. These signals are: `SIGILL`, `SIGABRT`, `SIGFPE`, `SIGSEGV`, + `SIGTERM` and `SIGKILL.` + +* Calls to raise() or abort() to programmatically raise a signal are + not detected by libuv; these will not trigger a signal watcher. + +.. versionchanged:: 1.15.0 SIGWINCH support on Windows was improved. +.. versionchanged:: 1.31.0 32-bit libuv SIGWINCH support on 64-bit Windows was + rolled back to old implementation. + +Unix notes +---------- + +* SIGKILL and SIGSTOP are impossible to catch. + +* Handling SIGBUS, SIGFPE, SIGILL or SIGSEGV via libuv results into undefined behavior. + +* SIGABRT will not be caught by libuv if generated by `abort()`, e.g. through `assert()`. + +* On Linux SIGRT0 and SIGRT1 (signals 32 and 33) are used by the NPTL pthreads library to + manage threads. Installing watchers for those signals will lead to unpredictable behavior + and is strongly discouraged. Future versions of libuv may simply reject them. + + +Data types +---------- + +.. c:type:: uv_signal_t + + Signal handle type. + +.. c:type:: void (*uv_signal_cb)(uv_signal_t* handle, int signum) + + Type definition for callback passed to :c:func:`uv_signal_start`. + + +Public members +^^^^^^^^^^^^^^ + +.. c:member:: int uv_signal_t.signum + + Signal being monitored by this handle. Readonly. + +.. seealso:: The :c:type:`uv_handle_t` members also apply. + + +API +--- + +.. c:function:: int uv_signal_init(uv_loop_t* loop, uv_signal_t* signal) + + Initialize the handle. + +.. c:function:: int uv_signal_start(uv_signal_t* signal, uv_signal_cb cb, int signum) + + Start the handle with the given callback, watching for the given signal. + +.. c:function:: int uv_signal_start_oneshot(uv_signal_t* signal, uv_signal_cb cb, int signum) + + .. versionadded:: 1.12.0 + + Same functionality as :c:func:`uv_signal_start` but the signal handler is reset the moment + the signal is received. + +.. c:function:: int uv_signal_stop(uv_signal_t* signal) + + Stop the handle, the callback will no longer be called. + +.. seealso:: The :c:type:`uv_handle_t` API functions also apply. diff --git a/include/libuv/docs/src/sphinx-plugins/manpage.py b/include/libuv/docs/src/sphinx-plugins/manpage.py new file mode 100644 index 000000000..6570aeaf3 --- /dev/null +++ b/include/libuv/docs/src/sphinx-plugins/manpage.py @@ -0,0 +1,45 @@ +# encoding: utf-8 + +# +# Copyright (c) 2013 Dariusz Dwornikowski. All rights reserved. +# +# Adapted from https://github.com/tdi/sphinxcontrib-manpage +# License: Apache 2 +# + + +import re + +from docutils import nodes, utils +from docutils.parsers.rst.roles import set_classes +from string import Template + + +def make_link_node(rawtext, app, name, manpage_num, options): + ref = app.config.man_url_regex + if not ref: + ref = "https://man7.org/linux/man-pages/man%s/%s.%s.html" %(manpage_num, name, manpage_num) + else: + s = Template(ref) + ref = s.substitute(num=manpage_num, topic=name) + set_classes(options) + node = nodes.reference(rawtext, "%s(%s)" % (name, manpage_num), refuri=ref, **options) + return node + + +def man_role(name, rawtext, text, lineno, inliner, options={}, content=[]): + app = inliner.document.settings.env.app + p = re.compile("([a-zA-Z0-9_\.-_]+)\((\d)\)") + m = p.match(text) + + manpage_num = m.group(2) + name = m.group(1) + node = make_link_node(rawtext, app, name, manpage_num, options) + return [node], [] + + +def setup(app): + app.add_role('man', man_role) + app.add_config_value('man_url_regex', None, 'env') + return + diff --git a/include/libuv/docs/src/static/architecture.png b/include/libuv/docs/src/static/architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..81e8749f2495741d4b4c2c5dd8a6bca8803d8818 GIT binary patch literal 206767 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

|<0@v3w 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 aM1obxbA2B^pWMfP?%y@PQ+oUP>;DChUmDi{ literal 0 HcmV?d00001 diff --git a/include/libuv/docs/src/static/diagrams.key/Data/st0-311.jpg b/include/libuv/docs/src/static/diagrams.key/Data/st0-311.jpg new file mode 100644 index 0000000000000000000000000000000000000000..08f23a90b6ea37b207550368944f3f727a58e5e6 GIT binary patch literal 14413 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 U{Vf@vRTq*L{vT{Dqk5SCA1ib~G5`Po literal 0 HcmV?d00001 diff --git a/include/libuv/docs/src/static/diagrams.key/Data/st1-475.jpg b/include/libuv/docs/src/static/diagrams.key/Data/st1-475.jpg new file mode 100644 index 0000000000000000000000000000000000000000..26e676a71a3c1d1c5b965e7db73a7553cd6bf71c GIT binary patch literal 8284 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 literal 0 HcmV?d00001 diff --git a/include/libuv/docs/src/static/diagrams.key/Index.zip b/include/libuv/docs/src/static/diagrams.key/Index.zip new file mode 100644 index 0000000000000000000000000000000000000000..17aedace14faf8f2990df3232565328901209d5a GIT binary patch literal 71160 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 + + + + Template: White (2014-02-28 09:41) + M6.2.2-1878-1 + + diff --git a/include/libuv/docs/src/static/diagrams.key/Metadata/DocumentIdentifier b/include/libuv/docs/src/static/diagrams.key/Metadata/DocumentIdentifier new file mode 100644 index 000000000..ddb18f01f --- /dev/null +++ b/include/libuv/docs/src/static/diagrams.key/Metadata/DocumentIdentifier @@ -0,0 +1 @@ +F69E9CD9-EEF1-4223-9DA4-A1EA7FE112BA \ No newline at end of file diff --git a/include/libuv/docs/src/static/diagrams.key/Metadata/Properties.plist b/include/libuv/docs/src/static/diagrams.key/Metadata/Properties.plist new file mode 100644 index 0000000000000000000000000000000000000000..74bc69317de4fc4ae87da8c71d1615385d70d1f2 GIT binary patch literal 340 zcmYc)$jK}&F)+B!$i&RT%Er#Y$;HjX%NJ3UT9#RynV%Pylb@WJlNytfpIn-onpYAU z>gf_)mRbao1G6)WeM@snG6NFRQ{x2$(=u~X-SUfa6HCG%Y9fm>5{ptnD&qyz4Xmup z3`|U&4UCL+jhsv!bxoX{O?55Y%*}M23@scDT}@4m%q)yTQ}g2eeBuRE+{`RpEuCE~ zbzNQE40TP6fZ8lw98GkADjm(;Tn!D4oE(A5h%u8h)KJez&wxln88{fk859`Q8B7=) l75HPbcg8&-~3o|P_8#_BY8yg!t2RA1> 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

2KUY7yz-7_`p(lc`HgEPC5(QDJ#Lr#t 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 Y>c{6t4Sw{%j~@8Z1FJo-=6lco0p?d?5&!@I literal 0 HcmV?d00001 diff --git a/include/libuv/docs/src/static/favicon.ico b/include/libuv/docs/src/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..2c40694cd282ff3d0db2c495c23f3beca40c17fa GIT binary patch literal 15086 zcmeI3J!oE47{^bE(6L>dlR?3;f?AMDoTP|gC(+GON^N!X18j$O66~NjsaQd=(xSMC zL)z#dI2x#nOSZwR!m3x7+NM_xVsKV)SFb<|EpaeeASlj(+q}%U2&yoPD*;Lm&Ey_UhK~ z_4og5Xr+DRP_sk!#l?%;Y8ZWV*^pfGuZHnl8XJu9Yiq#y(Ek|pp24Oo_K^744vEX% z&VG-&+OikQSk~Z?A1-gL|EIsK{O26#L%+nJ84qMmd3jchtG#hWC&Nw%vpc1cK(nJjju2B;@Lx= zdb$)`edHiu4|NQ8|RdXF;=Y~_aQds zM-KRv4+sy(@ZfC92gKI6;H2E;6K(qX-UX~%j0>mIj{8FYL$I_j$2@PzKi(Uqj$`zO z#4L5N(?>*Nx>uvT*8k+fg{fl8rQ8qrD`mB~$cC=BvHB2S^BB zo_6@a7e4Xb##oy-YQyg-4(0Y@Zd<>ncpwkI?I z#(nm2&Rk}dLs#8nOwJ%bGaw_^Z@yn{`_@$KJMZd%`rc^{L%16&qPZZ8yy&yXqHe8E zp4v^DL$+J0`R4$#?Y4)|~%PII2w6MbC!=E%c&^QfI|YuvXc=!{*1Y4?qsL2`h* z$o-C7&RrON*x$_I{LppiJ7@Zwgz&bZi%;^2d%G)#^1eckdtnIwS=8R#;7=cfx8p9} zQLZlb%I^mM$iuvbtO1gd?cjn{}^(OGIzMtu55|%vU*fmf#WDj|cBmZ3E&ULE z^DEBxoq?V3_c`Snao@w)J2O5Htv$MtTiLJ8Ps<-ZzF*?kddj;CIdjfw6M5D+I$xWc zhQGY!fnWQebuQ(MwWVLXOObWGTVP`@;#mIJ;fE&YSX18%)QRXmF?Jtf*EjFnSpH>g zJddJ&XMw)QMPI~mOn)r@Xj{7voZk&B*MxEA-}y5>b8-Dk``zH!=QGwGJ^1uJznGg9 z|1$TION}?{8}*_O`a|A9*KPP*J_HMHIpwG&wQL$C-RQE%*QsQ_U2XFL*_t?GB)|p zoTFdrz*D)r%G#KV^AR(r&U;7R5&a!b^kMysGZx8@XQ0nP@+p19c=pHC-WnK_JFI=lYsh%FxQ85$b^U(; zyvsXi2xoM5o45Q$;<>lsOiqr6az+ok?5p#43dY+lgu~eV!XCTR_3p~Qe5cL4mf+UW zmb(1x$@eO7H!864g(1E`#3$lv{^Anj&Bm9F=&$E59)YX*%ShmI{=yP?CV$TeB#Hb_ z`=8LKQ_5!f8%mqFnE#H7@%+D--L>Nu^WRyOxXOI~8?33Y;%}S(z@z1r`L%pA%3o~Y ik^kAsb9poGOsuT~U&vzKIyJc*jGI?B>sZ!zWBd=1T7Db= literal 0 HcmV?d00001 diff --git a/include/libuv/docs/src/static/logo.png b/include/libuv/docs/src/static/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..eaf1eee577b6774c345d5e21be51130a23b16627 GIT binary patch literal 33545 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 W3*P8um*8Y!7&$3rXpMwP!2bZAf*ghb literal 0 HcmV?d00001 diff --git a/include/libuv/docs/src/static/loop_iteration.png b/include/libuv/docs/src/static/loop_iteration.png new file mode 100644 index 0000000000000000000000000000000000000000..e769cf338b4456ea688b0abf888b646a4253e0eb GIT binary patch literal 80528 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

IlG||>&m()vh!j1bs%Xt$;1BU+W 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 0 if there is data available or < 0 on error. When we've + reached EOF, `nread` will be set to ``UV_EOF``. When `nread` < 0, + the `buf` parameter might not point to a valid buffer; in that case + `buf.len` and `buf.base` are both set to 0. + + .. note:: + `nread` might be 0, which does *not* indicate an error or EOF. This + is equivalent to ``EAGAIN`` or ``EWOULDBLOCK`` under ``read(2)``. + + The callee is responsible for stopping/closing the stream when an error happens + by calling :c:func:`uv_read_stop` or :c:func:`uv_close`. Trying to read + from the stream again is undefined. + + The callee is responsible for freeing the buffer, libuv does not reuse it. + The buffer may be a null buffer (where `buf->base` == NULL and `buf->len` == 0) + on error. + +.. c:type:: void (*uv_write_cb)(uv_write_t* req, int status) + + Callback called after data was written on a stream. `status` will be 0 in + case of success, < 0 otherwise. + +.. c:type:: void (*uv_connect_cb)(uv_connect_t* req, int status) + + Callback called after a connection started by :c:func:`uv_connect` is done. + `status` will be 0 in case of success, < 0 otherwise. + +.. c:type:: void (*uv_shutdown_cb)(uv_shutdown_t* req, int status) + + Callback called after a shutdown request has been completed. `status` will + be 0 in case of success, < 0 otherwise. + +.. c:type:: void (*uv_connection_cb)(uv_stream_t* server, int status) + + Callback called when a stream server has received an incoming connection. + The user can accept the connection by calling :c:func:`uv_accept`. + `status` will be 0 in case of success, < 0 otherwise. + + +Public members +^^^^^^^^^^^^^^ + +.. c:member:: size_t uv_stream_t.write_queue_size + + Contains the amount of queued bytes waiting to be sent. Readonly. + +.. c:member:: uv_stream_t* uv_connect_t.handle + + Pointer to the stream where this connection request is running. + +.. c:member:: uv_stream_t* uv_shutdown_t.handle + + Pointer to the stream where this shutdown request is running. + +.. c:member:: uv_stream_t* uv_write_t.handle + + Pointer to the stream where this write request is running. + +.. c:member:: uv_stream_t* uv_write_t.send_handle + + Pointer to the stream being sent using this write request. + +.. seealso:: The :c:type:`uv_handle_t` members also apply. + + +API +--- + +.. c:function:: int uv_shutdown(uv_shutdown_t* req, uv_stream_t* handle, uv_shutdown_cb cb) + + Shutdown the outgoing (write) side of a duplex stream. It waits for pending + write requests to complete. The `handle` should refer to a initialized stream. + `req` should be an uninitialized shutdown request struct. The `cb` is called + after shutdown is complete. + +.. c:function:: int uv_listen(uv_stream_t* stream, int backlog, uv_connection_cb cb) + + Start listening for incoming connections. `backlog` indicates the number of + connections the kernel might queue, same as :man:`listen(2)`. When a new + incoming connection is received the :c:type:`uv_connection_cb` callback is + called. + +.. c:function:: int uv_accept(uv_stream_t* server, uv_stream_t* client) + + This call is used in conjunction with :c:func:`uv_listen` to accept incoming + connections. Call this function after receiving a :c:type:`uv_connection_cb` + to accept the connection. Before calling this function the client handle must + be initialized. < 0 return value indicates an error. + + When the :c:type:`uv_connection_cb` callback is called it is guaranteed that + this function will complete successfully the first time. If you attempt to use + it more than once, it may fail. It is suggested to only call this function once + per :c:type:`uv_connection_cb` call. + + .. note:: + `server` and `client` must be handles running on the same loop. + +.. c:function:: int uv_read_start(uv_stream_t* stream, uv_alloc_cb alloc_cb, uv_read_cb read_cb) + + Read data from an incoming stream. The :c:type:`uv_read_cb` callback will + be made several times until there is no more data to read or + :c:func:`uv_read_stop` is called. + + .. versionchanged:: 1.38.0 :c:func:`uv_read_start()` now consistently + returns `UV_EALREADY` when called twice, and `UV_EINVAL` when the + stream is closing. With older libuv versions, it returns `UV_EALREADY` + on Windows but not UNIX, and `UV_EINVAL` on UNIX but not Windows. + +.. c:function:: int uv_read_stop(uv_stream_t*) + + Stop reading data from the stream. The :c:type:`uv_read_cb` callback will + no longer be called. + + This function is idempotent and may be safely called on a stopped stream. + + This function will always succeed; hence, checking its return value is + unnecessary. A non-zero return indicates that finishing releasing resources + may be pending on the next input event on that TTY on Windows, and does not + indicate failure. + +.. c:function:: int uv_write(uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb) + + Write data to stream. Buffers are written in order. Example: + + :: + + void cb(uv_write_t* req, int status) { + /* Logic which handles the write result */ + } + + uv_buf_t a[] = { + { .base = "1", .len = 1 }, + { .base = "2", .len = 1 } + }; + + uv_buf_t b[] = { + { .base = "3", .len = 1 }, + { .base = "4", .len = 1 } + }; + + uv_write_t req1; + uv_write_t req2; + + /* writes "1234" */ + uv_write(&req1, stream, a, 2, cb); + uv_write(&req2, stream, b, 2, cb); + + .. note:: + The memory pointed to by the buffers must remain valid until the callback gets called. + This also holds for :c:func:`uv_write2`. + +.. c:function:: int uv_write2(uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_stream_t* send_handle, uv_write_cb cb) + + Extended write function for sending handles over a pipe. The pipe must be + initialized with `ipc` == 1. + + .. note:: + `send_handle` must be a TCP, pipe and UDP handle on Unix, or a TCP + handle on Windows, which is a server or a connection (listening or + connected state). Bound sockets or pipes will be assumed to be servers. + +.. c:function:: int uv_try_write(uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs) + + Same as :c:func:`uv_write`, but won't queue a write request if it can't be + completed immediately. + + Will return either: + + * > 0: number of bytes written (can be less than the supplied buffer size). + * < 0: negative error code (``UV_EAGAIN`` is returned if no data can be sent + immediately). + +.. c:function:: int uv_try_write2(uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_stream_t* send_handle) + + Same as :c:func:`uv_try_write` and extended write function for sending + handles over a pipe like c:func:`uv_write2`. + + Try to send a handle is not supported on Windows, + where it returns ``UV_EAGAIN``. + + .. versionadded:: 1.42.0 + +.. c:function:: int uv_is_readable(const uv_stream_t* handle) + + Returns 1 if the stream is readable, 0 otherwise. + +.. c:function:: int uv_is_writable(const uv_stream_t* handle) + + Returns 1 if the stream is writable, 0 otherwise. + +.. c:function:: int uv_stream_set_blocking(uv_stream_t* handle, int blocking) + + Enable or disable blocking mode for a stream. + + When blocking mode is enabled all writes complete synchronously. The + interface remains unchanged otherwise, e.g. completion or failure of the + operation will still be reported through a callback which is made + asynchronously. + + .. warning:: + Relying too much on this API is not recommended. It is likely to change + significantly in the future. + + Currently only works on Windows for :c:type:`uv_pipe_t` handles. + On UNIX platforms, all :c:type:`uv_stream_t` handles are supported. + + Also libuv currently makes no ordering guarantee when the blocking mode + is changed after write requests have already been submitted. Therefore it is + recommended to set the blocking mode immediately after opening or creating + the stream. + + .. 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/include/libuv/docs/src/tcp.rst b/include/libuv/docs/src/tcp.rst new file mode 100644 index 000000000..cccc86bbf --- /dev/null +++ b/include/libuv/docs/src/tcp.rst @@ -0,0 +1,146 @@ + +.. _tcp: + +:c:type:`uv_tcp_t` --- TCP handle +================================= + +TCP handles are used to represent both TCP streams and servers. + +:c:type:`uv_tcp_t` is a 'subclass' of :c:type:`uv_stream_t`. + + +Data types +---------- + +.. c:type:: uv_tcp_t + + TCP handle type. + + +Public members +^^^^^^^^^^^^^^ + +N/A + +.. seealso:: The :c:type:`uv_stream_t` members also apply. + + +API +--- + +.. c:function:: int uv_tcp_init(uv_loop_t* loop, uv_tcp_t* handle) + + Initialize the handle. No socket is created as of yet. + +.. c:function:: int uv_tcp_init_ex(uv_loop_t* loop, uv_tcp_t* handle, unsigned int flags) + + Initialize the handle with the specified flags. At the moment only the lower 8 bits + of the `flags` parameter are used as the socket domain. A socket will be created + for the given domain. If the specified domain is ``AF_UNSPEC`` no socket is created, + just like :c:func:`uv_tcp_init`. + + .. versionadded:: 1.7.0 + +.. c:function:: int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) + + Open an existing file descriptor or SOCKET as a TCP handle. + + .. versionchanged:: 1.2.1 the file descriptor is set to non-blocking mode. + + .. note:: + The passed file descriptor or SOCKET is not checked for its type, but + it's required that it represents a valid stream socket. + +.. c:function:: int uv_tcp_nodelay(uv_tcp_t* handle, int enable) + + Enable `TCP_NODELAY`, which disables Nagle's algorithm. + +.. c:function:: int uv_tcp_keepalive(uv_tcp_t* handle, int enable, unsigned int delay) + + Enable / disable TCP keep-alive. `delay` is the initial delay in seconds, + ignored when `enable` is zero. + + After `delay` has been reached, 10 successive probes, each spaced 1 second + from the previous one, will still happen. If the connection is still lost + at the end of this procedure, then the handle is destroyed with a + ``UV_ETIMEDOUT`` error passed to the corresponding callback. + +.. c:function:: int uv_tcp_simultaneous_accepts(uv_tcp_t* handle, int enable) + + Enable / disable simultaneous asynchronous accept requests that are + queued by the operating system when listening for new TCP connections. + + This setting is used to tune a TCP server for the desired performance. + Having simultaneous accepts can significantly improve the rate of accepting + connections (which is why it is enabled by default) but may lead to uneven + load distribution in multi-process setups. + +.. c:function:: int uv_tcp_bind(uv_tcp_t* handle, const struct sockaddr* addr, unsigned int flags) + + Bind the handle to an address and port. `addr` should point to an + initialized ``struct sockaddr_in`` or ``struct sockaddr_in6``. + + When the port is already taken, you can expect to see an ``UV_EADDRINUSE`` + error from :c:func:`uv_listen` or :c:func:`uv_tcp_connect`. That is, + a successful call to this function does not guarantee that the call + to :c:func:`uv_listen` or :c:func:`uv_tcp_connect` will succeed as well. + + `flags` can contain ``UV_TCP_IPV6ONLY``, in which case dual-stack support + is disabled and only IPv6 is used. + +.. c:function:: int uv_tcp_getsockname(const uv_tcp_t* handle, struct sockaddr* name, int* namelen) + + Get the current address to which the handle is bound. `name` must point to + a valid and big enough chunk of memory, ``struct sockaddr_storage`` is + recommended for IPv4 and IPv6 support. + +.. c:function:: int uv_tcp_getpeername(const uv_tcp_t* handle, struct sockaddr* name, int* namelen) + + Get the address of the peer connected to the handle. `name` must point to + a valid and big enough chunk of memory, ``struct sockaddr_storage`` is + recommended for IPv4 and IPv6 support. + +.. c:function:: int uv_tcp_connect(uv_connect_t* req, uv_tcp_t* handle, const struct sockaddr* addr, uv_connect_cb cb) + + Establish an IPv4 or IPv6 TCP connection. Provide an initialized TCP handle + and an uninitialized :c:type:`uv_connect_t`. `addr` should point to an + initialized ``struct sockaddr_in`` or ``struct sockaddr_in6``. + + On Windows if the `addr` is initialized to point to an unspecified address + (``0.0.0.0`` or ``::``) it will be changed to point to ``localhost``. + This is done to match the behavior of Linux systems. + + The callback is made when the connection has been established or when a + connection error happened. + + .. versionchanged:: 1.19.0 added ``0.0.0.0`` and ``::`` to ``localhost`` + mapping + +.. seealso:: The :c:type:`uv_stream_t` API functions also apply. + +.. c:function:: int uv_tcp_close_reset(uv_tcp_t* handle, uv_close_cb close_cb) + + Resets a TCP connection by sending a RST packet. This is accomplished by + setting the `SO_LINGER` socket option with a linger interval of zero and + then calling :c:func:`uv_close`. + Due to some platform inconsistencies, mixing of :c:func:`uv_shutdown` and + :c:func:`uv_tcp_close_reset` calls is not allowed. + + .. versionadded:: 1.32.0 + +.. c:function:: int uv_socketpair(int type, int protocol, uv_os_sock_t socket_vector[2], int flags0, int flags1) + + Create a pair of connected sockets with the specified properties. + The resulting handles can be passed to `uv_tcp_open`, used with `uv_spawn`, + or for any other purpose. + + Valid values for `flags0` and `flags1` are: + + - UV_NONBLOCK_PIPE: Opens the specified socket handle for `OVERLAPPED` + or `FIONBIO`/`O_NONBLOCK` I/O usage. + This is recommended for handles that will be used by libuv, + and not usually recommended otherwise. + + Equivalent to :man:`socketpair(2)` with a domain of AF_UNIX. + + .. versionadded:: 1.41.0 diff --git a/include/libuv/docs/src/threading.rst b/include/libuv/docs/src/threading.rst new file mode 100644 index 000000000..7ca1d4b7a --- /dev/null +++ b/include/libuv/docs/src/threading.rst @@ -0,0 +1,197 @@ + +.. _threading: + +Threading and synchronization utilities +======================================= + +libuv provides cross-platform implementations for multiple threading and +synchronization primitives. The API largely follows the pthreads API. + + +Data types +---------- + +.. c:type:: uv_thread_t + + Thread data type. + +.. c:type:: void (*uv_thread_cb)(void* arg) + + Callback that is invoked to initialize thread execution. `arg` is the same + value that was passed to :c:func:`uv_thread_create`. + +.. c:type:: uv_key_t + + Thread-local key data type. + +.. c:type:: uv_once_t + + Once-only initializer data type. + +.. c:type:: uv_mutex_t + + Mutex data type. + +.. c:type:: uv_rwlock_t + + Read-write lock data type. + +.. c:type:: uv_sem_t + + Semaphore data type. + +.. c:type:: uv_cond_t + + Condition data type. + +.. c:type:: uv_barrier_t + + Barrier data type. + + +API +--- + +Threads +^^^^^^^ + +.. c:type:: uv_thread_options_t + + Options for spawning a new thread (passed to :c:func:`uv_thread_create_ex`). + + :: + + typedef struct uv_thread_options_s { + enum { + UV_THREAD_NO_FLAGS = 0x00, + UV_THREAD_HAS_STACK_SIZE = 0x01 + } flags; + size_t stack_size; + } uv_thread_options_t; + + More fields may be added to this struct at any time, so its exact + layout and size should not be relied upon. + + .. versionadded:: 1.26.0 + +.. c:function:: int uv_thread_create(uv_thread_t* tid, uv_thread_cb entry, void* arg) + + .. versionchanged:: 1.4.1 returns a UV_E* error code on failure + +.. c:function:: int uv_thread_create_ex(uv_thread_t* tid, const uv_thread_options_t* params, uv_thread_cb entry, void* arg) + + Like :c:func:`uv_thread_create`, but additionally specifies options for creating a new thread. + + If `UV_THREAD_HAS_STACK_SIZE` is set, `stack_size` specifies a stack size for the new thread. + `0` indicates that the default value should be used, i.e. behaves as if the flag was not set. + Other values will be rounded up to the nearest page boundary. + + .. versionadded:: 1.26.0 + +.. c:function:: uv_thread_t uv_thread_self(void) +.. c:function:: int uv_thread_join(uv_thread_t *tid) +.. c:function:: int uv_thread_equal(const uv_thread_t* t1, const uv_thread_t* t2) + +Thread-local storage +^^^^^^^^^^^^^^^^^^^^ + +.. note:: + The total thread-local storage size may be limited. That is, it may not be possible to + create many TLS keys. + +.. c:function:: int uv_key_create(uv_key_t* key) +.. c:function:: void uv_key_delete(uv_key_t* key) +.. c:function:: void* uv_key_get(uv_key_t* key) +.. c:function:: void uv_key_set(uv_key_t* key, void* value) + +Once-only initialization +^^^^^^^^^^^^^^^^^^^^^^^^ + +Runs a function once and only once. Concurrent calls to :c:func:`uv_once` with the +same guard will block all callers except one (it's unspecified which one). +The guard should be initialized statically with the UV_ONCE_INIT macro. + +.. c:function:: void uv_once(uv_once_t* guard, void (*callback)(void)) + +Mutex locks +^^^^^^^^^^^ + +Functions return 0 on success or an error code < 0 (unless the +return type is void, of course). + +.. c:function:: int uv_mutex_init(uv_mutex_t* handle) +.. c:function:: int uv_mutex_init_recursive(uv_mutex_t* handle) +.. c:function:: void uv_mutex_destroy(uv_mutex_t* handle) +.. c:function:: void uv_mutex_lock(uv_mutex_t* handle) +.. c:function:: int uv_mutex_trylock(uv_mutex_t* handle) +.. c:function:: void uv_mutex_unlock(uv_mutex_t* handle) + +Read-write locks +^^^^^^^^^^^^^^^^ + +Functions return 0 on success or an error code < 0 (unless the +return type is void, of course). + +.. c:function:: int uv_rwlock_init(uv_rwlock_t* rwlock) +.. c:function:: void uv_rwlock_destroy(uv_rwlock_t* rwlock) +.. c:function:: void uv_rwlock_rdlock(uv_rwlock_t* rwlock) +.. c:function:: int uv_rwlock_tryrdlock(uv_rwlock_t* rwlock) +.. c:function:: void uv_rwlock_rdunlock(uv_rwlock_t* rwlock) +.. c:function:: void uv_rwlock_wrlock(uv_rwlock_t* rwlock) +.. c:function:: int uv_rwlock_trywrlock(uv_rwlock_t* rwlock) +.. c:function:: void uv_rwlock_wrunlock(uv_rwlock_t* rwlock) + +Semaphores +^^^^^^^^^^ + +Functions return 0 on success or an error code < 0 (unless the +return type is void, of course). + +.. c:function:: int uv_sem_init(uv_sem_t* sem, unsigned int value) +.. c:function:: void uv_sem_destroy(uv_sem_t* sem) +.. c:function:: void uv_sem_post(uv_sem_t* sem) +.. c:function:: void uv_sem_wait(uv_sem_t* sem) +.. c:function:: int uv_sem_trywait(uv_sem_t* sem) + +Conditions +^^^^^^^^^^ + +Functions return 0 on success or an error code < 0 (unless the +return type is void, of course). + +.. note:: + 1. Callers should be prepared to deal with spurious wakeups on :c:func:`uv_cond_wait` + and :c:func:`uv_cond_timedwait`. + 2. The timeout parameter for :c:func:`uv_cond_timedwait` is relative to the time + at which function is called. + 3. On z/OS, the timeout parameter for :c:func:`uv_cond_timedwait` is converted to an + absolute system time at which the wait expires. If the current system clock time + passes the absolute time calculated before the condition is signaled, an ETIMEDOUT + error results. After the wait begins, the wait time is not affected by changes + to the system clock. + +.. c:function:: int uv_cond_init(uv_cond_t* cond) +.. c:function:: void uv_cond_destroy(uv_cond_t* cond) +.. c:function:: void uv_cond_signal(uv_cond_t* cond) +.. c:function:: void uv_cond_broadcast(uv_cond_t* cond) +.. c:function:: void uv_cond_wait(uv_cond_t* cond, uv_mutex_t* mutex) +.. c:function:: int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) + +Barriers +^^^^^^^^ + +Functions return 0 on success or an error code < 0 (unless the +return type is void, of course). + +.. note:: + :c:func:`uv_barrier_wait` returns a value > 0 to an arbitrarily chosen "serializer" thread + to facilitate cleanup, i.e. + + :: + + if (uv_barrier_wait(&barrier) > 0) + uv_barrier_destroy(&barrier); + +.. c:function:: int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) +.. c:function:: void uv_barrier_destroy(uv_barrier_t* barrier) +.. c:function:: int uv_barrier_wait(uv_barrier_t* barrier) diff --git a/include/libuv/docs/src/threadpool.rst b/include/libuv/docs/src/threadpool.rst new file mode 100644 index 000000000..cf6cdc1be --- /dev/null +++ b/include/libuv/docs/src/threadpool.rst @@ -0,0 +1,69 @@ + +.. _threadpool: + +Thread pool work scheduling +=========================== + +libuv provides a threadpool which can be used to run user code and get notified +in the loop thread. This thread pool is internally used to run all file system +operations, as well as getaddrinfo and getnameinfo requests. + +Its default size is 4, but it can be changed at startup time by setting the +``UV_THREADPOOL_SIZE`` environment variable to any value (the absolute maximum +is 1024). + +.. versionchanged:: 1.30.0 the maximum UV_THREADPOOL_SIZE allowed was increased from 128 to 1024. + +The threadpool is global and shared across all event loops. When a particular +function makes use of the threadpool (i.e. when using :c:func:`uv_queue_work`) +libuv preallocates and initializes the maximum number of threads allowed by +``UV_THREADPOOL_SIZE``. This causes a relatively minor memory overhead +(~1MB for 128 threads) but increases the performance of threading at runtime. + +.. note:: + Note that even though a global thread pool which is shared across all events + loops is used, the functions are not thread safe. + + +Data types +---------- + +.. c:type:: uv_work_t + + Work request type. + +.. c:type:: void (*uv_work_cb)(uv_work_t* req) + + Callback passed to :c:func:`uv_queue_work` which will be run on the thread + pool. + +.. c:type:: void (*uv_after_work_cb)(uv_work_t* req, int status) + + Callback passed to :c:func:`uv_queue_work` which will be called on the loop + thread after the work on the threadpool has been completed. If the work + was cancelled using :c:func:`uv_cancel` `status` will be ``UV_ECANCELED``. + + +Public members +^^^^^^^^^^^^^^ + +.. c:member:: uv_loop_t* uv_work_t.loop + + Loop that started this request and where completion will be reported. + Readonly. + +.. seealso:: The :c:type:`uv_req_t` members also apply. + + +API +--- + +.. c:function:: int uv_queue_work(uv_loop_t* loop, uv_work_t* req, uv_work_cb work_cb, uv_after_work_cb after_work_cb) + + Initializes a work request which will run the given `work_cb` in a thread + from the threadpool. Once `work_cb` is completed, `after_work_cb` will be + called on the loop thread. + + This request can be cancelled with :c:func:`uv_cancel`. + +.. seealso:: The :c:type:`uv_req_t` API functions also apply. diff --git a/include/libuv/docs/src/timer.rst b/include/libuv/docs/src/timer.rst new file mode 100644 index 000000000..070fa79da --- /dev/null +++ b/include/libuv/docs/src/timer.rst @@ -0,0 +1,88 @@ + +.. _timer: + +:c:type:`uv_timer_t` --- Timer handle +===================================== + +Timer handles are used to schedule callbacks to be called in the future. + + +Data types +---------- + +.. c:type:: uv_timer_t + + Timer handle type. + +.. c:type:: void (*uv_timer_cb)(uv_timer_t* handle) + + Type definition for callback passed to :c:func:`uv_timer_start`. + + +Public members +^^^^^^^^^^^^^^ + +N/A + +.. seealso:: The :c:type:`uv_handle_t` members also apply. + + +API +--- + +.. c:function:: int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle) + + Initialize the handle. + +.. c:function:: int uv_timer_start(uv_timer_t* handle, uv_timer_cb cb, uint64_t timeout, uint64_t repeat) + + Start the timer. `timeout` and `repeat` are in milliseconds. + + If `timeout` is zero, the callback fires on the next event loop iteration. + If `repeat` is non-zero, the callback fires first after `timeout` + milliseconds and then repeatedly after `repeat` milliseconds. + + .. note:: + Does not update the event loop's concept of "now". See :c:func:`uv_update_time` for more information. + + If the timer is already active, it is simply updated. + +.. c:function:: int uv_timer_stop(uv_timer_t* handle) + + Stop the timer, the callback will not be called anymore. + +.. c:function:: int uv_timer_again(uv_timer_t* handle) + + Stop the timer, and if it is repeating restart it using the repeat value + as the timeout. If the timer has never been started before it returns + UV_EINVAL. + +.. c:function:: void uv_timer_set_repeat(uv_timer_t* handle, uint64_t repeat) + + Set the repeat interval value in milliseconds. The timer will be scheduled + to run on the given interval, regardless of the callback execution + duration, and will follow normal timer semantics in the case of a + time-slice overrun. + + For example, if a 50ms repeating timer first runs for 17ms, it will be + scheduled to run again 33ms later. If other tasks consume more than the + 33ms following the first timer callback, then the callback will run as soon + as possible. + + .. note:: + If the repeat value is set from a timer callback it does not immediately take effect. + If the timer was non-repeating before, it will have been stopped. If it was repeating, + then the old repeat value will have been used to schedule the next timeout. + +.. c:function:: uint64_t uv_timer_get_repeat(const uv_timer_t* handle) + + Get the timer repeat value. + +.. c:function:: uint64_t uv_timer_get_due_in(const uv_timer_t* handle) + + Get the timer due value or 0 if it has expired. The time is relative to + :c:func:`uv_now()`. + + .. versionadded:: 1.40.0 + +.. seealso:: The :c:type:`uv_handle_t` API functions also apply. diff --git a/include/libuv/docs/src/tty.rst b/include/libuv/docs/src/tty.rst new file mode 100644 index 000000000..f1acfdc13 --- /dev/null +++ b/include/libuv/docs/src/tty.rst @@ -0,0 +1,140 @@ + +.. _tty: + +:c:type:`uv_tty_t` --- TTY handle +================================= + +TTY handles represent a stream for the console. + +:c:type:`uv_tty_t` is a 'subclass' of :c:type:`uv_stream_t`. + + +Data types +---------- + +.. c:type:: uv_tty_t + + TTY handle type. + +.. c:enum:: uv_tty_mode_t + + .. versionadded:: 1.2.0 + + TTY mode type: + + :: + + typedef enum { + /* Initial/normal terminal mode */ + UV_TTY_MODE_NORMAL, + /* Raw input mode (On Windows, ENABLE_WINDOW_INPUT is also enabled) */ + UV_TTY_MODE_RAW, + /* Binary-safe I/O mode for IPC (Unix-only) */ + UV_TTY_MODE_IO + } uv_tty_mode_t; + +.. c:enum:: uv_tty_vtermstate_t + + Console virtual terminal mode type: + + :: + + typedef enum { + /* + * The console supports handling of virtual terminal sequences + * (Windows10 new console, ConEmu) + */ + UV_TTY_SUPPORTED, + /* The console cannot process virtual terminal sequences. (Legacy + * console) + */ + UV_TTY_UNSUPPORTED + } uv_tty_vtermstate_t + + + +Public members +^^^^^^^^^^^^^^ + +N/A + +.. seealso:: The :c:type:`uv_stream_t` members also apply. + + +API +--- + +.. c:function:: int uv_tty_init(uv_loop_t* loop, uv_tty_t* handle, uv_file fd, int unused) + + Initialize a new TTY stream with the given file descriptor. Usually the + file descriptor will be: + + * 0 = stdin + * 1 = stdout + * 2 = stderr + + On Unix this function will determine the path of the fd of the terminal + using :man:`ttyname_r(3)`, open it, and use it if the passed file descriptor + refers to a TTY. This lets libuv put the tty in non-blocking mode without + affecting other processes that share the tty. + + This function is not thread safe on systems that don't support + ioctl TIOCGPTN or TIOCPTYGNAME, for instance OpenBSD and Solaris. + + .. note:: + If reopening the TTY fails, libuv falls back to blocking writes. + + .. versionchanged:: 1.23.1: the `readable` parameter is now unused and ignored. + The correct value will now be auto-detected from the kernel. + + .. versionchanged:: 1.9.0: the path of the TTY is determined by + :man:`ttyname_r(3)`. In earlier versions libuv opened + `/dev/tty` instead. + + .. versionchanged:: 1.5.0: trying to initialize a TTY stream with a file + descriptor that refers to a file returns `UV_EINVAL` + on UNIX. + +.. c:function:: int uv_tty_set_mode(uv_tty_t* handle, uv_tty_mode_t mode) + + .. versionchanged:: 1.2.0: the mode is specified as a + :c:type:`uv_tty_mode_t` value. + + Set the TTY using the specified terminal mode. + +.. c:function:: int uv_tty_reset_mode(void) + + To be called when the program exits. Resets TTY settings to default + values for the next process to take over. + + This function is async signal-safe on Unix platforms but can fail with error + code ``UV_EBUSY`` if you call it when execution is inside + :c:func:`uv_tty_set_mode`. + +.. c:function:: int uv_tty_get_winsize(uv_tty_t* handle, int* width, int* height) + + Gets the current Window size. On success it returns 0. + +.. seealso:: The :c:type:`uv_stream_t` API functions also apply. + +.. c:function:: void uv_tty_set_vterm_state(uv_tty_vtermstate_t state) + + Controls whether console virtual terminal sequences are processed by libuv + or console. + Useful in particular for enabling ConEmu support of ANSI X3.64 and Xterm + 256 colors. Otherwise Windows10 consoles are usually detected automatically. + + This function is only meaningful on Windows systems. On Unix it is silently + ignored. + + .. versionadded:: 1.33.0 + +.. c:function:: int uv_tty_get_vterm_state(uv_tty_vtermstate_t* state) + + Get the current state of whether console virtual terminal sequences are + handled by libuv or the console. + + This function is not implemented on Unix, where it returns ``UV_ENOTSUP``. + + .. versionadded:: 1.33.0 + diff --git a/include/libuv/docs/src/udp.rst b/include/libuv/docs/src/udp.rst new file mode 100644 index 000000000..009767d55 --- /dev/null +++ b/include/libuv/docs/src/udp.rst @@ -0,0 +1,450 @@ + +.. _udp: + +:c:type:`uv_udp_t` --- UDP handle +================================= + +UDP handles encapsulate UDP communication for both clients and servers. + + +Data types +---------- + +.. c:type:: uv_udp_t + + UDP handle type. + +.. c:type:: uv_udp_send_t + + UDP send request type. + +.. c:type:: uv_udp_flags + + Flags used in :c:func:`uv_udp_bind` and :c:type:`uv_udp_recv_cb`.. + + :: + + enum uv_udp_flags { + /* Disables dual stack mode. */ + UV_UDP_IPV6ONLY = 1, + /* + * Indicates message was truncated because read buffer was too small. The + * remainder was discarded by the OS. Used in uv_udp_recv_cb. + */ + UV_UDP_PARTIAL = 2, + /* + * Indicates if SO_REUSEADDR will be set when binding the handle in + * uv_udp_bind. + * This sets the SO_REUSEPORT socket flag on the BSDs and OS X. On other + * Unix platforms, it sets the SO_REUSEADDR flag. What that means is that + * multiple threads or processes can bind to the same address without error + * (provided they all set the flag) but only the last one to bind will receive + * any traffic, in effect "stealing" the port from the previous listener. + */ + UV_UDP_REUSEADDR = 4, + /* + * Indicates that the message was received by recvmmsg, so the buffer provided + * must not be freed by the recv_cb callback. + */ + UV_UDP_MMSG_CHUNK = 8, + /* + * Indicates that the buffer provided has been fully utilized by recvmmsg and + * that it should now be freed by the recv_cb callback. When this flag is set + * in uv_udp_recv_cb, nread will always be 0 and addr will always be NULL. + */ + UV_UDP_MMSG_FREE = 16, + /* + * Indicates if IP_RECVERR/IPV6_RECVERR will be set when binding the handle. + * This sets IP_RECVERR for IPv4 and IPV6_RECVERR for IPv6 UDP sockets on + * Linux. This stops the Linux kernel from supressing some ICMP error messages + * and enables full ICMP error reporting for faster failover. + * This flag is no-op on platforms other than Linux. + */ + UV_UDP_LINUX_RECVERR = 32, + /* + * Indicates that recvmmsg should be used, if available. + */ + UV_UDP_RECVMMSG = 256 + }; + +.. c:type:: void (*uv_udp_send_cb)(uv_udp_send_t* req, int status) + + Type definition for callback passed to :c:func:`uv_udp_send`, which is + called after the data was sent. + +.. c:type:: void (*uv_udp_recv_cb)(uv_udp_t* handle, ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned flags) + + Type definition for callback passed to :c:func:`uv_udp_recv_start`, which + is called when the endpoint receives data. + + * `handle`: UDP handle + * `nread`: Number of bytes that have been received. + 0 if there is no more data to read. Note that 0 may also mean that an + empty datagram was received (in this case `addr` is not NULL). < 0 if + a transmission error was detected; if using :man:`recvmmsg(2)` no more + chunks will be received and the buffer can be freed safely. + * `buf`: :c:type:`uv_buf_t` with the received data. + * `addr`: ``struct sockaddr*`` containing the address of the sender. + Can be NULL. Valid for the duration of the callback only. + * `flags`: One or more or'ed UV_UDP_* constants. + + The callee is responsible for freeing the buffer, libuv does not reuse it. + The buffer may be a null buffer (where `buf->base` == NULL and `buf->len` == 0) + on error. + + When using :man:`recvmmsg(2)`, chunks will have the `UV_UDP_MMSG_CHUNK` flag set, + those must not be freed. If no errors occur, there will be a final callback with + `nread` set to 0, `addr` set to NULL and the buffer pointing at the initially + allocated data with the `UV_UDP_MMSG_CHUNK` flag cleared and the `UV_UDP_MMSG_FREE` + flag set. If a UDP socket error occurs, `nread` will be < 0. In either scenario, + the callee can now safely free the provided buffer. + + .. versionchanged:: 1.40.0 added the `UV_UDP_MMSG_FREE` flag. + + .. note:: + The receive callback will be called with `nread` == 0 and `addr` == NULL when there is + nothing to read, and with `nread` == 0 and `addr` != NULL when an empty UDP packet is + received. + +.. c:enum:: uv_membership + + Membership type for a multicast address. + + :: + + typedef enum { + UV_LEAVE_GROUP = 0, + UV_JOIN_GROUP + } uv_membership; + + +Public members +^^^^^^^^^^^^^^ + +.. c:member:: size_t uv_udp_t.send_queue_size + + Number of bytes queued for sending. This field strictly shows how much + information is currently queued. + +.. c:member:: size_t uv_udp_t.send_queue_count + + Number of send requests currently in the queue awaiting to be processed. + +.. c:member:: uv_udp_t* uv_udp_send_t.handle + + UDP handle where this send request is taking place. + +.. seealso:: The :c:type:`uv_handle_t` members also apply. + + +API +--- + +.. c:function:: int uv_udp_init(uv_loop_t* loop, uv_udp_t* handle) + + Initialize a new UDP handle. The actual socket is created lazily. + Returns 0 on success. + +.. c:function:: int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) + + Initialize the handle with the specified flags. The lower 8 bits of the `flags` + parameter are used as the socket domain. A socket will be created for the given domain. + If the specified domain is ``AF_UNSPEC`` no socket is created, just like :c:func:`uv_udp_init`. + + The remaining bits can be used to set one of these flags: + + * `UV_UDP_RECVMMSG`: if set, and the platform supports it, :man:`recvmmsg(2)` will + be used. + + .. versionadded:: 1.7.0 + .. versionchanged:: 1.37.0 added the `UV_UDP_RECVMMSG` flag. + +.. c:function:: int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) + + Opens an existing file descriptor or Windows SOCKET as a UDP handle. + + Unix only: + The only requirement of the `sock` argument is that it follows the datagram + contract (works in unconnected mode, supports sendmsg()/recvmsg(), etc). + In other words, other datagram-type sockets like raw sockets or netlink + sockets can also be passed to this function. + + .. versionchanged:: 1.2.1 the file descriptor is set to non-blocking mode. + + .. note:: + The passed file descriptor or SOCKET is not checked for its type, but + it's required that it represents a valid datagram socket. + +.. c:function:: int uv_udp_bind(uv_udp_t* handle, const struct sockaddr* addr, unsigned int flags) + + Bind the UDP handle to an IP address and port. + + :param handle: UDP handle. Should have been initialized with + :c:func:`uv_udp_init`. + + :param addr: `struct sockaddr_in` or `struct sockaddr_in6` + with the address and port to bind to. + + :param flags: Indicate how the socket will be bound, + ``UV_UDP_IPV6ONLY``, ``UV_UDP_REUSEADDR``, and ``UV_UDP_RECVERR`` + are supported. + + :returns: 0 on success, or an error code < 0 on failure. + +.. c:function:: int uv_udp_connect(uv_udp_t* handle, const struct sockaddr* addr) + + Associate the UDP handle to a remote address and port, so every + message sent by this handle is automatically sent to that destination. + Calling this function with a `NULL` `addr` disconnects the handle. + Trying to call `uv_udp_connect()` on an already connected handle will result + in an `UV_EISCONN` error. Trying to disconnect a handle that is not + connected will return an `UV_ENOTCONN` error. + + :param handle: UDP handle. Should have been initialized with + :c:func:`uv_udp_init`. + + :param addr: `struct sockaddr_in` or `struct sockaddr_in6` + with the address and port to associate to. + + :returns: 0 on success, or an error code < 0 on failure. + + .. versionadded:: 1.27.0 + +.. c:function:: int uv_udp_getpeername(const uv_udp_t* handle, struct sockaddr* name, int* namelen) + + Get the remote IP and port of the UDP handle on connected UDP handles. + On unconnected handles, it returns `UV_ENOTCONN`. + + :param handle: UDP handle. Should have been initialized with + :c:func:`uv_udp_init` and bound. + + :param name: Pointer to the structure to be filled with the address data. + In order to support IPv4 and IPv6 `struct sockaddr_storage` should be + used. + + :param namelen: On input it indicates the data of the `name` field. On + output it indicates how much of it was filled. + + :returns: 0 on success, or an error code < 0 on failure + + .. versionadded:: 1.27.0 + +.. c:function:: int uv_udp_getsockname(const uv_udp_t* handle, struct sockaddr* name, int* namelen) + + Get the local IP and port of the UDP handle. + + :param handle: UDP handle. Should have been initialized with + :c:func:`uv_udp_init` and bound. + + :param name: Pointer to the structure to be filled with the address data. + In order to support IPv4 and IPv6 `struct sockaddr_storage` should be + used. + + :param namelen: On input it indicates the data of the `name` field. On + output it indicates how much of it was filled. + + :returns: 0 on success, or an error code < 0 on failure. + +.. c:function:: int uv_udp_set_membership(uv_udp_t* handle, const char* multicast_addr, const char* interface_addr, uv_membership membership) + + Set membership for a multicast address + + :param handle: UDP handle. Should have been initialized with + :c:func:`uv_udp_init`. + + :param multicast_addr: Multicast address to set membership for. + + :param interface_addr: Interface address. + + :param membership: Should be ``UV_JOIN_GROUP`` or ``UV_LEAVE_GROUP``. + + :returns: 0 on success, or an error code < 0 on failure. + +.. c:function:: int uv_udp_set_source_membership(uv_udp_t* handle, const char* multicast_addr, const char* interface_addr, const char* source_addr, uv_membership membership) + + Set membership for a source-specific multicast group. + + :param handle: UDP handle. Should have been initialized with + :c:func:`uv_udp_init`. + + :param multicast_addr: Multicast address to set membership for. + + :param interface_addr: Interface address. + + :param source_addr: Source address. + + :param membership: Should be ``UV_JOIN_GROUP`` or ``UV_LEAVE_GROUP``. + + :returns: 0 on success, or an error code < 0 on failure. + + .. versionadded:: 1.32.0 + +.. c:function:: int uv_udp_set_multicast_loop(uv_udp_t* handle, int on) + + Set IP multicast loop flag. Makes multicast packets loop back to + local sockets. + + :param handle: UDP handle. Should have been initialized with + :c:func:`uv_udp_init`. + + :param on: 1 for on, 0 for off. + + :returns: 0 on success, or an error code < 0 on failure. + +.. c:function:: int uv_udp_set_multicast_ttl(uv_udp_t* handle, int ttl) + + Set the multicast ttl. + + :param handle: UDP handle. Should have been initialized with + :c:func:`uv_udp_init`. + + :param ttl: 1 through 255. + + :returns: 0 on success, or an error code < 0 on failure. + +.. c:function:: int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr) + + Set the multicast interface to send or receive data on. + + :param handle: UDP handle. Should have been initialized with + :c:func:`uv_udp_init`. + + :param interface_addr: interface address. + + :returns: 0 on success, or an error code < 0 on failure. + +.. c:function:: int uv_udp_set_broadcast(uv_udp_t* handle, int on) + + Set broadcast on or off. + + :param handle: UDP handle. Should have been initialized with + :c:func:`uv_udp_init`. + + :param on: 1 for on, 0 for off. + + :returns: 0 on success, or an error code < 0 on failure. + +.. c:function:: int uv_udp_set_ttl(uv_udp_t* handle, int ttl) + + Set the time to live. + + :param handle: UDP handle. Should have been initialized with + :c:func:`uv_udp_init`. + + :param ttl: 1 through 255. + + :returns: 0 on success, or an error code < 0 on failure. + +.. c:function:: int uv_udp_send(uv_udp_send_t* req, uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr, uv_udp_send_cb send_cb) + + Send data over the UDP socket. If the socket has not previously been bound + with :c:func:`uv_udp_bind` it will be bound to 0.0.0.0 + (the "all interfaces" IPv4 address) and a random port number. + + On Windows if the `addr` is initialized to point to an unspecified address + (``0.0.0.0`` or ``::``) it will be changed to point to ``localhost``. + This is done to match the behavior of Linux systems. + + For connected UDP handles, `addr` must be set to `NULL`, otherwise it will + return `UV_EISCONN` error. + + For connectionless UDP handles, `addr` cannot be `NULL`, otherwise it will + return `UV_EDESTADDRREQ` error. + + :param req: UDP request handle. Need not be initialized. + + :param handle: UDP handle. Should have been initialized with + :c:func:`uv_udp_init`. + + :param bufs: List of buffers to send. + + :param nbufs: Number of buffers in `bufs`. + + :param addr: `struct sockaddr_in` or `struct sockaddr_in6` with the + address and port of the remote peer. + + :param send_cb: Callback to invoke when the data has been sent out. + + :returns: 0 on success, or an error code < 0 on failure. + + .. versionchanged:: 1.19.0 added ``0.0.0.0`` and ``::`` to ``localhost`` + mapping + + .. versionchanged:: 1.27.0 added support for connected sockets + +.. c:function:: int uv_udp_try_send(uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr) + + Same as :c:func:`uv_udp_send`, but won't queue a send request if it can't + be completed immediately. + + For connected UDP handles, `addr` must be set to `NULL`, otherwise it will + return `UV_EISCONN` error. + + For connectionless UDP handles, `addr` cannot be `NULL`, otherwise it will + return `UV_EDESTADDRREQ` error. + + :returns: >= 0: number of bytes sent (it matches the given buffer size). + < 0: negative error code (``UV_EAGAIN`` is returned when the message + can't be sent immediately). + + .. versionchanged:: 1.27.0 added support for connected sockets + +.. c:function:: int uv_udp_recv_start(uv_udp_t* handle, uv_alloc_cb alloc_cb, uv_udp_recv_cb recv_cb) + + Prepare for receiving data. If the socket has not previously been bound + with :c:func:`uv_udp_bind` it is bound to 0.0.0.0 (the "all interfaces" + IPv4 address) and a random port number. + + :param handle: UDP handle. Should have been initialized with + :c:func:`uv_udp_init`. + + :param alloc_cb: Callback to invoke when temporary storage is needed. + + :param recv_cb: Callback to invoke with received data. + + :returns: 0 on success, or an error code < 0 on failure. + + .. note:: + When using :man:`recvmmsg(2)`, the number of messages received at a time is limited + by the number of max size dgrams that will fit into the buffer allocated in `alloc_cb`, and + `suggested_size` in `alloc_cb` for udp_recv is always set to the size of 1 max size dgram. + + .. versionchanged:: 1.35.0 added support for :man:`recvmmsg(2)` on supported platforms). + The use of this feature requires a buffer larger than + 2 * 64KB to be passed to `alloc_cb`. + .. versionchanged:: 1.37.0 :man:`recvmmsg(2)` support is no longer enabled implicitly, + it must be explicitly requested by passing the `UV_UDP_RECVMMSG` flag to + :c:func:`uv_udp_init_ex`. + .. versionchanged:: 1.39.0 :c:func:`uv_udp_using_recvmmsg` can be used in `alloc_cb` to + determine if a buffer sized for use with :man:`recvmmsg(2)` should be + allocated for the current handle/platform. + +.. c:function:: int uv_udp_using_recvmmsg(uv_udp_t* handle) + + Returns 1 if the UDP handle was created with the `UV_UDP_RECVMMSG` flag + and the platform supports :man:`recvmmsg(2)`, 0 otherwise. + + .. versionadded:: 1.39.0 + +.. c:function:: int uv_udp_recv_stop(uv_udp_t* handle) + + Stop listening for incoming datagrams. + + :param handle: UDP handle. Should have been initialized with + :c:func:`uv_udp_init`. + + :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/libuv/docs/src/upgrading.rst b/include/libuv/docs/src/upgrading.rst new file mode 100644 index 000000000..32840c269 --- /dev/null +++ b/include/libuv/docs/src/upgrading.rst @@ -0,0 +1,11 @@ +.. _upgrading: + +Upgrading +========= + +Migration guides for different libuv versions, starting with 1.0. + +.. toctree:: + :maxdepth: 1 + + migration_010_100 diff --git a/include/libuv/docs/src/version.rst b/include/libuv/docs/src/version.rst new file mode 100644 index 000000000..13b094008 --- /dev/null +++ b/include/libuv/docs/src/version.rst @@ -0,0 +1,60 @@ + +.. _version: + +Version-checking macros and functions +===================================== + +Starting with version 1.0.0 libuv follows the `semantic versioning`_ +scheme. This means that new APIs can be introduced throughout the lifetime of +a major release. In this section you'll find all macros and functions that +will allow you to write or compile code conditionally, in order to work with +multiple libuv versions. + +.. _semantic versioning: https://semver.org + + +Macros +------ + +.. c:macro:: UV_VERSION_MAJOR + + libuv version's major number. + +.. c:macro:: UV_VERSION_MINOR + + libuv version's minor number. + +.. c:macro:: UV_VERSION_PATCH + + libuv version's patch number. + +.. c:macro:: UV_VERSION_IS_RELEASE + + Set to 1 to indicate a release version of libuv, 0 for a development + snapshot. + +.. c:macro:: UV_VERSION_SUFFIX + + libuv version suffix. Certain development releases such as Release Candidates + might have a suffix such as "rc". + +.. c:macro:: UV_VERSION_HEX + + Returns the libuv version packed into a single integer. 8 bits are used for + each component, with the patch number stored in the 8 least significant + bits. E.g. for libuv 1.2.3 this would be 0x010203. + + .. versionadded:: 1.7.0 + + +Functions +--------- + +.. c:function:: unsigned int uv_version(void) + + Returns :c:macro:`UV_VERSION_HEX`. + +.. c:function:: const char* uv_version_string(void) + + Returns the libuv version number as a string. For non-release versions the + version suffix is included. From db02274dc51e018a165d061944a5c4eb7a2ee2e2 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Tue, 17 Aug 2021 22:09:24 +0300 Subject: [PATCH 057/117] automatic libuv bindings generator --- libs/uv/uv_generated.c | 662 +++++++++++++++++++++++++++++++ other/uvgenerator/UVGenerator.hx | 193 +++++++++ other/uvgenerator/run.hxml | 2 + 3 files changed, 857 insertions(+) create mode 100644 libs/uv/uv_generated.c create mode 100644 other/uvgenerator/UVGenerator.hx create mode 100644 other/uvgenerator/run.hxml diff --git a/libs/uv/uv_generated.c b/libs/uv/uv_generated.c new file mode 100644 index 000000000..da3205814 --- /dev/null +++ b/libs/uv/uv_generated.c @@ -0,0 +1,662 @@ +HL_PRIM int HL_NAME(async_init_with_cb)( uv_loop_t* loop, uv_async_t* async ) { + return uv_async_init(loop, async, on_uv_async_cb); +} +DEFINE_PRIM(_I32, async_init_with_cb, _LOOP _ASYNC); + +DEFINE_PRIM(_I32, async_send, _ASYNC); + +DEFINE_PRIM(_I32, check_init, _LOOP _CHECK); + +HL_PRIM int HL_NAME(check_start_with_cb)( uv_check_t* check ) { + return uv_check_start(check, on_uv_check_cb); +} +DEFINE_PRIM(_I32, check_start_with_cb, _CHECK); + +DEFINE_PRIM(_I32, check_stop, _CHECK); + +HL_PRIM int HL_NAME(getaddrinfo_with_cb)( uv_loop_t* loop, uv_getaddrinfo_t* req, const char* node, const char* service, const struct addrinfo* hints ) { + return uv_getaddrinfo(loop, req, on_uv_getaddrinfo_cb, node, service, hints); +} +DEFINE_PRIM(_I32, getaddrinfo_with_cb, _LOOP _GETADDRINFO _BYTES _BYTES _REF(_ADDRINFO)); + +DEFINE_PRIM(_VOID, freeaddrinfo, _REF(_ADDRINFO)); + +HL_PRIM int HL_NAME(getnameinfo_with_cb)( uv_loop_t* loop, uv_getnameinfo_t* req, const struct sockaddr* addr, int flags ) { + return uv_getnameinfo(loop, req, on_uv_getnameinfo_cb, addr, flags); +} +DEFINE_PRIM(_I32, getnameinfo_with_cb, _LOOP _GETNAMEINFO _REF(_SOCKADDR) _I32); + +DEFINE_PRIM(_BYTES, strerror, _I32); + +DEFINE_PRIM(_BYTES, strerror_r, _I32 _BYTES _U64); + +DEFINE_PRIM(_BYTES, err_name, _I32); + +DEFINE_PRIM(_BYTES, err_name_r, _I32 _BYTES _U64); + +DEFINE_PRIM(_I32, translate_sys_error, _I32); + +DEFINE_PRIM(_VOID, fs_req_cleanup, _FS); + +HL_PRIM int HL_NAME(fs_close_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { + return uv_fs_close(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_close_with_cb, _LOOP _FS _FILE _BOOL); + +HL_PRIM int HL_NAME(fs_open_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, bool use_uv_fs_cb ) { + return uv_fs_open(loop, req, path, flags, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_open_with_cb, _LOOP _FS _BYTES _I32 _I32 _BOOL); + +HL_PRIM int HL_NAME(fs_read_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, bool use_uv_fs_cb ) { + return uv_fs_read(loop, req, file, bufs[], nbufs, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_read_with_cb, _LOOP _FS _FILE _BUF _U32 _I64 _BOOL); + +HL_PRIM int HL_NAME(fs_unlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { + return uv_fs_unlink(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_unlink_with_cb, _LOOP _FS _BYTES _BOOL); + +HL_PRIM int HL_NAME(fs_write_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, bool use_uv_fs_cb ) { + return uv_fs_write(loop, req, file, bufs[], nbufs, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_write_with_cb, _LOOP _FS _FILE _BUF _U32 _I64 _BOOL); + +HL_PRIM int HL_NAME(fs_mkdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { + return uv_fs_mkdir(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_mkdir_with_cb, _LOOP _FS _BYTES _I32 _BOOL); + +HL_PRIM int HL_NAME(fs_mkdtemp_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* tpl, bool use_uv_fs_cb ) { + return uv_fs_mkdtemp(loop, req, tpl, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_mkdtemp_with_cb, _LOOP _FS _BYTES _BOOL); + +HL_PRIM int HL_NAME(fs_mkstemp_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* tpl, bool use_uv_fs_cb ) { + return uv_fs_mkstemp(loop, req, tpl, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_mkstemp_with_cb, _LOOP _FS _BYTES _BOOL); + +HL_PRIM int HL_NAME(fs_rmdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { + return uv_fs_rmdir(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_rmdir_with_cb, _LOOP _FS _BYTES _BOOL); + +HL_PRIM int HL_NAME(fs_opendir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { + return uv_fs_opendir(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_opendir_with_cb, _LOOP _FS _BYTES _BOOL); + +HL_PRIM int HL_NAME(fs_closedir_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, bool use_uv_fs_cb ) { + return uv_fs_closedir(loop, req, dir, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_closedir_with_cb, _LOOP _FS _DIR _BOOL); + +HL_PRIM int HL_NAME(fs_readdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, bool use_uv_fs_cb ) { + return uv_fs_readdir(loop, req, dir, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_readdir_with_cb, _LOOP _FS _DIR _BOOL); + +HL_PRIM int HL_NAME(fs_scandir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, bool use_uv_fs_cb ) { + return uv_fs_scandir(loop, req, path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_scandir_with_cb, _LOOP _FS _BYTES _I32 _BOOL); + +DEFINE_PRIM(_I32, fs_scandir_next, _FS _DIRENT); + +HL_PRIM int HL_NAME(fs_stat_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { + return uv_fs_stat(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_stat_with_cb, _LOOP _FS _BYTES _BOOL); + +HL_PRIM int HL_NAME(fs_fstat_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { + return uv_fs_fstat(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_fstat_with_cb, _LOOP _FS _FILE _BOOL); + +HL_PRIM int HL_NAME(fs_lstat_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { + return uv_fs_lstat(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_lstat_with_cb, _LOOP _FS _BYTES _BOOL); + +HL_PRIM int HL_NAME(fs_statfs_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { + return uv_fs_statfs(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_statfs_with_cb, _LOOP _FS _BYTES _BOOL); + +HL_PRIM int HL_NAME(fs_rename_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, bool use_uv_fs_cb ) { + return uv_fs_rename(loop, req, path, new_path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_rename_with_cb, _LOOP _FS _BYTES _BYTES _BOOL); + +HL_PRIM int HL_NAME(fs_fsync_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { + return uv_fs_fsync(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_fsync_with_cb, _LOOP _FS _FILE _BOOL); + +HL_PRIM int HL_NAME(fs_fdatasync_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { + return uv_fs_fdatasync(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_fdatasync_with_cb, _LOOP _FS _FILE _BOOL); + +HL_PRIM int HL_NAME(fs_ftruncate_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, int64_t offset, bool use_uv_fs_cb ) { + return uv_fs_ftruncate(loop, req, file, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_ftruncate_with_cb, _LOOP _FS _FILE _I64 _BOOL); + +HL_PRIM int HL_NAME(fs_copyfile_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, bool use_uv_fs_cb ) { + return uv_fs_copyfile(loop, req, path, new_path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_copyfile_with_cb, _LOOP _FS _BYTES _BYTES _I32 _BOOL); + +HL_PRIM int HL_NAME(fs_sendfile_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file out_fd, uv_file in_fd, int64_t in_offset, size_t length, bool use_uv_fs_cb ) { + return uv_fs_sendfile(loop, req, out_fd, in_fd, in_offset, length, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_sendfile_with_cb, _LOOP _FS _FILE _FILE _I64 _U64 _BOOL); + +HL_PRIM int HL_NAME(fs_access_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { + return uv_fs_access(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_access_with_cb, _LOOP _FS _BYTES _I32 _BOOL); + +HL_PRIM int HL_NAME(fs_chmod_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { + return uv_fs_chmod(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_chmod_with_cb, _LOOP _FS _BYTES _I32 _BOOL); + +HL_PRIM int HL_NAME(fs_fchmod_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, int mode, bool use_uv_fs_cb ) { + return uv_fs_fchmod(loop, req, file, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_fchmod_with_cb, _LOOP _FS _FILE _I32 _BOOL); + +HL_PRIM int HL_NAME(fs_utime_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, bool use_uv_fs_cb ) { + return uv_fs_utime(loop, req, path, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_utime_with_cb, _LOOP _FS _BYTES _F64 _F64 _BOOL); + +HL_PRIM int HL_NAME(fs_futime_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime, double mtime, bool use_uv_fs_cb ) { + return uv_fs_futime(loop, req, file, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_futime_with_cb, _LOOP _FS _FILE _F64 _F64 _BOOL); + +HL_PRIM int HL_NAME(fs_lutime_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, bool use_uv_fs_cb ) { + return uv_fs_lutime(loop, req, path, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_lutime_with_cb, _LOOP _FS _BYTES _F64 _F64 _BOOL); + +HL_PRIM int HL_NAME(fs_link_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, bool use_uv_fs_cb ) { + return uv_fs_link(loop, req, path, new_path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_link_with_cb, _LOOP _FS _BYTES _BYTES _BOOL); + +HL_PRIM int HL_NAME(fs_symlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, bool use_uv_fs_cb ) { + return uv_fs_symlink(loop, req, path, new_path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_symlink_with_cb, _LOOP _FS _BYTES _BYTES _I32 _BOOL); + +HL_PRIM int HL_NAME(fs_readlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { + return uv_fs_readlink(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_readlink_with_cb, _LOOP _FS _BYTES _BOOL); + +HL_PRIM int HL_NAME(fs_realpath_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { + return uv_fs_realpath(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_realpath_with_cb, _LOOP _FS _BYTES _BOOL); + +HL_PRIM int HL_NAME(fs_chown_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { + return uv_fs_chown(loop, req, path, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_chown_with_cb, _LOOP _FS _BYTES _UID_T _GID_T _BOOL); + +HL_PRIM int HL_NAME(fs_fchown_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { + return uv_fs_fchown(loop, req, file, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_fchown_with_cb, _LOOP _FS _FILE _UID_T _GID_T _BOOL); + +HL_PRIM int HL_NAME(fs_lchown_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { + return uv_fs_lchown(loop, req, path, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_lchown_with_cb, _LOOP _FS _BYTES _UID_T _GID_T _BOOL); + +DEFINE_PRIM(_FS_TYPE, fs_get_type, _FS); + +DEFINE_PRIM(_I64, fs_get_result, _FS); + +DEFINE_PRIM(_I32, fs_get_system_error, _FS); + +DEFINE_PRIM(_DYN, fs_get_ptr, _FS); + +DEFINE_PRIM(_BYTES, fs_get_path, _FS); + +DEFINE_PRIM(_STAT, fs_get_statbuf, _FS); + +DEFINE_PRIM(_OS_FD_T, get_osfhandle, _I32); + +DEFINE_PRIM(_I32, open_osfhandle, _OS_FD_T); + +DEFINE_PRIM(_I32, fs_event_init, _LOOP _FS_EVENT); + +HL_PRIM int HL_NAME(fs_event_start_with_cb)( uv_fs_event_t* handle, const char* path, unsigned int flags ) { + return uv_fs_event_start(handle, on_uv_fs_event_cb, path, flags); +} +DEFINE_PRIM(_I32, fs_event_start_with_cb, _FS_EVENT _BYTES _U32); + +DEFINE_PRIM(_I32, fs_event_stop, _FS_EVENT); + +DEFINE_PRIM(_I32, fs_event_getpath, _FS_EVENT _BYTES _REF(_I64)); + +DEFINE_PRIM(_I32, fs_poll_init, _LOOP _FS_POLL); + +HL_PRIM int HL_NAME(fs_poll_start_with_cb)( uv_fs_poll_t* handle, const char* path, unsigned int interval ) { + return uv_fs_poll_start(handle, on_uv_fs_poll_cb, path, interval); +} +DEFINE_PRIM(_I32, fs_poll_start_with_cb, _FS_POLL _BYTES _U32); + +DEFINE_PRIM(_I32, fs_poll_stop, _FS_POLL); + +DEFINE_PRIM(_I32, fs_poll_getpath, _FS_POLL _BYTES _REF(_I64)); + +DEFINE_PRIM(_I32, is_active, _HANDLE); + +DEFINE_PRIM(_I32, is_closing, _HANDLE); + +HL_PRIM void HL_NAME(close_with_cb)( uv_handle_t* handle ) { + uv_close(handle, on_uv_close_cb); +} +DEFINE_PRIM(_VOID, close_with_cb, _HANDLE); + +DEFINE_PRIM(_VOID, ref, _HANDLE); + +DEFINE_PRIM(_VOID, unref, _HANDLE); + +DEFINE_PRIM(_I32, has_ref, _HANDLE); + +DEFINE_PRIM(_U64, handle_size, _HANDLE_TYPE); + +DEFINE_PRIM(_I32, send_buffer_size, _HANDLE _REF(_I32)); + +DEFINE_PRIM(_I32, recv_buffer_size, _HANDLE _REF(_I32)); + +DEFINE_PRIM(_I32, fileno, _HANDLE _OS_FD); + +DEFINE_PRIM(_LOOP, handle_get_loop, _HANDLE); + +DEFINE_PRIM(_DYN, handle_get_data, _HANDLE); + +DEFINE_PRIM(_DYN, handle_set_data, _HANDLE _DYN); + +DEFINE_PRIM(_HANDLE_TYPE, handle_get_type, _HANDLE); + +DEFINE_PRIM(_BYTES, handle_type_name, _HANDLE_TYPE); + +DEFINE_PRIM(_I32, idle_init, _LOOP _IDLE); + +HL_PRIM int HL_NAME(idle_start_with_cb)( uv_idle_t* idle ) { + return uv_idle_start(idle, on_uv_idle_cb); +} +DEFINE_PRIM(_I32, idle_start_with_cb, _IDLE); + +DEFINE_PRIM(_I32, idle_stop, _IDLE); + +DEFINE_PRIM(_I32, loop_init, _LOOP); + +DEFINE_PRIM(_I32, loop_close, _LOOP); + +DEFINE_PRIM(_LOOP, default_loop, _VOID); + +DEFINE_PRIM(_I32, run, _LOOP _RUN_MODE); + +DEFINE_PRIM(_I32, loop_alive, _LOOP); + +DEFINE_PRIM(_VOID, stop, _LOOP); + +DEFINE_PRIM(_U64, loop_size, _VOID); + +DEFINE_PRIM(_I32, backend_fd, _LOOP); + +DEFINE_PRIM(_I32, backend_timeout, _LOOP); + +DEFINE_PRIM(_I64, now, _LOOP); + +DEFINE_PRIM(_VOID, update_time, _LOOP); + +HL_PRIM void HL_NAME(walk_with_cb)( uv_loop_t* loop, void* arg ) { + uv_walk(loop, on_uv_walk_cb, arg); +} +DEFINE_PRIM(_VOID, walk_with_cb, _LOOP _DYN); + +DEFINE_PRIM(_I32, loop_fork, _LOOP); + +DEFINE_PRIM(_DYN, loop_get_data, _LOOP); + +DEFINE_PRIM(_DYN, loop_set_data, _LOOP _DYN); + +DEFINE_PRIM(_I64, metrics_idle_time, _LOOP); + +DEFINE_PRIM(_HANDLE_TYPE, guess_handle, _FILE); + +DEFINE_PRIM(_I32, replace_allocator, _MALLOC_FUNC _REALLOC_FUNC _CALLOC_FUNC _FREE_FUNC); + +DEFINE_PRIM(_VOID, library_shutdown, _VOID); + +DEFINE_PRIM(_BUF, buf_init, _BYTES _U32); + +DEFINE_PRIM(_REF(_BYTES), setup_args, _I32 _REF(_BYTES)); + +DEFINE_PRIM(_I32, get_process_title, _BYTES _U64); + +DEFINE_PRIM(_I32, set_process_title, _BYTES); + +DEFINE_PRIM(_I32, resident_set_memory, _REF(_I64)); + +DEFINE_PRIM(_I32, uptime, _REF(_F64)); + +DEFINE_PRIM(_I32, getrusage, _RUSAGE); + +DEFINE_PRIM(_I32, os_getpid, _VOID); + +DEFINE_PRIM(_I32, os_getppid, _VOID); + +DEFINE_PRIM(_I32, cpu_info, _REF(_CPU_INFO) _REF(_I32)); + +DEFINE_PRIM(_VOID, free_cpu_info, _CPU_INFO _I32); + +DEFINE_PRIM(_I32, interface_addresses, _REF(_INTERFACE_ADDRESS) _REF(_I32)); + +DEFINE_PRIM(_VOID, free_interface_addresses, _INTERFACE_ADDRESS _I32); + +DEFINE_PRIM(_VOID, loadavg, _F64); + +DEFINE_PRIM(_I32, ip4_addr, _BYTES _I32 _REF(_SOCKADDR_IN)); + +DEFINE_PRIM(_I32, ip6_addr, _BYTES _I32 _REF(_SOCKADDR_IN6)); + +DEFINE_PRIM(_I32, ip4_name, _REF(_SOCKADDR_IN) _BYTES _U64); + +DEFINE_PRIM(_I32, ip6_name, _REF(_SOCKADDR_IN6) _BYTES _U64); + +DEFINE_PRIM(_I32, inet_ntop, _I32 _DYN _BYTES _U64); + +DEFINE_PRIM(_I32, inet_pton, _I32 _BYTES _DYN); + +DEFINE_PRIM(_I32, if_indextoname, _U32 _BYTES _REF(_I64)); + +DEFINE_PRIM(_I32, if_indextoiid, _U32 _BYTES _REF(_I64)); + +DEFINE_PRIM(_I32, exepath, _BYTES _REF(_I64)); + +DEFINE_PRIM(_I32, cwd, _BYTES _REF(_I64)); + +DEFINE_PRIM(_I32, chdir, _BYTES); + +DEFINE_PRIM(_I32, os_homedir, _BYTES _REF(_I64)); + +DEFINE_PRIM(_I32, os_tmpdir, _BYTES _REF(_I64)); + +DEFINE_PRIM(_I32, os_get_passwd, _PASSWD); + +DEFINE_PRIM(_VOID, os_free_passwd, _PASSWD); + +DEFINE_PRIM(_I64, get_free_memory, _VOID); + +DEFINE_PRIM(_I64, get_total_memory, _VOID); + +DEFINE_PRIM(_I64, get_constrained_memory, _VOID); + +DEFINE_PRIM(_I64, hrtime, _VOID); + +DEFINE_PRIM(_VOID, print_all_handles, _LOOP _FILE); + +DEFINE_PRIM(_VOID, print_active_handles, _LOOP _FILE); + +DEFINE_PRIM(_I32, os_environ, _REF(_ENV_ITEM) _REF(_I32)); + +DEFINE_PRIM(_VOID, os_free_environ, _ENV_ITEM _I32); + +DEFINE_PRIM(_I32, os_getenv, _BYTES _BYTES _REF(_I64)); + +DEFINE_PRIM(_I32, os_setenv, _BYTES _BYTES); + +DEFINE_PRIM(_I32, os_unsetenv, _BYTES); + +DEFINE_PRIM(_I32, os_gethostname, _BYTES _REF(_I64)); + +DEFINE_PRIM(_I32, os_getpriority, _I32 _REF(_I32)); + +DEFINE_PRIM(_I32, os_setpriority, _I32 _I32); + +DEFINE_PRIM(_I32, os_uname, _UTSNAME); + +DEFINE_PRIM(_I32, gettimeofday, _TIMEVAL64); + +HL_PRIM int HL_NAME(random_with_cb)( uv_loop_t* loop, uv_random_t* req, void* buf, size_t buflen, unsigned int flags ) { + return uv_random(loop, req, buf, buflen, flags, on_uv_random_cb); +} +DEFINE_PRIM(_I32, random_with_cb, _LOOP _RANDOM _DYN _U64 _U32); + +DEFINE_PRIM(_VOID, sleep, _U32); + +DEFINE_PRIM(_I32, pipe_init, _LOOP _PIPE _I32); + +DEFINE_PRIM(_I32, pipe_open, _PIPE _FILE); + +DEFINE_PRIM(_I32, pipe_bind, _PIPE _BYTES); + +HL_PRIM void HL_NAME(pipe_connect_with_cb)( uv_connect_t* req, uv_pipe_t* handle, const char* name ) { + uv_pipe_connect(req, handle, name, on_uv_connect_cb); +} +DEFINE_PRIM(_VOID, pipe_connect_with_cb, _CONNECT _PIPE _BYTES); + +DEFINE_PRIM(_I32, pipe_getsockname, _PIPE _BYTES _REF(_I64)); + +DEFINE_PRIM(_I32, pipe_getpeername, _PIPE _BYTES _REF(_I64)); + +DEFINE_PRIM(_VOID, pipe_pending_instances, _PIPE _I32); + +DEFINE_PRIM(_I32, pipe_pending_count, _PIPE); + +DEFINE_PRIM(_HANDLE_TYPE, pipe_pending_type, _PIPE); + +DEFINE_PRIM(_I32, pipe_chmod, _PIPE _I32); + +DEFINE_PRIM(_I32, pipe, _FILE _I32 _I32); + +DEFINE_PRIM(_I32, prepare_init, _LOOP _PREPARE); + +HL_PRIM int HL_NAME(prepare_start_with_cb)( uv_prepare_t* prepare ) { + return uv_prepare_start(prepare, on_uv_prepare_cb); +} +DEFINE_PRIM(_I32, prepare_start_with_cb, _PREPARE); + +DEFINE_PRIM(_I32, prepare_stop, _PREPARE); + +DEFINE_PRIM(_VOID, disable_stdio_inheritance, _VOID); + +DEFINE_PRIM(_I32, spawn, _LOOP _PROCESS _PROCESS_OPTIONS); + +DEFINE_PRIM(_I32, process_kill, _PROCESS _I32); + +DEFINE_PRIM(_I32, kill, _I32 _I32); + +DEFINE_PRIM(_I32, process_get_pid, _PROCESS); + +DEFINE_PRIM(_I32, cancel, _REQ); + +DEFINE_PRIM(_U64, req_size, _REQ_TYPE); + +DEFINE_PRIM(_DYN, req_get_data, _REQ); + +DEFINE_PRIM(_DYN, req_set_data, _REQ _DYN); + +DEFINE_PRIM(_REQ_TYPE, req_get_type, _REQ); + +DEFINE_PRIM(_BYTES, req_type_name, _REQ_TYPE); + +DEFINE_PRIM(_I32, signal_init, _LOOP _SIGNAL); + +HL_PRIM int HL_NAME(signal_start_with_cb)( uv_signal_t* signal, int signum ) { + return uv_signal_start(signal, on_uv_signal_cb, signum); +} +DEFINE_PRIM(_I32, signal_start_with_cb, _SIGNAL _I32); + +HL_PRIM int HL_NAME(signal_start_oneshot_with_cb)( uv_signal_t* signal, int signum ) { + return uv_signal_start_oneshot(signal, on_uv_signal_cb, signum); +} +DEFINE_PRIM(_I32, signal_start_oneshot_with_cb, _SIGNAL _I32); + +DEFINE_PRIM(_I32, signal_stop, _SIGNAL); + +HL_PRIM int HL_NAME(shutdown_with_cb)( uv_shutdown_t* req, uv_stream_t* handle ) { + return uv_shutdown(req, handle, on_uv_shutdown_cb); +} +DEFINE_PRIM(_I32, shutdown_with_cb, _SHUTDOWN _STREAM); + +HL_PRIM int HL_NAME(listen_with_cb)( uv_stream_t* stream, int backlog ) { + return uv_listen(stream, backlog, on_uv_connection_cb); +} +DEFINE_PRIM(_I32, listen_with_cb, _STREAM _I32); + +DEFINE_PRIM(_I32, accept, _STREAM _STREAM); + +HL_PRIM int HL_NAME(read_start_with_cb)( uv_stream_t* stream ) { + return uv_read_start(stream, on_uv_alloc_cb, on_uv_read_cb); +} +DEFINE_PRIM(_I32, read_start_with_cb, _STREAM); + +DEFINE_PRIM(_I32, read_stop, _STREAM); + +HL_PRIM int HL_NAME(write_with_cb)( uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs ) { + return uv_write(req, handle, bufs[], nbufs, on_uv_write_cb); +} +DEFINE_PRIM(_I32, write_with_cb, _WRITE _STREAM _BUF _U32); + +HL_PRIM int HL_NAME(write2_with_cb)( uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_stream_t* send_handle ) { + return uv_write2(req, handle, bufs[], nbufs, send_handle, on_uv_write_cb); +} +DEFINE_PRIM(_I32, write2_with_cb, _WRITE _STREAM _BUF _U32 _STREAM); + +DEFINE_PRIM(_I32, try_write, _STREAM _BUF _U32); + +DEFINE_PRIM(_I32, try_write2, _STREAM _BUF _U32 _STREAM); + +DEFINE_PRIM(_I32, is_readable, _STREAM); + +DEFINE_PRIM(_I32, is_writable, _STREAM); + +DEFINE_PRIM(_I32, stream_set_blocking, _STREAM _I32); + +DEFINE_PRIM(_U64, stream_get_write_queue_size, _STREAM); + +DEFINE_PRIM(_I32, tcp_init, _LOOP _TCP); + +DEFINE_PRIM(_I32, tcp_init_ex, _LOOP _TCP _U32); + +DEFINE_PRIM(_I32, tcp_open, _TCP _OS_SOCK_T); + +DEFINE_PRIM(_I32, tcp_nodelay, _TCP _I32); + +DEFINE_PRIM(_I32, tcp_keepalive, _TCP _I32 _U32); + +DEFINE_PRIM(_I32, tcp_simultaneous_accepts, _TCP _I32); + +DEFINE_PRIM(_I32, tcp_bind, _TCP _REF(_SOCKADDR) _U32); + +DEFINE_PRIM(_I32, tcp_getsockname, _TCP _REF(_SOCKADDR) _REF(_I32)); + +DEFINE_PRIM(_I32, tcp_getpeername, _TCP _REF(_SOCKADDR) _REF(_I32)); + +HL_PRIM int HL_NAME(tcp_connect_with_cb)( uv_connect_t* req, uv_tcp_t* handle, const struct sockaddr* addr ) { + return uv_tcp_connect(req, handle, addr, on_uv_connect_cb); +} +DEFINE_PRIM(_I32, tcp_connect_with_cb, _CONNECT _TCP _REF(_SOCKADDR)); + +HL_PRIM int HL_NAME(tcp_close_reset_with_cb)( uv_tcp_t* handle ) { + return uv_tcp_close_reset(handle, on_uv_close_cb); +} +DEFINE_PRIM(_I32, tcp_close_reset_with_cb, _TCP); + +DEFINE_PRIM(_I32, socketpair, _I32 _I32 _OS_SOCK_T _I32 _I32); + +DEFINE_PRIM(_I32, timer_init, _LOOP _TIMER); + +HL_PRIM int HL_NAME(timer_start_with_cb)( uv_timer_t* handle, uint64_t timeout, uint64_t repeat ) { + return uv_timer_start(handle, on_uv_timer_cb, timeout, repeat); +} +DEFINE_PRIM(_I32, timer_start_with_cb, _TIMER _I64 _I64); + +DEFINE_PRIM(_I32, timer_stop, _TIMER); + +DEFINE_PRIM(_I32, timer_again, _TIMER); + +DEFINE_PRIM(_VOID, timer_set_repeat, _TIMER _I64); + +DEFINE_PRIM(_I64, timer_get_repeat, _TIMER); + +DEFINE_PRIM(_I64, timer_get_due_in, _TIMER); + +DEFINE_PRIM(_I32, tty_init, _LOOP _TTY _FILE _I32); + +DEFINE_PRIM(_I32, tty_set_mode, _TTY _TTY_MODE_T); + +DEFINE_PRIM(_I32, tty_reset_mode, _VOID); + +DEFINE_PRIM(_I32, tty_get_winsize, _TTY _REF(_I32) _REF(_I32)); + +DEFINE_PRIM(_VOID, tty_set_vterm_state, _TTY_VTERMSTATE_T); + +DEFINE_PRIM(_I32, tty_get_vterm_state, _TTY_VTERMSTATE); + +DEFINE_PRIM(_I32, udp_init, _LOOP _UDP); + +DEFINE_PRIM(_I32, udp_init_ex, _LOOP _UDP _U32); + +DEFINE_PRIM(_I32, udp_open, _UDP _OS_SOCK_T); + +DEFINE_PRIM(_I32, udp_bind, _UDP _REF(_SOCKADDR) _U32); + +DEFINE_PRIM(_I32, udp_connect, _UDP _REF(_SOCKADDR)); + +DEFINE_PRIM(_I32, udp_getpeername, _UDP _REF(_SOCKADDR) _REF(_I32)); + +DEFINE_PRIM(_I32, udp_getsockname, _UDP _REF(_SOCKADDR) _REF(_I32)); + +DEFINE_PRIM(_I32, udp_set_membership, _UDP _BYTES _BYTES _MEMBERSHIP); + +DEFINE_PRIM(_I32, udp_set_source_membership, _UDP _BYTES _BYTES _BYTES _MEMBERSHIP); + +DEFINE_PRIM(_I32, udp_set_multicast_loop, _UDP _I32); + +DEFINE_PRIM(_I32, udp_set_multicast_ttl, _UDP _I32); + +DEFINE_PRIM(_I32, udp_set_multicast_interface, _UDP _BYTES); + +DEFINE_PRIM(_I32, udp_set_broadcast, _UDP _I32); + +DEFINE_PRIM(_I32, udp_set_ttl, _UDP _I32); + +HL_PRIM int HL_NAME(udp_send_with_cb)( uv_udp_send_t* req, uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr ) { + return uv_udp_send(req, handle, bufs[], nbufs, addr, on_uv_udp_send_cb); +} +DEFINE_PRIM(_I32, udp_send_with_cb, _UDP_SEND _UDP _BUF _U32 _REF(_SOCKADDR)); + +DEFINE_PRIM(_I32, udp_try_send, _UDP _BUF _U32 _REF(_SOCKADDR)); + +HL_PRIM int HL_NAME(udp_recv_start_with_cb)( uv_udp_t* handle ) { + return uv_udp_recv_start(handle, on_uv_alloc_cb, on_uv_udp_recv_cb); +} +DEFINE_PRIM(_I32, udp_recv_start_with_cb, _UDP); + +DEFINE_PRIM(_I32, udp_using_recvmmsg, _UDP); + +DEFINE_PRIM(_I32, udp_recv_stop, _UDP); + +DEFINE_PRIM(_U64, udp_get_send_queue_size, _UDP); + +DEFINE_PRIM(_U64, udp_get_send_queue_count, _UDP); + +DEFINE_PRIM(_U32, version, _VOID); + +DEFINE_PRIM(_BYTES, version_string, _VOID); + diff --git a/other/uvgenerator/UVGenerator.hx b/other/uvgenerator/UVGenerator.hx new file mode 100644 index 000000000..e0285bd6c --- /dev/null +++ b/other/uvgenerator/UVGenerator.hx @@ -0,0 +1,193 @@ +import haxe.Exception; +import sys.io.File; +import eval.luv.File.FileSync; +import haxe.io.Path; +import haxe.PosInfos; +import eval.luv.Dir.DirSync; + +using StringTools; + +typedef TypeAndName = { + type:String, + name:String +} + +typedef FunctionSignature = { + var name:String; + var returnType:String; + var arguments:Array; +} + +class Skip extends Exception {} + +class UVGenerator { + static final skipDocs = ['api', 'dll', 'guide', 'index', 'migration_010_100', + 'poll', 'threading', 'threadpool', 'upgrading']; + static final skipFunctions = ['uv_loop_configure']; + static final allowNoCallback = ['uv_fs_cb']; + + static function main() { + var root = rootDir(); + var uvGeneratedC = Path.join([root, 'libs', 'uv', 'uv_generated.c']); + var docsDir = Path.join([root, 'include', 'libuv', 'docs', 'src']); + var outFile = File.write(uvGeneratedC); + var scan = DirSync.scan(docsDir).resolve(); + + var entry = null; + while(null != (entry = scan.next())) { + if(entry.kind != FILE || '.rst' != entry.name.sub(entry.name.length - 4)) + continue; + + if(skipDocs.contains(entry.name.sub(0, entry.name.length - 4).toString())) + continue; + + var path = Path.join([docsDir, entry.name.toString()]); + Sys.println('Generating ' + entry.name.sub(0, entry.name.length - 4).toString() + '...'); + + for(line in File.getContent(path).split('\n')) { + if(!line.startsWith('.. c:function:: ')) + continue; + try { + var sig = try { + parseSignature(line.substr('.. c:function:: '.length).trim()); + } catch(e:Skip) { + continue; + } + var needsWrapper = false; + for(a in sig.arguments) + if(a.type.endsWith('_cb')) { + needsWrapper = true; + break; + } + var str = needsWrapper ? generateWrapperWithCb(sig) : generateBinding(sig); + outFile.writeString(str); + } catch(e) { + Sys.stderr().writeString('Error on line: $line\n${e.details()}\n'); + Sys.exit(1); + } + outFile.writeString('\n'); + } + } + + outFile.close(); + scan.end(); + } + + static function rootDir(?p:PosInfos):String { + var generatorPath = FileSync.realPath(p.fileName).resolve().toString(); + return new Path(new Path(new Path(generatorPath).dir).dir).dir; + } + + static function parseSignature(str:String):FunctionSignature { + str = str.substr(0, str.length - (str.endsWith(';') ? 2 : 1)); + var parts = str.split('('); + var returnAndName = parseTypeAndName(parts[0]); + var args = parts[1].split(',').map(StringTools.trim); + if(skipFunctions.contains(returnAndName.name)) + throw new Skip('${returnAndName.name} is in the skipFunctions list'); + return { + name: returnAndName.name, + returnType: returnAndName.type, + arguments: args.map(parseTypeAndName) + } + } + + static function parseTypeAndName(str:String):TypeAndName { + var namePos = str.lastIndexOf(' ') + 1; + if(namePos <= 0) + return { + type: str, + name: '' + } + else + return { + type: str.substring(0, namePos - 1), + name: str.substr(namePos) + } + } + + static function mapType(type:String):String { + return switch type { + case 'int': '_I32'; + case 'int64_t': '_I64'; + case 'uint64_t': '_I64'; + case 'char*': '_BYTES'; + case 'void*': '_DYN'; + case 'void': '_VOID'; + case 'unsigned int': '_U32'; + case 'ssize_t': '_I64'; + case 'size_t*': '_REF(_I64)'; + case 'size_t': '_U64'; + case 'int*': '_REF(_I32)'; + case 'double*': '_REF(_F64)'; + case 'double': '_F64'; + case 'FILE*': '_FILE'; + case 'uv_pid_t': '_I32'; + case 'uv_buf_t': '_BUF'; + case _ if(type.endsWith('**')): + '_REF(' + mapType(type.substr(0, type.length - 1)) + ')'; + case _ if(type.startsWith('uv_')): + type = type.substr(3); + if(type.endsWith('_t*')) + type = type.substring(0, type.length - 3); + '_' + type.toUpperCase(); + case _ if(type.startsWith('struct ')): + type = '_' + type.substr('struct '.length).toUpperCase(); + type.endsWith('*') ? '_REF(${type.substr(0, type.length - 1)})' : type; + case _ if(type.startsWith('const ')): + mapType(type.substr('const '.length)); + case _: + throw 'Unknown type: "$type"'; + } + } + + static function functionName(name:String):String { + return if(name.startsWith('uv_')) + name.substr('uv_'.length) + else + throw 'Function name is expected to start with "uv_": "$name"'; + } + + static function callbackName(type:String):String { + // if(type.startsWith('uv_')) + // type = type.substr('uv_'.length); + return 'on_${type}'; + } + + static function generateBinding(sig:FunctionSignature):String { + var args = sig.arguments.map(a -> mapType(a.type)).join(' '); + return 'DEFINE_PRIM(${mapType(sig.returnType)}, ${functionName(sig.name)}, $args);\n'; + } + + static function generateWrapperWithCb(sig:FunctionSignature):String { + var fnName = '${functionName(sig.name)}_with_cb'; + + var args = sig.arguments + .filter(a -> !a.type.endsWith('_cb') || allowNoCallback.contains(a.type)) + .map(a -> a.type.endsWith('_cb') && allowNoCallback.contains(a.type) ? 'bool use_${a.type}' : '${a.type} ${a.name}') + .join(', '); + var lines = ['HL_PRIM ${sig.returnType} HL_NAME($fnName)( $args ) {']; + + var args = sig.arguments + .map(a -> { + var cbName = callbackName(a.type); + if(a.type.endsWith('_cb')) + allowNoCallback.contains(a.type) ? 'use_${a.type}?$cbName:NULL' : cbName + else + a.name; + }) + .join(', '); + var ret = sig.returnType == 'void' ? '' : 'return '; + lines.push(' $ret${sig.name}($args);'); + lines.push('}'); + + var args = sig.arguments + .filter(a -> !a.type.endsWith('_cb') || allowNoCallback.contains(a.type)) + .map(a -> a.type.endsWith('_cb') && allowNoCallback.contains(a.type) ? '_BOOL' : mapType(a.type)) + .join(' '); + lines.push('DEFINE_PRIM(${mapType(sig.returnType)}, $fnName, $args);'); + + return lines.join('\n') + '\n'; + } +} + diff --git a/other/uvgenerator/run.hxml b/other/uvgenerator/run.hxml new file mode 100644 index 000000000..52530b294 --- /dev/null +++ b/other/uvgenerator/run.hxml @@ -0,0 +1,2 @@ +--main UVGenerator +--interp \ No newline at end of file From 551c656ee3045554c919764aeccdb3c83dbc91dd Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Wed, 18 Aug 2021 02:10:17 +0300 Subject: [PATCH 058/117] [wip] Loop, Async, Timer --- libs/uv/uv.c | 533 +++++++++++++++----- libs/uv/uv_generated.c | 832 +++++++++++++++---------------- other/uvgenerator/UVGenerator.hx | 2 +- 3 files changed, 822 insertions(+), 545 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index ad0111d45..736eb12c1 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -11,6 +11,386 @@ # error "libuv1-dev required, uv version 0.x found" #endif +// Errors + +#define HL_UV_NOERR 0 +#define HL_UV_E2BIG 1 +#define HL_UV_EACCES 2 +#define HL_UV_EADDRINUSE 3 +#define HL_UV_EADDRNOTAVAIL 4 +#define HL_UV_EAFNOSUPPORT 5 +#define HL_UV_EAGAIN 6 +#define HL_UV_EAI_ADDRFAMILY 7 +#define HL_UV_EAI_AGAIN 8 +#define HL_UV_EAI_BADFLAGS 9 +#define HL_UV_EAI_BADHINTS 10 +#define HL_UV_EAI_CANCELED 11 +#define HL_UV_EAI_FAIL 12 +#define HL_UV_EAI_FAMILY 13 +#define HL_UV_EAI_MEMORY 14 +#define HL_UV_EAI_NODATA 15 +#define HL_UV_EAI_NONAME 16 +#define HL_UV_EAI_OVERFLOW 17 +#define HL_UV_EAI_PROTOCOL 18 +#define HL_UV_EAI_SERVICE 19 +#define HL_UV_EAI_SOCKTYPE 20 +#define HL_UV_EALREADY 21 +#define HL_UV_EBADF 22 +#define HL_UV_EBUSY 23 +#define HL_UV_ECANCELED 24 +#define HL_UV_ECHARSET 25 +#define HL_UV_ECONNABORTED 26 +#define HL_UV_ECONNREFUSED 27 +#define HL_UV_ECONNRESET 28 +#define HL_UV_EDESTADDRREQ 29 +#define HL_UV_EEXIST 30 +#define HL_UV_EFAULT 31 +#define HL_UV_EFBIG 32 +#define HL_UV_EHOSTUNREACH 33 +#define HL_UV_EINTR 34 +#define HL_UV_EINVAL 35 +#define HL_UV_EIO 36 +#define HL_UV_EISCONN 37 +#define HL_UV_EISDIR 38 +#define HL_UV_ELOOP 39 +#define HL_UV_EMFILE 40 +#define HL_UV_EMSGSIZE 41 +#define HL_UV_ENAMETOOLONG 42 +#define HL_UV_ENETDOWN 43 +#define HL_UV_ENETUNREACH 44 +#define HL_UV_ENFILE 45 +#define HL_UV_ENOBUFS 46 +#define HL_UV_ENODEV 47 +#define HL_UV_ENOENT 48 +#define HL_UV_ENOMEM 49 +#define HL_UV_ENONET 50 +#define HL_UV_ENOPROTOOPT 51 +#define HL_UV_ENOSPC 52 +#define HL_UV_ENOSYS 53 +#define HL_UV_ENOTCONN 54 +#define HL_UV_ENOTDIR 55 +#define HL_UV_ENOTEMPTY 56 +#define HL_UV_ENOTSOCK 57 +#define HL_UV_ENOTSUP 58 +#define HL_UV_EOVERFLOW 59 +#define HL_UV_EPERM 60 +#define HL_UV_EPIPE 61 +#define HL_UV_EPROTO 62 +#define HL_UV_EPROTONOSUPPORT 63 +#define HL_UV_EPROTOTYPE 64 +#define HL_UV_ERANGE 65 +#define HL_UV_EROFS 66 +#define HL_UV_ESHUTDOWN 67 +#define HL_UV_ESPIPE 68 +#define HL_UV_ESRCH 69 +#define HL_UV_ETIMEDOUT 70 +#define HL_UV_ETXTBSY 71 +#define HL_UV_EXDEV 72 +#define HL_UV_UNKNOWN 73 +#define HL_UV_EOF 74 +#define HL_UV_ENXIO 75 +#define HL_UV_EMLINK 76 +#define HL_UV_ENOTTY 77 +#define HL_UV_EFTYPE 78 +#define HL_UV_EILSEQ 79 +#define HL_UV_ESOCKTNOSUPPORT 80 + +static int errno_uv2hl( int uv_errno ) { + switch(uv_errno) { + case 0: return HL_UV_NOERR; + case UV_E2BIG: return HL_UV_E2BIG; + case UV_EACCES: return HL_UV_EACCES; + case UV_EADDRINUSE: return HL_UV_EADDRINUSE; + case UV_EADDRNOTAVAIL: return HL_UV_EADDRNOTAVAIL; + case UV_EAFNOSUPPORT: return HL_UV_EAFNOSUPPORT; + case UV_EAGAIN: return HL_UV_EAGAIN; + case UV_EAI_ADDRFAMILY: return HL_UV_EAI_ADDRFAMILY; + case UV_EAI_AGAIN: return HL_UV_EAI_AGAIN; + case UV_EAI_BADFLAGS: return HL_UV_EAI_BADFLAGS; + case UV_EAI_BADHINTS: return HL_UV_EAI_BADHINTS; + case UV_EAI_CANCELED: return HL_UV_EAI_CANCELED; + case UV_EAI_FAIL: return HL_UV_EAI_FAIL; + case UV_EAI_FAMILY: return HL_UV_EAI_FAMILY; + case UV_EAI_MEMORY: return HL_UV_EAI_MEMORY; + case UV_EAI_NODATA: return HL_UV_EAI_NODATA; + case UV_EAI_NONAME: return HL_UV_EAI_NONAME; + case UV_EAI_OVERFLOW: return HL_UV_EAI_OVERFLOW; + case UV_EAI_PROTOCOL: return HL_UV_EAI_PROTOCOL; + case UV_EAI_SERVICE: return HL_UV_EAI_SERVICE; + case UV_EAI_SOCKTYPE: return HL_UV_EAI_SOCKTYPE; + case UV_EALREADY: return HL_UV_EALREADY; + case UV_EBADF: return HL_UV_EBADF; + case UV_EBUSY: return HL_UV_EBUSY; + case UV_ECANCELED: return HL_UV_ECANCELED; + case UV_ECHARSET: return HL_UV_ECHARSET; + case UV_ECONNABORTED: return HL_UV_ECONNABORTED; + case UV_ECONNREFUSED: return HL_UV_ECONNREFUSED; + case UV_ECONNRESET: return HL_UV_ECONNRESET; + case UV_EDESTADDRREQ: return HL_UV_EDESTADDRREQ; + case UV_EEXIST: return HL_UV_EEXIST; + case UV_EFAULT: return HL_UV_EFAULT; + case UV_EFBIG: return HL_UV_EFBIG; + case UV_EHOSTUNREACH: return HL_UV_EHOSTUNREACH; + case UV_EINTR: return HL_UV_EINTR; + case UV_EINVAL: return HL_UV_EINVAL; + case UV_EIO: return HL_UV_EIO; + case UV_EISCONN: return HL_UV_EISCONN; + case UV_EISDIR: return HL_UV_EISDIR; + case UV_ELOOP: return HL_UV_ELOOP; + case UV_EMFILE: return HL_UV_EMFILE; + case UV_EMSGSIZE: return HL_UV_EMSGSIZE; + case UV_ENAMETOOLONG: return HL_UV_ENAMETOOLONG; + case UV_ENETDOWN: return HL_UV_ENETDOWN; + case UV_ENETUNREACH: return HL_UV_ENETUNREACH; + case UV_ENFILE: return HL_UV_ENFILE; + case UV_ENOBUFS: return HL_UV_ENOBUFS; + case UV_ENODEV: return HL_UV_ENODEV; + case UV_ENOENT: return HL_UV_ENOENT; + case UV_ENOMEM: return HL_UV_ENOMEM; + case UV_ENONET: return HL_UV_ENONET; + case UV_ENOPROTOOPT: return HL_UV_ENOPROTOOPT; + case UV_ENOSPC: return HL_UV_ENOSPC; + case UV_ENOSYS: return HL_UV_ENOSYS; + case UV_ENOTCONN: return HL_UV_ENOTCONN; + case UV_ENOTDIR: return HL_UV_ENOTDIR; + case UV_ENOTEMPTY: return HL_UV_ENOTEMPTY; + case UV_ENOTSOCK: return HL_UV_ENOTSOCK; + case UV_ENOTSUP: return HL_UV_ENOTSUP; + case UV_EOVERFLOW: return HL_UV_EOVERFLOW; + case UV_EPERM: return HL_UV_EPERM; + case UV_EPIPE: return HL_UV_EPIPE; + case UV_EPROTO: return HL_UV_EPROTO; + case UV_EPROTONOSUPPORT: return HL_UV_EPROTONOSUPPORT; + case UV_EPROTOTYPE: return HL_UV_EPROTOTYPE; + case UV_ERANGE: return HL_UV_ERANGE; + case UV_EROFS: return HL_UV_EROFS; + case UV_ESHUTDOWN: return HL_UV_ESHUTDOWN; + case UV_ESPIPE: return HL_UV_ESPIPE; + case UV_ESRCH: return HL_UV_ESRCH; + case UV_ETIMEDOUT: return HL_UV_ETIMEDOUT; + case UV_ETXTBSY: return HL_UV_ETXTBSY; + case UV_EXDEV: return HL_UV_EXDEV; + case UV_UNKNOWN: return HL_UV_UNKNOWN; + case UV_EOF: return HL_UV_EOF; + case UV_ENXIO: return HL_UV_ENXIO; + case UV_EMLINK: return HL_UV_EMLINK; + case UV_ENOTTY: return HL_UV_ENOTTY; + case UV_EFTYPE: return HL_UV_EFTYPE; + case UV_EILSEQ: return HL_UV_EILSEQ; + case UV_ESOCKTNOSUPPORT: return HL_UV_ESOCKTNOSUPPORT; + default: return HL_UV_UNKNOWN; + } +} + +static int errno_hl2uv( int uv_errno ) { + switch(uv_errno) { + case HL_UV_E2BIG: return UV_E2BIG; + case HL_UV_EACCES: return UV_EACCES; + case HL_UV_EADDRINUSE: return UV_EADDRINUSE; + case HL_UV_EADDRNOTAVAIL: return UV_EADDRNOTAVAIL; + case HL_UV_EAFNOSUPPORT: return UV_EAFNOSUPPORT; + case HL_UV_EAGAIN: return UV_EAGAIN; + case HL_UV_EAI_ADDRFAMILY: return UV_EAI_ADDRFAMILY; + case HL_UV_EAI_AGAIN: return UV_EAI_AGAIN; + case HL_UV_EAI_BADFLAGS: return UV_EAI_BADFLAGS; + case HL_UV_EAI_BADHINTS: return UV_EAI_BADHINTS; + case HL_UV_EAI_CANCELED: return UV_EAI_CANCELED; + case HL_UV_EAI_FAIL: return UV_EAI_FAIL; + case HL_UV_EAI_FAMILY: return UV_EAI_FAMILY; + case HL_UV_EAI_MEMORY: return UV_EAI_MEMORY; + case HL_UV_EAI_NODATA: return UV_EAI_NODATA; + case HL_UV_EAI_NONAME: return UV_EAI_NONAME; + case HL_UV_EAI_OVERFLOW: return UV_EAI_OVERFLOW; + case HL_UV_EAI_PROTOCOL: return UV_EAI_PROTOCOL; + case HL_UV_EAI_SERVICE: return UV_EAI_SERVICE; + case HL_UV_EAI_SOCKTYPE: return UV_EAI_SOCKTYPE; + case HL_UV_EALREADY: return UV_EALREADY; + case HL_UV_EBADF: return UV_EBADF; + case HL_UV_EBUSY: return UV_EBUSY; + case HL_UV_ECANCELED: return UV_ECANCELED; + case HL_UV_ECHARSET: return UV_ECHARSET; + case HL_UV_ECONNABORTED: return UV_ECONNABORTED; + case HL_UV_ECONNREFUSED: return UV_ECONNREFUSED; + case HL_UV_ECONNRESET: return UV_ECONNRESET; + case HL_UV_EDESTADDRREQ: return UV_EDESTADDRREQ; + case HL_UV_EEXIST: return UV_EEXIST; + case HL_UV_EFAULT: return UV_EFAULT; + case HL_UV_EFBIG: return UV_EFBIG; + case HL_UV_EHOSTUNREACH: return UV_EHOSTUNREACH; + case HL_UV_EINTR: return UV_EINTR; + case HL_UV_EINVAL: return UV_EINVAL; + case HL_UV_EIO: return UV_EIO; + case HL_UV_EISCONN: return UV_EISCONN; + case HL_UV_EISDIR: return UV_EISDIR; + case HL_UV_ELOOP: return UV_ELOOP; + case HL_UV_EMFILE: return UV_EMFILE; + case HL_UV_EMSGSIZE: return UV_EMSGSIZE; + case HL_UV_ENAMETOOLONG: return UV_ENAMETOOLONG; + case HL_UV_ENETDOWN: return UV_ENETDOWN; + case HL_UV_ENETUNREACH: return UV_ENETUNREACH; + case HL_UV_ENFILE: return UV_ENFILE; + case HL_UV_ENOBUFS: return UV_ENOBUFS; + case HL_UV_ENODEV: return UV_ENODEV; + case HL_UV_ENOENT: return UV_ENOENT; + case HL_UV_ENOMEM: return UV_ENOMEM; + case HL_UV_ENONET: return UV_ENONET; + case HL_UV_ENOPROTOOPT: return UV_ENOPROTOOPT; + case HL_UV_ENOSPC: return UV_ENOSPC; + case HL_UV_ENOSYS: return UV_ENOSYS; + case HL_UV_ENOTCONN: return UV_ENOTCONN; + case HL_UV_ENOTDIR: return UV_ENOTDIR; + case HL_UV_ENOTEMPTY: return UV_ENOTEMPTY; + case HL_UV_ENOTSOCK: return UV_ENOTSOCK; + case HL_UV_ENOTSUP: return UV_ENOTSUP; + case HL_UV_EOVERFLOW: return UV_EOVERFLOW; + case HL_UV_EPERM: return UV_EPERM; + case HL_UV_EPIPE: return UV_EPIPE; + case HL_UV_EPROTO: return UV_EPROTO; + case HL_UV_EPROTONOSUPPORT: return UV_EPROTONOSUPPORT; + case HL_UV_EPROTOTYPE: return UV_EPROTOTYPE; + case HL_UV_ERANGE: return UV_ERANGE; + case HL_UV_EROFS: return UV_EROFS; + case HL_UV_ESHUTDOWN: return UV_ESHUTDOWN; + case HL_UV_ESPIPE: return UV_ESPIPE; + case HL_UV_ESRCH: return UV_ESRCH; + case HL_UV_ETIMEDOUT: return UV_ETIMEDOUT; + case HL_UV_ETXTBSY: return UV_ETXTBSY; + case HL_UV_EXDEV: return UV_EXDEV; + case HL_UV_UNKNOWN: return UV_UNKNOWN; + case HL_UV_EOF: return UV_EOF; + case HL_UV_ENXIO: return UV_ENXIO; + case HL_UV_EMLINK: return UV_EMLINK; + case HL_UV_ENOTTY: return UV_ENOTTY; + case HL_UV_EFTYPE: return UV_EFTYPE; + case HL_UV_EILSEQ: return UV_EILSEQ; + case HL_UV_ESOCKTNOSUPPORT: return UV_ESOCKTNOSUPPORT; + default: return UV_UNKNOWN; + } +} + +static int hl_uv_errno( int result ) { + return result < 0 ? errno_uv2hl(result) : 0; +} + +HL_PRIM int HL_NAME(translate_uv_error)( int uv_errno ) { + return errno_uv2hl(uv_errno); +} +DEFINE_PRIM(_I32, translate_uv_error, _I32); + +HL_PRIM int HL_NAME(translate_to_uv_error)( int hl_errno ) { + return errno_hl2uv(hl_errno); +} +DEFINE_PRIM(_I32, translate_to_uv_error, _I32); + +// Common macros + +#define _U64 _I64 + +#define _POINTER _ABSTRACT(void_pointer) +#define _HANDLE _ABSTRACT(uv_handle) +#define _REQUEST _ABSTRACT(uv_req) +#define _LOOP _ABSTRACT(uv_loop) +#define _ASYNC _HANDLE +#define _CHECK _HANDLE +#define _TIMER _HANDLE +#define _GETADDRINFO _ABSTRACT(uv_getaddrinfo_t) + +#define UV_ALLOC(t) ((t*)malloc(sizeof(t))) +#define DATA(t,h) ((t)h->data) + +#define DEFINE_PRIM_ALLOC(r,t) \ + HL_PRIM t *HL_NAME(alloc_##t)() { \ + return UV_ALLOC(t); \ + } \ + DEFINE_PRIM(r, alloc_##t, _NO_ARG); + +// Handle + +#define HANDLE_DATA_FIELDS \ + hl_type *t; \ + vclosure *onClose; + +typedef struct { + HANDLE_DATA_FIELDS; +} vhandle_data; + +#define _HANDLE_DATA _OBJ(_FUN(_VOID,_NO_ARG)) + +HL_PRIM void *HL_NAME(handle_data_to_pointer)( vhandle_data *v ) { + return v; +} +DEFINE_PRIM(_POINTER, handle_data_to_pointer, _HANDLE_DATA); + +HL_PRIM vhandle_data *HL_NAME(handle_data_of_pointer)( void *v ) { + return v; +} +DEFINE_PRIM(_HANDLE_DATA, handle_data_of_pointer, _POINTER); + +HL_PRIM void HL_NAME(handle_set_data_with_gc)( uv_handle_t *h, vhandle_data *new_data ) { + void *current_data = uv_handle_get_data(h); + if( current_data == new_data ) + return; + if( current_data ) + hl_remove_root(current_data); + if( new_data ) + hl_add_root(new_data); + uv_handle_set_data(h, new_data); +} +DEFINE_PRIM(_VOID, handle_set_data_with_gc, _HANDLE _HANDLE_DATA); + +static void on_uv_close_cb( uv_handle_t *h ) { + vhandle_data *data = DATA(vhandle_data *, h); + if( data ) { + if( data->onClose ) + hl_call0(void, data->onClose); + hl_remove_root(data); + } + free(h); +} + +// Async + +typedef struct { + HANDLE_DATA_FIELDS; + vclosure *onSend; +} vasync_data; + +DEFINE_PRIM_ALLOC(_ASYNC, uv_async_t); + +static void on_uv_async_cb( uv_async_t *h ) { + vclosure *c = DATA(vasync_data *, h)->onSend; + hl_call1(void, c, uv_async_t *, h); +} + +// Timer + +typedef struct { + HANDLE_DATA_FIELDS; + vclosure *onTick; +} vtimer_data; + +DEFINE_PRIM_ALLOC(_TIMER, uv_timer_t); + +static void on_uv_timer_cb( uv_timer_t *h ) { + vclosure *c = DATA(vtimer_data *, h)->onTick; + hl_call1(void, c, uv_timer_t *, h); +} + +// Loop + +#define _RUN_MODE _I32 + +DEFINE_PRIM_ALLOC(_LOOP,uv_loop_t); + + + + +#define _DIR _ABSTRACT(uv_dir) +#define _SOCKADDR _ABSTRACT(uv_sockaddr_storage) +#define _CALLB _FUN(_VOID,_NO_ARG) +#define _TIMESPEC _OBJ(_I64 _I64) +#define _STAT _OBJ(_I64 _I64 _I64 _I64 _I64 _I64 _I64 _I64 _I64 _I64 _I64 _I64 _TIMESPEC _TIMESPEC _TIMESPEC _TIMESPEC) + typedef struct sockaddr uv_sockaddr; typedef struct sockaddr_in uv_sockaddr_in; typedef struct sockaddr_in6 uv_sockaddr_in6; @@ -31,16 +411,6 @@ typedef struct { } events_data; #define UV_DATA(h) ((events_data*)((h)->data)) - -#define _LOOP _ABSTRACT(uv_loop) -#define _HANDLE _ABSTRACT(uv_handle) -#define _REQUEST _ABSTRACT(uv_req) -#define _DIR _ABSTRACT(uv_dir) -#define _SOCKADDR _ABSTRACT(uv_sockaddr_storage) -#define _CALLB _FUN(_VOID,_NO_ARG) -#define _TIMESPEC _OBJ(_I64 _I64) -#define _STAT _OBJ(_I64 _I64 _I64 _I64 _I64 _I64 _I64 _I64 _I64 _I64 _I64 _I64 _TIMESPEC _TIMESPEC _TIMESPEC _TIMESPEC) -#define UV_ALLOC(t) ((t*)malloc(sizeof(t))) #define UV_ALLOC_REQ(t,r,c) \ t *r = UV_ALLOC(t); \ req_init_hl_data((uv_req_t *)r); \ @@ -81,98 +451,7 @@ static int bytes_geti32( vbyte *b, int pos ) { return b[pos] | (b[pos + 1] << 8) | (b[pos + 2] << 16) | (b[pos + 3] << 24); } -// Errors - -static int errno_uv2hx( int uv_errno ) { - switch(uv_errno) { - case 0: return 0; - case UV_E2BIG: return 1; - case UV_EACCES: return 2; - case UV_EADDRINUSE: return 3; - case UV_EADDRNOTAVAIL: return 4; - case UV_EAFNOSUPPORT: return 5; - case UV_EAGAIN: return 6; - case UV_EAI_ADDRFAMILY: return 7; - case UV_EAI_AGAIN: return 8; - case UV_EAI_BADFLAGS: return 9; - case UV_EAI_BADHINTS: return 10; - case UV_EAI_CANCELED: return 11; - case UV_EAI_FAIL: return 12; - case UV_EAI_FAMILY: return 13; - case UV_EAI_MEMORY: return 14; - case UV_EAI_NODATA: return 15; - case UV_EAI_NONAME: return 16; - case UV_EAI_OVERFLOW: return 17; - case UV_EAI_PROTOCOL: return 18; - case UV_EAI_SERVICE: return 19; - case UV_EAI_SOCKTYPE: return 20; - case UV_EALREADY: return 21; - case UV_EBADF: return 22; - case UV_EBUSY: return 23; - case UV_ECANCELED: return 24; - case UV_ECHARSET: return 25; - case UV_ECONNABORTED: return 26; - case UV_ECONNREFUSED: return 27; - case UV_ECONNRESET: return 28; - case UV_EDESTADDRREQ: return 29; - case UV_EEXIST: return 30; - case UV_EFAULT: return 31; - case UV_EFBIG: return 32; - case UV_EHOSTUNREACH: return 33; - case UV_EINTR: return 34; - case UV_EINVAL: return 35; - case UV_EIO: return 36; - case UV_EISCONN: return 37; - case UV_EISDIR: return 38; - case UV_ELOOP: return 39; - case UV_EMFILE: return 40; - case UV_EMSGSIZE: return 41; - case UV_ENAMETOOLONG: return 42; - case UV_ENETDOWN: return 43; - case UV_ENETUNREACH: return 44; - case UV_ENFILE: return 45; - case UV_ENOBUFS: return 46; - case UV_ENODEV: return 47; - case UV_ENOENT: return 48; - case UV_ENOMEM: return 49; - case UV_ENONET: return 50; - case UV_ENOPROTOOPT: return 51; - case UV_ENOSPC: return 52; - case UV_ENOSYS: return 53; - case UV_ENOTCONN: return 54; - case UV_ENOTDIR: return 55; - case UV_ENOTEMPTY: return 56; - case UV_ENOTSOCK: return 57; - case UV_ENOTSUP: return 58; - case UV_EOVERFLOW: return 59; - case UV_EPERM: return 60; - case UV_EPIPE: return 61; - case UV_EPROTO: return 62; - case UV_EPROTONOSUPPORT: return 63; - case UV_EPROTOTYPE: return 64; - case UV_ERANGE: return 65; - case UV_EROFS: return 66; - case UV_ESHUTDOWN: return 67; - case UV_ESPIPE: return 68; - case UV_ESRCH: return 69; - case UV_ETIMEDOUT: return 70; - case UV_ETXTBSY: return 71; - case UV_EXDEV: return 72; - case UV_UNKNOWN: return 73; - case UV_EOF: return 74; - case UV_ENXIO: return 75; - case UV_EMLINK: return 76; - case UV_ENOTTY: return 77; - case UV_EFTYPE: return 78; - case UV_EILSEQ: return 79; - case UV_ESOCKTNOSUPPORT: return 80; - default: return 73; //UV_UNKNOWN - } -} - -static int hx_errno( int result ) { - return result < 0 ? errno_uv2hx(result) : 0; -} +// exceptions static vclosure *c_exception; @@ -183,15 +462,13 @@ DEFINE_PRIM(_VOID, init_exception, _FUN(_DYN, _I32)); static void hx_error(int uv_errno) { if( c_exception ) { - vdynamic *exc = hl_call1(vdynamic *, c_exception, int, errno_uv2hx(uv_errno)); + vdynamic *exc = hl_call1(vdynamic *, c_exception, int, errno_uv2hl(uv_errno)); hl_throw(exc); } else { hl_error("%s", hl_to_utf16(uv_err_name(uv_errno))); } } -DEFINE_PRIM(_BYTES, strerror, _I32); - // Request static events_data *req_init_hl_data( uv_req_t *r ) { @@ -331,7 +608,7 @@ DEFINE_PRIM(_VOID, unref_wrap, _HANDLE); static void on_shutdown( uv_shutdown_t *r, int status ) { UV_GET_CLOSURE(c,r,0,"No callback in shutdown request"); - hl_call1(void, c, int, errno_uv2hx(status)); + hl_call1(void, c, int, errno_uv2hl(status)); free_req((uv_req_t *)r); } @@ -345,7 +622,7 @@ DEFINE_PRIM(_VOID, shutdown_wrap, _HANDLE _FUN(_VOID,_I32)); static void on_listen( uv_stream_t *h, int status ) { UV_GET_CLOSURE(c,h,EVT_STREAM_LISTEN,"No listen callback in stream handle"); - hl_call1(void, c, int, errno_uv2hx(status)); + hl_call1(void, c, int, errno_uv2hl(status)); } HL_PRIM void HL_NAME(listen_wrap)( uv_stream_t *h, int backlog, vclosure *c ) { @@ -370,7 +647,7 @@ static void on_alloc( uv_handle_t* h, size_t size, uv_buf_t *buf ) { static void on_read( uv_stream_t *h, ssize_t nread, const uv_buf_t *buf ) { UV_GET_CLOSURE(c,h,EVT_STREAM_READ,"No listen callback in stream handle"); if( nread < 0 ) { - hl_call3(void, c, int, errno_uv2hx(nread), vbyte *, NULL, int, 0); + hl_call3(void, c, int, errno_uv2hl(nread), vbyte *, NULL, int, 0); } else { hl_call3(void, c, int, 0, vbyte *, (vbyte *)buf->base, int, nread); } @@ -394,7 +671,7 @@ DEFINE_PRIM(_VOID, read_stop_wrap, _HANDLE); static void on_write( uv_write_t *r, int status ) { UV_GET_CLOSURE(c,r,0,"No callback in write request"); - hl_call1(void, c, int, hx_errno(status)); + hl_call1(void, c, int, hl_uv_errno(status)); free_req((uv_req_t *)r); } @@ -823,7 +1100,7 @@ DEFINE_PRIM(_SOCKADDR, tcp_getpeername_wrap, _HANDLE); static void on_connect( uv_connect_t *r, int status ) { UV_GET_CLOSURE(c,r,0,"No callback in connect request"); - hl_call1(void, c, int, errno_uv2hx(status)); + hl_call1(void, c, int, errno_uv2hl(status)); free_req((uv_req_t *)r); } @@ -1216,7 +1493,7 @@ DEFINE_PRIM(_VOID, udp_set_ttl_wrap, _HANDLE _I32); static void on_udp_send( uv_udp_send_t *r, int status ) { UV_GET_CLOSURE(c,r,0,"No callback in udp send request"); - hl_call1(void, c, int, hx_errno(status)); + hl_call1(void, c, int, hl_uv_errno(status)); free_req((uv_req_t *)r); } @@ -1257,7 +1534,7 @@ static void on_udp_recv( uv_udp_t *h, ssize_t nread, const uv_buf_t *buf, const hl_dyn_seti(hx_flags, hl_hash_utf8("partial"), &hlt_bool, 0 != (flags & UV_UDP_PARTIAL)); if( nread < 0 ) { - hl_call5(void, c, int, errno_uv2hx(nread), vbyte *, NULL, int, 0, uv_sockaddr_storage *, addr, vdynamic *, hx_flags); + hl_call5(void, c, int, errno_uv2hl(nread), vbyte *, NULL, int, 0, uv_sockaddr_storage *, addr, vdynamic *, hx_flags); } else { hl_call5(void, c, int, 0, vbyte *, (vbyte *)buf->base, int, nread, uv_sockaddr_storage *, addr, vdynamic *, hx_flags); } @@ -1351,7 +1628,7 @@ static void on_getaddrinfo( uv_getaddrinfo_t *r, int status, struct addrinfo *re } freeaddrinfo(res); - hl_call2(void,c,int,errno_uv2hx(status),varray *,addresses); + hl_call2(void,c,int,errno_uv2hl(status),varray *,addresses); free_req((uv_req_t *)r); } @@ -1408,7 +1685,7 @@ static void on_getnameinfo( uv_getnameinfo_t *r, int status, const char *hostnam vbyte * bservice = NULL; if( service ) bservice = hl_copy_bytes((const vbyte *)service, strlen(service)); - hl_call3(void,c,int,errno_uv2hx(status),vbyte *,bhost,vbyte *,bservice); + hl_call3(void,c,int,errno_uv2hl(status),vbyte *,bhost,vbyte *,bservice); free_req((uv_req_t *)r); } @@ -1442,7 +1719,7 @@ HL_PRIM uv_loop_t *HL_NAME(loop_init_wrap)( ) { } DEFINE_PRIM(_LOOP, loop_init_wrap, _NO_ARG); -DEFINE_PRIM(_LOOP, default_loop, _NO_ARG); +// DEFINE_PRIM(_LOOP, default_loop, _NO_ARG); HL_PRIM void HL_NAME(loop_close_wrap)( uv_loop_t *loop ) { UV_CHECK_NULL(loop,); @@ -1472,7 +1749,7 @@ DEFINE_PRIM(_VOID, stop_wrap, _LOOP); static void on_fs_open( uv_fs_t *r ) { UV_GET_CLOSURE(c,r,0,"No callback in fs_open request"); - hl_call2(void, c, int, hx_errno(r->result), int, r->result); + hl_call2(void, c, int, hl_uv_errno(r->result), int, r->result); free_req((uv_req_t *)r); } @@ -1523,25 +1800,25 @@ DEFINE_PRIM(_VOID, fs_open_wrap, _LOOP _STRING _ARR _FUN(_VOID,_I32 _I32)); static void on_fs_common( uv_fs_t *r ) { UV_GET_CLOSURE(c,r,0,"No callback in fs request"); - hl_call1(void,c,int,hx_errno(r->result)); + hl_call1(void,c,int,hl_uv_errno(r->result)); free_fs_req(r); } static void on_fs_bytes_handled( uv_fs_t *r ) { UV_GET_CLOSURE(c,r,0,"No callback in fs request"); - hl_call2(void,c,int,hx_errno(r->result),int64,r->result<0?0:r->result); + hl_call2(void,c,int,hl_uv_errno(r->result),int64,r->result<0?0:r->result); free_fs_req(r); } static void on_fs_path( uv_fs_t *r ) { UV_GET_CLOSURE(c,r,0,"No callback in fs request"); - hl_call2(void,c,int,hx_errno(r->result),vbyte *,(vbyte *)(r->result<0?NULL:r->path)); + hl_call2(void,c,int,hl_uv_errno(r->result),vbyte *,(vbyte *)(r->result<0?NULL:r->path)); free_fs_req(r); } static void on_fs_bytes( uv_fs_t *r ) { UV_GET_CLOSURE(c,r,0,"No callback in fs request"); - hl_call2(void,c,int,hx_errno(r->result),vbyte *,(vbyte *)(r->result<0?NULL:r->ptr)); + hl_call2(void,c,int,hl_uv_errno(r->result),vbyte *,(vbyte *)(r->result<0?NULL:r->ptr)); free_fs_req(r); } @@ -1602,7 +1879,7 @@ DEFINE_PRIM(_VOID, fs_mkdtemp_wrap, _LOOP _STRING _FUN(_VOID,_I32 _BYTES)); static void on_fs_mkstemp( uv_fs_t *r ) { UV_GET_CLOSURE(c,r,0,"No callback in fs_mkstemp request"); - hl_call3(void,c,int,hx_errno(r->result),int,r->result,vbyte *,(vbyte *)(r->result<0?NULL:r->path)); + hl_call3(void,c,int,hl_uv_errno(r->result),int,r->result,vbyte *,(vbyte *)(r->result<0?NULL:r->path)); free_fs_req(r); } @@ -1626,7 +1903,7 @@ DEFINE_PRIM(_VOID, fs_rmdir_wrap, _LOOP _STRING _FUN(_VOID,_I32)); static void on_fs_opendir( uv_fs_t *r ) { UV_GET_CLOSURE(c,r,0,"No callback in fs_opendir request"); - hl_call2(void,c,int,hx_errno(r->result),uv_dir_t *,(r->result<0?NULL:r->ptr)); + hl_call2(void,c,int,hl_uv_errno(r->result),uv_dir_t *,(r->result<0?NULL:r->ptr)); free_fs_req(r); } @@ -1651,7 +1928,7 @@ DEFINE_PRIM(_VOID, fs_closedir_wrap, _DIR _LOOP _FUN(_VOID,_I32)); static void on_fs_readdir( uv_fs_t *r ) { UV_GET_CLOSURE(c,r,0,"No callback in fs_readdir request"); if( r->result < 0 ) { - hl_call2(void,c,int,hx_errno(r->result),vdynamic *,NULL); + hl_call2(void,c,int,hl_uv_errno(r->result),vdynamic *,NULL); } else { uv_dir_t *dir = r->ptr; varray *entries = hl_alloc_array(&hlt_dyn, r->result); @@ -1726,7 +2003,7 @@ static vdynamic *alloc_stat_dyn( const uv_stat_t *stat ) { static void on_fs_stat( uv_fs_t *r ) { UV_GET_CLOSURE(c,r,0,"No callback in fs stat request"); if( r->result < 0 ) { - hl_call2(void,c,int,hx_errno(r->result),vdynamic *,NULL); + hl_call2(void,c,int,hl_uv_errno(r->result),vdynamic *,NULL); } else { hl_call2(void,c,int,0,vdynamic *,alloc_stat_dyn(&r->statbuf)); } @@ -1763,7 +2040,7 @@ static void on_fs_statfs( uv_fs_t *r ) { UV_GET_CLOSURE(c,r,0,"No callback in statfs request"); events_data *ev = UV_DATA(r); if( r->result < 0 ) { - hl_call2(void,c,int,hx_errno(r->result),vdynamic *,NULL); + hl_call2(void,c,int,hl_uv_errno(r->result),vdynamic *,NULL); } else { uv_statfs_t *stat = r->ptr; vdynamic *obj = (vdynamic *)hl_alloc_dynobj(); @@ -2008,7 +2285,7 @@ DEFINE_PRIM(_HANDLE, fs_event_init_wrap, _LOOP); static void on_fs_event( uv_fs_event_t *h, const char *filename, int events, int status ) { UV_GET_CLOSURE(c,h,0,"No callback in fs_event handle"); if( status < 0 ) { - hl_call3(void,c,int,errno_uv2hx(status),vbyte *,NULL,varray *,NULL); + hl_call3(void,c,int,errno_uv2hl(status),vbyte *,NULL,varray *,NULL); } else { int size = (0 != (UV_RENAME & events)) + (0 != (UV_CHANGE & events)); varray *a_events = hl_alloc_array(&hlt_i32, size); @@ -2292,7 +2569,7 @@ DEFINE_PRIM(_DYN, gettimeofday_wrap, _NO_ARG); static void on_random( uv_random_t* r, int status, void* buf, size_t buflen ) { UV_GET_CLOSURE(c,r,0,"No callback in random request"); - hl_call1(void,c,int,errno_uv2hx(status)); + hl_call1(void,c,int,errno_uv2hl(status)); } HL_PRIM void HL_NAME(random_wrap)( uv_loop_t *loop, vbyte *buf, int length, int flags, vclosure *c ) { @@ -2403,4 +2680,4 @@ HL_PRIM int HL_NAME(version_hex)() { } DEFINE_PRIM(_I32, version_hex, _NO_ARG); - +#include "uv_generated.c" \ No newline at end of file diff --git a/libs/uv/uv_generated.c b/libs/uv/uv_generated.c index da3205814..d88ef5d02 100644 --- a/libs/uv/uv_generated.c +++ b/libs/uv/uv_generated.c @@ -5,258 +5,258 @@ DEFINE_PRIM(_I32, async_init_with_cb, _LOOP _ASYNC); DEFINE_PRIM(_I32, async_send, _ASYNC); -DEFINE_PRIM(_I32, check_init, _LOOP _CHECK); +// DEFINE_PRIM(_I32, check_init, _LOOP _CHECK); -HL_PRIM int HL_NAME(check_start_with_cb)( uv_check_t* check ) { - return uv_check_start(check, on_uv_check_cb); -} -DEFINE_PRIM(_I32, check_start_with_cb, _CHECK); +// HL_PRIM int HL_NAME(check_start_with_cb)( uv_check_t* check ) { +// return uv_check_start(check, on_uv_check_cb); +// } +// DEFINE_PRIM(_I32, check_start_with_cb, _CHECK); -DEFINE_PRIM(_I32, check_stop, _CHECK); +// DEFINE_PRIM(_I32, check_stop, _CHECK); -HL_PRIM int HL_NAME(getaddrinfo_with_cb)( uv_loop_t* loop, uv_getaddrinfo_t* req, const char* node, const char* service, const struct addrinfo* hints ) { - return uv_getaddrinfo(loop, req, on_uv_getaddrinfo_cb, node, service, hints); -} -DEFINE_PRIM(_I32, getaddrinfo_with_cb, _LOOP _GETADDRINFO _BYTES _BYTES _REF(_ADDRINFO)); +// HL_PRIM int HL_NAME(getaddrinfo_with_cb)( uv_loop_t* loop, uv_getaddrinfo_t* req, const char* node, const char* service, const struct addrinfo* hints ) { +// return uv_getaddrinfo(loop, req, on_uv_getaddrinfo_cb, node, service, hints); +// } +// DEFINE_PRIM(_I32, getaddrinfo_with_cb, _LOOP _GETADDRINFO _BYTES _BYTES _REF(_ADDRINFO)); -DEFINE_PRIM(_VOID, freeaddrinfo, _REF(_ADDRINFO)); +// DEFINE_PRIM(_VOID, freeaddrinfo, _REF(_ADDRINFO)); -HL_PRIM int HL_NAME(getnameinfo_with_cb)( uv_loop_t* loop, uv_getnameinfo_t* req, const struct sockaddr* addr, int flags ) { - return uv_getnameinfo(loop, req, on_uv_getnameinfo_cb, addr, flags); -} -DEFINE_PRIM(_I32, getnameinfo_with_cb, _LOOP _GETNAMEINFO _REF(_SOCKADDR) _I32); +// HL_PRIM int HL_NAME(getnameinfo_with_cb)( uv_loop_t* loop, uv_getnameinfo_t* req, const struct sockaddr* addr, int flags ) { +// return uv_getnameinfo(loop, req, on_uv_getnameinfo_cb, addr, flags); +// } +// DEFINE_PRIM(_I32, getnameinfo_with_cb, _LOOP _GETNAMEINFO _REF(_SOCKADDR) _I32); DEFINE_PRIM(_BYTES, strerror, _I32); -DEFINE_PRIM(_BYTES, strerror_r, _I32 _BYTES _U64); +// DEFINE_PRIM(_BYTES, strerror_r, _I32 _BYTES _U64); DEFINE_PRIM(_BYTES, err_name, _I32); -DEFINE_PRIM(_BYTES, err_name_r, _I32 _BYTES _U64); - -DEFINE_PRIM(_I32, translate_sys_error, _I32); - -DEFINE_PRIM(_VOID, fs_req_cleanup, _FS); - -HL_PRIM int HL_NAME(fs_close_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { - return uv_fs_close(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_close_with_cb, _LOOP _FS _FILE _BOOL); - -HL_PRIM int HL_NAME(fs_open_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, bool use_uv_fs_cb ) { - return uv_fs_open(loop, req, path, flags, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_open_with_cb, _LOOP _FS _BYTES _I32 _I32 _BOOL); - -HL_PRIM int HL_NAME(fs_read_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, bool use_uv_fs_cb ) { - return uv_fs_read(loop, req, file, bufs[], nbufs, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_read_with_cb, _LOOP _FS _FILE _BUF _U32 _I64 _BOOL); - -HL_PRIM int HL_NAME(fs_unlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { - return uv_fs_unlink(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_unlink_with_cb, _LOOP _FS _BYTES _BOOL); - -HL_PRIM int HL_NAME(fs_write_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, bool use_uv_fs_cb ) { - return uv_fs_write(loop, req, file, bufs[], nbufs, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_write_with_cb, _LOOP _FS _FILE _BUF _U32 _I64 _BOOL); - -HL_PRIM int HL_NAME(fs_mkdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { - return uv_fs_mkdir(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_mkdir_with_cb, _LOOP _FS _BYTES _I32 _BOOL); - -HL_PRIM int HL_NAME(fs_mkdtemp_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* tpl, bool use_uv_fs_cb ) { - return uv_fs_mkdtemp(loop, req, tpl, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_mkdtemp_with_cb, _LOOP _FS _BYTES _BOOL); - -HL_PRIM int HL_NAME(fs_mkstemp_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* tpl, bool use_uv_fs_cb ) { - return uv_fs_mkstemp(loop, req, tpl, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_mkstemp_with_cb, _LOOP _FS _BYTES _BOOL); - -HL_PRIM int HL_NAME(fs_rmdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { - return uv_fs_rmdir(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_rmdir_with_cb, _LOOP _FS _BYTES _BOOL); - -HL_PRIM int HL_NAME(fs_opendir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { - return uv_fs_opendir(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_opendir_with_cb, _LOOP _FS _BYTES _BOOL); - -HL_PRIM int HL_NAME(fs_closedir_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, bool use_uv_fs_cb ) { - return uv_fs_closedir(loop, req, dir, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_closedir_with_cb, _LOOP _FS _DIR _BOOL); - -HL_PRIM int HL_NAME(fs_readdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, bool use_uv_fs_cb ) { - return uv_fs_readdir(loop, req, dir, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_readdir_with_cb, _LOOP _FS _DIR _BOOL); - -HL_PRIM int HL_NAME(fs_scandir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, bool use_uv_fs_cb ) { - return uv_fs_scandir(loop, req, path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_scandir_with_cb, _LOOP _FS _BYTES _I32 _BOOL); - -DEFINE_PRIM(_I32, fs_scandir_next, _FS _DIRENT); - -HL_PRIM int HL_NAME(fs_stat_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { - return uv_fs_stat(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_stat_with_cb, _LOOP _FS _BYTES _BOOL); - -HL_PRIM int HL_NAME(fs_fstat_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { - return uv_fs_fstat(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_fstat_with_cb, _LOOP _FS _FILE _BOOL); - -HL_PRIM int HL_NAME(fs_lstat_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { - return uv_fs_lstat(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_lstat_with_cb, _LOOP _FS _BYTES _BOOL); - -HL_PRIM int HL_NAME(fs_statfs_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { - return uv_fs_statfs(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_statfs_with_cb, _LOOP _FS _BYTES _BOOL); - -HL_PRIM int HL_NAME(fs_rename_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, bool use_uv_fs_cb ) { - return uv_fs_rename(loop, req, path, new_path, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_rename_with_cb, _LOOP _FS _BYTES _BYTES _BOOL); - -HL_PRIM int HL_NAME(fs_fsync_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { - return uv_fs_fsync(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_fsync_with_cb, _LOOP _FS _FILE _BOOL); +// DEFINE_PRIM(_BYTES, err_name_r, _I32 _BYTES _U64); + +// DEFINE_PRIM(_I32, translate_sys_error, _I32); + +// DEFINE_PRIM(_VOID, fs_req_cleanup, _FS); + +// HL_PRIM int HL_NAME(fs_close_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { +// return uv_fs_close(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_close_with_cb, _LOOP _FS _FILE _BOOL); + +// HL_PRIM int HL_NAME(fs_open_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, bool use_uv_fs_cb ) { +// return uv_fs_open(loop, req, path, flags, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_open_with_cb, _LOOP _FS _BYTES _I32 _I32 _BOOL); + +// HL_PRIM int HL_NAME(fs_read_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, bool use_uv_fs_cb ) { +// return uv_fs_read(loop, req, file, bufs[], nbufs, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_read_with_cb, _LOOP _FS _FILE _BUF _U32 _I64 _BOOL); + +// HL_PRIM int HL_NAME(fs_unlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { +// return uv_fs_unlink(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_unlink_with_cb, _LOOP _FS _BYTES _BOOL); + +// HL_PRIM int HL_NAME(fs_write_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, bool use_uv_fs_cb ) { +// return uv_fs_write(loop, req, file, bufs[], nbufs, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_write_with_cb, _LOOP _FS _FILE _BUF _U32 _I64 _BOOL); + +// HL_PRIM int HL_NAME(fs_mkdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { +// return uv_fs_mkdir(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_mkdir_with_cb, _LOOP _FS _BYTES _I32 _BOOL); + +// HL_PRIM int HL_NAME(fs_mkdtemp_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* tpl, bool use_uv_fs_cb ) { +// return uv_fs_mkdtemp(loop, req, tpl, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_mkdtemp_with_cb, _LOOP _FS _BYTES _BOOL); + +// HL_PRIM int HL_NAME(fs_mkstemp_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* tpl, bool use_uv_fs_cb ) { +// return uv_fs_mkstemp(loop, req, tpl, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_mkstemp_with_cb, _LOOP _FS _BYTES _BOOL); + +// HL_PRIM int HL_NAME(fs_rmdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { +// return uv_fs_rmdir(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_rmdir_with_cb, _LOOP _FS _BYTES _BOOL); + +// HL_PRIM int HL_NAME(fs_opendir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { +// return uv_fs_opendir(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_opendir_with_cb, _LOOP _FS _BYTES _BOOL); + +// HL_PRIM int HL_NAME(fs_closedir_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, bool use_uv_fs_cb ) { +// return uv_fs_closedir(loop, req, dir, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_closedir_with_cb, _LOOP _FS _DIR _BOOL); + +// HL_PRIM int HL_NAME(fs_readdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, bool use_uv_fs_cb ) { +// return uv_fs_readdir(loop, req, dir, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_readdir_with_cb, _LOOP _FS _DIR _BOOL); + +// HL_PRIM int HL_NAME(fs_scandir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, bool use_uv_fs_cb ) { +// return uv_fs_scandir(loop, req, path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_scandir_with_cb, _LOOP _FS _BYTES _I32 _BOOL); + +// DEFINE_PRIM(_I32, fs_scandir_next, _FS _DIRENT); + +// HL_PRIM int HL_NAME(fs_stat_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { +// return uv_fs_stat(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_stat_with_cb, _LOOP _FS _BYTES _BOOL); + +// HL_PRIM int HL_NAME(fs_fstat_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { +// return uv_fs_fstat(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_fstat_with_cb, _LOOP _FS _FILE _BOOL); + +// HL_PRIM int HL_NAME(fs_lstat_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { +// return uv_fs_lstat(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_lstat_with_cb, _LOOP _FS _BYTES _BOOL); + +// HL_PRIM int HL_NAME(fs_statfs_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { +// return uv_fs_statfs(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_statfs_with_cb, _LOOP _FS _BYTES _BOOL); + +// HL_PRIM int HL_NAME(fs_rename_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, bool use_uv_fs_cb ) { +// return uv_fs_rename(loop, req, path, new_path, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_rename_with_cb, _LOOP _FS _BYTES _BYTES _BOOL); + +// HL_PRIM int HL_NAME(fs_fsync_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { +// return uv_fs_fsync(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_fsync_with_cb, _LOOP _FS _FILE _BOOL); + +// HL_PRIM int HL_NAME(fs_fdatasync_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { +// return uv_fs_fdatasync(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_fdatasync_with_cb, _LOOP _FS _FILE _BOOL); + +// HL_PRIM int HL_NAME(fs_ftruncate_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, int64_t offset, bool use_uv_fs_cb ) { +// return uv_fs_ftruncate(loop, req, file, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_ftruncate_with_cb, _LOOP _FS _FILE _I64 _BOOL); + +// HL_PRIM int HL_NAME(fs_copyfile_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, bool use_uv_fs_cb ) { +// return uv_fs_copyfile(loop, req, path, new_path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_copyfile_with_cb, _LOOP _FS _BYTES _BYTES _I32 _BOOL); + +// HL_PRIM int HL_NAME(fs_sendfile_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file out_fd, uv_file in_fd, int64_t in_offset, size_t length, bool use_uv_fs_cb ) { +// return uv_fs_sendfile(loop, req, out_fd, in_fd, in_offset, length, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_sendfile_with_cb, _LOOP _FS _FILE _FILE _I64 _U64 _BOOL); + +// HL_PRIM int HL_NAME(fs_access_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { +// return uv_fs_access(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_access_with_cb, _LOOP _FS _BYTES _I32 _BOOL); -HL_PRIM int HL_NAME(fs_fdatasync_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { - return uv_fs_fdatasync(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_fdatasync_with_cb, _LOOP _FS _FILE _BOOL); +// HL_PRIM int HL_NAME(fs_chmod_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { +// return uv_fs_chmod(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_chmod_with_cb, _LOOP _FS _BYTES _I32 _BOOL); -HL_PRIM int HL_NAME(fs_ftruncate_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, int64_t offset, bool use_uv_fs_cb ) { - return uv_fs_ftruncate(loop, req, file, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_ftruncate_with_cb, _LOOP _FS _FILE _I64 _BOOL); +// HL_PRIM int HL_NAME(fs_fchmod_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, int mode, bool use_uv_fs_cb ) { +// return uv_fs_fchmod(loop, req, file, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_fchmod_with_cb, _LOOP _FS _FILE _I32 _BOOL); -HL_PRIM int HL_NAME(fs_copyfile_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, bool use_uv_fs_cb ) { - return uv_fs_copyfile(loop, req, path, new_path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_copyfile_with_cb, _LOOP _FS _BYTES _BYTES _I32 _BOOL); +// HL_PRIM int HL_NAME(fs_utime_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, bool use_uv_fs_cb ) { +// return uv_fs_utime(loop, req, path, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_utime_with_cb, _LOOP _FS _BYTES _F64 _F64 _BOOL); -HL_PRIM int HL_NAME(fs_sendfile_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file out_fd, uv_file in_fd, int64_t in_offset, size_t length, bool use_uv_fs_cb ) { - return uv_fs_sendfile(loop, req, out_fd, in_fd, in_offset, length, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_sendfile_with_cb, _LOOP _FS _FILE _FILE _I64 _U64 _BOOL); +// HL_PRIM int HL_NAME(fs_futime_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime, double mtime, bool use_uv_fs_cb ) { +// return uv_fs_futime(loop, req, file, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_futime_with_cb, _LOOP _FS _FILE _F64 _F64 _BOOL); -HL_PRIM int HL_NAME(fs_access_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { - return uv_fs_access(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_access_with_cb, _LOOP _FS _BYTES _I32 _BOOL); +// HL_PRIM int HL_NAME(fs_lutime_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, bool use_uv_fs_cb ) { +// return uv_fs_lutime(loop, req, path, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_lutime_with_cb, _LOOP _FS _BYTES _F64 _F64 _BOOL); -HL_PRIM int HL_NAME(fs_chmod_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { - return uv_fs_chmod(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_chmod_with_cb, _LOOP _FS _BYTES _I32 _BOOL); +// HL_PRIM int HL_NAME(fs_link_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, bool use_uv_fs_cb ) { +// return uv_fs_link(loop, req, path, new_path, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_link_with_cb, _LOOP _FS _BYTES _BYTES _BOOL); -HL_PRIM int HL_NAME(fs_fchmod_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, int mode, bool use_uv_fs_cb ) { - return uv_fs_fchmod(loop, req, file, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_fchmod_with_cb, _LOOP _FS _FILE _I32 _BOOL); +// HL_PRIM int HL_NAME(fs_symlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, bool use_uv_fs_cb ) { +// return uv_fs_symlink(loop, req, path, new_path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_symlink_with_cb, _LOOP _FS _BYTES _BYTES _I32 _BOOL); -HL_PRIM int HL_NAME(fs_utime_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, bool use_uv_fs_cb ) { - return uv_fs_utime(loop, req, path, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_utime_with_cb, _LOOP _FS _BYTES _F64 _F64 _BOOL); +// HL_PRIM int HL_NAME(fs_readlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { +// return uv_fs_readlink(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_readlink_with_cb, _LOOP _FS _BYTES _BOOL); -HL_PRIM int HL_NAME(fs_futime_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime, double mtime, bool use_uv_fs_cb ) { - return uv_fs_futime(loop, req, file, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_futime_with_cb, _LOOP _FS _FILE _F64 _F64 _BOOL); - -HL_PRIM int HL_NAME(fs_lutime_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, bool use_uv_fs_cb ) { - return uv_fs_lutime(loop, req, path, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_lutime_with_cb, _LOOP _FS _BYTES _F64 _F64 _BOOL); - -HL_PRIM int HL_NAME(fs_link_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, bool use_uv_fs_cb ) { - return uv_fs_link(loop, req, path, new_path, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_link_with_cb, _LOOP _FS _BYTES _BYTES _BOOL); - -HL_PRIM int HL_NAME(fs_symlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, bool use_uv_fs_cb ) { - return uv_fs_symlink(loop, req, path, new_path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_symlink_with_cb, _LOOP _FS _BYTES _BYTES _I32 _BOOL); - -HL_PRIM int HL_NAME(fs_readlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { - return uv_fs_readlink(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_readlink_with_cb, _LOOP _FS _BYTES _BOOL); - -HL_PRIM int HL_NAME(fs_realpath_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { - return uv_fs_realpath(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_realpath_with_cb, _LOOP _FS _BYTES _BOOL); +// HL_PRIM int HL_NAME(fs_realpath_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { +// return uv_fs_realpath(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_realpath_with_cb, _LOOP _FS _BYTES _BOOL); -HL_PRIM int HL_NAME(fs_chown_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { - return uv_fs_chown(loop, req, path, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_chown_with_cb, _LOOP _FS _BYTES _UID_T _GID_T _BOOL); +// HL_PRIM int HL_NAME(fs_chown_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { +// return uv_fs_chown(loop, req, path, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_chown_with_cb, _LOOP _FS _BYTES _UID_T _GID_T _BOOL); -HL_PRIM int HL_NAME(fs_fchown_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { - return uv_fs_fchown(loop, req, file, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_fchown_with_cb, _LOOP _FS _FILE _UID_T _GID_T _BOOL); +// HL_PRIM int HL_NAME(fs_fchown_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { +// return uv_fs_fchown(loop, req, file, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_fchown_with_cb, _LOOP _FS _FILE _UID_T _GID_T _BOOL); -HL_PRIM int HL_NAME(fs_lchown_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { - return uv_fs_lchown(loop, req, path, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_lchown_with_cb, _LOOP _FS _BYTES _UID_T _GID_T _BOOL); +// HL_PRIM int HL_NAME(fs_lchown_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { +// return uv_fs_lchown(loop, req, path, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_lchown_with_cb, _LOOP _FS _BYTES _UID_T _GID_T _BOOL); -DEFINE_PRIM(_FS_TYPE, fs_get_type, _FS); +// DEFINE_PRIM(_FS_TYPE, fs_get_type, _FS); -DEFINE_PRIM(_I64, fs_get_result, _FS); +// DEFINE_PRIM(_I64, fs_get_result, _FS); -DEFINE_PRIM(_I32, fs_get_system_error, _FS); +// DEFINE_PRIM(_I32, fs_get_system_error, _FS); -DEFINE_PRIM(_DYN, fs_get_ptr, _FS); +// DEFINE_PRIM(_POINTER, fs_get_ptr, _FS); -DEFINE_PRIM(_BYTES, fs_get_path, _FS); +// DEFINE_PRIM(_BYTES, fs_get_path, _FS); -DEFINE_PRIM(_STAT, fs_get_statbuf, _FS); +// DEFINE_PRIM(_STAT, fs_get_statbuf, _FS); -DEFINE_PRIM(_OS_FD_T, get_osfhandle, _I32); +// DEFINE_PRIM(_OS_FD_T, get_osfhandle, _I32); -DEFINE_PRIM(_I32, open_osfhandle, _OS_FD_T); +// DEFINE_PRIM(_I32, open_osfhandle, _OS_FD_T); -DEFINE_PRIM(_I32, fs_event_init, _LOOP _FS_EVENT); +// DEFINE_PRIM(_I32, fs_event_init, _LOOP _FS_EVENT); -HL_PRIM int HL_NAME(fs_event_start_with_cb)( uv_fs_event_t* handle, const char* path, unsigned int flags ) { - return uv_fs_event_start(handle, on_uv_fs_event_cb, path, flags); -} -DEFINE_PRIM(_I32, fs_event_start_with_cb, _FS_EVENT _BYTES _U32); +// HL_PRIM int HL_NAME(fs_event_start_with_cb)( uv_fs_event_t* handle, const char* path, unsigned int flags ) { +// return uv_fs_event_start(handle, on_uv_fs_event_cb, path, flags); +// } +// DEFINE_PRIM(_I32, fs_event_start_with_cb, _FS_EVENT _BYTES _U32); -DEFINE_PRIM(_I32, fs_event_stop, _FS_EVENT); +// DEFINE_PRIM(_I32, fs_event_stop, _FS_EVENT); -DEFINE_PRIM(_I32, fs_event_getpath, _FS_EVENT _BYTES _REF(_I64)); +// DEFINE_PRIM(_I32, fs_event_getpath, _FS_EVENT _BYTES _REF(_I64)); -DEFINE_PRIM(_I32, fs_poll_init, _LOOP _FS_POLL); +// DEFINE_PRIM(_I32, fs_poll_init, _LOOP _FS_POLL); -HL_PRIM int HL_NAME(fs_poll_start_with_cb)( uv_fs_poll_t* handle, const char* path, unsigned int interval ) { - return uv_fs_poll_start(handle, on_uv_fs_poll_cb, path, interval); -} -DEFINE_PRIM(_I32, fs_poll_start_with_cb, _FS_POLL _BYTES _U32); +// HL_PRIM int HL_NAME(fs_poll_start_with_cb)( uv_fs_poll_t* handle, const char* path, unsigned int interval ) { +// return uv_fs_poll_start(handle, on_uv_fs_poll_cb, path, interval); +// } +// DEFINE_PRIM(_I32, fs_poll_start_with_cb, _FS_POLL _BYTES _U32); -DEFINE_PRIM(_I32, fs_poll_stop, _FS_POLL); +// DEFINE_PRIM(_I32, fs_poll_stop, _FS_POLL); -DEFINE_PRIM(_I32, fs_poll_getpath, _FS_POLL _BYTES _REF(_I64)); +// DEFINE_PRIM(_I32, fs_poll_getpath, _FS_POLL _BYTES _REF(_I64)); DEFINE_PRIM(_I32, is_active, _HANDLE); @@ -273,32 +273,32 @@ DEFINE_PRIM(_VOID, unref, _HANDLE); DEFINE_PRIM(_I32, has_ref, _HANDLE); -DEFINE_PRIM(_U64, handle_size, _HANDLE_TYPE); +// DEFINE_PRIM(_U64, handle_size, _HANDLE_TYPE); -DEFINE_PRIM(_I32, send_buffer_size, _HANDLE _REF(_I32)); +// DEFINE_PRIM(_I32, send_buffer_size, _HANDLE _REF(_I32)); -DEFINE_PRIM(_I32, recv_buffer_size, _HANDLE _REF(_I32)); +// DEFINE_PRIM(_I32, recv_buffer_size, _HANDLE _REF(_I32)); -DEFINE_PRIM(_I32, fileno, _HANDLE _OS_FD); +// DEFINE_PRIM(_I32, fileno, _HANDLE _OS_FD); -DEFINE_PRIM(_LOOP, handle_get_loop, _HANDLE); +// DEFINE_PRIM(_LOOP, handle_get_loop, _HANDLE); -DEFINE_PRIM(_DYN, handle_get_data, _HANDLE); +DEFINE_PRIM(_POINTER, handle_get_data, _HANDLE); -DEFINE_PRIM(_DYN, handle_set_data, _HANDLE _DYN); +DEFINE_PRIM(_POINTER, handle_set_data, _HANDLE _POINTER); -DEFINE_PRIM(_HANDLE_TYPE, handle_get_type, _HANDLE); +// DEFINE_PRIM(_HANDLE_TYPE, handle_get_type, _HANDLE); -DEFINE_PRIM(_BYTES, handle_type_name, _HANDLE_TYPE); +// DEFINE_PRIM(_BYTES, handle_type_name, _HANDLE_TYPE); -DEFINE_PRIM(_I32, idle_init, _LOOP _IDLE); +// DEFINE_PRIM(_I32, idle_init, _LOOP _IDLE); -HL_PRIM int HL_NAME(idle_start_with_cb)( uv_idle_t* idle ) { - return uv_idle_start(idle, on_uv_idle_cb); -} -DEFINE_PRIM(_I32, idle_start_with_cb, _IDLE); +// HL_PRIM int HL_NAME(idle_start_with_cb)( uv_idle_t* idle ) { +// return uv_idle_start(idle, on_uv_idle_cb); +// } +// DEFINE_PRIM(_I32, idle_start_with_cb, _IDLE); -DEFINE_PRIM(_I32, idle_stop, _IDLE); +// DEFINE_PRIM(_I32, idle_stop, _IDLE); DEFINE_PRIM(_I32, loop_init, _LOOP); @@ -312,272 +312,272 @@ DEFINE_PRIM(_I32, loop_alive, _LOOP); DEFINE_PRIM(_VOID, stop, _LOOP); -DEFINE_PRIM(_U64, loop_size, _VOID); +// DEFINE_PRIM(_U64, loop_size, _VOID); -DEFINE_PRIM(_I32, backend_fd, _LOOP); +// DEFINE_PRIM(_I32, backend_fd, _LOOP); -DEFINE_PRIM(_I32, backend_timeout, _LOOP); +// DEFINE_PRIM(_I32, backend_timeout, _LOOP); -DEFINE_PRIM(_I64, now, _LOOP); +// DEFINE_PRIM(_I64, now, _LOOP); -DEFINE_PRIM(_VOID, update_time, _LOOP); +// DEFINE_PRIM(_VOID, update_time, _LOOP); -HL_PRIM void HL_NAME(walk_with_cb)( uv_loop_t* loop, void* arg ) { - uv_walk(loop, on_uv_walk_cb, arg); -} -DEFINE_PRIM(_VOID, walk_with_cb, _LOOP _DYN); +// HL_PRIM void HL_NAME(walk_with_cb)( uv_loop_t* loop, void* arg ) { +// uv_walk(loop, on_uv_walk_cb, arg); +// } +// DEFINE_PRIM(_VOID, walk_with_cb, _LOOP _POINTER); -DEFINE_PRIM(_I32, loop_fork, _LOOP); +// DEFINE_PRIM(_I32, loop_fork, _LOOP); -DEFINE_PRIM(_DYN, loop_get_data, _LOOP); +// DEFINE_PRIM(_POINTER, loop_get_data, _LOOP); -DEFINE_PRIM(_DYN, loop_set_data, _LOOP _DYN); +// DEFINE_PRIM(_POINTER, loop_set_data, _LOOP _POINTER); -DEFINE_PRIM(_I64, metrics_idle_time, _LOOP); +// DEFINE_PRIM(_I64, metrics_idle_time, _LOOP); -DEFINE_PRIM(_HANDLE_TYPE, guess_handle, _FILE); +// DEFINE_PRIM(_HANDLE_TYPE, guess_handle, _FILE); -DEFINE_PRIM(_I32, replace_allocator, _MALLOC_FUNC _REALLOC_FUNC _CALLOC_FUNC _FREE_FUNC); +// DEFINE_PRIM(_I32, replace_allocator, _MALLOC_FUNC _REALLOC_FUNC _CALLOC_FUNC _FREE_FUNC); -DEFINE_PRIM(_VOID, library_shutdown, _VOID); +// DEFINE_PRIM(_VOID, library_shutdown, _VOID); -DEFINE_PRIM(_BUF, buf_init, _BYTES _U32); +// DEFINE_PRIM(_BUF, buf_init, _BYTES _U32); -DEFINE_PRIM(_REF(_BYTES), setup_args, _I32 _REF(_BYTES)); +// DEFINE_PRIM(_REF(_BYTES), setup_args, _I32 _REF(_BYTES)); -DEFINE_PRIM(_I32, get_process_title, _BYTES _U64); +// DEFINE_PRIM(_I32, get_process_title, _BYTES _U64); -DEFINE_PRIM(_I32, set_process_title, _BYTES); +// DEFINE_PRIM(_I32, set_process_title, _BYTES); -DEFINE_PRIM(_I32, resident_set_memory, _REF(_I64)); +// DEFINE_PRIM(_I32, resident_set_memory, _REF(_I64)); -DEFINE_PRIM(_I32, uptime, _REF(_F64)); +// DEFINE_PRIM(_I32, uptime, _REF(_F64)); -DEFINE_PRIM(_I32, getrusage, _RUSAGE); +// DEFINE_PRIM(_I32, getrusage, _RUSAGE); -DEFINE_PRIM(_I32, os_getpid, _VOID); +// DEFINE_PRIM(_I32, os_getpid, _VOID); -DEFINE_PRIM(_I32, os_getppid, _VOID); +// DEFINE_PRIM(_I32, os_getppid, _VOID); -DEFINE_PRIM(_I32, cpu_info, _REF(_CPU_INFO) _REF(_I32)); +// DEFINE_PRIM(_I32, cpu_info, _REF(_CPU_INFO) _REF(_I32)); -DEFINE_PRIM(_VOID, free_cpu_info, _CPU_INFO _I32); +// DEFINE_PRIM(_VOID, free_cpu_info, _CPU_INFO _I32); -DEFINE_PRIM(_I32, interface_addresses, _REF(_INTERFACE_ADDRESS) _REF(_I32)); +// DEFINE_PRIM(_I32, interface_addresses, _REF(_INTERFACE_ADDRESS) _REF(_I32)); -DEFINE_PRIM(_VOID, free_interface_addresses, _INTERFACE_ADDRESS _I32); +// DEFINE_PRIM(_VOID, free_interface_addresses, _INTERFACE_ADDRESS _I32); -DEFINE_PRIM(_VOID, loadavg, _F64); +// DEFINE_PRIM(_VOID, loadavg, _F64); -DEFINE_PRIM(_I32, ip4_addr, _BYTES _I32 _REF(_SOCKADDR_IN)); +// DEFINE_PRIM(_I32, ip4_addr, _BYTES _I32 _REF(_SOCKADDR_IN)); -DEFINE_PRIM(_I32, ip6_addr, _BYTES _I32 _REF(_SOCKADDR_IN6)); +// DEFINE_PRIM(_I32, ip6_addr, _BYTES _I32 _REF(_SOCKADDR_IN6)); -DEFINE_PRIM(_I32, ip4_name, _REF(_SOCKADDR_IN) _BYTES _U64); +// DEFINE_PRIM(_I32, ip4_name, _REF(_SOCKADDR_IN) _BYTES _U64); -DEFINE_PRIM(_I32, ip6_name, _REF(_SOCKADDR_IN6) _BYTES _U64); +// DEFINE_PRIM(_I32, ip6_name, _REF(_SOCKADDR_IN6) _BYTES _U64); -DEFINE_PRIM(_I32, inet_ntop, _I32 _DYN _BYTES _U64); +// DEFINE_PRIM(_I32, inet_ntop, _I32 _POINTER _BYTES _U64); -DEFINE_PRIM(_I32, inet_pton, _I32 _BYTES _DYN); +// DEFINE_PRIM(_I32, inet_pton, _I32 _BYTES _POINTER); -DEFINE_PRIM(_I32, if_indextoname, _U32 _BYTES _REF(_I64)); +// DEFINE_PRIM(_I32, if_indextoname, _U32 _BYTES _REF(_I64)); -DEFINE_PRIM(_I32, if_indextoiid, _U32 _BYTES _REF(_I64)); +// DEFINE_PRIM(_I32, if_indextoiid, _U32 _BYTES _REF(_I64)); -DEFINE_PRIM(_I32, exepath, _BYTES _REF(_I64)); +// DEFINE_PRIM(_I32, exepath, _BYTES _REF(_I64)); -DEFINE_PRIM(_I32, cwd, _BYTES _REF(_I64)); +// DEFINE_PRIM(_I32, cwd, _BYTES _REF(_I64)); -DEFINE_PRIM(_I32, chdir, _BYTES); +// DEFINE_PRIM(_I32, chdir, _BYTES); -DEFINE_PRIM(_I32, os_homedir, _BYTES _REF(_I64)); +// DEFINE_PRIM(_I32, os_homedir, _BYTES _REF(_I64)); -DEFINE_PRIM(_I32, os_tmpdir, _BYTES _REF(_I64)); +// DEFINE_PRIM(_I32, os_tmpdir, _BYTES _REF(_I64)); -DEFINE_PRIM(_I32, os_get_passwd, _PASSWD); +// DEFINE_PRIM(_I32, os_get_passwd, _PASSWD); -DEFINE_PRIM(_VOID, os_free_passwd, _PASSWD); +// DEFINE_PRIM(_VOID, os_free_passwd, _PASSWD); -DEFINE_PRIM(_I64, get_free_memory, _VOID); +// DEFINE_PRIM(_I64, get_free_memory, _VOID); -DEFINE_PRIM(_I64, get_total_memory, _VOID); +// DEFINE_PRIM(_I64, get_total_memory, _VOID); -DEFINE_PRIM(_I64, get_constrained_memory, _VOID); +// DEFINE_PRIM(_I64, get_constrained_memory, _VOID); -DEFINE_PRIM(_I64, hrtime, _VOID); +// DEFINE_PRIM(_I64, hrtime, _VOID); -DEFINE_PRIM(_VOID, print_all_handles, _LOOP _FILE); +// DEFINE_PRIM(_VOID, print_all_handles, _LOOP _FILE); -DEFINE_PRIM(_VOID, print_active_handles, _LOOP _FILE); +// DEFINE_PRIM(_VOID, print_active_handles, _LOOP _FILE); -DEFINE_PRIM(_I32, os_environ, _REF(_ENV_ITEM) _REF(_I32)); +// DEFINE_PRIM(_I32, os_environ, _REF(_ENV_ITEM) _REF(_I32)); -DEFINE_PRIM(_VOID, os_free_environ, _ENV_ITEM _I32); +// DEFINE_PRIM(_VOID, os_free_environ, _ENV_ITEM _I32); -DEFINE_PRIM(_I32, os_getenv, _BYTES _BYTES _REF(_I64)); +// DEFINE_PRIM(_I32, os_getenv, _BYTES _BYTES _REF(_I64)); -DEFINE_PRIM(_I32, os_setenv, _BYTES _BYTES); +// DEFINE_PRIM(_I32, os_setenv, _BYTES _BYTES); -DEFINE_PRIM(_I32, os_unsetenv, _BYTES); +// DEFINE_PRIM(_I32, os_unsetenv, _BYTES); -DEFINE_PRIM(_I32, os_gethostname, _BYTES _REF(_I64)); +// DEFINE_PRIM(_I32, os_gethostname, _BYTES _REF(_I64)); -DEFINE_PRIM(_I32, os_getpriority, _I32 _REF(_I32)); +// DEFINE_PRIM(_I32, os_getpriority, _I32 _REF(_I32)); -DEFINE_PRIM(_I32, os_setpriority, _I32 _I32); +// DEFINE_PRIM(_I32, os_setpriority, _I32 _I32); -DEFINE_PRIM(_I32, os_uname, _UTSNAME); +// DEFINE_PRIM(_I32, os_uname, _UTSNAME); -DEFINE_PRIM(_I32, gettimeofday, _TIMEVAL64); +// DEFINE_PRIM(_I32, gettimeofday, _TIMEVAL64); -HL_PRIM int HL_NAME(random_with_cb)( uv_loop_t* loop, uv_random_t* req, void* buf, size_t buflen, unsigned int flags ) { - return uv_random(loop, req, buf, buflen, flags, on_uv_random_cb); -} -DEFINE_PRIM(_I32, random_with_cb, _LOOP _RANDOM _DYN _U64 _U32); +// HL_PRIM int HL_NAME(random_with_cb)( uv_loop_t* loop, uv_random_t* req, void* buf, size_t buflen, unsigned int flags ) { +// return uv_random(loop, req, buf, buflen, flags, on_uv_random_cb); +// } +// DEFINE_PRIM(_I32, random_with_cb, _LOOP _RANDOM _POINTER _U64 _U32); -DEFINE_PRIM(_VOID, sleep, _U32); +// DEFINE_PRIM(_VOID, sleep, _U32); -DEFINE_PRIM(_I32, pipe_init, _LOOP _PIPE _I32); +// DEFINE_PRIM(_I32, pipe_init, _LOOP _PIPE _I32); -DEFINE_PRIM(_I32, pipe_open, _PIPE _FILE); +// DEFINE_PRIM(_I32, pipe_open, _PIPE _FILE); -DEFINE_PRIM(_I32, pipe_bind, _PIPE _BYTES); +// DEFINE_PRIM(_I32, pipe_bind, _PIPE _BYTES); -HL_PRIM void HL_NAME(pipe_connect_with_cb)( uv_connect_t* req, uv_pipe_t* handle, const char* name ) { - uv_pipe_connect(req, handle, name, on_uv_connect_cb); -} -DEFINE_PRIM(_VOID, pipe_connect_with_cb, _CONNECT _PIPE _BYTES); +// HL_PRIM void HL_NAME(pipe_connect_with_cb)( uv_connect_t* req, uv_pipe_t* handle, const char* name ) { +// uv_pipe_connect(req, handle, name, on_uv_connect_cb); +// } +// DEFINE_PRIM(_VOID, pipe_connect_with_cb, _CONNECT _PIPE _BYTES); -DEFINE_PRIM(_I32, pipe_getsockname, _PIPE _BYTES _REF(_I64)); +// DEFINE_PRIM(_I32, pipe_getsockname, _PIPE _BYTES _REF(_I64)); -DEFINE_PRIM(_I32, pipe_getpeername, _PIPE _BYTES _REF(_I64)); +// DEFINE_PRIM(_I32, pipe_getpeername, _PIPE _BYTES _REF(_I64)); -DEFINE_PRIM(_VOID, pipe_pending_instances, _PIPE _I32); +// DEFINE_PRIM(_VOID, pipe_pending_instances, _PIPE _I32); -DEFINE_PRIM(_I32, pipe_pending_count, _PIPE); +// DEFINE_PRIM(_I32, pipe_pending_count, _PIPE); -DEFINE_PRIM(_HANDLE_TYPE, pipe_pending_type, _PIPE); +// DEFINE_PRIM(_HANDLE_TYPE, pipe_pending_type, _PIPE); -DEFINE_PRIM(_I32, pipe_chmod, _PIPE _I32); +// DEFINE_PRIM(_I32, pipe_chmod, _PIPE _I32); -DEFINE_PRIM(_I32, pipe, _FILE _I32 _I32); +// DEFINE_PRIM(_I32, pipe, _FILE _I32 _I32); -DEFINE_PRIM(_I32, prepare_init, _LOOP _PREPARE); +// DEFINE_PRIM(_I32, prepare_init, _LOOP _PREPARE); -HL_PRIM int HL_NAME(prepare_start_with_cb)( uv_prepare_t* prepare ) { - return uv_prepare_start(prepare, on_uv_prepare_cb); -} -DEFINE_PRIM(_I32, prepare_start_with_cb, _PREPARE); +// HL_PRIM int HL_NAME(prepare_start_with_cb)( uv_prepare_t* prepare ) { +// return uv_prepare_start(prepare, on_uv_prepare_cb); +// } +// DEFINE_PRIM(_I32, prepare_start_with_cb, _PREPARE); -DEFINE_PRIM(_I32, prepare_stop, _PREPARE); +// DEFINE_PRIM(_I32, prepare_stop, _PREPARE); -DEFINE_PRIM(_VOID, disable_stdio_inheritance, _VOID); +// DEFINE_PRIM(_VOID, disable_stdio_inheritance, _VOID); -DEFINE_PRIM(_I32, spawn, _LOOP _PROCESS _PROCESS_OPTIONS); +// DEFINE_PRIM(_I32, spawn, _LOOP _PROCESS _PROCESS_OPTIONS); -DEFINE_PRIM(_I32, process_kill, _PROCESS _I32); +// DEFINE_PRIM(_I32, process_kill, _PROCESS _I32); -DEFINE_PRIM(_I32, kill, _I32 _I32); +// DEFINE_PRIM(_I32, kill, _I32 _I32); -DEFINE_PRIM(_I32, process_get_pid, _PROCESS); +// DEFINE_PRIM(_I32, process_get_pid, _PROCESS); -DEFINE_PRIM(_I32, cancel, _REQ); +// DEFINE_PRIM(_I32, cancel, _REQ); -DEFINE_PRIM(_U64, req_size, _REQ_TYPE); +// DEFINE_PRIM(_U64, req_size, _REQ_TYPE); -DEFINE_PRIM(_DYN, req_get_data, _REQ); +// DEFINE_PRIM(_POINTER, req_get_data, _REQ); -DEFINE_PRIM(_DYN, req_set_data, _REQ _DYN); +// DEFINE_PRIM(_POINTER, req_set_data, _REQ _POINTER); -DEFINE_PRIM(_REQ_TYPE, req_get_type, _REQ); +// DEFINE_PRIM(_REQ_TYPE, req_get_type, _REQ); -DEFINE_PRIM(_BYTES, req_type_name, _REQ_TYPE); +// DEFINE_PRIM(_BYTES, req_type_name, _REQ_TYPE); -DEFINE_PRIM(_I32, signal_init, _LOOP _SIGNAL); +// DEFINE_PRIM(_I32, signal_init, _LOOP _SIGNAL); -HL_PRIM int HL_NAME(signal_start_with_cb)( uv_signal_t* signal, int signum ) { - return uv_signal_start(signal, on_uv_signal_cb, signum); -} -DEFINE_PRIM(_I32, signal_start_with_cb, _SIGNAL _I32); +// HL_PRIM int HL_NAME(signal_start_with_cb)( uv_signal_t* signal, int signum ) { +// return uv_signal_start(signal, on_uv_signal_cb, signum); +// } +// DEFINE_PRIM(_I32, signal_start_with_cb, _SIGNAL _I32); -HL_PRIM int HL_NAME(signal_start_oneshot_with_cb)( uv_signal_t* signal, int signum ) { - return uv_signal_start_oneshot(signal, on_uv_signal_cb, signum); -} -DEFINE_PRIM(_I32, signal_start_oneshot_with_cb, _SIGNAL _I32); +// HL_PRIM int HL_NAME(signal_start_oneshot_with_cb)( uv_signal_t* signal, int signum ) { +// return uv_signal_start_oneshot(signal, on_uv_signal_cb, signum); +// } +// DEFINE_PRIM(_I32, signal_start_oneshot_with_cb, _SIGNAL _I32); -DEFINE_PRIM(_I32, signal_stop, _SIGNAL); +// DEFINE_PRIM(_I32, signal_stop, _SIGNAL); -HL_PRIM int HL_NAME(shutdown_with_cb)( uv_shutdown_t* req, uv_stream_t* handle ) { - return uv_shutdown(req, handle, on_uv_shutdown_cb); -} -DEFINE_PRIM(_I32, shutdown_with_cb, _SHUTDOWN _STREAM); +// HL_PRIM int HL_NAME(shutdown_with_cb)( uv_shutdown_t* req, uv_stream_t* handle ) { +// return uv_shutdown(req, handle, on_uv_shutdown_cb); +// } +// DEFINE_PRIM(_I32, shutdown_with_cb, _SHUTDOWN _STREAM); -HL_PRIM int HL_NAME(listen_with_cb)( uv_stream_t* stream, int backlog ) { - return uv_listen(stream, backlog, on_uv_connection_cb); -} -DEFINE_PRIM(_I32, listen_with_cb, _STREAM _I32); +// HL_PRIM int HL_NAME(listen_with_cb)( uv_stream_t* stream, int backlog ) { +// return uv_listen(stream, backlog, on_uv_connection_cb); +// } +// DEFINE_PRIM(_I32, listen_with_cb, _STREAM _I32); -DEFINE_PRIM(_I32, accept, _STREAM _STREAM); +// DEFINE_PRIM(_I32, accept, _STREAM _STREAM); -HL_PRIM int HL_NAME(read_start_with_cb)( uv_stream_t* stream ) { - return uv_read_start(stream, on_uv_alloc_cb, on_uv_read_cb); -} -DEFINE_PRIM(_I32, read_start_with_cb, _STREAM); +// HL_PRIM int HL_NAME(read_start_with_cb)( uv_stream_t* stream ) { +// return uv_read_start(stream, on_uv_alloc_cb, on_uv_read_cb); +// } +// DEFINE_PRIM(_I32, read_start_with_cb, _STREAM); -DEFINE_PRIM(_I32, read_stop, _STREAM); +// DEFINE_PRIM(_I32, read_stop, _STREAM); -HL_PRIM int HL_NAME(write_with_cb)( uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs ) { - return uv_write(req, handle, bufs[], nbufs, on_uv_write_cb); -} -DEFINE_PRIM(_I32, write_with_cb, _WRITE _STREAM _BUF _U32); +// HL_PRIM int HL_NAME(write_with_cb)( uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs ) { +// return uv_write(req, handle, bufs[], nbufs, on_uv_write_cb); +// } +// DEFINE_PRIM(_I32, write_with_cb, _WRITE _STREAM _BUF _U32); -HL_PRIM int HL_NAME(write2_with_cb)( uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_stream_t* send_handle ) { - return uv_write2(req, handle, bufs[], nbufs, send_handle, on_uv_write_cb); -} -DEFINE_PRIM(_I32, write2_with_cb, _WRITE _STREAM _BUF _U32 _STREAM); +// HL_PRIM int HL_NAME(write2_with_cb)( uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_stream_t* send_handle ) { +// return uv_write2(req, handle, bufs[], nbufs, send_handle, on_uv_write_cb); +// } +// DEFINE_PRIM(_I32, write2_with_cb, _WRITE _STREAM _BUF _U32 _STREAM); -DEFINE_PRIM(_I32, try_write, _STREAM _BUF _U32); +// DEFINE_PRIM(_I32, try_write, _STREAM _BUF _U32); -DEFINE_PRIM(_I32, try_write2, _STREAM _BUF _U32 _STREAM); +// DEFINE_PRIM(_I32, try_write2, _STREAM _BUF _U32 _STREAM); -DEFINE_PRIM(_I32, is_readable, _STREAM); +// DEFINE_PRIM(_I32, is_readable, _STREAM); -DEFINE_PRIM(_I32, is_writable, _STREAM); +// DEFINE_PRIM(_I32, is_writable, _STREAM); -DEFINE_PRIM(_I32, stream_set_blocking, _STREAM _I32); +// DEFINE_PRIM(_I32, stream_set_blocking, _STREAM _I32); -DEFINE_PRIM(_U64, stream_get_write_queue_size, _STREAM); +// DEFINE_PRIM(_U64, stream_get_write_queue_size, _STREAM); -DEFINE_PRIM(_I32, tcp_init, _LOOP _TCP); +// DEFINE_PRIM(_I32, tcp_init, _LOOP _TCP); -DEFINE_PRIM(_I32, tcp_init_ex, _LOOP _TCP _U32); +// DEFINE_PRIM(_I32, tcp_init_ex, _LOOP _TCP _U32); -DEFINE_PRIM(_I32, tcp_open, _TCP _OS_SOCK_T); +// DEFINE_PRIM(_I32, tcp_open, _TCP _OS_SOCK_T); -DEFINE_PRIM(_I32, tcp_nodelay, _TCP _I32); +// DEFINE_PRIM(_I32, tcp_nodelay, _TCP _I32); -DEFINE_PRIM(_I32, tcp_keepalive, _TCP _I32 _U32); +// DEFINE_PRIM(_I32, tcp_keepalive, _TCP _I32 _U32); -DEFINE_PRIM(_I32, tcp_simultaneous_accepts, _TCP _I32); +// DEFINE_PRIM(_I32, tcp_simultaneous_accepts, _TCP _I32); -DEFINE_PRIM(_I32, tcp_bind, _TCP _REF(_SOCKADDR) _U32); +// DEFINE_PRIM(_I32, tcp_bind, _TCP _REF(_SOCKADDR) _U32); -DEFINE_PRIM(_I32, tcp_getsockname, _TCP _REF(_SOCKADDR) _REF(_I32)); +// DEFINE_PRIM(_I32, tcp_getsockname, _TCP _REF(_SOCKADDR) _REF(_I32)); -DEFINE_PRIM(_I32, tcp_getpeername, _TCP _REF(_SOCKADDR) _REF(_I32)); +// DEFINE_PRIM(_I32, tcp_getpeername, _TCP _REF(_SOCKADDR) _REF(_I32)); -HL_PRIM int HL_NAME(tcp_connect_with_cb)( uv_connect_t* req, uv_tcp_t* handle, const struct sockaddr* addr ) { - return uv_tcp_connect(req, handle, addr, on_uv_connect_cb); -} -DEFINE_PRIM(_I32, tcp_connect_with_cb, _CONNECT _TCP _REF(_SOCKADDR)); +// HL_PRIM int HL_NAME(tcp_connect_with_cb)( uv_connect_t* req, uv_tcp_t* handle, const struct sockaddr* addr ) { +// return uv_tcp_connect(req, handle, addr, on_uv_connect_cb); +// } +// DEFINE_PRIM(_I32, tcp_connect_with_cb, _CONNECT _TCP _REF(_SOCKADDR)); -HL_PRIM int HL_NAME(tcp_close_reset_with_cb)( uv_tcp_t* handle ) { - return uv_tcp_close_reset(handle, on_uv_close_cb); -} -DEFINE_PRIM(_I32, tcp_close_reset_with_cb, _TCP); +// HL_PRIM int HL_NAME(tcp_close_reset_with_cb)( uv_tcp_t* handle ) { +// return uv_tcp_close_reset(handle, on_uv_close_cb); +// } +// DEFINE_PRIM(_I32, tcp_close_reset_with_cb, _TCP); -DEFINE_PRIM(_I32, socketpair, _I32 _I32 _OS_SOCK_T _I32 _I32); +// DEFINE_PRIM(_I32, socketpair, _I32 _I32 _OS_SOCK_T _I32 _I32); DEFINE_PRIM(_I32, timer_init, _LOOP _TIMER); @@ -596,67 +596,67 @@ DEFINE_PRIM(_I64, timer_get_repeat, _TIMER); DEFINE_PRIM(_I64, timer_get_due_in, _TIMER); -DEFINE_PRIM(_I32, tty_init, _LOOP _TTY _FILE _I32); +// DEFINE_PRIM(_I32, tty_init, _LOOP _TTY _FILE _I32); -DEFINE_PRIM(_I32, tty_set_mode, _TTY _TTY_MODE_T); +// DEFINE_PRIM(_I32, tty_set_mode, _TTY _TTY_MODE_T); -DEFINE_PRIM(_I32, tty_reset_mode, _VOID); +// DEFINE_PRIM(_I32, tty_reset_mode, _VOID); -DEFINE_PRIM(_I32, tty_get_winsize, _TTY _REF(_I32) _REF(_I32)); +// DEFINE_PRIM(_I32, tty_get_winsize, _TTY _REF(_I32) _REF(_I32)); -DEFINE_PRIM(_VOID, tty_set_vterm_state, _TTY_VTERMSTATE_T); +// DEFINE_PRIM(_VOID, tty_set_vterm_state, _TTY_VTERMSTATE_T); -DEFINE_PRIM(_I32, tty_get_vterm_state, _TTY_VTERMSTATE); +// DEFINE_PRIM(_I32, tty_get_vterm_state, _TTY_VTERMSTATE); -DEFINE_PRIM(_I32, udp_init, _LOOP _UDP); +// DEFINE_PRIM(_I32, udp_init, _LOOP _UDP); -DEFINE_PRIM(_I32, udp_init_ex, _LOOP _UDP _U32); +// DEFINE_PRIM(_I32, udp_init_ex, _LOOP _UDP _U32); -DEFINE_PRIM(_I32, udp_open, _UDP _OS_SOCK_T); +// DEFINE_PRIM(_I32, udp_open, _UDP _OS_SOCK_T); -DEFINE_PRIM(_I32, udp_bind, _UDP _REF(_SOCKADDR) _U32); +// DEFINE_PRIM(_I32, udp_bind, _UDP _REF(_SOCKADDR) _U32); -DEFINE_PRIM(_I32, udp_connect, _UDP _REF(_SOCKADDR)); +// DEFINE_PRIM(_I32, udp_connect, _UDP _REF(_SOCKADDR)); -DEFINE_PRIM(_I32, udp_getpeername, _UDP _REF(_SOCKADDR) _REF(_I32)); +// DEFINE_PRIM(_I32, udp_getpeername, _UDP _REF(_SOCKADDR) _REF(_I32)); -DEFINE_PRIM(_I32, udp_getsockname, _UDP _REF(_SOCKADDR) _REF(_I32)); +// DEFINE_PRIM(_I32, udp_getsockname, _UDP _REF(_SOCKADDR) _REF(_I32)); -DEFINE_PRIM(_I32, udp_set_membership, _UDP _BYTES _BYTES _MEMBERSHIP); +// DEFINE_PRIM(_I32, udp_set_membership, _UDP _BYTES _BYTES _MEMBERSHIP); -DEFINE_PRIM(_I32, udp_set_source_membership, _UDP _BYTES _BYTES _BYTES _MEMBERSHIP); +// DEFINE_PRIM(_I32, udp_set_source_membership, _UDP _BYTES _BYTES _BYTES _MEMBERSHIP); -DEFINE_PRIM(_I32, udp_set_multicast_loop, _UDP _I32); +// DEFINE_PRIM(_I32, udp_set_multicast_loop, _UDP _I32); -DEFINE_PRIM(_I32, udp_set_multicast_ttl, _UDP _I32); +// DEFINE_PRIM(_I32, udp_set_multicast_ttl, _UDP _I32); -DEFINE_PRIM(_I32, udp_set_multicast_interface, _UDP _BYTES); +// DEFINE_PRIM(_I32, udp_set_multicast_interface, _UDP _BYTES); -DEFINE_PRIM(_I32, udp_set_broadcast, _UDP _I32); +// DEFINE_PRIM(_I32, udp_set_broadcast, _UDP _I32); -DEFINE_PRIM(_I32, udp_set_ttl, _UDP _I32); +// DEFINE_PRIM(_I32, udp_set_ttl, _UDP _I32); -HL_PRIM int HL_NAME(udp_send_with_cb)( uv_udp_send_t* req, uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr ) { - return uv_udp_send(req, handle, bufs[], nbufs, addr, on_uv_udp_send_cb); -} -DEFINE_PRIM(_I32, udp_send_with_cb, _UDP_SEND _UDP _BUF _U32 _REF(_SOCKADDR)); +// HL_PRIM int HL_NAME(udp_send_with_cb)( uv_udp_send_t* req, uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr ) { +// return uv_udp_send(req, handle, bufs[], nbufs, addr, on_uv_udp_send_cb); +// } +// DEFINE_PRIM(_I32, udp_send_with_cb, _UDP_SEND _UDP _BUF _U32 _REF(_SOCKADDR)); -DEFINE_PRIM(_I32, udp_try_send, _UDP _BUF _U32 _REF(_SOCKADDR)); +// DEFINE_PRIM(_I32, udp_try_send, _UDP _BUF _U32 _REF(_SOCKADDR)); -HL_PRIM int HL_NAME(udp_recv_start_with_cb)( uv_udp_t* handle ) { - return uv_udp_recv_start(handle, on_uv_alloc_cb, on_uv_udp_recv_cb); -} -DEFINE_PRIM(_I32, udp_recv_start_with_cb, _UDP); +// HL_PRIM int HL_NAME(udp_recv_start_with_cb)( uv_udp_t* handle ) { +// return uv_udp_recv_start(handle, on_uv_alloc_cb, on_uv_udp_recv_cb); +// } +// DEFINE_PRIM(_I32, udp_recv_start_with_cb, _UDP); -DEFINE_PRIM(_I32, udp_using_recvmmsg, _UDP); +// DEFINE_PRIM(_I32, udp_using_recvmmsg, _UDP); -DEFINE_PRIM(_I32, udp_recv_stop, _UDP); +// DEFINE_PRIM(_I32, udp_recv_stop, _UDP); -DEFINE_PRIM(_U64, udp_get_send_queue_size, _UDP); +// DEFINE_PRIM(_U64, udp_get_send_queue_size, _UDP); -DEFINE_PRIM(_U64, udp_get_send_queue_count, _UDP); +// DEFINE_PRIM(_U64, udp_get_send_queue_count, _UDP); -DEFINE_PRIM(_U32, version, _VOID); +// DEFINE_PRIM(_U32, version, _VOID); -DEFINE_PRIM(_BYTES, version_string, _VOID); +// DEFINE_PRIM(_BYTES, version_string, _VOID); diff --git a/other/uvgenerator/UVGenerator.hx b/other/uvgenerator/UVGenerator.hx index e0285bd6c..fcc2f432e 100644 --- a/other/uvgenerator/UVGenerator.hx +++ b/other/uvgenerator/UVGenerator.hx @@ -112,7 +112,7 @@ class UVGenerator { case 'int64_t': '_I64'; case 'uint64_t': '_I64'; case 'char*': '_BYTES'; - case 'void*': '_DYN'; + case 'void*': '_POINTER'; case 'void': '_VOID'; case 'unsigned int': '_U32'; case 'ssize_t': '_I64'; From e2295588095d6d814cac68529cf2b7540b7339a0 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Wed, 18 Aug 2021 12:28:22 +0300 Subject: [PATCH 059/117] also generate hx bindings --- libs/uv/uv.c | 2 + libs/uv/uv_generated.c | 828 +++++++++++++++---------------- other/uvgenerator/UV.hx | 292 +++++++++++ other/uvgenerator/UV.hx.header | 50 ++ other/uvgenerator/UVGenerator.hx | 133 ++++- 5 files changed, 865 insertions(+), 440 deletions(-) create mode 100644 other/uvgenerator/UV.hx create mode 100644 other/uvgenerator/UV.hx.header diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 736eb12c1..d37be70aa 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -294,6 +294,8 @@ DEFINE_PRIM(_I32, translate_to_uv_error, _I32); #define _CHECK _HANDLE #define _TIMER _HANDLE #define _GETADDRINFO _ABSTRACT(uv_getaddrinfo_t) +#define _HANDLE_TYPE _I32 +#define _OS_FD _ABSTRACT(uv_os_fd) #define UV_ALLOC(t) ((t*)malloc(sizeof(t))) #define DATA(t,h) ((t)h->data) diff --git a/libs/uv/uv_generated.c b/libs/uv/uv_generated.c index d88ef5d02..4384f9508 100644 --- a/libs/uv/uv_generated.c +++ b/libs/uv/uv_generated.c @@ -5,258 +5,258 @@ DEFINE_PRIM(_I32, async_init_with_cb, _LOOP _ASYNC); DEFINE_PRIM(_I32, async_send, _ASYNC); -// DEFINE_PRIM(_I32, check_init, _LOOP _CHECK); +DEFINE_PRIM(_I32, check_init, _LOOP _CHECK); -// HL_PRIM int HL_NAME(check_start_with_cb)( uv_check_t* check ) { -// return uv_check_start(check, on_uv_check_cb); -// } -// DEFINE_PRIM(_I32, check_start_with_cb, _CHECK); +HL_PRIM int HL_NAME(check_start_with_cb)( uv_check_t* check ) { + return uv_check_start(check, on_uv_check_cb); +} +DEFINE_PRIM(_I32, check_start_with_cb, _CHECK); -// DEFINE_PRIM(_I32, check_stop, _CHECK); +DEFINE_PRIM(_I32, check_stop, _CHECK); -// HL_PRIM int HL_NAME(getaddrinfo_with_cb)( uv_loop_t* loop, uv_getaddrinfo_t* req, const char* node, const char* service, const struct addrinfo* hints ) { -// return uv_getaddrinfo(loop, req, on_uv_getaddrinfo_cb, node, service, hints); -// } -// DEFINE_PRIM(_I32, getaddrinfo_with_cb, _LOOP _GETADDRINFO _BYTES _BYTES _REF(_ADDRINFO)); +HL_PRIM int HL_NAME(getaddrinfo_with_cb)( uv_loop_t* loop, uv_getaddrinfo_t* req, const char* node, const char* service, const struct addrinfo* hints ) { + return uv_getaddrinfo(loop, req, on_uv_getaddrinfo_cb, node, service, hints); +} +DEFINE_PRIM(_I32, getaddrinfo_with_cb, _LOOP _GETADDRINFO _BYTES _BYTES _REF(_ADDRINFO)); -// DEFINE_PRIM(_VOID, freeaddrinfo, _REF(_ADDRINFO)); +DEFINE_PRIM(_VOID, freeaddrinfo, _REF(_ADDRINFO)); -// HL_PRIM int HL_NAME(getnameinfo_with_cb)( uv_loop_t* loop, uv_getnameinfo_t* req, const struct sockaddr* addr, int flags ) { -// return uv_getnameinfo(loop, req, on_uv_getnameinfo_cb, addr, flags); -// } -// DEFINE_PRIM(_I32, getnameinfo_with_cb, _LOOP _GETNAMEINFO _REF(_SOCKADDR) _I32); +HL_PRIM int HL_NAME(getnameinfo_with_cb)( uv_loop_t* loop, uv_getnameinfo_t* req, const struct sockaddr* addr, int flags ) { + return uv_getnameinfo(loop, req, on_uv_getnameinfo_cb, addr, flags); +} +DEFINE_PRIM(_I32, getnameinfo_with_cb, _LOOP _GETNAMEINFO _REF(_SOCKADDR) _I32); DEFINE_PRIM(_BYTES, strerror, _I32); -// DEFINE_PRIM(_BYTES, strerror_r, _I32 _BYTES _U64); +DEFINE_PRIM(_BYTES, strerror_r, _I32 _BYTES _U64); DEFINE_PRIM(_BYTES, err_name, _I32); -// DEFINE_PRIM(_BYTES, err_name_r, _I32 _BYTES _U64); - -// DEFINE_PRIM(_I32, translate_sys_error, _I32); - -// DEFINE_PRIM(_VOID, fs_req_cleanup, _FS); - -// HL_PRIM int HL_NAME(fs_close_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { -// return uv_fs_close(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_close_with_cb, _LOOP _FS _FILE _BOOL); - -// HL_PRIM int HL_NAME(fs_open_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, bool use_uv_fs_cb ) { -// return uv_fs_open(loop, req, path, flags, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_open_with_cb, _LOOP _FS _BYTES _I32 _I32 _BOOL); - -// HL_PRIM int HL_NAME(fs_read_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, bool use_uv_fs_cb ) { -// return uv_fs_read(loop, req, file, bufs[], nbufs, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_read_with_cb, _LOOP _FS _FILE _BUF _U32 _I64 _BOOL); - -// HL_PRIM int HL_NAME(fs_unlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { -// return uv_fs_unlink(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_unlink_with_cb, _LOOP _FS _BYTES _BOOL); - -// HL_PRIM int HL_NAME(fs_write_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, bool use_uv_fs_cb ) { -// return uv_fs_write(loop, req, file, bufs[], nbufs, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_write_with_cb, _LOOP _FS _FILE _BUF _U32 _I64 _BOOL); - -// HL_PRIM int HL_NAME(fs_mkdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { -// return uv_fs_mkdir(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_mkdir_with_cb, _LOOP _FS _BYTES _I32 _BOOL); - -// HL_PRIM int HL_NAME(fs_mkdtemp_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* tpl, bool use_uv_fs_cb ) { -// return uv_fs_mkdtemp(loop, req, tpl, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_mkdtemp_with_cb, _LOOP _FS _BYTES _BOOL); - -// HL_PRIM int HL_NAME(fs_mkstemp_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* tpl, bool use_uv_fs_cb ) { -// return uv_fs_mkstemp(loop, req, tpl, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_mkstemp_with_cb, _LOOP _FS _BYTES _BOOL); - -// HL_PRIM int HL_NAME(fs_rmdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { -// return uv_fs_rmdir(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_rmdir_with_cb, _LOOP _FS _BYTES _BOOL); - -// HL_PRIM int HL_NAME(fs_opendir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { -// return uv_fs_opendir(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_opendir_with_cb, _LOOP _FS _BYTES _BOOL); - -// HL_PRIM int HL_NAME(fs_closedir_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, bool use_uv_fs_cb ) { -// return uv_fs_closedir(loop, req, dir, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_closedir_with_cb, _LOOP _FS _DIR _BOOL); - -// HL_PRIM int HL_NAME(fs_readdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, bool use_uv_fs_cb ) { -// return uv_fs_readdir(loop, req, dir, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_readdir_with_cb, _LOOP _FS _DIR _BOOL); - -// HL_PRIM int HL_NAME(fs_scandir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, bool use_uv_fs_cb ) { -// return uv_fs_scandir(loop, req, path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_scandir_with_cb, _LOOP _FS _BYTES _I32 _BOOL); - -// DEFINE_PRIM(_I32, fs_scandir_next, _FS _DIRENT); - -// HL_PRIM int HL_NAME(fs_stat_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { -// return uv_fs_stat(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_stat_with_cb, _LOOP _FS _BYTES _BOOL); - -// HL_PRIM int HL_NAME(fs_fstat_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { -// return uv_fs_fstat(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_fstat_with_cb, _LOOP _FS _FILE _BOOL); - -// HL_PRIM int HL_NAME(fs_lstat_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { -// return uv_fs_lstat(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_lstat_with_cb, _LOOP _FS _BYTES _BOOL); - -// HL_PRIM int HL_NAME(fs_statfs_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { -// return uv_fs_statfs(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_statfs_with_cb, _LOOP _FS _BYTES _BOOL); - -// HL_PRIM int HL_NAME(fs_rename_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, bool use_uv_fs_cb ) { -// return uv_fs_rename(loop, req, path, new_path, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_rename_with_cb, _LOOP _FS _BYTES _BYTES _BOOL); - -// HL_PRIM int HL_NAME(fs_fsync_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { -// return uv_fs_fsync(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_fsync_with_cb, _LOOP _FS _FILE _BOOL); - -// HL_PRIM int HL_NAME(fs_fdatasync_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { -// return uv_fs_fdatasync(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_fdatasync_with_cb, _LOOP _FS _FILE _BOOL); - -// HL_PRIM int HL_NAME(fs_ftruncate_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, int64_t offset, bool use_uv_fs_cb ) { -// return uv_fs_ftruncate(loop, req, file, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_ftruncate_with_cb, _LOOP _FS _FILE _I64 _BOOL); - -// HL_PRIM int HL_NAME(fs_copyfile_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, bool use_uv_fs_cb ) { -// return uv_fs_copyfile(loop, req, path, new_path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_copyfile_with_cb, _LOOP _FS _BYTES _BYTES _I32 _BOOL); - -// HL_PRIM int HL_NAME(fs_sendfile_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file out_fd, uv_file in_fd, int64_t in_offset, size_t length, bool use_uv_fs_cb ) { -// return uv_fs_sendfile(loop, req, out_fd, in_fd, in_offset, length, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_sendfile_with_cb, _LOOP _FS _FILE _FILE _I64 _U64 _BOOL); - -// HL_PRIM int HL_NAME(fs_access_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { -// return uv_fs_access(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_access_with_cb, _LOOP _FS _BYTES _I32 _BOOL); +DEFINE_PRIM(_BYTES, err_name_r, _I32 _BYTES _U64); + +DEFINE_PRIM(_I32, translate_sys_error, _I32); + +DEFINE_PRIM(_VOID, fs_req_cleanup, _FS); + +HL_PRIM int HL_NAME(fs_close_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { + return uv_fs_close(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_close_with_cb, _LOOP _FS _FILE _BOOL); + +HL_PRIM int HL_NAME(fs_open_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, bool use_uv_fs_cb ) { + return uv_fs_open(loop, req, path, flags, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_open_with_cb, _LOOP _FS _BYTES _I32 _I32 _BOOL); + +HL_PRIM int HL_NAME(fs_read_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, bool use_uv_fs_cb ) { + return uv_fs_read(loop, req, file, bufs[], nbufs, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_read_with_cb, _LOOP _FS _FILE _BUF _U32 _I64 _BOOL); + +HL_PRIM int HL_NAME(fs_unlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { + return uv_fs_unlink(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_unlink_with_cb, _LOOP _FS _BYTES _BOOL); + +HL_PRIM int HL_NAME(fs_write_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, bool use_uv_fs_cb ) { + return uv_fs_write(loop, req, file, bufs[], nbufs, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_write_with_cb, _LOOP _FS _FILE _BUF _U32 _I64 _BOOL); + +HL_PRIM int HL_NAME(fs_mkdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { + return uv_fs_mkdir(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_mkdir_with_cb, _LOOP _FS _BYTES _I32 _BOOL); + +HL_PRIM int HL_NAME(fs_mkdtemp_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* tpl, bool use_uv_fs_cb ) { + return uv_fs_mkdtemp(loop, req, tpl, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_mkdtemp_with_cb, _LOOP _FS _BYTES _BOOL); + +HL_PRIM int HL_NAME(fs_mkstemp_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* tpl, bool use_uv_fs_cb ) { + return uv_fs_mkstemp(loop, req, tpl, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_mkstemp_with_cb, _LOOP _FS _BYTES _BOOL); + +HL_PRIM int HL_NAME(fs_rmdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { + return uv_fs_rmdir(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_rmdir_with_cb, _LOOP _FS _BYTES _BOOL); + +HL_PRIM int HL_NAME(fs_opendir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { + return uv_fs_opendir(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_opendir_with_cb, _LOOP _FS _BYTES _BOOL); + +HL_PRIM int HL_NAME(fs_closedir_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, bool use_uv_fs_cb ) { + return uv_fs_closedir(loop, req, dir, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_closedir_with_cb, _LOOP _FS _DIR _BOOL); + +HL_PRIM int HL_NAME(fs_readdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, bool use_uv_fs_cb ) { + return uv_fs_readdir(loop, req, dir, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_readdir_with_cb, _LOOP _FS _DIR _BOOL); + +HL_PRIM int HL_NAME(fs_scandir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, bool use_uv_fs_cb ) { + return uv_fs_scandir(loop, req, path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_scandir_with_cb, _LOOP _FS _BYTES _I32 _BOOL); + +DEFINE_PRIM(_I32, fs_scandir_next, _FS _DIRENT); + +HL_PRIM int HL_NAME(fs_stat_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { + return uv_fs_stat(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_stat_with_cb, _LOOP _FS _BYTES _BOOL); + +HL_PRIM int HL_NAME(fs_fstat_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { + return uv_fs_fstat(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_fstat_with_cb, _LOOP _FS _FILE _BOOL); + +HL_PRIM int HL_NAME(fs_lstat_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { + return uv_fs_lstat(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_lstat_with_cb, _LOOP _FS _BYTES _BOOL); + +HL_PRIM int HL_NAME(fs_statfs_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { + return uv_fs_statfs(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_statfs_with_cb, _LOOP _FS _BYTES _BOOL); + +HL_PRIM int HL_NAME(fs_rename_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, bool use_uv_fs_cb ) { + return uv_fs_rename(loop, req, path, new_path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_rename_with_cb, _LOOP _FS _BYTES _BYTES _BOOL); + +HL_PRIM int HL_NAME(fs_fsync_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { + return uv_fs_fsync(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_fsync_with_cb, _LOOP _FS _FILE _BOOL); -// HL_PRIM int HL_NAME(fs_chmod_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { -// return uv_fs_chmod(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_chmod_with_cb, _LOOP _FS _BYTES _I32 _BOOL); +HL_PRIM int HL_NAME(fs_fdatasync_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { + return uv_fs_fdatasync(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_fdatasync_with_cb, _LOOP _FS _FILE _BOOL); -// HL_PRIM int HL_NAME(fs_fchmod_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, int mode, bool use_uv_fs_cb ) { -// return uv_fs_fchmod(loop, req, file, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_fchmod_with_cb, _LOOP _FS _FILE _I32 _BOOL); +HL_PRIM int HL_NAME(fs_ftruncate_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, int64_t offset, bool use_uv_fs_cb ) { + return uv_fs_ftruncate(loop, req, file, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_ftruncate_with_cb, _LOOP _FS _FILE _I64 _BOOL); -// HL_PRIM int HL_NAME(fs_utime_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, bool use_uv_fs_cb ) { -// return uv_fs_utime(loop, req, path, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_utime_with_cb, _LOOP _FS _BYTES _F64 _F64 _BOOL); +HL_PRIM int HL_NAME(fs_copyfile_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, bool use_uv_fs_cb ) { + return uv_fs_copyfile(loop, req, path, new_path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_copyfile_with_cb, _LOOP _FS _BYTES _BYTES _I32 _BOOL); -// HL_PRIM int HL_NAME(fs_futime_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime, double mtime, bool use_uv_fs_cb ) { -// return uv_fs_futime(loop, req, file, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_futime_with_cb, _LOOP _FS _FILE _F64 _F64 _BOOL); +HL_PRIM int HL_NAME(fs_sendfile_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file out_fd, uv_file in_fd, int64_t in_offset, size_t length, bool use_uv_fs_cb ) { + return uv_fs_sendfile(loop, req, out_fd, in_fd, in_offset, length, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_sendfile_with_cb, _LOOP _FS _FILE _FILE _I64 _U64 _BOOL); -// HL_PRIM int HL_NAME(fs_lutime_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, bool use_uv_fs_cb ) { -// return uv_fs_lutime(loop, req, path, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_lutime_with_cb, _LOOP _FS _BYTES _F64 _F64 _BOOL); +HL_PRIM int HL_NAME(fs_access_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { + return uv_fs_access(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_access_with_cb, _LOOP _FS _BYTES _I32 _BOOL); -// HL_PRIM int HL_NAME(fs_link_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, bool use_uv_fs_cb ) { -// return uv_fs_link(loop, req, path, new_path, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_link_with_cb, _LOOP _FS _BYTES _BYTES _BOOL); +HL_PRIM int HL_NAME(fs_chmod_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { + return uv_fs_chmod(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_chmod_with_cb, _LOOP _FS _BYTES _I32 _BOOL); -// HL_PRIM int HL_NAME(fs_symlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, bool use_uv_fs_cb ) { -// return uv_fs_symlink(loop, req, path, new_path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_symlink_with_cb, _LOOP _FS _BYTES _BYTES _I32 _BOOL); +HL_PRIM int HL_NAME(fs_fchmod_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, int mode, bool use_uv_fs_cb ) { + return uv_fs_fchmod(loop, req, file, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_fchmod_with_cb, _LOOP _FS _FILE _I32 _BOOL); -// HL_PRIM int HL_NAME(fs_readlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { -// return uv_fs_readlink(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_readlink_with_cb, _LOOP _FS _BYTES _BOOL); +HL_PRIM int HL_NAME(fs_utime_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, bool use_uv_fs_cb ) { + return uv_fs_utime(loop, req, path, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_utime_with_cb, _LOOP _FS _BYTES _F64 _F64 _BOOL); -// HL_PRIM int HL_NAME(fs_realpath_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { -// return uv_fs_realpath(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_realpath_with_cb, _LOOP _FS _BYTES _BOOL); +HL_PRIM int HL_NAME(fs_futime_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime, double mtime, bool use_uv_fs_cb ) { + return uv_fs_futime(loop, req, file, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_futime_with_cb, _LOOP _FS _FILE _F64 _F64 _BOOL); + +HL_PRIM int HL_NAME(fs_lutime_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, bool use_uv_fs_cb ) { + return uv_fs_lutime(loop, req, path, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_lutime_with_cb, _LOOP _FS _BYTES _F64 _F64 _BOOL); + +HL_PRIM int HL_NAME(fs_link_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, bool use_uv_fs_cb ) { + return uv_fs_link(loop, req, path, new_path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_link_with_cb, _LOOP _FS _BYTES _BYTES _BOOL); + +HL_PRIM int HL_NAME(fs_symlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, bool use_uv_fs_cb ) { + return uv_fs_symlink(loop, req, path, new_path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_symlink_with_cb, _LOOP _FS _BYTES _BYTES _I32 _BOOL); + +HL_PRIM int HL_NAME(fs_readlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { + return uv_fs_readlink(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_readlink_with_cb, _LOOP _FS _BYTES _BOOL); + +HL_PRIM int HL_NAME(fs_realpath_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { + return uv_fs_realpath(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_realpath_with_cb, _LOOP _FS _BYTES _BOOL); -// HL_PRIM int HL_NAME(fs_chown_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { -// return uv_fs_chown(loop, req, path, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_chown_with_cb, _LOOP _FS _BYTES _UID_T _GID_T _BOOL); +HL_PRIM int HL_NAME(fs_chown_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { + return uv_fs_chown(loop, req, path, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_chown_with_cb, _LOOP _FS _BYTES _UID_T _GID_T _BOOL); -// HL_PRIM int HL_NAME(fs_fchown_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { -// return uv_fs_fchown(loop, req, file, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_fchown_with_cb, _LOOP _FS _FILE _UID_T _GID_T _BOOL); +HL_PRIM int HL_NAME(fs_fchown_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { + return uv_fs_fchown(loop, req, file, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_fchown_with_cb, _LOOP _FS _FILE _UID_T _GID_T _BOOL); -// HL_PRIM int HL_NAME(fs_lchown_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { -// return uv_fs_lchown(loop, req, path, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_lchown_with_cb, _LOOP _FS _BYTES _UID_T _GID_T _BOOL); +HL_PRIM int HL_NAME(fs_lchown_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { + return uv_fs_lchown(loop, req, path, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_lchown_with_cb, _LOOP _FS _BYTES _UID_T _GID_T _BOOL); -// DEFINE_PRIM(_FS_TYPE, fs_get_type, _FS); +DEFINE_PRIM(_FS_TYPE, fs_get_type, _FS); -// DEFINE_PRIM(_I64, fs_get_result, _FS); +DEFINE_PRIM(_I64, fs_get_result, _FS); -// DEFINE_PRIM(_I32, fs_get_system_error, _FS); +DEFINE_PRIM(_I32, fs_get_system_error, _FS); -// DEFINE_PRIM(_POINTER, fs_get_ptr, _FS); +DEFINE_PRIM(_POINTER, fs_get_ptr, _FS); -// DEFINE_PRIM(_BYTES, fs_get_path, _FS); +DEFINE_PRIM(_BYTES, fs_get_path, _FS); -// DEFINE_PRIM(_STAT, fs_get_statbuf, _FS); +DEFINE_PRIM(_STAT, fs_get_statbuf, _FS); -// DEFINE_PRIM(_OS_FD_T, get_osfhandle, _I32); +DEFINE_PRIM(_OS_FD_T, get_osfhandle, _I32); -// DEFINE_PRIM(_I32, open_osfhandle, _OS_FD_T); +DEFINE_PRIM(_I32, open_osfhandle, _OS_FD_T); -// DEFINE_PRIM(_I32, fs_event_init, _LOOP _FS_EVENT); +DEFINE_PRIM(_I32, fs_event_init, _LOOP _FS_EVENT); -// HL_PRIM int HL_NAME(fs_event_start_with_cb)( uv_fs_event_t* handle, const char* path, unsigned int flags ) { -// return uv_fs_event_start(handle, on_uv_fs_event_cb, path, flags); -// } -// DEFINE_PRIM(_I32, fs_event_start_with_cb, _FS_EVENT _BYTES _U32); +HL_PRIM int HL_NAME(fs_event_start_with_cb)( uv_fs_event_t* handle, const char* path, unsigned int flags ) { + return uv_fs_event_start(handle, on_uv_fs_event_cb, path, flags); +} +DEFINE_PRIM(_I32, fs_event_start_with_cb, _FS_EVENT _BYTES _U32); -// DEFINE_PRIM(_I32, fs_event_stop, _FS_EVENT); +DEFINE_PRIM(_I32, fs_event_stop, _FS_EVENT); -// DEFINE_PRIM(_I32, fs_event_getpath, _FS_EVENT _BYTES _REF(_I64)); +DEFINE_PRIM(_I32, fs_event_getpath, _FS_EVENT _BYTES _REF(_I64)); -// DEFINE_PRIM(_I32, fs_poll_init, _LOOP _FS_POLL); +DEFINE_PRIM(_I32, fs_poll_init, _LOOP _FS_POLL); -// HL_PRIM int HL_NAME(fs_poll_start_with_cb)( uv_fs_poll_t* handle, const char* path, unsigned int interval ) { -// return uv_fs_poll_start(handle, on_uv_fs_poll_cb, path, interval); -// } -// DEFINE_PRIM(_I32, fs_poll_start_with_cb, _FS_POLL _BYTES _U32); +HL_PRIM int HL_NAME(fs_poll_start_with_cb)( uv_fs_poll_t* handle, const char* path, unsigned int interval ) { + return uv_fs_poll_start(handle, on_uv_fs_poll_cb, path, interval); +} +DEFINE_PRIM(_I32, fs_poll_start_with_cb, _FS_POLL _BYTES _U32); -// DEFINE_PRIM(_I32, fs_poll_stop, _FS_POLL); +DEFINE_PRIM(_I32, fs_poll_stop, _FS_POLL); -// DEFINE_PRIM(_I32, fs_poll_getpath, _FS_POLL _BYTES _REF(_I64)); +DEFINE_PRIM(_I32, fs_poll_getpath, _FS_POLL _BYTES _REF(_I64)); DEFINE_PRIM(_I32, is_active, _HANDLE); @@ -273,32 +273,32 @@ DEFINE_PRIM(_VOID, unref, _HANDLE); DEFINE_PRIM(_I32, has_ref, _HANDLE); -// DEFINE_PRIM(_U64, handle_size, _HANDLE_TYPE); +DEFINE_PRIM(_U64, handle_size, _HANDLE_TYPE); -// DEFINE_PRIM(_I32, send_buffer_size, _HANDLE _REF(_I32)); +DEFINE_PRIM(_I32, send_buffer_size, _HANDLE _REF(_I32)); -// DEFINE_PRIM(_I32, recv_buffer_size, _HANDLE _REF(_I32)); +DEFINE_PRIM(_I32, recv_buffer_size, _HANDLE _REF(_I32)); -// DEFINE_PRIM(_I32, fileno, _HANDLE _OS_FD); +DEFINE_PRIM(_I32, fileno, _HANDLE _OS_FD); -// DEFINE_PRIM(_LOOP, handle_get_loop, _HANDLE); +DEFINE_PRIM(_LOOP, handle_get_loop, _HANDLE); DEFINE_PRIM(_POINTER, handle_get_data, _HANDLE); DEFINE_PRIM(_POINTER, handle_set_data, _HANDLE _POINTER); -// DEFINE_PRIM(_HANDLE_TYPE, handle_get_type, _HANDLE); +DEFINE_PRIM(_HANDLE_TYPE, handle_get_type, _HANDLE); -// DEFINE_PRIM(_BYTES, handle_type_name, _HANDLE_TYPE); +DEFINE_PRIM(_BYTES, handle_type_name, _HANDLE_TYPE); -// DEFINE_PRIM(_I32, idle_init, _LOOP _IDLE); +DEFINE_PRIM(_I32, idle_init, _LOOP _IDLE); -// HL_PRIM int HL_NAME(idle_start_with_cb)( uv_idle_t* idle ) { -// return uv_idle_start(idle, on_uv_idle_cb); -// } -// DEFINE_PRIM(_I32, idle_start_with_cb, _IDLE); +HL_PRIM int HL_NAME(idle_start_with_cb)( uv_idle_t* idle ) { + return uv_idle_start(idle, on_uv_idle_cb); +} +DEFINE_PRIM(_I32, idle_start_with_cb, _IDLE); -// DEFINE_PRIM(_I32, idle_stop, _IDLE); +DEFINE_PRIM(_I32, idle_stop, _IDLE); DEFINE_PRIM(_I32, loop_init, _LOOP); @@ -312,272 +312,272 @@ DEFINE_PRIM(_I32, loop_alive, _LOOP); DEFINE_PRIM(_VOID, stop, _LOOP); -// DEFINE_PRIM(_U64, loop_size, _VOID); +DEFINE_PRIM(_U64, loop_size, _VOID); -// DEFINE_PRIM(_I32, backend_fd, _LOOP); +DEFINE_PRIM(_I32, backend_fd, _LOOP); -// DEFINE_PRIM(_I32, backend_timeout, _LOOP); +DEFINE_PRIM(_I32, backend_timeout, _LOOP); -// DEFINE_PRIM(_I64, now, _LOOP); +DEFINE_PRIM(_I64, now, _LOOP); -// DEFINE_PRIM(_VOID, update_time, _LOOP); +DEFINE_PRIM(_VOID, update_time, _LOOP); -// HL_PRIM void HL_NAME(walk_with_cb)( uv_loop_t* loop, void* arg ) { -// uv_walk(loop, on_uv_walk_cb, arg); -// } -// DEFINE_PRIM(_VOID, walk_with_cb, _LOOP _POINTER); +HL_PRIM void HL_NAME(walk_with_cb)( uv_loop_t* loop, void* arg ) { + uv_walk(loop, on_uv_walk_cb, arg); +} +DEFINE_PRIM(_VOID, walk_with_cb, _LOOP _POINTER); -// DEFINE_PRIM(_I32, loop_fork, _LOOP); +DEFINE_PRIM(_I32, loop_fork, _LOOP); -// DEFINE_PRIM(_POINTER, loop_get_data, _LOOP); +DEFINE_PRIM(_POINTER, loop_get_data, _LOOP); -// DEFINE_PRIM(_POINTER, loop_set_data, _LOOP _POINTER); +DEFINE_PRIM(_POINTER, loop_set_data, _LOOP _POINTER); -// DEFINE_PRIM(_I64, metrics_idle_time, _LOOP); +DEFINE_PRIM(_I64, metrics_idle_time, _LOOP); -// DEFINE_PRIM(_HANDLE_TYPE, guess_handle, _FILE); +DEFINE_PRIM(_HANDLE_TYPE, guess_handle, _FILE); -// DEFINE_PRIM(_I32, replace_allocator, _MALLOC_FUNC _REALLOC_FUNC _CALLOC_FUNC _FREE_FUNC); +DEFINE_PRIM(_I32, replace_allocator, _MALLOC_FUNC _REALLOC_FUNC _CALLOC_FUNC _FREE_FUNC); -// DEFINE_PRIM(_VOID, library_shutdown, _VOID); +DEFINE_PRIM(_VOID, library_shutdown, _VOID); -// DEFINE_PRIM(_BUF, buf_init, _BYTES _U32); +DEFINE_PRIM(_BUF, buf_init, _BYTES _U32); -// DEFINE_PRIM(_REF(_BYTES), setup_args, _I32 _REF(_BYTES)); +DEFINE_PRIM(_REF(_BYTES), setup_args, _I32 _REF(_BYTES)); -// DEFINE_PRIM(_I32, get_process_title, _BYTES _U64); +DEFINE_PRIM(_I32, get_process_title, _BYTES _U64); -// DEFINE_PRIM(_I32, set_process_title, _BYTES); +DEFINE_PRIM(_I32, set_process_title, _BYTES); -// DEFINE_PRIM(_I32, resident_set_memory, _REF(_I64)); +DEFINE_PRIM(_I32, resident_set_memory, _REF(_I64)); -// DEFINE_PRIM(_I32, uptime, _REF(_F64)); +DEFINE_PRIM(_I32, uptime, _REF(_F64)); -// DEFINE_PRIM(_I32, getrusage, _RUSAGE); +DEFINE_PRIM(_I32, getrusage, _RUSAGE); -// DEFINE_PRIM(_I32, os_getpid, _VOID); +DEFINE_PRIM(_I32, os_getpid, _VOID); -// DEFINE_PRIM(_I32, os_getppid, _VOID); +DEFINE_PRIM(_I32, os_getppid, _VOID); -// DEFINE_PRIM(_I32, cpu_info, _REF(_CPU_INFO) _REF(_I32)); +DEFINE_PRIM(_I32, cpu_info, _REF(_CPU_INFO) _REF(_I32)); -// DEFINE_PRIM(_VOID, free_cpu_info, _CPU_INFO _I32); +DEFINE_PRIM(_VOID, free_cpu_info, _CPU_INFO _I32); -// DEFINE_PRIM(_I32, interface_addresses, _REF(_INTERFACE_ADDRESS) _REF(_I32)); +DEFINE_PRIM(_I32, interface_addresses, _REF(_INTERFACE_ADDRESS) _REF(_I32)); -// DEFINE_PRIM(_VOID, free_interface_addresses, _INTERFACE_ADDRESS _I32); +DEFINE_PRIM(_VOID, free_interface_addresses, _INTERFACE_ADDRESS _I32); -// DEFINE_PRIM(_VOID, loadavg, _F64); +DEFINE_PRIM(_VOID, loadavg, _F64); -// DEFINE_PRIM(_I32, ip4_addr, _BYTES _I32 _REF(_SOCKADDR_IN)); +DEFINE_PRIM(_I32, ip4_addr, _BYTES _I32 _REF(_SOCKADDR_IN)); -// DEFINE_PRIM(_I32, ip6_addr, _BYTES _I32 _REF(_SOCKADDR_IN6)); +DEFINE_PRIM(_I32, ip6_addr, _BYTES _I32 _REF(_SOCKADDR_IN6)); -// DEFINE_PRIM(_I32, ip4_name, _REF(_SOCKADDR_IN) _BYTES _U64); +DEFINE_PRIM(_I32, ip4_name, _REF(_SOCKADDR_IN) _BYTES _U64); -// DEFINE_PRIM(_I32, ip6_name, _REF(_SOCKADDR_IN6) _BYTES _U64); +DEFINE_PRIM(_I32, ip6_name, _REF(_SOCKADDR_IN6) _BYTES _U64); -// DEFINE_PRIM(_I32, inet_ntop, _I32 _POINTER _BYTES _U64); +DEFINE_PRIM(_I32, inet_ntop, _I32 _POINTER _BYTES _U64); -// DEFINE_PRIM(_I32, inet_pton, _I32 _BYTES _POINTER); +DEFINE_PRIM(_I32, inet_pton, _I32 _BYTES _POINTER); -// DEFINE_PRIM(_I32, if_indextoname, _U32 _BYTES _REF(_I64)); +DEFINE_PRIM(_I32, if_indextoname, _U32 _BYTES _REF(_I64)); -// DEFINE_PRIM(_I32, if_indextoiid, _U32 _BYTES _REF(_I64)); +DEFINE_PRIM(_I32, if_indextoiid, _U32 _BYTES _REF(_I64)); -// DEFINE_PRIM(_I32, exepath, _BYTES _REF(_I64)); +DEFINE_PRIM(_I32, exepath, _BYTES _REF(_I64)); -// DEFINE_PRIM(_I32, cwd, _BYTES _REF(_I64)); +DEFINE_PRIM(_I32, cwd, _BYTES _REF(_I64)); -// DEFINE_PRIM(_I32, chdir, _BYTES); +DEFINE_PRIM(_I32, chdir, _BYTES); -// DEFINE_PRIM(_I32, os_homedir, _BYTES _REF(_I64)); +DEFINE_PRIM(_I32, os_homedir, _BYTES _REF(_I64)); -// DEFINE_PRIM(_I32, os_tmpdir, _BYTES _REF(_I64)); +DEFINE_PRIM(_I32, os_tmpdir, _BYTES _REF(_I64)); -// DEFINE_PRIM(_I32, os_get_passwd, _PASSWD); +DEFINE_PRIM(_I32, os_get_passwd, _PASSWD); -// DEFINE_PRIM(_VOID, os_free_passwd, _PASSWD); +DEFINE_PRIM(_VOID, os_free_passwd, _PASSWD); -// DEFINE_PRIM(_I64, get_free_memory, _VOID); +DEFINE_PRIM(_I64, get_free_memory, _VOID); -// DEFINE_PRIM(_I64, get_total_memory, _VOID); +DEFINE_PRIM(_I64, get_total_memory, _VOID); -// DEFINE_PRIM(_I64, get_constrained_memory, _VOID); +DEFINE_PRIM(_I64, get_constrained_memory, _VOID); -// DEFINE_PRIM(_I64, hrtime, _VOID); +DEFINE_PRIM(_I64, hrtime, _VOID); -// DEFINE_PRIM(_VOID, print_all_handles, _LOOP _FILE); +DEFINE_PRIM(_VOID, print_all_handles, _LOOP _FILE); -// DEFINE_PRIM(_VOID, print_active_handles, _LOOP _FILE); +DEFINE_PRIM(_VOID, print_active_handles, _LOOP _FILE); -// DEFINE_PRIM(_I32, os_environ, _REF(_ENV_ITEM) _REF(_I32)); +DEFINE_PRIM(_I32, os_environ, _REF(_ENV_ITEM) _REF(_I32)); -// DEFINE_PRIM(_VOID, os_free_environ, _ENV_ITEM _I32); +DEFINE_PRIM(_VOID, os_free_environ, _ENV_ITEM _I32); -// DEFINE_PRIM(_I32, os_getenv, _BYTES _BYTES _REF(_I64)); +DEFINE_PRIM(_I32, os_getenv, _BYTES _BYTES _REF(_I64)); -// DEFINE_PRIM(_I32, os_setenv, _BYTES _BYTES); +DEFINE_PRIM(_I32, os_setenv, _BYTES _BYTES); -// DEFINE_PRIM(_I32, os_unsetenv, _BYTES); +DEFINE_PRIM(_I32, os_unsetenv, _BYTES); -// DEFINE_PRIM(_I32, os_gethostname, _BYTES _REF(_I64)); +DEFINE_PRIM(_I32, os_gethostname, _BYTES _REF(_I64)); -// DEFINE_PRIM(_I32, os_getpriority, _I32 _REF(_I32)); +DEFINE_PRIM(_I32, os_getpriority, _I32 _REF(_I32)); -// DEFINE_PRIM(_I32, os_setpriority, _I32 _I32); +DEFINE_PRIM(_I32, os_setpriority, _I32 _I32); -// DEFINE_PRIM(_I32, os_uname, _UTSNAME); +DEFINE_PRIM(_I32, os_uname, _UTSNAME); -// DEFINE_PRIM(_I32, gettimeofday, _TIMEVAL64); +DEFINE_PRIM(_I32, gettimeofday, _TIMEVAL64); -// HL_PRIM int HL_NAME(random_with_cb)( uv_loop_t* loop, uv_random_t* req, void* buf, size_t buflen, unsigned int flags ) { -// return uv_random(loop, req, buf, buflen, flags, on_uv_random_cb); -// } -// DEFINE_PRIM(_I32, random_with_cb, _LOOP _RANDOM _POINTER _U64 _U32); +HL_PRIM int HL_NAME(random_with_cb)( uv_loop_t* loop, uv_random_t* req, void* buf, size_t buflen, unsigned int flags ) { + return uv_random(loop, req, buf, buflen, flags, on_uv_random_cb); +} +DEFINE_PRIM(_I32, random_with_cb, _LOOP _RANDOM _POINTER _U64 _U32); -// DEFINE_PRIM(_VOID, sleep, _U32); +DEFINE_PRIM(_VOID, sleep, _U32); -// DEFINE_PRIM(_I32, pipe_init, _LOOP _PIPE _I32); +DEFINE_PRIM(_I32, pipe_init, _LOOP _PIPE _I32); -// DEFINE_PRIM(_I32, pipe_open, _PIPE _FILE); +DEFINE_PRIM(_I32, pipe_open, _PIPE _FILE); -// DEFINE_PRIM(_I32, pipe_bind, _PIPE _BYTES); +DEFINE_PRIM(_I32, pipe_bind, _PIPE _BYTES); -// HL_PRIM void HL_NAME(pipe_connect_with_cb)( uv_connect_t* req, uv_pipe_t* handle, const char* name ) { -// uv_pipe_connect(req, handle, name, on_uv_connect_cb); -// } -// DEFINE_PRIM(_VOID, pipe_connect_with_cb, _CONNECT _PIPE _BYTES); +HL_PRIM void HL_NAME(pipe_connect_with_cb)( uv_connect_t* req, uv_pipe_t* handle, const char* name ) { + uv_pipe_connect(req, handle, name, on_uv_connect_cb); +} +DEFINE_PRIM(_VOID, pipe_connect_with_cb, _CONNECT _PIPE _BYTES); -// DEFINE_PRIM(_I32, pipe_getsockname, _PIPE _BYTES _REF(_I64)); +DEFINE_PRIM(_I32, pipe_getsockname, _PIPE _BYTES _REF(_I64)); -// DEFINE_PRIM(_I32, pipe_getpeername, _PIPE _BYTES _REF(_I64)); +DEFINE_PRIM(_I32, pipe_getpeername, _PIPE _BYTES _REF(_I64)); -// DEFINE_PRIM(_VOID, pipe_pending_instances, _PIPE _I32); +DEFINE_PRIM(_VOID, pipe_pending_instances, _PIPE _I32); -// DEFINE_PRIM(_I32, pipe_pending_count, _PIPE); +DEFINE_PRIM(_I32, pipe_pending_count, _PIPE); -// DEFINE_PRIM(_HANDLE_TYPE, pipe_pending_type, _PIPE); +DEFINE_PRIM(_HANDLE_TYPE, pipe_pending_type, _PIPE); -// DEFINE_PRIM(_I32, pipe_chmod, _PIPE _I32); +DEFINE_PRIM(_I32, pipe_chmod, _PIPE _I32); -// DEFINE_PRIM(_I32, pipe, _FILE _I32 _I32); +DEFINE_PRIM(_I32, pipe, _FILE _I32 _I32); -// DEFINE_PRIM(_I32, prepare_init, _LOOP _PREPARE); +DEFINE_PRIM(_I32, prepare_init, _LOOP _PREPARE); -// HL_PRIM int HL_NAME(prepare_start_with_cb)( uv_prepare_t* prepare ) { -// return uv_prepare_start(prepare, on_uv_prepare_cb); -// } -// DEFINE_PRIM(_I32, prepare_start_with_cb, _PREPARE); +HL_PRIM int HL_NAME(prepare_start_with_cb)( uv_prepare_t* prepare ) { + return uv_prepare_start(prepare, on_uv_prepare_cb); +} +DEFINE_PRIM(_I32, prepare_start_with_cb, _PREPARE); -// DEFINE_PRIM(_I32, prepare_stop, _PREPARE); +DEFINE_PRIM(_I32, prepare_stop, _PREPARE); -// DEFINE_PRIM(_VOID, disable_stdio_inheritance, _VOID); +DEFINE_PRIM(_VOID, disable_stdio_inheritance, _VOID); -// DEFINE_PRIM(_I32, spawn, _LOOP _PROCESS _PROCESS_OPTIONS); +DEFINE_PRIM(_I32, spawn, _LOOP _PROCESS _PROCESS_OPTIONS); -// DEFINE_PRIM(_I32, process_kill, _PROCESS _I32); +DEFINE_PRIM(_I32, process_kill, _PROCESS _I32); -// DEFINE_PRIM(_I32, kill, _I32 _I32); +DEFINE_PRIM(_I32, kill, _I32 _I32); -// DEFINE_PRIM(_I32, process_get_pid, _PROCESS); +DEFINE_PRIM(_I32, process_get_pid, _PROCESS); -// DEFINE_PRIM(_I32, cancel, _REQ); +DEFINE_PRIM(_I32, cancel, _REQ); -// DEFINE_PRIM(_U64, req_size, _REQ_TYPE); +DEFINE_PRIM(_U64, req_size, _REQ_TYPE); -// DEFINE_PRIM(_POINTER, req_get_data, _REQ); +DEFINE_PRIM(_POINTER, req_get_data, _REQ); -// DEFINE_PRIM(_POINTER, req_set_data, _REQ _POINTER); +DEFINE_PRIM(_POINTER, req_set_data, _REQ _POINTER); -// DEFINE_PRIM(_REQ_TYPE, req_get_type, _REQ); +DEFINE_PRIM(_REQ_TYPE, req_get_type, _REQ); -// DEFINE_PRIM(_BYTES, req_type_name, _REQ_TYPE); +DEFINE_PRIM(_BYTES, req_type_name, _REQ_TYPE); -// DEFINE_PRIM(_I32, signal_init, _LOOP _SIGNAL); +DEFINE_PRIM(_I32, signal_init, _LOOP _SIGNAL); -// HL_PRIM int HL_NAME(signal_start_with_cb)( uv_signal_t* signal, int signum ) { -// return uv_signal_start(signal, on_uv_signal_cb, signum); -// } -// DEFINE_PRIM(_I32, signal_start_with_cb, _SIGNAL _I32); +HL_PRIM int HL_NAME(signal_start_with_cb)( uv_signal_t* signal, int signum ) { + return uv_signal_start(signal, on_uv_signal_cb, signum); +} +DEFINE_PRIM(_I32, signal_start_with_cb, _SIGNAL _I32); -// HL_PRIM int HL_NAME(signal_start_oneshot_with_cb)( uv_signal_t* signal, int signum ) { -// return uv_signal_start_oneshot(signal, on_uv_signal_cb, signum); -// } -// DEFINE_PRIM(_I32, signal_start_oneshot_with_cb, _SIGNAL _I32); +HL_PRIM int HL_NAME(signal_start_oneshot_with_cb)( uv_signal_t* signal, int signum ) { + return uv_signal_start_oneshot(signal, on_uv_signal_cb, signum); +} +DEFINE_PRIM(_I32, signal_start_oneshot_with_cb, _SIGNAL _I32); -// DEFINE_PRIM(_I32, signal_stop, _SIGNAL); +DEFINE_PRIM(_I32, signal_stop, _SIGNAL); -// HL_PRIM int HL_NAME(shutdown_with_cb)( uv_shutdown_t* req, uv_stream_t* handle ) { -// return uv_shutdown(req, handle, on_uv_shutdown_cb); -// } -// DEFINE_PRIM(_I32, shutdown_with_cb, _SHUTDOWN _STREAM); +HL_PRIM int HL_NAME(shutdown_with_cb)( uv_shutdown_t* req, uv_stream_t* handle ) { + return uv_shutdown(req, handle, on_uv_shutdown_cb); +} +DEFINE_PRIM(_I32, shutdown_with_cb, _SHUTDOWN _STREAM); -// HL_PRIM int HL_NAME(listen_with_cb)( uv_stream_t* stream, int backlog ) { -// return uv_listen(stream, backlog, on_uv_connection_cb); -// } -// DEFINE_PRIM(_I32, listen_with_cb, _STREAM _I32); +HL_PRIM int HL_NAME(listen_with_cb)( uv_stream_t* stream, int backlog ) { + return uv_listen(stream, backlog, on_uv_connection_cb); +} +DEFINE_PRIM(_I32, listen_with_cb, _STREAM _I32); -// DEFINE_PRIM(_I32, accept, _STREAM _STREAM); +DEFINE_PRIM(_I32, accept, _STREAM _STREAM); -// HL_PRIM int HL_NAME(read_start_with_cb)( uv_stream_t* stream ) { -// return uv_read_start(stream, on_uv_alloc_cb, on_uv_read_cb); -// } -// DEFINE_PRIM(_I32, read_start_with_cb, _STREAM); +HL_PRIM int HL_NAME(read_start_with_cb)( uv_stream_t* stream ) { + return uv_read_start(stream, on_uv_alloc_cb, on_uv_read_cb); +} +DEFINE_PRIM(_I32, read_start_with_cb, _STREAM); -// DEFINE_PRIM(_I32, read_stop, _STREAM); +DEFINE_PRIM(_I32, read_stop, _STREAM); -// HL_PRIM int HL_NAME(write_with_cb)( uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs ) { -// return uv_write(req, handle, bufs[], nbufs, on_uv_write_cb); -// } -// DEFINE_PRIM(_I32, write_with_cb, _WRITE _STREAM _BUF _U32); +HL_PRIM int HL_NAME(write_with_cb)( uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs ) { + return uv_write(req, handle, bufs[], nbufs, on_uv_write_cb); +} +DEFINE_PRIM(_I32, write_with_cb, _WRITE _STREAM _BUF _U32); -// HL_PRIM int HL_NAME(write2_with_cb)( uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_stream_t* send_handle ) { -// return uv_write2(req, handle, bufs[], nbufs, send_handle, on_uv_write_cb); -// } -// DEFINE_PRIM(_I32, write2_with_cb, _WRITE _STREAM _BUF _U32 _STREAM); +HL_PRIM int HL_NAME(write2_with_cb)( uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_stream_t* send_handle ) { + return uv_write2(req, handle, bufs[], nbufs, send_handle, on_uv_write_cb); +} +DEFINE_PRIM(_I32, write2_with_cb, _WRITE _STREAM _BUF _U32 _STREAM); -// DEFINE_PRIM(_I32, try_write, _STREAM _BUF _U32); +DEFINE_PRIM(_I32, try_write, _STREAM _BUF _U32); -// DEFINE_PRIM(_I32, try_write2, _STREAM _BUF _U32 _STREAM); +DEFINE_PRIM(_I32, try_write2, _STREAM _BUF _U32 _STREAM); -// DEFINE_PRIM(_I32, is_readable, _STREAM); +DEFINE_PRIM(_I32, is_readable, _STREAM); -// DEFINE_PRIM(_I32, is_writable, _STREAM); +DEFINE_PRIM(_I32, is_writable, _STREAM); -// DEFINE_PRIM(_I32, stream_set_blocking, _STREAM _I32); +DEFINE_PRIM(_I32, stream_set_blocking, _STREAM _I32); -// DEFINE_PRIM(_U64, stream_get_write_queue_size, _STREAM); +DEFINE_PRIM(_U64, stream_get_write_queue_size, _STREAM); -// DEFINE_PRIM(_I32, tcp_init, _LOOP _TCP); +DEFINE_PRIM(_I32, tcp_init, _LOOP _TCP); -// DEFINE_PRIM(_I32, tcp_init_ex, _LOOP _TCP _U32); +DEFINE_PRIM(_I32, tcp_init_ex, _LOOP _TCP _U32); -// DEFINE_PRIM(_I32, tcp_open, _TCP _OS_SOCK_T); +DEFINE_PRIM(_I32, tcp_open, _TCP _OS_SOCK_T); -// DEFINE_PRIM(_I32, tcp_nodelay, _TCP _I32); +DEFINE_PRIM(_I32, tcp_nodelay, _TCP _I32); -// DEFINE_PRIM(_I32, tcp_keepalive, _TCP _I32 _U32); +DEFINE_PRIM(_I32, tcp_keepalive, _TCP _I32 _U32); -// DEFINE_PRIM(_I32, tcp_simultaneous_accepts, _TCP _I32); +DEFINE_PRIM(_I32, tcp_simultaneous_accepts, _TCP _I32); -// DEFINE_PRIM(_I32, tcp_bind, _TCP _REF(_SOCKADDR) _U32); +DEFINE_PRIM(_I32, tcp_bind, _TCP _REF(_SOCKADDR) _U32); -// DEFINE_PRIM(_I32, tcp_getsockname, _TCP _REF(_SOCKADDR) _REF(_I32)); +DEFINE_PRIM(_I32, tcp_getsockname, _TCP _REF(_SOCKADDR) _REF(_I32)); -// DEFINE_PRIM(_I32, tcp_getpeername, _TCP _REF(_SOCKADDR) _REF(_I32)); +DEFINE_PRIM(_I32, tcp_getpeername, _TCP _REF(_SOCKADDR) _REF(_I32)); -// HL_PRIM int HL_NAME(tcp_connect_with_cb)( uv_connect_t* req, uv_tcp_t* handle, const struct sockaddr* addr ) { -// return uv_tcp_connect(req, handle, addr, on_uv_connect_cb); -// } -// DEFINE_PRIM(_I32, tcp_connect_with_cb, _CONNECT _TCP _REF(_SOCKADDR)); +HL_PRIM int HL_NAME(tcp_connect_with_cb)( uv_connect_t* req, uv_tcp_t* handle, const struct sockaddr* addr ) { + return uv_tcp_connect(req, handle, addr, on_uv_connect_cb); +} +DEFINE_PRIM(_I32, tcp_connect_with_cb, _CONNECT _TCP _REF(_SOCKADDR)); -// HL_PRIM int HL_NAME(tcp_close_reset_with_cb)( uv_tcp_t* handle ) { -// return uv_tcp_close_reset(handle, on_uv_close_cb); -// } -// DEFINE_PRIM(_I32, tcp_close_reset_with_cb, _TCP); +HL_PRIM int HL_NAME(tcp_close_reset_with_cb)( uv_tcp_t* handle ) { + return uv_tcp_close_reset(handle, on_uv_close_cb); +} +DEFINE_PRIM(_I32, tcp_close_reset_with_cb, _TCP); -// DEFINE_PRIM(_I32, socketpair, _I32 _I32 _OS_SOCK_T _I32 _I32); +DEFINE_PRIM(_I32, socketpair, _I32 _I32 _OS_SOCK_T _I32 _I32); DEFINE_PRIM(_I32, timer_init, _LOOP _TIMER); @@ -596,67 +596,67 @@ DEFINE_PRIM(_I64, timer_get_repeat, _TIMER); DEFINE_PRIM(_I64, timer_get_due_in, _TIMER); -// DEFINE_PRIM(_I32, tty_init, _LOOP _TTY _FILE _I32); +DEFINE_PRIM(_I32, tty_init, _LOOP _TTY _FILE _I32); -// DEFINE_PRIM(_I32, tty_set_mode, _TTY _TTY_MODE_T); +DEFINE_PRIM(_I32, tty_set_mode, _TTY _TTY_MODE_T); -// DEFINE_PRIM(_I32, tty_reset_mode, _VOID); +DEFINE_PRIM(_I32, tty_reset_mode, _VOID); -// DEFINE_PRIM(_I32, tty_get_winsize, _TTY _REF(_I32) _REF(_I32)); +DEFINE_PRIM(_I32, tty_get_winsize, _TTY _REF(_I32) _REF(_I32)); -// DEFINE_PRIM(_VOID, tty_set_vterm_state, _TTY_VTERMSTATE_T); +DEFINE_PRIM(_VOID, tty_set_vterm_state, _TTY_VTERMSTATE_T); -// DEFINE_PRIM(_I32, tty_get_vterm_state, _TTY_VTERMSTATE); +DEFINE_PRIM(_I32, tty_get_vterm_state, _TTY_VTERMSTATE); -// DEFINE_PRIM(_I32, udp_init, _LOOP _UDP); +DEFINE_PRIM(_I32, udp_init, _LOOP _UDP); -// DEFINE_PRIM(_I32, udp_init_ex, _LOOP _UDP _U32); +DEFINE_PRIM(_I32, udp_init_ex, _LOOP _UDP _U32); -// DEFINE_PRIM(_I32, udp_open, _UDP _OS_SOCK_T); +DEFINE_PRIM(_I32, udp_open, _UDP _OS_SOCK_T); -// DEFINE_PRIM(_I32, udp_bind, _UDP _REF(_SOCKADDR) _U32); +DEFINE_PRIM(_I32, udp_bind, _UDP _REF(_SOCKADDR) _U32); -// DEFINE_PRIM(_I32, udp_connect, _UDP _REF(_SOCKADDR)); +DEFINE_PRIM(_I32, udp_connect, _UDP _REF(_SOCKADDR)); -// DEFINE_PRIM(_I32, udp_getpeername, _UDP _REF(_SOCKADDR) _REF(_I32)); +DEFINE_PRIM(_I32, udp_getpeername, _UDP _REF(_SOCKADDR) _REF(_I32)); -// DEFINE_PRIM(_I32, udp_getsockname, _UDP _REF(_SOCKADDR) _REF(_I32)); +DEFINE_PRIM(_I32, udp_getsockname, _UDP _REF(_SOCKADDR) _REF(_I32)); -// DEFINE_PRIM(_I32, udp_set_membership, _UDP _BYTES _BYTES _MEMBERSHIP); +DEFINE_PRIM(_I32, udp_set_membership, _UDP _BYTES _BYTES _MEMBERSHIP); -// DEFINE_PRIM(_I32, udp_set_source_membership, _UDP _BYTES _BYTES _BYTES _MEMBERSHIP); +DEFINE_PRIM(_I32, udp_set_source_membership, _UDP _BYTES _BYTES _BYTES _MEMBERSHIP); -// DEFINE_PRIM(_I32, udp_set_multicast_loop, _UDP _I32); +DEFINE_PRIM(_I32, udp_set_multicast_loop, _UDP _I32); -// DEFINE_PRIM(_I32, udp_set_multicast_ttl, _UDP _I32); +DEFINE_PRIM(_I32, udp_set_multicast_ttl, _UDP _I32); -// DEFINE_PRIM(_I32, udp_set_multicast_interface, _UDP _BYTES); +DEFINE_PRIM(_I32, udp_set_multicast_interface, _UDP _BYTES); -// DEFINE_PRIM(_I32, udp_set_broadcast, _UDP _I32); +DEFINE_PRIM(_I32, udp_set_broadcast, _UDP _I32); -// DEFINE_PRIM(_I32, udp_set_ttl, _UDP _I32); +DEFINE_PRIM(_I32, udp_set_ttl, _UDP _I32); -// HL_PRIM int HL_NAME(udp_send_with_cb)( uv_udp_send_t* req, uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr ) { -// return uv_udp_send(req, handle, bufs[], nbufs, addr, on_uv_udp_send_cb); -// } -// DEFINE_PRIM(_I32, udp_send_with_cb, _UDP_SEND _UDP _BUF _U32 _REF(_SOCKADDR)); +HL_PRIM int HL_NAME(udp_send_with_cb)( uv_udp_send_t* req, uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr ) { + return uv_udp_send(req, handle, bufs[], nbufs, addr, on_uv_udp_send_cb); +} +DEFINE_PRIM(_I32, udp_send_with_cb, _UDP_SEND _UDP _BUF _U32 _REF(_SOCKADDR)); -// DEFINE_PRIM(_I32, udp_try_send, _UDP _BUF _U32 _REF(_SOCKADDR)); +DEFINE_PRIM(_I32, udp_try_send, _UDP _BUF _U32 _REF(_SOCKADDR)); -// HL_PRIM int HL_NAME(udp_recv_start_with_cb)( uv_udp_t* handle ) { -// return uv_udp_recv_start(handle, on_uv_alloc_cb, on_uv_udp_recv_cb); -// } -// DEFINE_PRIM(_I32, udp_recv_start_with_cb, _UDP); +HL_PRIM int HL_NAME(udp_recv_start_with_cb)( uv_udp_t* handle ) { + return uv_udp_recv_start(handle, on_uv_alloc_cb, on_uv_udp_recv_cb); +} +DEFINE_PRIM(_I32, udp_recv_start_with_cb, _UDP); -// DEFINE_PRIM(_I32, udp_using_recvmmsg, _UDP); +DEFINE_PRIM(_I32, udp_using_recvmmsg, _UDP); -// DEFINE_PRIM(_I32, udp_recv_stop, _UDP); +DEFINE_PRIM(_I32, udp_recv_stop, _UDP); -// DEFINE_PRIM(_U64, udp_get_send_queue_size, _UDP); +DEFINE_PRIM(_U64, udp_get_send_queue_size, _UDP); -// DEFINE_PRIM(_U64, udp_get_send_queue_count, _UDP); +DEFINE_PRIM(_U64, udp_get_send_queue_count, _UDP); -// DEFINE_PRIM(_U32, version, _VOID); +DEFINE_PRIM(_U32, version, _VOID); -// DEFINE_PRIM(_BYTES, version_string, _VOID); +DEFINE_PRIM(_BYTES, version_string, _VOID); diff --git a/other/uvgenerator/UV.hx b/other/uvgenerator/UV.hx new file mode 100644 index 000000000..b0aae14c6 --- /dev/null +++ b/other/uvgenerator/UV.hx @@ -0,0 +1,292 @@ +/* + * Copyright (C)2005-2019 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package hl.uv; + +// This file is automatically generated by a tool in Hashlink repo. +// see /other/uvgenerator + +// Contents of UV.hx.header + +typedef UInt = Int; +typedef U64 = I64; + +/** + Automatically generated bindings for libuv. + Avoid using this module directly. + BACKWARD COMPATIBILITY OF THIS MODULE IS NOT MAINTAINED. +**/ +@:hlNative("uv") +class UV { + static public inline function resolve(result:Int):Int { + if(result < 0) + throw new UVException(translate_uv_error(result)); + return this; + } + + static public function translate_uv_error(uvErrno:Int):UVError return UV_NOERR; + static public function translate_to_uv_error(errno:Int):Int return 0; + static public function handle_set_data_with_gc(data:HandleData):Void {} + +// Auto generated + static public function async_init_with_cb(loop:Loop, async:Async):Int return cast null; + static public function async_send(async:Async):Int return cast null; + static public function check_init(loop:Loop, check:Check):Int return cast null; + static public function check_start_with_cb(check:Check):Int return cast null; + static public function check_stop(check:Check):Int return cast null; + static public function getaddrinfo_with_cb(loop:Loop, req:Getaddrinfo, node:Bytes, service:Bytes, hints:Ref):Int return cast null; + static public function freeaddrinfo(ai:Ref):Void {} + static public function getnameinfo_with_cb(loop:Loop, req:Getnameinfo, addr:Ref, flags:Int):Int return cast null; + static public function strerror(err:Int):Bytes return cast null; + static public function strerror_r(err:Int, buf:Bytes, buflen:U64):Bytes return cast null; + static public function err_name(err:Int):Bytes return cast null; + static public function err_name_r(err:Int, buf:Bytes, buflen:U64):Bytes return cast null; + static public function translate_sys_error(sys_errno:Int):Int return cast null; + static public function fs_req_cleanup(req:Fs):Void {} + static public function fs_close_with_cb(loop:Loop, req:Fs, file:UvFile, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_open_with_cb(loop:Loop, req:Fs, path:Bytes, flags:Int, mode:Int, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_read_with_cb(loop:Loop, req:Fs, file:UvFile, bufs:Ref, nbufs:UInt, offset:I64, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_unlink_with_cb(loop:Loop, req:Fs, path:Bytes, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_write_with_cb(loop:Loop, req:Fs, file:UvFile, bufs:Ref, nbufs:UInt, offset:I64, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_mkdir_with_cb(loop:Loop, req:Fs, path:Bytes, mode:Int, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_mkdtemp_with_cb(loop:Loop, req:Fs, tpl:Bytes, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_mkstemp_with_cb(loop:Loop, req:Fs, tpl:Bytes, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_rmdir_with_cb(loop:Loop, req:Fs, path:Bytes, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_opendir_with_cb(loop:Loop, req:Fs, path:Bytes, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_closedir_with_cb(loop:Loop, req:Fs, dir:Dir, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_readdir_with_cb(loop:Loop, req:Fs, dir:Dir, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_scandir_with_cb(loop:Loop, req:Fs, path:Bytes, flags:Int, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_scandir_next(req:Fs, ent:Dirent):Int return cast null; + static public function fs_stat_with_cb(loop:Loop, req:Fs, path:Bytes, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_fstat_with_cb(loop:Loop, req:Fs, file:UvFile, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_lstat_with_cb(loop:Loop, req:Fs, path:Bytes, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_statfs_with_cb(loop:Loop, req:Fs, path:Bytes, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_rename_with_cb(loop:Loop, req:Fs, path:Bytes, new_path:Bytes, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_fsync_with_cb(loop:Loop, req:Fs, file:UvFile, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_fdatasync_with_cb(loop:Loop, req:Fs, file:UvFile, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_ftruncate_with_cb(loop:Loop, req:Fs, file:UvFile, offset:I64, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_copyfile_with_cb(loop:Loop, req:Fs, path:Bytes, new_path:Bytes, flags:Int, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_sendfile_with_cb(loop:Loop, req:Fs, out_fd:UvFile, in_fd:UvFile, in_offset:I64, length:U64, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_access_with_cb(loop:Loop, req:Fs, path:Bytes, mode:Int, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_chmod_with_cb(loop:Loop, req:Fs, path:Bytes, mode:Int, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_fchmod_with_cb(loop:Loop, req:Fs, file:UvFile, mode:Int, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_utime_with_cb(loop:Loop, req:Fs, path:Bytes, atime:Float, mtime:Float, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_futime_with_cb(loop:Loop, req:Fs, file:UvFile, atime:Float, mtime:Float, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_lutime_with_cb(loop:Loop, req:Fs, path:Bytes, atime:Float, mtime:Float, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_link_with_cb(loop:Loop, req:Fs, path:Bytes, new_path:Bytes, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_symlink_with_cb(loop:Loop, req:Fs, path:Bytes, new_path:Bytes, flags:Int, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_readlink_with_cb(loop:Loop, req:Fs, path:Bytes, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_realpath_with_cb(loop:Loop, req:Fs, path:Bytes, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_chown_with_cb(loop:Loop, req:Fs, path:Bytes, uid:Uid, gid:Gid, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_fchown_with_cb(loop:Loop, req:Fs, file:UvFile, uid:Uid, gid:Gid, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_lchown_with_cb(loop:Loop, req:Fs, path:Bytes, uid:Uid, gid:Gid, use_uv_fs_cb:Bool):Int return cast null; + static public function fs_get_type(req:Fs):UvFsType return cast null; + static public function fs_get_result(req:Fs):I64 return cast null; + static public function fs_get_system_error(req:Fs):Int return cast null; + static public function fs_get_ptr(req:Fs):Pointer return cast null; + static public function fs_get_path(req:Fs):Bytes return cast null; + static public function fs_get_statbuf(req:Fs):Stat return cast null; + static public function get_osfhandle(fd:Int):OsFd return cast null; + static public function open_osfhandle(os_fd:OsFd):Int return cast null; + static public function fs_event_init(loop:Loop, handle:FsEvent):Int return cast null; + static public function fs_event_start_with_cb(handle:FsEvent, path:Bytes, flags:UInt):Int return cast null; + static public function fs_event_stop(handle:FsEvent):Int return cast null; + static public function fs_event_getpath(handle:FsEvent, buffer:Bytes, size:Ref):Int return cast null; + static public function fs_poll_init(loop:Loop, handle:FsPoll):Int return cast null; + static public function fs_poll_start_with_cb(handle:FsPoll, path:Bytes, interval:UInt):Int return cast null; + static public function fs_poll_stop(handle:FsPoll):Int return cast null; + static public function fs_poll_getpath(handle:FsPoll, buffer:Bytes, size:Ref):Int return cast null; + static public function is_active(handle:Handle):Int return cast null; + static public function is_closing(handle:Handle):Int return cast null; + static public function close_with_cb(handle:Handle):Void {} + static public function ref(handle:Handle):Void {} + static public function unref(handle:Handle):Void {} + static public function has_ref(handle:Handle):Int return cast null; + static public function handle_size(type:UvHandleType):U64 return cast null; + static public function send_buffer_size(handle:Handle, value:Ref):Int return cast null; + static public function recv_buffer_size(handle:Handle, value:Ref):Int return cast null; + static public function fileno(handle:Handle, fd:OsFd):Int return cast null; + static public function handle_get_loop(handle:Handle):Loop return cast null; + static public function handle_get_data(handle:Handle):Pointer return cast null; + static public function handle_set_data(handle:Handle, data:Pointer):Pointer return cast null; + static public function handle_get_type(handle:Handle):UvHandleType return cast null; + static public function handle_type_name(type:UvHandleType):Bytes return cast null; + static public function idle_init(loop:Loop, idle:Idle):Int return cast null; + static public function idle_start_with_cb(idle:Idle):Int return cast null; + static public function idle_stop(idle:Idle):Int return cast null; + static public function loop_init(loop:Loop):Int return cast null; + static public function loop_close(loop:Loop):Int return cast null; + static public function default_loop():Loop return cast null; + static public function run(loop:Loop, mode:UvRunMode):Int return cast null; + static public function loop_alive(loop:Loop):Int return cast null; + static public function stop(loop:Loop):Void {} + static public function loop_size():U64 return cast null; + static public function backend_fd(loop:Loop):Int return cast null; + static public function backend_timeout(loop:Loop):Int return cast null; + static public function now(loop:Loop):U64 return cast null; + static public function update_time(loop:Loop):Void {} + static public function walk_with_cb(loop:Loop, arg:Pointer):Void {} + static public function loop_fork(loop:Loop):Int return cast null; + static public function loop_get_data(loop:Loop):Pointer return cast null; + static public function loop_set_data(loop:Loop, data:Pointer):Pointer return cast null; + static public function metrics_idle_time(loop:Loop):U64 return cast null; + static public function guess_handle(file:UvFile):UvHandleType return cast null; + static public function replace_allocator(malloc_func:UvMallocFunc, realloc_func:UvReallocFunc, calloc_func:UvCallocFunc, free_func:UvFreeFunc):Int return cast null; + static public function library_shutdown():Void {} + static public function buf_init(base:Bytes, len:UInt):Buf return cast null; + static public function setup_args(argc:Int, argv:Ref):Ref return cast null; + static public function get_process_title(buffer:Bytes, size:U64):Int return cast null; + static public function set_process_title(title:Bytes):Int return cast null; + static public function resident_set_memory(rss:Ref):Int return cast null; + static public function uptime(uptime:Ref):Int return cast null; + static public function getrusage(rusage:Rusage):Int return cast null; + static public function os_getpid():Pid return cast null; + static public function os_getppid():Pid return cast null; + static public function cpu_info(cpu_infos:Ref, count:Ref):Int return cast null; + static public function free_cpu_info(cpu_infos:CpuInfo, count:Int):Void {} + static public function interface_addresses(addresses:Ref, count:Ref):Int return cast null; + static public function free_interface_addresses(addresses:InterfaceAddress, count:Int):Void {} + static public function loadavg(avg:Ref):Void {} + static public function ip4_addr(ip:Bytes, port:Int, addr:Ref):Int return cast null; + static public function ip6_addr(ip:Bytes, port:Int, addr:Ref):Int return cast null; + static public function ip4_name(src:Ref, dst:Bytes, size:U64):Int return cast null; + static public function ip6_name(src:Ref, dst:Bytes, size:U64):Int return cast null; + static public function inet_ntop(af:Int, src:Pointer, dst:Bytes, size:U64):Int return cast null; + static public function inet_pton(af:Int, src:Bytes, dst:Pointer):Int return cast null; + static public function if_indextoname(ifindex:UInt, buffer:Bytes, size:Ref):Int return cast null; + static public function if_indextoiid(ifindex:UInt, buffer:Bytes, size:Ref):Int return cast null; + static public function exepath(buffer:Bytes, size:Ref):Int return cast null; + static public function cwd(buffer:Bytes, size:Ref):Int return cast null; + static public function chdir(dir:Bytes):Int return cast null; + static public function os_homedir(buffer:Bytes, size:Ref):Int return cast null; + static public function os_tmpdir(buffer:Bytes, size:Ref):Int return cast null; + static public function os_get_passwd(pwd:Passwd):Int return cast null; + static public function os_free_passwd(pwd:Passwd):Void {} + static public function get_free_memory():U64 return cast null; + static public function get_total_memory():U64 return cast null; + static public function get_constrained_memory():U64 return cast null; + static public function hrtime():U64 return cast null; + static public function print_all_handles(loop:Loop, stream:Ref):Void {} + static public function print_active_handles(loop:Loop, stream:Ref):Void {} + static public function os_environ(envitems:Ref, count:Ref):Int return cast null; + static public function os_free_environ(envitems:EnvItem, count:Int):Void {} + static public function os_getenv(name:Bytes, buffer:Bytes, size:Ref):Int return cast null; + static public function os_setenv(name:Bytes, value:Bytes):Int return cast null; + static public function os_unsetenv(name:Bytes):Int return cast null; + static public function os_gethostname(buffer:Bytes, size:Ref):Int return cast null; + static public function os_getpriority(pid:Pid, priority:Ref):Int return cast null; + static public function os_setpriority(pid:Pid, priority:Int):Int return cast null; + static public function os_uname(buffer:Utsname):Int return cast null; + static public function gettimeofday(tv:Timeval64):Int return cast null; + static public function random_with_cb(loop:Loop, req:Random, buf:Pointer, buflen:U64, flags:UInt):Int return cast null; + static public function sleep(msec:UInt):Void {} + static public function pipe_init(loop:Loop, handle:Pipe, ipc:Int):Int return cast null; + static public function pipe_open(handle:Pipe, file:UvFile):Int return cast null; + static public function pipe_bind(handle:Pipe, name:Bytes):Int return cast null; + static public function pipe_connect_with_cb(req:Connect, handle:Pipe, name:Bytes):Void {} + static public function pipe_getsockname(handle:Pipe, buffer:Bytes, size:Ref):Int return cast null; + static public function pipe_getpeername(handle:Pipe, buffer:Bytes, size:Ref):Int return cast null; + static public function pipe_pending_instances(handle:Pipe, count:Int):Void {} + static public function pipe_pending_count(handle:Pipe):Int return cast null; + static public function pipe_pending_type(handle:Pipe):UvHandleType return cast null; + static public function pipe_chmod(handle:Pipe, flags:Int):Int return cast null; + static public function pipe(fds:Ref, read_flags:Int, write_flags:Int):Int return cast null; + static public function prepare_init(loop:Loop, prepare:Prepare):Int return cast null; + static public function prepare_start_with_cb(prepare:Prepare):Int return cast null; + static public function prepare_stop(prepare:Prepare):Int return cast null; + static public function disable_stdio_inheritance():Void {} + static public function spawn(loop:Loop, handle:Process, options:ProcessOptions):Int return cast null; + static public function process_kill(handle:Process, signum:Int):Int return cast null; + static public function kill(pid:Int, signum:Int):Int return cast null; + static public function process_get_pid(handle:Process):Pid return cast null; + static public function cancel(req:Req):Int return cast null; + static public function req_size(type:UvReqType):U64 return cast null; + static public function req_get_data(req:Req):Pointer return cast null; + static public function req_set_data(req:Req, data:Pointer):Pointer return cast null; + static public function req_get_type(req:Req):UvReqType return cast null; + static public function req_type_name(type:UvReqType):Bytes return cast null; + static public function signal_init(loop:Loop, signal:Signal):Int return cast null; + static public function signal_start_with_cb(signal:Signal, signum:Int):Int return cast null; + static public function signal_start_oneshot_with_cb(signal:Signal, signum:Int):Int return cast null; + static public function signal_stop(signal:Signal):Int return cast null; + static public function shutdown_with_cb(req:Shutdown, handle:Stream):Int return cast null; + static public function listen_with_cb(stream:Stream, backlog:Int):Int return cast null; + static public function accept(server:Stream, client:Stream):Int return cast null; + static public function read_start_with_cb(stream:Stream):Int return cast null; + static public function read_stop(:Stream):Int return cast null; + static public function write_with_cb(req:Write, handle:Stream, bufs:Ref, nbufs:UInt):Int return cast null; + static public function write2_with_cb(req:Write, handle:Stream, bufs:Ref, nbufs:UInt, send_handle:Stream):Int return cast null; + static public function try_write(handle:Stream, bufs:Ref, nbufs:UInt):Int return cast null; + static public function try_write2(handle:Stream, bufs:Ref, nbufs:UInt, send_handle:Stream):Int return cast null; + static public function is_readable(handle:Stream):Int return cast null; + static public function is_writable(handle:Stream):Int return cast null; + static public function stream_set_blocking(handle:Stream, blocking:Int):Int return cast null; + static public function stream_get_write_queue_size(stream:Stream):U64 return cast null; + static public function tcp_init(loop:Loop, handle:Tcp):Int return cast null; + static public function tcp_init_ex(loop:Loop, handle:Tcp, flags:UInt):Int return cast null; + static public function tcp_open(handle:Tcp, sock:OsSock):Int return cast null; + static public function tcp_nodelay(handle:Tcp, enable:Int):Int return cast null; + static public function tcp_keepalive(handle:Tcp, enable:Int, delay:UInt):Int return cast null; + static public function tcp_simultaneous_accepts(handle:Tcp, enable:Int):Int return cast null; + static public function tcp_bind(handle:Tcp, addr:Ref, flags:UInt):Int return cast null; + static public function tcp_getsockname(handle:Tcp, name:Ref, namelen:Ref):Int return cast null; + static public function tcp_getpeername(handle:Tcp, name:Ref, namelen:Ref):Int return cast null; + static public function tcp_connect_with_cb(req:Connect, handle:Tcp, addr:Ref):Int return cast null; + static public function tcp_close_reset_with_cb(handle:Tcp):Int return cast null; + static public function socketpair(type:Int, protocol:Int, socket_vector:Ref, flags0:Int, flags1:Int):Int return cast null; + static public function timer_init(loop:Loop, handle:Timer):Int return cast null; + static public function timer_start_with_cb(handle:Timer, timeout:U64, repeat:U64):Int return cast null; + static public function timer_stop(handle:Timer):Int return cast null; + static public function timer_again(handle:Timer):Int return cast null; + static public function timer_set_repeat(handle:Timer, repeat:U64):Void {} + static public function timer_get_repeat(handle:Timer):U64 return cast null; + static public function timer_get_due_in(handle:Timer):U64 return cast null; + static public function tty_init(loop:Loop, handle:Tty, fd:UvFile, unused:Int):Int return cast null; + static public function tty_set_mode(handle:Tty, mode:TtyMode):Int return cast null; + static public function tty_reset_mode():Int return cast null; + static public function tty_get_winsize(handle:Tty, width:Ref, height:Ref):Int return cast null; + static public function tty_set_vterm_state(state:TtyVtermstate):Void {} + static public function tty_get_vterm_state(state:TtyVtermstate):Int return cast null; + static public function udp_init(loop:Loop, handle:Udp):Int return cast null; + static public function udp_init_ex(loop:Loop, handle:Udp, flags:UInt):Int return cast null; + static public function udp_open(handle:Udp, sock:OsSock):Int return cast null; + static public function udp_bind(handle:Udp, addr:Ref, flags:UInt):Int return cast null; + static public function udp_connect(handle:Udp, addr:Ref):Int return cast null; + static public function udp_getpeername(handle:Udp, name:Ref, namelen:Ref):Int return cast null; + static public function udp_getsockname(handle:Udp, name:Ref, namelen:Ref):Int return cast null; + static public function udp_set_membership(handle:Udp, multicast_addr:Bytes, interface_addr:Bytes, membership:UvMembership):Int return cast null; + static public function udp_set_source_membership(handle:Udp, multicast_addr:Bytes, interface_addr:Bytes, source_addr:Bytes, membership:UvMembership):Int return cast null; + static public function udp_set_multicast_loop(handle:Udp, on:Int):Int return cast null; + static public function udp_set_multicast_ttl(handle:Udp, ttl:Int):Int return cast null; + static public function udp_set_multicast_interface(handle:Udp, interface_addr:Bytes):Int return cast null; + static public function udp_set_broadcast(handle:Udp, on:Int):Int return cast null; + static public function udp_set_ttl(handle:Udp, ttl:Int):Int return cast null; + static public function udp_send_with_cb(req:UdpSend, handle:Udp, bufs:Ref, nbufs:UInt, addr:Ref):Int return cast null; + static public function udp_try_send(handle:Udp, bufs:Ref, nbufs:UInt, addr:Ref):Int return cast null; + static public function udp_recv_start_with_cb(handle:Udp):Int return cast null; + static public function udp_using_recvmmsg(handle:Udp):Int return cast null; + static public function udp_recv_stop(handle:Udp):Int return cast null; + static public function udp_get_send_queue_size(handle:Udp):U64 return cast null; + static public function udp_get_send_queue_count(handle:Udp):U64 return cast null; + static public function version():UInt return cast null; + static public function version_string():Bytes return cast null; +} diff --git a/other/uvgenerator/UV.hx.header b/other/uvgenerator/UV.hx.header new file mode 100644 index 000000000..e149f1955 --- /dev/null +++ b/other/uvgenerator/UV.hx.header @@ -0,0 +1,50 @@ +/* + * Copyright (C)2005-2019 Haxe Foundation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +package hl.uv; + +// This file is automatically generated by a tool in Hashlink repo. +// see /other/uvgenerator + +// Contents of UV.hx.header + +typedef UInt = Int; +typedef U64 = I64; + +/** + Automatically generated bindings for libuv. + Avoid using this module directly. + BACKWARD COMPATIBILITY OF THIS MODULE IS NOT MAINTAINED. +**/ +@:hlNative("uv") +class UV { + static public inline function resolve(result:Int):Int { + if(result < 0) + throw new UVException(translate_uv_error(result)); + return this; + } + + static public function translate_uv_error(uvErrno:Int):UVError return UV_NOERR; + static public function translate_to_uv_error(errno:Int):Int return 0; + static public function handle_set_data_with_gc(data:HandleData):Void {} + +// Auto generated diff --git a/other/uvgenerator/UVGenerator.hx b/other/uvgenerator/UVGenerator.hx index fcc2f432e..16fc57639 100644 --- a/other/uvgenerator/UVGenerator.hx +++ b/other/uvgenerator/UVGenerator.hx @@ -21,17 +21,24 @@ typedef FunctionSignature = { class Skip extends Exception {} class UVGenerator { + static inline var DOCS = 'include/libuv/docs/src'; + static inline var UV_GENERATED_C = 'libs/uv/uv_generated.c'; + static inline var UV_HX = 'other/uvgenerator/UV.hx'; + static inline var UV_HX_HEADER = 'other/uvgenerator/UV.hx.header'; + static final skipDocs = ['api', 'dll', 'guide', 'index', 'migration_010_100', 'poll', 'threading', 'threadpool', 'upgrading']; - static final skipFunctions = ['uv_loop_configure']; + static final skipFunctions = ['uv_loop_configure']; // TODO: don't skip these static final allowNoCallback = ['uv_fs_cb']; static function main() { var root = rootDir(); - var uvGeneratedC = Path.join([root, 'libs', 'uv', 'uv_generated.c']); - var docsDir = Path.join([root, 'include', 'libuv', 'docs', 'src']); - var outFile = File.write(uvGeneratedC); + var docsDir = Path.join([root, DOCS]); var scan = DirSync.scan(docsDir).resolve(); + var cFile = File.write(Path.join([root, UV_GENERATED_C])); + var hxFile = File.write(Path.join([root, UV_HX])); + + hxFile.writeString(File.getContent(Path.join([root, UV_HX_HEADER]))); var entry = null; while(null != (entry = scan.next())) { @@ -53,26 +60,31 @@ class UVGenerator { } catch(e:Skip) { continue; } - var needsWrapper = false; - for(a in sig.arguments) - if(a.type.endsWith('_cb')) { - needsWrapper = true; - break; - } - var str = needsWrapper ? generateWrapperWithCb(sig) : generateBinding(sig); - outFile.writeString(str); + var cStr = needsCbWrapper(sig) ? cWrapperWithCb(sig) : cBinding(sig); + cFile.writeString(cStr); + hxFile.writeString(hxBinding(sig)); } catch(e) { Sys.stderr().writeString('Error on line: $line\n${e.details()}\n'); Sys.exit(1); } - outFile.writeString('\n'); + cFile.writeString('\n'); } } + hxFile.writeString('}\n'); - outFile.close(); + cFile.close(); + hxFile.close(); scan.end(); } + static function needsCbWrapper(sig:FunctionSignature):Bool { + for(a in sig.arguments) + if(a.type.endsWith('_cb')) { + return true; + } + return false; + } + static function rootDir(?p:PosInfos):String { var generatorPath = FileSync.realPath(p.fileName).resolve().toString(); return new Path(new Path(new Path(generatorPath).dir).dir).dir; @@ -106,7 +118,7 @@ class UVGenerator { } } - static function mapType(type:String):String { + static function mapHLType(type:String):String { return switch type { case 'int': '_I32'; case 'int64_t': '_I64'; @@ -125,7 +137,7 @@ class UVGenerator { case 'uv_pid_t': '_I32'; case 'uv_buf_t': '_BUF'; case _ if(type.endsWith('**')): - '_REF(' + mapType(type.substr(0, type.length - 1)) + ')'; + '_REF(' + mapHLType(type.substr(0, type.length - 1)) + ')'; case _ if(type.startsWith('uv_')): type = type.substr(3); if(type.endsWith('_t*')) @@ -135,12 +147,48 @@ class UVGenerator { type = '_' + type.substr('struct '.length).toUpperCase(); type.endsWith('*') ? '_REF(${type.substr(0, type.length - 1)})' : type; case _ if(type.startsWith('const ')): - mapType(type.substr('const '.length)); + mapHLType(type.substr('const '.length)); case _: throw 'Unknown type: "$type"'; } } + static final reCapitalize = ~/_[a-z]/g; + + static function snakeToPascalCase(str:String):String { + return str.charAt(0).toUpperCase() + reCapitalize.map(str.substr(1), r -> r.matched(0).replace('_', '').toUpperCase()); + } + + static function mapHXType(type:String):String { + if(type.startsWith('const ')) + type = type.substr('const '.length); + if(type.startsWith('struct ')) + type = type.substr('struct '.length); + + return switch type { + case 'void*': 'Pointer'; + case 'char*': 'Bytes'; + case 'double': 'Float'; + case 'int64_t': 'I64'; + case 'uint64_t': 'U64'; + case 'size_t': 'U64'; + case 'ssize_t': 'I64'; + case _ if(type.startsWith('unsigned ')): + 'U' + mapHXType(type.substr('unsigned '.length)); + case _ if(type.startsWith('uv_') && type.endsWith('_t*')): + type = type.substr(3, type.length - 3 - 3); + snakeToPascalCase(type); + case _ if(type.startsWith('uv_') && type.endsWith('_t')): + type = type.substr(3, type.length - 3 - 2); + snakeToPascalCase(type); + case _ if(type.endsWith('*')): + var hxType = mapHXType(type.substr(0, type.length - 1)); + 'Ref<$hxType>'; + case _: + snakeToPascalCase(type); + } + } + static function functionName(name:String):String { return if(name.startsWith('uv_')) name.substr('uv_'.length) @@ -148,19 +196,52 @@ class UVGenerator { throw 'Function name is expected to start with "uv_": "$name"'; } + static function functionNameWithCb(name:String):String { + return '${functionName(name)}_with_cb'; + } + static function callbackName(type:String):String { - // if(type.startsWith('uv_')) - // type = type.substr('uv_'.length); return 'on_${type}'; } - static function generateBinding(sig:FunctionSignature):String { - var args = sig.arguments.map(a -> mapType(a.type)).join(' '); - return 'DEFINE_PRIM(${mapType(sig.returnType)}, ${functionName(sig.name)}, $args);\n'; + static function hxBinding(sig:FunctionSignature):String { + function compose(name:String, args:Array) { + var args = args.join(', '); + var ret = mapHXType(sig.returnType); + var body = switch ret { + case 'Void': '{}'; + case _: 'return cast null;'; + } + return '\tstatic public function $name($args):$ret $body\n'; + } + function mapArg(a:TypeAndName):String { + if(a.name.endsWith(']')) { + var openPos = a.name.lastIndexOf('['); + return '${a.name.substring(0, openPos)}:Ref<${mapHXType(a.type)}>'; + } else { + return '${a.name}:${mapHXType(a.type)}'; + } + } + if(needsCbWrapper(sig)) { + var fnName = functionNameWithCb(sig.name); + var args = sig.arguments + .filter(a -> !a.type.endsWith('_cb') || allowNoCallback.contains(a.type)) + .map(a -> a.type.endsWith('_cb') && allowNoCallback.contains(a.type) ? 'use_${a.type}:Bool' : mapArg(a)); + return compose(fnName, args); + } else { + var fnName = functionName(sig.name); + var args = sig.arguments.filter(a -> a.type != 'void').map(mapArg); + return compose(fnName, args); + } + } + + static function cBinding(sig:FunctionSignature):String { + var args = sig.arguments.map(a -> mapHLType(a.type)).join(' '); + return 'DEFINE_PRIM(${mapHLType(sig.returnType)}, ${functionName(sig.name)}, $args);\n'; } - static function generateWrapperWithCb(sig:FunctionSignature):String { - var fnName = '${functionName(sig.name)}_with_cb'; + static function cWrapperWithCb(sig:FunctionSignature):String { + var fnName = functionNameWithCb(sig.name); var args = sig.arguments .filter(a -> !a.type.endsWith('_cb') || allowNoCallback.contains(a.type)) @@ -183,9 +264,9 @@ class UVGenerator { var args = sig.arguments .filter(a -> !a.type.endsWith('_cb') || allowNoCallback.contains(a.type)) - .map(a -> a.type.endsWith('_cb') && allowNoCallback.contains(a.type) ? '_BOOL' : mapType(a.type)) + .map(a -> a.type.endsWith('_cb') && allowNoCallback.contains(a.type) ? '_BOOL' : mapHLType(a.type)) .join(' '); - lines.push('DEFINE_PRIM(${mapType(sig.returnType)}, $fnName, $args);'); + lines.push('DEFINE_PRIM(${mapHLType(sig.returnType)}, $fnName, $args);'); return lines.join('\n') + '\n'; } From 585f699d78e73e7b10d409d9b765e210f56d67e8 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Wed, 18 Aug 2021 13:59:19 +0300 Subject: [PATCH 060/117] fixes to uvgenerator --- libs/uv/uv.c | 61 +-- libs/uv/uv_generated.c | 830 +++++++++++++++---------------- other/uvgenerator/.gitignore | 1 + other/uvgenerator/UV.hx | 292 ----------- other/uvgenerator/UV.hx.header | 20 +- other/uvgenerator/UVGenerator.hx | 11 +- 6 files changed, 466 insertions(+), 749 deletions(-) create mode 100644 other/uvgenerator/.gitignore delete mode 100644 other/uvgenerator/UV.hx diff --git a/libs/uv/uv.c b/libs/uv/uv.c index d37be70aa..053704c9e 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -11,6 +11,30 @@ # error "libuv1-dev required, uv version 0.x found" #endif +// Common macros + +#define _U64 _I64 + +#define _POINTER _ABSTRACT(void_pointer) +#define _HANDLE _ABSTRACT(uv_handle) +#define _REQUEST _ABSTRACT(uv_req) +#define _LOOP _ABSTRACT(uv_loop) +#define _ASYNC _HANDLE +#define _CHECK _HANDLE +#define _TIMER _HANDLE +#define _GETADDRINFO _ABSTRACT(uv_getaddrinfo_t) +#define _HANDLE_TYPE _I32 +#define _OS_FD _ABSTRACT(uv_os_fd) + +#define UV_ALLOC(t) ((t*)malloc(sizeof(t))) +#define DATA(t,h) ((t)h->data) + +#define DEFINE_PRIM_ALLOC(r,t) \ + HL_PRIM uv_##t##_t *HL_NAME(alloc_##t)() { \ + return UV_ALLOC(uv_##t##_t); \ + } \ + DEFINE_PRIM(r, alloc_##t, _NO_ARG); + // Errors #define HL_UV_NOERR 0 @@ -282,30 +306,6 @@ HL_PRIM int HL_NAME(translate_to_uv_error)( int hl_errno ) { } DEFINE_PRIM(_I32, translate_to_uv_error, _I32); -// Common macros - -#define _U64 _I64 - -#define _POINTER _ABSTRACT(void_pointer) -#define _HANDLE _ABSTRACT(uv_handle) -#define _REQUEST _ABSTRACT(uv_req) -#define _LOOP _ABSTRACT(uv_loop) -#define _ASYNC _HANDLE -#define _CHECK _HANDLE -#define _TIMER _HANDLE -#define _GETADDRINFO _ABSTRACT(uv_getaddrinfo_t) -#define _HANDLE_TYPE _I32 -#define _OS_FD _ABSTRACT(uv_os_fd) - -#define UV_ALLOC(t) ((t*)malloc(sizeof(t))) -#define DATA(t,h) ((t)h->data) - -#define DEFINE_PRIM_ALLOC(r,t) \ - HL_PRIM t *HL_NAME(alloc_##t)() { \ - return UV_ALLOC(t); \ - } \ - DEFINE_PRIM(r, alloc_##t, _NO_ARG); - // Handle #define HANDLE_DATA_FIELDS \ @@ -357,7 +357,7 @@ typedef struct { vclosure *onSend; } vasync_data; -DEFINE_PRIM_ALLOC(_ASYNC, uv_async_t); +DEFINE_PRIM_ALLOC(_ASYNC, async); static void on_uv_async_cb( uv_async_t *h ) { vclosure *c = DATA(vasync_data *, h)->onSend; @@ -371,7 +371,7 @@ typedef struct { vclosure *onTick; } vtimer_data; -DEFINE_PRIM_ALLOC(_TIMER, uv_timer_t); +DEFINE_PRIM_ALLOC(_TIMER, timer); static void on_uv_timer_cb( uv_timer_t *h ) { vclosure *c = DATA(vtimer_data *, h)->onTick; @@ -382,10 +382,13 @@ static void on_uv_timer_cb( uv_timer_t *h ) { #define _RUN_MODE _I32 -DEFINE_PRIM_ALLOC(_LOOP,uv_loop_t); +DEFINE_PRIM_ALLOC(_LOOP, loop); +// auto-generated libuv bindings +#include "uv_generated.c" +// TODO: remove everything below #define _DIR _ABSTRACT(uv_dir) #define _SOCKADDR _ABSTRACT(uv_sockaddr_storage) @@ -2680,6 +2683,4 @@ DEFINE_PRIM(_BYTES, version_suffix, _NO_ARG); HL_PRIM int HL_NAME(version_hex)() { return UV_VERSION_HEX; } -DEFINE_PRIM(_I32, version_hex, _NO_ARG); - -#include "uv_generated.c" \ No newline at end of file +DEFINE_PRIM(_I32, version_hex, _NO_ARG); \ No newline at end of file diff --git a/libs/uv/uv_generated.c b/libs/uv/uv_generated.c index 4384f9508..3ea358462 100644 --- a/libs/uv/uv_generated.c +++ b/libs/uv/uv_generated.c @@ -5,258 +5,258 @@ DEFINE_PRIM(_I32, async_init_with_cb, _LOOP _ASYNC); DEFINE_PRIM(_I32, async_send, _ASYNC); -DEFINE_PRIM(_I32, check_init, _LOOP _CHECK); +// DEFINE_PRIM(_I32, check_init, _LOOP _CHECK); -HL_PRIM int HL_NAME(check_start_with_cb)( uv_check_t* check ) { - return uv_check_start(check, on_uv_check_cb); -} -DEFINE_PRIM(_I32, check_start_with_cb, _CHECK); +// HL_PRIM int HL_NAME(check_start_with_cb)( uv_check_t* check ) { +// return uv_check_start(check, on_uv_check_cb); +// } +// DEFINE_PRIM(_I32, check_start_with_cb, _CHECK); -DEFINE_PRIM(_I32, check_stop, _CHECK); +// DEFINE_PRIM(_I32, check_stop, _CHECK); -HL_PRIM int HL_NAME(getaddrinfo_with_cb)( uv_loop_t* loop, uv_getaddrinfo_t* req, const char* node, const char* service, const struct addrinfo* hints ) { - return uv_getaddrinfo(loop, req, on_uv_getaddrinfo_cb, node, service, hints); -} -DEFINE_PRIM(_I32, getaddrinfo_with_cb, _LOOP _GETADDRINFO _BYTES _BYTES _REF(_ADDRINFO)); +// HL_PRIM int HL_NAME(getaddrinfo_with_cb)( uv_loop_t* loop, uv_getaddrinfo_t* req, const char* node, const char* service, const struct addrinfo* hints ) { +// return uv_getaddrinfo(loop, req, on_uv_getaddrinfo_cb, node, service, hints); +// } +// DEFINE_PRIM(_I32, getaddrinfo_with_cb, _LOOP _GETADDRINFO _BYTES _BYTES _REF(_ADDRINFO)); -DEFINE_PRIM(_VOID, freeaddrinfo, _REF(_ADDRINFO)); +// DEFINE_PRIM(_VOID, freeaddrinfo, _REF(_ADDRINFO)); -HL_PRIM int HL_NAME(getnameinfo_with_cb)( uv_loop_t* loop, uv_getnameinfo_t* req, const struct sockaddr* addr, int flags ) { - return uv_getnameinfo(loop, req, on_uv_getnameinfo_cb, addr, flags); -} -DEFINE_PRIM(_I32, getnameinfo_with_cb, _LOOP _GETNAMEINFO _REF(_SOCKADDR) _I32); +// HL_PRIM int HL_NAME(getnameinfo_with_cb)( uv_loop_t* loop, uv_getnameinfo_t* req, const struct sockaddr* addr, int flags ) { +// return uv_getnameinfo(loop, req, on_uv_getnameinfo_cb, addr, flags); +// } +// DEFINE_PRIM(_I32, getnameinfo_with_cb, _LOOP _GETNAMEINFO _REF(_SOCKADDR) _I32); DEFINE_PRIM(_BYTES, strerror, _I32); -DEFINE_PRIM(_BYTES, strerror_r, _I32 _BYTES _U64); +// DEFINE_PRIM(_BYTES, strerror_r, _I32 _BYTES _U64); DEFINE_PRIM(_BYTES, err_name, _I32); -DEFINE_PRIM(_BYTES, err_name_r, _I32 _BYTES _U64); - -DEFINE_PRIM(_I32, translate_sys_error, _I32); - -DEFINE_PRIM(_VOID, fs_req_cleanup, _FS); - -HL_PRIM int HL_NAME(fs_close_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { - return uv_fs_close(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_close_with_cb, _LOOP _FS _FILE _BOOL); - -HL_PRIM int HL_NAME(fs_open_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, bool use_uv_fs_cb ) { - return uv_fs_open(loop, req, path, flags, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_open_with_cb, _LOOP _FS _BYTES _I32 _I32 _BOOL); - -HL_PRIM int HL_NAME(fs_read_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, bool use_uv_fs_cb ) { - return uv_fs_read(loop, req, file, bufs[], nbufs, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_read_with_cb, _LOOP _FS _FILE _BUF _U32 _I64 _BOOL); - -HL_PRIM int HL_NAME(fs_unlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { - return uv_fs_unlink(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_unlink_with_cb, _LOOP _FS _BYTES _BOOL); - -HL_PRIM int HL_NAME(fs_write_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, bool use_uv_fs_cb ) { - return uv_fs_write(loop, req, file, bufs[], nbufs, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_write_with_cb, _LOOP _FS _FILE _BUF _U32 _I64 _BOOL); - -HL_PRIM int HL_NAME(fs_mkdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { - return uv_fs_mkdir(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_mkdir_with_cb, _LOOP _FS _BYTES _I32 _BOOL); - -HL_PRIM int HL_NAME(fs_mkdtemp_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* tpl, bool use_uv_fs_cb ) { - return uv_fs_mkdtemp(loop, req, tpl, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_mkdtemp_with_cb, _LOOP _FS _BYTES _BOOL); - -HL_PRIM int HL_NAME(fs_mkstemp_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* tpl, bool use_uv_fs_cb ) { - return uv_fs_mkstemp(loop, req, tpl, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_mkstemp_with_cb, _LOOP _FS _BYTES _BOOL); - -HL_PRIM int HL_NAME(fs_rmdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { - return uv_fs_rmdir(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_rmdir_with_cb, _LOOP _FS _BYTES _BOOL); - -HL_PRIM int HL_NAME(fs_opendir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { - return uv_fs_opendir(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_opendir_with_cb, _LOOP _FS _BYTES _BOOL); - -HL_PRIM int HL_NAME(fs_closedir_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, bool use_uv_fs_cb ) { - return uv_fs_closedir(loop, req, dir, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_closedir_with_cb, _LOOP _FS _DIR _BOOL); - -HL_PRIM int HL_NAME(fs_readdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, bool use_uv_fs_cb ) { - return uv_fs_readdir(loop, req, dir, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_readdir_with_cb, _LOOP _FS _DIR _BOOL); - -HL_PRIM int HL_NAME(fs_scandir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, bool use_uv_fs_cb ) { - return uv_fs_scandir(loop, req, path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_scandir_with_cb, _LOOP _FS _BYTES _I32 _BOOL); - -DEFINE_PRIM(_I32, fs_scandir_next, _FS _DIRENT); - -HL_PRIM int HL_NAME(fs_stat_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { - return uv_fs_stat(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_stat_with_cb, _LOOP _FS _BYTES _BOOL); - -HL_PRIM int HL_NAME(fs_fstat_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { - return uv_fs_fstat(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_fstat_with_cb, _LOOP _FS _FILE _BOOL); - -HL_PRIM int HL_NAME(fs_lstat_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { - return uv_fs_lstat(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_lstat_with_cb, _LOOP _FS _BYTES _BOOL); - -HL_PRIM int HL_NAME(fs_statfs_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { - return uv_fs_statfs(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_statfs_with_cb, _LOOP _FS _BYTES _BOOL); - -HL_PRIM int HL_NAME(fs_rename_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, bool use_uv_fs_cb ) { - return uv_fs_rename(loop, req, path, new_path, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_rename_with_cb, _LOOP _FS _BYTES _BYTES _BOOL); - -HL_PRIM int HL_NAME(fs_fsync_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { - return uv_fs_fsync(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_fsync_with_cb, _LOOP _FS _FILE _BOOL); +// DEFINE_PRIM(_BYTES, err_name_r, _I32 _BYTES _U64); + +// DEFINE_PRIM(_I32, translate_sys_error, _I32); + +// DEFINE_PRIM(_VOID, fs_req_cleanup, _FS); + +// HL_PRIM int HL_NAME(fs_close_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { +// return uv_fs_close(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_close_with_cb, _LOOP _FS _FILE _BOOL); + +// HL_PRIM int HL_NAME(fs_open_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, bool use_uv_fs_cb ) { +// return uv_fs_open(loop, req, path, flags, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_open_with_cb, _LOOP _FS _BYTES _I32 _I32 _BOOL); + +// HL_PRIM int HL_NAME(fs_read_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, bool use_uv_fs_cb ) { +// return uv_fs_read(loop, req, file, bufs[], nbufs, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_read_with_cb, _LOOP _FS _FILE _BUF _U32 _I64 _BOOL); + +// HL_PRIM int HL_NAME(fs_unlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { +// return uv_fs_unlink(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_unlink_with_cb, _LOOP _FS _BYTES _BOOL); + +// HL_PRIM int HL_NAME(fs_write_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, bool use_uv_fs_cb ) { +// return uv_fs_write(loop, req, file, bufs[], nbufs, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_write_with_cb, _LOOP _FS _FILE _BUF _U32 _I64 _BOOL); + +// HL_PRIM int HL_NAME(fs_mkdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { +// return uv_fs_mkdir(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_mkdir_with_cb, _LOOP _FS _BYTES _I32 _BOOL); + +// HL_PRIM int HL_NAME(fs_mkdtemp_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* tpl, bool use_uv_fs_cb ) { +// return uv_fs_mkdtemp(loop, req, tpl, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_mkdtemp_with_cb, _LOOP _FS _BYTES _BOOL); + +// HL_PRIM int HL_NAME(fs_mkstemp_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* tpl, bool use_uv_fs_cb ) { +// return uv_fs_mkstemp(loop, req, tpl, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_mkstemp_with_cb, _LOOP _FS _BYTES _BOOL); + +// HL_PRIM int HL_NAME(fs_rmdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { +// return uv_fs_rmdir(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_rmdir_with_cb, _LOOP _FS _BYTES _BOOL); + +// HL_PRIM int HL_NAME(fs_opendir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { +// return uv_fs_opendir(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_opendir_with_cb, _LOOP _FS _BYTES _BOOL); + +// HL_PRIM int HL_NAME(fs_closedir_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, bool use_uv_fs_cb ) { +// return uv_fs_closedir(loop, req, dir, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_closedir_with_cb, _LOOP _FS _DIR _BOOL); + +// HL_PRIM int HL_NAME(fs_readdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, bool use_uv_fs_cb ) { +// return uv_fs_readdir(loop, req, dir, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_readdir_with_cb, _LOOP _FS _DIR _BOOL); + +// HL_PRIM int HL_NAME(fs_scandir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, bool use_uv_fs_cb ) { +// return uv_fs_scandir(loop, req, path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_scandir_with_cb, _LOOP _FS _BYTES _I32 _BOOL); + +// DEFINE_PRIM(_I32, fs_scandir_next, _FS _DIRENT); + +// HL_PRIM int HL_NAME(fs_stat_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { +// return uv_fs_stat(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_stat_with_cb, _LOOP _FS _BYTES _BOOL); + +// HL_PRIM int HL_NAME(fs_fstat_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { +// return uv_fs_fstat(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_fstat_with_cb, _LOOP _FS _FILE _BOOL); + +// HL_PRIM int HL_NAME(fs_lstat_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { +// return uv_fs_lstat(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_lstat_with_cb, _LOOP _FS _BYTES _BOOL); + +// HL_PRIM int HL_NAME(fs_statfs_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { +// return uv_fs_statfs(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_statfs_with_cb, _LOOP _FS _BYTES _BOOL); + +// HL_PRIM int HL_NAME(fs_rename_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, bool use_uv_fs_cb ) { +// return uv_fs_rename(loop, req, path, new_path, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_rename_with_cb, _LOOP _FS _BYTES _BYTES _BOOL); + +// HL_PRIM int HL_NAME(fs_fsync_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { +// return uv_fs_fsync(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_fsync_with_cb, _LOOP _FS _FILE _BOOL); + +// HL_PRIM int HL_NAME(fs_fdatasync_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { +// return uv_fs_fdatasync(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_fdatasync_with_cb, _LOOP _FS _FILE _BOOL); + +// HL_PRIM int HL_NAME(fs_ftruncate_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, int64_t offset, bool use_uv_fs_cb ) { +// return uv_fs_ftruncate(loop, req, file, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_ftruncate_with_cb, _LOOP _FS _FILE _I64 _BOOL); + +// HL_PRIM int HL_NAME(fs_copyfile_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, bool use_uv_fs_cb ) { +// return uv_fs_copyfile(loop, req, path, new_path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_copyfile_with_cb, _LOOP _FS _BYTES _BYTES _I32 _BOOL); + +// HL_PRIM int HL_NAME(fs_sendfile_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file out_fd, uv_file in_fd, int64_t in_offset, size_t length, bool use_uv_fs_cb ) { +// return uv_fs_sendfile(loop, req, out_fd, in_fd, in_offset, length, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_sendfile_with_cb, _LOOP _FS _FILE _FILE _I64 _U64 _BOOL); + +// HL_PRIM int HL_NAME(fs_access_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { +// return uv_fs_access(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_access_with_cb, _LOOP _FS _BYTES _I32 _BOOL); -HL_PRIM int HL_NAME(fs_fdatasync_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { - return uv_fs_fdatasync(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_fdatasync_with_cb, _LOOP _FS _FILE _BOOL); +// HL_PRIM int HL_NAME(fs_chmod_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { +// return uv_fs_chmod(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_chmod_with_cb, _LOOP _FS _BYTES _I32 _BOOL); -HL_PRIM int HL_NAME(fs_ftruncate_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, int64_t offset, bool use_uv_fs_cb ) { - return uv_fs_ftruncate(loop, req, file, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_ftruncate_with_cb, _LOOP _FS _FILE _I64 _BOOL); +// HL_PRIM int HL_NAME(fs_fchmod_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, int mode, bool use_uv_fs_cb ) { +// return uv_fs_fchmod(loop, req, file, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_fchmod_with_cb, _LOOP _FS _FILE _I32 _BOOL); -HL_PRIM int HL_NAME(fs_copyfile_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, bool use_uv_fs_cb ) { - return uv_fs_copyfile(loop, req, path, new_path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_copyfile_with_cb, _LOOP _FS _BYTES _BYTES _I32 _BOOL); +// HL_PRIM int HL_NAME(fs_utime_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, bool use_uv_fs_cb ) { +// return uv_fs_utime(loop, req, path, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_utime_with_cb, _LOOP _FS _BYTES _F64 _F64 _BOOL); -HL_PRIM int HL_NAME(fs_sendfile_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file out_fd, uv_file in_fd, int64_t in_offset, size_t length, bool use_uv_fs_cb ) { - return uv_fs_sendfile(loop, req, out_fd, in_fd, in_offset, length, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_sendfile_with_cb, _LOOP _FS _FILE _FILE _I64 _U64 _BOOL); +// HL_PRIM int HL_NAME(fs_futime_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime, double mtime, bool use_uv_fs_cb ) { +// return uv_fs_futime(loop, req, file, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_futime_with_cb, _LOOP _FS _FILE _F64 _F64 _BOOL); -HL_PRIM int HL_NAME(fs_access_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { - return uv_fs_access(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_access_with_cb, _LOOP _FS _BYTES _I32 _BOOL); +// HL_PRIM int HL_NAME(fs_lutime_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, bool use_uv_fs_cb ) { +// return uv_fs_lutime(loop, req, path, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_lutime_with_cb, _LOOP _FS _BYTES _F64 _F64 _BOOL); -HL_PRIM int HL_NAME(fs_chmod_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { - return uv_fs_chmod(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_chmod_with_cb, _LOOP _FS _BYTES _I32 _BOOL); +// HL_PRIM int HL_NAME(fs_link_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, bool use_uv_fs_cb ) { +// return uv_fs_link(loop, req, path, new_path, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_link_with_cb, _LOOP _FS _BYTES _BYTES _BOOL); -HL_PRIM int HL_NAME(fs_fchmod_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, int mode, bool use_uv_fs_cb ) { - return uv_fs_fchmod(loop, req, file, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_fchmod_with_cb, _LOOP _FS _FILE _I32 _BOOL); +// HL_PRIM int HL_NAME(fs_symlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, bool use_uv_fs_cb ) { +// return uv_fs_symlink(loop, req, path, new_path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_symlink_with_cb, _LOOP _FS _BYTES _BYTES _I32 _BOOL); -HL_PRIM int HL_NAME(fs_utime_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, bool use_uv_fs_cb ) { - return uv_fs_utime(loop, req, path, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_utime_with_cb, _LOOP _FS _BYTES _F64 _F64 _BOOL); +// HL_PRIM int HL_NAME(fs_readlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { +// return uv_fs_readlink(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_readlink_with_cb, _LOOP _FS _BYTES _BOOL); -HL_PRIM int HL_NAME(fs_futime_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime, double mtime, bool use_uv_fs_cb ) { - return uv_fs_futime(loop, req, file, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_futime_with_cb, _LOOP _FS _FILE _F64 _F64 _BOOL); - -HL_PRIM int HL_NAME(fs_lutime_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, bool use_uv_fs_cb ) { - return uv_fs_lutime(loop, req, path, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_lutime_with_cb, _LOOP _FS _BYTES _F64 _F64 _BOOL); - -HL_PRIM int HL_NAME(fs_link_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, bool use_uv_fs_cb ) { - return uv_fs_link(loop, req, path, new_path, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_link_with_cb, _LOOP _FS _BYTES _BYTES _BOOL); - -HL_PRIM int HL_NAME(fs_symlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, bool use_uv_fs_cb ) { - return uv_fs_symlink(loop, req, path, new_path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_symlink_with_cb, _LOOP _FS _BYTES _BYTES _I32 _BOOL); - -HL_PRIM int HL_NAME(fs_readlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { - return uv_fs_readlink(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_readlink_with_cb, _LOOP _FS _BYTES _BOOL); - -HL_PRIM int HL_NAME(fs_realpath_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { - return uv_fs_realpath(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_realpath_with_cb, _LOOP _FS _BYTES _BOOL); +// HL_PRIM int HL_NAME(fs_realpath_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { +// return uv_fs_realpath(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_realpath_with_cb, _LOOP _FS _BYTES _BOOL); -HL_PRIM int HL_NAME(fs_chown_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { - return uv_fs_chown(loop, req, path, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_chown_with_cb, _LOOP _FS _BYTES _UID_T _GID_T _BOOL); +// HL_PRIM int HL_NAME(fs_chown_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { +// return uv_fs_chown(loop, req, path, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_chown_with_cb, _LOOP _FS _BYTES _UID_T _GID_T _BOOL); -HL_PRIM int HL_NAME(fs_fchown_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { - return uv_fs_fchown(loop, req, file, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_fchown_with_cb, _LOOP _FS _FILE _UID_T _GID_T _BOOL); +// HL_PRIM int HL_NAME(fs_fchown_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { +// return uv_fs_fchown(loop, req, file, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_fchown_with_cb, _LOOP _FS _FILE _UID_T _GID_T _BOOL); -HL_PRIM int HL_NAME(fs_lchown_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { - return uv_fs_lchown(loop, req, path, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); -} -DEFINE_PRIM(_I32, fs_lchown_with_cb, _LOOP _FS _BYTES _UID_T _GID_T _BOOL); +// HL_PRIM int HL_NAME(fs_lchown_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { +// return uv_fs_lchown(loop, req, path, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); +// } +// DEFINE_PRIM(_I32, fs_lchown_with_cb, _LOOP _FS _BYTES _UID_T _GID_T _BOOL); -DEFINE_PRIM(_FS_TYPE, fs_get_type, _FS); +// DEFINE_PRIM(_FS_TYPE, fs_get_type, _FS); -DEFINE_PRIM(_I64, fs_get_result, _FS); +// DEFINE_PRIM(_I64, fs_get_result, _FS); -DEFINE_PRIM(_I32, fs_get_system_error, _FS); +// DEFINE_PRIM(_I32, fs_get_system_error, _FS); -DEFINE_PRIM(_POINTER, fs_get_ptr, _FS); +// DEFINE_PRIM(_POINTER, fs_get_ptr, _FS); -DEFINE_PRIM(_BYTES, fs_get_path, _FS); +// DEFINE_PRIM(_BYTES, fs_get_path, _FS); -DEFINE_PRIM(_STAT, fs_get_statbuf, _FS); +// DEFINE_PRIM(_STAT, fs_get_statbuf, _FS); -DEFINE_PRIM(_OS_FD_T, get_osfhandle, _I32); +// DEFINE_PRIM(_OS_FD_T, get_osfhandle, _I32); -DEFINE_PRIM(_I32, open_osfhandle, _OS_FD_T); +// DEFINE_PRIM(_I32, open_osfhandle, _OS_FD_T); -DEFINE_PRIM(_I32, fs_event_init, _LOOP _FS_EVENT); +// DEFINE_PRIM(_I32, fs_event_init, _LOOP _FS_EVENT); -HL_PRIM int HL_NAME(fs_event_start_with_cb)( uv_fs_event_t* handle, const char* path, unsigned int flags ) { - return uv_fs_event_start(handle, on_uv_fs_event_cb, path, flags); -} -DEFINE_PRIM(_I32, fs_event_start_with_cb, _FS_EVENT _BYTES _U32); +// HL_PRIM int HL_NAME(fs_event_start_with_cb)( uv_fs_event_t* handle, const char* path, unsigned int flags ) { +// return uv_fs_event_start(handle, on_uv_fs_event_cb, path, flags); +// } +// DEFINE_PRIM(_I32, fs_event_start_with_cb, _FS_EVENT _BYTES _U32); -DEFINE_PRIM(_I32, fs_event_stop, _FS_EVENT); +// DEFINE_PRIM(_I32, fs_event_stop, _FS_EVENT); -DEFINE_PRIM(_I32, fs_event_getpath, _FS_EVENT _BYTES _REF(_I64)); +// DEFINE_PRIM(_I32, fs_event_getpath, _FS_EVENT _BYTES _REF(_I64)); -DEFINE_PRIM(_I32, fs_poll_init, _LOOP _FS_POLL); +// DEFINE_PRIM(_I32, fs_poll_init, _LOOP _FS_POLL); -HL_PRIM int HL_NAME(fs_poll_start_with_cb)( uv_fs_poll_t* handle, const char* path, unsigned int interval ) { - return uv_fs_poll_start(handle, on_uv_fs_poll_cb, path, interval); -} -DEFINE_PRIM(_I32, fs_poll_start_with_cb, _FS_POLL _BYTES _U32); +// HL_PRIM int HL_NAME(fs_poll_start_with_cb)( uv_fs_poll_t* handle, const char* path, unsigned int interval ) { +// return uv_fs_poll_start(handle, on_uv_fs_poll_cb, path, interval); +// } +// DEFINE_PRIM(_I32, fs_poll_start_with_cb, _FS_POLL _BYTES _U32); -DEFINE_PRIM(_I32, fs_poll_stop, _FS_POLL); +// DEFINE_PRIM(_I32, fs_poll_stop, _FS_POLL); -DEFINE_PRIM(_I32, fs_poll_getpath, _FS_POLL _BYTES _REF(_I64)); +// DEFINE_PRIM(_I32, fs_poll_getpath, _FS_POLL _BYTES _REF(_I64)); DEFINE_PRIM(_I32, is_active, _HANDLE); @@ -273,38 +273,38 @@ DEFINE_PRIM(_VOID, unref, _HANDLE); DEFINE_PRIM(_I32, has_ref, _HANDLE); -DEFINE_PRIM(_U64, handle_size, _HANDLE_TYPE); +// DEFINE_PRIM(_U64, handle_size, _HANDLE_TYPE); -DEFINE_PRIM(_I32, send_buffer_size, _HANDLE _REF(_I32)); +// DEFINE_PRIM(_I32, send_buffer_size, _HANDLE _REF(_I32)); -DEFINE_PRIM(_I32, recv_buffer_size, _HANDLE _REF(_I32)); +// DEFINE_PRIM(_I32, recv_buffer_size, _HANDLE _REF(_I32)); -DEFINE_PRIM(_I32, fileno, _HANDLE _OS_FD); +// DEFINE_PRIM(_I32, fileno, _HANDLE _OS_FD); -DEFINE_PRIM(_LOOP, handle_get_loop, _HANDLE); +// DEFINE_PRIM(_LOOP, handle_get_loop, _HANDLE); DEFINE_PRIM(_POINTER, handle_get_data, _HANDLE); DEFINE_PRIM(_POINTER, handle_set_data, _HANDLE _POINTER); -DEFINE_PRIM(_HANDLE_TYPE, handle_get_type, _HANDLE); +// DEFINE_PRIM(_HANDLE_TYPE, handle_get_type, _HANDLE); -DEFINE_PRIM(_BYTES, handle_type_name, _HANDLE_TYPE); +// DEFINE_PRIM(_BYTES, handle_type_name, _HANDLE_TYPE); -DEFINE_PRIM(_I32, idle_init, _LOOP _IDLE); +// DEFINE_PRIM(_I32, idle_init, _LOOP _IDLE); -HL_PRIM int HL_NAME(idle_start_with_cb)( uv_idle_t* idle ) { - return uv_idle_start(idle, on_uv_idle_cb); -} -DEFINE_PRIM(_I32, idle_start_with_cb, _IDLE); +// HL_PRIM int HL_NAME(idle_start_with_cb)( uv_idle_t* idle ) { +// return uv_idle_start(idle, on_uv_idle_cb); +// } +// DEFINE_PRIM(_I32, idle_start_with_cb, _IDLE); -DEFINE_PRIM(_I32, idle_stop, _IDLE); +// DEFINE_PRIM(_I32, idle_stop, _IDLE); DEFINE_PRIM(_I32, loop_init, _LOOP); DEFINE_PRIM(_I32, loop_close, _LOOP); -DEFINE_PRIM(_LOOP, default_loop, _VOID); +DEFINE_PRIM(_LOOP, default_loop, _NO_ARG); DEFINE_PRIM(_I32, run, _LOOP _RUN_MODE); @@ -312,272 +312,272 @@ DEFINE_PRIM(_I32, loop_alive, _LOOP); DEFINE_PRIM(_VOID, stop, _LOOP); -DEFINE_PRIM(_U64, loop_size, _VOID); +// DEFINE_PRIM(_U64, loop_size, _NO_ARG); -DEFINE_PRIM(_I32, backend_fd, _LOOP); +// DEFINE_PRIM(_I32, backend_fd, _LOOP); -DEFINE_PRIM(_I32, backend_timeout, _LOOP); +// DEFINE_PRIM(_I32, backend_timeout, _LOOP); -DEFINE_PRIM(_I64, now, _LOOP); +// DEFINE_PRIM(_I64, now, _LOOP); -DEFINE_PRIM(_VOID, update_time, _LOOP); +// DEFINE_PRIM(_VOID, update_time, _LOOP); -HL_PRIM void HL_NAME(walk_with_cb)( uv_loop_t* loop, void* arg ) { - uv_walk(loop, on_uv_walk_cb, arg); -} -DEFINE_PRIM(_VOID, walk_with_cb, _LOOP _POINTER); +// HL_PRIM void HL_NAME(walk_with_cb)( uv_loop_t* loop, void* arg ) { +// uv_walk(loop, on_uv_walk_cb, arg); +// } +// DEFINE_PRIM(_VOID, walk_with_cb, _LOOP _POINTER); -DEFINE_PRIM(_I32, loop_fork, _LOOP); +// DEFINE_PRIM(_I32, loop_fork, _LOOP); -DEFINE_PRIM(_POINTER, loop_get_data, _LOOP); +// DEFINE_PRIM(_POINTER, loop_get_data, _LOOP); -DEFINE_PRIM(_POINTER, loop_set_data, _LOOP _POINTER); +// DEFINE_PRIM(_POINTER, loop_set_data, _LOOP _POINTER); -DEFINE_PRIM(_I64, metrics_idle_time, _LOOP); +// DEFINE_PRIM(_I64, metrics_idle_time, _LOOP); -DEFINE_PRIM(_HANDLE_TYPE, guess_handle, _FILE); +// DEFINE_PRIM(_HANDLE_TYPE, guess_handle, _FILE); -DEFINE_PRIM(_I32, replace_allocator, _MALLOC_FUNC _REALLOC_FUNC _CALLOC_FUNC _FREE_FUNC); +// DEFINE_PRIM(_I32, replace_allocator, _MALLOC_FUNC _REALLOC_FUNC _CALLOC_FUNC _FREE_FUNC); -DEFINE_PRIM(_VOID, library_shutdown, _VOID); +// DEFINE_PRIM(_VOID, library_shutdown, _NO_ARG); -DEFINE_PRIM(_BUF, buf_init, _BYTES _U32); +// DEFINE_PRIM(_BUF, buf_init, _BYTES _U32); -DEFINE_PRIM(_REF(_BYTES), setup_args, _I32 _REF(_BYTES)); +// DEFINE_PRIM(_REF(_BYTES), setup_args, _I32 _REF(_BYTES)); -DEFINE_PRIM(_I32, get_process_title, _BYTES _U64); +// DEFINE_PRIM(_I32, get_process_title, _BYTES _U64); -DEFINE_PRIM(_I32, set_process_title, _BYTES); +// DEFINE_PRIM(_I32, set_process_title, _BYTES); -DEFINE_PRIM(_I32, resident_set_memory, _REF(_I64)); +// DEFINE_PRIM(_I32, resident_set_memory, _REF(_I64)); -DEFINE_PRIM(_I32, uptime, _REF(_F64)); +// DEFINE_PRIM(_I32, uptime, _REF(_F64)); -DEFINE_PRIM(_I32, getrusage, _RUSAGE); +// DEFINE_PRIM(_I32, getrusage, _RUSAGE); -DEFINE_PRIM(_I32, os_getpid, _VOID); +// DEFINE_PRIM(_I32, os_getpid, _NO_ARG); -DEFINE_PRIM(_I32, os_getppid, _VOID); +// DEFINE_PRIM(_I32, os_getppid, _NO_ARG); -DEFINE_PRIM(_I32, cpu_info, _REF(_CPU_INFO) _REF(_I32)); +// DEFINE_PRIM(_I32, cpu_info, _REF(_CPU_INFO) _REF(_I32)); -DEFINE_PRIM(_VOID, free_cpu_info, _CPU_INFO _I32); +// DEFINE_PRIM(_VOID, free_cpu_info, _CPU_INFO _I32); -DEFINE_PRIM(_I32, interface_addresses, _REF(_INTERFACE_ADDRESS) _REF(_I32)); +// DEFINE_PRIM(_I32, interface_addresses, _REF(_INTERFACE_ADDRESS) _REF(_I32)); -DEFINE_PRIM(_VOID, free_interface_addresses, _INTERFACE_ADDRESS _I32); +// DEFINE_PRIM(_VOID, free_interface_addresses, _INTERFACE_ADDRESS _I32); -DEFINE_PRIM(_VOID, loadavg, _F64); +// DEFINE_PRIM(_VOID, loadavg, _F64); -DEFINE_PRIM(_I32, ip4_addr, _BYTES _I32 _REF(_SOCKADDR_IN)); +// DEFINE_PRIM(_I32, ip4_addr, _BYTES _I32 _REF(_SOCKADDR_IN)); -DEFINE_PRIM(_I32, ip6_addr, _BYTES _I32 _REF(_SOCKADDR_IN6)); +// DEFINE_PRIM(_I32, ip6_addr, _BYTES _I32 _REF(_SOCKADDR_IN6)); -DEFINE_PRIM(_I32, ip4_name, _REF(_SOCKADDR_IN) _BYTES _U64); +// DEFINE_PRIM(_I32, ip4_name, _REF(_SOCKADDR_IN) _BYTES _U64); -DEFINE_PRIM(_I32, ip6_name, _REF(_SOCKADDR_IN6) _BYTES _U64); +// DEFINE_PRIM(_I32, ip6_name, _REF(_SOCKADDR_IN6) _BYTES _U64); -DEFINE_PRIM(_I32, inet_ntop, _I32 _POINTER _BYTES _U64); +// DEFINE_PRIM(_I32, inet_ntop, _I32 _POINTER _BYTES _U64); -DEFINE_PRIM(_I32, inet_pton, _I32 _BYTES _POINTER); +// DEFINE_PRIM(_I32, inet_pton, _I32 _BYTES _POINTER); -DEFINE_PRIM(_I32, if_indextoname, _U32 _BYTES _REF(_I64)); +// DEFINE_PRIM(_I32, if_indextoname, _U32 _BYTES _REF(_I64)); -DEFINE_PRIM(_I32, if_indextoiid, _U32 _BYTES _REF(_I64)); +// DEFINE_PRIM(_I32, if_indextoiid, _U32 _BYTES _REF(_I64)); -DEFINE_PRIM(_I32, exepath, _BYTES _REF(_I64)); +// DEFINE_PRIM(_I32, exepath, _BYTES _REF(_I64)); -DEFINE_PRIM(_I32, cwd, _BYTES _REF(_I64)); +// DEFINE_PRIM(_I32, cwd, _BYTES _REF(_I64)); -DEFINE_PRIM(_I32, chdir, _BYTES); +// DEFINE_PRIM(_I32, chdir, _BYTES); -DEFINE_PRIM(_I32, os_homedir, _BYTES _REF(_I64)); +// DEFINE_PRIM(_I32, os_homedir, _BYTES _REF(_I64)); -DEFINE_PRIM(_I32, os_tmpdir, _BYTES _REF(_I64)); +// DEFINE_PRIM(_I32, os_tmpdir, _BYTES _REF(_I64)); -DEFINE_PRIM(_I32, os_get_passwd, _PASSWD); +// DEFINE_PRIM(_I32, os_get_passwd, _PASSWD); -DEFINE_PRIM(_VOID, os_free_passwd, _PASSWD); +// DEFINE_PRIM(_VOID, os_free_passwd, _PASSWD); -DEFINE_PRIM(_I64, get_free_memory, _VOID); +// DEFINE_PRIM(_I64, get_free_memory, _NO_ARG); -DEFINE_PRIM(_I64, get_total_memory, _VOID); +// DEFINE_PRIM(_I64, get_total_memory, _NO_ARG); -DEFINE_PRIM(_I64, get_constrained_memory, _VOID); +// DEFINE_PRIM(_I64, get_constrained_memory, _NO_ARG); -DEFINE_PRIM(_I64, hrtime, _VOID); +// DEFINE_PRIM(_I64, hrtime, _NO_ARG); -DEFINE_PRIM(_VOID, print_all_handles, _LOOP _FILE); +// DEFINE_PRIM(_VOID, print_all_handles, _LOOP _FILE); -DEFINE_PRIM(_VOID, print_active_handles, _LOOP _FILE); +// DEFINE_PRIM(_VOID, print_active_handles, _LOOP _FILE); -DEFINE_PRIM(_I32, os_environ, _REF(_ENV_ITEM) _REF(_I32)); +// DEFINE_PRIM(_I32, os_environ, _REF(_ENV_ITEM) _REF(_I32)); -DEFINE_PRIM(_VOID, os_free_environ, _ENV_ITEM _I32); +// DEFINE_PRIM(_VOID, os_free_environ, _ENV_ITEM _I32); -DEFINE_PRIM(_I32, os_getenv, _BYTES _BYTES _REF(_I64)); +// DEFINE_PRIM(_I32, os_getenv, _BYTES _BYTES _REF(_I64)); -DEFINE_PRIM(_I32, os_setenv, _BYTES _BYTES); +// DEFINE_PRIM(_I32, os_setenv, _BYTES _BYTES); -DEFINE_PRIM(_I32, os_unsetenv, _BYTES); +// DEFINE_PRIM(_I32, os_unsetenv, _BYTES); -DEFINE_PRIM(_I32, os_gethostname, _BYTES _REF(_I64)); +// DEFINE_PRIM(_I32, os_gethostname, _BYTES _REF(_I64)); -DEFINE_PRIM(_I32, os_getpriority, _I32 _REF(_I32)); +// DEFINE_PRIM(_I32, os_getpriority, _I32 _REF(_I32)); -DEFINE_PRIM(_I32, os_setpriority, _I32 _I32); +// DEFINE_PRIM(_I32, os_setpriority, _I32 _I32); -DEFINE_PRIM(_I32, os_uname, _UTSNAME); +// DEFINE_PRIM(_I32, os_uname, _UTSNAME); -DEFINE_PRIM(_I32, gettimeofday, _TIMEVAL64); +// DEFINE_PRIM(_I32, gettimeofday, _TIMEVAL64); -HL_PRIM int HL_NAME(random_with_cb)( uv_loop_t* loop, uv_random_t* req, void* buf, size_t buflen, unsigned int flags ) { - return uv_random(loop, req, buf, buflen, flags, on_uv_random_cb); -} -DEFINE_PRIM(_I32, random_with_cb, _LOOP _RANDOM _POINTER _U64 _U32); +// HL_PRIM int HL_NAME(random_with_cb)( uv_loop_t* loop, uv_random_t* req, void* buf, size_t buflen, unsigned int flags ) { +// return uv_random(loop, req, buf, buflen, flags, on_uv_random_cb); +// } +// DEFINE_PRIM(_I32, random_with_cb, _LOOP _RANDOM _POINTER _U64 _U32); -DEFINE_PRIM(_VOID, sleep, _U32); +// DEFINE_PRIM(_VOID, sleep, _U32); -DEFINE_PRIM(_I32, pipe_init, _LOOP _PIPE _I32); +// DEFINE_PRIM(_I32, pipe_init, _LOOP _PIPE _I32); -DEFINE_PRIM(_I32, pipe_open, _PIPE _FILE); +// DEFINE_PRIM(_I32, pipe_open, _PIPE _FILE); -DEFINE_PRIM(_I32, pipe_bind, _PIPE _BYTES); +// DEFINE_PRIM(_I32, pipe_bind, _PIPE _BYTES); -HL_PRIM void HL_NAME(pipe_connect_with_cb)( uv_connect_t* req, uv_pipe_t* handle, const char* name ) { - uv_pipe_connect(req, handle, name, on_uv_connect_cb); -} -DEFINE_PRIM(_VOID, pipe_connect_with_cb, _CONNECT _PIPE _BYTES); +// HL_PRIM void HL_NAME(pipe_connect_with_cb)( uv_connect_t* req, uv_pipe_t* handle, const char* name ) { +// uv_pipe_connect(req, handle, name, on_uv_connect_cb); +// } +// DEFINE_PRIM(_VOID, pipe_connect_with_cb, _CONNECT _PIPE _BYTES); -DEFINE_PRIM(_I32, pipe_getsockname, _PIPE _BYTES _REF(_I64)); +// DEFINE_PRIM(_I32, pipe_getsockname, _PIPE _BYTES _REF(_I64)); -DEFINE_PRIM(_I32, pipe_getpeername, _PIPE _BYTES _REF(_I64)); +// DEFINE_PRIM(_I32, pipe_getpeername, _PIPE _BYTES _REF(_I64)); -DEFINE_PRIM(_VOID, pipe_pending_instances, _PIPE _I32); +// DEFINE_PRIM(_VOID, pipe_pending_instances, _PIPE _I32); -DEFINE_PRIM(_I32, pipe_pending_count, _PIPE); +// DEFINE_PRIM(_I32, pipe_pending_count, _PIPE); -DEFINE_PRIM(_HANDLE_TYPE, pipe_pending_type, _PIPE); +// DEFINE_PRIM(_HANDLE_TYPE, pipe_pending_type, _PIPE); -DEFINE_PRIM(_I32, pipe_chmod, _PIPE _I32); +// DEFINE_PRIM(_I32, pipe_chmod, _PIPE _I32); -DEFINE_PRIM(_I32, pipe, _FILE _I32 _I32); +// DEFINE_PRIM(_I32, pipe, _FILE _I32 _I32); -DEFINE_PRIM(_I32, prepare_init, _LOOP _PREPARE); +// DEFINE_PRIM(_I32, prepare_init, _LOOP _PREPARE); -HL_PRIM int HL_NAME(prepare_start_with_cb)( uv_prepare_t* prepare ) { - return uv_prepare_start(prepare, on_uv_prepare_cb); -} -DEFINE_PRIM(_I32, prepare_start_with_cb, _PREPARE); +// HL_PRIM int HL_NAME(prepare_start_with_cb)( uv_prepare_t* prepare ) { +// return uv_prepare_start(prepare, on_uv_prepare_cb); +// } +// DEFINE_PRIM(_I32, prepare_start_with_cb, _PREPARE); -DEFINE_PRIM(_I32, prepare_stop, _PREPARE); +// DEFINE_PRIM(_I32, prepare_stop, _PREPARE); -DEFINE_PRIM(_VOID, disable_stdio_inheritance, _VOID); +// DEFINE_PRIM(_VOID, disable_stdio_inheritance, _NO_ARG); -DEFINE_PRIM(_I32, spawn, _LOOP _PROCESS _PROCESS_OPTIONS); +// DEFINE_PRIM(_I32, spawn, _LOOP _PROCESS _PROCESS_OPTIONS); -DEFINE_PRIM(_I32, process_kill, _PROCESS _I32); +// DEFINE_PRIM(_I32, process_kill, _PROCESS _I32); -DEFINE_PRIM(_I32, kill, _I32 _I32); +// DEFINE_PRIM(_I32, kill, _I32 _I32); -DEFINE_PRIM(_I32, process_get_pid, _PROCESS); +// DEFINE_PRIM(_I32, process_get_pid, _PROCESS); -DEFINE_PRIM(_I32, cancel, _REQ); +// DEFINE_PRIM(_I32, cancel, _REQ); -DEFINE_PRIM(_U64, req_size, _REQ_TYPE); +// DEFINE_PRIM(_U64, req_size, _REQ_TYPE); -DEFINE_PRIM(_POINTER, req_get_data, _REQ); +// DEFINE_PRIM(_POINTER, req_get_data, _REQ); -DEFINE_PRIM(_POINTER, req_set_data, _REQ _POINTER); +// DEFINE_PRIM(_POINTER, req_set_data, _REQ _POINTER); -DEFINE_PRIM(_REQ_TYPE, req_get_type, _REQ); +// DEFINE_PRIM(_REQ_TYPE, req_get_type, _REQ); -DEFINE_PRIM(_BYTES, req_type_name, _REQ_TYPE); +// DEFINE_PRIM(_BYTES, req_type_name, _REQ_TYPE); -DEFINE_PRIM(_I32, signal_init, _LOOP _SIGNAL); +// DEFINE_PRIM(_I32, signal_init, _LOOP _SIGNAL); -HL_PRIM int HL_NAME(signal_start_with_cb)( uv_signal_t* signal, int signum ) { - return uv_signal_start(signal, on_uv_signal_cb, signum); -} -DEFINE_PRIM(_I32, signal_start_with_cb, _SIGNAL _I32); +// HL_PRIM int HL_NAME(signal_start_with_cb)( uv_signal_t* signal, int signum ) { +// return uv_signal_start(signal, on_uv_signal_cb, signum); +// } +// DEFINE_PRIM(_I32, signal_start_with_cb, _SIGNAL _I32); -HL_PRIM int HL_NAME(signal_start_oneshot_with_cb)( uv_signal_t* signal, int signum ) { - return uv_signal_start_oneshot(signal, on_uv_signal_cb, signum); -} -DEFINE_PRIM(_I32, signal_start_oneshot_with_cb, _SIGNAL _I32); +// HL_PRIM int HL_NAME(signal_start_oneshot_with_cb)( uv_signal_t* signal, int signum ) { +// return uv_signal_start_oneshot(signal, on_uv_signal_cb, signum); +// } +// DEFINE_PRIM(_I32, signal_start_oneshot_with_cb, _SIGNAL _I32); -DEFINE_PRIM(_I32, signal_stop, _SIGNAL); +// DEFINE_PRIM(_I32, signal_stop, _SIGNAL); -HL_PRIM int HL_NAME(shutdown_with_cb)( uv_shutdown_t* req, uv_stream_t* handle ) { - return uv_shutdown(req, handle, on_uv_shutdown_cb); -} -DEFINE_PRIM(_I32, shutdown_with_cb, _SHUTDOWN _STREAM); +// HL_PRIM int HL_NAME(shutdown_with_cb)( uv_shutdown_t* req, uv_stream_t* handle ) { +// return uv_shutdown(req, handle, on_uv_shutdown_cb); +// } +// DEFINE_PRIM(_I32, shutdown_with_cb, _SHUTDOWN _STREAM); -HL_PRIM int HL_NAME(listen_with_cb)( uv_stream_t* stream, int backlog ) { - return uv_listen(stream, backlog, on_uv_connection_cb); -} -DEFINE_PRIM(_I32, listen_with_cb, _STREAM _I32); +// HL_PRIM int HL_NAME(listen_with_cb)( uv_stream_t* stream, int backlog ) { +// return uv_listen(stream, backlog, on_uv_connection_cb); +// } +// DEFINE_PRIM(_I32, listen_with_cb, _STREAM _I32); -DEFINE_PRIM(_I32, accept, _STREAM _STREAM); +// DEFINE_PRIM(_I32, accept, _STREAM _STREAM); -HL_PRIM int HL_NAME(read_start_with_cb)( uv_stream_t* stream ) { - return uv_read_start(stream, on_uv_alloc_cb, on_uv_read_cb); -} -DEFINE_PRIM(_I32, read_start_with_cb, _STREAM); +// HL_PRIM int HL_NAME(read_start_with_cb)( uv_stream_t* stream ) { +// return uv_read_start(stream, on_uv_alloc_cb, on_uv_read_cb); +// } +// DEFINE_PRIM(_I32, read_start_with_cb, _STREAM); -DEFINE_PRIM(_I32, read_stop, _STREAM); +// DEFINE_PRIM(_I32, read_stop, _STREAM); -HL_PRIM int HL_NAME(write_with_cb)( uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs ) { - return uv_write(req, handle, bufs[], nbufs, on_uv_write_cb); -} -DEFINE_PRIM(_I32, write_with_cb, _WRITE _STREAM _BUF _U32); +// HL_PRIM int HL_NAME(write_with_cb)( uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs ) { +// return uv_write(req, handle, bufs[], nbufs, on_uv_write_cb); +// } +// DEFINE_PRIM(_I32, write_with_cb, _WRITE _STREAM _BUF _U32); -HL_PRIM int HL_NAME(write2_with_cb)( uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_stream_t* send_handle ) { - return uv_write2(req, handle, bufs[], nbufs, send_handle, on_uv_write_cb); -} -DEFINE_PRIM(_I32, write2_with_cb, _WRITE _STREAM _BUF _U32 _STREAM); +// HL_PRIM int HL_NAME(write2_with_cb)( uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_stream_t* send_handle ) { +// return uv_write2(req, handle, bufs[], nbufs, send_handle, on_uv_write_cb); +// } +// DEFINE_PRIM(_I32, write2_with_cb, _WRITE _STREAM _BUF _U32 _STREAM); -DEFINE_PRIM(_I32, try_write, _STREAM _BUF _U32); +// DEFINE_PRIM(_I32, try_write, _STREAM _BUF _U32); -DEFINE_PRIM(_I32, try_write2, _STREAM _BUF _U32 _STREAM); +// DEFINE_PRIM(_I32, try_write2, _STREAM _BUF _U32 _STREAM); -DEFINE_PRIM(_I32, is_readable, _STREAM); +// DEFINE_PRIM(_I32, is_readable, _STREAM); -DEFINE_PRIM(_I32, is_writable, _STREAM); +// DEFINE_PRIM(_I32, is_writable, _STREAM); -DEFINE_PRIM(_I32, stream_set_blocking, _STREAM _I32); +// DEFINE_PRIM(_I32, stream_set_blocking, _STREAM _I32); -DEFINE_PRIM(_U64, stream_get_write_queue_size, _STREAM); +// DEFINE_PRIM(_U64, stream_get_write_queue_size, _STREAM); -DEFINE_PRIM(_I32, tcp_init, _LOOP _TCP); +// DEFINE_PRIM(_I32, tcp_init, _LOOP _TCP); -DEFINE_PRIM(_I32, tcp_init_ex, _LOOP _TCP _U32); +// DEFINE_PRIM(_I32, tcp_init_ex, _LOOP _TCP _U32); -DEFINE_PRIM(_I32, tcp_open, _TCP _OS_SOCK_T); +// DEFINE_PRIM(_I32, tcp_open, _TCP _OS_SOCK_T); -DEFINE_PRIM(_I32, tcp_nodelay, _TCP _I32); +// DEFINE_PRIM(_I32, tcp_nodelay, _TCP _I32); -DEFINE_PRIM(_I32, tcp_keepalive, _TCP _I32 _U32); +// DEFINE_PRIM(_I32, tcp_keepalive, _TCP _I32 _U32); -DEFINE_PRIM(_I32, tcp_simultaneous_accepts, _TCP _I32); +// DEFINE_PRIM(_I32, tcp_simultaneous_accepts, _TCP _I32); -DEFINE_PRIM(_I32, tcp_bind, _TCP _REF(_SOCKADDR) _U32); +// DEFINE_PRIM(_I32, tcp_bind, _TCP _REF(_SOCKADDR) _U32); -DEFINE_PRIM(_I32, tcp_getsockname, _TCP _REF(_SOCKADDR) _REF(_I32)); +// DEFINE_PRIM(_I32, tcp_getsockname, _TCP _REF(_SOCKADDR) _REF(_I32)); -DEFINE_PRIM(_I32, tcp_getpeername, _TCP _REF(_SOCKADDR) _REF(_I32)); +// DEFINE_PRIM(_I32, tcp_getpeername, _TCP _REF(_SOCKADDR) _REF(_I32)); -HL_PRIM int HL_NAME(tcp_connect_with_cb)( uv_connect_t* req, uv_tcp_t* handle, const struct sockaddr* addr ) { - return uv_tcp_connect(req, handle, addr, on_uv_connect_cb); -} -DEFINE_PRIM(_I32, tcp_connect_with_cb, _CONNECT _TCP _REF(_SOCKADDR)); +// HL_PRIM int HL_NAME(tcp_connect_with_cb)( uv_connect_t* req, uv_tcp_t* handle, const struct sockaddr* addr ) { +// return uv_tcp_connect(req, handle, addr, on_uv_connect_cb); +// } +// DEFINE_PRIM(_I32, tcp_connect_with_cb, _CONNECT _TCP _REF(_SOCKADDR)); -HL_PRIM int HL_NAME(tcp_close_reset_with_cb)( uv_tcp_t* handle ) { - return uv_tcp_close_reset(handle, on_uv_close_cb); -} -DEFINE_PRIM(_I32, tcp_close_reset_with_cb, _TCP); +// HL_PRIM int HL_NAME(tcp_close_reset_with_cb)( uv_tcp_t* handle ) { +// return uv_tcp_close_reset(handle, on_uv_close_cb); +// } +// DEFINE_PRIM(_I32, tcp_close_reset_with_cb, _TCP); -DEFINE_PRIM(_I32, socketpair, _I32 _I32 _OS_SOCK_T _I32 _I32); +// DEFINE_PRIM(_I32, socketpair, _I32 _I32 _OS_SOCK_T _I32 _I32); DEFINE_PRIM(_I32, timer_init, _LOOP _TIMER); @@ -596,67 +596,67 @@ DEFINE_PRIM(_I64, timer_get_repeat, _TIMER); DEFINE_PRIM(_I64, timer_get_due_in, _TIMER); -DEFINE_PRIM(_I32, tty_init, _LOOP _TTY _FILE _I32); +// DEFINE_PRIM(_I32, tty_init, _LOOP _TTY _FILE _I32); -DEFINE_PRIM(_I32, tty_set_mode, _TTY _TTY_MODE_T); +// DEFINE_PRIM(_I32, tty_set_mode, _TTY _TTY_MODE_T); -DEFINE_PRIM(_I32, tty_reset_mode, _VOID); +// DEFINE_PRIM(_I32, tty_reset_mode, _NO_ARG); -DEFINE_PRIM(_I32, tty_get_winsize, _TTY _REF(_I32) _REF(_I32)); +// DEFINE_PRIM(_I32, tty_get_winsize, _TTY _REF(_I32) _REF(_I32)); -DEFINE_PRIM(_VOID, tty_set_vterm_state, _TTY_VTERMSTATE_T); +// DEFINE_PRIM(_VOID, tty_set_vterm_state, _TTY_VTERMSTATE_T); -DEFINE_PRIM(_I32, tty_get_vterm_state, _TTY_VTERMSTATE); +// DEFINE_PRIM(_I32, tty_get_vterm_state, _TTY_VTERMSTATE); -DEFINE_PRIM(_I32, udp_init, _LOOP _UDP); +// DEFINE_PRIM(_I32, udp_init, _LOOP _UDP); -DEFINE_PRIM(_I32, udp_init_ex, _LOOP _UDP _U32); +// DEFINE_PRIM(_I32, udp_init_ex, _LOOP _UDP _U32); -DEFINE_PRIM(_I32, udp_open, _UDP _OS_SOCK_T); +// DEFINE_PRIM(_I32, udp_open, _UDP _OS_SOCK_T); -DEFINE_PRIM(_I32, udp_bind, _UDP _REF(_SOCKADDR) _U32); +// DEFINE_PRIM(_I32, udp_bind, _UDP _REF(_SOCKADDR) _U32); -DEFINE_PRIM(_I32, udp_connect, _UDP _REF(_SOCKADDR)); +// DEFINE_PRIM(_I32, udp_connect, _UDP _REF(_SOCKADDR)); -DEFINE_PRIM(_I32, udp_getpeername, _UDP _REF(_SOCKADDR) _REF(_I32)); +// DEFINE_PRIM(_I32, udp_getpeername, _UDP _REF(_SOCKADDR) _REF(_I32)); -DEFINE_PRIM(_I32, udp_getsockname, _UDP _REF(_SOCKADDR) _REF(_I32)); +// DEFINE_PRIM(_I32, udp_getsockname, _UDP _REF(_SOCKADDR) _REF(_I32)); -DEFINE_PRIM(_I32, udp_set_membership, _UDP _BYTES _BYTES _MEMBERSHIP); +// DEFINE_PRIM(_I32, udp_set_membership, _UDP _BYTES _BYTES _MEMBERSHIP); -DEFINE_PRIM(_I32, udp_set_source_membership, _UDP _BYTES _BYTES _BYTES _MEMBERSHIP); +// DEFINE_PRIM(_I32, udp_set_source_membership, _UDP _BYTES _BYTES _BYTES _MEMBERSHIP); -DEFINE_PRIM(_I32, udp_set_multicast_loop, _UDP _I32); +// DEFINE_PRIM(_I32, udp_set_multicast_loop, _UDP _I32); -DEFINE_PRIM(_I32, udp_set_multicast_ttl, _UDP _I32); +// DEFINE_PRIM(_I32, udp_set_multicast_ttl, _UDP _I32); -DEFINE_PRIM(_I32, udp_set_multicast_interface, _UDP _BYTES); +// DEFINE_PRIM(_I32, udp_set_multicast_interface, _UDP _BYTES); -DEFINE_PRIM(_I32, udp_set_broadcast, _UDP _I32); +// DEFINE_PRIM(_I32, udp_set_broadcast, _UDP _I32); -DEFINE_PRIM(_I32, udp_set_ttl, _UDP _I32); +// DEFINE_PRIM(_I32, udp_set_ttl, _UDP _I32); -HL_PRIM int HL_NAME(udp_send_with_cb)( uv_udp_send_t* req, uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr ) { - return uv_udp_send(req, handle, bufs[], nbufs, addr, on_uv_udp_send_cb); -} -DEFINE_PRIM(_I32, udp_send_with_cb, _UDP_SEND _UDP _BUF _U32 _REF(_SOCKADDR)); +// HL_PRIM int HL_NAME(udp_send_with_cb)( uv_udp_send_t* req, uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr ) { +// return uv_udp_send(req, handle, bufs[], nbufs, addr, on_uv_udp_send_cb); +// } +// DEFINE_PRIM(_I32, udp_send_with_cb, _UDP_SEND _UDP _BUF _U32 _REF(_SOCKADDR)); -DEFINE_PRIM(_I32, udp_try_send, _UDP _BUF _U32 _REF(_SOCKADDR)); +// DEFINE_PRIM(_I32, udp_try_send, _UDP _BUF _U32 _REF(_SOCKADDR)); -HL_PRIM int HL_NAME(udp_recv_start_with_cb)( uv_udp_t* handle ) { - return uv_udp_recv_start(handle, on_uv_alloc_cb, on_uv_udp_recv_cb); -} -DEFINE_PRIM(_I32, udp_recv_start_with_cb, _UDP); +// HL_PRIM int HL_NAME(udp_recv_start_with_cb)( uv_udp_t* handle ) { +// return uv_udp_recv_start(handle, on_uv_alloc_cb, on_uv_udp_recv_cb); +// } +// DEFINE_PRIM(_I32, udp_recv_start_with_cb, _UDP); -DEFINE_PRIM(_I32, udp_using_recvmmsg, _UDP); +// DEFINE_PRIM(_I32, udp_using_recvmmsg, _UDP); -DEFINE_PRIM(_I32, udp_recv_stop, _UDP); +// DEFINE_PRIM(_I32, udp_recv_stop, _UDP); -DEFINE_PRIM(_U64, udp_get_send_queue_size, _UDP); +// DEFINE_PRIM(_U64, udp_get_send_queue_size, _UDP); -DEFINE_PRIM(_U64, udp_get_send_queue_count, _UDP); +// DEFINE_PRIM(_U64, udp_get_send_queue_count, _UDP); -DEFINE_PRIM(_U32, version, _VOID); +// DEFINE_PRIM(_U32, version, _NO_ARG); -DEFINE_PRIM(_BYTES, version_string, _VOID); +// DEFINE_PRIM(_BYTES, version_string, _NO_ARG); diff --git a/other/uvgenerator/.gitignore b/other/uvgenerator/.gitignore new file mode 100644 index 000000000..b383a2a74 --- /dev/null +++ b/other/uvgenerator/.gitignore @@ -0,0 +1 @@ +UV.hx \ No newline at end of file diff --git a/other/uvgenerator/UV.hx b/other/uvgenerator/UV.hx deleted file mode 100644 index b0aae14c6..000000000 --- a/other/uvgenerator/UV.hx +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright (C)2005-2019 Haxe Foundation - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - */ - -package hl.uv; - -// This file is automatically generated by a tool in Hashlink repo. -// see /other/uvgenerator - -// Contents of UV.hx.header - -typedef UInt = Int; -typedef U64 = I64; - -/** - Automatically generated bindings for libuv. - Avoid using this module directly. - BACKWARD COMPATIBILITY OF THIS MODULE IS NOT MAINTAINED. -**/ -@:hlNative("uv") -class UV { - static public inline function resolve(result:Int):Int { - if(result < 0) - throw new UVException(translate_uv_error(result)); - return this; - } - - static public function translate_uv_error(uvErrno:Int):UVError return UV_NOERR; - static public function translate_to_uv_error(errno:Int):Int return 0; - static public function handle_set_data_with_gc(data:HandleData):Void {} - -// Auto generated - static public function async_init_with_cb(loop:Loop, async:Async):Int return cast null; - static public function async_send(async:Async):Int return cast null; - static public function check_init(loop:Loop, check:Check):Int return cast null; - static public function check_start_with_cb(check:Check):Int return cast null; - static public function check_stop(check:Check):Int return cast null; - static public function getaddrinfo_with_cb(loop:Loop, req:Getaddrinfo, node:Bytes, service:Bytes, hints:Ref):Int return cast null; - static public function freeaddrinfo(ai:Ref):Void {} - static public function getnameinfo_with_cb(loop:Loop, req:Getnameinfo, addr:Ref, flags:Int):Int return cast null; - static public function strerror(err:Int):Bytes return cast null; - static public function strerror_r(err:Int, buf:Bytes, buflen:U64):Bytes return cast null; - static public function err_name(err:Int):Bytes return cast null; - static public function err_name_r(err:Int, buf:Bytes, buflen:U64):Bytes return cast null; - static public function translate_sys_error(sys_errno:Int):Int return cast null; - static public function fs_req_cleanup(req:Fs):Void {} - static public function fs_close_with_cb(loop:Loop, req:Fs, file:UvFile, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_open_with_cb(loop:Loop, req:Fs, path:Bytes, flags:Int, mode:Int, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_read_with_cb(loop:Loop, req:Fs, file:UvFile, bufs:Ref, nbufs:UInt, offset:I64, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_unlink_with_cb(loop:Loop, req:Fs, path:Bytes, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_write_with_cb(loop:Loop, req:Fs, file:UvFile, bufs:Ref, nbufs:UInt, offset:I64, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_mkdir_with_cb(loop:Loop, req:Fs, path:Bytes, mode:Int, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_mkdtemp_with_cb(loop:Loop, req:Fs, tpl:Bytes, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_mkstemp_with_cb(loop:Loop, req:Fs, tpl:Bytes, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_rmdir_with_cb(loop:Loop, req:Fs, path:Bytes, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_opendir_with_cb(loop:Loop, req:Fs, path:Bytes, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_closedir_with_cb(loop:Loop, req:Fs, dir:Dir, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_readdir_with_cb(loop:Loop, req:Fs, dir:Dir, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_scandir_with_cb(loop:Loop, req:Fs, path:Bytes, flags:Int, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_scandir_next(req:Fs, ent:Dirent):Int return cast null; - static public function fs_stat_with_cb(loop:Loop, req:Fs, path:Bytes, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_fstat_with_cb(loop:Loop, req:Fs, file:UvFile, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_lstat_with_cb(loop:Loop, req:Fs, path:Bytes, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_statfs_with_cb(loop:Loop, req:Fs, path:Bytes, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_rename_with_cb(loop:Loop, req:Fs, path:Bytes, new_path:Bytes, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_fsync_with_cb(loop:Loop, req:Fs, file:UvFile, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_fdatasync_with_cb(loop:Loop, req:Fs, file:UvFile, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_ftruncate_with_cb(loop:Loop, req:Fs, file:UvFile, offset:I64, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_copyfile_with_cb(loop:Loop, req:Fs, path:Bytes, new_path:Bytes, flags:Int, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_sendfile_with_cb(loop:Loop, req:Fs, out_fd:UvFile, in_fd:UvFile, in_offset:I64, length:U64, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_access_with_cb(loop:Loop, req:Fs, path:Bytes, mode:Int, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_chmod_with_cb(loop:Loop, req:Fs, path:Bytes, mode:Int, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_fchmod_with_cb(loop:Loop, req:Fs, file:UvFile, mode:Int, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_utime_with_cb(loop:Loop, req:Fs, path:Bytes, atime:Float, mtime:Float, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_futime_with_cb(loop:Loop, req:Fs, file:UvFile, atime:Float, mtime:Float, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_lutime_with_cb(loop:Loop, req:Fs, path:Bytes, atime:Float, mtime:Float, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_link_with_cb(loop:Loop, req:Fs, path:Bytes, new_path:Bytes, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_symlink_with_cb(loop:Loop, req:Fs, path:Bytes, new_path:Bytes, flags:Int, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_readlink_with_cb(loop:Loop, req:Fs, path:Bytes, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_realpath_with_cb(loop:Loop, req:Fs, path:Bytes, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_chown_with_cb(loop:Loop, req:Fs, path:Bytes, uid:Uid, gid:Gid, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_fchown_with_cb(loop:Loop, req:Fs, file:UvFile, uid:Uid, gid:Gid, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_lchown_with_cb(loop:Loop, req:Fs, path:Bytes, uid:Uid, gid:Gid, use_uv_fs_cb:Bool):Int return cast null; - static public function fs_get_type(req:Fs):UvFsType return cast null; - static public function fs_get_result(req:Fs):I64 return cast null; - static public function fs_get_system_error(req:Fs):Int return cast null; - static public function fs_get_ptr(req:Fs):Pointer return cast null; - static public function fs_get_path(req:Fs):Bytes return cast null; - static public function fs_get_statbuf(req:Fs):Stat return cast null; - static public function get_osfhandle(fd:Int):OsFd return cast null; - static public function open_osfhandle(os_fd:OsFd):Int return cast null; - static public function fs_event_init(loop:Loop, handle:FsEvent):Int return cast null; - static public function fs_event_start_with_cb(handle:FsEvent, path:Bytes, flags:UInt):Int return cast null; - static public function fs_event_stop(handle:FsEvent):Int return cast null; - static public function fs_event_getpath(handle:FsEvent, buffer:Bytes, size:Ref):Int return cast null; - static public function fs_poll_init(loop:Loop, handle:FsPoll):Int return cast null; - static public function fs_poll_start_with_cb(handle:FsPoll, path:Bytes, interval:UInt):Int return cast null; - static public function fs_poll_stop(handle:FsPoll):Int return cast null; - static public function fs_poll_getpath(handle:FsPoll, buffer:Bytes, size:Ref):Int return cast null; - static public function is_active(handle:Handle):Int return cast null; - static public function is_closing(handle:Handle):Int return cast null; - static public function close_with_cb(handle:Handle):Void {} - static public function ref(handle:Handle):Void {} - static public function unref(handle:Handle):Void {} - static public function has_ref(handle:Handle):Int return cast null; - static public function handle_size(type:UvHandleType):U64 return cast null; - static public function send_buffer_size(handle:Handle, value:Ref):Int return cast null; - static public function recv_buffer_size(handle:Handle, value:Ref):Int return cast null; - static public function fileno(handle:Handle, fd:OsFd):Int return cast null; - static public function handle_get_loop(handle:Handle):Loop return cast null; - static public function handle_get_data(handle:Handle):Pointer return cast null; - static public function handle_set_data(handle:Handle, data:Pointer):Pointer return cast null; - static public function handle_get_type(handle:Handle):UvHandleType return cast null; - static public function handle_type_name(type:UvHandleType):Bytes return cast null; - static public function idle_init(loop:Loop, idle:Idle):Int return cast null; - static public function idle_start_with_cb(idle:Idle):Int return cast null; - static public function idle_stop(idle:Idle):Int return cast null; - static public function loop_init(loop:Loop):Int return cast null; - static public function loop_close(loop:Loop):Int return cast null; - static public function default_loop():Loop return cast null; - static public function run(loop:Loop, mode:UvRunMode):Int return cast null; - static public function loop_alive(loop:Loop):Int return cast null; - static public function stop(loop:Loop):Void {} - static public function loop_size():U64 return cast null; - static public function backend_fd(loop:Loop):Int return cast null; - static public function backend_timeout(loop:Loop):Int return cast null; - static public function now(loop:Loop):U64 return cast null; - static public function update_time(loop:Loop):Void {} - static public function walk_with_cb(loop:Loop, arg:Pointer):Void {} - static public function loop_fork(loop:Loop):Int return cast null; - static public function loop_get_data(loop:Loop):Pointer return cast null; - static public function loop_set_data(loop:Loop, data:Pointer):Pointer return cast null; - static public function metrics_idle_time(loop:Loop):U64 return cast null; - static public function guess_handle(file:UvFile):UvHandleType return cast null; - static public function replace_allocator(malloc_func:UvMallocFunc, realloc_func:UvReallocFunc, calloc_func:UvCallocFunc, free_func:UvFreeFunc):Int return cast null; - static public function library_shutdown():Void {} - static public function buf_init(base:Bytes, len:UInt):Buf return cast null; - static public function setup_args(argc:Int, argv:Ref):Ref return cast null; - static public function get_process_title(buffer:Bytes, size:U64):Int return cast null; - static public function set_process_title(title:Bytes):Int return cast null; - static public function resident_set_memory(rss:Ref):Int return cast null; - static public function uptime(uptime:Ref):Int return cast null; - static public function getrusage(rusage:Rusage):Int return cast null; - static public function os_getpid():Pid return cast null; - static public function os_getppid():Pid return cast null; - static public function cpu_info(cpu_infos:Ref, count:Ref):Int return cast null; - static public function free_cpu_info(cpu_infos:CpuInfo, count:Int):Void {} - static public function interface_addresses(addresses:Ref, count:Ref):Int return cast null; - static public function free_interface_addresses(addresses:InterfaceAddress, count:Int):Void {} - static public function loadavg(avg:Ref):Void {} - static public function ip4_addr(ip:Bytes, port:Int, addr:Ref):Int return cast null; - static public function ip6_addr(ip:Bytes, port:Int, addr:Ref):Int return cast null; - static public function ip4_name(src:Ref, dst:Bytes, size:U64):Int return cast null; - static public function ip6_name(src:Ref, dst:Bytes, size:U64):Int return cast null; - static public function inet_ntop(af:Int, src:Pointer, dst:Bytes, size:U64):Int return cast null; - static public function inet_pton(af:Int, src:Bytes, dst:Pointer):Int return cast null; - static public function if_indextoname(ifindex:UInt, buffer:Bytes, size:Ref):Int return cast null; - static public function if_indextoiid(ifindex:UInt, buffer:Bytes, size:Ref):Int return cast null; - static public function exepath(buffer:Bytes, size:Ref):Int return cast null; - static public function cwd(buffer:Bytes, size:Ref):Int return cast null; - static public function chdir(dir:Bytes):Int return cast null; - static public function os_homedir(buffer:Bytes, size:Ref):Int return cast null; - static public function os_tmpdir(buffer:Bytes, size:Ref):Int return cast null; - static public function os_get_passwd(pwd:Passwd):Int return cast null; - static public function os_free_passwd(pwd:Passwd):Void {} - static public function get_free_memory():U64 return cast null; - static public function get_total_memory():U64 return cast null; - static public function get_constrained_memory():U64 return cast null; - static public function hrtime():U64 return cast null; - static public function print_all_handles(loop:Loop, stream:Ref):Void {} - static public function print_active_handles(loop:Loop, stream:Ref):Void {} - static public function os_environ(envitems:Ref, count:Ref):Int return cast null; - static public function os_free_environ(envitems:EnvItem, count:Int):Void {} - static public function os_getenv(name:Bytes, buffer:Bytes, size:Ref):Int return cast null; - static public function os_setenv(name:Bytes, value:Bytes):Int return cast null; - static public function os_unsetenv(name:Bytes):Int return cast null; - static public function os_gethostname(buffer:Bytes, size:Ref):Int return cast null; - static public function os_getpriority(pid:Pid, priority:Ref):Int return cast null; - static public function os_setpriority(pid:Pid, priority:Int):Int return cast null; - static public function os_uname(buffer:Utsname):Int return cast null; - static public function gettimeofday(tv:Timeval64):Int return cast null; - static public function random_with_cb(loop:Loop, req:Random, buf:Pointer, buflen:U64, flags:UInt):Int return cast null; - static public function sleep(msec:UInt):Void {} - static public function pipe_init(loop:Loop, handle:Pipe, ipc:Int):Int return cast null; - static public function pipe_open(handle:Pipe, file:UvFile):Int return cast null; - static public function pipe_bind(handle:Pipe, name:Bytes):Int return cast null; - static public function pipe_connect_with_cb(req:Connect, handle:Pipe, name:Bytes):Void {} - static public function pipe_getsockname(handle:Pipe, buffer:Bytes, size:Ref):Int return cast null; - static public function pipe_getpeername(handle:Pipe, buffer:Bytes, size:Ref):Int return cast null; - static public function pipe_pending_instances(handle:Pipe, count:Int):Void {} - static public function pipe_pending_count(handle:Pipe):Int return cast null; - static public function pipe_pending_type(handle:Pipe):UvHandleType return cast null; - static public function pipe_chmod(handle:Pipe, flags:Int):Int return cast null; - static public function pipe(fds:Ref, read_flags:Int, write_flags:Int):Int return cast null; - static public function prepare_init(loop:Loop, prepare:Prepare):Int return cast null; - static public function prepare_start_with_cb(prepare:Prepare):Int return cast null; - static public function prepare_stop(prepare:Prepare):Int return cast null; - static public function disable_stdio_inheritance():Void {} - static public function spawn(loop:Loop, handle:Process, options:ProcessOptions):Int return cast null; - static public function process_kill(handle:Process, signum:Int):Int return cast null; - static public function kill(pid:Int, signum:Int):Int return cast null; - static public function process_get_pid(handle:Process):Pid return cast null; - static public function cancel(req:Req):Int return cast null; - static public function req_size(type:UvReqType):U64 return cast null; - static public function req_get_data(req:Req):Pointer return cast null; - static public function req_set_data(req:Req, data:Pointer):Pointer return cast null; - static public function req_get_type(req:Req):UvReqType return cast null; - static public function req_type_name(type:UvReqType):Bytes return cast null; - static public function signal_init(loop:Loop, signal:Signal):Int return cast null; - static public function signal_start_with_cb(signal:Signal, signum:Int):Int return cast null; - static public function signal_start_oneshot_with_cb(signal:Signal, signum:Int):Int return cast null; - static public function signal_stop(signal:Signal):Int return cast null; - static public function shutdown_with_cb(req:Shutdown, handle:Stream):Int return cast null; - static public function listen_with_cb(stream:Stream, backlog:Int):Int return cast null; - static public function accept(server:Stream, client:Stream):Int return cast null; - static public function read_start_with_cb(stream:Stream):Int return cast null; - static public function read_stop(:Stream):Int return cast null; - static public function write_with_cb(req:Write, handle:Stream, bufs:Ref, nbufs:UInt):Int return cast null; - static public function write2_with_cb(req:Write, handle:Stream, bufs:Ref, nbufs:UInt, send_handle:Stream):Int return cast null; - static public function try_write(handle:Stream, bufs:Ref, nbufs:UInt):Int return cast null; - static public function try_write2(handle:Stream, bufs:Ref, nbufs:UInt, send_handle:Stream):Int return cast null; - static public function is_readable(handle:Stream):Int return cast null; - static public function is_writable(handle:Stream):Int return cast null; - static public function stream_set_blocking(handle:Stream, blocking:Int):Int return cast null; - static public function stream_get_write_queue_size(stream:Stream):U64 return cast null; - static public function tcp_init(loop:Loop, handle:Tcp):Int return cast null; - static public function tcp_init_ex(loop:Loop, handle:Tcp, flags:UInt):Int return cast null; - static public function tcp_open(handle:Tcp, sock:OsSock):Int return cast null; - static public function tcp_nodelay(handle:Tcp, enable:Int):Int return cast null; - static public function tcp_keepalive(handle:Tcp, enable:Int, delay:UInt):Int return cast null; - static public function tcp_simultaneous_accepts(handle:Tcp, enable:Int):Int return cast null; - static public function tcp_bind(handle:Tcp, addr:Ref, flags:UInt):Int return cast null; - static public function tcp_getsockname(handle:Tcp, name:Ref, namelen:Ref):Int return cast null; - static public function tcp_getpeername(handle:Tcp, name:Ref, namelen:Ref):Int return cast null; - static public function tcp_connect_with_cb(req:Connect, handle:Tcp, addr:Ref):Int return cast null; - static public function tcp_close_reset_with_cb(handle:Tcp):Int return cast null; - static public function socketpair(type:Int, protocol:Int, socket_vector:Ref, flags0:Int, flags1:Int):Int return cast null; - static public function timer_init(loop:Loop, handle:Timer):Int return cast null; - static public function timer_start_with_cb(handle:Timer, timeout:U64, repeat:U64):Int return cast null; - static public function timer_stop(handle:Timer):Int return cast null; - static public function timer_again(handle:Timer):Int return cast null; - static public function timer_set_repeat(handle:Timer, repeat:U64):Void {} - static public function timer_get_repeat(handle:Timer):U64 return cast null; - static public function timer_get_due_in(handle:Timer):U64 return cast null; - static public function tty_init(loop:Loop, handle:Tty, fd:UvFile, unused:Int):Int return cast null; - static public function tty_set_mode(handle:Tty, mode:TtyMode):Int return cast null; - static public function tty_reset_mode():Int return cast null; - static public function tty_get_winsize(handle:Tty, width:Ref, height:Ref):Int return cast null; - static public function tty_set_vterm_state(state:TtyVtermstate):Void {} - static public function tty_get_vterm_state(state:TtyVtermstate):Int return cast null; - static public function udp_init(loop:Loop, handle:Udp):Int return cast null; - static public function udp_init_ex(loop:Loop, handle:Udp, flags:UInt):Int return cast null; - static public function udp_open(handle:Udp, sock:OsSock):Int return cast null; - static public function udp_bind(handle:Udp, addr:Ref, flags:UInt):Int return cast null; - static public function udp_connect(handle:Udp, addr:Ref):Int return cast null; - static public function udp_getpeername(handle:Udp, name:Ref, namelen:Ref):Int return cast null; - static public function udp_getsockname(handle:Udp, name:Ref, namelen:Ref):Int return cast null; - static public function udp_set_membership(handle:Udp, multicast_addr:Bytes, interface_addr:Bytes, membership:UvMembership):Int return cast null; - static public function udp_set_source_membership(handle:Udp, multicast_addr:Bytes, interface_addr:Bytes, source_addr:Bytes, membership:UvMembership):Int return cast null; - static public function udp_set_multicast_loop(handle:Udp, on:Int):Int return cast null; - static public function udp_set_multicast_ttl(handle:Udp, ttl:Int):Int return cast null; - static public function udp_set_multicast_interface(handle:Udp, interface_addr:Bytes):Int return cast null; - static public function udp_set_broadcast(handle:Udp, on:Int):Int return cast null; - static public function udp_set_ttl(handle:Udp, ttl:Int):Int return cast null; - static public function udp_send_with_cb(req:UdpSend, handle:Udp, bufs:Ref, nbufs:UInt, addr:Ref):Int return cast null; - static public function udp_try_send(handle:Udp, bufs:Ref, nbufs:UInt, addr:Ref):Int return cast null; - static public function udp_recv_start_with_cb(handle:Udp):Int return cast null; - static public function udp_using_recvmmsg(handle:Udp):Int return cast null; - static public function udp_recv_stop(handle:Udp):Int return cast null; - static public function udp_get_send_queue_size(handle:Udp):U64 return cast null; - static public function udp_get_send_queue_count(handle:Udp):U64 return cast null; - static public function version():UInt return cast null; - static public function version_string():Bytes return cast null; -} diff --git a/other/uvgenerator/UV.hx.header b/other/uvgenerator/UV.hx.header index e149f1955..e8093a360 100644 --- a/other/uvgenerator/UV.hx.header +++ b/other/uvgenerator/UV.hx.header @@ -30,21 +30,29 @@ package hl.uv; typedef UInt = Int; typedef U64 = I64; +typedef UvRunMode = Loop.LoopRunMode; + /** Automatically generated bindings for libuv. Avoid using this module directly. BACKWARD COMPATIBILITY OF THIS MODULE IS NOT MAINTAINED. **/ @:hlNative("uv") -class UV { - static public inline function resolve(result:Int):Int { +extern class UV { + extern static public inline function resolve(result:Int):Int { if(result < 0) throw new UVException(translate_uv_error(result)); - return this; + return result; } - static public function translate_uv_error(uvErrno:Int):UVError return UV_NOERR; - static public function translate_to_uv_error(errno:Int):Int return 0; - static public function handle_set_data_with_gc(data:HandleData):Void {} + static public function translate_uv_error(uvErrno:Int):UVError; + static public function translate_to_uv_error(errno:Int):Int; + static public function handle_data_of_pointer(ptr:Pointer):HandleData; + static public function handle_data_to_pointer(data:HandleData):Pointer; + static public function handle_set_data_with_gc(handle:Handle, data:HandleData):Void; + static public function alloc_loop():Loop; + static public function alloc_async():Async; + static public function alloc_timer():Timer; // Auto generated + diff --git a/other/uvgenerator/UVGenerator.hx b/other/uvgenerator/UVGenerator.hx index 16fc57639..3d85cbcbd 100644 --- a/other/uvgenerator/UVGenerator.hx +++ b/other/uvgenerator/UVGenerator.hx @@ -208,11 +208,7 @@ class UVGenerator { function compose(name:String, args:Array) { var args = args.join(', '); var ret = mapHXType(sig.returnType); - var body = switch ret { - case 'Void': '{}'; - case _: 'return cast null;'; - } - return '\tstatic public function $name($args):$ret $body\n'; + return '\tstatic public function $name($args):$ret;\n'; } function mapArg(a:TypeAndName):String { if(a.name.endsWith(']')) { @@ -236,7 +232,10 @@ class UVGenerator { } static function cBinding(sig:FunctionSignature):String { - var args = sig.arguments.map(a -> mapHLType(a.type)).join(' '); + var args = switch sig.arguments { + case [{type:'void'}]: '_NO_ARG'; + case _: sig.arguments.map(a -> mapHLType(a.type)).join(' '); + } return 'DEFINE_PRIM(${mapHLType(sig.returnType)}, ${functionName(sig.name)}, $args);\n'; } From beb8b738371583fcf9309bc641727967d6ad47f1 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Wed, 18 Aug 2021 15:03:01 +0300 Subject: [PATCH 061/117] check --- libs/uv/uv.c | 14 ++++++++++++++ libs/uv/uv_generated.c | 12 ++++++------ other/uvsample/CheckSample.hx | 21 +++++++++++++++++++++ other/uvsample/UVSample.hx | 3 ++- 4 files changed, 43 insertions(+), 7 deletions(-) create mode 100644 other/uvsample/CheckSample.hx diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 053704c9e..862809b10 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -364,6 +364,20 @@ static void on_uv_async_cb( uv_async_t *h ) { hl_call1(void, c, uv_async_t *, h); } +// Check + +typedef struct { + HANDLE_DATA_FIELDS; + vclosure *onCheck; +} vcheck_data; + +DEFINE_PRIM_ALLOC(_CHECK, check); + +static void on_uv_check_cb( uv_check_t *h ) { + vclosure *c = DATA(vcheck_data *, h)->onCheck; + hl_call0(void, c); +} + // Timer typedef struct { diff --git a/libs/uv/uv_generated.c b/libs/uv/uv_generated.c index 3ea358462..c8b3c8263 100644 --- a/libs/uv/uv_generated.c +++ b/libs/uv/uv_generated.c @@ -5,14 +5,14 @@ DEFINE_PRIM(_I32, async_init_with_cb, _LOOP _ASYNC); DEFINE_PRIM(_I32, async_send, _ASYNC); -// DEFINE_PRIM(_I32, check_init, _LOOP _CHECK); +DEFINE_PRIM(_I32, check_init, _LOOP _CHECK); -// HL_PRIM int HL_NAME(check_start_with_cb)( uv_check_t* check ) { -// return uv_check_start(check, on_uv_check_cb); -// } -// DEFINE_PRIM(_I32, check_start_with_cb, _CHECK); +HL_PRIM int HL_NAME(check_start_with_cb)( uv_check_t* check ) { + return uv_check_start(check, on_uv_check_cb); +} +DEFINE_PRIM(_I32, check_start_with_cb, _CHECK); -// DEFINE_PRIM(_I32, check_stop, _CHECK); +DEFINE_PRIM(_I32, check_stop, _CHECK); // HL_PRIM int HL_NAME(getaddrinfo_with_cb)( uv_loop_t* loop, uv_getaddrinfo_t* req, const char* node, const char* service, const struct addrinfo* hints ) { // return uv_getaddrinfo(loop, req, on_uv_getaddrinfo_cb, node, service, hints); diff --git a/other/uvsample/CheckSample.hx b/other/uvsample/CheckSample.hx new file mode 100644 index 000000000..b0073846a --- /dev/null +++ b/other/uvsample/CheckSample.hx @@ -0,0 +1,21 @@ +import hl.uv.Timer; +import hl.uv.Check; +import sys.thread.Thread; + +class CheckSample { + public static function main() { + var loop = Thread.current().events; + var timer = Timer.init(loop); + timer.start(() -> { + timer.stop(); + timer.close(); + }, 10, 10); + + var check = Check.init(loop); + check.start(() -> { + Log.print('Check'); + check.stop(); + check.close(); + }); + } +} \ No newline at end of file diff --git a/other/uvsample/UVSample.hx b/other/uvsample/UVSample.hx index 02f3ef038..468d311be 100644 --- a/other/uvsample/UVSample.hx +++ b/other/uvsample/UVSample.hx @@ -1,5 +1,6 @@ class UVSample { static function main() { + CheckSample.main(); // TcpSample.main(); // DnsSample.main(); // UdpSample.main(); @@ -10,6 +11,6 @@ class UVSample { // FsPollSample.main(); // MiscSample.main(); // TtySample.main(); - VersionSample.main(); + // VersionSample.main(); } } \ No newline at end of file From 1e705a3b618015a7d8ea087589333944c5d6fe79 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Wed, 18 Aug 2021 18:30:15 +0300 Subject: [PATCH 062/117] requests; dns --- libs/uv/uv.c | 205 ++++++++++++++++++++++++++++--- libs/uv/uv_generated.c | 52 ++++---- other/uvgenerator/UV.hx.header | 30 ++++- other/uvgenerator/UVGenerator.hx | 9 +- other/uvsample/DnsSample.hx | 4 +- other/uvsample/UVSample.hx | 2 +- 6 files changed, 248 insertions(+), 54 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 862809b10..bfeab2db1 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -17,12 +17,15 @@ #define _POINTER _ABSTRACT(void_pointer) #define _HANDLE _ABSTRACT(uv_handle) -#define _REQUEST _ABSTRACT(uv_req) +#define _REQ _ABSTRACT(uv_req) #define _LOOP _ABSTRACT(uv_loop) #define _ASYNC _HANDLE #define _CHECK _HANDLE #define _TIMER _HANDLE -#define _GETADDRINFO _ABSTRACT(uv_getaddrinfo_t) +#define _SOCKADDR _ABSTRACT(uv_sockaddr_storage) +#define _GETADDRINFO _REQ +#define _GETNAMEINFO _REQ +#define _ADDRINFO _ABSTRACT(struct_addrinfo) #define _HANDLE_TYPE _I32 #define _OS_FD _ABSTRACT(uv_os_fd) @@ -35,6 +38,30 @@ } \ DEFINE_PRIM(r, alloc_##t, _NO_ARG); +#define UV_SET_DATA(h,new_data) \ + if( h->data != new_data ) { \ + if( h->data ) \ + hl_remove_root(h->data); \ + if( new_data ) \ + hl_add_root(new_data); \ + h->data = new_data; \ + } + +typedef struct sockaddr uv_sockaddr; +typedef struct sockaddr_in uv_sockaddr_in; +typedef struct sockaddr_in6 uv_sockaddr_in6; +typedef struct sockaddr_storage uv_sockaddr_storage; + +HL_PRIM void HL_NAME(free)( vdynamic *v ) { + if( v ) { + if( v->v.ptr ) + free(v->v.ptr); + if( v->v.bytes ) + free(v->v.bytes); + } +} +DEFINE_PRIM(_VOID, free, _DYN); + // Errors #define HL_UV_NOERR 0 @@ -329,14 +356,7 @@ HL_PRIM vhandle_data *HL_NAME(handle_data_of_pointer)( void *v ) { DEFINE_PRIM(_HANDLE_DATA, handle_data_of_pointer, _POINTER); HL_PRIM void HL_NAME(handle_set_data_with_gc)( uv_handle_t *h, vhandle_data *new_data ) { - void *current_data = uv_handle_get_data(h); - if( current_data == new_data ) - return; - if( current_data ) - hl_remove_root(current_data); - if( new_data ) - hl_add_root(new_data); - uv_handle_set_data(h, new_data); + UV_SET_DATA(h, new_data); } DEFINE_PRIM(_VOID, handle_set_data_with_gc, _HANDLE _HANDLE_DATA); @@ -350,6 +370,32 @@ static void on_uv_close_cb( uv_handle_t *h ) { free(h); } +// Request + +#define REQ_DATA_FIELDS \ + hl_type *t; + +typedef struct { + REQ_DATA_FIELDS; +} vreq_data; + +#define _REQ_DATA _OBJ() + +HL_PRIM void *HL_NAME(req_data_to_pointer)( vreq_data *v ) { + return v; +} +DEFINE_PRIM(_POINTER, req_data_to_pointer, _REQ_DATA); + +HL_PRIM vreq_data *HL_NAME(req_data_of_pointer)( void *v ) { + return v; +} +DEFINE_PRIM(_REQ_DATA, req_data_of_pointer, _POINTER); + +HL_PRIM void HL_NAME(req_set_data_with_gc)( uv_req_t *r, vreq_data *new_data ) { + UV_SET_DATA(r, new_data); +} +DEFINE_PRIM(_VOID, req_set_data_with_gc, _REQ _REQ_DATA); + // Async typedef struct { @@ -398,23 +444,148 @@ static void on_uv_timer_cb( uv_timer_t *h ) { DEFINE_PRIM_ALLOC(_LOOP, loop); +// DNS + +typedef struct { + REQ_DATA_FIELDS; + vclosure *callback; +} vdns_data; + +DEFINE_PRIM_ALLOC(_GETADDRINFO, getaddrinfo); +DEFINE_PRIM_ALLOC(_GETNAMEINFO, getnameinfo); + +//see hl.uv.Dns.AddrInfoFlags +#define HL_UV_AI_PASSIVE 1 +#define HL_UV_AI_CANONNAME 2 +#define HL_UV_AI_NUMERICHOST 4 +#define HL_UV_AI_V4MAPPED 8 +#define HL_UV_AI_ALL 16 +#define HL_UV_AI_ADDRCONFIG 32 +#define HL_UV_AI_NUMERICSERV 64 + +//see hl.uv.Dns.NameInfoFlags +#define HL_UV_NI_NUMERICHOST 1 +#define HL_UV_NI_NUMERICSERV 2 +#define HL_UV_NI_NOFQDN 4 +#define HL_UV_NI_NAMEREQD 8 +#define HL_UV_NI_DGRAM 16 + +HL_PRIM int HL_NAME(nameinfo_flags_to_native)( int flags ) { + int native = 0; + if( flags & HL_UV_NI_NUMERICHOST ) native |= NI_NUMERICHOST; + if( flags & HL_UV_NI_NUMERICSERV ) native |= NI_NUMERICSERV; + if( flags & HL_UV_NI_NOFQDN ) native |= NI_NOFQDN; + if( flags & HL_UV_NI_NAMEREQD ) native |= NI_NAMEREQD; + if( flags & HL_UV_NI_DGRAM ) native |= NI_DGRAM; + return native; +} +DEFINE_PRIM(_I32, nameinfo_flags_to_native, _I32); + +//see hl.uv.SockAddr.AddressFamily +#define HL_UV_UNSPEC -1 +#define HL_UV_INET -2 +#define HL_UV_INET6 -3 + +//see hl.uv.SockAddr.SocketType +#define HL_UV_STREAM -1 +#define HL_UV_DGRAM -2 +#define HL_UV_RAW -3 + +HL_PRIM struct addrinfo *HL_NAME(alloc_addrinfo)( int flags, int family, int socktype, int protocol ) { + struct addrinfo *info = UV_ALLOC(struct addrinfo); + + info->ai_flags = 0; + if( flags & HL_UV_AI_PASSIVE ) info->ai_flags |= AI_PASSIVE; + if( flags & HL_UV_AI_CANONNAME ) info->ai_flags |= AI_CANONNAME; + if( flags & HL_UV_AI_NUMERICHOST ) info->ai_flags |= AI_NUMERICHOST; + if( flags & HL_UV_AI_V4MAPPED ) info->ai_flags |= AI_V4MAPPED; + if( flags & HL_UV_AI_ALL ) info->ai_flags |= AI_ALL; + if( flags & HL_UV_AI_ADDRCONFIG ) info->ai_flags |= AI_ADDRCONFIG; + if( flags & HL_UV_AI_NUMERICSERV ) info->ai_flags |= AI_NUMERICSERV; + + switch( family ) { + case HL_UV_UNSPEC: info->ai_family = PF_UNSPEC; break; + case HL_UV_INET: info->ai_family = PF_INET; break; + case HL_UV_INET6: info->ai_family = PF_INET6; break; + default: info->ai_family = family; break; + } + + switch( socktype ) { + case HL_UV_STREAM: info->ai_socktype = SOCK_STREAM; break; + case HL_UV_DGRAM: info->ai_socktype = SOCK_DGRAM; break; + case HL_UV_RAW: info->ai_socktype = SOCK_RAW; break; + default: info->ai_socktype = socktype; break; + } + + info->ai_protocol = protocol; + return info; +} +DEFINE_PRIM(_ADDRINFO, alloc_addrinfo, _I32 _I32 _I32 _I32); + +HL_PRIM int HL_NAME(addrinfo_family)( struct addrinfo *ai ) { + switch( ai->ai_family ) { + case PF_UNSPEC: return 0; + case PF_INET: return -1; + case PF_INET6: return -2; + default: return ai->ai_family; + } +} +DEFINE_PRIM(_I32, addrinfo_family, _ADDRINFO); + +HL_PRIM int HL_NAME(addrinfo_socktype)( struct addrinfo *ai ) { + switch( ai->ai_socktype ) { + case SOCK_STREAM: return -1; + case SOCK_DGRAM: return -2; + case SOCK_RAW: return -3; + default: return ai->ai_socktype; + } +} +DEFINE_PRIM(_I32, addrinfo_socktype, _ADDRINFO); + +HL_PRIM int HL_NAME(addrinfo_protocol)( struct addrinfo *ai ) { + return ai->ai_protocol; +} +DEFINE_PRIM(_I32, addrinfo_protocol, _ADDRINFO); + +HL_PRIM uv_sockaddr_storage *HL_NAME(addrinfo_addr)( struct addrinfo *ai ) { + uv_sockaddr_storage *addr = UV_ALLOC(uv_sockaddr_storage); + memcpy(addr, ai->ai_addr, ai->ai_addrlen); + return addr; +} +DEFINE_PRIM(_SOCKADDR, addrinfo_addr, _ADDRINFO); + +HL_PRIM vbyte *HL_NAME(addrinfo_canonname)( struct addrinfo *ai ) { + return (vbyte *)ai->ai_canonname; +} +DEFINE_PRIM(_BYTES, addrinfo_canonname, _ADDRINFO); + +HL_PRIM struct addrinfo *HL_NAME(addrinfo_next)( struct addrinfo *ai ) { + return ai->ai_next; +} +DEFINE_PRIM(_ADDRINFO, addrinfo_next, _ADDRINFO); + +static void on_uv_getaddrinfo_cb( uv_getaddrinfo_t *r, int status, struct addrinfo *res ) { + vclosure *c = DATA(vdns_data *,r)->callback; + hl_call2(void,c,int,errno_uv2hl(status),struct addrinfo *,res); +} + +static void on_uv_getnameinfo_cb( uv_getnameinfo_t *r, int status, const char *hostname, const char *service ) { + vclosure *c = DATA(vdns_data *,r)->callback; + hl_call3(void,c,int,errno_uv2hl(status),const char *,hostname,const char *,service); +} + // auto-generated libuv bindings #include "uv_generated.c" + // TODO: remove everything below #define _DIR _ABSTRACT(uv_dir) -#define _SOCKADDR _ABSTRACT(uv_sockaddr_storage) #define _CALLB _FUN(_VOID,_NO_ARG) #define _TIMESPEC _OBJ(_I64 _I64) #define _STAT _OBJ(_I64 _I64 _I64 _I64 _I64 _I64 _I64 _I64 _I64 _I64 _I64 _I64 _TIMESPEC _TIMESPEC _TIMESPEC _TIMESPEC) -typedef struct sockaddr uv_sockaddr; -typedef struct sockaddr_in uv_sockaddr_in; -typedef struct sockaddr_in6 uv_sockaddr_in6; -typedef struct sockaddr_storage uv_sockaddr_storage; - #define EVT_CLOSE 1 #define EVT_STREAM_READ 0 @@ -541,7 +712,7 @@ HL_PRIM void HL_NAME(cancel_wrap)( uv_req_t *r ) { UV_CHECK_ERROR(uv_cancel(r),,); free_req(r); } -DEFINE_PRIM(_VOID, cancel_wrap, _REQUEST); +DEFINE_PRIM(_VOID, cancel_wrap, _REQ); // HANDLE diff --git a/libs/uv/uv_generated.c b/libs/uv/uv_generated.c index c8b3c8263..f8e0a52ce 100644 --- a/libs/uv/uv_generated.c +++ b/libs/uv/uv_generated.c @@ -14,17 +14,17 @@ DEFINE_PRIM(_I32, check_start_with_cb, _CHECK); DEFINE_PRIM(_I32, check_stop, _CHECK); -// HL_PRIM int HL_NAME(getaddrinfo_with_cb)( uv_loop_t* loop, uv_getaddrinfo_t* req, const char* node, const char* service, const struct addrinfo* hints ) { -// return uv_getaddrinfo(loop, req, on_uv_getaddrinfo_cb, node, service, hints); -// } -// DEFINE_PRIM(_I32, getaddrinfo_with_cb, _LOOP _GETADDRINFO _BYTES _BYTES _REF(_ADDRINFO)); +HL_PRIM int HL_NAME(getaddrinfo_with_cb)( uv_loop_t* loop, uv_getaddrinfo_t* req, const char* node, const char* service, const struct addrinfo* hints ) { + return uv_getaddrinfo(loop, req, on_uv_getaddrinfo_cb, node, service, hints); +} +DEFINE_PRIM(_I32, getaddrinfo_with_cb, _LOOP _GETADDRINFO _BYTES _BYTES _ADDRINFO); -// DEFINE_PRIM(_VOID, freeaddrinfo, _REF(_ADDRINFO)); +DEFINE_PRIM(_VOID, freeaddrinfo, _ADDRINFO); -// HL_PRIM int HL_NAME(getnameinfo_with_cb)( uv_loop_t* loop, uv_getnameinfo_t* req, const struct sockaddr* addr, int flags ) { -// return uv_getnameinfo(loop, req, on_uv_getnameinfo_cb, addr, flags); -// } -// DEFINE_PRIM(_I32, getnameinfo_with_cb, _LOOP _GETNAMEINFO _REF(_SOCKADDR) _I32); +HL_PRIM int HL_NAME(getnameinfo_with_cb)( uv_loop_t* loop, uv_getnameinfo_t* req, const struct sockaddr* addr, int flags ) { + return uv_getnameinfo(loop, req, on_uv_getnameinfo_cb, addr, flags); +} +DEFINE_PRIM(_I32, getnameinfo_with_cb, _LOOP _GETNAMEINFO _SOCKADDR _I32); DEFINE_PRIM(_BYTES, strerror, _I32); @@ -369,13 +369,13 @@ DEFINE_PRIM(_VOID, stop, _LOOP); // DEFINE_PRIM(_VOID, loadavg, _F64); -// DEFINE_PRIM(_I32, ip4_addr, _BYTES _I32 _REF(_SOCKADDR_IN)); +// DEFINE_PRIM(_I32, ip4_addr, _BYTES _I32 _SOCKADDR_IN); -// DEFINE_PRIM(_I32, ip6_addr, _BYTES _I32 _REF(_SOCKADDR_IN6)); +// DEFINE_PRIM(_I32, ip6_addr, _BYTES _I32 _SOCKADDR_IN6); -// DEFINE_PRIM(_I32, ip4_name, _REF(_SOCKADDR_IN) _BYTES _U64); +// DEFINE_PRIM(_I32, ip4_name, _SOCKADDR_IN _BYTES _U64); -// DEFINE_PRIM(_I32, ip6_name, _REF(_SOCKADDR_IN6) _BYTES _U64); +// DEFINE_PRIM(_I32, ip6_name, _SOCKADDR_IN6 _BYTES _U64); // DEFINE_PRIM(_I32, inet_ntop, _I32 _POINTER _BYTES _U64); @@ -482,13 +482,13 @@ DEFINE_PRIM(_VOID, stop, _LOOP); // DEFINE_PRIM(_I32, process_get_pid, _PROCESS); -// DEFINE_PRIM(_I32, cancel, _REQ); +DEFINE_PRIM(_I32, cancel, _REQ); // DEFINE_PRIM(_U64, req_size, _REQ_TYPE); -// DEFINE_PRIM(_POINTER, req_get_data, _REQ); +DEFINE_PRIM(_POINTER, req_get_data, _REQ); -// DEFINE_PRIM(_POINTER, req_set_data, _REQ _POINTER); +DEFINE_PRIM(_POINTER, req_set_data, _REQ _POINTER); // DEFINE_PRIM(_REQ_TYPE, req_get_type, _REQ); @@ -561,16 +561,16 @@ DEFINE_PRIM(_VOID, stop, _LOOP); // DEFINE_PRIM(_I32, tcp_simultaneous_accepts, _TCP _I32); -// DEFINE_PRIM(_I32, tcp_bind, _TCP _REF(_SOCKADDR) _U32); +// DEFINE_PRIM(_I32, tcp_bind, _TCP _SOCKADDR _U32); -// DEFINE_PRIM(_I32, tcp_getsockname, _TCP _REF(_SOCKADDR) _REF(_I32)); +// DEFINE_PRIM(_I32, tcp_getsockname, _TCP _SOCKADDR _REF(_I32)); -// DEFINE_PRIM(_I32, tcp_getpeername, _TCP _REF(_SOCKADDR) _REF(_I32)); +// DEFINE_PRIM(_I32, tcp_getpeername, _TCP _SOCKADDR _REF(_I32)); // HL_PRIM int HL_NAME(tcp_connect_with_cb)( uv_connect_t* req, uv_tcp_t* handle, const struct sockaddr* addr ) { // return uv_tcp_connect(req, handle, addr, on_uv_connect_cb); // } -// DEFINE_PRIM(_I32, tcp_connect_with_cb, _CONNECT _TCP _REF(_SOCKADDR)); +// DEFINE_PRIM(_I32, tcp_connect_with_cb, _CONNECT _TCP _SOCKADDR); // HL_PRIM int HL_NAME(tcp_close_reset_with_cb)( uv_tcp_t* handle ) { // return uv_tcp_close_reset(handle, on_uv_close_cb); @@ -614,13 +614,13 @@ DEFINE_PRIM(_I64, timer_get_due_in, _TIMER); // DEFINE_PRIM(_I32, udp_open, _UDP _OS_SOCK_T); -// DEFINE_PRIM(_I32, udp_bind, _UDP _REF(_SOCKADDR) _U32); +// DEFINE_PRIM(_I32, udp_bind, _UDP _SOCKADDR _U32); -// DEFINE_PRIM(_I32, udp_connect, _UDP _REF(_SOCKADDR)); +// DEFINE_PRIM(_I32, udp_connect, _UDP _SOCKADDR); -// DEFINE_PRIM(_I32, udp_getpeername, _UDP _REF(_SOCKADDR) _REF(_I32)); +// DEFINE_PRIM(_I32, udp_getpeername, _UDP _SOCKADDR _REF(_I32)); -// DEFINE_PRIM(_I32, udp_getsockname, _UDP _REF(_SOCKADDR) _REF(_I32)); +// DEFINE_PRIM(_I32, udp_getsockname, _UDP _SOCKADDR _REF(_I32)); // DEFINE_PRIM(_I32, udp_set_membership, _UDP _BYTES _BYTES _MEMBERSHIP); @@ -639,9 +639,9 @@ DEFINE_PRIM(_I64, timer_get_due_in, _TIMER); // HL_PRIM int HL_NAME(udp_send_with_cb)( uv_udp_send_t* req, uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr ) { // return uv_udp_send(req, handle, bufs[], nbufs, addr, on_uv_udp_send_cb); // } -// DEFINE_PRIM(_I32, udp_send_with_cb, _UDP_SEND _UDP _BUF _U32 _REF(_SOCKADDR)); +// DEFINE_PRIM(_I32, udp_send_with_cb, _UDP_SEND _UDP _BUF _U32 _SOCKADDR); -// DEFINE_PRIM(_I32, udp_try_send, _UDP _BUF _U32 _REF(_SOCKADDR)); +// DEFINE_PRIM(_I32, udp_try_send, _UDP _BUF _U32 _SOCKADDR); // HL_PRIM int HL_NAME(udp_recv_start_with_cb)( uv_udp_t* handle ) { // return uv_udp_recv_start(handle, on_uv_alloc_cb, on_uv_udp_recv_cb); diff --git a/other/uvgenerator/UV.hx.header b/other/uvgenerator/UV.hx.header index e8093a360..8dc378b67 100644 --- a/other/uvgenerator/UV.hx.header +++ b/other/uvgenerator/UV.hx.header @@ -22,15 +22,24 @@ package hl.uv; +import hl.uv.SockAddr; +import hl.uv.Dns; + // This file is automatically generated by a tool in Hashlink repo. // see /other/uvgenerator // Contents of UV.hx.header -typedef UInt = Int; -typedef U64 = I64; +private abstract Addrinfo(Abstract<"struct_addrinfo">) {} + +typedef RawAddrInfo = Addrinfo; -typedef UvRunMode = Loop.LoopRunMode; +private typedef UInt = Int; +private typedef U64 = I64; +private typedef UvRunMode = Loop.LoopRunMode; +private typedef Sockaddr = SockAddr; +private typedef Getaddrinfo = AddrInfoRequest; +private typedef Getnameinfo = NameInfoRequest; /** Automatically generated bindings for libuv. @@ -45,14 +54,29 @@ extern class UV { return result; } + static public function free(v:Dynamic):Void; static public function translate_uv_error(uvErrno:Int):UVError; static public function translate_to_uv_error(errno:Int):Int; static public function handle_data_of_pointer(ptr:Pointer):HandleData; static public function handle_data_to_pointer(data:HandleData):Pointer; static public function handle_set_data_with_gc(handle:Handle, data:HandleData):Void; + static public function req_data_of_pointer(ptr:Pointer):ReqData; + static public function req_data_to_pointer(data:ReqData):Pointer; + static public function req_set_data_with_gc(handle:Req, data:ReqData):Void; static public function alloc_loop():Loop; static public function alloc_async():Async; static public function alloc_timer():Timer; + static public function alloc_check():Check; + static public function alloc_getaddrinfo():Getaddrinfo; + static public function alloc_getnameinfo():Getnameinfo; + static public function alloc_addrinfo(flags:Int, family:AddressFamily, socktype:SocketType, protocol:Int):Addrinfo; + static public function addrinfo_family(ai:Addrinfo):AddressFamily; + static public function addrinfo_socktype(ai:Addrinfo):SocketType; + static public function addrinfo_protocol(ai:Addrinfo):Int; + static public function addrinfo_addr(ai:Addrinfo):SockAddr; + static public function addrinfo_canonname(ai:Addrinfo):Bytes; + static public function addrinfo_next(ai:Addrinfo):Null; + static public function nameinfo_flags_to_native(ai:NameInfoFlags):Int; // Auto generated diff --git a/other/uvgenerator/UVGenerator.hx b/other/uvgenerator/UVGenerator.hx index 3d85cbcbd..54895bc29 100644 --- a/other/uvgenerator/UVGenerator.hx +++ b/other/uvgenerator/UVGenerator.hx @@ -143,9 +143,8 @@ class UVGenerator { if(type.endsWith('_t*')) type = type.substring(0, type.length - 3); '_' + type.toUpperCase(); - case _ if(type.startsWith('struct ')): - type = '_' + type.substr('struct '.length).toUpperCase(); - type.endsWith('*') ? '_REF(${type.substr(0, type.length - 1)})' : type; + case _ if(type.startsWith('struct ') && type.endsWith("*")): + '_' + type.substring('struct '.length, type.length - 1).toUpperCase(); case _ if(type.startsWith('const ')): mapHLType(type.substr('const '.length)); case _: @@ -162,8 +161,6 @@ class UVGenerator { static function mapHXType(type:String):String { if(type.startsWith('const ')) type = type.substr('const '.length); - if(type.startsWith('struct ')) - type = type.substr('struct '.length); return switch type { case 'void*': 'Pointer'; @@ -175,6 +172,8 @@ class UVGenerator { case 'ssize_t': 'I64'; case _ if(type.startsWith('unsigned ')): 'U' + mapHXType(type.substr('unsigned '.length)); + case _ if(type.startsWith('struct ') && type.endsWith('*')): + mapHXType(type.substring('struct '.length, type.length - 1)); case _ if(type.startsWith('uv_') && type.endsWith('_t*')): type = type.substr(3, type.length - 3 - 3); snakeToPascalCase(type); diff --git a/other/uvsample/DnsSample.hx b/other/uvsample/DnsSample.hx index 3ffdd9db9..82039903c 100644 --- a/other/uvsample/DnsSample.hx +++ b/other/uvsample/DnsSample.hx @@ -7,13 +7,13 @@ class DnsSample { public static function main() { var loop = Thread.current().events; Dns.getAddrInfo(loop, 'haxe.org', 'http', - { flags: {canonName: true}, family: INET }, + { flags: AI_CANONNAME, family: INET }, (e, infos) -> switch e { case UV_NOERR: for(i in infos) { Log.print('getAddrInfo: addr ${i.addr}, canonname ${i.canonName}'); if(i.canonName != null) { - Dns.getNameInfo(loop, i.addr, { nameReqd: true }, + Dns.getNameInfo(loop, i.addr, NI_NAMEREQD, (e, name, service) -> switch e { case UV_NOERR: Log.print('getNameInfo: host $name, service $service'); diff --git a/other/uvsample/UVSample.hx b/other/uvsample/UVSample.hx index 468d311be..3fc27e37b 100644 --- a/other/uvsample/UVSample.hx +++ b/other/uvsample/UVSample.hx @@ -2,7 +2,7 @@ class UVSample { static function main() { CheckSample.main(); // TcpSample.main(); - // DnsSample.main(); + DnsSample.main(); // UdpSample.main(); // PipeSample.main(); // ProcessSample.main(); From d627588cad850b0f52ea6770e2ad997e11ca0dce Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Wed, 18 Aug 2021 21:45:08 +0300 Subject: [PATCH 063/117] update generator --- libs/uv/uv.c | 140 +++++- libs/uv/uv_generated.c | 792 +++++++++++++++---------------- other/uvgenerator/UVGenerator.hx | 17 +- 3 files changed, 525 insertions(+), 424 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index bfeab2db1..2ec17c567 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -14,6 +14,7 @@ // Common macros #define _U64 _I64 +#define _U32 _I32 #define _POINTER _ABSTRACT(void_pointer) #define _HANDLE _ABSTRACT(uv_handle) @@ -28,6 +29,105 @@ #define _ADDRINFO _ABSTRACT(struct_addrinfo) #define _HANDLE_TYPE _I32 #define _OS_FD _ABSTRACT(uv_os_fd) +#define _FS _REQ +#define _UID_T _I32 +#define _GID_T _I32 +#define _FILE _I32 +#define _BUF _ABSTRACT(uv_buf) +#define _DIR _ABSTRACT(uv_dir) +#define _FS_POLL _HANDLE +#define _IDLE _HANDLE +#define _RANDOM _REQ +#define _PIPE _HANDLE +#define _CONNECT _REQ +#define _PREPARE _HANDLE +#define _PROCESS _HANDLE +#define _SIGNAL _HANDLE +#define _SHUTDOWN _REQ +#define _STREAM _HANDLE +#define _WRITE _REQ +#define _TCP _HANDLE +#define _TTY _HANDLE +#define _UDP _HANDLE +#define _UDP_SEND _REQ + +typedef struct sockaddr uv_sockaddr; +typedef struct sockaddr_in uv_sockaddr_in; +typedef struct sockaddr_in6 uv_sockaddr_in6; +typedef struct sockaddr_storage uv_sockaddr_storage; + +// TODO { + #define _DIRENT _I32 + #define _FS_TYPE _I32 + #define _STAT _I32 + #define _OS_FD_T _I32 + #define _FS_EVENT _I32 + #define _MALLOC_FUNC _I32 + #define _REALLOC_FUNC _I32 + #define _CALLOC_FUNC _I32 + #define _FREE_FUNC _I32 + #define _RUSAGE _I32 + #define _INTERFACE_ADDRESS _I32 + #define _SOCKADDR_IN _I32 + #define _SOCKADDR_IN6 _I32 + #define _CPU_INFO _I32 + #define _PASSWD _I32 + #define _ENV_ITEM _I32 + #define _TIMEVAL64 _I32 + #define _PROCESS_OPTIONS _I32 + #define _REQ_TYPE _I32 + #define _OS_SOCK_T _I32 + #define _TTY_MODE_T _I32 + #define _TTY_VTERMSTATE_T _I32 + #define _TTY_VTERMSTATE _I32 + #define _MEMBERSHIP _I32 + #define _UTSNAME _I32 + + static void on_uv_fs_poll_cb( uv_fs_poll_t *h, int status, const uv_stat_t *prev, const uv_stat_t *curr ) { + } + + static void on_uv_fs_event_cb( uv_fs_event_t *h, const char *filename, int events, int status ) { + } + + static void on_uv_idle_cb( uv_idle_t *h ) { + } + + static void on_uv_walk_cb( uv_handle_t* handle, void* arg ) { + } + + static void on_uv_random_cb( uv_random_t* r, int status, void* buf, size_t buflen ) { + } + + static void on_uv_connect_cb( uv_connect_t *r, int status ) { + } + + static void on_uv_prepare_cb( uv_prepare_t *h ) { + } + + static void on_uv_signal_cb( uv_signal_t *h, int signum ) { + } + + static void on_uv_shutdown_cb( uv_shutdown_t *r, int status ) { + } + + static void on_uv_connection_cb( uv_stream_t *h, int status ) { + } + + static void on_uv_alloc_cb( uv_handle_t* h, size_t size, uv_buf_t *buf ) { + } + + static void on_uv_write_cb( uv_write_t *r, int status ) { + } + + static void on_uv_read_cb( uv_stream_t *h, ssize_t nread, const uv_buf_t *buf ) { + } + + static void on_uv_udp_send_cb( uv_udp_send_t *r, int status ) { + } + + static void on_uv_udp_recv_cb( uv_udp_t *h, ssize_t nread, const uv_buf_t *buf, const uv_sockaddr *src_addr, unsigned flags ) { + } +// } #define UV_ALLOC(t) ((t*)malloc(sizeof(t))) #define DATA(t,h) ((t)h->data) @@ -47,11 +147,6 @@ h->data = new_data; \ } -typedef struct sockaddr uv_sockaddr; -typedef struct sockaddr_in uv_sockaddr_in; -typedef struct sockaddr_in6 uv_sockaddr_in6; -typedef struct sockaddr_storage uv_sockaddr_storage; - HL_PRIM void HL_NAME(free)( vdynamic *v ) { if( v ) { if( v->v.ptr ) @@ -376,9 +471,14 @@ static void on_uv_close_cb( uv_handle_t *h ) { hl_type *t; typedef struct { - REQ_DATA_FIELDS; + REQ_DATA_FIELDS } vreq_data; +typedef struct { + REQ_DATA_FIELDS + vclosure *callback; +} vreq_cb_data; + #define _REQ_DATA _OBJ() HL_PRIM void *HL_NAME(req_data_to_pointer)( vreq_data *v ) { @@ -446,11 +546,6 @@ DEFINE_PRIM_ALLOC(_LOOP, loop); // DNS -typedef struct { - REQ_DATA_FIELDS; - vclosure *callback; -} vdns_data; - DEFINE_PRIM_ALLOC(_GETADDRINFO, getaddrinfo); DEFINE_PRIM_ALLOC(_GETNAMEINFO, getnameinfo); @@ -565,15 +660,22 @@ HL_PRIM struct addrinfo *HL_NAME(addrinfo_next)( struct addrinfo *ai ) { DEFINE_PRIM(_ADDRINFO, addrinfo_next, _ADDRINFO); static void on_uv_getaddrinfo_cb( uv_getaddrinfo_t *r, int status, struct addrinfo *res ) { - vclosure *c = DATA(vdns_data *,r)->callback; + vclosure *c = DATA(vreq_cb_data *,r)->callback; hl_call2(void,c,int,errno_uv2hl(status),struct addrinfo *,res); } static void on_uv_getnameinfo_cb( uv_getnameinfo_t *r, int status, const char *hostname, const char *service ) { - vclosure *c = DATA(vdns_data *,r)->callback; + vclosure *c = DATA(vreq_cb_data *,r)->callback; hl_call3(void,c,int,errno_uv2hl(status),const char *,hostname,const char *,service); } +// File system + +static void on_uv_fs_cb( uv_fs_t *r ) { + vclosure *c = DATA(vreq_cb_data *,r)->callback; + hl_call1(void,c,uv_fs_t *,r); +} + // auto-generated libuv bindings #include "uv_generated.c" @@ -581,10 +683,8 @@ static void on_uv_getnameinfo_cb( uv_getnameinfo_t *r, int status, const char *h // TODO: remove everything below -#define _DIR _ABSTRACT(uv_dir) #define _CALLB _FUN(_VOID,_NO_ARG) #define _TIMESPEC _OBJ(_I64 _I64) -#define _STAT _OBJ(_I64 _I64 _I64 _I64 _I64 _I64 _I64 _I64 _I64 _I64 _I64 _I64 _TIMESPEC _TIMESPEC _TIMESPEC _TIMESPEC) #define EVT_CLOSE 1 @@ -1547,8 +1647,6 @@ HL_PRIM int HL_NAME(process_pid)( uv_process_t *h ) { } DEFINE_PRIM(_I32, process_pid, _HANDLE); -DEFINE_PRIM(_VOID, disable_stdio_inheritance, _NO_ARG); - HL_PRIM void HL_NAME(process_kill_wrap)( uv_process_t *h, int signum ) { UV_CHECK_NULL(h,); UV_CHECK_ERROR(uv_process_kill(h, signum_hx2uv(signum)),,); @@ -2592,9 +2690,6 @@ HL_PRIM vdynamic *HL_NAME(getrusage_wrap)() { } DEFINE_PRIM(_DYN, getrusage_wrap, _NO_ARG); -DEFINE_PRIM(_I32, os_getpid, _NO_ARG); -DEFINE_PRIM(_I32, os_getppid, _NO_ARG); - HL_PRIM varray *HL_NAME(cpu_info_wrap)() { uv_cpu_info_t *infos; int count; @@ -2713,11 +2808,6 @@ HL_PRIM vdynamic *HL_NAME(os_getpasswd_wrap)() { } DEFINE_PRIM(_DYN, os_getpasswd_wrap, _NO_ARG); -DEFINE_PRIM(_I64, get_free_memory, _NO_ARG); -DEFINE_PRIM(_I64, get_total_memory, _NO_ARG); -DEFINE_PRIM(_I64, get_constrained_memory, _NO_ARG); -DEFINE_PRIM(_I64, hrtime, _NO_ARG); - HL_PRIM vbyte *HL_NAME(os_gethostname_wrap)() { return os_str(uv_os_gethostname); } diff --git a/libs/uv/uv_generated.c b/libs/uv/uv_generated.c index f8e0a52ce..59fc5cbe2 100644 --- a/libs/uv/uv_generated.c +++ b/libs/uv/uv_generated.c @@ -28,235 +28,235 @@ DEFINE_PRIM(_I32, getnameinfo_with_cb, _LOOP _GETNAMEINFO _SOCKADDR _I32); DEFINE_PRIM(_BYTES, strerror, _I32); -// DEFINE_PRIM(_BYTES, strerror_r, _I32 _BYTES _U64); +DEFINE_PRIM(_BYTES, strerror_r, _I32 _BYTES _U64); DEFINE_PRIM(_BYTES, err_name, _I32); -// DEFINE_PRIM(_BYTES, err_name_r, _I32 _BYTES _U64); - -// DEFINE_PRIM(_I32, translate_sys_error, _I32); - -// DEFINE_PRIM(_VOID, fs_req_cleanup, _FS); - -// HL_PRIM int HL_NAME(fs_close_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { -// return uv_fs_close(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_close_with_cb, _LOOP _FS _FILE _BOOL); - -// HL_PRIM int HL_NAME(fs_open_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, bool use_uv_fs_cb ) { -// return uv_fs_open(loop, req, path, flags, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_open_with_cb, _LOOP _FS _BYTES _I32 _I32 _BOOL); - -// HL_PRIM int HL_NAME(fs_read_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, bool use_uv_fs_cb ) { -// return uv_fs_read(loop, req, file, bufs[], nbufs, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_read_with_cb, _LOOP _FS _FILE _BUF _U32 _I64 _BOOL); - -// HL_PRIM int HL_NAME(fs_unlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { -// return uv_fs_unlink(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_unlink_with_cb, _LOOP _FS _BYTES _BOOL); - -// HL_PRIM int HL_NAME(fs_write_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, bool use_uv_fs_cb ) { -// return uv_fs_write(loop, req, file, bufs[], nbufs, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_write_with_cb, _LOOP _FS _FILE _BUF _U32 _I64 _BOOL); - -// HL_PRIM int HL_NAME(fs_mkdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { -// return uv_fs_mkdir(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_mkdir_with_cb, _LOOP _FS _BYTES _I32 _BOOL); - -// HL_PRIM int HL_NAME(fs_mkdtemp_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* tpl, bool use_uv_fs_cb ) { -// return uv_fs_mkdtemp(loop, req, tpl, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_mkdtemp_with_cb, _LOOP _FS _BYTES _BOOL); - -// HL_PRIM int HL_NAME(fs_mkstemp_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* tpl, bool use_uv_fs_cb ) { -// return uv_fs_mkstemp(loop, req, tpl, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_mkstemp_with_cb, _LOOP _FS _BYTES _BOOL); - -// HL_PRIM int HL_NAME(fs_rmdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { -// return uv_fs_rmdir(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_rmdir_with_cb, _LOOP _FS _BYTES _BOOL); - -// HL_PRIM int HL_NAME(fs_opendir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { -// return uv_fs_opendir(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_opendir_with_cb, _LOOP _FS _BYTES _BOOL); - -// HL_PRIM int HL_NAME(fs_closedir_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, bool use_uv_fs_cb ) { -// return uv_fs_closedir(loop, req, dir, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_closedir_with_cb, _LOOP _FS _DIR _BOOL); - -// HL_PRIM int HL_NAME(fs_readdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, bool use_uv_fs_cb ) { -// return uv_fs_readdir(loop, req, dir, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_readdir_with_cb, _LOOP _FS _DIR _BOOL); - -// HL_PRIM int HL_NAME(fs_scandir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, bool use_uv_fs_cb ) { -// return uv_fs_scandir(loop, req, path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_scandir_with_cb, _LOOP _FS _BYTES _I32 _BOOL); - -// DEFINE_PRIM(_I32, fs_scandir_next, _FS _DIRENT); - -// HL_PRIM int HL_NAME(fs_stat_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { -// return uv_fs_stat(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_stat_with_cb, _LOOP _FS _BYTES _BOOL); - -// HL_PRIM int HL_NAME(fs_fstat_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { -// return uv_fs_fstat(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_fstat_with_cb, _LOOP _FS _FILE _BOOL); - -// HL_PRIM int HL_NAME(fs_lstat_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { -// return uv_fs_lstat(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_lstat_with_cb, _LOOP _FS _BYTES _BOOL); - -// HL_PRIM int HL_NAME(fs_statfs_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { -// return uv_fs_statfs(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_statfs_with_cb, _LOOP _FS _BYTES _BOOL); - -// HL_PRIM int HL_NAME(fs_rename_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, bool use_uv_fs_cb ) { -// return uv_fs_rename(loop, req, path, new_path, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_rename_with_cb, _LOOP _FS _BYTES _BYTES _BOOL); - -// HL_PRIM int HL_NAME(fs_fsync_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { -// return uv_fs_fsync(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_fsync_with_cb, _LOOP _FS _FILE _BOOL); - -// HL_PRIM int HL_NAME(fs_fdatasync_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { -// return uv_fs_fdatasync(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_fdatasync_with_cb, _LOOP _FS _FILE _BOOL); - -// HL_PRIM int HL_NAME(fs_ftruncate_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, int64_t offset, bool use_uv_fs_cb ) { -// return uv_fs_ftruncate(loop, req, file, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_ftruncate_with_cb, _LOOP _FS _FILE _I64 _BOOL); - -// HL_PRIM int HL_NAME(fs_copyfile_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, bool use_uv_fs_cb ) { -// return uv_fs_copyfile(loop, req, path, new_path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_copyfile_with_cb, _LOOP _FS _BYTES _BYTES _I32 _BOOL); - -// HL_PRIM int HL_NAME(fs_sendfile_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file out_fd, uv_file in_fd, int64_t in_offset, size_t length, bool use_uv_fs_cb ) { -// return uv_fs_sendfile(loop, req, out_fd, in_fd, in_offset, length, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_sendfile_with_cb, _LOOP _FS _FILE _FILE _I64 _U64 _BOOL); - -// HL_PRIM int HL_NAME(fs_access_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { -// return uv_fs_access(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_access_with_cb, _LOOP _FS _BYTES _I32 _BOOL); +DEFINE_PRIM(_BYTES, err_name_r, _I32 _BYTES _U64); -// HL_PRIM int HL_NAME(fs_chmod_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { -// return uv_fs_chmod(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_chmod_with_cb, _LOOP _FS _BYTES _I32 _BOOL); +DEFINE_PRIM(_I32, translate_sys_error, _I32); -// HL_PRIM int HL_NAME(fs_fchmod_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, int mode, bool use_uv_fs_cb ) { -// return uv_fs_fchmod(loop, req, file, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_fchmod_with_cb, _LOOP _FS _FILE _I32 _BOOL); +DEFINE_PRIM(_VOID, fs_req_cleanup, _FS); -// HL_PRIM int HL_NAME(fs_utime_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, bool use_uv_fs_cb ) { -// return uv_fs_utime(loop, req, path, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_utime_with_cb, _LOOP _FS _BYTES _F64 _F64 _BOOL); +HL_PRIM int HL_NAME(fs_close_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { + return uv_fs_close(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_close_with_cb, _LOOP _FS _FILE _BOOL); -// HL_PRIM int HL_NAME(fs_futime_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime, double mtime, bool use_uv_fs_cb ) { -// return uv_fs_futime(loop, req, file, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_futime_with_cb, _LOOP _FS _FILE _F64 _F64 _BOOL); +HL_PRIM int HL_NAME(fs_open_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, bool use_uv_fs_cb ) { + return uv_fs_open(loop, req, path, flags, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_open_with_cb, _LOOP _FS _BYTES _I32 _I32 _BOOL); -// HL_PRIM int HL_NAME(fs_lutime_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, bool use_uv_fs_cb ) { -// return uv_fs_lutime(loop, req, path, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_lutime_with_cb, _LOOP _FS _BYTES _F64 _F64 _BOOL); +HL_PRIM int HL_NAME(fs_read_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, bool use_uv_fs_cb ) { + return uv_fs_read(loop, req, file, bufs, nbufs, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_read_with_cb, _LOOP _FS _FILE _REF(_BUF) _U32 _I64 _BOOL); -// HL_PRIM int HL_NAME(fs_link_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, bool use_uv_fs_cb ) { -// return uv_fs_link(loop, req, path, new_path, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_link_with_cb, _LOOP _FS _BYTES _BYTES _BOOL); +HL_PRIM int HL_NAME(fs_unlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { + return uv_fs_unlink(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_unlink_with_cb, _LOOP _FS _BYTES _BOOL); -// HL_PRIM int HL_NAME(fs_symlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, bool use_uv_fs_cb ) { -// return uv_fs_symlink(loop, req, path, new_path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_symlink_with_cb, _LOOP _FS _BYTES _BYTES _I32 _BOOL); +HL_PRIM int HL_NAME(fs_write_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, bool use_uv_fs_cb ) { + return uv_fs_write(loop, req, file, bufs, nbufs, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_write_with_cb, _LOOP _FS _FILE _REF(_BUF) _U32 _I64 _BOOL); -// HL_PRIM int HL_NAME(fs_readlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { -// return uv_fs_readlink(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_readlink_with_cb, _LOOP _FS _BYTES _BOOL); +HL_PRIM int HL_NAME(fs_mkdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { + return uv_fs_mkdir(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_mkdir_with_cb, _LOOP _FS _BYTES _I32 _BOOL); -// HL_PRIM int HL_NAME(fs_realpath_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { -// return uv_fs_realpath(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_realpath_with_cb, _LOOP _FS _BYTES _BOOL); +HL_PRIM int HL_NAME(fs_mkdtemp_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* tpl, bool use_uv_fs_cb ) { + return uv_fs_mkdtemp(loop, req, tpl, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_mkdtemp_with_cb, _LOOP _FS _BYTES _BOOL); -// HL_PRIM int HL_NAME(fs_chown_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { -// return uv_fs_chown(loop, req, path, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_chown_with_cb, _LOOP _FS _BYTES _UID_T _GID_T _BOOL); +HL_PRIM int HL_NAME(fs_mkstemp_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* tpl, bool use_uv_fs_cb ) { + return uv_fs_mkstemp(loop, req, tpl, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_mkstemp_with_cb, _LOOP _FS _BYTES _BOOL); -// HL_PRIM int HL_NAME(fs_fchown_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { -// return uv_fs_fchown(loop, req, file, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_fchown_with_cb, _LOOP _FS _FILE _UID_T _GID_T _BOOL); +HL_PRIM int HL_NAME(fs_rmdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { + return uv_fs_rmdir(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_rmdir_with_cb, _LOOP _FS _BYTES _BOOL); -// HL_PRIM int HL_NAME(fs_lchown_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { -// return uv_fs_lchown(loop, req, path, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); -// } -// DEFINE_PRIM(_I32, fs_lchown_with_cb, _LOOP _FS _BYTES _UID_T _GID_T _BOOL); +HL_PRIM int HL_NAME(fs_opendir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { + return uv_fs_opendir(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_opendir_with_cb, _LOOP _FS _BYTES _BOOL); -// DEFINE_PRIM(_FS_TYPE, fs_get_type, _FS); +HL_PRIM int HL_NAME(fs_closedir_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, bool use_uv_fs_cb ) { + return uv_fs_closedir(loop, req, dir, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_closedir_with_cb, _LOOP _FS _DIR _BOOL); -// DEFINE_PRIM(_I64, fs_get_result, _FS); +HL_PRIM int HL_NAME(fs_readdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, bool use_uv_fs_cb ) { + return uv_fs_readdir(loop, req, dir, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_readdir_with_cb, _LOOP _FS _DIR _BOOL); -// DEFINE_PRIM(_I32, fs_get_system_error, _FS); +HL_PRIM int HL_NAME(fs_scandir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, bool use_uv_fs_cb ) { + return uv_fs_scandir(loop, req, path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_scandir_with_cb, _LOOP _FS _BYTES _I32 _BOOL); -// DEFINE_PRIM(_POINTER, fs_get_ptr, _FS); +DEFINE_PRIM(_I32, fs_scandir_next, _FS _DIRENT); -// DEFINE_PRIM(_BYTES, fs_get_path, _FS); +HL_PRIM int HL_NAME(fs_stat_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { + return uv_fs_stat(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_stat_with_cb, _LOOP _FS _BYTES _BOOL); -// DEFINE_PRIM(_STAT, fs_get_statbuf, _FS); +HL_PRIM int HL_NAME(fs_fstat_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { + return uv_fs_fstat(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_fstat_with_cb, _LOOP _FS _FILE _BOOL); -// DEFINE_PRIM(_OS_FD_T, get_osfhandle, _I32); +HL_PRIM int HL_NAME(fs_lstat_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { + return uv_fs_lstat(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_lstat_with_cb, _LOOP _FS _BYTES _BOOL); -// DEFINE_PRIM(_I32, open_osfhandle, _OS_FD_T); +HL_PRIM int HL_NAME(fs_statfs_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { + return uv_fs_statfs(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_statfs_with_cb, _LOOP _FS _BYTES _BOOL); -// DEFINE_PRIM(_I32, fs_event_init, _LOOP _FS_EVENT); +HL_PRIM int HL_NAME(fs_rename_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, bool use_uv_fs_cb ) { + return uv_fs_rename(loop, req, path, new_path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_rename_with_cb, _LOOP _FS _BYTES _BYTES _BOOL); -// HL_PRIM int HL_NAME(fs_event_start_with_cb)( uv_fs_event_t* handle, const char* path, unsigned int flags ) { -// return uv_fs_event_start(handle, on_uv_fs_event_cb, path, flags); -// } -// DEFINE_PRIM(_I32, fs_event_start_with_cb, _FS_EVENT _BYTES _U32); +HL_PRIM int HL_NAME(fs_fsync_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { + return uv_fs_fsync(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_fsync_with_cb, _LOOP _FS _FILE _BOOL); -// DEFINE_PRIM(_I32, fs_event_stop, _FS_EVENT); +HL_PRIM int HL_NAME(fs_fdatasync_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { + return uv_fs_fdatasync(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_fdatasync_with_cb, _LOOP _FS _FILE _BOOL); -// DEFINE_PRIM(_I32, fs_event_getpath, _FS_EVENT _BYTES _REF(_I64)); +HL_PRIM int HL_NAME(fs_ftruncate_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, int64_t offset, bool use_uv_fs_cb ) { + return uv_fs_ftruncate(loop, req, file, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_ftruncate_with_cb, _LOOP _FS _FILE _I64 _BOOL); -// DEFINE_PRIM(_I32, fs_poll_init, _LOOP _FS_POLL); +HL_PRIM int HL_NAME(fs_copyfile_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, bool use_uv_fs_cb ) { + return uv_fs_copyfile(loop, req, path, new_path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_copyfile_with_cb, _LOOP _FS _BYTES _BYTES _I32 _BOOL); + +HL_PRIM int HL_NAME(fs_sendfile_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file out_fd, uv_file in_fd, int64_t in_offset, size_t length, bool use_uv_fs_cb ) { + return uv_fs_sendfile(loop, req, out_fd, in_fd, in_offset, length, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_sendfile_with_cb, _LOOP _FS _FILE _FILE _I64 _U64 _BOOL); + +HL_PRIM int HL_NAME(fs_access_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { + return uv_fs_access(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_access_with_cb, _LOOP _FS _BYTES _I32 _BOOL); + +HL_PRIM int HL_NAME(fs_chmod_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { + return uv_fs_chmod(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_chmod_with_cb, _LOOP _FS _BYTES _I32 _BOOL); + +HL_PRIM int HL_NAME(fs_fchmod_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, int mode, bool use_uv_fs_cb ) { + return uv_fs_fchmod(loop, req, file, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_fchmod_with_cb, _LOOP _FS _FILE _I32 _BOOL); + +HL_PRIM int HL_NAME(fs_utime_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, bool use_uv_fs_cb ) { + return uv_fs_utime(loop, req, path, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_utime_with_cb, _LOOP _FS _BYTES _F64 _F64 _BOOL); + +HL_PRIM int HL_NAME(fs_futime_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime, double mtime, bool use_uv_fs_cb ) { + return uv_fs_futime(loop, req, file, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_futime_with_cb, _LOOP _FS _FILE _F64 _F64 _BOOL); + +HL_PRIM int HL_NAME(fs_lutime_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, bool use_uv_fs_cb ) { + return uv_fs_lutime(loop, req, path, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_lutime_with_cb, _LOOP _FS _BYTES _F64 _F64 _BOOL); -// HL_PRIM int HL_NAME(fs_poll_start_with_cb)( uv_fs_poll_t* handle, const char* path, unsigned int interval ) { -// return uv_fs_poll_start(handle, on_uv_fs_poll_cb, path, interval); -// } -// DEFINE_PRIM(_I32, fs_poll_start_with_cb, _FS_POLL _BYTES _U32); +HL_PRIM int HL_NAME(fs_link_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, bool use_uv_fs_cb ) { + return uv_fs_link(loop, req, path, new_path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_link_with_cb, _LOOP _FS _BYTES _BYTES _BOOL); -// DEFINE_PRIM(_I32, fs_poll_stop, _FS_POLL); +HL_PRIM int HL_NAME(fs_symlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, bool use_uv_fs_cb ) { + return uv_fs_symlink(loop, req, path, new_path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_symlink_with_cb, _LOOP _FS _BYTES _BYTES _I32 _BOOL); -// DEFINE_PRIM(_I32, fs_poll_getpath, _FS_POLL _BYTES _REF(_I64)); +HL_PRIM int HL_NAME(fs_readlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { + return uv_fs_readlink(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_readlink_with_cb, _LOOP _FS _BYTES _BOOL); + +HL_PRIM int HL_NAME(fs_realpath_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { + return uv_fs_realpath(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_realpath_with_cb, _LOOP _FS _BYTES _BOOL); + +HL_PRIM int HL_NAME(fs_chown_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { + return uv_fs_chown(loop, req, path, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_chown_with_cb, _LOOP _FS _BYTES _UID_T _GID_T _BOOL); + +HL_PRIM int HL_NAME(fs_fchown_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { + return uv_fs_fchown(loop, req, file, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_fchown_with_cb, _LOOP _FS _FILE _UID_T _GID_T _BOOL); + +HL_PRIM int HL_NAME(fs_lchown_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { + return uv_fs_lchown(loop, req, path, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); +} +DEFINE_PRIM(_I32, fs_lchown_with_cb, _LOOP _FS _BYTES _UID_T _GID_T _BOOL); + +DEFINE_PRIM(_FS_TYPE, fs_get_type, _FS); + +DEFINE_PRIM(_I64, fs_get_result, _FS); + +DEFINE_PRIM(_I32, fs_get_system_error, _FS); + +DEFINE_PRIM(_POINTER, fs_get_ptr, _FS); + +DEFINE_PRIM(_BYTES, fs_get_path, _FS); + +DEFINE_PRIM(_STAT, fs_get_statbuf, _FS); + +DEFINE_PRIM(_OS_FD_T, get_osfhandle, _I32); + +DEFINE_PRIM(_I32, open_osfhandle, _OS_FD_T); + +DEFINE_PRIM(_I32, fs_event_init, _LOOP _FS_EVENT); + +HL_PRIM int HL_NAME(fs_event_start_with_cb)( uv_fs_event_t* handle, const char* path, unsigned int flags ) { + return uv_fs_event_start(handle, on_uv_fs_event_cb, path, flags); +} +DEFINE_PRIM(_I32, fs_event_start_with_cb, _FS_EVENT _BYTES _U32); + +DEFINE_PRIM(_I32, fs_event_stop, _FS_EVENT); + +DEFINE_PRIM(_I32, fs_event_getpath, _FS_EVENT _BYTES _REF(_I64)); + +DEFINE_PRIM(_I32, fs_poll_init, _LOOP _FS_POLL); + +HL_PRIM int HL_NAME(fs_poll_start_with_cb)( uv_fs_poll_t* handle, const char* path, unsigned int interval ) { + return uv_fs_poll_start(handle, on_uv_fs_poll_cb, path, interval); +} +DEFINE_PRIM(_I32, fs_poll_start_with_cb, _FS_POLL _BYTES _U32); + +DEFINE_PRIM(_I32, fs_poll_stop, _FS_POLL); + +DEFINE_PRIM(_I32, fs_poll_getpath, _FS_POLL _BYTES _REF(_I64)); DEFINE_PRIM(_I32, is_active, _HANDLE); @@ -273,32 +273,32 @@ DEFINE_PRIM(_VOID, unref, _HANDLE); DEFINE_PRIM(_I32, has_ref, _HANDLE); -// DEFINE_PRIM(_U64, handle_size, _HANDLE_TYPE); +DEFINE_PRIM(_U64, handle_size, _HANDLE_TYPE); -// DEFINE_PRIM(_I32, send_buffer_size, _HANDLE _REF(_I32)); +DEFINE_PRIM(_I32, send_buffer_size, _HANDLE _REF(_I32)); -// DEFINE_PRIM(_I32, recv_buffer_size, _HANDLE _REF(_I32)); +DEFINE_PRIM(_I32, recv_buffer_size, _HANDLE _REF(_I32)); -// DEFINE_PRIM(_I32, fileno, _HANDLE _OS_FD); +DEFINE_PRIM(_I32, fileno, _HANDLE _OS_FD); -// DEFINE_PRIM(_LOOP, handle_get_loop, _HANDLE); +DEFINE_PRIM(_LOOP, handle_get_loop, _HANDLE); DEFINE_PRIM(_POINTER, handle_get_data, _HANDLE); DEFINE_PRIM(_POINTER, handle_set_data, _HANDLE _POINTER); -// DEFINE_PRIM(_HANDLE_TYPE, handle_get_type, _HANDLE); +DEFINE_PRIM(_HANDLE_TYPE, handle_get_type, _HANDLE); -// DEFINE_PRIM(_BYTES, handle_type_name, _HANDLE_TYPE); +DEFINE_PRIM(_BYTES, handle_type_name, _HANDLE_TYPE); -// DEFINE_PRIM(_I32, idle_init, _LOOP _IDLE); +DEFINE_PRIM(_I32, idle_init, _LOOP _IDLE); -// HL_PRIM int HL_NAME(idle_start_with_cb)( uv_idle_t* idle ) { -// return uv_idle_start(idle, on_uv_idle_cb); -// } -// DEFINE_PRIM(_I32, idle_start_with_cb, _IDLE); +HL_PRIM int HL_NAME(idle_start_with_cb)( uv_idle_t* idle ) { + return uv_idle_start(idle, on_uv_idle_cb); +} +DEFINE_PRIM(_I32, idle_start_with_cb, _IDLE); -// DEFINE_PRIM(_I32, idle_stop, _IDLE); +DEFINE_PRIM(_I32, idle_stop, _IDLE); DEFINE_PRIM(_I32, loop_init, _LOOP); @@ -312,272 +312,272 @@ DEFINE_PRIM(_I32, loop_alive, _LOOP); DEFINE_PRIM(_VOID, stop, _LOOP); -// DEFINE_PRIM(_U64, loop_size, _NO_ARG); +DEFINE_PRIM(_U64, loop_size, _NO_ARG); -// DEFINE_PRIM(_I32, backend_fd, _LOOP); +DEFINE_PRIM(_I32, backend_fd, _LOOP); -// DEFINE_PRIM(_I32, backend_timeout, _LOOP); +DEFINE_PRIM(_I32, backend_timeout, _LOOP); -// DEFINE_PRIM(_I64, now, _LOOP); +DEFINE_PRIM(_I64, now, _LOOP); -// DEFINE_PRIM(_VOID, update_time, _LOOP); +DEFINE_PRIM(_VOID, update_time, _LOOP); -// HL_PRIM void HL_NAME(walk_with_cb)( uv_loop_t* loop, void* arg ) { -// uv_walk(loop, on_uv_walk_cb, arg); -// } -// DEFINE_PRIM(_VOID, walk_with_cb, _LOOP _POINTER); +HL_PRIM void HL_NAME(walk_with_cb)( uv_loop_t* loop, void* arg ) { + uv_walk(loop, on_uv_walk_cb, arg); +} +DEFINE_PRIM(_VOID, walk_with_cb, _LOOP _POINTER); -// DEFINE_PRIM(_I32, loop_fork, _LOOP); +DEFINE_PRIM(_I32, loop_fork, _LOOP); -// DEFINE_PRIM(_POINTER, loop_get_data, _LOOP); +DEFINE_PRIM(_POINTER, loop_get_data, _LOOP); -// DEFINE_PRIM(_POINTER, loop_set_data, _LOOP _POINTER); +DEFINE_PRIM(_POINTER, loop_set_data, _LOOP _POINTER); -// DEFINE_PRIM(_I64, metrics_idle_time, _LOOP); +DEFINE_PRIM(_I64, metrics_idle_time, _LOOP); -// DEFINE_PRIM(_HANDLE_TYPE, guess_handle, _FILE); +DEFINE_PRIM(_HANDLE_TYPE, guess_handle, _FILE); -// DEFINE_PRIM(_I32, replace_allocator, _MALLOC_FUNC _REALLOC_FUNC _CALLOC_FUNC _FREE_FUNC); +DEFINE_PRIM(_I32, replace_allocator, _MALLOC_FUNC _REALLOC_FUNC _CALLOC_FUNC _FREE_FUNC); -// DEFINE_PRIM(_VOID, library_shutdown, _NO_ARG); +DEFINE_PRIM(_VOID, library_shutdown, _NO_ARG); -// DEFINE_PRIM(_BUF, buf_init, _BYTES _U32); +DEFINE_PRIM(_BUF, buf_init, _BYTES _U32); -// DEFINE_PRIM(_REF(_BYTES), setup_args, _I32 _REF(_BYTES)); +DEFINE_PRIM(_REF(_BYTES), setup_args, _I32 _REF(_BYTES)); -// DEFINE_PRIM(_I32, get_process_title, _BYTES _U64); +DEFINE_PRIM(_I32, get_process_title, _BYTES _U64); -// DEFINE_PRIM(_I32, set_process_title, _BYTES); +DEFINE_PRIM(_I32, set_process_title, _BYTES); -// DEFINE_PRIM(_I32, resident_set_memory, _REF(_I64)); +DEFINE_PRIM(_I32, resident_set_memory, _REF(_I64)); -// DEFINE_PRIM(_I32, uptime, _REF(_F64)); +DEFINE_PRIM(_I32, uptime, _REF(_F64)); -// DEFINE_PRIM(_I32, getrusage, _RUSAGE); +DEFINE_PRIM(_I32, getrusage, _RUSAGE); -// DEFINE_PRIM(_I32, os_getpid, _NO_ARG); +DEFINE_PRIM(_I32, os_getpid, _NO_ARG); -// DEFINE_PRIM(_I32, os_getppid, _NO_ARG); +DEFINE_PRIM(_I32, os_getppid, _NO_ARG); -// DEFINE_PRIM(_I32, cpu_info, _REF(_CPU_INFO) _REF(_I32)); +DEFINE_PRIM(_I32, cpu_info, _REF(_CPU_INFO) _REF(_I32)); -// DEFINE_PRIM(_VOID, free_cpu_info, _CPU_INFO _I32); +DEFINE_PRIM(_VOID, free_cpu_info, _CPU_INFO _I32); -// DEFINE_PRIM(_I32, interface_addresses, _REF(_INTERFACE_ADDRESS) _REF(_I32)); +DEFINE_PRIM(_I32, interface_addresses, _REF(_INTERFACE_ADDRESS) _REF(_I32)); -// DEFINE_PRIM(_VOID, free_interface_addresses, _INTERFACE_ADDRESS _I32); +DEFINE_PRIM(_VOID, free_interface_addresses, _INTERFACE_ADDRESS _I32); -// DEFINE_PRIM(_VOID, loadavg, _F64); +DEFINE_PRIM(_VOID, loadavg, _REF(_F64)); -// DEFINE_PRIM(_I32, ip4_addr, _BYTES _I32 _SOCKADDR_IN); +DEFINE_PRIM(_I32, ip4_addr, _BYTES _I32 _SOCKADDR_IN); -// DEFINE_PRIM(_I32, ip6_addr, _BYTES _I32 _SOCKADDR_IN6); +DEFINE_PRIM(_I32, ip6_addr, _BYTES _I32 _SOCKADDR_IN6); -// DEFINE_PRIM(_I32, ip4_name, _SOCKADDR_IN _BYTES _U64); +DEFINE_PRIM(_I32, ip4_name, _SOCKADDR_IN _BYTES _U64); -// DEFINE_PRIM(_I32, ip6_name, _SOCKADDR_IN6 _BYTES _U64); +DEFINE_PRIM(_I32, ip6_name, _SOCKADDR_IN6 _BYTES _U64); -// DEFINE_PRIM(_I32, inet_ntop, _I32 _POINTER _BYTES _U64); +DEFINE_PRIM(_I32, inet_ntop, _I32 _POINTER _BYTES _U64); -// DEFINE_PRIM(_I32, inet_pton, _I32 _BYTES _POINTER); +DEFINE_PRIM(_I32, inet_pton, _I32 _BYTES _POINTER); -// DEFINE_PRIM(_I32, if_indextoname, _U32 _BYTES _REF(_I64)); +DEFINE_PRIM(_I32, if_indextoname, _U32 _BYTES _REF(_I64)); -// DEFINE_PRIM(_I32, if_indextoiid, _U32 _BYTES _REF(_I64)); +DEFINE_PRIM(_I32, if_indextoiid, _U32 _BYTES _REF(_I64)); -// DEFINE_PRIM(_I32, exepath, _BYTES _REF(_I64)); +DEFINE_PRIM(_I32, exepath, _BYTES _REF(_I64)); -// DEFINE_PRIM(_I32, cwd, _BYTES _REF(_I64)); +DEFINE_PRIM(_I32, cwd, _BYTES _REF(_I64)); -// DEFINE_PRIM(_I32, chdir, _BYTES); +DEFINE_PRIM(_I32, chdir, _BYTES); -// DEFINE_PRIM(_I32, os_homedir, _BYTES _REF(_I64)); +DEFINE_PRIM(_I32, os_homedir, _BYTES _REF(_I64)); -// DEFINE_PRIM(_I32, os_tmpdir, _BYTES _REF(_I64)); +DEFINE_PRIM(_I32, os_tmpdir, _BYTES _REF(_I64)); -// DEFINE_PRIM(_I32, os_get_passwd, _PASSWD); +DEFINE_PRIM(_I32, os_get_passwd, _PASSWD); -// DEFINE_PRIM(_VOID, os_free_passwd, _PASSWD); +DEFINE_PRIM(_VOID, os_free_passwd, _PASSWD); -// DEFINE_PRIM(_I64, get_free_memory, _NO_ARG); +DEFINE_PRIM(_I64, get_free_memory, _NO_ARG); -// DEFINE_PRIM(_I64, get_total_memory, _NO_ARG); +DEFINE_PRIM(_I64, get_total_memory, _NO_ARG); -// DEFINE_PRIM(_I64, get_constrained_memory, _NO_ARG); +DEFINE_PRIM(_I64, get_constrained_memory, _NO_ARG); -// DEFINE_PRIM(_I64, hrtime, _NO_ARG); +DEFINE_PRIM(_I64, hrtime, _NO_ARG); -// DEFINE_PRIM(_VOID, print_all_handles, _LOOP _FILE); +DEFINE_PRIM(_VOID, print_all_handles, _LOOP _FILE); -// DEFINE_PRIM(_VOID, print_active_handles, _LOOP _FILE); +DEFINE_PRIM(_VOID, print_active_handles, _LOOP _FILE); -// DEFINE_PRIM(_I32, os_environ, _REF(_ENV_ITEM) _REF(_I32)); +DEFINE_PRIM(_I32, os_environ, _REF(_ENV_ITEM) _REF(_I32)); -// DEFINE_PRIM(_VOID, os_free_environ, _ENV_ITEM _I32); +DEFINE_PRIM(_VOID, os_free_environ, _ENV_ITEM _I32); -// DEFINE_PRIM(_I32, os_getenv, _BYTES _BYTES _REF(_I64)); +DEFINE_PRIM(_I32, os_getenv, _BYTES _BYTES _REF(_I64)); -// DEFINE_PRIM(_I32, os_setenv, _BYTES _BYTES); +DEFINE_PRIM(_I32, os_setenv, _BYTES _BYTES); -// DEFINE_PRIM(_I32, os_unsetenv, _BYTES); +DEFINE_PRIM(_I32, os_unsetenv, _BYTES); -// DEFINE_PRIM(_I32, os_gethostname, _BYTES _REF(_I64)); +DEFINE_PRIM(_I32, os_gethostname, _BYTES _REF(_I64)); -// DEFINE_PRIM(_I32, os_getpriority, _I32 _REF(_I32)); +DEFINE_PRIM(_I32, os_getpriority, _I32 _REF(_I32)); -// DEFINE_PRIM(_I32, os_setpriority, _I32 _I32); +DEFINE_PRIM(_I32, os_setpriority, _I32 _I32); -// DEFINE_PRIM(_I32, os_uname, _UTSNAME); +DEFINE_PRIM(_I32, os_uname, _UTSNAME); -// DEFINE_PRIM(_I32, gettimeofday, _TIMEVAL64); +DEFINE_PRIM(_I32, gettimeofday, _TIMEVAL64); -// HL_PRIM int HL_NAME(random_with_cb)( uv_loop_t* loop, uv_random_t* req, void* buf, size_t buflen, unsigned int flags ) { -// return uv_random(loop, req, buf, buflen, flags, on_uv_random_cb); -// } -// DEFINE_PRIM(_I32, random_with_cb, _LOOP _RANDOM _POINTER _U64 _U32); +HL_PRIM int HL_NAME(random_with_cb)( uv_loop_t* loop, uv_random_t* req, void* buf, size_t buflen, unsigned int flags ) { + return uv_random(loop, req, buf, buflen, flags, on_uv_random_cb); +} +DEFINE_PRIM(_I32, random_with_cb, _LOOP _RANDOM _POINTER _U64 _U32); -// DEFINE_PRIM(_VOID, sleep, _U32); +DEFINE_PRIM(_VOID, sleep, _U32); -// DEFINE_PRIM(_I32, pipe_init, _LOOP _PIPE _I32); +DEFINE_PRIM(_I32, pipe_init, _LOOP _PIPE _I32); -// DEFINE_PRIM(_I32, pipe_open, _PIPE _FILE); +DEFINE_PRIM(_I32, pipe_open, _PIPE _FILE); -// DEFINE_PRIM(_I32, pipe_bind, _PIPE _BYTES); +DEFINE_PRIM(_I32, pipe_bind, _PIPE _BYTES); -// HL_PRIM void HL_NAME(pipe_connect_with_cb)( uv_connect_t* req, uv_pipe_t* handle, const char* name ) { -// uv_pipe_connect(req, handle, name, on_uv_connect_cb); -// } -// DEFINE_PRIM(_VOID, pipe_connect_with_cb, _CONNECT _PIPE _BYTES); +HL_PRIM void HL_NAME(pipe_connect_with_cb)( uv_connect_t* req, uv_pipe_t* handle, const char* name ) { + uv_pipe_connect(req, handle, name, on_uv_connect_cb); +} +DEFINE_PRIM(_VOID, pipe_connect_with_cb, _CONNECT _PIPE _BYTES); -// DEFINE_PRIM(_I32, pipe_getsockname, _PIPE _BYTES _REF(_I64)); +DEFINE_PRIM(_I32, pipe_getsockname, _PIPE _BYTES _REF(_I64)); -// DEFINE_PRIM(_I32, pipe_getpeername, _PIPE _BYTES _REF(_I64)); +DEFINE_PRIM(_I32, pipe_getpeername, _PIPE _BYTES _REF(_I64)); -// DEFINE_PRIM(_VOID, pipe_pending_instances, _PIPE _I32); +DEFINE_PRIM(_VOID, pipe_pending_instances, _PIPE _I32); -// DEFINE_PRIM(_I32, pipe_pending_count, _PIPE); +DEFINE_PRIM(_I32, pipe_pending_count, _PIPE); -// DEFINE_PRIM(_HANDLE_TYPE, pipe_pending_type, _PIPE); +DEFINE_PRIM(_HANDLE_TYPE, pipe_pending_type, _PIPE); -// DEFINE_PRIM(_I32, pipe_chmod, _PIPE _I32); +DEFINE_PRIM(_I32, pipe_chmod, _PIPE _I32); -// DEFINE_PRIM(_I32, pipe, _FILE _I32 _I32); +DEFINE_PRIM(_I32, pipe, _REF(_FILE) _I32 _I32); -// DEFINE_PRIM(_I32, prepare_init, _LOOP _PREPARE); +DEFINE_PRIM(_I32, prepare_init, _LOOP _PREPARE); -// HL_PRIM int HL_NAME(prepare_start_with_cb)( uv_prepare_t* prepare ) { -// return uv_prepare_start(prepare, on_uv_prepare_cb); -// } -// DEFINE_PRIM(_I32, prepare_start_with_cb, _PREPARE); +HL_PRIM int HL_NAME(prepare_start_with_cb)( uv_prepare_t* prepare ) { + return uv_prepare_start(prepare, on_uv_prepare_cb); +} +DEFINE_PRIM(_I32, prepare_start_with_cb, _PREPARE); -// DEFINE_PRIM(_I32, prepare_stop, _PREPARE); +DEFINE_PRIM(_I32, prepare_stop, _PREPARE); -// DEFINE_PRIM(_VOID, disable_stdio_inheritance, _NO_ARG); +DEFINE_PRIM(_VOID, disable_stdio_inheritance, _NO_ARG); -// DEFINE_PRIM(_I32, spawn, _LOOP _PROCESS _PROCESS_OPTIONS); +DEFINE_PRIM(_I32, spawn, _LOOP _PROCESS _PROCESS_OPTIONS); -// DEFINE_PRIM(_I32, process_kill, _PROCESS _I32); +DEFINE_PRIM(_I32, process_kill, _PROCESS _I32); -// DEFINE_PRIM(_I32, kill, _I32 _I32); +DEFINE_PRIM(_I32, kill, _I32 _I32); -// DEFINE_PRIM(_I32, process_get_pid, _PROCESS); +DEFINE_PRIM(_I32, process_get_pid, _PROCESS); DEFINE_PRIM(_I32, cancel, _REQ); -// DEFINE_PRIM(_U64, req_size, _REQ_TYPE); +DEFINE_PRIM(_U64, req_size, _REQ_TYPE); DEFINE_PRIM(_POINTER, req_get_data, _REQ); DEFINE_PRIM(_POINTER, req_set_data, _REQ _POINTER); -// DEFINE_PRIM(_REQ_TYPE, req_get_type, _REQ); +DEFINE_PRIM(_REQ_TYPE, req_get_type, _REQ); -// DEFINE_PRIM(_BYTES, req_type_name, _REQ_TYPE); +DEFINE_PRIM(_BYTES, req_type_name, _REQ_TYPE); -// DEFINE_PRIM(_I32, signal_init, _LOOP _SIGNAL); +DEFINE_PRIM(_I32, signal_init, _LOOP _SIGNAL); -// HL_PRIM int HL_NAME(signal_start_with_cb)( uv_signal_t* signal, int signum ) { -// return uv_signal_start(signal, on_uv_signal_cb, signum); -// } -// DEFINE_PRIM(_I32, signal_start_with_cb, _SIGNAL _I32); +HL_PRIM int HL_NAME(signal_start_with_cb)( uv_signal_t* signal, int signum ) { + return uv_signal_start(signal, on_uv_signal_cb, signum); +} +DEFINE_PRIM(_I32, signal_start_with_cb, _SIGNAL _I32); -// HL_PRIM int HL_NAME(signal_start_oneshot_with_cb)( uv_signal_t* signal, int signum ) { -// return uv_signal_start_oneshot(signal, on_uv_signal_cb, signum); -// } -// DEFINE_PRIM(_I32, signal_start_oneshot_with_cb, _SIGNAL _I32); +HL_PRIM int HL_NAME(signal_start_oneshot_with_cb)( uv_signal_t* signal, int signum ) { + return uv_signal_start_oneshot(signal, on_uv_signal_cb, signum); +} +DEFINE_PRIM(_I32, signal_start_oneshot_with_cb, _SIGNAL _I32); -// DEFINE_PRIM(_I32, signal_stop, _SIGNAL); +DEFINE_PRIM(_I32, signal_stop, _SIGNAL); -// HL_PRIM int HL_NAME(shutdown_with_cb)( uv_shutdown_t* req, uv_stream_t* handle ) { -// return uv_shutdown(req, handle, on_uv_shutdown_cb); -// } -// DEFINE_PRIM(_I32, shutdown_with_cb, _SHUTDOWN _STREAM); +HL_PRIM int HL_NAME(shutdown_with_cb)( uv_shutdown_t* req, uv_stream_t* handle ) { + return uv_shutdown(req, handle, on_uv_shutdown_cb); +} +DEFINE_PRIM(_I32, shutdown_with_cb, _SHUTDOWN _STREAM); -// HL_PRIM int HL_NAME(listen_with_cb)( uv_stream_t* stream, int backlog ) { -// return uv_listen(stream, backlog, on_uv_connection_cb); -// } -// DEFINE_PRIM(_I32, listen_with_cb, _STREAM _I32); +HL_PRIM int HL_NAME(listen_with_cb)( uv_stream_t* stream, int backlog ) { + return uv_listen(stream, backlog, on_uv_connection_cb); +} +DEFINE_PRIM(_I32, listen_with_cb, _STREAM _I32); -// DEFINE_PRIM(_I32, accept, _STREAM _STREAM); +DEFINE_PRIM(_I32, accept, _STREAM _STREAM); -// HL_PRIM int HL_NAME(read_start_with_cb)( uv_stream_t* stream ) { -// return uv_read_start(stream, on_uv_alloc_cb, on_uv_read_cb); -// } -// DEFINE_PRIM(_I32, read_start_with_cb, _STREAM); +HL_PRIM int HL_NAME(read_start_with_cb)( uv_stream_t* stream ) { + return uv_read_start(stream, on_uv_alloc_cb, on_uv_read_cb); +} +DEFINE_PRIM(_I32, read_start_with_cb, _STREAM); -// DEFINE_PRIM(_I32, read_stop, _STREAM); +DEFINE_PRIM(_I32, read_stop, _STREAM); -// HL_PRIM int HL_NAME(write_with_cb)( uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs ) { -// return uv_write(req, handle, bufs[], nbufs, on_uv_write_cb); -// } -// DEFINE_PRIM(_I32, write_with_cb, _WRITE _STREAM _BUF _U32); +HL_PRIM int HL_NAME(write_with_cb)( uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs ) { + return uv_write(req, handle, bufs, nbufs, on_uv_write_cb); +} +DEFINE_PRIM(_I32, write_with_cb, _WRITE _STREAM _REF(_BUF) _U32); -// HL_PRIM int HL_NAME(write2_with_cb)( uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_stream_t* send_handle ) { -// return uv_write2(req, handle, bufs[], nbufs, send_handle, on_uv_write_cb); -// } -// DEFINE_PRIM(_I32, write2_with_cb, _WRITE _STREAM _BUF _U32 _STREAM); +HL_PRIM int HL_NAME(write2_with_cb)( uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_stream_t* send_handle ) { + return uv_write2(req, handle, bufs, nbufs, send_handle, on_uv_write_cb); +} +DEFINE_PRIM(_I32, write2_with_cb, _WRITE _STREAM _REF(_BUF) _U32 _STREAM); -// DEFINE_PRIM(_I32, try_write, _STREAM _BUF _U32); +DEFINE_PRIM(_I32, try_write, _STREAM _REF(_BUF) _U32); -// DEFINE_PRIM(_I32, try_write2, _STREAM _BUF _U32 _STREAM); +DEFINE_PRIM(_I32, try_write2, _STREAM _REF(_BUF) _U32 _STREAM); -// DEFINE_PRIM(_I32, is_readable, _STREAM); +DEFINE_PRIM(_I32, is_readable, _STREAM); -// DEFINE_PRIM(_I32, is_writable, _STREAM); +DEFINE_PRIM(_I32, is_writable, _STREAM); -// DEFINE_PRIM(_I32, stream_set_blocking, _STREAM _I32); +DEFINE_PRIM(_I32, stream_set_blocking, _STREAM _I32); -// DEFINE_PRIM(_U64, stream_get_write_queue_size, _STREAM); +DEFINE_PRIM(_U64, stream_get_write_queue_size, _STREAM); -// DEFINE_PRIM(_I32, tcp_init, _LOOP _TCP); +DEFINE_PRIM(_I32, tcp_init, _LOOP _TCP); -// DEFINE_PRIM(_I32, tcp_init_ex, _LOOP _TCP _U32); +DEFINE_PRIM(_I32, tcp_init_ex, _LOOP _TCP _U32); -// DEFINE_PRIM(_I32, tcp_open, _TCP _OS_SOCK_T); +DEFINE_PRIM(_I32, tcp_open, _TCP _OS_SOCK_T); -// DEFINE_PRIM(_I32, tcp_nodelay, _TCP _I32); +DEFINE_PRIM(_I32, tcp_nodelay, _TCP _I32); -// DEFINE_PRIM(_I32, tcp_keepalive, _TCP _I32 _U32); +DEFINE_PRIM(_I32, tcp_keepalive, _TCP _I32 _U32); -// DEFINE_PRIM(_I32, tcp_simultaneous_accepts, _TCP _I32); +DEFINE_PRIM(_I32, tcp_simultaneous_accepts, _TCP _I32); -// DEFINE_PRIM(_I32, tcp_bind, _TCP _SOCKADDR _U32); +DEFINE_PRIM(_I32, tcp_bind, _TCP _SOCKADDR _U32); -// DEFINE_PRIM(_I32, tcp_getsockname, _TCP _SOCKADDR _REF(_I32)); +DEFINE_PRIM(_I32, tcp_getsockname, _TCP _SOCKADDR _REF(_I32)); -// DEFINE_PRIM(_I32, tcp_getpeername, _TCP _SOCKADDR _REF(_I32)); +DEFINE_PRIM(_I32, tcp_getpeername, _TCP _SOCKADDR _REF(_I32)); -// HL_PRIM int HL_NAME(tcp_connect_with_cb)( uv_connect_t* req, uv_tcp_t* handle, const struct sockaddr* addr ) { -// return uv_tcp_connect(req, handle, addr, on_uv_connect_cb); -// } -// DEFINE_PRIM(_I32, tcp_connect_with_cb, _CONNECT _TCP _SOCKADDR); +HL_PRIM int HL_NAME(tcp_connect_with_cb)( uv_connect_t* req, uv_tcp_t* handle, const struct sockaddr* addr ) { + return uv_tcp_connect(req, handle, addr, on_uv_connect_cb); +} +DEFINE_PRIM(_I32, tcp_connect_with_cb, _CONNECT _TCP _SOCKADDR); -// HL_PRIM int HL_NAME(tcp_close_reset_with_cb)( uv_tcp_t* handle ) { -// return uv_tcp_close_reset(handle, on_uv_close_cb); -// } -// DEFINE_PRIM(_I32, tcp_close_reset_with_cb, _TCP); +HL_PRIM int HL_NAME(tcp_close_reset_with_cb)( uv_tcp_t* handle ) { + return uv_tcp_close_reset(handle, on_uv_close_cb); +} +DEFINE_PRIM(_I32, tcp_close_reset_with_cb, _TCP); -// DEFINE_PRIM(_I32, socketpair, _I32 _I32 _OS_SOCK_T _I32 _I32); +DEFINE_PRIM(_I32, socketpair, _I32 _I32 _REF(_OS_SOCK_T) _I32 _I32); DEFINE_PRIM(_I32, timer_init, _LOOP _TIMER); @@ -596,67 +596,67 @@ DEFINE_PRIM(_I64, timer_get_repeat, _TIMER); DEFINE_PRIM(_I64, timer_get_due_in, _TIMER); -// DEFINE_PRIM(_I32, tty_init, _LOOP _TTY _FILE _I32); +DEFINE_PRIM(_I32, tty_init, _LOOP _TTY _FILE _I32); -// DEFINE_PRIM(_I32, tty_set_mode, _TTY _TTY_MODE_T); +DEFINE_PRIM(_I32, tty_set_mode, _TTY _TTY_MODE_T); -// DEFINE_PRIM(_I32, tty_reset_mode, _NO_ARG); +DEFINE_PRIM(_I32, tty_reset_mode, _NO_ARG); -// DEFINE_PRIM(_I32, tty_get_winsize, _TTY _REF(_I32) _REF(_I32)); +DEFINE_PRIM(_I32, tty_get_winsize, _TTY _REF(_I32) _REF(_I32)); -// DEFINE_PRIM(_VOID, tty_set_vterm_state, _TTY_VTERMSTATE_T); +DEFINE_PRIM(_VOID, tty_set_vterm_state, _TTY_VTERMSTATE_T); -// DEFINE_PRIM(_I32, tty_get_vterm_state, _TTY_VTERMSTATE); +DEFINE_PRIM(_I32, tty_get_vterm_state, _TTY_VTERMSTATE); -// DEFINE_PRIM(_I32, udp_init, _LOOP _UDP); +DEFINE_PRIM(_I32, udp_init, _LOOP _UDP); -// DEFINE_PRIM(_I32, udp_init_ex, _LOOP _UDP _U32); +DEFINE_PRIM(_I32, udp_init_ex, _LOOP _UDP _U32); -// DEFINE_PRIM(_I32, udp_open, _UDP _OS_SOCK_T); +DEFINE_PRIM(_I32, udp_open, _UDP _OS_SOCK_T); -// DEFINE_PRIM(_I32, udp_bind, _UDP _SOCKADDR _U32); +DEFINE_PRIM(_I32, udp_bind, _UDP _SOCKADDR _U32); -// DEFINE_PRIM(_I32, udp_connect, _UDP _SOCKADDR); +DEFINE_PRIM(_I32, udp_connect, _UDP _SOCKADDR); -// DEFINE_PRIM(_I32, udp_getpeername, _UDP _SOCKADDR _REF(_I32)); +DEFINE_PRIM(_I32, udp_getpeername, _UDP _SOCKADDR _REF(_I32)); -// DEFINE_PRIM(_I32, udp_getsockname, _UDP _SOCKADDR _REF(_I32)); +DEFINE_PRIM(_I32, udp_getsockname, _UDP _SOCKADDR _REF(_I32)); -// DEFINE_PRIM(_I32, udp_set_membership, _UDP _BYTES _BYTES _MEMBERSHIP); +DEFINE_PRIM(_I32, udp_set_membership, _UDP _BYTES _BYTES _MEMBERSHIP); -// DEFINE_PRIM(_I32, udp_set_source_membership, _UDP _BYTES _BYTES _BYTES _MEMBERSHIP); +DEFINE_PRIM(_I32, udp_set_source_membership, _UDP _BYTES _BYTES _BYTES _MEMBERSHIP); -// DEFINE_PRIM(_I32, udp_set_multicast_loop, _UDP _I32); +DEFINE_PRIM(_I32, udp_set_multicast_loop, _UDP _I32); -// DEFINE_PRIM(_I32, udp_set_multicast_ttl, _UDP _I32); +DEFINE_PRIM(_I32, udp_set_multicast_ttl, _UDP _I32); -// DEFINE_PRIM(_I32, udp_set_multicast_interface, _UDP _BYTES); +DEFINE_PRIM(_I32, udp_set_multicast_interface, _UDP _BYTES); -// DEFINE_PRIM(_I32, udp_set_broadcast, _UDP _I32); +DEFINE_PRIM(_I32, udp_set_broadcast, _UDP _I32); -// DEFINE_PRIM(_I32, udp_set_ttl, _UDP _I32); +DEFINE_PRIM(_I32, udp_set_ttl, _UDP _I32); -// HL_PRIM int HL_NAME(udp_send_with_cb)( uv_udp_send_t* req, uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr ) { -// return uv_udp_send(req, handle, bufs[], nbufs, addr, on_uv_udp_send_cb); -// } -// DEFINE_PRIM(_I32, udp_send_with_cb, _UDP_SEND _UDP _BUF _U32 _SOCKADDR); +HL_PRIM int HL_NAME(udp_send_with_cb)( uv_udp_send_t* req, uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr ) { + return uv_udp_send(req, handle, bufs, nbufs, addr, on_uv_udp_send_cb); +} +DEFINE_PRIM(_I32, udp_send_with_cb, _UDP_SEND _UDP _REF(_BUF) _U32 _SOCKADDR); -// DEFINE_PRIM(_I32, udp_try_send, _UDP _BUF _U32 _SOCKADDR); +DEFINE_PRIM(_I32, udp_try_send, _UDP _REF(_BUF) _U32 _SOCKADDR); -// HL_PRIM int HL_NAME(udp_recv_start_with_cb)( uv_udp_t* handle ) { -// return uv_udp_recv_start(handle, on_uv_alloc_cb, on_uv_udp_recv_cb); -// } -// DEFINE_PRIM(_I32, udp_recv_start_with_cb, _UDP); +HL_PRIM int HL_NAME(udp_recv_start_with_cb)( uv_udp_t* handle ) { + return uv_udp_recv_start(handle, on_uv_alloc_cb, on_uv_udp_recv_cb); +} +DEFINE_PRIM(_I32, udp_recv_start_with_cb, _UDP); -// DEFINE_PRIM(_I32, udp_using_recvmmsg, _UDP); +DEFINE_PRIM(_I32, udp_using_recvmmsg, _UDP); -// DEFINE_PRIM(_I32, udp_recv_stop, _UDP); +DEFINE_PRIM(_I32, udp_recv_stop, _UDP); -// DEFINE_PRIM(_U64, udp_get_send_queue_size, _UDP); +DEFINE_PRIM(_U64, udp_get_send_queue_size, _UDP); -// DEFINE_PRIM(_U64, udp_get_send_queue_count, _UDP); +DEFINE_PRIM(_U64, udp_get_send_queue_count, _UDP); -// DEFINE_PRIM(_U32, version, _NO_ARG); +DEFINE_PRIM(_U32, version, _NO_ARG); -// DEFINE_PRIM(_BYTES, version_string, _NO_ARG); +DEFINE_PRIM(_BYTES, version_string, _NO_ARG); diff --git a/other/uvgenerator/UVGenerator.hx b/other/uvgenerator/UVGenerator.hx index 54895bc29..a555851c8 100644 --- a/other/uvgenerator/UVGenerator.hx +++ b/other/uvgenerator/UVGenerator.hx @@ -152,6 +152,13 @@ class UVGenerator { } } + static function mapHLArg(a:TypeAndName):String { + var type = mapHLType(a.type); + if(a.name.endsWith(']')) + type = '_REF($type)'; + return type; + } + static final reCapitalize = ~/_[a-z]/g; static function snakeToPascalCase(str:String):String { @@ -233,7 +240,7 @@ class UVGenerator { static function cBinding(sig:FunctionSignature):String { var args = switch sig.arguments { case [{type:'void'}]: '_NO_ARG'; - case _: sig.arguments.map(a -> mapHLType(a.type)).join(' '); + case _: sig.arguments.map(mapHLArg).join(' '); } return 'DEFINE_PRIM(${mapHLType(sig.returnType)}, ${functionName(sig.name)}, $args);\n'; } @@ -253,7 +260,11 @@ class UVGenerator { if(a.type.endsWith('_cb')) allowNoCallback.contains(a.type) ? 'use_${a.type}?$cbName:NULL' : cbName else - a.name; + if(a.name.endsWith(']')) { + var openPos = a.name.lastIndexOf('['); + a.name.substring(0, openPos); + } else + a.name; }) .join(', '); var ret = sig.returnType == 'void' ? '' : 'return '; @@ -262,7 +273,7 @@ class UVGenerator { var args = sig.arguments .filter(a -> !a.type.endsWith('_cb') || allowNoCallback.contains(a.type)) - .map(a -> a.type.endsWith('_cb') && allowNoCallback.contains(a.type) ? '_BOOL' : mapHLType(a.type)) + .map(a -> a.type.endsWith('_cb') && allowNoCallback.contains(a.type) ? '_BOOL' : mapHLArg(a)) .join(' '); lines.push('DEFINE_PRIM(${mapHLType(sig.returnType)}, $fnName, $args);'); From 9c61a144b3eb635ea89be23c24194883d8915b1d Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Thu, 19 Aug 2021 23:58:15 +0300 Subject: [PATCH 064/117] update generator --- libs/uv/uv.c | 9 ++++++ libs/uv/uv_generated.c | 22 ------------- other/uvgenerator/UV.hx.header | 54 +++++++++++++++++++++----------- other/uvgenerator/UVGenerator.hx | 46 +++++++++++++++++++++++++-- 4 files changed, 88 insertions(+), 43 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 2ec17c567..daea041c0 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -428,6 +428,15 @@ HL_PRIM int HL_NAME(translate_to_uv_error)( int hl_errno ) { } DEFINE_PRIM(_I32, translate_to_uv_error, _I32); +// Buf + +HL_PRIM void HL_NAME(check_bufs)( uv_buf_t bufs[], int length ) { + for(int i = 0; i < length; i++) { + printf("%s\n", bufs[i].base); + } +} +DEFINE_PRIM(_VOID, check_bufs, _REF(_BUF) _I32); + // Handle #define HANDLE_DATA_FIELDS \ diff --git a/libs/uv/uv_generated.c b/libs/uv/uv_generated.c index 59fc5cbe2..b7466b2f9 100644 --- a/libs/uv/uv_generated.c +++ b/libs/uv/uv_generated.c @@ -232,10 +232,6 @@ DEFINE_PRIM(_BYTES, fs_get_path, _FS); DEFINE_PRIM(_STAT, fs_get_statbuf, _FS); -DEFINE_PRIM(_OS_FD_T, get_osfhandle, _I32); - -DEFINE_PRIM(_I32, open_osfhandle, _OS_FD_T); - DEFINE_PRIM(_I32, fs_event_init, _LOOP _FS_EVENT); HL_PRIM int HL_NAME(fs_event_start_with_cb)( uv_fs_event_t* handle, const char* path, unsigned int flags ) { @@ -279,8 +275,6 @@ DEFINE_PRIM(_I32, send_buffer_size, _HANDLE _REF(_I32)); DEFINE_PRIM(_I32, recv_buffer_size, _HANDLE _REF(_I32)); -DEFINE_PRIM(_I32, fileno, _HANDLE _OS_FD); - DEFINE_PRIM(_LOOP, handle_get_loop, _HANDLE); DEFINE_PRIM(_POINTER, handle_get_data, _HANDLE); @@ -337,8 +331,6 @@ DEFINE_PRIM(_I64, metrics_idle_time, _LOOP); DEFINE_PRIM(_HANDLE_TYPE, guess_handle, _FILE); -DEFINE_PRIM(_I32, replace_allocator, _MALLOC_FUNC _REALLOC_FUNC _CALLOC_FUNC _FREE_FUNC); - DEFINE_PRIM(_VOID, library_shutdown, _NO_ARG); DEFINE_PRIM(_BUF, buf_init, _BYTES _U32); @@ -407,14 +399,6 @@ DEFINE_PRIM(_I64, get_constrained_memory, _NO_ARG); DEFINE_PRIM(_I64, hrtime, _NO_ARG); -DEFINE_PRIM(_VOID, print_all_handles, _LOOP _FILE); - -DEFINE_PRIM(_VOID, print_active_handles, _LOOP _FILE); - -DEFINE_PRIM(_I32, os_environ, _REF(_ENV_ITEM) _REF(_I32)); - -DEFINE_PRIM(_VOID, os_free_environ, _ENV_ITEM _I32); - DEFINE_PRIM(_I32, os_getenv, _BYTES _BYTES _REF(_I64)); DEFINE_PRIM(_I32, os_setenv, _BYTES _BYTES); @@ -553,8 +537,6 @@ DEFINE_PRIM(_I32, tcp_init, _LOOP _TCP); DEFINE_PRIM(_I32, tcp_init_ex, _LOOP _TCP _U32); -DEFINE_PRIM(_I32, tcp_open, _TCP _OS_SOCK_T); - DEFINE_PRIM(_I32, tcp_nodelay, _TCP _I32); DEFINE_PRIM(_I32, tcp_keepalive, _TCP _I32 _U32); @@ -577,8 +559,6 @@ HL_PRIM int HL_NAME(tcp_close_reset_with_cb)( uv_tcp_t* handle ) { } DEFINE_PRIM(_I32, tcp_close_reset_with_cb, _TCP); -DEFINE_PRIM(_I32, socketpair, _I32 _I32 _REF(_OS_SOCK_T) _I32 _I32); - DEFINE_PRIM(_I32, timer_init, _LOOP _TIMER); HL_PRIM int HL_NAME(timer_start_with_cb)( uv_timer_t* handle, uint64_t timeout, uint64_t repeat ) { @@ -612,8 +592,6 @@ DEFINE_PRIM(_I32, udp_init, _LOOP _UDP); DEFINE_PRIM(_I32, udp_init_ex, _LOOP _UDP _U32); -DEFINE_PRIM(_I32, udp_open, _UDP _OS_SOCK_T); - DEFINE_PRIM(_I32, udp_bind, _UDP _SOCKADDR _U32); DEFINE_PRIM(_I32, udp_connect, _UDP _SOCKADDR); diff --git a/other/uvgenerator/UV.hx.header b/other/uvgenerator/UV.hx.header index 8dc378b67..4b1235cad 100644 --- a/other/uvgenerator/UV.hx.header +++ b/other/uvgenerator/UV.hx.header @@ -22,24 +22,40 @@ package hl.uv; +import hl.uv.Request; +import hl.uv.Handle; import hl.uv.SockAddr; import hl.uv.Dns; +import hl.uv.Loop; +import hl.uv.File; +import hl.uv.Stream; +import hl.uv.Tty; +import hl.uv.Udp; +import hl.uv.Misc.RandomRequest; // This file is automatically generated by a tool in Hashlink repo. // see /other/uvgenerator // Contents of UV.hx.header -private abstract Addrinfo(Abstract<"struct_addrinfo">) {} - -typedef RawAddrInfo = Addrinfo; +abstract RawAddrInfo(Abstract<"struct_addrinfo">) {} +abstract RawDirent(Abstract<"uv_dirent">) {} +abstract RawStat(Abstract<"uv_stat">) {} +abstract RawStatFs(Abstract<"uv_statfs">) {} +abstract RawRUsage(Abstract<"uv_rusage">) {} +abstract RawCpuInfo(Abstract<"uv_cpuinfo">) {} +abstract RawInterfaceAddress(Abstract<"uv_interface_address">) {} +abstract RawSockAddrIn(Abstract<"struct_sockaddr_in">) {} +abstract RawSockAddrIn6(Abstract<"struct_sockaddr_in6">) {} +abstract RawPasswd(Abstract<"uv_passwd">) {} +abstract RawUtsName(Abstract<"uv_utsname">) {} +abstract RawTimeVal(Abstract<"uv_timeval">) {} +abstract RawTimeVal64(Abstract<"uv_timeval64">) {} +abstract RawProcessOptions(Abstract<"uv_process_options">) {} +//TODO: implement these private typedef UInt = Int; private typedef U64 = I64; -private typedef UvRunMode = Loop.LoopRunMode; -private typedef Sockaddr = SockAddr; -private typedef Getaddrinfo = AddrInfoRequest; -private typedef Getnameinfo = NameInfoRequest; /** Automatically generated bindings for libuv. @@ -60,22 +76,22 @@ extern class UV { static public function handle_data_of_pointer(ptr:Pointer):HandleData; static public function handle_data_to_pointer(data:HandleData):Pointer; static public function handle_set_data_with_gc(handle:Handle, data:HandleData):Void; - static public function req_data_of_pointer(ptr:Pointer):ReqData; - static public function req_data_to_pointer(data:ReqData):Pointer; - static public function req_set_data_with_gc(handle:Req, data:ReqData):Void; + static public function req_data_of_pointer(ptr:Pointer):RequestData; + static public function req_data_to_pointer(data:RequestData):Pointer; + static public function req_set_data_with_gc(req:Request, data:RequestData):Void; static public function alloc_loop():Loop; static public function alloc_async():Async; static public function alloc_timer():Timer; static public function alloc_check():Check; - static public function alloc_getaddrinfo():Getaddrinfo; - static public function alloc_getnameinfo():Getnameinfo; - static public function alloc_addrinfo(flags:Int, family:AddressFamily, socktype:SocketType, protocol:Int):Addrinfo; - static public function addrinfo_family(ai:Addrinfo):AddressFamily; - static public function addrinfo_socktype(ai:Addrinfo):SocketType; - static public function addrinfo_protocol(ai:Addrinfo):Int; - static public function addrinfo_addr(ai:Addrinfo):SockAddr; - static public function addrinfo_canonname(ai:Addrinfo):Bytes; - static public function addrinfo_next(ai:Addrinfo):Null; + static public function alloc_getaddrinfo():AddrInfoRequest; + static public function alloc_getnameinfo():NameInfoRequest; + static public function alloc_addrinfo(flags:Int, family:AddressFamily, socktype:SocketType, protocol:Int):RawAddrInfo; + static public function addrinfo_family(ai:RawAddrInfo):AddressFamily; + static public function addrinfo_socktype(ai:RawAddrInfo):SocketType; + static public function addrinfo_protocol(ai:RawAddrInfo):Int; + static public function addrinfo_addr(ai:RawAddrInfo):SockAddr; + static public function addrinfo_canonname(ai:RawAddrInfo):Bytes; + static public function addrinfo_next(ai:RawAddrInfo):Null; static public function nameinfo_flags_to_native(ai:NameInfoFlags):Int; // Auto generated diff --git a/other/uvgenerator/UVGenerator.hx b/other/uvgenerator/UVGenerator.hx index a555851c8..4c2c40077 100644 --- a/other/uvgenerator/UVGenerator.hx +++ b/other/uvgenerator/UVGenerator.hx @@ -28,7 +28,10 @@ class UVGenerator { static final skipDocs = ['api', 'dll', 'guide', 'index', 'migration_010_100', 'poll', 'threading', 'threadpool', 'upgrading']; - static final skipFunctions = ['uv_loop_configure']; // TODO: don't skip these + static final skipFunctions = ['uv_replace_allocator', 'uv_get_osfhandle', + 'uv_fileno', 'uv_open_osfhandle', 'uv_print_all_handles', 'uv_print_active_handles', + 'uv_os_environ', 'uv_os_free_environ', 'uv_tcp_open', 'uv_udp_open', 'uv_socketpair', + 'uv_loop_configure']; // TODO: don't skip uv_loop_configure static final allowNoCallback = ['uv_fs_cb']; static function main() { @@ -177,6 +180,43 @@ class UVGenerator { case 'uint64_t': 'U64'; case 'size_t': 'U64'; case 'ssize_t': 'I64'; + case 'uv_buf_t': 'Buffer'; + case 'uv_req_t*': 'Request'; + case 'uv_req_type': 'RequestType'; + case 'uv_handle_t*': 'Handle'; + case 'uv_handle_type': 'HandleType'; + case 'uv_file': 'File'; + case 'uv_run_mode': 'LoopRunMode'; + case 'sockaddr': 'SockAddr'; + case 'addrinfo': 'RawAddrInfo'; + case 'uv_getaddrinfo_t*': 'AddrInfoRequest'; + case 'uv_getnameinfo_t*': 'NameInfoRequest'; + case 'uv_fs_t*': 'FsRequest'; + case 'uv_dirent_t*': 'RawDirent'; + case 'uv_uid_t': 'Int'; + case 'uv_gid_t': 'Int'; + case 'uv_fs_type': 'FsRequestType'; + case 'uv_stat_t*': 'RawStat'; + case 'uv_statfs_t*': 'RawStatFs'; + case 'uv_rusage_t*': 'RawRUsage'; + case 'uv_cpu_info_t*': 'RawCpuInfo'; + case 'uv_pid_t': 'Int'; + case 'uv_interface_address_t*': 'RawInterfaceAddress'; + case 'sockaddr_in': 'RawSockAddrIn'; + case 'sockaddr_in6': 'RawSockAddrIn6'; + case 'uv_passwd_t*': 'RawPasswd'; + case 'uv_utsname_t*': 'RawUtsName'; + case 'uv_timeval_t*': 'RawTimeVal'; + case 'uv_timeval64_t*': 'RawTimeVal64'; + case 'uv_random_t*': 'RandomRequest'; + case 'uv_connect_t*': 'ConnectRequest'; + case 'uv_process_options_t*': 'RawProcessOptions'; + case 'uv_shutdown_t*': 'ShutdownRequest'; + case 'uv_write_t*': 'WriteRequest'; + case 'uv_tty_vtermstate_t': 'TtyVTermState'; + case 'uv_tty_vtermstate_t*': 'Ref'; + case 'uv_membership': 'UdpMembership'; + case 'uv_udp_send_t*': 'UdpSendRequest'; case _ if(type.startsWith('unsigned ')): 'U' + mapHXType(type.substr('unsigned '.length)); case _ if(type.startsWith('struct ') && type.endsWith('*')): @@ -221,7 +261,9 @@ class UVGenerator { var openPos = a.name.lastIndexOf('['); return '${a.name.substring(0, openPos)}:Ref<${mapHXType(a.type)}>'; } else { - return '${a.name}:${mapHXType(a.type)}'; + var type = mapHXType(a.type); + var name = a.name == '' ? type.toLowerCase() : a.name; + return '${name}:${type}'; } } if(needsCbWrapper(sig)) { From 00dae335b3437e2cab12885985f89df1af1309c6 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 20 Aug 2021 00:06:01 +0300 Subject: [PATCH 065/117] update types --- libs/uv/uv.c | 47 ++++++++++++++++++++++------------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index daea041c0..deb8579f9 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -50,6 +50,28 @@ #define _TTY _HANDLE #define _UDP _HANDLE #define _UDP_SEND _REQ +#define _STAT _ABSTRACT(uv_stat) +#define _ADDRINFO _ABSTRACT(struct_addrinfo) +#define _DIRENT _ABSTRACT(uv_dirent) +#define _STAT _ABSTRACT(uv_stat) +#define _STATFS _ABSTRACT(uv_statfs) +#define _RUSAGE _ABSTRACT(uv_rusage) +#define _CPU_INFO _ABSTRACT(uv_cpuinfo) +#define _INTERFACE_ADDRESS _ABSTRACT(uv_interface_address) +#define _SOCKADDR_IN _ABSTRACT(struct_sockaddr_in) +#define _SOCKADDR_IN6 _ABSTRACT(struct_sockaddr_in6) +#define _PASSWD _ABSTRACT(uv_passwd) +#define _UTSNAME _ABSTRACT(uv_utsname) +#define _TIMEVAL _ABSTRACT(uv_timeval) +#define _TIMEVAL64 _ABSTRACT(uv_timeval64) +#define _PROCESS_OPTIONS _ABSTRACT(uv_process_options) +#define _REQ_TYPE _I32 +#define _TTY_MODE_T _I32 +#define _TTY_VTERMSTATE_T _I32 +#define _TTY_VTERMSTATE _REF(_TTY_VTERMSTATE_T) +#define _MEMBERSHIP _I32 +#define _FS_TYPE _I32 +#define _FS_EVENT _HANDLE typedef struct sockaddr uv_sockaddr; typedef struct sockaddr_in uv_sockaddr_in; @@ -57,31 +79,6 @@ typedef struct sockaddr_in6 uv_sockaddr_in6; typedef struct sockaddr_storage uv_sockaddr_storage; // TODO { - #define _DIRENT _I32 - #define _FS_TYPE _I32 - #define _STAT _I32 - #define _OS_FD_T _I32 - #define _FS_EVENT _I32 - #define _MALLOC_FUNC _I32 - #define _REALLOC_FUNC _I32 - #define _CALLOC_FUNC _I32 - #define _FREE_FUNC _I32 - #define _RUSAGE _I32 - #define _INTERFACE_ADDRESS _I32 - #define _SOCKADDR_IN _I32 - #define _SOCKADDR_IN6 _I32 - #define _CPU_INFO _I32 - #define _PASSWD _I32 - #define _ENV_ITEM _I32 - #define _TIMEVAL64 _I32 - #define _PROCESS_OPTIONS _I32 - #define _REQ_TYPE _I32 - #define _OS_SOCK_T _I32 - #define _TTY_MODE_T _I32 - #define _TTY_VTERMSTATE_T _I32 - #define _TTY_VTERMSTATE _I32 - #define _MEMBERSHIP _I32 - #define _UTSNAME _I32 static void on_uv_fs_poll_cb( uv_fs_poll_t *h, int status, const uv_stat_t *prev, const uv_stat_t *curr ) { } From 9f830c3e91079d2c73455d4d79436c4d1953e9f3 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 20 Aug 2021 11:17:15 +0300 Subject: [PATCH 066/117] DEFINE_PRIM_STRUCT_FIELD --- libs/uv/uv.c | 35 +++++++++++++++------------------- other/uvgenerator/UV.hx.header | 12 ++++++------ 2 files changed, 21 insertions(+), 26 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index deb8579f9..da397c87c 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -135,6 +135,12 @@ typedef struct sockaddr_storage uv_sockaddr_storage; } \ DEFINE_PRIM(r, alloc_##t, _NO_ARG); +#define DEFINE_PRIM_STRUCT_FIELD(hl_return,c_return,hl_struct,c_struct,field) \ + HL_PRIM c_return HL_NAME(c_struct##_##field)( struct c_struct *s ) { \ + return (c_return)s->field; \ + } \ + DEFINE_PRIM(hl_return, c_struct##_##field, hl_struct); + #define UV_SET_DATA(h,new_data) \ if( h->data != new_data ) { \ if( h->data ) \ @@ -623,7 +629,7 @@ HL_PRIM struct addrinfo *HL_NAME(alloc_addrinfo)( int flags, int family, int soc } DEFINE_PRIM(_ADDRINFO, alloc_addrinfo, _I32 _I32 _I32 _I32); -HL_PRIM int HL_NAME(addrinfo_family)( struct addrinfo *ai ) { +HL_PRIM int HL_NAME(addrinfo_ai_family)( struct addrinfo *ai ) { switch( ai->ai_family ) { case PF_UNSPEC: return 0; case PF_INET: return -1; @@ -631,9 +637,9 @@ HL_PRIM int HL_NAME(addrinfo_family)( struct addrinfo *ai ) { default: return ai->ai_family; } } -DEFINE_PRIM(_I32, addrinfo_family, _ADDRINFO); +DEFINE_PRIM(_I32, addrinfo_ai_family, _ADDRINFO); -HL_PRIM int HL_NAME(addrinfo_socktype)( struct addrinfo *ai ) { +HL_PRIM int HL_NAME(addrinfo_ai_socktype)( struct addrinfo *ai ) { switch( ai->ai_socktype ) { case SOCK_STREAM: return -1; case SOCK_DGRAM: return -2; @@ -641,29 +647,18 @@ HL_PRIM int HL_NAME(addrinfo_socktype)( struct addrinfo *ai ) { default: return ai->ai_socktype; } } -DEFINE_PRIM(_I32, addrinfo_socktype, _ADDRINFO); - -HL_PRIM int HL_NAME(addrinfo_protocol)( struct addrinfo *ai ) { - return ai->ai_protocol; -} -DEFINE_PRIM(_I32, addrinfo_protocol, _ADDRINFO); +DEFINE_PRIM(_I32, addrinfo_ai_socktype, _ADDRINFO); -HL_PRIM uv_sockaddr_storage *HL_NAME(addrinfo_addr)( struct addrinfo *ai ) { +HL_PRIM uv_sockaddr_storage *HL_NAME(addrinfo_ai_addr)( struct addrinfo *ai ) { uv_sockaddr_storage *addr = UV_ALLOC(uv_sockaddr_storage); memcpy(addr, ai->ai_addr, ai->ai_addrlen); return addr; } -DEFINE_PRIM(_SOCKADDR, addrinfo_addr, _ADDRINFO); +DEFINE_PRIM(_SOCKADDR, addrinfo_ai_addr, _ADDRINFO); -HL_PRIM vbyte *HL_NAME(addrinfo_canonname)( struct addrinfo *ai ) { - return (vbyte *)ai->ai_canonname; -} -DEFINE_PRIM(_BYTES, addrinfo_canonname, _ADDRINFO); - -HL_PRIM struct addrinfo *HL_NAME(addrinfo_next)( struct addrinfo *ai ) { - return ai->ai_next; -} -DEFINE_PRIM(_ADDRINFO, addrinfo_next, _ADDRINFO); +DEFINE_PRIM_STRUCT_FIELD(_I32, int, _ADDRINFO, addrinfo, ai_protocol); +DEFINE_PRIM_STRUCT_FIELD(_BYTES, vbyte *, _ADDRINFO, addrinfo, ai_canonname); +DEFINE_PRIM_STRUCT_FIELD(_ADDRINFO, struct addrinfo *, _ADDRINFO, addrinfo, ai_next); static void on_uv_getaddrinfo_cb( uv_getaddrinfo_t *r, int status, struct addrinfo *res ) { vclosure *c = DATA(vreq_cb_data *,r)->callback; diff --git a/other/uvgenerator/UV.hx.header b/other/uvgenerator/UV.hx.header index 4b1235cad..7adcddcd0 100644 --- a/other/uvgenerator/UV.hx.header +++ b/other/uvgenerator/UV.hx.header @@ -86,12 +86,12 @@ extern class UV { static public function alloc_getaddrinfo():AddrInfoRequest; static public function alloc_getnameinfo():NameInfoRequest; static public function alloc_addrinfo(flags:Int, family:AddressFamily, socktype:SocketType, protocol:Int):RawAddrInfo; - static public function addrinfo_family(ai:RawAddrInfo):AddressFamily; - static public function addrinfo_socktype(ai:RawAddrInfo):SocketType; - static public function addrinfo_protocol(ai:RawAddrInfo):Int; - static public function addrinfo_addr(ai:RawAddrInfo):SockAddr; - static public function addrinfo_canonname(ai:RawAddrInfo):Bytes; - static public function addrinfo_next(ai:RawAddrInfo):Null; + static public function addrinfo_ai_family(ai:RawAddrInfo):AddressFamily; + static public function addrinfo_ai_socktype(ai:RawAddrInfo):SocketType; + static public function addrinfo_ai_protocol(ai:RawAddrInfo):Int; + static public function addrinfo_ai_addr(ai:RawAddrInfo):SockAddr; + static public function addrinfo_ai_canonname(ai:RawAddrInfo):Bytes; + static public function addrinfo_ai_next(ai:RawAddrInfo):Null; static public function nameinfo_flags_to_native(ai:NameInfoFlags):Int; // Auto generated From a25faea70ab7a01cc3ac634e79271d90133084cd Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 20 Aug 2021 18:55:23 +0300 Subject: [PATCH 067/117] refactoring & fixed "free" --- libs/uv/uv.c | 99 ++++++++++++++++++---------------- other/uvgenerator/UV.hx.header | 11 +++- 2 files changed, 62 insertions(+), 48 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index da397c87c..e93647d19 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -141,6 +141,18 @@ typedef struct sockaddr_storage uv_sockaddr_storage; } \ DEFINE_PRIM(hl_return, c_struct##_##field, hl_struct); +#define DEFINE_PRIM_TO_POINTER(hl_type,uv_name) \ + HL_PRIM void *HL_NAME(uv_name##_to_pointer)( uv_##uv_name##_t *v ) { \ + return v; \ + } \ + DEFINE_PRIM(_POINTER, uv_name##_to_pointer, hl_type); + +#define DEFINE_PRIM_OF_POINTER(hl_type,uv_name) \ + HL_PRIM uv_##uv_name##_t *HL_NAME(uv_name##_of_pointer)( void *ptr ) { \ + return ptr; \ + } \ + DEFINE_PRIM(hl_type, uv_name##_of_pointer, _POINTER); + #define UV_SET_DATA(h,new_data) \ if( h->data != new_data ) { \ if( h->data ) \ @@ -150,15 +162,15 @@ typedef struct sockaddr_storage uv_sockaddr_storage; h->data = new_data; \ } -HL_PRIM void HL_NAME(free)( vdynamic *v ) { - if( v ) { - if( v->v.ptr ) - free(v->v.ptr); - if( v->v.bytes ) - free(v->v.bytes); - } +HL_PRIM void HL_NAME(free)( void *ptr ) { + free(ptr); } -DEFINE_PRIM(_VOID, free, _DYN); +DEFINE_PRIM(_VOID, free, _POINTER); + +HL_PRIM void *HL_NAME(bytes_to_pointer)( vbyte *bytes ) { + return bytes; +} +DEFINE_PRIM(_POINTER, bytes_to_pointer, _BYTES); // Errors @@ -422,7 +434,7 @@ static int hl_uv_errno( int result ) { } HL_PRIM int HL_NAME(translate_uv_error)( int uv_errno ) { - return errno_uv2hl(uv_errno); + return uv_errno < 0 ? errno_uv2hl(uv_errno) : 0; } DEFINE_PRIM(_I32, translate_uv_error, _I32); @@ -448,33 +460,23 @@ DEFINE_PRIM(_VOID, check_bufs, _REF(_BUF) _I32); typedef struct { HANDLE_DATA_FIELDS; -} vhandle_data; +} uv_handle_data_t; #define _HANDLE_DATA _OBJ(_FUN(_VOID,_NO_ARG)) -HL_PRIM void *HL_NAME(handle_data_to_pointer)( vhandle_data *v ) { - return v; -} -DEFINE_PRIM(_POINTER, handle_data_to_pointer, _HANDLE_DATA); - -HL_PRIM vhandle_data *HL_NAME(handle_data_of_pointer)( void *v ) { - return v; -} -DEFINE_PRIM(_HANDLE_DATA, handle_data_of_pointer, _POINTER); +DEFINE_PRIM_TO_POINTER(_HANDLE, handle); +DEFINE_PRIM_OF_POINTER(_HANDLE, handle); +DEFINE_PRIM_TO_POINTER(_HANDLE_DATA, handle_data); +DEFINE_PRIM_OF_POINTER(_HANDLE_DATA, handle_data); -HL_PRIM void HL_NAME(handle_set_data_with_gc)( uv_handle_t *h, vhandle_data *new_data ) { +HL_PRIM void HL_NAME(handle_set_data_with_gc)( uv_handle_t *h, uv_handle_data_t *new_data ) { UV_SET_DATA(h, new_data); } DEFINE_PRIM(_VOID, handle_set_data_with_gc, _HANDLE _HANDLE_DATA); static void on_uv_close_cb( uv_handle_t *h ) { - vhandle_data *data = DATA(vhandle_data *, h); - if( data ) { - if( data->onClose ) - hl_call0(void, data->onClose); - hl_remove_root(data); - } - free(h); + uv_handle_data_t *data = DATA(uv_handle_data_t *, h); + hl_call0(void, data->onClose); } // Request @@ -484,26 +486,21 @@ static void on_uv_close_cb( uv_handle_t *h ) { typedef struct { REQ_DATA_FIELDS -} vreq_data; +} uv_req_data_t; typedef struct { REQ_DATA_FIELDS vclosure *callback; -} vreq_cb_data; +} uv_req_cb_data_t; #define _REQ_DATA _OBJ() -HL_PRIM void *HL_NAME(req_data_to_pointer)( vreq_data *v ) { - return v; -} -DEFINE_PRIM(_POINTER, req_data_to_pointer, _REQ_DATA); - -HL_PRIM vreq_data *HL_NAME(req_data_of_pointer)( void *v ) { - return v; -} -DEFINE_PRIM(_REQ_DATA, req_data_of_pointer, _POINTER); +DEFINE_PRIM_TO_POINTER(_REQ,req); +DEFINE_PRIM_OF_POINTER(_REQ,req); +DEFINE_PRIM_TO_POINTER(_REQ_DATA,req_data); +DEFINE_PRIM_OF_POINTER(_REQ_DATA,req_data); -HL_PRIM void HL_NAME(req_set_data_with_gc)( uv_req_t *r, vreq_data *new_data ) { +HL_PRIM void HL_NAME(req_set_data_with_gc)( uv_req_t *r, uv_req_data_t *new_data ) { UV_SET_DATA(r, new_data); } DEFINE_PRIM(_VOID, req_set_data_with_gc, _REQ _REQ_DATA); @@ -513,12 +510,12 @@ DEFINE_PRIM(_VOID, req_set_data_with_gc, _REQ _REQ_DATA); typedef struct { HANDLE_DATA_FIELDS; vclosure *onSend; -} vasync_data; +} uv_async_data_t; DEFINE_PRIM_ALLOC(_ASYNC, async); static void on_uv_async_cb( uv_async_t *h ) { - vclosure *c = DATA(vasync_data *, h)->onSend; + vclosure *c = DATA(uv_async_data_t *, h)->onSend; hl_call1(void, c, uv_async_t *, h); } @@ -527,12 +524,12 @@ static void on_uv_async_cb( uv_async_t *h ) { typedef struct { HANDLE_DATA_FIELDS; vclosure *onCheck; -} vcheck_data; +} uv_check_data_t; DEFINE_PRIM_ALLOC(_CHECK, check); static void on_uv_check_cb( uv_check_t *h ) { - vclosure *c = DATA(vcheck_data *, h)->onCheck; + vclosure *c = DATA(uv_check_data_t *, h)->onCheck; hl_call0(void, c); } @@ -661,21 +658,29 @@ DEFINE_PRIM_STRUCT_FIELD(_BYTES, vbyte *, _ADDRINFO, addrinfo, ai_canonname); DEFINE_PRIM_STRUCT_FIELD(_ADDRINFO, struct addrinfo *, _ADDRINFO, addrinfo, ai_next); static void on_uv_getaddrinfo_cb( uv_getaddrinfo_t *r, int status, struct addrinfo *res ) { - vclosure *c = DATA(vreq_cb_data *,r)->callback; + vclosure *c = DATA(uv_req_cb_data_t *,r)->callback; hl_call2(void,c,int,errno_uv2hl(status),struct addrinfo *,res); } static void on_uv_getnameinfo_cb( uv_getnameinfo_t *r, int status, const char *hostname, const char *service ) { - vclosure *c = DATA(vreq_cb_data *,r)->callback; + vclosure *c = DATA(uv_req_cb_data_t *,r)->callback; hl_call3(void,c,int,errno_uv2hl(status),const char *,hostname,const char *,service); } // File system +DEFINE_PRIM_ALLOC(_FS, fs); + static void on_uv_fs_cb( uv_fs_t *r ) { - vclosure *c = DATA(vreq_cb_data *,r)->callback; - hl_call1(void,c,uv_fs_t *,r); + vclosure *c = DATA(uv_req_cb_data_t *, r)->callback; + hl_call1(void, c, uv_fs_t *, r); +} + +HL_PRIM uv_dir_t *HL_NAME(pointer_to_dir)( void *ptr ) { + return ptr; } +DEFINE_PRIM(_DIR, pointer_to_dir, _POINTER); + // auto-generated libuv bindings #include "uv_generated.c" diff --git a/other/uvgenerator/UV.hx.header b/other/uvgenerator/UV.hx.header index 7adcddcd0..aeb17f18a 100644 --- a/other/uvgenerator/UV.hx.header +++ b/other/uvgenerator/UV.hx.header @@ -70,15 +70,22 @@ extern class UV { return result; } - static public function free(v:Dynamic):Void; + extern static public inline function toUTF8(s:String):Bytes { + return @:privateAccess s.toUtf8(); + } + + static public function free(ptr:Pointer):Void; + static public function bytes_to_pointer(bytes:Bytes):Pointer; static public function translate_uv_error(uvErrno:Int):UVError; static public function translate_to_uv_error(errno:Int):Int; static public function handle_data_of_pointer(ptr:Pointer):HandleData; static public function handle_data_to_pointer(data:HandleData):Pointer; + static public function handle_to_pointer(data:Handle):Pointer; static public function handle_set_data_with_gc(handle:Handle, data:HandleData):Void; static public function req_data_of_pointer(ptr:Pointer):RequestData; static public function req_data_to_pointer(data:RequestData):Pointer; static public function req_set_data_with_gc(req:Request, data:RequestData):Void; + static public function req_to_pointer(req:Request):Pointer; static public function alloc_loop():Loop; static public function alloc_async():Async; static public function alloc_timer():Timer; @@ -93,6 +100,8 @@ extern class UV { static public function addrinfo_ai_canonname(ai:RawAddrInfo):Bytes; static public function addrinfo_ai_next(ai:RawAddrInfo):Null; static public function nameinfo_flags_to_native(ai:NameInfoFlags):Int; + static public function alloc_fs():FsRequest; + static public function pointer_to_dir(req:Pointer):Dir; // Auto generated From ffd177234d57e94fa0e1ce504f0b72b5da2d3980 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 20 Aug 2021 20:24:49 +0300 Subject: [PATCH 068/117] dir --- libs/uv/uv.c | 38 ++++++++++++++++++++++++++-------- other/uvgenerator/UV.hx.header | 15 ++++++++++++-- other/uvsample/DirSample.hx | 29 ++++++++++++++++++++++++++ other/uvsample/FileSample.hx | 16 -------------- other/uvsample/UVSample.hx | 1 + 5 files changed, 72 insertions(+), 27 deletions(-) create mode 100644 other/uvsample/DirSample.hx diff --git a/libs/uv/uv.c b/libs/uv/uv.c index e93647d19..b140bcfd0 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -135,12 +135,18 @@ typedef struct sockaddr_storage uv_sockaddr_storage; } \ DEFINE_PRIM(r, alloc_##t, _NO_ARG); -#define DEFINE_PRIM_STRUCT_FIELD(hl_return,c_return,hl_struct,c_struct,field) \ +#define DEFINE_PRIM_C_FIELD(hl_return,c_return,hl_struct,c_struct,field) \ HL_PRIM c_return HL_NAME(c_struct##_##field)( struct c_struct *s ) { \ return (c_return)s->field; \ } \ DEFINE_PRIM(hl_return, c_struct##_##field, hl_struct); +#define DEFINE_PRIM_UV_FIELD(hl_return,c_return,hl_struct,uv_name,field) \ + HL_PRIM c_return HL_NAME(uv_name##_##field)( uv_##uv_name##_t *s ) { \ + return (c_return)s->field; \ + } \ + DEFINE_PRIM(hl_return, uv_name##_##field, hl_struct); + #define DEFINE_PRIM_TO_POINTER(hl_type,uv_name) \ HL_PRIM void *HL_NAME(uv_name##_to_pointer)( uv_##uv_name##_t *v ) { \ return v; \ @@ -148,10 +154,10 @@ typedef struct sockaddr_storage uv_sockaddr_storage; DEFINE_PRIM(_POINTER, uv_name##_to_pointer, hl_type); #define DEFINE_PRIM_OF_POINTER(hl_type,uv_name) \ - HL_PRIM uv_##uv_name##_t *HL_NAME(uv_name##_of_pointer)( void *ptr ) { \ + HL_PRIM uv_##uv_name##_t *HL_NAME(pointer_to_##uv_name)( void *ptr ) { \ return ptr; \ } \ - DEFINE_PRIM(hl_type, uv_name##_of_pointer, _POINTER); + DEFINE_PRIM(hl_type, pointer_to_##uv_name, _POINTER); #define UV_SET_DATA(h,new_data) \ if( h->data != new_data ) { \ @@ -653,9 +659,9 @@ HL_PRIM uv_sockaddr_storage *HL_NAME(addrinfo_ai_addr)( struct addrinfo *ai ) { } DEFINE_PRIM(_SOCKADDR, addrinfo_ai_addr, _ADDRINFO); -DEFINE_PRIM_STRUCT_FIELD(_I32, int, _ADDRINFO, addrinfo, ai_protocol); -DEFINE_PRIM_STRUCT_FIELD(_BYTES, vbyte *, _ADDRINFO, addrinfo, ai_canonname); -DEFINE_PRIM_STRUCT_FIELD(_ADDRINFO, struct addrinfo *, _ADDRINFO, addrinfo, ai_next); +DEFINE_PRIM_C_FIELD(_I32, int, _ADDRINFO, addrinfo, ai_protocol); +DEFINE_PRIM_C_FIELD(_BYTES, vbyte *, _ADDRINFO, addrinfo, ai_canonname); +DEFINE_PRIM_C_FIELD(_ADDRINFO, struct addrinfo *, _ADDRINFO, addrinfo, ai_next); static void on_uv_getaddrinfo_cb( uv_getaddrinfo_t *r, int status, struct addrinfo *res ) { vclosure *c = DATA(uv_req_cb_data_t *,r)->callback; @@ -676,10 +682,24 @@ static void on_uv_fs_cb( uv_fs_t *r ) { hl_call1(void, c, uv_fs_t *, r); } -HL_PRIM uv_dir_t *HL_NAME(pointer_to_dir)( void *ptr ) { - return ptr; +DEFINE_PRIM_OF_POINTER(_DIR,dir); + +HL_PRIM void HL_NAME(dir_init)( uv_dir_t *dir, int num_entries ) { + dir->nentries = num_entries; + dir->dirents = malloc(sizeof(uv_dirent_t) * num_entries); +} +DEFINE_PRIM(_VOID, dir_init, _DIR _I32); + +HL_PRIM uv_dirent_t *HL_NAME(dir_dirent)( uv_dir_t *dir, int index ) { + return &dir->dirents[index]; } -DEFINE_PRIM(_DIR, pointer_to_dir, _POINTER); +DEFINE_PRIM(_DIRENT, dir_dirent, _DIR _I32); + +DEFINE_PRIM_UV_FIELD(_I32, int, _DIR, dir, nentries); +DEFINE_PRIM_UV_FIELD(_BYTES, vbyte *, _DIRENT, dirent, name); +DEFINE_PRIM_UV_FIELD(_I32, int, _DIRENT, dirent, type); +DEFINE_PRIM_TO_POINTER(_DIRENT, dirent); + // auto-generated libuv bindings diff --git a/other/uvgenerator/UV.hx.header b/other/uvgenerator/UV.hx.header index aeb17f18a..8241b1700 100644 --- a/other/uvgenerator/UV.hx.header +++ b/other/uvgenerator/UV.hx.header @@ -32,6 +32,7 @@ import hl.uv.Stream; import hl.uv.Tty; import hl.uv.Udp; import hl.uv.Misc.RandomRequest; +import hl.uv.Dir; // This file is automatically generated by a tool in Hashlink repo. // see /other/uvgenerator @@ -74,15 +75,19 @@ extern class UV { return @:privateAccess s.toUtf8(); } + extern static public inline function fromUTF8(b:Bytes):String { + return @:privateAccess String.fromUTF8(b); + } + static public function free(ptr:Pointer):Void; static public function bytes_to_pointer(bytes:Bytes):Pointer; static public function translate_uv_error(uvErrno:Int):UVError; static public function translate_to_uv_error(errno:Int):Int; - static public function handle_data_of_pointer(ptr:Pointer):HandleData; + static public function pointer_to_handle_data(ptr:Pointer):HandleData; static public function handle_data_to_pointer(data:HandleData):Pointer; static public function handle_to_pointer(data:Handle):Pointer; static public function handle_set_data_with_gc(handle:Handle, data:HandleData):Void; - static public function req_data_of_pointer(ptr:Pointer):RequestData; + static public function pointer_to_req_data(ptr:Pointer):RequestData; static public function req_data_to_pointer(data:RequestData):Pointer; static public function req_set_data_with_gc(req:Request, data:RequestData):Void; static public function req_to_pointer(req:Request):Pointer; @@ -102,6 +107,12 @@ extern class UV { static public function nameinfo_flags_to_native(ai:NameInfoFlags):Int; static public function alloc_fs():FsRequest; static public function pointer_to_dir(req:Pointer):Dir; + static public function dir_init(dir:Dir, num_entries:Int):Void; + static public function dir_nentries(dir:Dir):Int; + static public function dir_dirent(dir:Dir, index:Int):RawDirent; + static public function dirent_to_pointer(dirent:RawDirent):Pointer; + static public function dirent_name(dirent:RawDirent):Bytes; + static public function dirent_type(dirent:RawDirent):DirEntryType; // Auto generated diff --git a/other/uvsample/DirSample.hx b/other/uvsample/DirSample.hx new file mode 100644 index 000000000..f62d9b6c7 --- /dev/null +++ b/other/uvsample/DirSample.hx @@ -0,0 +1,29 @@ +import hl.uv.Misc; +import hl.uv.Dir; +import hl.uv.UVException; +import sys.thread.Thread; + +class DirSample { + public static function main() { + var loop = Thread.current().events; + var path = Misc.tmpDir(); + inline function print(msg) + Log.print('DIR: $msg'); + Dir.open(loop, path, (e, dir) -> switch e { + case UV_NOERR: + dir.read(loop, 3, (e, entries) -> switch e { + case UV_NOERR: + for(i in 0...entries.length) + print('\t${entries[i]}'); + dir.close(loop, e -> switch e { + case UV_NOERR: print('Done'); + case _: throw new UVException(e); + }); + case _: + throw new UVException(e); + }); + case _: + throw new UVException(e); + }); + } +} \ No newline at end of file diff --git a/other/uvsample/FileSample.hx b/other/uvsample/FileSample.hx index d8774df99..86155b319 100644 --- a/other/uvsample/FileSample.hx +++ b/other/uvsample/FileSample.hx @@ -41,7 +41,6 @@ class FileSample { utime, linkSymlinkReadLinkRealPath, chown, - openReadCloseDir, ]; actions.next(); } @@ -350,19 +349,4 @@ class FileSample { })); }); } - - static function openReadCloseDir(actions:Actions) { - var path = Misc.tmpDir(); - Log.print('Reading 3 entries from dir $path...'); - Dir.open(loop, path, (e, dir) -> handle(() -> { - dir.read(loop, 3, (e, entries) -> handle(() -> { - for(i in 0...entries.length) - Log.print('\t${entries[i]}'); - dir.close(loop, handle(() -> { - Log.print('Done'); - actions.next(); - })); - })(e)); - })(e)); - } } \ No newline at end of file diff --git a/other/uvsample/UVSample.hx b/other/uvsample/UVSample.hx index 3fc27e37b..58b21d827 100644 --- a/other/uvsample/UVSample.hx +++ b/other/uvsample/UVSample.hx @@ -7,6 +7,7 @@ class UVSample { // PipeSample.main(); // ProcessSample.main(); // FileSample.main(); + DirSample.main(); // FsEventSample.main(); // FsPollSample.main(); // MiscSample.main(); From 1496974930bb2d6be50aa2d93613bff971e340dc Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Tue, 24 Aug 2021 21:28:45 +0300 Subject: [PATCH 069/117] generator refactoring --- libs/uv/uv.c | 53 ++++++------- other/uvgenerator/UV.hx.header | 129 +++++++++++++++++-------------- other/uvgenerator/UVGenerator.hx | 95 +++++++++++------------ other/uvsample/UVSample.hx | 4 +- 4 files changed, 146 insertions(+), 135 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index b140bcfd0..9375ec234 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -17,16 +17,16 @@ #define _U32 _I32 #define _POINTER _ABSTRACT(void_pointer) -#define _HANDLE _ABSTRACT(uv_handle) -#define _REQ _ABSTRACT(uv_req) -#define _LOOP _ABSTRACT(uv_loop) +#define _HANDLE _ABSTRACT(uv_handle_t_star) +#define _REQ _ABSTRACT(uv_req_t_star) +#define _LOOP _ABSTRACT(uv_loop_t_star) #define _ASYNC _HANDLE #define _CHECK _HANDLE #define _TIMER _HANDLE -#define _SOCKADDR _ABSTRACT(uv_sockaddr_storage) +#define _SOCKADDR _ABSTRACT(sockaddr_star) #define _GETADDRINFO _REQ #define _GETNAMEINFO _REQ -#define _ADDRINFO _ABSTRACT(struct_addrinfo) +#define _ADDRINFO _ABSTRACT(addrinfo_star) #define _HANDLE_TYPE _I32 #define _OS_FD _ABSTRACT(uv_os_fd) #define _FS _REQ @@ -34,7 +34,7 @@ #define _GID_T _I32 #define _FILE _I32 #define _BUF _ABSTRACT(uv_buf) -#define _DIR _ABSTRACT(uv_dir) +#define _DIR _ABSTRACT(uv_dir_t_star) #define _FS_POLL _HANDLE #define _IDLE _HANDLE #define _RANDOM _REQ @@ -50,21 +50,19 @@ #define _TTY _HANDLE #define _UDP _HANDLE #define _UDP_SEND _REQ -#define _STAT _ABSTRACT(uv_stat) -#define _ADDRINFO _ABSTRACT(struct_addrinfo) -#define _DIRENT _ABSTRACT(uv_dirent) -#define _STAT _ABSTRACT(uv_stat) -#define _STATFS _ABSTRACT(uv_statfs) -#define _RUSAGE _ABSTRACT(uv_rusage) -#define _CPU_INFO _ABSTRACT(uv_cpuinfo) -#define _INTERFACE_ADDRESS _ABSTRACT(uv_interface_address) +#define _DIRENT _ABSTRACT(uv_dirent_t_star) +#define _STAT _ABSTRACT(uv_stat_t_star) +#define _STATFS _ABSTRACT(uv_statfs_t_star) +#define _RUSAGE _ABSTRACT(uv_rusage_t_star) +#define _CPU_INFO _ABSTRACT(uv_cpuinfo_t_star) +#define _INTERFACE_ADDRESS _ABSTRACT(uv_interface_address_t_star) #define _SOCKADDR_IN _ABSTRACT(struct_sockaddr_in) #define _SOCKADDR_IN6 _ABSTRACT(struct_sockaddr_in6) -#define _PASSWD _ABSTRACT(uv_passwd) -#define _UTSNAME _ABSTRACT(uv_utsname) -#define _TIMEVAL _ABSTRACT(uv_timeval) -#define _TIMEVAL64 _ABSTRACT(uv_timeval64) -#define _PROCESS_OPTIONS _ABSTRACT(uv_process_options) +#define _PASSWD _ABSTRACT(uv_passwd_t_star) +#define _UTSNAME _ABSTRACT(uv_utsname_t_star) +#define _TIMEVAL _ABSTRACT(uv_timeval_t_star) +#define _TIMEVAL64 _ABSTRACT(uv_timeval64_t_star) +#define _PROCESS_OPTIONS _ABSTRACT(uv_process_options_t_star) #define _REQ_TYPE _I32 #define _TTY_MODE_T _I32 #define _TTY_VTERMSTATE_T _I32 @@ -462,13 +460,14 @@ DEFINE_PRIM(_VOID, check_bufs, _REF(_BUF) _I32); #define HANDLE_DATA_FIELDS \ hl_type *t; \ + uv_handle_t *_h; \ vclosure *onClose; typedef struct { HANDLE_DATA_FIELDS; } uv_handle_data_t; -#define _HANDLE_DATA _OBJ(_FUN(_VOID,_NO_ARG)) +#define _HANDLE_DATA _OBJ(_HANDLE _FUN(_VOID,_NO_ARG)) DEFINE_PRIM_TO_POINTER(_HANDLE, handle); DEFINE_PRIM_OF_POINTER(_HANDLE, handle); @@ -488,7 +487,8 @@ static void on_uv_close_cb( uv_handle_t *h ) { // Request #define REQ_DATA_FIELDS \ - hl_type *t; + hl_type *t; \ + uv_req_t *r; typedef struct { REQ_DATA_FIELDS @@ -499,7 +499,7 @@ typedef struct { vclosure *callback; } uv_req_cb_data_t; -#define _REQ_DATA _OBJ() +#define _REQ_DATA _OBJ(_REQ) DEFINE_PRIM_TO_POINTER(_REQ,req); DEFINE_PRIM_OF_POINTER(_REQ,req); @@ -509,7 +509,7 @@ DEFINE_PRIM_OF_POINTER(_REQ_DATA,req_data); HL_PRIM void HL_NAME(req_set_data_with_gc)( uv_req_t *r, uv_req_data_t *new_data ) { UV_SET_DATA(r, new_data); } -DEFINE_PRIM(_VOID, req_set_data_with_gc, _REQ _REQ_DATA); +DEFINE_PRIM(_VOID, req_set_data_with_gc, _REQ _OBJ()); // Async @@ -550,7 +550,7 @@ DEFINE_PRIM_ALLOC(_TIMER, timer); static void on_uv_timer_cb( uv_timer_t *h ) { vclosure *c = DATA(vtimer_data *, h)->onTick; - hl_call1(void, c, uv_timer_t *, h); + hl_call0(void, c); } // Loop @@ -558,6 +558,7 @@ static void on_uv_timer_cb( uv_timer_t *h ) { #define _RUN_MODE _I32 DEFINE_PRIM_ALLOC(_LOOP, loop); +DEFINE_PRIM_TO_POINTER(_LOOP, loop); // DNS @@ -665,12 +666,12 @@ DEFINE_PRIM_C_FIELD(_ADDRINFO, struct addrinfo *, _ADDRINFO, addrinfo, ai_next); static void on_uv_getaddrinfo_cb( uv_getaddrinfo_t *r, int status, struct addrinfo *res ) { vclosure *c = DATA(uv_req_cb_data_t *,r)->callback; - hl_call2(void,c,int,errno_uv2hl(status),struct addrinfo *,res); + hl_call2(void,c,int,status,struct addrinfo *,res); } static void on_uv_getnameinfo_cb( uv_getnameinfo_t *r, int status, const char *hostname, const char *service ) { vclosure *c = DATA(uv_req_cb_data_t *,r)->callback; - hl_call3(void,c,int,errno_uv2hl(status),const char *,hostname,const char *,service); + hl_call3(void,c,int,status,const char *,hostname,const char *,service); } // File system diff --git a/other/uvgenerator/UV.hx.header b/other/uvgenerator/UV.hx.header index 8241b1700..9b75dba0c 100644 --- a/other/uvgenerator/UV.hx.header +++ b/other/uvgenerator/UV.hx.header @@ -20,39 +20,50 @@ * DEALINGS IN THE SOFTWARE. */ +// This file is automatically generated by a tool in Hashlink repo. +// see /other/uvgenerator + +// Contents of /other/uvgenerator/UV.hx.header : + package hl.uv; -import hl.uv.Request; -import hl.uv.Handle; import hl.uv.SockAddr; import hl.uv.Dns; +import hl.uv.Dir; import hl.uv.Loop; import hl.uv.File; -import hl.uv.Stream; -import hl.uv.Tty; -import hl.uv.Udp; -import hl.uv.Misc.RandomRequest; -import hl.uv.Dir; -// This file is automatically generated by a tool in Hashlink repo. -// see /other/uvgenerator +typedef UvUidT = Int; +typedef UvGidT = Int; +typedef UvRunMode = LoopRunMode; +typedef UvFsType = FsRequestType; -// Contents of UV.hx.header - -abstract RawAddrInfo(Abstract<"struct_addrinfo">) {} -abstract RawDirent(Abstract<"uv_dirent">) {} -abstract RawStat(Abstract<"uv_stat">) {} -abstract RawStatFs(Abstract<"uv_statfs">) {} -abstract RawRUsage(Abstract<"uv_rusage">) {} -abstract RawCpuInfo(Abstract<"uv_cpuinfo">) {} -abstract RawInterfaceAddress(Abstract<"uv_interface_address">) {} -abstract RawSockAddrIn(Abstract<"struct_sockaddr_in">) {} -abstract RawSockAddrIn6(Abstract<"struct_sockaddr_in6">) {} -abstract RawPasswd(Abstract<"uv_passwd">) {} -abstract RawUtsName(Abstract<"uv_utsname">) {} -abstract RawTimeVal(Abstract<"uv_timeval">) {} -abstract RawTimeVal64(Abstract<"uv_timeval64">) {} -abstract RawProcessOptions(Abstract<"uv_process_options">) {} +abstract UvFile(Int) {} + +abstract RefUvUdpSendT(RefUvReqT) to RefUvReqT {} +abstract RefUvWriteT(RefUvReqT) to RefUvReqT {} +abstract RefUvShutdownT(RefUvReqT) to RefUvReqT {} +abstract RefUvRandomT(RefUvReqT) to RefUvReqT {} +abstract RefUvGetnameinfoT(RefUvReqT) to RefUvReqT {} +abstract RefUvGetaddrinfoT(RefUvReqT) to RefUvReqT {} +abstract RefUvFsT(RefUvReqT) to RefUvReqT {} +abstract RefUvConnectT(RefUvReqT) to RefUvReqT {} + +abstract RefUvUdpT(RefUvHandleT) to RefUvHandleT {} +abstract RefUvTtyT(RefUvHandleT) to RefUvHandleT {} +abstract RefUvTimerT(RefUvHandleT) to RefUvHandleT {} +abstract RefUvTcpT(RefUvHandleT) to RefUvHandleT {} +abstract RefUvStreamT(RefUvHandleT) to RefUvHandleT {} +abstract RefUvSignalT(RefUvHandleT) to RefUvHandleT {} +abstract RefUvRusageT(RefUvHandleT) to RefUvHandleT {} +abstract RefUvProcessT(RefUvHandleT) to RefUvHandleT {} +abstract RefUvPrepareT(RefUvHandleT) to RefUvHandleT {} +abstract RefUvPipeT(RefUvHandleT) to RefUvHandleT {} +abstract RefUvIdleT(RefUvHandleT) to RefUvHandleT {} +abstract RefUvFsPollT(RefUvHandleT) to RefUvHandleT {} +abstract RefUvFsEventT(RefUvHandleT) to RefUvHandleT {} +abstract RefUvCheckT(RefUvHandleT) to RefUvHandleT {} +abstract RefUvAsyncT(RefUvHandleT) to RefUvHandleT {} //TODO: implement these private typedef UInt = Int; @@ -71,6 +82,15 @@ extern class UV { return result; } + extern static public inline function throwErr(result:Int):Int { + throw new UVException(translate_uv_error(result)); + } + + extern static public inline function checkLoop(loop:Loop):Void { + if(loop == null) + throw new UVException(UV_EINVAL); + } + extern static public inline function toUTF8(s:String):Bytes { return @:privateAccess s.toUtf8(); } @@ -83,36 +103,33 @@ extern class UV { static public function bytes_to_pointer(bytes:Bytes):Pointer; static public function translate_uv_error(uvErrno:Int):UVError; static public function translate_to_uv_error(errno:Int):Int; - static public function pointer_to_handle_data(ptr:Pointer):HandleData; - static public function handle_data_to_pointer(data:HandleData):Pointer; - static public function handle_to_pointer(data:Handle):Pointer; - static public function handle_set_data_with_gc(handle:Handle, data:HandleData):Void; - static public function pointer_to_req_data(ptr:Pointer):RequestData; - static public function req_data_to_pointer(data:RequestData):Pointer; - static public function req_set_data_with_gc(req:Request, data:RequestData):Void; - static public function req_to_pointer(req:Request):Pointer; - static public function alloc_loop():Loop; - static public function alloc_async():Async; - static public function alloc_timer():Timer; - static public function alloc_check():Check; - static public function alloc_getaddrinfo():AddrInfoRequest; - static public function alloc_getnameinfo():NameInfoRequest; - static public function alloc_addrinfo(flags:Int, family:AddressFamily, socktype:SocketType, protocol:Int):RawAddrInfo; - static public function addrinfo_ai_family(ai:RawAddrInfo):AddressFamily; - static public function addrinfo_ai_socktype(ai:RawAddrInfo):SocketType; - static public function addrinfo_ai_protocol(ai:RawAddrInfo):Int; - static public function addrinfo_ai_addr(ai:RawAddrInfo):SockAddr; - static public function addrinfo_ai_canonname(ai:RawAddrInfo):Bytes; - static public function addrinfo_ai_next(ai:RawAddrInfo):Null; + static public function handle_to_pointer(data:RefUvHandleT):Pointer; + static public function handle_set_data_with_gc(handle:RefUvHandleT, data:Handle):Void; + static public function req_set_data_with_gc(req:RefUvReqT, data:Request):Void; + static public function req_to_pointer(req:RefUvReqT):Pointer; + static public function alloc_loop():RefUvLoopT; + static public function loop_to_pointer(req:RefUvLoopT):Pointer; + static public function alloc_async():RefUvAsyncT; + static public function alloc_timer():RefUvTimerT; + static public function alloc_check():RefUvCheckT; + static public function alloc_getaddrinfo():RefUvGetaddrinfoT; + static public function alloc_getnameinfo():RefUvGetnameinfoT; + static public function alloc_addrinfo(flags:Int, family:AddressFamily, socktype:SocketType, protocol:Int):RefCAddrinfo; + static public function addrinfo_ai_family(ai:RefCAddrinfo):AddressFamily; + static public function addrinfo_ai_socktype(ai:RefCAddrinfo):SocketType; + static public function addrinfo_ai_protocol(ai:RefCAddrinfo):Int; + static public function addrinfo_ai_addr(ai:RefCAddrinfo):SockAddr; + static public function addrinfo_ai_canonname(ai:RefCAddrinfo):Bytes; + static public function addrinfo_ai_next(ai:RefCAddrinfo):Null; static public function nameinfo_flags_to_native(ai:NameInfoFlags):Int; - static public function alloc_fs():FsRequest; - static public function pointer_to_dir(req:Pointer):Dir; - static public function dir_init(dir:Dir, num_entries:Int):Void; - static public function dir_nentries(dir:Dir):Int; - static public function dir_dirent(dir:Dir, index:Int):RawDirent; - static public function dirent_to_pointer(dirent:RawDirent):Pointer; - static public function dirent_name(dirent:RawDirent):Bytes; - static public function dirent_type(dirent:RawDirent):DirEntryType; - -// Auto generated + static public function alloc_fs():RefUvFsT; + static public function pointer_to_dir(req:Pointer):RefUvDirT; + static public function dir_init(dir:RefUvDirT, num_entries:Int):Void; + static public function dir_nentries(dir:RefUvDirT):Int; + static public function dir_dirent(dir:RefUvDirT, index:Int):RefUvDirentT; + static public function dirent_to_pointer(dirent:RefUvDirentT):Pointer; + static public function dirent_name(dirent:RefUvDirentT):Bytes; + static public function dirent_type(dirent:RefUvDirentT):DirEntryType; + +// Auto generated content : diff --git a/other/uvgenerator/UVGenerator.hx b/other/uvgenerator/UVGenerator.hx index 4c2c40077..55bb5e002 100644 --- a/other/uvgenerator/UVGenerator.hx +++ b/other/uvgenerator/UVGenerator.hx @@ -34,6 +34,9 @@ class UVGenerator { 'uv_loop_configure']; // TODO: don't skip uv_loop_configure static final allowNoCallback = ['uv_fs_cb']; + static final predefinedHxTypes = new Map(); + static final hxTypesToGenerate = new Map(); + static function main() { var root = rootDir(); var docsDir = Path.join([root, DOCS]); @@ -41,7 +44,9 @@ class UVGenerator { var cFile = File.write(Path.join([root, UV_GENERATED_C])); var hxFile = File.write(Path.join([root, UV_HX])); - hxFile.writeString(File.getContent(Path.join([root, UV_HX_HEADER]))); + var hxHeader = File.getContent(Path.join([root, UV_HX_HEADER])); + collectPredefinedHxTypesToMap(hxHeader, predefinedHxTypes); + hxFile.writeString(hxHeader); var entry = null; while(null != (entry = scan.next())) { @@ -73,13 +78,33 @@ class UVGenerator { cFile.writeString('\n'); } } - hxFile.writeString('}\n'); + hxFile.writeString('}\n\n'); + + hxFile.writeString(generateHXTypes(hxTypesToGenerate)); cFile.close(); hxFile.close(); scan.end(); } + static function collectPredefinedHxTypesToMap(hxCode:String, map:Map) { + var re = ~/(abstract|typedef|class)\s+([^ (<]+)/; + var pos = 0; + while(re.matchSub(hxCode, pos)) { + map.set(re.matched(2), re.matched(1)); + var p = re.matchedPos(); + pos = p.pos + p.len; + } + } + + static function generateHXTypes(types:Map):String { + var lines = []; + for(hx => c in types) { + lines.push('abstract $hx(Abstract<"$c">) {}'); + } + return lines.join('\n'); + } + static function needsCbWrapper(sig:FunctionSignature):Bool { for(a in sig.arguments) if(a.type.endsWith('_cb')) { @@ -168,70 +193,38 @@ class UVGenerator { return str.charAt(0).toUpperCase() + reCapitalize.map(str.substr(1), r -> r.matched(0).replace('_', '').toUpperCase()); } - static function mapHXType(type:String):String { + static function mapHXType(type:String, isRef:Bool = false):String { if(type.startsWith('const ')) type = type.substr('const '.length); return switch type { case 'void*': 'Pointer'; + case 'void': 'Void'; + case 'int': 'Int'; + case 'int*': 'Ref'; case 'char*': 'Bytes'; case 'double': 'Float'; + case 'double*': 'Ref'; case 'int64_t': 'I64'; + case 'int64_t*': 'Ref'; case 'uint64_t': 'U64'; + case 'uint64_t*': 'Ref'; case 'size_t': 'U64'; + case 'size_t*': 'Ref'; case 'ssize_t': 'I64'; - case 'uv_buf_t': 'Buffer'; - case 'uv_req_t*': 'Request'; - case 'uv_req_type': 'RequestType'; - case 'uv_handle_t*': 'Handle'; - case 'uv_handle_type': 'HandleType'; - case 'uv_file': 'File'; - case 'uv_run_mode': 'LoopRunMode'; - case 'sockaddr': 'SockAddr'; - case 'addrinfo': 'RawAddrInfo'; - case 'uv_getaddrinfo_t*': 'AddrInfoRequest'; - case 'uv_getnameinfo_t*': 'NameInfoRequest'; - case 'uv_fs_t*': 'FsRequest'; - case 'uv_dirent_t*': 'RawDirent'; - case 'uv_uid_t': 'Int'; - case 'uv_gid_t': 'Int'; - case 'uv_fs_type': 'FsRequestType'; - case 'uv_stat_t*': 'RawStat'; - case 'uv_statfs_t*': 'RawStatFs'; - case 'uv_rusage_t*': 'RawRUsage'; - case 'uv_cpu_info_t*': 'RawCpuInfo'; - case 'uv_pid_t': 'Int'; - case 'uv_interface_address_t*': 'RawInterfaceAddress'; - case 'sockaddr_in': 'RawSockAddrIn'; - case 'sockaddr_in6': 'RawSockAddrIn6'; - case 'uv_passwd_t*': 'RawPasswd'; - case 'uv_utsname_t*': 'RawUtsName'; - case 'uv_timeval_t*': 'RawTimeVal'; - case 'uv_timeval64_t*': 'RawTimeVal64'; - case 'uv_random_t*': 'RandomRequest'; - case 'uv_connect_t*': 'ConnectRequest'; - case 'uv_process_options_t*': 'RawProcessOptions'; - case 'uv_shutdown_t*': 'ShutdownRequest'; - case 'uv_write_t*': 'WriteRequest'; - case 'uv_tty_vtermstate_t': 'TtyVTermState'; - case 'uv_tty_vtermstate_t*': 'Ref'; - case 'uv_membership': 'UdpMembership'; - case 'uv_udp_send_t*': 'UdpSendRequest'; + case 'ssize_t*': 'Ref'; case _ if(type.startsWith('unsigned ')): 'U' + mapHXType(type.substr('unsigned '.length)); - case _ if(type.startsWith('struct ') && type.endsWith('*')): - mapHXType(type.substring('struct '.length, type.length - 1)); - case _ if(type.startsWith('uv_') && type.endsWith('_t*')): - type = type.substr(3, type.length - 3 - 3); - snakeToPascalCase(type); - case _ if(type.startsWith('uv_') && type.endsWith('_t')): - type = type.substr(3, type.length - 3 - 2); - snakeToPascalCase(type); + case _ if(type.startsWith('struct ')): + mapHXType(type.substr('struct '.length)); case _ if(type.endsWith('*')): - var hxType = mapHXType(type.substr(0, type.length - 1)); - 'Ref<$hxType>'; + mapHXType(type.substr(0, type.length - 1), true); case _: - snakeToPascalCase(type); + var hxType = snakeToPascalCase(type); + var finalName = (isRef ? 'Ref' : '') + (type.startsWith('uv_') ? hxType : 'C$hxType'); + if(!predefinedHxTypes.exists(finalName)) + hxTypesToGenerate.set(finalName, type + (isRef ? '_star' : '')); + finalName; } } diff --git a/other/uvsample/UVSample.hx b/other/uvsample/UVSample.hx index 58b21d827..55d629e69 100644 --- a/other/uvsample/UVSample.hx +++ b/other/uvsample/UVSample.hx @@ -2,12 +2,12 @@ class UVSample { static function main() { CheckSample.main(); // TcpSample.main(); - DnsSample.main(); + // DnsSample.main(); // UdpSample.main(); // PipeSample.main(); // ProcessSample.main(); // FileSample.main(); - DirSample.main(); + // DirSample.main(); // FsEventSample.main(); // FsPollSample.main(); // MiscSample.main(); From 3ee8c9cd7c43c3e975bcabd35d9ac3942e1e243e Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Tue, 24 Aug 2021 22:11:39 +0300 Subject: [PATCH 070/117] compilable again --- libs/uv/uv.c | 10 +-- libs/uv/uv_generated.c | 6 -- other/uvgenerator/UV.hx.header | 110 +++++++++++++++++-------------- other/uvgenerator/UVGenerator.hx | 45 ++++++------- other/uvsample/UVSample.hx | 4 +- 5 files changed, 90 insertions(+), 85 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 9375ec234..1545a6084 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -33,7 +33,7 @@ #define _UID_T _I32 #define _GID_T _I32 #define _FILE _I32 -#define _BUF _ABSTRACT(uv_buf) +#define _BUF _ABSTRACT(uv_buf_t) #define _DIR _ABSTRACT(uv_dir_t_star) #define _FS_POLL _HANDLE #define _IDLE _HANDLE @@ -54,10 +54,10 @@ #define _STAT _ABSTRACT(uv_stat_t_star) #define _STATFS _ABSTRACT(uv_statfs_t_star) #define _RUSAGE _ABSTRACT(uv_rusage_t_star) -#define _CPU_INFO _ABSTRACT(uv_cpuinfo_t_star) +#define _CPU_INFO _ABSTRACT(uv_cpu_info_t_star) #define _INTERFACE_ADDRESS _ABSTRACT(uv_interface_address_t_star) -#define _SOCKADDR_IN _ABSTRACT(struct_sockaddr_in) -#define _SOCKADDR_IN6 _ABSTRACT(struct_sockaddr_in6) +#define _SOCKADDR_IN _ABSTRACT(sockaddr_in_star) +#define _SOCKADDR_IN6 _ABSTRACT(sockaddr_in6_star) #define _PASSWD _ABSTRACT(uv_passwd_t_star) #define _UTSNAME _ABSTRACT(uv_utsname_t_star) #define _TIMEVAL _ABSTRACT(uv_timeval_t_star) @@ -509,7 +509,7 @@ DEFINE_PRIM_OF_POINTER(_REQ_DATA,req_data); HL_PRIM void HL_NAME(req_set_data_with_gc)( uv_req_t *r, uv_req_data_t *new_data ) { UV_SET_DATA(r, new_data); } -DEFINE_PRIM(_VOID, req_set_data_with_gc, _REQ _OBJ()); +DEFINE_PRIM(_VOID, req_set_data_with_gc, _REQ _REQ_DATA); // Async diff --git a/libs/uv/uv_generated.c b/libs/uv/uv_generated.c index b7466b2f9..d65ced005 100644 --- a/libs/uv/uv_generated.c +++ b/libs/uv/uv_generated.c @@ -335,12 +335,6 @@ DEFINE_PRIM(_VOID, library_shutdown, _NO_ARG); DEFINE_PRIM(_BUF, buf_init, _BYTES _U32); -DEFINE_PRIM(_REF(_BYTES), setup_args, _I32 _REF(_BYTES)); - -DEFINE_PRIM(_I32, get_process_title, _BYTES _U64); - -DEFINE_PRIM(_I32, set_process_title, _BYTES); - DEFINE_PRIM(_I32, resident_set_memory, _REF(_I64)); DEFINE_PRIM(_I32, uptime, _REF(_F64)); diff --git a/other/uvgenerator/UV.hx.header b/other/uvgenerator/UV.hx.header index 9b75dba0c..2c2600274 100644 --- a/other/uvgenerator/UV.hx.header +++ b/other/uvgenerator/UV.hx.header @@ -27,43 +27,53 @@ package hl.uv; +import hl.uv.Handle; +import hl.uv.Request; import hl.uv.SockAddr; import hl.uv.Dns; import hl.uv.Dir; import hl.uv.Loop; import hl.uv.File; +import hl.uv.Tty; +import hl.uv.Udp; typedef UvUidT = Int; typedef UvGidT = Int; +typedef UvPidT = Int; +typedef UvHandleType = HandleType; +typedef UvReqType = RequestType; typedef UvRunMode = LoopRunMode; typedef UvFsType = FsRequestType; +typedef UvTtyModeT = TtyMode; +typedef UvTtyVtermstateT = TtyVTermState; +typedef UvTtyVtermstateTStar = Ref; +typedef UvMembership = UdpMembership; abstract UvFile(Int) {} -abstract RefUvUdpSendT(RefUvReqT) to RefUvReqT {} -abstract RefUvWriteT(RefUvReqT) to RefUvReqT {} -abstract RefUvShutdownT(RefUvReqT) to RefUvReqT {} -abstract RefUvRandomT(RefUvReqT) to RefUvReqT {} -abstract RefUvGetnameinfoT(RefUvReqT) to RefUvReqT {} -abstract RefUvGetaddrinfoT(RefUvReqT) to RefUvReqT {} -abstract RefUvFsT(RefUvReqT) to RefUvReqT {} -abstract RefUvConnectT(RefUvReqT) to RefUvReqT {} - -abstract RefUvUdpT(RefUvHandleT) to RefUvHandleT {} -abstract RefUvTtyT(RefUvHandleT) to RefUvHandleT {} -abstract RefUvTimerT(RefUvHandleT) to RefUvHandleT {} -abstract RefUvTcpT(RefUvHandleT) to RefUvHandleT {} -abstract RefUvStreamT(RefUvHandleT) to RefUvHandleT {} -abstract RefUvSignalT(RefUvHandleT) to RefUvHandleT {} -abstract RefUvRusageT(RefUvHandleT) to RefUvHandleT {} -abstract RefUvProcessT(RefUvHandleT) to RefUvHandleT {} -abstract RefUvPrepareT(RefUvHandleT) to RefUvHandleT {} -abstract RefUvPipeT(RefUvHandleT) to RefUvHandleT {} -abstract RefUvIdleT(RefUvHandleT) to RefUvHandleT {} -abstract RefUvFsPollT(RefUvHandleT) to RefUvHandleT {} -abstract RefUvFsEventT(RefUvHandleT) to RefUvHandleT {} -abstract RefUvCheckT(RefUvHandleT) to RefUvHandleT {} -abstract RefUvAsyncT(RefUvHandleT) to RefUvHandleT {} +abstract UvUdpSendTStar(UvReqTStar) to UvReqTStar {} +abstract UvWriteTStar(UvReqTStar) to UvReqTStar {} +abstract UvShutdownTStar(UvReqTStar) to UvReqTStar {} +abstract UvRandomTStar(UvReqTStar) to UvReqTStar {} +abstract UvGetnameinfoTStar(UvReqTStar) to UvReqTStar {} +abstract UvGetaddrinfoTStar(UvReqTStar) to UvReqTStar {} +abstract UvFsTStar(UvReqTStar) to UvReqTStar {} +abstract UvConnectTStar(UvReqTStar) to UvReqTStar {} + +abstract UvUdpTStar(UvHandleTStar) to UvHandleTStar {} +abstract UvTtyTStar(UvHandleTStar) to UvHandleTStar {} +abstract UvTimerTStar(UvHandleTStar) to UvHandleTStar {} +abstract UvTcpTStar(UvHandleTStar) to UvHandleTStar {} +abstract UvStreamTStar(UvHandleTStar) to UvHandleTStar {} +abstract UvSignalTStar(UvHandleTStar) to UvHandleTStar {} +abstract UvProcessTStar(UvHandleTStar) to UvHandleTStar {} +abstract UvPrepareTStar(UvHandleTStar) to UvHandleTStar {} +abstract UvPipeTStar(UvHandleTStar) to UvHandleTStar {} +abstract UvIdleTStar(UvHandleTStar) to UvHandleTStar {} +abstract UvFsPollTStar(UvHandleTStar) to UvHandleTStar {} +abstract UvFsEventTStar(UvHandleTStar) to UvHandleTStar {} +abstract UvCheckTStar(UvHandleTStar) to UvHandleTStar {} +abstract UvAsyncTStar(UvHandleTStar) to UvHandleTStar {} //TODO: implement these private typedef UInt = Int; @@ -103,33 +113,33 @@ extern class UV { static public function bytes_to_pointer(bytes:Bytes):Pointer; static public function translate_uv_error(uvErrno:Int):UVError; static public function translate_to_uv_error(errno:Int):Int; - static public function handle_to_pointer(data:RefUvHandleT):Pointer; - static public function handle_set_data_with_gc(handle:RefUvHandleT, data:Handle):Void; - static public function req_set_data_with_gc(req:RefUvReqT, data:Request):Void; - static public function req_to_pointer(req:RefUvReqT):Pointer; - static public function alloc_loop():RefUvLoopT; - static public function loop_to_pointer(req:RefUvLoopT):Pointer; - static public function alloc_async():RefUvAsyncT; - static public function alloc_timer():RefUvTimerT; - static public function alloc_check():RefUvCheckT; - static public function alloc_getaddrinfo():RefUvGetaddrinfoT; - static public function alloc_getnameinfo():RefUvGetnameinfoT; - static public function alloc_addrinfo(flags:Int, family:AddressFamily, socktype:SocketType, protocol:Int):RefCAddrinfo; - static public function addrinfo_ai_family(ai:RefCAddrinfo):AddressFamily; - static public function addrinfo_ai_socktype(ai:RefCAddrinfo):SocketType; - static public function addrinfo_ai_protocol(ai:RefCAddrinfo):Int; - static public function addrinfo_ai_addr(ai:RefCAddrinfo):SockAddr; - static public function addrinfo_ai_canonname(ai:RefCAddrinfo):Bytes; - static public function addrinfo_ai_next(ai:RefCAddrinfo):Null; + static public function handle_to_pointer(data:UvHandleTStar):Pointer; + static public function handle_set_data_with_gc(handle:UvHandleTStar, data:Handle):Void; + static public function req_set_data_with_gc(req:UvReqTStar, data:Request):Void; + static public function req_to_pointer(req:UvReqTStar):Pointer; + static public function alloc_loop():UvLoopTStar; + static public function loop_to_pointer(req:UvLoopTStar):Pointer; + static public function alloc_async():UvAsyncTStar; + static public function alloc_timer():UvTimerTStar; + static public function alloc_check():UvCheckTStar; + static public function alloc_getaddrinfo():UvGetaddrinfoTStar; + static public function alloc_getnameinfo():UvGetnameinfoTStar; + static public function alloc_addrinfo(flags:Int, family:AddressFamily, socktype:SocketType, protocol:Int):CAddrinfoStar; + static public function addrinfo_ai_family(ai:CAddrinfoStar):AddressFamily; + static public function addrinfo_ai_socktype(ai:CAddrinfoStar):SocketType; + static public function addrinfo_ai_protocol(ai:CAddrinfoStar):Int; + static public function addrinfo_ai_addr(ai:CAddrinfoStar):SockAddr; + static public function addrinfo_ai_canonname(ai:CAddrinfoStar):Bytes; + static public function addrinfo_ai_next(ai:CAddrinfoStar):Null; static public function nameinfo_flags_to_native(ai:NameInfoFlags):Int; - static public function alloc_fs():RefUvFsT; - static public function pointer_to_dir(req:Pointer):RefUvDirT; - static public function dir_init(dir:RefUvDirT, num_entries:Int):Void; - static public function dir_nentries(dir:RefUvDirT):Int; - static public function dir_dirent(dir:RefUvDirT, index:Int):RefUvDirentT; - static public function dirent_to_pointer(dirent:RefUvDirentT):Pointer; - static public function dirent_name(dirent:RefUvDirentT):Bytes; - static public function dirent_type(dirent:RefUvDirentT):DirEntryType; + static public function alloc_fs():UvFsTStar; + static public function pointer_to_dir(req:Pointer):UvDirTStar; + static public function dir_init(dir:UvDirTStar, num_entries:Int):Void; + static public function dir_nentries(dir:UvDirTStar):Int; + static public function dir_dirent(dir:UvDirTStar, index:Int):UvDirentTStar; + static public function dirent_to_pointer(dirent:UvDirentTStar):Pointer; + static public function dirent_name(dirent:UvDirentTStar):Bytes; + static public function dirent_type(dirent:UvDirentTStar):DirEntryType; // Auto generated content : diff --git a/other/uvgenerator/UVGenerator.hx b/other/uvgenerator/UVGenerator.hx index 55bb5e002..5a20b905b 100644 --- a/other/uvgenerator/UVGenerator.hx +++ b/other/uvgenerator/UVGenerator.hx @@ -30,7 +30,8 @@ class UVGenerator { 'poll', 'threading', 'threadpool', 'upgrading']; static final skipFunctions = ['uv_replace_allocator', 'uv_get_osfhandle', 'uv_fileno', 'uv_open_osfhandle', 'uv_print_all_handles', 'uv_print_active_handles', - 'uv_os_environ', 'uv_os_free_environ', 'uv_tcp_open', 'uv_udp_open', 'uv_socketpair', + 'uv_os_environ', 'uv_os_free_environ', 'uv_setup_args', 'uv_get_process_title', + 'uv_set_process_title', 'uv_tcp_open', 'uv_udp_open', 'uv_socketpair', 'uv_loop_configure']; // TODO: don't skip uv_loop_configure static final allowNoCallback = ['uv_fs_cb']; @@ -193,35 +194,35 @@ class UVGenerator { return str.charAt(0).toUpperCase() + reCapitalize.map(str.substr(1), r -> r.matched(0).replace('_', '').toUpperCase()); } - static function mapHXType(type:String, isRef:Bool = false):String { + static function mapHXType(type:String):String { if(type.startsWith('const ')) type = type.substr('const '.length); + var isRef = type.endsWith('*'); + if(isRef) + type = type.substr(0, type.length - 1); + + inline function handleRef(t:String) + return isRef ? 'Ref<$t>' : t; + return switch type { - case 'void*': 'Pointer'; - case 'void': 'Void'; - case 'int': 'Int'; - case 'int*': 'Ref'; - case 'char*': 'Bytes'; - case 'double': 'Float'; - case 'double*': 'Ref'; - case 'int64_t': 'I64'; - case 'int64_t*': 'Ref'; - case 'uint64_t': 'U64'; - case 'uint64_t*': 'Ref'; - case 'size_t': 'U64'; - case 'size_t*': 'Ref'; - case 'ssize_t': 'I64'; - case 'ssize_t*': 'Ref'; + case 'void': isRef ? 'Pointer' : 'Void'; + case 'char': isRef ? 'Bytes' : 'Int'; + case 'int': handleRef('Int'); + case 'double': handleRef('Float'); + case 'int64_t': handleRef('I64'); + case 'uint64_t': handleRef('U64'); + case 'size_t': handleRef('U64'); + case 'ssize_t': handleRef('I64'); case _ if(type.startsWith('unsigned ')): - 'U' + mapHXType(type.substr('unsigned '.length)); - case _ if(type.startsWith('struct ')): - mapHXType(type.substr('struct '.length)); + handleRef('U' + mapHXType(type.substr('unsigned '.length))); case _ if(type.endsWith('*')): - mapHXType(type.substr(0, type.length - 1), true); + handleRef(mapHXType(type)); case _: + if(type.startsWith('struct ')) + type = type.substr('struct '.length); var hxType = snakeToPascalCase(type); - var finalName = (isRef ? 'Ref' : '') + (type.startsWith('uv_') ? hxType : 'C$hxType'); + var finalName = (type.startsWith('uv_') ? hxType : 'C$hxType') + (isRef ? 'Star' : ''); if(!predefinedHxTypes.exists(finalName)) hxTypesToGenerate.set(finalName, type + (isRef ? '_star' : '')); finalName; diff --git a/other/uvsample/UVSample.hx b/other/uvsample/UVSample.hx index 55d629e69..58b21d827 100644 --- a/other/uvsample/UVSample.hx +++ b/other/uvsample/UVSample.hx @@ -2,12 +2,12 @@ class UVSample { static function main() { CheckSample.main(); // TcpSample.main(); - // DnsSample.main(); + DnsSample.main(); // UdpSample.main(); // PipeSample.main(); // ProcessSample.main(); // FileSample.main(); - // DirSample.main(); + DirSample.main(); // FsEventSample.main(); // FsPollSample.main(); // MiscSample.main(); From ed1dd5c3287a68696034d0e365704e4181b80b2e Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Wed, 25 Aug 2021 21:14:11 +0300 Subject: [PATCH 071/117] update generator --- libs/uv/uv_generated.c | 16 ++++++++-------- other/uvgenerator/UV.hx.header | 28 ++++++++++++++++++++++++---- other/uvgenerator/UVGenerator.hx | 13 ++++++++++--- 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/libs/uv/uv_generated.c b/libs/uv/uv_generated.c index d65ced005..a6014dd1f 100644 --- a/libs/uv/uv_generated.c +++ b/libs/uv/uv_generated.c @@ -51,7 +51,7 @@ DEFINE_PRIM(_I32, fs_open_with_cb, _LOOP _FS _BYTES _I32 _I32 _BOOL); HL_PRIM int HL_NAME(fs_read_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, bool use_uv_fs_cb ) { return uv_fs_read(loop, req, file, bufs, nbufs, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); } -DEFINE_PRIM(_I32, fs_read_with_cb, _LOOP _FS _FILE _REF(_BUF) _U32 _I64 _BOOL); +DEFINE_PRIM(_I32, fs_read_with_cb, _LOOP _FS _FILE _BUF_ARR _U32 _I64 _BOOL); HL_PRIM int HL_NAME(fs_unlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { return uv_fs_unlink(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); @@ -61,7 +61,7 @@ DEFINE_PRIM(_I32, fs_unlink_with_cb, _LOOP _FS _BYTES _BOOL); HL_PRIM int HL_NAME(fs_write_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, bool use_uv_fs_cb ) { return uv_fs_write(loop, req, file, bufs, nbufs, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); } -DEFINE_PRIM(_I32, fs_write_with_cb, _LOOP _FS _FILE _REF(_BUF) _U32 _I64 _BOOL); +DEFINE_PRIM(_I32, fs_write_with_cb, _LOOP _FS _FILE _BUF_ARR _U32 _I64 _BOOL); HL_PRIM int HL_NAME(fs_mkdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { return uv_fs_mkdir(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); @@ -508,16 +508,16 @@ DEFINE_PRIM(_I32, read_stop, _STREAM); HL_PRIM int HL_NAME(write_with_cb)( uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs ) { return uv_write(req, handle, bufs, nbufs, on_uv_write_cb); } -DEFINE_PRIM(_I32, write_with_cb, _WRITE _STREAM _REF(_BUF) _U32); +DEFINE_PRIM(_I32, write_with_cb, _WRITE _STREAM _BUF_ARR _U32); HL_PRIM int HL_NAME(write2_with_cb)( uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_stream_t* send_handle ) { return uv_write2(req, handle, bufs, nbufs, send_handle, on_uv_write_cb); } -DEFINE_PRIM(_I32, write2_with_cb, _WRITE _STREAM _REF(_BUF) _U32 _STREAM); +DEFINE_PRIM(_I32, write2_with_cb, _WRITE _STREAM _BUF_ARR _U32 _STREAM); -DEFINE_PRIM(_I32, try_write, _STREAM _REF(_BUF) _U32); +DEFINE_PRIM(_I32, try_write, _STREAM _BUF_ARR _U32); -DEFINE_PRIM(_I32, try_write2, _STREAM _REF(_BUF) _U32 _STREAM); +DEFINE_PRIM(_I32, try_write2, _STREAM _BUF_ARR _U32 _STREAM); DEFINE_PRIM(_I32, is_readable, _STREAM); @@ -611,9 +611,9 @@ DEFINE_PRIM(_I32, udp_set_ttl, _UDP _I32); HL_PRIM int HL_NAME(udp_send_with_cb)( uv_udp_send_t* req, uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr ) { return uv_udp_send(req, handle, bufs, nbufs, addr, on_uv_udp_send_cb); } -DEFINE_PRIM(_I32, udp_send_with_cb, _UDP_SEND _UDP _REF(_BUF) _U32 _SOCKADDR); +DEFINE_PRIM(_I32, udp_send_with_cb, _UDP_SEND _UDP _BUF_ARR _U32 _SOCKADDR); -DEFINE_PRIM(_I32, udp_try_send, _UDP _REF(_BUF) _U32 _SOCKADDR); +DEFINE_PRIM(_I32, udp_try_send, _UDP _BUF_ARR _U32 _SOCKADDR); HL_PRIM int HL_NAME(udp_recv_start_with_cb)( uv_udp_t* handle ) { return uv_udp_recv_start(handle, on_uv_alloc_cb, on_uv_udp_recv_cb); diff --git a/other/uvgenerator/UV.hx.header b/other/uvgenerator/UV.hx.header index 2c2600274..aa80e1045 100644 --- a/other/uvgenerator/UV.hx.header +++ b/other/uvgenerator/UV.hx.header @@ -61,20 +61,23 @@ abstract UvFsTStar(UvReqTStar) to UvReqTStar {} abstract UvConnectTStar(UvReqTStar) to UvReqTStar {} abstract UvUdpTStar(UvHandleTStar) to UvHandleTStar {} -abstract UvTtyTStar(UvHandleTStar) to UvHandleTStar {} -abstract UvTimerTStar(UvHandleTStar) to UvHandleTStar {} -abstract UvTcpTStar(UvHandleTStar) to UvHandleTStar {} abstract UvStreamTStar(UvHandleTStar) to UvHandleTStar {} +abstract UvTtyTStar(UvStreamTStar) to UvStreamTStar to UvHandleTStar {} +abstract UvTcpTStar(UvStreamTStar) to UvStreamTStar to UvHandleTStar {} +abstract UvPipeTStar(UvStreamTStar) to UvStreamTStar to UvHandleTStar {} +abstract UvTimerTStar(UvHandleTStar) to UvHandleTStar {} abstract UvSignalTStar(UvHandleTStar) to UvHandleTStar {} abstract UvProcessTStar(UvHandleTStar) to UvHandleTStar {} abstract UvPrepareTStar(UvHandleTStar) to UvHandleTStar {} -abstract UvPipeTStar(UvHandleTStar) to UvHandleTStar {} abstract UvIdleTStar(UvHandleTStar) to UvHandleTStar {} abstract UvFsPollTStar(UvHandleTStar) to UvHandleTStar {} abstract UvFsEventTStar(UvHandleTStar) to UvHandleTStar {} abstract UvCheckTStar(UvHandleTStar) to UvHandleTStar {} abstract UvAsyncTStar(UvHandleTStar) to UvHandleTStar {} +abstract UvBufTArr(Abstract<"uv_buf_t_arr">) {} +abstract CSockaddrStorageStar(Abstract<"sockaddr_storage_star">) {} + //TODO: implement these private typedef UInt = Int; private typedef U64 = I64; @@ -122,9 +125,22 @@ extern class UV { static public function alloc_async():UvAsyncTStar; static public function alloc_timer():UvTimerTStar; static public function alloc_check():UvCheckTStar; + static public function alloc_tcp():UvTcpTStar; + static public function alloc_sockaddr_storage():CSockaddrStorageStar; + static public function sockaddr_storage_size():Int; + static public function sockaddr_storage_to_pointer(addr:CSockaddrStorageStar):Pointer; + static public function sockaddr_of_storage(addr:CSockaddrStorageStar):CSockaddrStar; + // static public function alloc_udp():UvUdpTStar; + // static public function alloc_pipe():UvPipeTStar; + // static public function alloc_tty():UvTtyTStar; static public function alloc_getaddrinfo():UvGetaddrinfoTStar; static public function alloc_getnameinfo():UvGetnameinfoTStar; static public function alloc_addrinfo(flags:Int, family:AddressFamily, socktype:SocketType, protocol:Int):CAddrinfoStar; + static public function alloc_shutdown():UvShutdownTStar; + static public function alloc_write():UvWriteTStar; + static public function alloc_connect():UvConnectTStar; + static public function address_family_to_af(family:AddressFamily):Int; + static public function address_family_to_pf(family:AddressFamily):Int; static public function addrinfo_ai_family(ai:CAddrinfoStar):AddressFamily; static public function addrinfo_ai_socktype(ai:CAddrinfoStar):SocketType; static public function addrinfo_ai_protocol(ai:CAddrinfoStar):Int; @@ -140,6 +156,10 @@ extern class UV { static public function dirent_to_pointer(dirent:UvDirentTStar):Pointer; static public function dirent_name(dirent:UvDirentTStar):Bytes; static public function dirent_type(dirent:UvDirentTStar):DirEntryType; + static public function buf_to_pointer(buf:UvBufTArr):Pointer; + static public function alloc_buf(bytes:Bytes, bytesLength:Int):UvBufTArr; + static public function buf_base(buf:UvBufTArr):Bytes; + static public function buf_len(buf:UvBufTArr):U64; // Auto generated content : diff --git a/other/uvgenerator/UVGenerator.hx b/other/uvgenerator/UVGenerator.hx index 5a20b905b..8e0263d80 100644 --- a/other/uvgenerator/UVGenerator.hx +++ b/other/uvgenerator/UVGenerator.hx @@ -181,10 +181,15 @@ class UVGenerator { } } + static function isUvBuf(cType:String):Bool { + return cType == 'uv_buf_t' || cType == 'const uv_buf_t'; + } + static function mapHLArg(a:TypeAndName):String { var type = mapHLType(a.type); - if(a.name.endsWith(']')) - type = '_REF($type)'; + if(a.name.endsWith(']')) { + type = isUvBuf(a.type) ? '${type}_ARR' : '_REF($type)'; + } return type; } @@ -253,7 +258,9 @@ class UVGenerator { function mapArg(a:TypeAndName):String { if(a.name.endsWith(']')) { var openPos = a.name.lastIndexOf('['); - return '${a.name.substring(0, openPos)}:Ref<${mapHXType(a.type)}>'; + var hxType = mapHXType(a.type); + hxType = isUvBuf(a.type) ? hxType + 'Arr' : 'Ref<$hxType>'; + return '${a.name.substring(0, openPos)}:$hxType'; } else { var type = mapHXType(a.type); var name = a.name == '' ? type.toLowerCase() : a.name; From 6df3472918745d2f7bff784bea063211d95b4a5e Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Wed, 25 Aug 2021 21:16:46 +0300 Subject: [PATCH 072/117] Stream; Tcp --- libs/uv/uv.c | 206 +++++++++++++++++++++++++++---------- other/uvsample/UVSample.hx | 2 +- src/std/bytes.c | 3 +- 3 files changed, 152 insertions(+), 59 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 1545a6084..b991fcd24 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -24,6 +24,9 @@ #define _CHECK _HANDLE #define _TIMER _HANDLE #define _SOCKADDR _ABSTRACT(sockaddr_star) +#define _SOCKADDR_IN _ABSTRACT(sockaddr_in_star) +#define _SOCKADDR_IN6 _ABSTRACT(sockaddr_in6_star) +#define _SOCKADDR_STORAGE _ABSTRACT(sockaddr_storage_star) #define _GETADDRINFO _REQ #define _GETNAMEINFO _REQ #define _ADDRINFO _ABSTRACT(addrinfo_star) @@ -34,6 +37,7 @@ #define _GID_T _I32 #define _FILE _I32 #define _BUF _ABSTRACT(uv_buf_t) +#define _BUF_ARR _ABSTRACT(uv_buf_t_arr) #define _DIR _ABSTRACT(uv_dir_t_star) #define _FS_POLL _HANDLE #define _IDLE _HANDLE @@ -56,8 +60,6 @@ #define _RUSAGE _ABSTRACT(uv_rusage_t_star) #define _CPU_INFO _ABSTRACT(uv_cpu_info_t_star) #define _INTERFACE_ADDRESS _ABSTRACT(uv_interface_address_t_star) -#define _SOCKADDR_IN _ABSTRACT(sockaddr_in_star) -#define _SOCKADDR_IN6 _ABSTRACT(sockaddr_in6_star) #define _PASSWD _ABSTRACT(uv_passwd_t_star) #define _UTSNAME _ABSTRACT(uv_utsname_t_star) #define _TIMEVAL _ABSTRACT(uv_timeval_t_star) @@ -93,30 +95,12 @@ typedef struct sockaddr_storage uv_sockaddr_storage; static void on_uv_random_cb( uv_random_t* r, int status, void* buf, size_t buflen ) { } - static void on_uv_connect_cb( uv_connect_t *r, int status ) { - } - static void on_uv_prepare_cb( uv_prepare_t *h ) { } static void on_uv_signal_cb( uv_signal_t *h, int signum ) { } - static void on_uv_shutdown_cb( uv_shutdown_t *r, int status ) { - } - - static void on_uv_connection_cb( uv_stream_t *h, int status ) { - } - - static void on_uv_alloc_cb( uv_handle_t* h, size_t size, uv_buf_t *buf ) { - } - - static void on_uv_write_cb( uv_write_t *r, int status ) { - } - - static void on_uv_read_cb( uv_stream_t *h, ssize_t nread, const uv_buf_t *buf ) { - } - static void on_uv_udp_send_cb( uv_udp_send_t *r, int status ) { } @@ -127,11 +111,11 @@ typedef struct sockaddr_storage uv_sockaddr_storage; #define UV_ALLOC(t) ((t*)malloc(sizeof(t))) #define DATA(t,h) ((t)h->data) -#define DEFINE_PRIM_ALLOC(r,t) \ - HL_PRIM uv_##t##_t *HL_NAME(alloc_##t)() { \ - return UV_ALLOC(uv_##t##_t); \ +#define DEFINE_PRIM_ALLOC(hl_type,uv_name) \ + HL_PRIM uv_##uv_name##_t *HL_NAME(alloc_##uv_name)() { \ + return UV_ALLOC(uv_##uv_name##_t); \ } \ - DEFINE_PRIM(r, alloc_##t, _NO_ARG); + DEFINE_PRIM(hl_type, alloc_##uv_name, _NO_ARG); #define DEFINE_PRIM_C_FIELD(hl_return,c_return,hl_struct,c_struct,field) \ HL_PRIM c_return HL_NAME(c_struct##_##field)( struct c_struct *s ) { \ @@ -449,12 +433,39 @@ DEFINE_PRIM(_I32, translate_to_uv_error, _I32); // Buf -HL_PRIM void HL_NAME(check_bufs)( uv_buf_t bufs[], int length ) { - for(int i = 0; i < length; i++) { - printf("%s\n", bufs[i].base); - } +// HL_PRIM void HL_NAME(check_bufs)( const uv_buf_t bufs[], int length ) { +// for(int i = 0; i < length; i++) { +// printf("len: %ld; base: %s\n", bufs[i].len, bufs[i].base); +// } +// } +// DEFINE_PRIM(_VOID, check_bufs, _REF(_BUF) _I32); + +// HL_PRIM uv_buf_t HL_NAME(buf_init_wrap)( vbyte *bytes, int length ) { +// printf("%d\n", length); +// uv_buf_t b = uv_buf_init((char *)bytes, length); +// printf("%ld\n", b.len); +// return b; +// } +// DEFINE_PRIM(_BUF, buf_init_wrap, _BYTES _U32); + +// HL_PRIM void HL_NAME(check_bufs)( const uv_buf_t bufs[], int length ) { +// for(int i = 0; i < length; i++) { +// printf("len: %ld; base: %s\n", bufs[i].len, bufs[i].base); +// } +// } +// DEFINE_PRIM(_VOID, check_bufs, _REF(_BUF) _I32); + +HL_PRIM uv_buf_t *HL_NAME(alloc_buf)( vbyte *bytes, int length ) { + uv_buf_t *buf = UV_ALLOC(uv_buf_t); + buf->base = (char *)bytes; + buf->len = length; + return buf; } -DEFINE_PRIM(_VOID, check_bufs, _REF(_BUF) _I32); +DEFINE_PRIM(_BUF_ARR, alloc_buf, _BYTES _I32); + +DEFINE_PRIM_TO_POINTER(_BUF_ARR, buf); +DEFINE_PRIM_UV_FIELD(_BYTES, vbyte *, _BUF_ARR, buf, base); +DEFINE_PRIM_UV_FIELD(_U64, int64, _BUF_ARR, buf, len); // Handle @@ -488,7 +499,7 @@ static void on_uv_close_cb( uv_handle_t *h ) { #define REQ_DATA_FIELDS \ hl_type *t; \ - uv_req_t *r; + uv_req_t *_r; typedef struct { REQ_DATA_FIELDS @@ -544,12 +555,12 @@ static void on_uv_check_cb( uv_check_t *h ) { typedef struct { HANDLE_DATA_FIELDS; vclosure *onTick; -} vtimer_data; +} uv_timer_data; DEFINE_PRIM_ALLOC(_TIMER, timer); static void on_uv_timer_cb( uv_timer_t *h ) { - vclosure *c = DATA(vtimer_data *, h)->onTick; + vclosure *c = DATA(uv_timer_data *, h)->onTick; hl_call0(void, c); } @@ -560,6 +571,28 @@ static void on_uv_timer_cb( uv_timer_t *h ) { DEFINE_PRIM_ALLOC(_LOOP, loop); DEFINE_PRIM_TO_POINTER(_LOOP, loop); +// SockAddr + +HL_PRIM struct sockaddr_storage *HL_NAME(alloc_sockaddr_storage)() { + return UV_ALLOC(struct sockaddr_storage); +} +DEFINE_PRIM(_SOCKADDR_STORAGE, alloc_sockaddr_storage, _NO_ARG); + +HL_PRIM int HL_NAME(sockaddr_storage_size)() { + return sizeof(struct sockaddr_storage); +} +DEFINE_PRIM(_I32, sockaddr_storage_size, _NO_ARG); + +HL_PRIM void *HL_NAME(sockaddr_storage_to_pointer)( struct sockaddr_storage *addr ) { + return addr; +} +DEFINE_PRIM(_POINTER, sockaddr_storage_to_pointer, _SOCKADDR_STORAGE); + +HL_PRIM struct sockaddr *HL_NAME(sockaddr_of_storage)( struct sockaddr_storage *addr ) { + return (struct sockaddr *)addr; +} +DEFINE_PRIM(_SOCKADDR, sockaddr_of_storage, _SOCKADDR_STORAGE); + // DNS DEFINE_PRIM_ALLOC(_GETADDRINFO, getaddrinfo); @@ -597,6 +630,26 @@ DEFINE_PRIM(_I32, nameinfo_flags_to_native, _I32); #define HL_UV_INET -2 #define HL_UV_INET6 -3 +HL_PRIM int HL_NAME(address_family_to_pf)( int family ) { + switch( family ) { + case HL_UV_UNSPEC: return PF_UNSPEC; + case HL_UV_INET: return PF_INET; + case HL_UV_INET6: return PF_INET6; + default: return family; + } +} +DEFINE_PRIM(_I32, address_family_to_pf, _I32) + +HL_PRIM int HL_NAME(address_family_to_af)( int family ) { + switch( family ) { + case HL_UV_UNSPEC: return AF_UNSPEC; + case HL_UV_INET: return AF_INET; + case HL_UV_INET6: return AF_INET6; + default: return family; + } +} +DEFINE_PRIM(_I32, address_family_to_af, _I32) + //see hl.uv.SockAddr.SocketType #define HL_UV_STREAM -1 #define HL_UV_DGRAM -2 @@ -614,12 +667,7 @@ HL_PRIM struct addrinfo *HL_NAME(alloc_addrinfo)( int flags, int family, int soc if( flags & HL_UV_AI_ADDRCONFIG ) info->ai_flags |= AI_ADDRCONFIG; if( flags & HL_UV_AI_NUMERICSERV ) info->ai_flags |= AI_NUMERICSERV; - switch( family ) { - case HL_UV_UNSPEC: info->ai_family = PF_UNSPEC; break; - case HL_UV_INET: info->ai_family = PF_INET; break; - case HL_UV_INET6: info->ai_family = PF_INET6; break; - default: info->ai_family = family; break; - } + info->ai_family = uv_address_family_to_pf(family); switch( socktype ) { case HL_UV_STREAM: info->ai_socktype = SOCK_STREAM; break; @@ -658,7 +706,7 @@ HL_PRIM uv_sockaddr_storage *HL_NAME(addrinfo_ai_addr)( struct addrinfo *ai ) { memcpy(addr, ai->ai_addr, ai->ai_addrlen); return addr; } -DEFINE_PRIM(_SOCKADDR, addrinfo_ai_addr, _ADDRINFO); +DEFINE_PRIM(_SOCKADDR_STORAGE, addrinfo_ai_addr, _ADDRINFO); DEFINE_PRIM_C_FIELD(_I32, int, _ADDRINFO, addrinfo, ai_protocol); DEFINE_PRIM_C_FIELD(_BYTES, vbyte *, _ADDRINFO, addrinfo, ai_canonname); @@ -674,6 +722,51 @@ static void on_uv_getnameinfo_cb( uv_getnameinfo_t *r, int status, const char *h hl_call3(void,c,int,status,const char *,hostname,const char *,service); } +// Stream + +typedef struct { + HANDLE_DATA_FIELDS; + vclosure *onConnection; + vclosure *onRead; +} uv_stream_data_t; + +DEFINE_PRIM_ALLOC(_WRITE, write); +DEFINE_PRIM_ALLOC(_CONNECT, connect); +DEFINE_PRIM_ALLOC(_SHUTDOWN, shutdown); + +static void on_uv_write_cb( uv_write_t *r, int status ) { + vclosure *c = DATA(uv_req_cb_data_t *, r)->callback; + hl_call0(void, c); +} + +static void on_uv_connect_cb( uv_connect_t *r, int status ) { + vclosure *c = DATA(uv_req_cb_data_t *, r)->callback; + hl_call1(void, c, int, status); +} + +static void on_uv_shutdown_cb( uv_shutdown_t *r, int status ) { + vclosure *c = DATA(uv_req_cb_data_t *, r)->callback; + hl_call1(void, c, int, status); +} + +static void on_uv_connection_cb( uv_stream_t *h, int status ) { + vclosure *c = DATA(uv_stream_data_t *, h)->onConnection; + hl_call1(void, c, int, status); +} + +static void on_uv_alloc_cb( uv_handle_t* h, size_t size, uv_buf_t *buf ) { + *buf = uv_buf_init(malloc(size), (int)size); +} + +static void on_uv_read_cb( uv_stream_t *h, ssize_t nread, const uv_buf_t *buf ) { + vclosure *c = DATA(uv_stream_data_t *, h)->onRead; + hl_call2(void, c, int64, nread, const uv_buf_t *, buf); +} + +// TCP + +DEFINE_PRIM_ALLOC(_TCP, tcp); + // File system DEFINE_PRIM_ALLOC(_FS, fs); @@ -702,7 +795,6 @@ DEFINE_PRIM_UV_FIELD(_I32, int, _DIRENT, dirent, type); DEFINE_PRIM_TO_POINTER(_DIRENT, dirent); - // auto-generated libuv bindings #include "uv_generated.c" @@ -1308,7 +1400,7 @@ HL_PRIM uv_sockaddr_storage *HL_NAME(ip4_addr_wrap)( vstring *ip, int port ) { UV_CHECK_ERROR(uv_ip4_addr(hl_to_utf8(ip->bytes), port, (uv_sockaddr_in *)addr),free(addr),NULL); return addr; } -DEFINE_PRIM(_SOCKADDR, ip4_addr_wrap, _STRING _I32); +DEFINE_PRIM(_SOCKADDR_STORAGE, ip4_addr_wrap, _STRING _I32); HL_PRIM uv_sockaddr_storage *HL_NAME(ip6_addr_wrap)( vstring *ip, int port ) { UV_CHECK_NULL(ip,NULL); @@ -1316,7 +1408,7 @@ HL_PRIM uv_sockaddr_storage *HL_NAME(ip6_addr_wrap)( vstring *ip, int port ) { UV_CHECK_ERROR(uv_ip6_addr(hl_to_utf8(ip->bytes), port, (uv_sockaddr_in6 *)addr),free(addr),NULL); return addr; } -DEFINE_PRIM(_SOCKADDR, ip6_addr_wrap, _STRING _I32); +DEFINE_PRIM(_SOCKADDR_STORAGE, ip6_addr_wrap, _STRING _I32); HL_PRIM vdynamic *HL_NAME(sockaddr_get_port)( uv_sockaddr_storage *addr ) { UV_CHECK_NULL(addr,NULL); @@ -1330,7 +1422,7 @@ HL_PRIM vdynamic *HL_NAME(sockaddr_get_port)( uv_sockaddr_storage *addr ) { } return hl_make_dyn(&port, &hlt_i32); } -DEFINE_PRIM(_NULL(_I32), sockaddr_get_port, _SOCKADDR); +DEFINE_PRIM(_NULL(_I32), sockaddr_get_port, _SOCKADDR_STORAGE); HL_PRIM uv_sockaddr_storage *HL_NAME(sockaddr_cast_ptr)( vdynamic *ptr ) { UV_CHECK_NULL(ptr,NULL); @@ -1338,7 +1430,7 @@ HL_PRIM uv_sockaddr_storage *HL_NAME(sockaddr_cast_ptr)( vdynamic *ptr ) { hl_error("Invalid usage of hl.uv.SockAddr.castPtr()"); return (uv_sockaddr_storage *)ptr->v.ptr; } -DEFINE_PRIM(_SOCKADDR, sockaddr_cast_ptr, _DYN); +DEFINE_PRIM(_SOCKADDR_STORAGE, sockaddr_cast_ptr, _DYN); //How to return vstring instead of vbyte? HL_PRIM vbyte *HL_NAME(ip_name_wrap)( uv_sockaddr_storage *addr ) { @@ -1353,7 +1445,7 @@ HL_PRIM vbyte *HL_NAME(ip_name_wrap)( uv_sockaddr_storage *addr ) { } return dst; } -DEFINE_PRIM(_BYTES, ip_name_wrap, _SOCKADDR); +DEFINE_PRIM(_BYTES, ip_name_wrap, _SOCKADDR_STORAGE); // TCP @@ -1395,7 +1487,7 @@ HL_PRIM void HL_NAME(tcp_bind_wrap)( uv_tcp_t *h, uv_sockaddr_storage *addr, vdy int flags = ipv6_only && ipv6_only->v.b ? UV_TCP_IPV6ONLY : 0; UV_CHECK_ERROR(uv_tcp_bind(h,(uv_sockaddr *)addr,flags),,); } -DEFINE_PRIM(_VOID, tcp_bind_wrap, _HANDLE _SOCKADDR _NULL(_BOOL)); +DEFINE_PRIM(_VOID, tcp_bind_wrap, _HANDLE _SOCKADDR_STORAGE _NULL(_BOOL)); HL_PRIM uv_sockaddr_storage *HL_NAME(tcp_getsockname_wrap)( uv_tcp_t *h ) { UV_CHECK_NULL(h,NULL); @@ -1404,7 +1496,7 @@ HL_PRIM uv_sockaddr_storage *HL_NAME(tcp_getsockname_wrap)( uv_tcp_t *h ) { UV_CHECK_ERROR(uv_tcp_getsockname(h,(uv_sockaddr *)addr,&size),free(addr),NULL); return addr; } -DEFINE_PRIM(_SOCKADDR, tcp_getsockname_wrap, _HANDLE); +DEFINE_PRIM(_SOCKADDR_STORAGE, tcp_getsockname_wrap, _HANDLE); HL_PRIM uv_sockaddr_storage *HL_NAME(tcp_getpeername_wrap)( uv_tcp_t *h ) { UV_CHECK_NULL(h,NULL); @@ -1413,7 +1505,7 @@ HL_PRIM uv_sockaddr_storage *HL_NAME(tcp_getpeername_wrap)( uv_tcp_t *h ) { UV_CHECK_ERROR(uv_tcp_getpeername(h,(uv_sockaddr *)addr,&size),free(addr),NULL); return addr; } -DEFINE_PRIM(_SOCKADDR, tcp_getpeername_wrap, _HANDLE); +DEFINE_PRIM(_SOCKADDR_STORAGE, tcp_getpeername_wrap, _HANDLE); static void on_connect( uv_connect_t *r, int status ) { UV_GET_CLOSURE(c,r,0,"No callback in connect request"); @@ -1428,7 +1520,7 @@ HL_PRIM void HL_NAME(tcp_connect_wrap)( uv_tcp_t *h, uv_sockaddr_storage *addr, UV_ALLOC_REQ(uv_connect_t,r,c); UV_CHECK_ERROR(uv_tcp_connect(r, h,(uv_sockaddr *)addr,on_connect),free_req((uv_req_t *)r),); } -DEFINE_PRIM(_VOID, tcp_connect_wrap, _HANDLE _SOCKADDR _FUN(_VOID,_I32)); +DEFINE_PRIM(_VOID, tcp_connect_wrap, _HANDLE _SOCKADDR_STORAGE _FUN(_VOID,_I32)); HL_PRIM void HL_NAME(tcp_close_reset_wrap)( uv_tcp_t *h, vclosure *c ) { UV_CHECK_NULL(h,); @@ -1715,13 +1807,13 @@ HL_PRIM void HL_NAME(udp_bind_wrap)( uv_udp_t *h, uv_sockaddr_storage *addr, vdy flags |= UV_UDP_REUSEADDR; UV_CHECK_ERROR(uv_udp_bind(h,(uv_sockaddr *)addr,flags),,); } -DEFINE_PRIM(_VOID, udp_bind_wrap, _HANDLE _SOCKADDR _NULL(_BOOL) _NULL(_BOOL)); +DEFINE_PRIM(_VOID, udp_bind_wrap, _HANDLE _SOCKADDR_STORAGE _NULL(_BOOL) _NULL(_BOOL)); HL_PRIM void HL_NAME(udp_connect_wrap)( uv_udp_t *h, uv_sockaddr_storage *addr ) { UV_CHECK_NULL(h,); UV_CHECK_ERROR(uv_udp_connect(h,(uv_sockaddr *)addr),,); } -DEFINE_PRIM(_VOID, udp_connect_wrap, _HANDLE _SOCKADDR _NULL(_BOOL)); +DEFINE_PRIM(_VOID, udp_connect_wrap, _HANDLE _SOCKADDR_STORAGE _NULL(_BOOL)); HL_PRIM uv_sockaddr_storage *HL_NAME(udp_getsockname_wrap)( uv_udp_t *h ) { UV_CHECK_NULL(h,NULL); @@ -1730,7 +1822,7 @@ HL_PRIM uv_sockaddr_storage *HL_NAME(udp_getsockname_wrap)( uv_udp_t *h ) { UV_CHECK_ERROR(uv_udp_getsockname(h,(uv_sockaddr *)addr,&size),free(addr),NULL); return addr; } -DEFINE_PRIM(_SOCKADDR, udp_getsockname_wrap, _HANDLE); +DEFINE_PRIM(_SOCKADDR_STORAGE, udp_getsockname_wrap, _HANDLE); HL_PRIM uv_sockaddr_storage *HL_NAME(udp_getpeername_wrap)( uv_udp_t *h ) { UV_CHECK_NULL(h,NULL); @@ -1739,7 +1831,7 @@ HL_PRIM uv_sockaddr_storage *HL_NAME(udp_getpeername_wrap)( uv_udp_t *h ) { UV_CHECK_ERROR(uv_udp_getpeername(h,(uv_sockaddr *)addr,&size),free(addr),NULL); return addr; } -DEFINE_PRIM(_SOCKADDR, udp_getpeername_wrap, _HANDLE); +DEFINE_PRIM(_SOCKADDR_STORAGE, udp_getpeername_wrap, _HANDLE); static uv_membership udp_membership( int hx_membership ) { switch( hx_membership ) { @@ -1824,7 +1916,7 @@ HL_PRIM void HL_NAME(udp_send_wrap)( uv_udp_t *h, vbyte *data, int length, uv_so buf.len = length; UV_CHECK_ERROR(uv_udp_send(r,h,&buf,1,(uv_sockaddr *)addr,on_udp_send),free_req((uv_req_t *)r),); } -DEFINE_PRIM(_VOID, udp_send_wrap, _HANDLE _BYTES _I32 _SOCKADDR _FUN(_VOID,_I32)); +DEFINE_PRIM(_VOID, udp_send_wrap, _HANDLE _BYTES _I32 _SOCKADDR_STORAGE _FUN(_VOID,_I32)); HL_PRIM int HL_NAME(udp_try_send_wrap)( uv_udp_t *h, vbyte *data, int length, uv_sockaddr_storage *addr ) { UV_CHECK_NULL(h,0); @@ -1832,7 +1924,7 @@ HL_PRIM int HL_NAME(udp_try_send_wrap)( uv_udp_t *h, vbyte *data, int length, uv UV_CHECK_ERROR(uv_udp_try_send(h,&buf,1,(uv_sockaddr *)addr),,0); return __result__; } -DEFINE_PRIM(_I32, udp_try_send_wrap, _HANDLE _BYTES _I32 _SOCKADDR); +DEFINE_PRIM(_I32, udp_try_send_wrap, _HANDLE _BYTES _I32 _SOCKADDR_STORAGE); static void on_udp_recv( uv_udp_t *h, ssize_t nread, const uv_buf_t *buf, const uv_sockaddr *src_addr, unsigned flags ) { UV_GET_CLOSURE(c,h,0,"No recv callback in udp handle"); @@ -1863,7 +1955,7 @@ HL_PRIM void HL_NAME(udp_recv_start_wrap)( uv_udp_t *h, vclosure *c ) { handle_register_callback((uv_handle_t *)h,c,0); UV_CHECK_ERROR(uv_udp_recv_start(h,on_alloc,on_udp_recv),handle_clear_callback((uv_handle_t*)h,0),); } -DEFINE_PRIM(_VOID, udp_recv_start_wrap, _HANDLE _FUN(_VOID,_I32 _BYTES _I32 _SOCKADDR _DYN)); +DEFINE_PRIM(_VOID, udp_recv_start_wrap, _HANDLE _FUN(_VOID,_I32 _BYTES _I32 _SOCKADDR_STORAGE _DYN)); HL_PRIM bool HL_NAME(udp_using_recvmmsg_wrap)( uv_udp_t *h ) { UV_CHECK_NULL(h,false); @@ -2023,7 +2115,7 @@ HL_PRIM void HL_NAME(getnameinfo_wrap)( uv_loop_t *l, uv_sockaddr_storage *addr, flags |= NI_NUMERICSERV; UV_CHECK_ERROR(uv_getnameinfo(l,r,on_getnameinfo,(uv_sockaddr *)addr,flags),free_req((uv_req_t *)r),); } -DEFINE_PRIM(_VOID, getnameinfo_wrap, _LOOP _SOCKADDR _NULL(_BOOL) _NULL(_BOOL) _NULL(_BOOL) _NULL(_BOOL) _NULL(_BOOL) _FUN(_VOID,_I32 _BYTES _BYTES)); +DEFINE_PRIM(_VOID, getnameinfo_wrap, _LOOP _SOCKADDR_STORAGE _NULL(_BOOL) _NULL(_BOOL) _NULL(_BOOL) _NULL(_BOOL) _NULL(_BOOL) _FUN(_VOID,_I32 _BYTES _BYTES)); // loop diff --git a/other/uvsample/UVSample.hx b/other/uvsample/UVSample.hx index 58b21d827..010b34ca4 100644 --- a/other/uvsample/UVSample.hx +++ b/other/uvsample/UVSample.hx @@ -1,7 +1,7 @@ class UVSample { static function main() { CheckSample.main(); - // TcpSample.main(); + TcpSample.main(); DnsSample.main(); // UdpSample.main(); // PipeSample.main(); diff --git a/src/std/bytes.c b/src/std/bytes.c index dde91fc03..a0a0722cc 100644 --- a/src/std/bytes.c +++ b/src/std/bytes.c @@ -180,7 +180,7 @@ HL_PRIM double hl_parse_float( vbyte *bytes, int pos, int len ) { uchar *str = (uchar*)(bytes+pos); uchar *end = NULL; double d; - while( *str == ' ' ) str++; + while( *str == ' ' ) str++; d = utod(str,&end); if( end == str ) return hl_nan(); @@ -252,6 +252,7 @@ HL_PRIM int hl_string_compare( vbyte *a, vbyte *b, int len ) { } DEFINE_PRIM(_BYTES,alloc_bytes,_I32); +DEFINE_PRIM(_BYTES,copy_bytes,_BYTES _I32); DEFINE_PRIM(_VOID,bytes_blit,_BYTES _I32 _BYTES _I32 _I32); DEFINE_PRIM(_I32,bytes_compare,_BYTES _I32 _BYTES _I32 _I32); DEFINE_PRIM(_I32,bytes_compare16,_BYTES _BYTES _I32); From f23399b764ff15d17a48027840e3fc491ac6dbc7 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Thu, 26 Aug 2021 21:34:05 +0300 Subject: [PATCH 073/117] expose uv_alloc_cb; update stream; udp --- libs/uv/uv.c | 68 ++++++++++++++++++++++++++------ libs/uv/uv_generated.c | 2 - other/uvgenerator/UV.hx.header | 5 ++- other/uvgenerator/UVGenerator.hx | 2 +- other/uvsample/UVSample.hx | 10 ++--- other/uvsample/UdpSample.hx | 7 +++- 6 files changed, 72 insertions(+), 22 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index b991fcd24..cf22e7978 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -100,12 +100,6 @@ typedef struct sockaddr_storage uv_sockaddr_storage; static void on_uv_signal_cb( uv_signal_t *h, int signum ) { } - - static void on_uv_udp_send_cb( uv_udp_send_t *r, int status ) { - } - - static void on_uv_udp_recv_cb( uv_udp_t *h, ssize_t nread, const uv_buf_t *buf, const uv_sockaddr *src_addr, unsigned flags ) { - } // } #define UV_ALLOC(t) ((t*)malloc(sizeof(t))) @@ -463,6 +457,12 @@ HL_PRIM uv_buf_t *HL_NAME(alloc_buf)( vbyte *bytes, int length ) { } DEFINE_PRIM(_BUF_ARR, alloc_buf, _BYTES _I32); +HL_PRIM void HL_NAME(buf_set)( uv_buf_t *buf, vbyte *bytes, int length ) { // TODO: change `length` to `int64` + buf->base = (char *)bytes; + buf->len = length; +} +DEFINE_PRIM(_VOID, buf_set, _BUF_ARR _BYTES _I32); + DEFINE_PRIM_TO_POINTER(_BUF_ARR, buf); DEFINE_PRIM_UV_FIELD(_BYTES, vbyte *, _BUF_ARR, buf, base); DEFINE_PRIM_UV_FIELD(_U64, int64, _BUF_ARR, buf, len); @@ -478,6 +478,14 @@ typedef struct { HANDLE_DATA_FIELDS; } uv_handle_data_t; +#define HANDLE_DATA_WITH_ALLOC_FIELDS \ + HANDLE_DATA_FIELDS; \ + vclosure *onAlloc; + +typedef struct { + HANDLE_DATA_WITH_ALLOC_FIELDS; +} uv_handle_data_with_alloc_t; + #define _HANDLE_DATA _OBJ(_HANDLE _FUN(_VOID,_NO_ARG)) DEFINE_PRIM_TO_POINTER(_HANDLE, handle); @@ -495,6 +503,13 @@ static void on_uv_close_cb( uv_handle_t *h ) { hl_call0(void, data->onClose); } +static void on_uv_alloc_cb( uv_handle_t* h, size_t size, uv_buf_t *buf ) { + vclosure *c = DATA(uv_handle_data_with_alloc_t *, h)->onAlloc; + hl_call2(void, c, uv_buf_t *, buf, int, (int)size); + if( buf->base ) + hl_add_root(buf->base); +} + // Request #define REQ_DATA_FIELDS \ @@ -593,6 +608,15 @@ HL_PRIM struct sockaddr *HL_NAME(sockaddr_of_storage)( struct sockaddr_storage * } DEFINE_PRIM(_SOCKADDR, sockaddr_of_storage, _SOCKADDR_STORAGE); +HL_PRIM struct sockaddr_storage *HL_NAME(sockaddr_to_storage)( struct sockaddr *addr ) { + if( !addr ) + return NULL; + struct sockaddr_storage *storage = UV_ALLOC(struct sockaddr_storage); + memcpy(storage, addr, sizeof(struct sockaddr_storage)); + return storage; +} +DEFINE_PRIM(_SOCKADDR_STORAGE, sockaddr_to_storage, _SOCKADDR); + // DNS DEFINE_PRIM_ALLOC(_GETADDRINFO, getaddrinfo); @@ -725,7 +749,7 @@ static void on_uv_getnameinfo_cb( uv_getnameinfo_t *r, int status, const char *h // Stream typedef struct { - HANDLE_DATA_FIELDS; + HANDLE_DATA_WITH_ALLOC_FIELDS; vclosure *onConnection; vclosure *onRead; } uv_stream_data_t; @@ -736,7 +760,7 @@ DEFINE_PRIM_ALLOC(_SHUTDOWN, shutdown); static void on_uv_write_cb( uv_write_t *r, int status ) { vclosure *c = DATA(uv_req_cb_data_t *, r)->callback; - hl_call0(void, c); + hl_call1(void, c, int, status); } static void on_uv_connect_cb( uv_connect_t *r, int status ) { @@ -754,12 +778,10 @@ static void on_uv_connection_cb( uv_stream_t *h, int status ) { hl_call1(void, c, int, status); } -static void on_uv_alloc_cb( uv_handle_t* h, size_t size, uv_buf_t *buf ) { - *buf = uv_buf_init(malloc(size), (int)size); -} - static void on_uv_read_cb( uv_stream_t *h, ssize_t nread, const uv_buf_t *buf ) { vclosure *c = DATA(uv_stream_data_t *, h)->onRead; + if( buf->base ) + hl_remove_root(buf->base); hl_call2(void, c, int64, nread, const uv_buf_t *, buf); } @@ -767,6 +789,28 @@ static void on_uv_read_cb( uv_stream_t *h, ssize_t nread, const uv_buf_t *buf ) DEFINE_PRIM_ALLOC(_TCP, tcp); +// UDP + +typedef struct { + HANDLE_DATA_WITH_ALLOC_FIELDS; + vclosure *onRecv; +} uv_udp_data_t; + +DEFINE_PRIM_ALLOC(_UDP, udp); +DEFINE_PRIM_ALLOC(_UDP_SEND, udp_send); + +static void on_uv_udp_send_cb( uv_udp_send_t *r, int status ) { + vclosure *c = DATA(uv_req_cb_data_t *, r)->callback; + hl_call1(void, c, int, status); +} + +static void on_uv_udp_recv_cb( uv_udp_t *h, ssize_t nread, const uv_buf_t *buf, const uv_sockaddr *src_addr, unsigned flags ) { + vclosure *c = DATA(uv_udp_data_t *, h)->onRecv; + if( (nread <= 0 && !src_addr) || (flags & UV_UDP_MMSG_FREE) ) + hl_remove_root(buf->base); + hl_call4(void, c, int64, nread, const uv_buf_t *, buf, const uv_sockaddr *, src_addr, unsigned, flags); +} + // File system DEFINE_PRIM_ALLOC(_FS, fs); diff --git a/libs/uv/uv_generated.c b/libs/uv/uv_generated.c index a6014dd1f..09766676b 100644 --- a/libs/uv/uv_generated.c +++ b/libs/uv/uv_generated.c @@ -333,8 +333,6 @@ DEFINE_PRIM(_HANDLE_TYPE, guess_handle, _FILE); DEFINE_PRIM(_VOID, library_shutdown, _NO_ARG); -DEFINE_PRIM(_BUF, buf_init, _BYTES _U32); - DEFINE_PRIM(_I32, resident_set_memory, _REF(_I64)); DEFINE_PRIM(_I32, uptime, _REF(_F64)); diff --git a/other/uvgenerator/UV.hx.header b/other/uvgenerator/UV.hx.header index aa80e1045..d7617548c 100644 --- a/other/uvgenerator/UV.hx.header +++ b/other/uvgenerator/UV.hx.header @@ -130,7 +130,9 @@ extern class UV { static public function sockaddr_storage_size():Int; static public function sockaddr_storage_to_pointer(addr:CSockaddrStorageStar):Pointer; static public function sockaddr_of_storage(addr:CSockaddrStorageStar):CSockaddrStar; - // static public function alloc_udp():UvUdpTStar; + static public function sockaddr_to_storage(addr:CSockaddrStar):CSockaddrStorageStar; + static public function alloc_udp():UvUdpTStar; + static public function alloc_udp_send():UvUdpSendTStar; // static public function alloc_pipe():UvPipeTStar; // static public function alloc_tty():UvTtyTStar; static public function alloc_getaddrinfo():UvGetaddrinfoTStar; @@ -158,6 +160,7 @@ extern class UV { static public function dirent_type(dirent:UvDirentTStar):DirEntryType; static public function buf_to_pointer(buf:UvBufTArr):Pointer; static public function alloc_buf(bytes:Bytes, bytesLength:Int):UvBufTArr; + static public function buf_set(buf:UvBufTArr, base:Bytes, length:Int):Void; static public function buf_base(buf:UvBufTArr):Bytes; static public function buf_len(buf:UvBufTArr):U64; diff --git a/other/uvgenerator/UVGenerator.hx b/other/uvgenerator/UVGenerator.hx index 8e0263d80..e44efcfe0 100644 --- a/other/uvgenerator/UVGenerator.hx +++ b/other/uvgenerator/UVGenerator.hx @@ -31,7 +31,7 @@ class UVGenerator { static final skipFunctions = ['uv_replace_allocator', 'uv_get_osfhandle', 'uv_fileno', 'uv_open_osfhandle', 'uv_print_all_handles', 'uv_print_active_handles', 'uv_os_environ', 'uv_os_free_environ', 'uv_setup_args', 'uv_get_process_title', - 'uv_set_process_title', 'uv_tcp_open', 'uv_udp_open', 'uv_socketpair', + 'uv_set_process_title', 'uv_tcp_open', 'uv_udp_open', 'uv_socketpair', 'uv_buf_init', 'uv_loop_configure']; // TODO: don't skip uv_loop_configure static final allowNoCallback = ['uv_fs_cb']; diff --git a/other/uvsample/UVSample.hx b/other/uvsample/UVSample.hx index 010b34ca4..800c9d6a5 100644 --- a/other/uvsample/UVSample.hx +++ b/other/uvsample/UVSample.hx @@ -1,13 +1,13 @@ class UVSample { static function main() { - CheckSample.main(); - TcpSample.main(); - DnsSample.main(); - // UdpSample.main(); + // CheckSample.main(); + // TcpSample.main(); + // DnsSample.main(); + UdpSample.main(); // PipeSample.main(); // ProcessSample.main(); // FileSample.main(); - DirSample.main(); + // DirSample.main(); // FsEventSample.main(); // FsPollSample.main(); // MiscSample.main(); diff --git a/other/uvsample/UdpSample.hx b/other/uvsample/UdpSample.hx index 6ff2eb69c..7ea61f958 100644 --- a/other/uvsample/UdpSample.hx +++ b/other/uvsample/UdpSample.hx @@ -32,7 +32,12 @@ class UdpSample { if(addr == null && !flags.mmsgChunk) udp.close(() -> print('Done')); } - print('...with flags $flags'); + var o = { + mmsgChunk: flags.mmsgChunk, + mmsgFree: flags.mmsgFree, + partial: flags.partial, + } + print('...with flags $o'); case _: throw new UVException(e); }); From 052e17b9f88464c7336f344c23a43100916238fb Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Thu, 26 Aug 2021 22:12:12 +0300 Subject: [PATCH 074/117] pipe --- libs/uv/uv.c | 4 ++++ other/uvgenerator/UV.hx.header | 2 +- other/uvsample/UVSample.hx | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index cf22e7978..6e01006a6 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -789,6 +789,10 @@ static void on_uv_read_cb( uv_stream_t *h, ssize_t nread, const uv_buf_t *buf ) DEFINE_PRIM_ALLOC(_TCP, tcp); +// Pipe + +DEFINE_PRIM_ALLOC(_PIPE, pipe); + // UDP typedef struct { diff --git a/other/uvgenerator/UV.hx.header b/other/uvgenerator/UV.hx.header index d7617548c..aa22b225b 100644 --- a/other/uvgenerator/UV.hx.header +++ b/other/uvgenerator/UV.hx.header @@ -133,7 +133,7 @@ extern class UV { static public function sockaddr_to_storage(addr:CSockaddrStar):CSockaddrStorageStar; static public function alloc_udp():UvUdpTStar; static public function alloc_udp_send():UvUdpSendTStar; - // static public function alloc_pipe():UvPipeTStar; + static public function alloc_pipe():UvPipeTStar; // static public function alloc_tty():UvTtyTStar; static public function alloc_getaddrinfo():UvGetaddrinfoTStar; static public function alloc_getnameinfo():UvGetnameinfoTStar; diff --git a/other/uvsample/UVSample.hx b/other/uvsample/UVSample.hx index 800c9d6a5..b88e3b4b8 100644 --- a/other/uvsample/UVSample.hx +++ b/other/uvsample/UVSample.hx @@ -3,8 +3,8 @@ class UVSample { // CheckSample.main(); // TcpSample.main(); // DnsSample.main(); - UdpSample.main(); - // PipeSample.main(); + // UdpSample.main(); + PipeSample.main(); // ProcessSample.main(); // FileSample.main(); // DirSample.main(); From 13d6169e6e61fd6537b0b049576e8583f8539e0d Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 27 Aug 2021 07:53:45 +0300 Subject: [PATCH 075/117] uvsample --- other/uvsample/UVSample.hx | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/other/uvsample/UVSample.hx b/other/uvsample/UVSample.hx index b88e3b4b8..050ee22a2 100644 --- a/other/uvsample/UVSample.hx +++ b/other/uvsample/UVSample.hx @@ -1,10 +1,11 @@ class UVSample { static function main() { + run(); // CheckSample.main(); // TcpSample.main(); // DnsSample.main(); // UdpSample.main(); - PipeSample.main(); + // PipeSample.main(); // ProcessSample.main(); // FileSample.main(); // DirSample.main(); @@ -14,4 +15,17 @@ class UVSample { // TtySample.main(); // VersionSample.main(); } + + macro static public function run() { + var sample = haxe.macro.Context.definedValue('SAMPLE'); + if(sample == null || sample == '') + sample = Sys.getEnv('SAMPLE'); + if(sample == null || sample == '') { + Sys.println('Add -D SAMPLE= or set environment variable SAMPLE='); + Sys.println('\twhere is a sample name. For example: SAMPLE=Tcp'); + Sys.exit(1); + } + sample += 'Sample'; + return macro $i{sample}.main(); + } } \ No newline at end of file From c26ff282503fe96db9b9d0f4889db4b1fab3ed6b Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 27 Aug 2021 12:59:49 +0300 Subject: [PATCH 076/117] process --- libs/uv/uv.c | 251 ++++++++++++++++++++++++-------- other/uvgenerator/UV.hx.header | 13 +- other/uvsample/ProcessSample.hx | 4 +- 3 files changed, 203 insertions(+), 65 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 6e01006a6..e716c6608 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -64,6 +64,7 @@ #define _UTSNAME _ABSTRACT(uv_utsname_t_star) #define _TIMEVAL _ABSTRACT(uv_timeval_t_star) #define _TIMEVAL64 _ABSTRACT(uv_timeval64_t_star) +#define _STDIO_CONTAINER _ABSTRACT(uv_stdio_container_t_star) #define _PROCESS_OPTIONS _ABSTRACT(uv_process_options_t_star) #define _REQ_TYPE _I32 #define _TTY_MODE_T _I32 @@ -105,12 +106,24 @@ typedef struct sockaddr_storage uv_sockaddr_storage; #define UV_ALLOC(t) ((t*)malloc(sizeof(t))) #define DATA(t,h) ((t)h->data) +#define UV_CHECK_NULL(v,fail_return) \ + if( !v ) { \ + hl_null_access(); \ + return fail_return; \ + } + #define DEFINE_PRIM_ALLOC(hl_type,uv_name) \ HL_PRIM uv_##uv_name##_t *HL_NAME(alloc_##uv_name)() { \ return UV_ALLOC(uv_##uv_name##_t); \ } \ DEFINE_PRIM(hl_type, alloc_##uv_name, _NO_ARG); +#define DEFINE_PRIM_FREE(hl_type, name) \ + HL_PRIM void HL_NAME(free_##name)( void *ptr ) { \ + free(ptr); \ + } \ + DEFINE_PRIM(_VOID, free_##name, hl_type); + #define DEFINE_PRIM_C_FIELD(hl_return,c_return,hl_struct,c_struct,field) \ HL_PRIM c_return HL_NAME(c_struct##_##field)( struct c_struct *s ) { \ return (c_return)s->field; \ @@ -154,6 +167,11 @@ HL_PRIM void *HL_NAME(bytes_to_pointer)( vbyte *bytes ) { } DEFINE_PRIM(_POINTER, bytes_to_pointer, _BYTES); +HL_PRIM vbyte *HL_NAME(bytes_of_pointer)( void *ptr ) { + return ptr; +} +DEFINE_PRIM(_BYTES, bytes_of_pointer, _POINTER); + // Errors #define HL_UV_NOERR 0 @@ -425,6 +443,27 @@ HL_PRIM int HL_NAME(translate_to_uv_error)( int hl_errno ) { } DEFINE_PRIM(_I32, translate_to_uv_error, _I32); +// C arrays + +HL_PRIM vbyte **HL_NAME(alloc_char_array)( int length ) { + return malloc(sizeof(vbyte *) * length); +} +DEFINE_PRIM(_REF(_BYTES), alloc_char_array, _I32); + +HL_PRIM void HL_NAME(free_char_array)( vbyte **a ) { + free(a); +} +DEFINE_PRIM(_VOID, free_char_array, _REF(_BYTES)); + +HL_PRIM void HL_NAME(print_char_array)( void **arr ) { + int i = -1; + while( arr[++i] ) { + char *item = arr[i]; + printf("index %d; str %s\n", i, item); + }; +} +DEFINE_PRIM(_VOID, print_char_array, _REF(_POINTER)); + // Buf // HL_PRIM void HL_NAME(check_bufs)( const uv_buf_t bufs[], int length ) { @@ -815,6 +854,147 @@ static void on_uv_udp_recv_cb( uv_udp_t *h, ssize_t nread, const uv_buf_t *buf, hl_call4(void, c, int64, nread, const uv_buf_t *, buf, const uv_sockaddr *, src_addr, unsigned, flags); } +// Signal + +HL_PRIM int HL_NAME(translate_to_sys_signal)( int hx ) { + switch(hx) { + case -1: return SIGABRT; break; + case -2: return SIGFPE; break; + case -3: return SIGHUP; break; + case -4: return SIGILL; break; + case -5: return SIGINT; break; + case -6: return SIGKILL; break; + case -7: return SIGSEGV; break; + case -8: return SIGTERM; break; + case -9: return SIGWINCH; break; + default: return hx; break; + } +} +DEFINE_PRIM(_I32, translate_to_sys_signal, _I32); + +HL_PRIM int HL_NAME(translate_sys_signal)( int uv ) { + switch(uv) { + case SIGABRT: return -1; break; + case SIGFPE: return -2; break; + case SIGHUP: return -3; break; + case SIGILL: return -4; break; + case SIGINT: return -5; break; + case SIGKILL: return -6; break; + case SIGSEGV: return -7; break; + case SIGTERM: return -8; break; + case SIGWINCH: return -9; break; + default: return uv; break; + } +} +DEFINE_PRIM(_I32, translate_sys_signal, _I32); + +// Process + +typedef struct { + HANDLE_DATA_FIELDS; + vclosure *onExit; +} uv_process_data_t; + +DEFINE_PRIM_ALLOC(_PROCESS, process); + +static void on_uv_exit_cb(uv_process_t *h, int64_t exit_status, int term_signal) { + vclosure *c = DATA(uv_process_data_t *, h)->onExit; + if( c ) + hl_call3(void, c, uv_process_data_t *, h->data, int64, exit_status, int, uv_translate_sys_signal(term_signal)); +} + +// Constructors of hl.uv.Process.ProcessStdio enum +typedef struct { //ProcessStdio.FD + hl_type *t; + int index; + int fd; +} stdio_fd; + +typedef struct { //ProcessStdio.PIPE + hl_type *t; + int index; + uv_pipe_t *pipe; + int permissions; + vdynamic *nonBlock; +} stdio_pipe; + +typedef struct { //ProcessStdio.STREAM + hl_type *t; + int index; + uv_stream_t *stream; +} stdio_stream; + +HL_PRIM uv_stdio_container_t *HL_NAME(alloc_stdio_container)( varray *stdio, int count ) { + uv_stdio_container_t *container = malloc(sizeof(uv_stdio_container_t) * stdio->size); + for (int i = 0; i < count; i++) { + venum *io = hl_aptr(stdio, venum *)[i]; + if( !io ) { + container[i].flags = UV_IGNORE; + continue; + } + // On Haxe side: enum ProcessStdio + stdio_pipe *cfg; + switch( io->index ) { + case 0: // IGNORE + container[i].flags = UV_IGNORE; + break; + case 1: // INHERIT + container[i].flags = UV_INHERIT_FD; + container[i].data.fd = i; + break; + case 2: // FD(fd:StdioFd) + container[i].flags = UV_INHERIT_FD; + container[i].data.fd = ((stdio_fd *)io)->fd; + break; + case 3: // PIPE + cfg = (stdio_pipe *)io; + UV_CHECK_NULL(cfg->pipe,NULL); + container[i].flags = UV_CREATE_PIPE; + container[i].flags |= cfg->permissions; + // switch( cfg->permissions ) { + // case 1: container[i].flags |= UV_READABLE_PIPE; break; + // case 2: container[i].flags |= UV_WRITABLE_PIPE; break; + // case 3: + // default: container[i].flags |= UV_READABLE_PIPE | UV_WRITABLE_PIPE; break; + // } + if( cfg->nonBlock && cfg->nonBlock->v.b ) + container[i].flags |= UV_OVERLAPPED_PIPE; + container[i].data.stream = (uv_stream_t *)cfg->pipe; + break; + case 4: // STREAM + UV_CHECK_NULL(((stdio_stream *)io)->stream,NULL); + container[i].flags = UV_INHERIT_STREAM; + container[i].data.stream = ((stdio_stream *)io)->stream; + break; + default: + container[i].flags = UV_IGNORE; + break; + } + } + return container; +} +DEFINE_PRIM(_STDIO_CONTAINER, alloc_stdio_container, _ARR _I32); +DEFINE_PRIM_FREE(_STDIO_CONTAINER, stdio_container); + +HL_PRIM uv_process_options_t *HL_NAME(alloc_process_options)( vbyte *file, vbyte **args, vbyte **env, + vbyte *cwd, int flags, int stdio_count, uv_stdio_container_t *stdio, uv_uid_t uid, uv_gid_t gid ) { + + uv_process_options_t *options = UV_ALLOC(uv_process_options_t); + options->file = (char *)file; + options->exit_cb = on_uv_exit_cb; + options->args = (char **)args; + options->env = (char **)env; + options->cwd = (char *)cwd; + options->flags = flags; + options->uid = uid; + options->gid = gid; + options->stdio_count = stdio_count; + options->stdio = stdio; + return options; +} +DEFINE_PRIM(_PROCESS_OPTIONS, alloc_process_options, _BYTES _REF(_BYTES) _REF(_BYTES) _BYTES _I32 _I32 _STDIO_CONTAINER _I32 _I32); +DEFINE_PRIM_FREE(_PROCESS_OPTIONS, process_options); + // File system DEFINE_PRIM_ALLOC(_FS, fs); @@ -873,11 +1053,6 @@ typedef struct { req_init_hl_data((uv_req_t *)r); \ if( c ) \ req_register_callback((uv_req_t *)r,c,0); -#define UV_CHECK_NULL(handle,fail_return) \ - if( !handle ) { \ - hl_null_access(); \ - return fail_return; \ - } #define UV_CHECK_ERROR(action,cleanup,fail_return) \ int __result__ = action; \ if(__result__ < 0) { \ @@ -1350,45 +1525,15 @@ DEFINE_PRIM(_VOID, check_stop_wrap, _HANDLE); // Signal -static int signum_hx2uv( int hx ) { - switch(hx) { - case -1: return SIGABRT; break; - case -2: return SIGFPE; break; - case -3: return SIGHUP; break; - case -4: return SIGILL; break; - case -5: return SIGINT; break; - case -6: return SIGKILL; break; - case -7: return SIGSEGV; break; - case -8: return SIGTERM; break; - case -9: return SIGWINCH; break; - default: return hx; break; - } -} - -static int signum_uv2hx( int uv ) { - switch(uv) { - case SIGABRT: return -1; break; - case SIGFPE: return -2; break; - case SIGHUP: return -3; break; - case SIGILL: return -4; break; - case SIGINT: return -5; break; - case SIGKILL: return -6; break; - case SIGSEGV: return -7; break; - case SIGTERM: return -8; break; - case SIGWINCH: return -9; break; - default: return uv; break; - } -} - static void on_signal( uv_signal_t *h, int signum ) { UV_GET_CLOSURE(c,h,0,"No callback in signal handle"); - hl_call1(void, c, int, signum_uv2hx(signum)); + hl_call1(void, c, int, uv_translate_sys_signal(signum)); } static void on_signal_oneshot( uv_signal_t *h, int signum ) { UV_GET_CLOSURE(c,h,0,"No callback in signal handle"); handle_clear_callback((uv_handle_t *)h,0); - hl_call1(void, c, int, signum_uv2hx(signum)); + hl_call1(void, c, int, uv_translate_sys_signal(signum)); } HL_PRIM uv_signal_t *HL_NAME(signal_init_wrap)( uv_loop_t *loop ) { @@ -1404,7 +1549,7 @@ HL_PRIM void HL_NAME(signal_start_wrap)( uv_signal_t *h, int signum, vclosure *c UV_CHECK_NULL(h,); UV_CHECK_NULL(c,); handle_register_callback((uv_handle_t*)h,c,0); - UV_CHECK_ERROR(uv_signal_start(h, on_signal, signum_hx2uv(signum)),handle_clear_callback((uv_handle_t *)h,0),); + UV_CHECK_ERROR(uv_signal_start(h, on_signal, uv_translate_to_sys_signal(signum)),handle_clear_callback((uv_handle_t *)h,0),); } DEFINE_PRIM(_VOID, signal_start_wrap, _HANDLE _I32 _FUN(_VOID,_I32)); @@ -1412,7 +1557,7 @@ HL_PRIM void HL_NAME(signal_start_oneshot_wrap)( uv_signal_t *h, int signum, vcl UV_CHECK_NULL(h,); UV_CHECK_NULL(c,); handle_register_callback((uv_handle_t*)h,c,0); - UV_CHECK_ERROR(uv_signal_start_oneshot(h, on_signal_oneshot, signum_hx2uv(signum)),handle_clear_callback((uv_handle_t *)h,0),); + UV_CHECK_ERROR(uv_signal_start_oneshot(h, on_signal_oneshot, uv_translate_to_sys_signal(signum)),handle_clear_callback((uv_handle_t *)h,0),); } DEFINE_PRIM(_VOID, signal_start_oneshot_wrap, _HANDLE _I32 _FUN(_VOID,_I32)); @@ -1424,7 +1569,7 @@ DEFINE_PRIM(_VOID, signal_stop_wrap, _HANDLE); HL_PRIM int HL_NAME(signal_get_sigNum_wrap)(uv_signal_t *h) { UV_CHECK_NULL(h,0); - return signum_uv2hx(h->signum); + return uv_translate_sys_signal(h->signum); } DEFINE_PRIM(_I32, signal_get_sigNum_wrap, _HANDLE); @@ -1681,31 +1826,11 @@ static void on_process_exit(uv_process_t *h, int64_t exit_status, int term_signa events_data *ev = UV_DATA(h); vclosure *c = ev ? ev->events[0] : NULL; if( c ) { - hl_call3(void, c, uv_process_t *, h, int64, exit_status, int, signum_uv2hx(term_signal)); + hl_call3(void, c, uv_process_t *, h, int64, exit_status, int, uv_translate_sys_signal(term_signal)); handle_clear_callback((uv_handle_t *)h, 0); } } -typedef struct { - hl_type *t; - int index; - int fd; -} stdio_fd; - -typedef struct { - hl_type *t; - int index; - uv_pipe_t *pipe; - int permissions; - vdynamic *nonBlock; -} stdio_pipe; - -typedef struct { - hl_type *t; - int index; - uv_stream_t *stream; -} stdio_stream; - HL_PRIM uv_process_t *HL_NAME(spawn_wrap)( uv_loop_t *loop, vstring *file, varray *args, vclosure *on_exit, varray *stdio, varray *env, vstring *cwd, vdynamic *uid, vdynamic *gid, vdynamic *detached, vdynamic *windowsVerbatimArguments, vdynamic *windowsHide, @@ -1816,12 +1941,12 @@ DEFINE_PRIM(_I32, process_pid, _HANDLE); HL_PRIM void HL_NAME(process_kill_wrap)( uv_process_t *h, int signum ) { UV_CHECK_NULL(h,); - UV_CHECK_ERROR(uv_process_kill(h, signum_hx2uv(signum)),,); + UV_CHECK_ERROR(uv_process_kill(h, uv_translate_to_sys_signal(signum)),,); } DEFINE_PRIM(_VOID, process_kill_wrap, _HANDLE _I32); HL_PRIM void HL_NAME(kill_wrap)( int pid, int signum ) { - UV_CHECK_ERROR(uv_kill(pid, signum_hx2uv(signum)),,); + UV_CHECK_ERROR(uv_kill(pid, uv_translate_to_sys_signal(signum)),,); } DEFINE_PRIM(_VOID, kill_wrap, _I32 _I32); diff --git a/other/uvgenerator/UV.hx.header b/other/uvgenerator/UV.hx.header index aa22b225b..c5cdc120d 100644 --- a/other/uvgenerator/UV.hx.header +++ b/other/uvgenerator/UV.hx.header @@ -36,6 +36,8 @@ import hl.uv.Loop; import hl.uv.File; import hl.uv.Tty; import hl.uv.Udp; +import hl.uv.Process; +import hl.uv.Signal; typedef UvUidT = Int; typedef UvGidT = Int; @@ -77,6 +79,7 @@ abstract UvAsyncTStar(UvHandleTStar) to UvHandleTStar {} abstract UvBufTArr(Abstract<"uv_buf_t_arr">) {} abstract CSockaddrStorageStar(Abstract<"sockaddr_storage_star">) {} +abstract UvStdioContainerTStar(Abstract<"uv_stdio_container_t_star">) {} //TODO: implement these private typedef UInt = Int; @@ -113,9 +116,13 @@ extern class UV { } static public function free(ptr:Pointer):Void; + static public function alloc_char_array(length:Int):Ref; + static public function free_char_array(a:Ref):Void; static public function bytes_to_pointer(bytes:Bytes):Pointer; static public function translate_uv_error(uvErrno:Int):UVError; static public function translate_to_uv_error(errno:Int):Int; + static public function translate_sys_signal(sigNum:Int):SigNum; + static public function translate_to_sys_signal(sigNum:SigNum):Int; static public function handle_to_pointer(data:UvHandleTStar):Pointer; static public function handle_set_data_with_gc(handle:UvHandleTStar, data:Handle):Void; static public function req_set_data_with_gc(req:UvReqTStar, data:Request):Void; @@ -134,7 +141,11 @@ extern class UV { static public function alloc_udp():UvUdpTStar; static public function alloc_udp_send():UvUdpSendTStar; static public function alloc_pipe():UvPipeTStar; - // static public function alloc_tty():UvTtyTStar; + static public function alloc_stdio_container(stdio:NativeArray, count:Int):UvStdioContainerTStar; //Dynamic should contain hl.uv.Process.ProcessStdio instances + static public function free_stdio_container(stdio:UvStdioContainerTStar):Void; + static public function alloc_process_options(file:Bytes, args:Ref, env:Ref, cwd:Bytes, flags:Int, stdio_count:Int, stdio:UvStdioContainerTStar, uid:UvUidT, gid:UvGidT):UvProcessOptionsTStar; + static public function free_process_options(options:UvProcessOptionsTStar):Void; + static public function alloc_process():UvProcessTStar; static public function alloc_getaddrinfo():UvGetaddrinfoTStar; static public function alloc_getnameinfo():UvGetnameinfoTStar; static public function alloc_addrinfo(flags:Int, family:AddressFamily, socktype:SocketType, protocol:Int):CAddrinfoStar; diff --git a/other/uvsample/ProcessSample.hx b/other/uvsample/ProcessSample.hx index 5341209ca..c409ea364 100644 --- a/other/uvsample/ProcessSample.hx +++ b/other/uvsample/ProcessSample.hx @@ -22,7 +22,9 @@ class ProcessSample { p.close(() -> Log.print('process closed')); }, } - var p = Process.spawn(Thread.current().events, cmd, [cmd], opt); + var args = [cmd]; + // args.push('non-existent-path'); + var p = Process.spawn(Thread.current().events, cmd, args, opt); Log.print('pid ${p.pid}'); // p.kill(SIGINT); // Process.killPid(p.pid, SIGINT); From e980b51a13341afce336de6f6fae5bb50a9913b7 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 27 Aug 2021 18:09:07 +0300 Subject: [PATCH 077/117] memory deallocations --- libs/uv/uv.c | 85 +++++----------------------------- other/uvgenerator/UV.hx.header | 16 +++---- 2 files changed, 20 insertions(+), 81 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index e716c6608..c3888a2c5 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -136,12 +136,6 @@ typedef struct sockaddr_storage uv_sockaddr_storage; } \ DEFINE_PRIM(hl_return, uv_name##_##field, hl_struct); -#define DEFINE_PRIM_TO_POINTER(hl_type,uv_name) \ - HL_PRIM void *HL_NAME(uv_name##_to_pointer)( uv_##uv_name##_t *v ) { \ - return v; \ - } \ - DEFINE_PRIM(_POINTER, uv_name##_to_pointer, hl_type); - #define DEFINE_PRIM_OF_POINTER(hl_type,uv_name) \ HL_PRIM uv_##uv_name##_t *HL_NAME(pointer_to_##uv_name)( void *ptr ) { \ return ptr; \ @@ -157,21 +151,6 @@ typedef struct sockaddr_storage uv_sockaddr_storage; h->data = new_data; \ } -HL_PRIM void HL_NAME(free)( void *ptr ) { - free(ptr); -} -DEFINE_PRIM(_VOID, free, _POINTER); - -HL_PRIM void *HL_NAME(bytes_to_pointer)( vbyte *bytes ) { - return bytes; -} -DEFINE_PRIM(_POINTER, bytes_to_pointer, _BYTES); - -HL_PRIM vbyte *HL_NAME(bytes_of_pointer)( void *ptr ) { - return ptr; -} -DEFINE_PRIM(_BYTES, bytes_of_pointer, _POINTER); - // Errors #define HL_UV_NOERR 0 @@ -443,51 +422,19 @@ HL_PRIM int HL_NAME(translate_to_uv_error)( int hl_errno ) { } DEFINE_PRIM(_I32, translate_to_uv_error, _I32); -// C arrays +// Various utils + +DEFINE_PRIM_FREE(_BYTES, bytes); HL_PRIM vbyte **HL_NAME(alloc_char_array)( int length ) { return malloc(sizeof(vbyte *) * length); } DEFINE_PRIM(_REF(_BYTES), alloc_char_array, _I32); -HL_PRIM void HL_NAME(free_char_array)( vbyte **a ) { - free(a); -} -DEFINE_PRIM(_VOID, free_char_array, _REF(_BYTES)); - -HL_PRIM void HL_NAME(print_char_array)( void **arr ) { - int i = -1; - while( arr[++i] ) { - char *item = arr[i]; - printf("index %d; str %s\n", i, item); - }; -} -DEFINE_PRIM(_VOID, print_char_array, _REF(_POINTER)); +DEFINE_PRIM_FREE(_REF(_BYTES), char_array); // Buf -// HL_PRIM void HL_NAME(check_bufs)( const uv_buf_t bufs[], int length ) { -// for(int i = 0; i < length; i++) { -// printf("len: %ld; base: %s\n", bufs[i].len, bufs[i].base); -// } -// } -// DEFINE_PRIM(_VOID, check_bufs, _REF(_BUF) _I32); - -// HL_PRIM uv_buf_t HL_NAME(buf_init_wrap)( vbyte *bytes, int length ) { -// printf("%d\n", length); -// uv_buf_t b = uv_buf_init((char *)bytes, length); -// printf("%ld\n", b.len); -// return b; -// } -// DEFINE_PRIM(_BUF, buf_init_wrap, _BYTES _U32); - -// HL_PRIM void HL_NAME(check_bufs)( const uv_buf_t bufs[], int length ) { -// for(int i = 0; i < length; i++) { -// printf("len: %ld; base: %s\n", bufs[i].len, bufs[i].base); -// } -// } -// DEFINE_PRIM(_VOID, check_bufs, _REF(_BUF) _I32); - HL_PRIM uv_buf_t *HL_NAME(alloc_buf)( vbyte *bytes, int length ) { uv_buf_t *buf = UV_ALLOC(uv_buf_t); buf->base = (char *)bytes; @@ -502,7 +449,7 @@ HL_PRIM void HL_NAME(buf_set)( uv_buf_t *buf, vbyte *bytes, int length ) { // TO } DEFINE_PRIM(_VOID, buf_set, _BUF_ARR _BYTES _I32); -DEFINE_PRIM_TO_POINTER(_BUF_ARR, buf); +DEFINE_PRIM_FREE(_BUF_ARR, buf); DEFINE_PRIM_UV_FIELD(_BYTES, vbyte *, _BUF_ARR, buf, base); DEFINE_PRIM_UV_FIELD(_U64, int64, _BUF_ARR, buf, len); @@ -527,10 +474,7 @@ typedef struct { #define _HANDLE_DATA _OBJ(_HANDLE _FUN(_VOID,_NO_ARG)) -DEFINE_PRIM_TO_POINTER(_HANDLE, handle); -DEFINE_PRIM_OF_POINTER(_HANDLE, handle); -DEFINE_PRIM_TO_POINTER(_HANDLE_DATA, handle_data); -DEFINE_PRIM_OF_POINTER(_HANDLE_DATA, handle_data); +DEFINE_PRIM_FREE(_HANDLE, handle); HL_PRIM void HL_NAME(handle_set_data_with_gc)( uv_handle_t *h, uv_handle_data_t *new_data ) { UV_SET_DATA(h, new_data); @@ -566,10 +510,7 @@ typedef struct { #define _REQ_DATA _OBJ(_REQ) -DEFINE_PRIM_TO_POINTER(_REQ,req); -DEFINE_PRIM_OF_POINTER(_REQ,req); -DEFINE_PRIM_TO_POINTER(_REQ_DATA,req_data); -DEFINE_PRIM_OF_POINTER(_REQ_DATA,req_data); +DEFINE_PRIM_FREE(_REQ,req); HL_PRIM void HL_NAME(req_set_data_with_gc)( uv_req_t *r, uv_req_data_t *new_data ) { UV_SET_DATA(r, new_data); @@ -623,10 +564,12 @@ static void on_uv_timer_cb( uv_timer_t *h ) { #define _RUN_MODE _I32 DEFINE_PRIM_ALLOC(_LOOP, loop); -DEFINE_PRIM_TO_POINTER(_LOOP, loop); +DEFINE_PRIM_FREE(_LOOP, loop); // SockAddr +DEFINE_PRIM_FREE(_SOCKADDR_STORAGE, sockaddr_storage); + HL_PRIM struct sockaddr_storage *HL_NAME(alloc_sockaddr_storage)() { return UV_ALLOC(struct sockaddr_storage); } @@ -637,11 +580,6 @@ HL_PRIM int HL_NAME(sockaddr_storage_size)() { } DEFINE_PRIM(_I32, sockaddr_storage_size, _NO_ARG); -HL_PRIM void *HL_NAME(sockaddr_storage_to_pointer)( struct sockaddr_storage *addr ) { - return addr; -} -DEFINE_PRIM(_POINTER, sockaddr_storage_to_pointer, _SOCKADDR_STORAGE); - HL_PRIM struct sockaddr *HL_NAME(sockaddr_of_storage)( struct sockaddr_storage *addr ) { return (struct sockaddr *)addr; } @@ -1005,6 +943,7 @@ static void on_uv_fs_cb( uv_fs_t *r ) { } DEFINE_PRIM_OF_POINTER(_DIR,dir); +DEFINE_PRIM_FREE(_DIR,dir); HL_PRIM void HL_NAME(dir_init)( uv_dir_t *dir, int num_entries ) { dir->nentries = num_entries; @@ -1020,7 +959,7 @@ DEFINE_PRIM(_DIRENT, dir_dirent, _DIR _I32); DEFINE_PRIM_UV_FIELD(_I32, int, _DIR, dir, nentries); DEFINE_PRIM_UV_FIELD(_BYTES, vbyte *, _DIRENT, dirent, name); DEFINE_PRIM_UV_FIELD(_I32, int, _DIRENT, dirent, type); -DEFINE_PRIM_TO_POINTER(_DIRENT, dirent); +DEFINE_PRIM_FREE(_DIRENT, dirent); // auto-generated libuv bindings diff --git a/other/uvgenerator/UV.hx.header b/other/uvgenerator/UV.hx.header index c5cdc120d..79ce19aed 100644 --- a/other/uvgenerator/UV.hx.header +++ b/other/uvgenerator/UV.hx.header @@ -115,27 +115,26 @@ extern class UV { return @:privateAccess String.fromUTF8(b); } - static public function free(ptr:Pointer):Void; static public function alloc_char_array(length:Int):Ref; static public function free_char_array(a:Ref):Void; - static public function bytes_to_pointer(bytes:Bytes):Pointer; + static public function free_bytes(bytes:Bytes):Void; static public function translate_uv_error(uvErrno:Int):UVError; static public function translate_to_uv_error(errno:Int):Int; static public function translate_sys_signal(sigNum:Int):SigNum; static public function translate_to_sys_signal(sigNum:SigNum):Int; - static public function handle_to_pointer(data:UvHandleTStar):Pointer; + static public function free_handle(data:UvHandleTStar):Void; static public function handle_set_data_with_gc(handle:UvHandleTStar, data:Handle):Void; static public function req_set_data_with_gc(req:UvReqTStar, data:Request):Void; - static public function req_to_pointer(req:UvReqTStar):Pointer; + static public function free_req(req:UvReqTStar):Void; static public function alloc_loop():UvLoopTStar; - static public function loop_to_pointer(req:UvLoopTStar):Pointer; + static public function free_loop(req:UvLoopTStar):Void; static public function alloc_async():UvAsyncTStar; static public function alloc_timer():UvTimerTStar; static public function alloc_check():UvCheckTStar; static public function alloc_tcp():UvTcpTStar; static public function alloc_sockaddr_storage():CSockaddrStorageStar; static public function sockaddr_storage_size():Int; - static public function sockaddr_storage_to_pointer(addr:CSockaddrStorageStar):Pointer; + static public function free_sockaddr_storage(addr:CSockaddrStorageStar):Void; static public function sockaddr_of_storage(addr:CSockaddrStorageStar):CSockaddrStar; static public function sockaddr_to_storage(addr:CSockaddrStar):CSockaddrStorageStar; static public function alloc_udp():UvUdpTStar; @@ -163,13 +162,14 @@ extern class UV { static public function nameinfo_flags_to_native(ai:NameInfoFlags):Int; static public function alloc_fs():UvFsTStar; static public function pointer_to_dir(req:Pointer):UvDirTStar; + static public function free_dir(dirent:UvDirTStar):Void; static public function dir_init(dir:UvDirTStar, num_entries:Int):Void; static public function dir_nentries(dir:UvDirTStar):Int; static public function dir_dirent(dir:UvDirTStar, index:Int):UvDirentTStar; - static public function dirent_to_pointer(dirent:UvDirentTStar):Pointer; + static public function free_dirent(dirent:UvDirentTStar):Void; static public function dirent_name(dirent:UvDirentTStar):Bytes; static public function dirent_type(dirent:UvDirentTStar):DirEntryType; - static public function buf_to_pointer(buf:UvBufTArr):Pointer; + static public function free_buf(buf:UvBufTArr):Void; static public function alloc_buf(bytes:Bytes, bytesLength:Int):UvBufTArr; static public function buf_set(buf:UvBufTArr, base:Bytes, length:Int):Void; static public function buf_base(buf:UvBufTArr):Bytes; From 9cf570f39f9ca7c1677479fc27bd6a7d5a8815e5 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 27 Aug 2021 18:57:16 +0300 Subject: [PATCH 078/117] tty --- libs/uv/uv.c | 3 +++ other/uvgenerator/UV.hx.header | 5 ++++- other/uvsample/CheckSample.hx | 3 ++- other/uvsample/TtySample.hx | 5 +++++ 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index c3888a2c5..61c55d3bd 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -961,6 +961,9 @@ DEFINE_PRIM_UV_FIELD(_BYTES, vbyte *, _DIRENT, dirent, name); DEFINE_PRIM_UV_FIELD(_I32, int, _DIRENT, dirent, type); DEFINE_PRIM_FREE(_DIRENT, dirent); +// Tty + +DEFINE_PRIM_ALLOC(_TTY, tty); // auto-generated libuv bindings #include "uv_generated.c" diff --git a/other/uvgenerator/UV.hx.header b/other/uvgenerator/UV.hx.header index 79ce19aed..96bdd5da6 100644 --- a/other/uvgenerator/UV.hx.header +++ b/other/uvgenerator/UV.hx.header @@ -51,7 +51,9 @@ typedef UvTtyVtermstateT = TtyVTermState; typedef UvTtyVtermstateTStar = Ref; typedef UvMembership = UdpMembership; -abstract UvFile(Int) {} +abstract UvFile(Int) { + @:allow(hl.uv) inline function new(fd:Int) this = fd; +} abstract UvUdpSendTStar(UvReqTStar) to UvReqTStar {} abstract UvWriteTStar(UvReqTStar) to UvReqTStar {} @@ -174,6 +176,7 @@ extern class UV { static public function buf_set(buf:UvBufTArr, base:Bytes, length:Int):Void; static public function buf_base(buf:UvBufTArr):Bytes; static public function buf_len(buf:UvBufTArr):U64; + static public function alloc_tty():UvTtyTStar; // Auto generated content : diff --git a/other/uvsample/CheckSample.hx b/other/uvsample/CheckSample.hx index b0073846a..62330bf5b 100644 --- a/other/uvsample/CheckSample.hx +++ b/other/uvsample/CheckSample.hx @@ -1,3 +1,4 @@ +import hl.I64; import hl.uv.Timer; import hl.uv.Check; import sys.thread.Thread; @@ -9,7 +10,7 @@ class CheckSample { timer.start(() -> { timer.stop(); timer.close(); - }, 10, 10); + }, I64.ofInt(10), I64.ofInt(10)); var check = Check.init(loop); check.start(() -> { diff --git a/other/uvsample/TtySample.hx b/other/uvsample/TtySample.hx index e59a05d80..0fddfc08a 100644 --- a/other/uvsample/TtySample.hx +++ b/other/uvsample/TtySample.hx @@ -15,6 +15,11 @@ class TtySample { tty.setMode(TTY_MODE_NORMAL); print('window size: ' + tty.getWinSize()); Tty.resetMode(); + // var state = Tty.getVTermState(); + // print('vterm state: ' + state); + // print('changing vterm state...'); + // Tty.setVTermState(state == TTY_SUPPORTED ? TTY_UNSUPPORTED : TTY_SUPPORTED); + // print('vterm state: ' + Tty.getVTermState()); tty.close(() -> print('Done')); } } \ No newline at end of file From 1b9982550e0690358ca77ab26e7d6f1a90798c7b Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Sat, 28 Aug 2021 00:28:42 +0300 Subject: [PATCH 079/117] version --- libs/uv/uv.c | 59 ++++++++++++---------------------- other/uvgenerator/UV.hx.header | 6 ++++ 2 files changed, 27 insertions(+), 38 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 61c55d3bd..e9d126985 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -965,6 +965,27 @@ DEFINE_PRIM_FREE(_DIRENT, dirent); DEFINE_PRIM_ALLOC(_TTY, tty); +// version + +#define DEFINE_PRIM_VERSION(name, value, hl_type, c_type) \ + HL_PRIM c_type HL_NAME(version_##name)() { \ + return (c_type)value; \ + } \ + DEFINE_PRIM(hl_type, version_##name, _NO_ARG); \ + +HL_PRIM vbyte *HL_NAME(version_string_wrap)() { + const char *v = uv_version_string(); + return hl_copy_bytes((vbyte *)v, strlen(v)); +} +DEFINE_PRIM(_BYTES, version_string_wrap, _NO_ARG); + +DEFINE_PRIM_VERSION(major, UV_VERSION_MAJOR, _I32, int); +DEFINE_PRIM_VERSION(minor, UV_VERSION_MINOR, _I32, int); +DEFINE_PRIM_VERSION(patch, UV_VERSION_PATCH, _I32, int); +DEFINE_PRIM_VERSION(hex, UV_VERSION_HEX, _I32, int); +DEFINE_PRIM_VERSION(is_release, UV_VERSION_IS_RELEASE, _BOOL, bool); +DEFINE_PRIM_VERSION(suffix, UV_VERSION_SUFFIX, _BYTES, vbyte *); + // auto-generated libuv bindings #include "uv_generated.c" @@ -3155,41 +3176,3 @@ HL_PRIM int HL_NAME(tty_get_vterm_state_wrap)() { } } DEFINE_PRIM(_I32, tty_get_vterm_state_wrap, _NO_ARG); - -// version - -HL_PRIM vbyte *HL_NAME(version_string_wrap)() { - const char *v = uv_version_string(); - return hl_copy_bytes((vbyte *)v, strlen(v)); -} -DEFINE_PRIM(_BYTES, version_string_wrap, _NO_ARG); - -HL_PRIM int HL_NAME(version_major)() { - return UV_VERSION_MAJOR; -} -DEFINE_PRIM(_I32, version_major, _NO_ARG); - -HL_PRIM int HL_NAME(version_minor)() { - return UV_VERSION_MINOR; -} -DEFINE_PRIM(_I32, version_minor, _NO_ARG); - -HL_PRIM int HL_NAME(version_patch)() { - return UV_VERSION_PATCH; -} -DEFINE_PRIM(_I32, version_patch, _NO_ARG); - -HL_PRIM bool HL_NAME(version_is_release)() { - return UV_VERSION_IS_RELEASE; -} -DEFINE_PRIM(_BOOL, version_is_release, _NO_ARG); - -HL_PRIM vbyte *HL_NAME(version_suffix)() { - return (vbyte *)UV_VERSION_SUFFIX; -} -DEFINE_PRIM(_BYTES, version_suffix, _NO_ARG); - -HL_PRIM int HL_NAME(version_hex)() { - return UV_VERSION_HEX; -} -DEFINE_PRIM(_I32, version_hex, _NO_ARG); \ No newline at end of file diff --git a/other/uvgenerator/UV.hx.header b/other/uvgenerator/UV.hx.header index 96bdd5da6..9c08a4695 100644 --- a/other/uvgenerator/UV.hx.header +++ b/other/uvgenerator/UV.hx.header @@ -177,6 +177,12 @@ extern class UV { static public function buf_base(buf:UvBufTArr):Bytes; static public function buf_len(buf:UvBufTArr):U64; static public function alloc_tty():UvTtyTStar; + static public function version_major():Int; + static public function version_minor():Int; + static public function version_patch():Int; + static public function version_hex():Int; + static public function version_suffix():Bytes; + static public function version_is_release():Bool; // Auto generated content : From 02b5eab1fed78c9801f20e3e7156335c35e3be0f Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Mon, 30 Aug 2021 18:48:18 +0300 Subject: [PATCH 080/117] fs_event --- libs/uv/uv.c | 17 ++++++++++++++--- other/uvgenerator/UV.hx.header | 14 ++++++++++++++ other/uvsample/FsEventSample.hx | 3 ++- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index e9d126985..59d8d1101 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -84,9 +84,6 @@ typedef struct sockaddr_storage uv_sockaddr_storage; static void on_uv_fs_poll_cb( uv_fs_poll_t *h, int status, const uv_stat_t *prev, const uv_stat_t *curr ) { } - static void on_uv_fs_event_cb( uv_fs_event_t *h, const char *filename, int events, int status ) { - } - static void on_uv_idle_cb( uv_idle_t *h ) { } @@ -965,6 +962,20 @@ DEFINE_PRIM_FREE(_DIRENT, dirent); DEFINE_PRIM_ALLOC(_TTY, tty); +// Fs event + +typedef struct { + HANDLE_DATA_FIELDS; + vclosure *onEvent; +} uv_fs_event_data_t; + +static void on_uv_fs_event_cb( uv_fs_event_t *h, const char *filename, int events, int status ) { + vclosure *c = DATA(uv_fs_event_data_t *, h)->onEvent; + hl_call3(void, c, int, status, vbyte *, (vbyte *)filename, int, events); +} + +DEFINE_PRIM_ALLOC(_FS_EVENT, fs_event); + // version #define DEFINE_PRIM_VERSION(name, value, hl_type, c_type) \ diff --git a/other/uvgenerator/UV.hx.header b/other/uvgenerator/UV.hx.header index 9c08a4695..5f1cacfd1 100644 --- a/other/uvgenerator/UV.hx.header +++ b/other/uvgenerator/UV.hx.header @@ -117,6 +117,19 @@ extern class UV { return @:privateAccess String.fromUTF8(b); } + extern static public inline function getName(fn:(buf:Bytes, size:Ref)->Int):String { + var size = I64.ofInt(256); + var buf = null; + var eNoBufs = UVError.UV_ENOBUFS.toNative(); + var result = eNoBufs; + while (result == eNoBufs) { + buf = new Bytes(size.toInt()); + result = fn(buf, Ref.make(size)); + } + result.resolve(); + return buf.fromUTF8(); + } + static public function alloc_char_array(length:Int):Ref; static public function free_char_array(a:Ref):Void; static public function free_bytes(bytes:Bytes):Void; @@ -163,6 +176,7 @@ extern class UV { static public function addrinfo_ai_next(ai:CAddrinfoStar):Null; static public function nameinfo_flags_to_native(ai:NameInfoFlags):Int; static public function alloc_fs():UvFsTStar; + static public function alloc_fs_event():UvFsEventTStar; static public function pointer_to_dir(req:Pointer):UvDirTStar; static public function free_dir(dirent:UvDirTStar):Void; static public function dir_init(dir:UvDirTStar, num_entries:Int):Void; diff --git a/other/uvsample/FsEventSample.hx b/other/uvsample/FsEventSample.hx index 36dc0162e..eb9084ca2 100644 --- a/other/uvsample/FsEventSample.hx +++ b/other/uvsample/FsEventSample.hx @@ -14,7 +14,8 @@ class FsEventSample { File.saveContent(path, 'Hello, world'); event.start(path, null, (e, path, events) -> switch e { case UV_NOERR: - Log.print('FS events $events on $path'); + Log.print('FS event monitors ${event.getPath()}'); + Log.print('FS event {change:${events.change}, rename:${events.rename}} on $path'); event.stop(); event.close(); case _: From 803637883eaded14c7152bf78573e80908886d66 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Mon, 30 Aug 2021 19:25:53 +0300 Subject: [PATCH 081/117] fs_poll --- libs/uv/uv.c | 47 +++++++++++++++++++++++++++++++--- other/uvgenerator/UV.hx.header | 20 +++++++++++++++ 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 59d8d1101..39bcc4f2a 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -57,6 +57,7 @@ #define _DIRENT _ABSTRACT(uv_dirent_t_star) #define _STAT _ABSTRACT(uv_stat_t_star) #define _STATFS _ABSTRACT(uv_statfs_t_star) +#define _TIMESPEC _ABSTRACT(uv_timespec_t_star) #define _RUSAGE _ABSTRACT(uv_rusage_t_star) #define _CPU_INFO _ABSTRACT(uv_cpu_info_t_star) #define _INTERFACE_ADDRESS _ABSTRACT(uv_interface_address_t_star) @@ -81,9 +82,6 @@ typedef struct sockaddr_storage uv_sockaddr_storage; // TODO { - static void on_uv_fs_poll_cb( uv_fs_poll_t *h, int status, const uv_stat_t *prev, const uv_stat_t *curr ) { - } - static void on_uv_idle_cb( uv_idle_t *h ) { } @@ -958,6 +956,33 @@ DEFINE_PRIM_UV_FIELD(_BYTES, vbyte *, _DIRENT, dirent, name); DEFINE_PRIM_UV_FIELD(_I32, int, _DIRENT, dirent, type); DEFINE_PRIM_FREE(_DIRENT, dirent); +DEFINE_PRIM_UV_FIELD(_U64, int64, _STAT, stat, st_dev); +DEFINE_PRIM_UV_FIELD(_U64, int64, _STAT, stat, st_mode); +DEFINE_PRIM_UV_FIELD(_U64, int64, _STAT, stat, st_nlink); +DEFINE_PRIM_UV_FIELD(_U64, int64, _STAT, stat, st_uid); +DEFINE_PRIM_UV_FIELD(_U64, int64, _STAT, stat, st_gid); +DEFINE_PRIM_UV_FIELD(_U64, int64, _STAT, stat, st_rdev); +DEFINE_PRIM_UV_FIELD(_U64, int64, _STAT, stat, st_ino); +DEFINE_PRIM_UV_FIELD(_U64, int64, _STAT, stat, st_size); +DEFINE_PRIM_UV_FIELD(_U64, int64, _STAT, stat, st_blksize); +DEFINE_PRIM_UV_FIELD(_U64, int64, _STAT, stat, st_blocks); +DEFINE_PRIM_UV_FIELD(_U64, int64, _STAT, stat, st_flags); +DEFINE_PRIM_UV_FIELD(_U64, int64, _STAT, stat, st_gen); + +#define DEFINE_PRIM_STAT_TIME(field) \ + HL_PRIM uv_timespec_t *HL_NAME(stat_##field)( uv_stat_t *stat ) { \ + return &stat->field; \ + } \ + DEFINE_PRIM(_TIMESPEC, stat_##field, _STAT); + +DEFINE_PRIM_STAT_TIME(st_atim); +DEFINE_PRIM_STAT_TIME(st_mtim); +DEFINE_PRIM_STAT_TIME(st_ctim); +DEFINE_PRIM_STAT_TIME(st_birthtim); + +DEFINE_PRIM_UV_FIELD(_I64, int64, _TIMESPEC, timespec, tv_sec); +DEFINE_PRIM_UV_FIELD(_I64, int64, _TIMESPEC, timespec, tv_nsec); + // Tty DEFINE_PRIM_ALLOC(_TTY, tty); @@ -976,6 +1001,20 @@ static void on_uv_fs_event_cb( uv_fs_event_t *h, const char *filename, int event DEFINE_PRIM_ALLOC(_FS_EVENT, fs_event); +// Fs poll + +typedef struct { + HANDLE_DATA_FIELDS; + vclosure *onChange; +} uv_fs_poll_data_t; + +static void on_uv_fs_poll_cb( uv_fs_poll_t *h, int status, const uv_stat_t *prev, const uv_stat_t *curr ) { + vclosure *c = DATA(uv_fs_poll_data_t *, h)->onChange; + hl_call3(void, c, int, status, const uv_stat_t *, prev, const uv_stat_t *, curr); +} + +DEFINE_PRIM_ALLOC(_FS_POLL, fs_poll); + // version #define DEFINE_PRIM_VERSION(name, value, hl_type, c_type) \ @@ -1005,7 +1044,7 @@ DEFINE_PRIM_VERSION(suffix, UV_VERSION_SUFFIX, _BYTES, vbyte *); // TODO: remove everything below #define _CALLB _FUN(_VOID,_NO_ARG) -#define _TIMESPEC _OBJ(_I64 _I64) +#define _TIMESPEC_OBJ _OBJ(_I64 _I64) #define EVT_CLOSE 1 diff --git a/other/uvgenerator/UV.hx.header b/other/uvgenerator/UV.hx.header index 5f1cacfd1..ade327393 100644 --- a/other/uvgenerator/UV.hx.header +++ b/other/uvgenerator/UV.hx.header @@ -82,6 +82,7 @@ abstract UvAsyncTStar(UvHandleTStar) to UvHandleTStar {} abstract UvBufTArr(Abstract<"uv_buf_t_arr">) {} abstract CSockaddrStorageStar(Abstract<"sockaddr_storage_star">) {} abstract UvStdioContainerTStar(Abstract<"uv_stdio_container_t_star">) {} +abstract UvTimespecTStar(Abstract<"uv_timespec_t_star">) {} //TODO: implement these private typedef UInt = Int; @@ -177,11 +178,30 @@ extern class UV { static public function nameinfo_flags_to_native(ai:NameInfoFlags):Int; static public function alloc_fs():UvFsTStar; static public function alloc_fs_event():UvFsEventTStar; + static public function alloc_fs_poll():UvFsPollTStar; static public function pointer_to_dir(req:Pointer):UvDirTStar; static public function free_dir(dirent:UvDirTStar):Void; static public function dir_init(dir:UvDirTStar, num_entries:Int):Void; static public function dir_nentries(dir:UvDirTStar):Int; static public function dir_dirent(dir:UvDirTStar, index:Int):UvDirentTStar; + static public function stat_st_dev(stat:UvStatTStar):U64; + static public function stat_st_mode(stat:UvStatTStar):U64; + static public function stat_st_nlink(stat:UvStatTStar):U64; + static public function stat_st_uid(stat:UvStatTStar):U64; + static public function stat_st_gid(stat:UvStatTStar):U64; + static public function stat_st_rdev(stat:UvStatTStar):U64; + static public function stat_st_ino(stat:UvStatTStar):U64; + static public function stat_st_size(stat:UvStatTStar):U64; + static public function stat_st_blksize(stat:UvStatTStar):U64; + static public function stat_st_blocks(stat:UvStatTStar):U64; + static public function stat_st_flags(stat:UvStatTStar):U64; + static public function stat_st_gen(stat:UvStatTStar):U64; + static public function stat_st_atim(stat:UvStatTStar):UvTimespecTStar; + static public function stat_st_mtim(stat:UvStatTStar):UvTimespecTStar; + static public function stat_st_ctim(stat:UvStatTStar):UvTimespecTStar; + static public function stat_st_birthtim(stat:UvStatTStar):UvTimespecTStar; + static public function timespec_tv_sec(times:UvTimespecTStar):I64; + static public function timespec_tv_nsec(times:UvTimespecTStar):I64; static public function free_dirent(dirent:UvDirentTStar):Void; static public function dirent_name(dirent:UvDirentTStar):Bytes; static public function dirent_type(dirent:UvDirentTStar):DirEntryType; From f36a44ef120f276fd14800f9d81742c21af0d389 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Mon, 30 Aug 2021 19:32:23 +0300 Subject: [PATCH 082/117] idle --- libs/uv/uv.c | 18 ++++++++++++++---- other/uvgenerator/UV.hx.header | 5 +++-- other/uvsample/IdleSample.hx | 22 ++++++++++++++++++++++ other/uvsample/UVSample.hx | 1 + 4 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 other/uvsample/IdleSample.hx diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 39bcc4f2a..99c2b9d58 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -81,10 +81,6 @@ typedef struct sockaddr_in6 uv_sockaddr_in6; typedef struct sockaddr_storage uv_sockaddr_storage; // TODO { - - static void on_uv_idle_cb( uv_idle_t *h ) { - } - static void on_uv_walk_cb( uv_handle_t* handle, void* arg ) { } @@ -540,6 +536,20 @@ static void on_uv_check_cb( uv_check_t *h ) { hl_call0(void, c); } +// Idle + +typedef struct { + HANDLE_DATA_FIELDS; + vclosure *onIdle; +} uv_idle_data_t; + +DEFINE_PRIM_ALLOC(_IDLE, idle); + +static void on_uv_idle_cb( uv_idle_t *h ) { + vclosure *c = DATA(uv_idle_data_t *, h)->onIdle; + hl_call0(void, c); +} + // Timer typedef struct { diff --git a/other/uvgenerator/UV.hx.header b/other/uvgenerator/UV.hx.header index ade327393..0538afe9f 100644 --- a/other/uvgenerator/UV.hx.header +++ b/other/uvgenerator/UV.hx.header @@ -85,8 +85,8 @@ abstract UvStdioContainerTStar(Abstract<"uv_stdio_container_t_star">) {} abstract UvTimespecTStar(Abstract<"uv_timespec_t_star">) {} //TODO: implement these -private typedef UInt = Int; -private typedef U64 = I64; +typedef UInt = Int; +typedef U64 = I64; /** Automatically generated bindings for libuv. @@ -147,6 +147,7 @@ extern class UV { static public function alloc_async():UvAsyncTStar; static public function alloc_timer():UvTimerTStar; static public function alloc_check():UvCheckTStar; + static public function alloc_idle():UvIdleTStar; static public function alloc_tcp():UvTcpTStar; static public function alloc_sockaddr_storage():CSockaddrStorageStar; static public function sockaddr_storage_size():Int; diff --git a/other/uvsample/IdleSample.hx b/other/uvsample/IdleSample.hx new file mode 100644 index 000000000..b5d04ecce --- /dev/null +++ b/other/uvsample/IdleSample.hx @@ -0,0 +1,22 @@ +import hl.I64; +import hl.uv.Timer; +import hl.uv.Idle; +import sys.thread.Thread; + +class IdleSample { + public static function main() { + var loop = Thread.current().events; + var timer = Timer.init(loop); + timer.start(() -> { + timer.stop(); + timer.close(); + }, I64.ofInt(10), I64.ofInt(10)); + + var idle = Idle.init(loop); + idle.start(() -> { + Log.print('Idle'); + idle.stop(); + idle.close(); + }); + } +} \ No newline at end of file diff --git a/other/uvsample/UVSample.hx b/other/uvsample/UVSample.hx index 050ee22a2..e29cb98ec 100644 --- a/other/uvsample/UVSample.hx +++ b/other/uvsample/UVSample.hx @@ -2,6 +2,7 @@ class UVSample { static function main() { run(); // CheckSample.main(); + // IdleSample.main(); // TcpSample.main(); // DnsSample.main(); // UdpSample.main(); From 830a9bc52afdc62790752c896443049487a12338 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Mon, 30 Aug 2021 19:36:54 +0300 Subject: [PATCH 083/117] Prepare --- libs/uv/uv.c | 17 ++++++++++++++--- other/uvgenerator/UV.hx.header | 1 + other/uvsample/PrepareSample.hx | 22 ++++++++++++++++++++++ other/uvsample/UVSample.hx | 1 + 4 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 other/uvsample/PrepareSample.hx diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 99c2b9d58..bd0b22818 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -87,9 +87,6 @@ typedef struct sockaddr_storage uv_sockaddr_storage; static void on_uv_random_cb( uv_random_t* r, int status, void* buf, size_t buflen ) { } - static void on_uv_prepare_cb( uv_prepare_t *h ) { - } - static void on_uv_signal_cb( uv_signal_t *h, int signum ) { } // } @@ -536,6 +533,20 @@ static void on_uv_check_cb( uv_check_t *h ) { hl_call0(void, c); } +// Prepare + +typedef struct { + HANDLE_DATA_FIELDS; + vclosure *onPrepare; +} uv_prepare_data_t; + +DEFINE_PRIM_ALLOC(_PREPARE, prepare); + +static void on_uv_prepare_cb( uv_prepare_t *h ) { + vclosure *c = DATA(uv_prepare_data_t *, h)->onPrepare; + hl_call0(void, c); +} + // Idle typedef struct { diff --git a/other/uvgenerator/UV.hx.header b/other/uvgenerator/UV.hx.header index 0538afe9f..582ce8fca 100644 --- a/other/uvgenerator/UV.hx.header +++ b/other/uvgenerator/UV.hx.header @@ -147,6 +147,7 @@ extern class UV { static public function alloc_async():UvAsyncTStar; static public function alloc_timer():UvTimerTStar; static public function alloc_check():UvCheckTStar; + static public function alloc_prepare():UvPrepareTStar; static public function alloc_idle():UvIdleTStar; static public function alloc_tcp():UvTcpTStar; static public function alloc_sockaddr_storage():CSockaddrStorageStar; diff --git a/other/uvsample/PrepareSample.hx b/other/uvsample/PrepareSample.hx new file mode 100644 index 000000000..88c39e440 --- /dev/null +++ b/other/uvsample/PrepareSample.hx @@ -0,0 +1,22 @@ +import hl.I64; +import hl.uv.Timer; +import hl.uv.Prepare; +import sys.thread.Thread; + +class PrepareSample { + public static function main() { + var loop = Thread.current().events; + var timer = Timer.init(loop); + timer.start(() -> { + timer.stop(); + timer.close(); + }, I64.ofInt(10), I64.ofInt(10)); + + var prepare = Prepare.init(loop); + prepare.start(() -> { + Log.print('Prepare'); + prepare.stop(); + prepare.close(); + }); + } +} \ No newline at end of file diff --git a/other/uvsample/UVSample.hx b/other/uvsample/UVSample.hx index e29cb98ec..30c7296c5 100644 --- a/other/uvsample/UVSample.hx +++ b/other/uvsample/UVSample.hx @@ -2,6 +2,7 @@ class UVSample { static function main() { run(); // CheckSample.main(); + // PrepareSample.main(); // IdleSample.main(); // TcpSample.main(); // DnsSample.main(); From b9c3fcd8d3617516ac2e489c940600eb8d0bd2ea Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Mon, 30 Aug 2021 20:28:02 +0300 Subject: [PATCH 084/117] minor refactoring --- libs/uv/uv.c | 60 +++++++++++++--------------------------------------- 1 file changed, 15 insertions(+), 45 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index bd0b22818..faaf306cc 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -448,13 +448,18 @@ DEFINE_PRIM_UV_FIELD(_U64, int64, _BUF_ARR, buf, len); uv_handle_t *_h; \ vclosure *onClose; +#define HANDLE_DATA_WITH_ALLOC_FIELDS \ + HANDLE_DATA_FIELDS; \ + vclosure *onAlloc; + typedef struct { HANDLE_DATA_FIELDS; } uv_handle_data_t; -#define HANDLE_DATA_WITH_ALLOC_FIELDS \ - HANDLE_DATA_FIELDS; \ - vclosure *onAlloc; +typedef struct { + HANDLE_DATA_FIELDS; + vclosure *callback; +} uv_handle_cb_data_t; typedef struct { HANDLE_DATA_WITH_ALLOC_FIELDS; @@ -507,71 +512,46 @@ DEFINE_PRIM(_VOID, req_set_data_with_gc, _REQ _REQ_DATA); // Async -typedef struct { - HANDLE_DATA_FIELDS; - vclosure *onSend; -} uv_async_data_t; - DEFINE_PRIM_ALLOC(_ASYNC, async); static void on_uv_async_cb( uv_async_t *h ) { - vclosure *c = DATA(uv_async_data_t *, h)->onSend; + vclosure *c = DATA(uv_handle_cb_data_t *, h)->callback; hl_call1(void, c, uv_async_t *, h); } // Check -typedef struct { - HANDLE_DATA_FIELDS; - vclosure *onCheck; -} uv_check_data_t; - DEFINE_PRIM_ALLOC(_CHECK, check); static void on_uv_check_cb( uv_check_t *h ) { - vclosure *c = DATA(uv_check_data_t *, h)->onCheck; + vclosure *c = DATA(uv_handle_cb_data_t *, h)->callback; hl_call0(void, c); } // Prepare -typedef struct { - HANDLE_DATA_FIELDS; - vclosure *onPrepare; -} uv_prepare_data_t; - DEFINE_PRIM_ALLOC(_PREPARE, prepare); static void on_uv_prepare_cb( uv_prepare_t *h ) { - vclosure *c = DATA(uv_prepare_data_t *, h)->onPrepare; + vclosure *c = DATA(uv_handle_cb_data_t *, h)->callback; hl_call0(void, c); } // Idle -typedef struct { - HANDLE_DATA_FIELDS; - vclosure *onIdle; -} uv_idle_data_t; - DEFINE_PRIM_ALLOC(_IDLE, idle); static void on_uv_idle_cb( uv_idle_t *h ) { - vclosure *c = DATA(uv_idle_data_t *, h)->onIdle; + vclosure *c = DATA(uv_handle_cb_data_t *, h)->callback; hl_call0(void, c); } // Timer -typedef struct { - HANDLE_DATA_FIELDS; - vclosure *onTick; -} uv_timer_data; - DEFINE_PRIM_ALLOC(_TIMER, timer); static void on_uv_timer_cb( uv_timer_t *h ) { - vclosure *c = DATA(uv_timer_data *, h)->onTick; + vclosure *c = DATA(uv_handle_cb_data_t *, h)->callback; hl_call0(void, c); } @@ -1010,13 +990,8 @@ DEFINE_PRIM_ALLOC(_TTY, tty); // Fs event -typedef struct { - HANDLE_DATA_FIELDS; - vclosure *onEvent; -} uv_fs_event_data_t; - static void on_uv_fs_event_cb( uv_fs_event_t *h, const char *filename, int events, int status ) { - vclosure *c = DATA(uv_fs_event_data_t *, h)->onEvent; + vclosure *c = DATA(uv_handle_cb_data_t *, h)->callback; hl_call3(void, c, int, status, vbyte *, (vbyte *)filename, int, events); } @@ -1024,13 +999,8 @@ DEFINE_PRIM_ALLOC(_FS_EVENT, fs_event); // Fs poll -typedef struct { - HANDLE_DATA_FIELDS; - vclosure *onChange; -} uv_fs_poll_data_t; - static void on_uv_fs_poll_cb( uv_fs_poll_t *h, int status, const uv_stat_t *prev, const uv_stat_t *curr ) { - vclosure *c = DATA(uv_fs_poll_data_t *, h)->onChange; + vclosure *c = DATA(uv_handle_cb_data_t *, h)->callback; hl_call3(void, c, int, status, const uv_stat_t *, prev, const uv_stat_t *, curr); } From d1e1dea3dc0384f756566d6a54d06aead81ebb1d Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Mon, 30 Aug 2021 22:09:13 +0300 Subject: [PATCH 085/117] signal --- libs/uv/uv.c | 14 +++++++++++--- other/uvgenerator/UV.hx.header | 2 ++ other/uvsample/SignalSample.hx | 15 +++++++++++++++ other/uvsample/UVSample.hx | 1 + 4 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 other/uvsample/SignalSample.hx diff --git a/libs/uv/uv.c b/libs/uv/uv.c index faaf306cc..0fe0eb9be 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -86,9 +86,6 @@ typedef struct sockaddr_storage uv_sockaddr_storage; static void on_uv_random_cb( uv_random_t* r, int status, void* buf, size_t buflen ) { } - - static void on_uv_signal_cb( uv_signal_t *h, int signum ) { - } // } #define UV_ALLOC(t) ((t*)malloc(sizeof(t))) @@ -1006,6 +1003,17 @@ static void on_uv_fs_poll_cb( uv_fs_poll_t *h, int status, const uv_stat_t *prev DEFINE_PRIM_ALLOC(_FS_POLL, fs_poll); +// Signal + +static void on_uv_signal_cb( uv_signal_t *h, int signum ) { + vclosure *c = DATA(uv_handle_cb_data_t *, h)->callback; + hl_call1(void, c, int, signum); +} + +DEFINE_PRIM_ALLOC(_SIGNAL, signal); +DEFINE_PRIM_UV_FIELD(_I32, int, _SIGNAL, signal, signum); + + // version #define DEFINE_PRIM_VERSION(name, value, hl_type, c_type) \ diff --git a/other/uvgenerator/UV.hx.header b/other/uvgenerator/UV.hx.header index 582ce8fca..2b490d566 100644 --- a/other/uvgenerator/UV.hx.header +++ b/other/uvgenerator/UV.hx.header @@ -213,6 +213,8 @@ extern class UV { static public function buf_base(buf:UvBufTArr):Bytes; static public function buf_len(buf:UvBufTArr):U64; static public function alloc_tty():UvTtyTStar; + static public function alloc_signal():UvSignalTStar; + static public function signal_signum(signal:UvSignalTStar):Int; static public function version_major():Int; static public function version_minor():Int; static public function version_patch():Int; diff --git a/other/uvsample/SignalSample.hx b/other/uvsample/SignalSample.hx new file mode 100644 index 000000000..7a1491913 --- /dev/null +++ b/other/uvsample/SignalSample.hx @@ -0,0 +1,15 @@ +import hl.uv.Signal; +import sys.thread.Thread; + +class SignalSample { + public static function main() { + var loop = Thread.current().events; + var signal = Signal.init(loop); + Log.print('SignalSample: waiting for Ctrl^C...'); + signal.start(SIGINT, () -> { + Log.print('SignalSample: got Ctrl^C'); + signal.stop(); + signal.close(); + }); + } +} \ No newline at end of file diff --git a/other/uvsample/UVSample.hx b/other/uvsample/UVSample.hx index 30c7296c5..ef8556c7d 100644 --- a/other/uvsample/UVSample.hx +++ b/other/uvsample/UVSample.hx @@ -15,6 +15,7 @@ class UVSample { // FsPollSample.main(); // MiscSample.main(); // TtySample.main(); + // SignalSample.main(); // VersionSample.main(); } From 3b5efe3f40c6235aa40e6eab4f90c0b0ab57044c Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Tue, 31 Aug 2021 12:40:55 +0300 Subject: [PATCH 086/117] wip misc --- libs/uv/uv.c | 66 +++++++++++++++++++++++++++----- libs/uv/uv_generated.c | 4 -- other/uvgenerator/UV.hx.header | 33 ++++++++++++++++ other/uvgenerator/UVGenerator.hx | 2 +- 4 files changed, 90 insertions(+), 15 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 0fe0eb9be..91cebf1c4 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -60,6 +60,7 @@ #define _TIMESPEC _ABSTRACT(uv_timespec_t_star) #define _RUSAGE _ABSTRACT(uv_rusage_t_star) #define _CPU_INFO _ABSTRACT(uv_cpu_info_t_star) +#define _CPU_TIMES _ABSTRACT(uv_cpu_times_t_star) #define _INTERFACE_ADDRESS _ABSTRACT(uv_interface_address_t_star) #define _PASSWD _ABSTRACT(uv_passwd_t_star) #define _UTSNAME _ABSTRACT(uv_utsname_t_star) @@ -75,6 +76,8 @@ #define _FS_TYPE _I32 #define _FS_EVENT _HANDLE +typedef struct uv_cpu_times_s uv_cpu_times_t; + typedef struct sockaddr uv_sockaddr; typedef struct sockaddr_in uv_sockaddr_in; typedef struct sockaddr_in6 uv_sockaddr_in6; @@ -121,6 +124,13 @@ typedef struct sockaddr_storage uv_sockaddr_storage; } \ DEFINE_PRIM(hl_return, uv_name##_##field, hl_struct); +#define DEFINE_PRIM_UV_FIELD_REF(hl_return,c_return,hl_struct,uv_name,field) \ + HL_PRIM c_return HL_NAME(uv_name##_##field)( uv_##uv_name##_t *s ) { \ + return &s->field; \ + } \ + DEFINE_PRIM(hl_return, uv_name##_##field, hl_struct); + + #define DEFINE_PRIM_OF_POINTER(hl_type,uv_name) \ HL_PRIM uv_##uv_name##_t *HL_NAME(pointer_to_##uv_name)( void *ptr ) { \ return ptr; \ @@ -967,16 +977,10 @@ DEFINE_PRIM_UV_FIELD(_U64, int64, _STAT, stat, st_blocks); DEFINE_PRIM_UV_FIELD(_U64, int64, _STAT, stat, st_flags); DEFINE_PRIM_UV_FIELD(_U64, int64, _STAT, stat, st_gen); -#define DEFINE_PRIM_STAT_TIME(field) \ - HL_PRIM uv_timespec_t *HL_NAME(stat_##field)( uv_stat_t *stat ) { \ - return &stat->field; \ - } \ - DEFINE_PRIM(_TIMESPEC, stat_##field, _STAT); - -DEFINE_PRIM_STAT_TIME(st_atim); -DEFINE_PRIM_STAT_TIME(st_mtim); -DEFINE_PRIM_STAT_TIME(st_ctim); -DEFINE_PRIM_STAT_TIME(st_birthtim); +DEFINE_PRIM_UV_FIELD_REF(_TIMESPEC, uv_timespec_t *, _STAT, stat, st_atim); +DEFINE_PRIM_UV_FIELD_REF(_TIMESPEC, uv_timespec_t *, _STAT, stat, st_mtim); +DEFINE_PRIM_UV_FIELD_REF(_TIMESPEC, uv_timespec_t *, _STAT, stat, st_ctim); +DEFINE_PRIM_UV_FIELD_REF(_TIMESPEC, uv_timespec_t *, _STAT, stat, st_birthtim); DEFINE_PRIM_UV_FIELD(_I64, int64, _TIMESPEC, timespec, tv_sec); DEFINE_PRIM_UV_FIELD(_I64, int64, _TIMESPEC, timespec, tv_nsec); @@ -1035,6 +1039,48 @@ DEFINE_PRIM_VERSION(hex, UV_VERSION_HEX, _I32, int); DEFINE_PRIM_VERSION(is_release, UV_VERSION_IS_RELEASE, _BOOL, bool); DEFINE_PRIM_VERSION(suffix, UV_VERSION_SUFFIX, _BYTES, vbyte *); +// Misc + +DEFINE_PRIM_ALLOC(_RUSAGE, rusage); +DEFINE_PRIM_ALLOC(_TIMEVAL64, timeval64); +DEFINE_PRIM_ALLOC(_CPU_INFO, cpu_info); + +DEFINE_PRIM_FREE(_RUSAGE, rusage); +DEFINE_PRIM_FREE(_TIMEVAL64, timeval64); + +DEFINE_PRIM_UV_FIELD(_I64, int64, _TIMEVAL, timeval, tv_sec); +DEFINE_PRIM_UV_FIELD(_I64, int64, _TIMEVAL, timeval, tv_usec); +DEFINE_PRIM_UV_FIELD(_I64, int64, _TIMEVAL64, timeval64, tv_sec); +DEFINE_PRIM_UV_FIELD(_I32, int, _TIMEVAL64, timeval64, tv_usec); + +DEFINE_PRIM_UV_FIELD_REF(_TIMEVAL, uv_timeval_t *, _RUSAGE, rusage, ru_utime); +DEFINE_PRIM_UV_FIELD_REF(_TIMEVAL, uv_timeval_t *, _RUSAGE, rusage, ru_stime); +DEFINE_PRIM_UV_FIELD(_U64, int64, _RUSAGE, rusage, ru_maxrss); +DEFINE_PRIM_UV_FIELD(_U64, int64, _RUSAGE, rusage, ru_ixrss); +DEFINE_PRIM_UV_FIELD(_U64, int64, _RUSAGE, rusage, ru_idrss); +DEFINE_PRIM_UV_FIELD(_U64, int64, _RUSAGE, rusage, ru_isrss); +DEFINE_PRIM_UV_FIELD(_U64, int64, _RUSAGE, rusage, ru_minflt); +DEFINE_PRIM_UV_FIELD(_U64, int64, _RUSAGE, rusage, ru_majflt); +DEFINE_PRIM_UV_FIELD(_U64, int64, _RUSAGE, rusage, ru_nswap); +DEFINE_PRIM_UV_FIELD(_U64, int64, _RUSAGE, rusage, ru_inblock); +DEFINE_PRIM_UV_FIELD(_U64, int64, _RUSAGE, rusage, ru_oublock); +DEFINE_PRIM_UV_FIELD(_U64, int64, _RUSAGE, rusage, ru_msgsnd); +DEFINE_PRIM_UV_FIELD(_U64, int64, _RUSAGE, rusage, ru_msgrcv); +DEFINE_PRIM_UV_FIELD(_U64, int64, _RUSAGE, rusage, ru_nsignals); +DEFINE_PRIM_UV_FIELD(_U64, int64, _RUSAGE, rusage, ru_nvcsw); +DEFINE_PRIM_UV_FIELD(_U64, int64, _RUSAGE, rusage, ru_nivcsw); + +DEFINE_PRIM_UV_FIELD(_BYTES, vbyte *, _CPU_INFO, cpu_info, model); +DEFINE_PRIM_UV_FIELD(_I32, int, _CPU_INFO, cpu_info, speed); +DEFINE_PRIM_UV_FIELD_REF(_CPU_TIMES, uv_cpu_times_t *, _CPU_INFO, cpu_info, cpu_times); + +DEFINE_PRIM_UV_FIELD(_U64, int64, _CPU_TIMES, cpu_times, user); +DEFINE_PRIM_UV_FIELD(_U64, int64, _CPU_TIMES, cpu_times, nice); +DEFINE_PRIM_UV_FIELD(_U64, int64, _CPU_TIMES, cpu_times, sys); +DEFINE_PRIM_UV_FIELD(_U64, int64, _CPU_TIMES, cpu_times, idle); +DEFINE_PRIM_UV_FIELD(_U64, int64, _CPU_TIMES, cpu_times, irq); + + // auto-generated libuv bindings #include "uv_generated.c" diff --git a/libs/uv/uv_generated.c b/libs/uv/uv_generated.c index 09766676b..597249918 100644 --- a/libs/uv/uv_generated.c +++ b/libs/uv/uv_generated.c @@ -361,10 +361,6 @@ DEFINE_PRIM(_I32, ip4_name, _SOCKADDR_IN _BYTES _U64); DEFINE_PRIM(_I32, ip6_name, _SOCKADDR_IN6 _BYTES _U64); -DEFINE_PRIM(_I32, inet_ntop, _I32 _POINTER _BYTES _U64); - -DEFINE_PRIM(_I32, inet_pton, _I32 _BYTES _POINTER); - DEFINE_PRIM(_I32, if_indextoname, _U32 _BYTES _REF(_I64)); DEFINE_PRIM(_I32, if_indextoiid, _U32 _BYTES _REF(_I64)); diff --git a/other/uvgenerator/UV.hx.header b/other/uvgenerator/UV.hx.header index 2b490d566..e91edcf0b 100644 --- a/other/uvgenerator/UV.hx.header +++ b/other/uvgenerator/UV.hx.header @@ -83,6 +83,8 @@ abstract UvBufTArr(Abstract<"uv_buf_t_arr">) {} abstract CSockaddrStorageStar(Abstract<"sockaddr_storage_star">) {} abstract UvStdioContainerTStar(Abstract<"uv_stdio_container_t_star">) {} abstract UvTimespecTStar(Abstract<"uv_timespec_t_star">) {} +abstract UvTimevalTStar(Abstract<"uv_timeval_t_star">) {} +abstract UvCpuTimesTStar(Abstract<"uv_cpu_times_t_star">) {} //TODO: implement these typedef UInt = Int; @@ -221,6 +223,37 @@ extern class UV { static public function version_hex():Int; static public function version_suffix():Bytes; static public function version_is_release():Bool; + static public function alloc_rusage():UvRusageTStar; + static public function alloc_timeval64():UvTimeval64TStar; + static public function alloc_cpuinfo():UvCpuInfoTStar; + static public function timeval_tv_sec(timeval:UvTimevalTStar):I64; + static public function timeval_tv_usec(timeval:UvTimevalTStar):I64; + static public function timeval64_tv_sec(timeval:UvTimeval64TStar):I64; + static public function timeval64_tv_usec(timeval:UvTimeval64TStar):Int; + static public function rusage_ru_utime(rusage:UvRusageTStar):UvTimevalTStar; + static public function rusage_ru_stime(rusage:UvRusageTStar):UvTimevalTStar; + static public function rusage_ru_maxrss(rusage:UvRusageTStar):U64; + static public function rusage_ru_ixrss(rusage:UvRusageTStar):U64; + static public function rusage_ru_idrss(rusage:UvRusageTStar):U64; + static public function rusage_ru_isrss(rusage:UvRusageTStar):U64; + static public function rusage_ru_minflt(rusage:UvRusageTStar):U64; + static public function rusage_ru_majflt(rusage:UvRusageTStar):U64; + static public function rusage_ru_nswap(rusage:UvRusageTStar):U64; + static public function rusage_ru_inblock(rusage:UvRusageTStar):U64; + static public function rusage_ru_oublock(rusage:UvRusageTStar):U64; + static public function rusage_ru_msgsnd(rusage:UvRusageTStar):U64; + static public function rusage_ru_msgrcv(rusage:UvRusageTStar):U64; + static public function rusage_ru_nsignals(rusage:UvRusageTStar):U64; + static public function rusage_ru_nvcsw(rusage:UvRusageTStar):U64; + static public function rusage_ru_nivcsw(rusage:UvRusageTStar):U64; + static public function cpu_info_model(cpu_info:UvCpuInfoTStar):Bytes; + static public function cpu_info_speed(cpu_info:UvCpuInfoTStar):Int; + static public function cpu_info_cpu_times(cpu_info:UvCpuInfoTStar):UvCpuTimesTStar; + static public function cpu_times_user(cpu_times:UvCpuTimesTStar):U64; + static public function cpu_times_nice(cpu_times:UvCpuTimesTStar):U64; + static public function cpu_times_sys(cpu_times:UvCpuTimesTStar):U64; + static public function cpu_times_idle(cpu_times:UvCpuTimesTStar):U64; + static public function cpu_times_irq(cpu_times:UvCpuTimesTStar):U64; // Auto generated content : diff --git a/other/uvgenerator/UVGenerator.hx b/other/uvgenerator/UVGenerator.hx index e44efcfe0..c939b061d 100644 --- a/other/uvgenerator/UVGenerator.hx +++ b/other/uvgenerator/UVGenerator.hx @@ -32,7 +32,7 @@ class UVGenerator { 'uv_fileno', 'uv_open_osfhandle', 'uv_print_all_handles', 'uv_print_active_handles', 'uv_os_environ', 'uv_os_free_environ', 'uv_setup_args', 'uv_get_process_title', 'uv_set_process_title', 'uv_tcp_open', 'uv_udp_open', 'uv_socketpair', 'uv_buf_init', - 'uv_loop_configure']; // TODO: don't skip uv_loop_configure + 'uv_inet_ntop', 'uv_inet_pton', 'uv_loop_configure']; // TODO: don't skip uv_loop_configure static final allowNoCallback = ['uv_fs_cb']; static final predefinedHxTypes = new Map(); From c1e57f9961400f13461e6f48c96ede9524da061b Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Tue, 31 Aug 2021 17:14:15 +0300 Subject: [PATCH 087/117] automatically generate bindings for structs --- libs/uv/uv.c | 48 ----- libs/uv/uv_generated.c | 330 ++++++++----------------------- other/uvgenerator/UV.hx.header | 47 +---- other/uvgenerator/UVGenerator.hx | 141 +++++++++++-- 4 files changed, 211 insertions(+), 355 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 91cebf1c4..0e3acf819 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -960,31 +960,8 @@ HL_PRIM uv_dirent_t *HL_NAME(dir_dirent)( uv_dir_t *dir, int index ) { DEFINE_PRIM(_DIRENT, dir_dirent, _DIR _I32); DEFINE_PRIM_UV_FIELD(_I32, int, _DIR, dir, nentries); -DEFINE_PRIM_UV_FIELD(_BYTES, vbyte *, _DIRENT, dirent, name); -DEFINE_PRIM_UV_FIELD(_I32, int, _DIRENT, dirent, type); DEFINE_PRIM_FREE(_DIRENT, dirent); -DEFINE_PRIM_UV_FIELD(_U64, int64, _STAT, stat, st_dev); -DEFINE_PRIM_UV_FIELD(_U64, int64, _STAT, stat, st_mode); -DEFINE_PRIM_UV_FIELD(_U64, int64, _STAT, stat, st_nlink); -DEFINE_PRIM_UV_FIELD(_U64, int64, _STAT, stat, st_uid); -DEFINE_PRIM_UV_FIELD(_U64, int64, _STAT, stat, st_gid); -DEFINE_PRIM_UV_FIELD(_U64, int64, _STAT, stat, st_rdev); -DEFINE_PRIM_UV_FIELD(_U64, int64, _STAT, stat, st_ino); -DEFINE_PRIM_UV_FIELD(_U64, int64, _STAT, stat, st_size); -DEFINE_PRIM_UV_FIELD(_U64, int64, _STAT, stat, st_blksize); -DEFINE_PRIM_UV_FIELD(_U64, int64, _STAT, stat, st_blocks); -DEFINE_PRIM_UV_FIELD(_U64, int64, _STAT, stat, st_flags); -DEFINE_PRIM_UV_FIELD(_U64, int64, _STAT, stat, st_gen); - -DEFINE_PRIM_UV_FIELD_REF(_TIMESPEC, uv_timespec_t *, _STAT, stat, st_atim); -DEFINE_PRIM_UV_FIELD_REF(_TIMESPEC, uv_timespec_t *, _STAT, stat, st_mtim); -DEFINE_PRIM_UV_FIELD_REF(_TIMESPEC, uv_timespec_t *, _STAT, stat, st_ctim); -DEFINE_PRIM_UV_FIELD_REF(_TIMESPEC, uv_timespec_t *, _STAT, stat, st_birthtim); - -DEFINE_PRIM_UV_FIELD(_I64, int64, _TIMESPEC, timespec, tv_sec); -DEFINE_PRIM_UV_FIELD(_I64, int64, _TIMESPEC, timespec, tv_nsec); - // Tty DEFINE_PRIM_ALLOC(_TTY, tty); @@ -1017,7 +994,6 @@ static void on_uv_signal_cb( uv_signal_t *h, int signum ) { DEFINE_PRIM_ALLOC(_SIGNAL, signal); DEFINE_PRIM_UV_FIELD(_I32, int, _SIGNAL, signal, signum); - // version #define DEFINE_PRIM_VERSION(name, value, hl_type, c_type) \ @@ -1041,35 +1017,11 @@ DEFINE_PRIM_VERSION(suffix, UV_VERSION_SUFFIX, _BYTES, vbyte *); // Misc -DEFINE_PRIM_ALLOC(_RUSAGE, rusage); -DEFINE_PRIM_ALLOC(_TIMEVAL64, timeval64); DEFINE_PRIM_ALLOC(_CPU_INFO, cpu_info); DEFINE_PRIM_FREE(_RUSAGE, rusage); DEFINE_PRIM_FREE(_TIMEVAL64, timeval64); -DEFINE_PRIM_UV_FIELD(_I64, int64, _TIMEVAL, timeval, tv_sec); -DEFINE_PRIM_UV_FIELD(_I64, int64, _TIMEVAL, timeval, tv_usec); -DEFINE_PRIM_UV_FIELD(_I64, int64, _TIMEVAL64, timeval64, tv_sec); -DEFINE_PRIM_UV_FIELD(_I32, int, _TIMEVAL64, timeval64, tv_usec); - -DEFINE_PRIM_UV_FIELD_REF(_TIMEVAL, uv_timeval_t *, _RUSAGE, rusage, ru_utime); -DEFINE_PRIM_UV_FIELD_REF(_TIMEVAL, uv_timeval_t *, _RUSAGE, rusage, ru_stime); -DEFINE_PRIM_UV_FIELD(_U64, int64, _RUSAGE, rusage, ru_maxrss); -DEFINE_PRIM_UV_FIELD(_U64, int64, _RUSAGE, rusage, ru_ixrss); -DEFINE_PRIM_UV_FIELD(_U64, int64, _RUSAGE, rusage, ru_idrss); -DEFINE_PRIM_UV_FIELD(_U64, int64, _RUSAGE, rusage, ru_isrss); -DEFINE_PRIM_UV_FIELD(_U64, int64, _RUSAGE, rusage, ru_minflt); -DEFINE_PRIM_UV_FIELD(_U64, int64, _RUSAGE, rusage, ru_majflt); -DEFINE_PRIM_UV_FIELD(_U64, int64, _RUSAGE, rusage, ru_nswap); -DEFINE_PRIM_UV_FIELD(_U64, int64, _RUSAGE, rusage, ru_inblock); -DEFINE_PRIM_UV_FIELD(_U64, int64, _RUSAGE, rusage, ru_oublock); -DEFINE_PRIM_UV_FIELD(_U64, int64, _RUSAGE, rusage, ru_msgsnd); -DEFINE_PRIM_UV_FIELD(_U64, int64, _RUSAGE, rusage, ru_msgrcv); -DEFINE_PRIM_UV_FIELD(_U64, int64, _RUSAGE, rusage, ru_nsignals); -DEFINE_PRIM_UV_FIELD(_U64, int64, _RUSAGE, rusage, ru_nvcsw); -DEFINE_PRIM_UV_FIELD(_U64, int64, _RUSAGE, rusage, ru_nivcsw); - DEFINE_PRIM_UV_FIELD(_BYTES, vbyte *, _CPU_INFO, cpu_info, model); DEFINE_PRIM_UV_FIELD(_I32, int, _CPU_INFO, cpu_info, speed); DEFINE_PRIM_UV_FIELD_REF(_CPU_TIMES, uv_cpu_times_t *, _CPU_INFO, cpu_info, cpu_times); diff --git a/libs/uv/uv_generated.c b/libs/uv/uv_generated.c index 597249918..9bebb2c8f 100644 --- a/libs/uv/uv_generated.c +++ b/libs/uv/uv_generated.c @@ -2,627 +2,463 @@ HL_PRIM int HL_NAME(async_init_with_cb)( uv_loop_t* loop, uv_async_t* async ) { return uv_async_init(loop, async, on_uv_async_cb); } DEFINE_PRIM(_I32, async_init_with_cb, _LOOP _ASYNC); - DEFINE_PRIM(_I32, async_send, _ASYNC); - DEFINE_PRIM(_I32, check_init, _LOOP _CHECK); - HL_PRIM int HL_NAME(check_start_with_cb)( uv_check_t* check ) { return uv_check_start(check, on_uv_check_cb); } DEFINE_PRIM(_I32, check_start_with_cb, _CHECK); - DEFINE_PRIM(_I32, check_stop, _CHECK); - HL_PRIM int HL_NAME(getaddrinfo_with_cb)( uv_loop_t* loop, uv_getaddrinfo_t* req, const char* node, const char* service, const struct addrinfo* hints ) { return uv_getaddrinfo(loop, req, on_uv_getaddrinfo_cb, node, service, hints); } DEFINE_PRIM(_I32, getaddrinfo_with_cb, _LOOP _GETADDRINFO _BYTES _BYTES _ADDRINFO); - DEFINE_PRIM(_VOID, freeaddrinfo, _ADDRINFO); - HL_PRIM int HL_NAME(getnameinfo_with_cb)( uv_loop_t* loop, uv_getnameinfo_t* req, const struct sockaddr* addr, int flags ) { return uv_getnameinfo(loop, req, on_uv_getnameinfo_cb, addr, flags); } DEFINE_PRIM(_I32, getnameinfo_with_cb, _LOOP _GETNAMEINFO _SOCKADDR _I32); - DEFINE_PRIM(_BYTES, strerror, _I32); - DEFINE_PRIM(_BYTES, strerror_r, _I32 _BYTES _U64); - DEFINE_PRIM(_BYTES, err_name, _I32); - DEFINE_PRIM(_BYTES, err_name_r, _I32 _BYTES _U64); - DEFINE_PRIM(_I32, translate_sys_error, _I32); - +DEFINE_PRIM_UV_FIELD(_I64, long, _TIMESPEC, timespec, tv_sec); +DEFINE_PRIM_UV_FIELD(_I64, long, _TIMESPEC, timespec, tv_nsec); +DEFINE_PRIM_ALLOC(_TIMESPEC, timespec); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _STAT, stat, st_dev); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _STAT, stat, st_mode); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _STAT, stat, st_nlink); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _STAT, stat, st_uid); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _STAT, stat, st_gid); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _STAT, stat, st_rdev); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _STAT, stat, st_ino); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _STAT, stat, st_size); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _STAT, stat, st_blksize); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _STAT, stat, st_blocks); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _STAT, stat, st_flags); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _STAT, stat, st_gen); +DEFINE_PRIM_UV_FIELD_REF(_TIMESPEC, uv_timespec_t *, _STAT, stat, st_atim); +DEFINE_PRIM_UV_FIELD_REF(_TIMESPEC, uv_timespec_t *, _STAT, stat, st_mtim); +DEFINE_PRIM_UV_FIELD_REF(_TIMESPEC, uv_timespec_t *, _STAT, stat, st_ctim); +DEFINE_PRIM_UV_FIELD_REF(_TIMESPEC, uv_timespec_t *, _STAT, stat, st_birthtim); +DEFINE_PRIM_ALLOC(_STAT, stat); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _STATFS, statfs, f_type); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _STATFS, statfs, f_bsize); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _STATFS, statfs, f_blocks); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _STATFS, statfs, f_bfree); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _STATFS, statfs, f_bavail); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _STATFS, statfs, f_files); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _STATFS, statfs, f_ffree); +DEFINE_PRIM_ALLOC(_STATFS, statfs); +DEFINE_PRIM_UV_FIELD(_BYTES, const char*, _DIRENT, dirent, name); +DEFINE_PRIM_UV_FIELD(_I32, uv_dirent_type_t, _DIRENT, dirent, type); +DEFINE_PRIM_ALLOC(_DIRENT, dirent); DEFINE_PRIM(_VOID, fs_req_cleanup, _FS); - HL_PRIM int HL_NAME(fs_close_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { return uv_fs_close(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_close_with_cb, _LOOP _FS _FILE _BOOL); - HL_PRIM int HL_NAME(fs_open_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, bool use_uv_fs_cb ) { return uv_fs_open(loop, req, path, flags, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_open_with_cb, _LOOP _FS _BYTES _I32 _I32 _BOOL); - HL_PRIM int HL_NAME(fs_read_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, bool use_uv_fs_cb ) { return uv_fs_read(loop, req, file, bufs, nbufs, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_read_with_cb, _LOOP _FS _FILE _BUF_ARR _U32 _I64 _BOOL); - HL_PRIM int HL_NAME(fs_unlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { return uv_fs_unlink(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_unlink_with_cb, _LOOP _FS _BYTES _BOOL); - HL_PRIM int HL_NAME(fs_write_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, bool use_uv_fs_cb ) { return uv_fs_write(loop, req, file, bufs, nbufs, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_write_with_cb, _LOOP _FS _FILE _BUF_ARR _U32 _I64 _BOOL); - HL_PRIM int HL_NAME(fs_mkdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { return uv_fs_mkdir(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_mkdir_with_cb, _LOOP _FS _BYTES _I32 _BOOL); - HL_PRIM int HL_NAME(fs_mkdtemp_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* tpl, bool use_uv_fs_cb ) { return uv_fs_mkdtemp(loop, req, tpl, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_mkdtemp_with_cb, _LOOP _FS _BYTES _BOOL); - HL_PRIM int HL_NAME(fs_mkstemp_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* tpl, bool use_uv_fs_cb ) { return uv_fs_mkstemp(loop, req, tpl, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_mkstemp_with_cb, _LOOP _FS _BYTES _BOOL); - HL_PRIM int HL_NAME(fs_rmdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { return uv_fs_rmdir(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_rmdir_with_cb, _LOOP _FS _BYTES _BOOL); - HL_PRIM int HL_NAME(fs_opendir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { return uv_fs_opendir(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_opendir_with_cb, _LOOP _FS _BYTES _BOOL); - HL_PRIM int HL_NAME(fs_closedir_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, bool use_uv_fs_cb ) { return uv_fs_closedir(loop, req, dir, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_closedir_with_cb, _LOOP _FS _DIR _BOOL); - HL_PRIM int HL_NAME(fs_readdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, bool use_uv_fs_cb ) { return uv_fs_readdir(loop, req, dir, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_readdir_with_cb, _LOOP _FS _DIR _BOOL); - HL_PRIM int HL_NAME(fs_scandir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, bool use_uv_fs_cb ) { return uv_fs_scandir(loop, req, path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_scandir_with_cb, _LOOP _FS _BYTES _I32 _BOOL); - DEFINE_PRIM(_I32, fs_scandir_next, _FS _DIRENT); - HL_PRIM int HL_NAME(fs_stat_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { return uv_fs_stat(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_stat_with_cb, _LOOP _FS _BYTES _BOOL); - HL_PRIM int HL_NAME(fs_fstat_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { return uv_fs_fstat(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_fstat_with_cb, _LOOP _FS _FILE _BOOL); - HL_PRIM int HL_NAME(fs_lstat_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { return uv_fs_lstat(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_lstat_with_cb, _LOOP _FS _BYTES _BOOL); - HL_PRIM int HL_NAME(fs_statfs_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { return uv_fs_statfs(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_statfs_with_cb, _LOOP _FS _BYTES _BOOL); - HL_PRIM int HL_NAME(fs_rename_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, bool use_uv_fs_cb ) { return uv_fs_rename(loop, req, path, new_path, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_rename_with_cb, _LOOP _FS _BYTES _BYTES _BOOL); - HL_PRIM int HL_NAME(fs_fsync_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { return uv_fs_fsync(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_fsync_with_cb, _LOOP _FS _FILE _BOOL); - HL_PRIM int HL_NAME(fs_fdatasync_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, bool use_uv_fs_cb ) { return uv_fs_fdatasync(loop, req, file, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_fdatasync_with_cb, _LOOP _FS _FILE _BOOL); - HL_PRIM int HL_NAME(fs_ftruncate_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, int64_t offset, bool use_uv_fs_cb ) { return uv_fs_ftruncate(loop, req, file, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_ftruncate_with_cb, _LOOP _FS _FILE _I64 _BOOL); - HL_PRIM int HL_NAME(fs_copyfile_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, bool use_uv_fs_cb ) { return uv_fs_copyfile(loop, req, path, new_path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_copyfile_with_cb, _LOOP _FS _BYTES _BYTES _I32 _BOOL); - HL_PRIM int HL_NAME(fs_sendfile_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file out_fd, uv_file in_fd, int64_t in_offset, size_t length, bool use_uv_fs_cb ) { return uv_fs_sendfile(loop, req, out_fd, in_fd, in_offset, length, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_sendfile_with_cb, _LOOP _FS _FILE _FILE _I64 _U64 _BOOL); - HL_PRIM int HL_NAME(fs_access_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { return uv_fs_access(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_access_with_cb, _LOOP _FS _BYTES _I32 _BOOL); - HL_PRIM int HL_NAME(fs_chmod_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { return uv_fs_chmod(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_chmod_with_cb, _LOOP _FS _BYTES _I32 _BOOL); - HL_PRIM int HL_NAME(fs_fchmod_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, int mode, bool use_uv_fs_cb ) { return uv_fs_fchmod(loop, req, file, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_fchmod_with_cb, _LOOP _FS _FILE _I32 _BOOL); - HL_PRIM int HL_NAME(fs_utime_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, bool use_uv_fs_cb ) { return uv_fs_utime(loop, req, path, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_utime_with_cb, _LOOP _FS _BYTES _F64 _F64 _BOOL); - HL_PRIM int HL_NAME(fs_futime_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime, double mtime, bool use_uv_fs_cb ) { return uv_fs_futime(loop, req, file, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_futime_with_cb, _LOOP _FS _FILE _F64 _F64 _BOOL); - HL_PRIM int HL_NAME(fs_lutime_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, bool use_uv_fs_cb ) { return uv_fs_lutime(loop, req, path, atime, mtime, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_lutime_with_cb, _LOOP _FS _BYTES _F64 _F64 _BOOL); - HL_PRIM int HL_NAME(fs_link_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, bool use_uv_fs_cb ) { return uv_fs_link(loop, req, path, new_path, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_link_with_cb, _LOOP _FS _BYTES _BYTES _BOOL); - HL_PRIM int HL_NAME(fs_symlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, bool use_uv_fs_cb ) { return uv_fs_symlink(loop, req, path, new_path, flags, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_symlink_with_cb, _LOOP _FS _BYTES _BYTES _I32 _BOOL); - HL_PRIM int HL_NAME(fs_readlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { return uv_fs_readlink(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_readlink_with_cb, _LOOP _FS _BYTES _BOOL); - HL_PRIM int HL_NAME(fs_realpath_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { return uv_fs_realpath(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_realpath_with_cb, _LOOP _FS _BYTES _BOOL); - HL_PRIM int HL_NAME(fs_chown_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { return uv_fs_chown(loop, req, path, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_chown_with_cb, _LOOP _FS _BYTES _UID_T _GID_T _BOOL); - HL_PRIM int HL_NAME(fs_fchown_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { return uv_fs_fchown(loop, req, file, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_fchown_with_cb, _LOOP _FS _FILE _UID_T _GID_T _BOOL); - HL_PRIM int HL_NAME(fs_lchown_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, bool use_uv_fs_cb ) { return uv_fs_lchown(loop, req, path, uid, gid, use_uv_fs_cb?on_uv_fs_cb:NULL); } DEFINE_PRIM(_I32, fs_lchown_with_cb, _LOOP _FS _BYTES _UID_T _GID_T _BOOL); - DEFINE_PRIM(_FS_TYPE, fs_get_type, _FS); - DEFINE_PRIM(_I64, fs_get_result, _FS); - DEFINE_PRIM(_I32, fs_get_system_error, _FS); - DEFINE_PRIM(_POINTER, fs_get_ptr, _FS); - DEFINE_PRIM(_BYTES, fs_get_path, _FS); - DEFINE_PRIM(_STAT, fs_get_statbuf, _FS); - DEFINE_PRIM(_I32, fs_event_init, _LOOP _FS_EVENT); - HL_PRIM int HL_NAME(fs_event_start_with_cb)( uv_fs_event_t* handle, const char* path, unsigned int flags ) { return uv_fs_event_start(handle, on_uv_fs_event_cb, path, flags); } DEFINE_PRIM(_I32, fs_event_start_with_cb, _FS_EVENT _BYTES _U32); - DEFINE_PRIM(_I32, fs_event_stop, _FS_EVENT); - -DEFINE_PRIM(_I32, fs_event_getpath, _FS_EVENT _BYTES _REF(_I64)); - +DEFINE_PRIM(_I32, fs_event_getpath, _FS_EVENT _BYTES _REF(_U64)); DEFINE_PRIM(_I32, fs_poll_init, _LOOP _FS_POLL); - HL_PRIM int HL_NAME(fs_poll_start_with_cb)( uv_fs_poll_t* handle, const char* path, unsigned int interval ) { return uv_fs_poll_start(handle, on_uv_fs_poll_cb, path, interval); } DEFINE_PRIM(_I32, fs_poll_start_with_cb, _FS_POLL _BYTES _U32); - DEFINE_PRIM(_I32, fs_poll_stop, _FS_POLL); - -DEFINE_PRIM(_I32, fs_poll_getpath, _FS_POLL _BYTES _REF(_I64)); - +DEFINE_PRIM(_I32, fs_poll_getpath, _FS_POLL _BYTES _REF(_U64)); DEFINE_PRIM(_I32, is_active, _HANDLE); - DEFINE_PRIM(_I32, is_closing, _HANDLE); - HL_PRIM void HL_NAME(close_with_cb)( uv_handle_t* handle ) { uv_close(handle, on_uv_close_cb); } DEFINE_PRIM(_VOID, close_with_cb, _HANDLE); - DEFINE_PRIM(_VOID, ref, _HANDLE); - DEFINE_PRIM(_VOID, unref, _HANDLE); - DEFINE_PRIM(_I32, has_ref, _HANDLE); - DEFINE_PRIM(_U64, handle_size, _HANDLE_TYPE); - DEFINE_PRIM(_I32, send_buffer_size, _HANDLE _REF(_I32)); - DEFINE_PRIM(_I32, recv_buffer_size, _HANDLE _REF(_I32)); - DEFINE_PRIM(_LOOP, handle_get_loop, _HANDLE); - DEFINE_PRIM(_POINTER, handle_get_data, _HANDLE); - DEFINE_PRIM(_POINTER, handle_set_data, _HANDLE _POINTER); - DEFINE_PRIM(_HANDLE_TYPE, handle_get_type, _HANDLE); - DEFINE_PRIM(_BYTES, handle_type_name, _HANDLE_TYPE); - DEFINE_PRIM(_I32, idle_init, _LOOP _IDLE); - HL_PRIM int HL_NAME(idle_start_with_cb)( uv_idle_t* idle ) { return uv_idle_start(idle, on_uv_idle_cb); } DEFINE_PRIM(_I32, idle_start_with_cb, _IDLE); - DEFINE_PRIM(_I32, idle_stop, _IDLE); - DEFINE_PRIM(_I32, loop_init, _LOOP); - DEFINE_PRIM(_I32, loop_close, _LOOP); - DEFINE_PRIM(_LOOP, default_loop, _NO_ARG); - DEFINE_PRIM(_I32, run, _LOOP _RUN_MODE); - DEFINE_PRIM(_I32, loop_alive, _LOOP); - DEFINE_PRIM(_VOID, stop, _LOOP); - DEFINE_PRIM(_U64, loop_size, _NO_ARG); - DEFINE_PRIM(_I32, backend_fd, _LOOP); - DEFINE_PRIM(_I32, backend_timeout, _LOOP); - -DEFINE_PRIM(_I64, now, _LOOP); - +DEFINE_PRIM(_U64, now, _LOOP); DEFINE_PRIM(_VOID, update_time, _LOOP); - HL_PRIM void HL_NAME(walk_with_cb)( uv_loop_t* loop, void* arg ) { uv_walk(loop, on_uv_walk_cb, arg); } DEFINE_PRIM(_VOID, walk_with_cb, _LOOP _POINTER); - DEFINE_PRIM(_I32, loop_fork, _LOOP); - DEFINE_PRIM(_POINTER, loop_get_data, _LOOP); - DEFINE_PRIM(_POINTER, loop_set_data, _LOOP _POINTER); - -DEFINE_PRIM(_I64, metrics_idle_time, _LOOP); - +DEFINE_PRIM(_U64, metrics_idle_time, _LOOP); +DEFINE_PRIM_UV_FIELD(_I64, long, _TIMEVAL, timeval, tv_sec); +DEFINE_PRIM_UV_FIELD(_I64, long, _TIMEVAL, timeval, tv_usec); +DEFINE_PRIM_ALLOC(_TIMEVAL, timeval); +DEFINE_PRIM_UV_FIELD(_I64, int64_t, _TIMEVAL64, timeval64, tv_sec); +DEFINE_PRIM_UV_FIELD(_I32, int32_t, _TIMEVAL64, timeval64, tv_usec); +DEFINE_PRIM_ALLOC(_TIMEVAL64, timeval64); +DEFINE_PRIM_UV_FIELD_REF(_TIMEVAL, uv_timeval_t *, _RUSAGE, rusage, ru_utime); +DEFINE_PRIM_UV_FIELD_REF(_TIMEVAL, uv_timeval_t *, _RUSAGE, rusage, ru_stime); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _RUSAGE, rusage, ru_maxrss); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _RUSAGE, rusage, ru_ixrss); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _RUSAGE, rusage, ru_idrss); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _RUSAGE, rusage, ru_isrss); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _RUSAGE, rusage, ru_minflt); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _RUSAGE, rusage, ru_majflt); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _RUSAGE, rusage, ru_nswap); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _RUSAGE, rusage, ru_inblock); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _RUSAGE, rusage, ru_oublock); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _RUSAGE, rusage, ru_msgsnd); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _RUSAGE, rusage, ru_msgrcv); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _RUSAGE, rusage, ru_nsignals); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _RUSAGE, rusage, ru_nvcsw); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _RUSAGE, rusage, ru_nivcsw); +DEFINE_PRIM_ALLOC(_RUSAGE, rusage); +DEFINE_PRIM_UV_FIELD(_BYTES, char*, _PASSWD, passwd, username); +DEFINE_PRIM_UV_FIELD(_I64, long, _PASSWD, passwd, uid); +DEFINE_PRIM_UV_FIELD(_I64, long, _PASSWD, passwd, gid); +DEFINE_PRIM_UV_FIELD(_BYTES, char*, _PASSWD, passwd, shell); +DEFINE_PRIM_UV_FIELD(_BYTES, char*, _PASSWD, passwd, homedir); +DEFINE_PRIM_ALLOC(_PASSWD, passwd); DEFINE_PRIM(_HANDLE_TYPE, guess_handle, _FILE); - DEFINE_PRIM(_VOID, library_shutdown, _NO_ARG); - -DEFINE_PRIM(_I32, resident_set_memory, _REF(_I64)); - +DEFINE_PRIM(_I32, resident_set_memory, _REF(_U64)); DEFINE_PRIM(_I32, uptime, _REF(_F64)); - DEFINE_PRIM(_I32, getrusage, _RUSAGE); - DEFINE_PRIM(_I32, os_getpid, _NO_ARG); - DEFINE_PRIM(_I32, os_getppid, _NO_ARG); - DEFINE_PRIM(_I32, cpu_info, _REF(_CPU_INFO) _REF(_I32)); - DEFINE_PRIM(_VOID, free_cpu_info, _CPU_INFO _I32); - DEFINE_PRIM(_I32, interface_addresses, _REF(_INTERFACE_ADDRESS) _REF(_I32)); - DEFINE_PRIM(_VOID, free_interface_addresses, _INTERFACE_ADDRESS _I32); - DEFINE_PRIM(_VOID, loadavg, _REF(_F64)); - DEFINE_PRIM(_I32, ip4_addr, _BYTES _I32 _SOCKADDR_IN); - DEFINE_PRIM(_I32, ip6_addr, _BYTES _I32 _SOCKADDR_IN6); - DEFINE_PRIM(_I32, ip4_name, _SOCKADDR_IN _BYTES _U64); - DEFINE_PRIM(_I32, ip6_name, _SOCKADDR_IN6 _BYTES _U64); - -DEFINE_PRIM(_I32, if_indextoname, _U32 _BYTES _REF(_I64)); - -DEFINE_PRIM(_I32, if_indextoiid, _U32 _BYTES _REF(_I64)); - -DEFINE_PRIM(_I32, exepath, _BYTES _REF(_I64)); - -DEFINE_PRIM(_I32, cwd, _BYTES _REF(_I64)); - +DEFINE_PRIM(_I32, if_indextoname, _U32 _BYTES _REF(_U64)); +DEFINE_PRIM(_I32, if_indextoiid, _U32 _BYTES _REF(_U64)); +DEFINE_PRIM(_I32, exepath, _BYTES _REF(_U64)); +DEFINE_PRIM(_I32, cwd, _BYTES _REF(_U64)); DEFINE_PRIM(_I32, chdir, _BYTES); - -DEFINE_PRIM(_I32, os_homedir, _BYTES _REF(_I64)); - -DEFINE_PRIM(_I32, os_tmpdir, _BYTES _REF(_I64)); - +DEFINE_PRIM(_I32, os_homedir, _BYTES _REF(_U64)); +DEFINE_PRIM(_I32, os_tmpdir, _BYTES _REF(_U64)); DEFINE_PRIM(_I32, os_get_passwd, _PASSWD); - DEFINE_PRIM(_VOID, os_free_passwd, _PASSWD); - -DEFINE_PRIM(_I64, get_free_memory, _NO_ARG); - -DEFINE_PRIM(_I64, get_total_memory, _NO_ARG); - -DEFINE_PRIM(_I64, get_constrained_memory, _NO_ARG); - -DEFINE_PRIM(_I64, hrtime, _NO_ARG); - -DEFINE_PRIM(_I32, os_getenv, _BYTES _BYTES _REF(_I64)); - +DEFINE_PRIM(_U64, get_free_memory, _NO_ARG); +DEFINE_PRIM(_U64, get_total_memory, _NO_ARG); +DEFINE_PRIM(_U64, get_constrained_memory, _NO_ARG); +DEFINE_PRIM(_U64, hrtime, _NO_ARG); +DEFINE_PRIM(_I32, os_getenv, _BYTES _BYTES _REF(_U64)); DEFINE_PRIM(_I32, os_setenv, _BYTES _BYTES); - DEFINE_PRIM(_I32, os_unsetenv, _BYTES); - -DEFINE_PRIM(_I32, os_gethostname, _BYTES _REF(_I64)); - +DEFINE_PRIM(_I32, os_gethostname, _BYTES _REF(_U64)); DEFINE_PRIM(_I32, os_getpriority, _I32 _REF(_I32)); - DEFINE_PRIM(_I32, os_setpriority, _I32 _I32); - DEFINE_PRIM(_I32, os_uname, _UTSNAME); - DEFINE_PRIM(_I32, gettimeofday, _TIMEVAL64); - HL_PRIM int HL_NAME(random_with_cb)( uv_loop_t* loop, uv_random_t* req, void* buf, size_t buflen, unsigned int flags ) { return uv_random(loop, req, buf, buflen, flags, on_uv_random_cb); } DEFINE_PRIM(_I32, random_with_cb, _LOOP _RANDOM _POINTER _U64 _U32); - DEFINE_PRIM(_VOID, sleep, _U32); - DEFINE_PRIM(_I32, pipe_init, _LOOP _PIPE _I32); - DEFINE_PRIM(_I32, pipe_open, _PIPE _FILE); - DEFINE_PRIM(_I32, pipe_bind, _PIPE _BYTES); - HL_PRIM void HL_NAME(pipe_connect_with_cb)( uv_connect_t* req, uv_pipe_t* handle, const char* name ) { uv_pipe_connect(req, handle, name, on_uv_connect_cb); } DEFINE_PRIM(_VOID, pipe_connect_with_cb, _CONNECT _PIPE _BYTES); - -DEFINE_PRIM(_I32, pipe_getsockname, _PIPE _BYTES _REF(_I64)); - -DEFINE_PRIM(_I32, pipe_getpeername, _PIPE _BYTES _REF(_I64)); - +DEFINE_PRIM(_I32, pipe_getsockname, _PIPE _BYTES _REF(_U64)); +DEFINE_PRIM(_I32, pipe_getpeername, _PIPE _BYTES _REF(_U64)); DEFINE_PRIM(_VOID, pipe_pending_instances, _PIPE _I32); - DEFINE_PRIM(_I32, pipe_pending_count, _PIPE); - DEFINE_PRIM(_HANDLE_TYPE, pipe_pending_type, _PIPE); - DEFINE_PRIM(_I32, pipe_chmod, _PIPE _I32); - DEFINE_PRIM(_I32, pipe, _REF(_FILE) _I32 _I32); - DEFINE_PRIM(_I32, prepare_init, _LOOP _PREPARE); - HL_PRIM int HL_NAME(prepare_start_with_cb)( uv_prepare_t* prepare ) { return uv_prepare_start(prepare, on_uv_prepare_cb); } DEFINE_PRIM(_I32, prepare_start_with_cb, _PREPARE); - DEFINE_PRIM(_I32, prepare_stop, _PREPARE); - DEFINE_PRIM(_VOID, disable_stdio_inheritance, _NO_ARG); - DEFINE_PRIM(_I32, spawn, _LOOP _PROCESS _PROCESS_OPTIONS); - DEFINE_PRIM(_I32, process_kill, _PROCESS _I32); - DEFINE_PRIM(_I32, kill, _I32 _I32); - DEFINE_PRIM(_I32, process_get_pid, _PROCESS); - DEFINE_PRIM(_I32, cancel, _REQ); - DEFINE_PRIM(_U64, req_size, _REQ_TYPE); - DEFINE_PRIM(_POINTER, req_get_data, _REQ); - DEFINE_PRIM(_POINTER, req_set_data, _REQ _POINTER); - DEFINE_PRIM(_REQ_TYPE, req_get_type, _REQ); - DEFINE_PRIM(_BYTES, req_type_name, _REQ_TYPE); - DEFINE_PRIM(_I32, signal_init, _LOOP _SIGNAL); - HL_PRIM int HL_NAME(signal_start_with_cb)( uv_signal_t* signal, int signum ) { return uv_signal_start(signal, on_uv_signal_cb, signum); } DEFINE_PRIM(_I32, signal_start_with_cb, _SIGNAL _I32); - HL_PRIM int HL_NAME(signal_start_oneshot_with_cb)( uv_signal_t* signal, int signum ) { return uv_signal_start_oneshot(signal, on_uv_signal_cb, signum); } DEFINE_PRIM(_I32, signal_start_oneshot_with_cb, _SIGNAL _I32); - DEFINE_PRIM(_I32, signal_stop, _SIGNAL); - HL_PRIM int HL_NAME(shutdown_with_cb)( uv_shutdown_t* req, uv_stream_t* handle ) { return uv_shutdown(req, handle, on_uv_shutdown_cb); } DEFINE_PRIM(_I32, shutdown_with_cb, _SHUTDOWN _STREAM); - HL_PRIM int HL_NAME(listen_with_cb)( uv_stream_t* stream, int backlog ) { return uv_listen(stream, backlog, on_uv_connection_cb); } DEFINE_PRIM(_I32, listen_with_cb, _STREAM _I32); - DEFINE_PRIM(_I32, accept, _STREAM _STREAM); - HL_PRIM int HL_NAME(read_start_with_cb)( uv_stream_t* stream ) { return uv_read_start(stream, on_uv_alloc_cb, on_uv_read_cb); } DEFINE_PRIM(_I32, read_start_with_cb, _STREAM); - DEFINE_PRIM(_I32, read_stop, _STREAM); - HL_PRIM int HL_NAME(write_with_cb)( uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs ) { return uv_write(req, handle, bufs, nbufs, on_uv_write_cb); } DEFINE_PRIM(_I32, write_with_cb, _WRITE _STREAM _BUF_ARR _U32); - HL_PRIM int HL_NAME(write2_with_cb)( uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_stream_t* send_handle ) { return uv_write2(req, handle, bufs, nbufs, send_handle, on_uv_write_cb); } DEFINE_PRIM(_I32, write2_with_cb, _WRITE _STREAM _BUF_ARR _U32 _STREAM); - DEFINE_PRIM(_I32, try_write, _STREAM _BUF_ARR _U32); - DEFINE_PRIM(_I32, try_write2, _STREAM _BUF_ARR _U32 _STREAM); - DEFINE_PRIM(_I32, is_readable, _STREAM); - DEFINE_PRIM(_I32, is_writable, _STREAM); - DEFINE_PRIM(_I32, stream_set_blocking, _STREAM _I32); - DEFINE_PRIM(_U64, stream_get_write_queue_size, _STREAM); - DEFINE_PRIM(_I32, tcp_init, _LOOP _TCP); - DEFINE_PRIM(_I32, tcp_init_ex, _LOOP _TCP _U32); - DEFINE_PRIM(_I32, tcp_nodelay, _TCP _I32); - DEFINE_PRIM(_I32, tcp_keepalive, _TCP _I32 _U32); - DEFINE_PRIM(_I32, tcp_simultaneous_accepts, _TCP _I32); - DEFINE_PRIM(_I32, tcp_bind, _TCP _SOCKADDR _U32); - DEFINE_PRIM(_I32, tcp_getsockname, _TCP _SOCKADDR _REF(_I32)); - DEFINE_PRIM(_I32, tcp_getpeername, _TCP _SOCKADDR _REF(_I32)); - HL_PRIM int HL_NAME(tcp_connect_with_cb)( uv_connect_t* req, uv_tcp_t* handle, const struct sockaddr* addr ) { return uv_tcp_connect(req, handle, addr, on_uv_connect_cb); } DEFINE_PRIM(_I32, tcp_connect_with_cb, _CONNECT _TCP _SOCKADDR); - HL_PRIM int HL_NAME(tcp_close_reset_with_cb)( uv_tcp_t* handle ) { return uv_tcp_close_reset(handle, on_uv_close_cb); } DEFINE_PRIM(_I32, tcp_close_reset_with_cb, _TCP); - DEFINE_PRIM(_I32, timer_init, _LOOP _TIMER); - HL_PRIM int HL_NAME(timer_start_with_cb)( uv_timer_t* handle, uint64_t timeout, uint64_t repeat ) { return uv_timer_start(handle, on_uv_timer_cb, timeout, repeat); } -DEFINE_PRIM(_I32, timer_start_with_cb, _TIMER _I64 _I64); - +DEFINE_PRIM(_I32, timer_start_with_cb, _TIMER _U64 _U64); DEFINE_PRIM(_I32, timer_stop, _TIMER); - DEFINE_PRIM(_I32, timer_again, _TIMER); - -DEFINE_PRIM(_VOID, timer_set_repeat, _TIMER _I64); - -DEFINE_PRIM(_I64, timer_get_repeat, _TIMER); - -DEFINE_PRIM(_I64, timer_get_due_in, _TIMER); - +DEFINE_PRIM(_VOID, timer_set_repeat, _TIMER _U64); +DEFINE_PRIM(_U64, timer_get_repeat, _TIMER); +DEFINE_PRIM(_U64, timer_get_due_in, _TIMER); DEFINE_PRIM(_I32, tty_init, _LOOP _TTY _FILE _I32); - DEFINE_PRIM(_I32, tty_set_mode, _TTY _TTY_MODE_T); - DEFINE_PRIM(_I32, tty_reset_mode, _NO_ARG); - DEFINE_PRIM(_I32, tty_get_winsize, _TTY _REF(_I32) _REF(_I32)); - DEFINE_PRIM(_VOID, tty_set_vterm_state, _TTY_VTERMSTATE_T); - DEFINE_PRIM(_I32, tty_get_vterm_state, _TTY_VTERMSTATE); - DEFINE_PRIM(_I32, udp_init, _LOOP _UDP); - DEFINE_PRIM(_I32, udp_init_ex, _LOOP _UDP _U32); - DEFINE_PRIM(_I32, udp_bind, _UDP _SOCKADDR _U32); - DEFINE_PRIM(_I32, udp_connect, _UDP _SOCKADDR); - DEFINE_PRIM(_I32, udp_getpeername, _UDP _SOCKADDR _REF(_I32)); - DEFINE_PRIM(_I32, udp_getsockname, _UDP _SOCKADDR _REF(_I32)); - DEFINE_PRIM(_I32, udp_set_membership, _UDP _BYTES _BYTES _MEMBERSHIP); - DEFINE_PRIM(_I32, udp_set_source_membership, _UDP _BYTES _BYTES _BYTES _MEMBERSHIP); - DEFINE_PRIM(_I32, udp_set_multicast_loop, _UDP _I32); - DEFINE_PRIM(_I32, udp_set_multicast_ttl, _UDP _I32); - DEFINE_PRIM(_I32, udp_set_multicast_interface, _UDP _BYTES); - DEFINE_PRIM(_I32, udp_set_broadcast, _UDP _I32); - DEFINE_PRIM(_I32, udp_set_ttl, _UDP _I32); - HL_PRIM int HL_NAME(udp_send_with_cb)( uv_udp_send_t* req, uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr ) { return uv_udp_send(req, handle, bufs, nbufs, addr, on_uv_udp_send_cb); } DEFINE_PRIM(_I32, udp_send_with_cb, _UDP_SEND _UDP _BUF_ARR _U32 _SOCKADDR); - DEFINE_PRIM(_I32, udp_try_send, _UDP _BUF_ARR _U32 _SOCKADDR); - HL_PRIM int HL_NAME(udp_recv_start_with_cb)( uv_udp_t* handle ) { return uv_udp_recv_start(handle, on_uv_alloc_cb, on_uv_udp_recv_cb); } DEFINE_PRIM(_I32, udp_recv_start_with_cb, _UDP); - DEFINE_PRIM(_I32, udp_using_recvmmsg, _UDP); - DEFINE_PRIM(_I32, udp_recv_stop, _UDP); - DEFINE_PRIM(_U64, udp_get_send_queue_size, _UDP); - DEFINE_PRIM(_U64, udp_get_send_queue_count, _UDP); - DEFINE_PRIM(_U32, version, _NO_ARG); - DEFINE_PRIM(_BYTES, version_string, _NO_ARG); - diff --git a/other/uvgenerator/UV.hx.header b/other/uvgenerator/UV.hx.header index e91edcf0b..e40fe9953 100644 --- a/other/uvgenerator/UV.hx.header +++ b/other/uvgenerator/UV.hx.header @@ -50,6 +50,7 @@ typedef UvTtyModeT = TtyMode; typedef UvTtyVtermstateT = TtyVTermState; typedef UvTtyVtermstateTStar = Ref; typedef UvMembership = UdpMembership; +typedef UvDirentTypeT = DirEntryType; abstract UvFile(Int) { @:allow(hl.uv) inline function new(fd:Int) this = fd; @@ -184,31 +185,11 @@ extern class UV { static public function alloc_fs_event():UvFsEventTStar; static public function alloc_fs_poll():UvFsPollTStar; static public function pointer_to_dir(req:Pointer):UvDirTStar; - static public function free_dir(dirent:UvDirTStar):Void; + static public function free_dir(dir:UvDirTStar):Void; static public function dir_init(dir:UvDirTStar, num_entries:Int):Void; static public function dir_nentries(dir:UvDirTStar):Int; static public function dir_dirent(dir:UvDirTStar, index:Int):UvDirentTStar; - static public function stat_st_dev(stat:UvStatTStar):U64; - static public function stat_st_mode(stat:UvStatTStar):U64; - static public function stat_st_nlink(stat:UvStatTStar):U64; - static public function stat_st_uid(stat:UvStatTStar):U64; - static public function stat_st_gid(stat:UvStatTStar):U64; - static public function stat_st_rdev(stat:UvStatTStar):U64; - static public function stat_st_ino(stat:UvStatTStar):U64; - static public function stat_st_size(stat:UvStatTStar):U64; - static public function stat_st_blksize(stat:UvStatTStar):U64; - static public function stat_st_blocks(stat:UvStatTStar):U64; - static public function stat_st_flags(stat:UvStatTStar):U64; - static public function stat_st_gen(stat:UvStatTStar):U64; - static public function stat_st_atim(stat:UvStatTStar):UvTimespecTStar; - static public function stat_st_mtim(stat:UvStatTStar):UvTimespecTStar; - static public function stat_st_ctim(stat:UvStatTStar):UvTimespecTStar; - static public function stat_st_birthtim(stat:UvStatTStar):UvTimespecTStar; - static public function timespec_tv_sec(times:UvTimespecTStar):I64; - static public function timespec_tv_nsec(times:UvTimespecTStar):I64; static public function free_dirent(dirent:UvDirentTStar):Void; - static public function dirent_name(dirent:UvDirentTStar):Bytes; - static public function dirent_type(dirent:UvDirentTStar):DirEntryType; static public function free_buf(buf:UvBufTArr):Void; static public function alloc_buf(bytes:Bytes, bytesLength:Int):UvBufTArr; static public function buf_set(buf:UvBufTArr, base:Bytes, length:Int):Void; @@ -223,29 +204,7 @@ extern class UV { static public function version_hex():Int; static public function version_suffix():Bytes; static public function version_is_release():Bool; - static public function alloc_rusage():UvRusageTStar; - static public function alloc_timeval64():UvTimeval64TStar; - static public function alloc_cpuinfo():UvCpuInfoTStar; - static public function timeval_tv_sec(timeval:UvTimevalTStar):I64; - static public function timeval_tv_usec(timeval:UvTimevalTStar):I64; - static public function timeval64_tv_sec(timeval:UvTimeval64TStar):I64; - static public function timeval64_tv_usec(timeval:UvTimeval64TStar):Int; - static public function rusage_ru_utime(rusage:UvRusageTStar):UvTimevalTStar; - static public function rusage_ru_stime(rusage:UvRusageTStar):UvTimevalTStar; - static public function rusage_ru_maxrss(rusage:UvRusageTStar):U64; - static public function rusage_ru_ixrss(rusage:UvRusageTStar):U64; - static public function rusage_ru_idrss(rusage:UvRusageTStar):U64; - static public function rusage_ru_isrss(rusage:UvRusageTStar):U64; - static public function rusage_ru_minflt(rusage:UvRusageTStar):U64; - static public function rusage_ru_majflt(rusage:UvRusageTStar):U64; - static public function rusage_ru_nswap(rusage:UvRusageTStar):U64; - static public function rusage_ru_inblock(rusage:UvRusageTStar):U64; - static public function rusage_ru_oublock(rusage:UvRusageTStar):U64; - static public function rusage_ru_msgsnd(rusage:UvRusageTStar):U64; - static public function rusage_ru_msgrcv(rusage:UvRusageTStar):U64; - static public function rusage_ru_nsignals(rusage:UvRusageTStar):U64; - static public function rusage_ru_nvcsw(rusage:UvRusageTStar):U64; - static public function rusage_ru_nivcsw(rusage:UvRusageTStar):U64; + static public function alloc_cpu_info():UvCpuInfoTStar; static public function cpu_info_model(cpu_info:UvCpuInfoTStar):Bytes; static public function cpu_info_speed(cpu_info:UvCpuInfoTStar):Int; static public function cpu_info_cpu_times(cpu_info:UvCpuInfoTStar):UvCpuTimesTStar; diff --git a/other/uvgenerator/UVGenerator.hx b/other/uvgenerator/UVGenerator.hx index c939b061d..c81124627 100644 --- a/other/uvgenerator/UVGenerator.hx +++ b/other/uvgenerator/UVGenerator.hx @@ -18,6 +18,11 @@ typedef FunctionSignature = { var arguments:Array; } +typedef StructSignature = { + var name:String; + var fields:Array; +} + class Skip extends Exception {} class UVGenerator { @@ -33,6 +38,9 @@ class UVGenerator { 'uv_os_environ', 'uv_os_free_environ', 'uv_setup_args', 'uv_get_process_title', 'uv_set_process_title', 'uv_tcp_open', 'uv_udp_open', 'uv_socketpair', 'uv_buf_init', 'uv_inet_ntop', 'uv_inet_pton', 'uv_loop_configure']; // TODO: don't skip uv_loop_configure + static final skipStructs = ['uv_process_options_t', 'uv_utsname_t', 'uv_env_item_t', 'uv_dir_t', + 'uv_interface_address_t', 'uv_cpu_info_t']; + static final skipStructFields = ['f_spare[4]', 'phys_addr[6]']; static final allowNoCallback = ['uv_fs_cb']; static final predefinedHxTypes = new Map(); @@ -60,23 +68,31 @@ class UVGenerator { var path = Path.join([docsDir, entry.name.toString()]); Sys.println('Generating ' + entry.name.sub(0, entry.name.length - 4).toString() + '...'); - for(line in File.getContent(path).split('\n')) { - if(!line.startsWith('.. c:function:: ')) - continue; + var lines = File.getContent(path).split('\n'); + var totalLines = lines.length; + while(lines.length > 0) { + var line = lines.shift().trim(); + var lineNumber = totalLines - lines.length; try { - var sig = try { - parseSignature(line.substr('.. c:function:: '.length).trim()); - } catch(e:Skip) { - continue; + if(line.startsWith('.. c:function:: ')) { + var sig = parseSignature(line.substr('.. c:function:: '.length).trim()); + var cStr = needsCbWrapper(sig) ? cFnWrapperWithCb(sig) : cFnBinding(sig); + cFile.writeString(cStr); + hxFile.writeString(hxBinding(sig)); + } else if(line.startsWith('typedef struct ') && line.endsWith('{')) { + for(sig in parseStruct(lines)) { + if(skipStructs.contains(sig.name)) + continue; + cFile.writeString(cStructBinding(sig)); + hxFile.writeString(hxStructBinding(sig)); + } } - var cStr = needsCbWrapper(sig) ? cWrapperWithCb(sig) : cBinding(sig); - cFile.writeString(cStr); - hxFile.writeString(hxBinding(sig)); + } catch(e:Skip) { + continue; } catch(e) { - Sys.stderr().writeString('Error on line: $line\n${e.details()}\n'); + Sys.stderr().writeString('Error on symbol at line: $lineNumber\n${e.details()}\n'); Sys.exit(1); } - cFile.writeString('\n'); } } hxFile.writeString('}\n\n'); @@ -119,6 +135,41 @@ class UVGenerator { return new Path(new Path(new Path(generatorPath).dir).dir).dir; } + static function parseStruct(lines:Array):Array { + var result = []; + var fields = []; + var name = null; + while(lines.length > 0) { + var line = lines.shift(); + //strip comments + var commentPos = line.indexOf('//'); + if(commentPos >= 0) + line = line.substr(0, commentPos); + commentPos = line.indexOf('/*'); + if(commentPos >= 0) + line = line.substr(0, commentPos) + line.substr(line.indexOf('*/') + 2); + line = line.trim(); + //leave unions for manual handling + if(line.startsWith('union ')) + continue; + if(line.startsWith('struct ') && line.endsWith('{')) { + result = result.concat(parseStruct(lines)); + } else { + var splitPos = line.lastIndexOf(' '); + if(line.endsWith(';')) + line = line.substr(0, line.length - 1); + if(line.startsWith('}')) { + name = line.substr(splitPos + 1); + break; + } else { + fields.push(parseTypeAndName(line)); + } + } + } + result.push({name:name, fields:fields}); + return result; + } + static function parseSignature(str:String):FunctionSignature { str = str.substr(0, str.length - (str.endsWith(';') ? 2 : 1)); var parts = str.split('('); @@ -150,14 +201,19 @@ class UVGenerator { static function mapHLType(type:String):String { return switch type { case 'int': '_I32'; + case 'int32_t': '_I32'; + case 'int32_t*': '_REF(_I32)'; case 'int64_t': '_I64'; - case 'uint64_t': '_I64'; + case 'uint64_t': '_U64'; + case 'long': '_I64'; + case 'long*': '_REF(_I64)'; + case 'long long': '_I64'; case 'char*': '_BYTES'; case 'void*': '_POINTER'; case 'void': '_VOID'; case 'unsigned int': '_U32'; case 'ssize_t': '_I64'; - case 'size_t*': '_REF(_I64)'; + case 'size_t*': '_REF(_U64)'; case 'size_t': '_U64'; case 'int*': '_REF(_I32)'; case 'double*': '_REF(_F64)'; @@ -165,6 +221,7 @@ class UVGenerator { case 'FILE*': '_FILE'; case 'uv_pid_t': '_I32'; case 'uv_buf_t': '_BUF'; + case 'uv_dirent_type_t': '_I32'; case _ if(type.endsWith('**')): '_REF(' + mapHLType(type.substr(0, type.length - 1)) + ')'; case _ if(type.startsWith('uv_')): @@ -214,11 +271,15 @@ class UVGenerator { case 'void': isRef ? 'Pointer' : 'Void'; case 'char': isRef ? 'Bytes' : 'Int'; case 'int': handleRef('Int'); + case 'long': handleRef('I64'); + case 'long long': handleRef('I64'); case 'double': handleRef('Float'); + case 'int32_t': handleRef('Int'); case 'int64_t': handleRef('I64'); case 'uint64_t': handleRef('U64'); case 'size_t': handleRef('U64'); case 'ssize_t': handleRef('I64'); + case 'uv_dirent_type_t': 'Int'; case _ if(type.startsWith('unsigned ')): handleRef('U' + mapHXType(type.substr('unsigned '.length))); case _ if(type.endsWith('*')): @@ -280,7 +341,7 @@ class UVGenerator { } } - static function cBinding(sig:FunctionSignature):String { + static function cFnBinding(sig:FunctionSignature):String { var args = switch sig.arguments { case [{type:'void'}]: '_NO_ARG'; case _: sig.arguments.map(mapHLArg).join(' '); @@ -288,7 +349,7 @@ class UVGenerator { return 'DEFINE_PRIM(${mapHLType(sig.returnType)}, ${functionName(sig.name)}, $args);\n'; } - static function cWrapperWithCb(sig:FunctionSignature):String { + static function cFnWrapperWithCb(sig:FunctionSignature):String { var fnName = functionNameWithCb(sig.name); var args = sig.arguments @@ -322,5 +383,53 @@ class UVGenerator { return lines.join('\n') + '\n'; } + + static function validateStructName(name:String):String { + if(!name.startsWith('uv_') || !name.endsWith('_t')) + throw new Skip('Struct $name does not conform naming pattern.'); + return name.substring('uv_'.length, name.length - '_t'.length); + } + + static function cStructBinding(sig:StructSignature):String { + var name = validateStructName(sig.name); + var cStruct = sig.name + '*'; + var hlStruct = mapHLType(cStruct); + var result = sig.fields + .filter(f -> !skipStructFields.contains(f.name)) + .map(f -> { + if(structFieldNeedsRef(f.type)) { + var hlType = mapHLType(f.type + '*'); + 'DEFINE_PRIM_UV_FIELD_REF($hlType, ${f.type} *, $hlStruct, $name, ${f.name});'; + } else { + 'DEFINE_PRIM_UV_FIELD(${mapHLType(f.type)}, ${f.type}, $hlStruct, $name, ${f.name});'; + } + }); + result.push('DEFINE_PRIM_ALLOC($hlStruct, $name);'); + return result.join('\n') + '\n'; + } + + static function hxStructBinding(sig:StructSignature):String { + var name = validateStructName(sig.name); + var cStruct = sig.name + '*'; + var hxStruct = mapHXType(cStruct); + var result = sig.fields + .filter(f -> !skipStructFields.contains(f.name)) + .map(f -> { + var cType = f.type; + if(structFieldNeedsRef(f.type)) + cType += '*'; + '\tstatic public function ${name}_${f.name}($name:$hxStruct):${mapHXType(cType)};'; + }); + result.push('\tstatic public function alloc_$name():$hxStruct;'); + return result.join('\n') + '\n'; + } + + static function structFieldNeedsRef(fieldType:String):Bool { + return !fieldType.endsWith('*') && !fieldType.startsWith('unsigned') && switch fieldType { + case 'int' | 'bool' | 'double' | 'int64_t' | 'uint64_t' | 'size_t' + | 'ssize_t' | 'long' | 'long long' | 'int32_t' | 'uv_dirent_type_t' : false; + case _: true; + } + } } From 6494d18b1afc9cfd7523d7620a261cc0517588c3 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Tue, 31 Aug 2021 21:28:26 +0300 Subject: [PATCH 088/117] skip uv_loadavg, uv_cpu_info, uv_interface_addresses --- libs/uv/uv.c | 25 ++++++------------------- libs/uv/uv_generated.c | 18 ++++++++---------- other/uvgenerator/UV.hx.header | 10 +--------- other/uvgenerator/UVGenerator.hx | 5 +++-- other/uvsample/MiscSample.hx | 11 ----------- 5 files changed, 18 insertions(+), 51 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 0e3acf819..977eaa2a0 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -37,7 +37,7 @@ #define _GID_T _I32 #define _FILE _I32 #define _BUF _ABSTRACT(uv_buf_t) -#define _BUF_ARR _ABSTRACT(uv_buf_t_arr) +#define _BUF_ARRAY _ABSTRACT(uv_buf_t_arr) #define _DIR _ABSTRACT(uv_dir_t_star) #define _FS_POLL _HANDLE #define _IDLE _HANDLE @@ -436,17 +436,17 @@ HL_PRIM uv_buf_t *HL_NAME(alloc_buf)( vbyte *bytes, int length ) { buf->len = length; return buf; } -DEFINE_PRIM(_BUF_ARR, alloc_buf, _BYTES _I32); +DEFINE_PRIM(_BUF_ARRAY, alloc_buf, _BYTES _I32); HL_PRIM void HL_NAME(buf_set)( uv_buf_t *buf, vbyte *bytes, int length ) { // TODO: change `length` to `int64` buf->base = (char *)bytes; buf->len = length; } -DEFINE_PRIM(_VOID, buf_set, _BUF_ARR _BYTES _I32); +DEFINE_PRIM(_VOID, buf_set, _BUF_ARRAY _BYTES _I32); -DEFINE_PRIM_FREE(_BUF_ARR, buf); -DEFINE_PRIM_UV_FIELD(_BYTES, vbyte *, _BUF_ARR, buf, base); -DEFINE_PRIM_UV_FIELD(_U64, int64, _BUF_ARR, buf, len); +DEFINE_PRIM_FREE(_BUF_ARRAY, buf); +DEFINE_PRIM_UV_FIELD(_BYTES, vbyte *, _BUF_ARRAY, buf, base); +DEFINE_PRIM_UV_FIELD(_U64, int64, _BUF_ARRAY, buf, len); // Handle @@ -1017,22 +1017,9 @@ DEFINE_PRIM_VERSION(suffix, UV_VERSION_SUFFIX, _BYTES, vbyte *); // Misc -DEFINE_PRIM_ALLOC(_CPU_INFO, cpu_info); - DEFINE_PRIM_FREE(_RUSAGE, rusage); DEFINE_PRIM_FREE(_TIMEVAL64, timeval64); -DEFINE_PRIM_UV_FIELD(_BYTES, vbyte *, _CPU_INFO, cpu_info, model); -DEFINE_PRIM_UV_FIELD(_I32, int, _CPU_INFO, cpu_info, speed); -DEFINE_PRIM_UV_FIELD_REF(_CPU_TIMES, uv_cpu_times_t *, _CPU_INFO, cpu_info, cpu_times); - -DEFINE_PRIM_UV_FIELD(_U64, int64, _CPU_TIMES, cpu_times, user); -DEFINE_PRIM_UV_FIELD(_U64, int64, _CPU_TIMES, cpu_times, nice); -DEFINE_PRIM_UV_FIELD(_U64, int64, _CPU_TIMES, cpu_times, sys); -DEFINE_PRIM_UV_FIELD(_U64, int64, _CPU_TIMES, cpu_times, idle); -DEFINE_PRIM_UV_FIELD(_U64, int64, _CPU_TIMES, cpu_times, irq); - - // auto-generated libuv bindings #include "uv_generated.c" diff --git a/libs/uv/uv_generated.c b/libs/uv/uv_generated.c index 9bebb2c8f..9206de2fc 100644 --- a/libs/uv/uv_generated.c +++ b/libs/uv/uv_generated.c @@ -66,7 +66,7 @@ DEFINE_PRIM(_I32, fs_open_with_cb, _LOOP _FS _BYTES _I32 _I32 _BOOL); HL_PRIM int HL_NAME(fs_read_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, bool use_uv_fs_cb ) { return uv_fs_read(loop, req, file, bufs, nbufs, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); } -DEFINE_PRIM(_I32, fs_read_with_cb, _LOOP _FS _FILE _BUF_ARR _U32 _I64 _BOOL); +DEFINE_PRIM(_I32, fs_read_with_cb, _LOOP _FS _FILE _BUF_ARRAY _U32 _I64 _BOOL); HL_PRIM int HL_NAME(fs_unlink_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, bool use_uv_fs_cb ) { return uv_fs_unlink(loop, req, path, use_uv_fs_cb?on_uv_fs_cb:NULL); } @@ -74,7 +74,7 @@ DEFINE_PRIM(_I32, fs_unlink_with_cb, _LOOP _FS _BYTES _BOOL); HL_PRIM int HL_NAME(fs_write_with_cb)( uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, bool use_uv_fs_cb ) { return uv_fs_write(loop, req, file, bufs, nbufs, offset, use_uv_fs_cb?on_uv_fs_cb:NULL); } -DEFINE_PRIM(_I32, fs_write_with_cb, _LOOP _FS _FILE _BUF_ARR _U32 _I64 _BOOL); +DEFINE_PRIM(_I32, fs_write_with_cb, _LOOP _FS _FILE _BUF_ARRAY _U32 _I64 _BOOL); HL_PRIM int HL_NAME(fs_mkdir_with_cb)( uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, bool use_uv_fs_cb ) { return uv_fs_mkdir(loop, req, path, mode, use_uv_fs_cb?on_uv_fs_cb:NULL); } @@ -298,11 +298,9 @@ DEFINE_PRIM(_I32, uptime, _REF(_F64)); DEFINE_PRIM(_I32, getrusage, _RUSAGE); DEFINE_PRIM(_I32, os_getpid, _NO_ARG); DEFINE_PRIM(_I32, os_getppid, _NO_ARG); -DEFINE_PRIM(_I32, cpu_info, _REF(_CPU_INFO) _REF(_I32)); DEFINE_PRIM(_VOID, free_cpu_info, _CPU_INFO _I32); DEFINE_PRIM(_I32, interface_addresses, _REF(_INTERFACE_ADDRESS) _REF(_I32)); DEFINE_PRIM(_VOID, free_interface_addresses, _INTERFACE_ADDRESS _I32); -DEFINE_PRIM(_VOID, loadavg, _REF(_F64)); DEFINE_PRIM(_I32, ip4_addr, _BYTES _I32 _SOCKADDR_IN); DEFINE_PRIM(_I32, ip6_addr, _BYTES _I32 _SOCKADDR_IN6); DEFINE_PRIM(_I32, ip4_name, _SOCKADDR_IN _BYTES _U64); @@ -391,13 +389,13 @@ DEFINE_PRIM(_I32, read_stop, _STREAM); HL_PRIM int HL_NAME(write_with_cb)( uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs ) { return uv_write(req, handle, bufs, nbufs, on_uv_write_cb); } -DEFINE_PRIM(_I32, write_with_cb, _WRITE _STREAM _BUF_ARR _U32); +DEFINE_PRIM(_I32, write_with_cb, _WRITE _STREAM _BUF_ARRAY _U32); HL_PRIM int HL_NAME(write2_with_cb)( uv_write_t* req, uv_stream_t* handle, const uv_buf_t bufs[], unsigned int nbufs, uv_stream_t* send_handle ) { return uv_write2(req, handle, bufs, nbufs, send_handle, on_uv_write_cb); } -DEFINE_PRIM(_I32, write2_with_cb, _WRITE _STREAM _BUF_ARR _U32 _STREAM); -DEFINE_PRIM(_I32, try_write, _STREAM _BUF_ARR _U32); -DEFINE_PRIM(_I32, try_write2, _STREAM _BUF_ARR _U32 _STREAM); +DEFINE_PRIM(_I32, write2_with_cb, _WRITE _STREAM _BUF_ARRAY _U32 _STREAM); +DEFINE_PRIM(_I32, try_write, _STREAM _BUF_ARRAY _U32); +DEFINE_PRIM(_I32, try_write2, _STREAM _BUF_ARRAY _U32 _STREAM); DEFINE_PRIM(_I32, is_readable, _STREAM); DEFINE_PRIM(_I32, is_writable, _STREAM); DEFINE_PRIM(_I32, stream_set_blocking, _STREAM _I32); @@ -450,8 +448,8 @@ DEFINE_PRIM(_I32, udp_set_ttl, _UDP _I32); HL_PRIM int HL_NAME(udp_send_with_cb)( uv_udp_send_t* req, uv_udp_t* handle, const uv_buf_t bufs[], unsigned int nbufs, const struct sockaddr* addr ) { return uv_udp_send(req, handle, bufs, nbufs, addr, on_uv_udp_send_cb); } -DEFINE_PRIM(_I32, udp_send_with_cb, _UDP_SEND _UDP _BUF_ARR _U32 _SOCKADDR); -DEFINE_PRIM(_I32, udp_try_send, _UDP _BUF_ARR _U32 _SOCKADDR); +DEFINE_PRIM(_I32, udp_send_with_cb, _UDP_SEND _UDP _BUF_ARRAY _U32 _SOCKADDR); +DEFINE_PRIM(_I32, udp_try_send, _UDP _BUF_ARRAY _U32 _SOCKADDR); HL_PRIM int HL_NAME(udp_recv_start_with_cb)( uv_udp_t* handle ) { return uv_udp_recv_start(handle, on_uv_alloc_cb, on_uv_udp_recv_cb); } diff --git a/other/uvgenerator/UV.hx.header b/other/uvgenerator/UV.hx.header index e40fe9953..de282bffd 100644 --- a/other/uvgenerator/UV.hx.header +++ b/other/uvgenerator/UV.hx.header @@ -204,15 +204,7 @@ extern class UV { static public function version_hex():Int; static public function version_suffix():Bytes; static public function version_is_release():Bool; - static public function alloc_cpu_info():UvCpuInfoTStar; - static public function cpu_info_model(cpu_info:UvCpuInfoTStar):Bytes; - static public function cpu_info_speed(cpu_info:UvCpuInfoTStar):Int; - static public function cpu_info_cpu_times(cpu_info:UvCpuInfoTStar):UvCpuTimesTStar; - static public function cpu_times_user(cpu_times:UvCpuTimesTStar):U64; - static public function cpu_times_nice(cpu_times:UvCpuTimesTStar):U64; - static public function cpu_times_sys(cpu_times:UvCpuTimesTStar):U64; - static public function cpu_times_idle(cpu_times:UvCpuTimesTStar):U64; - static public function cpu_times_irq(cpu_times:UvCpuTimesTStar):U64; + static public function free_rusage(rusage:UvRusageTStar):Void; // Auto generated content : diff --git a/other/uvgenerator/UVGenerator.hx b/other/uvgenerator/UVGenerator.hx index c81124627..4d76187bb 100644 --- a/other/uvgenerator/UVGenerator.hx +++ b/other/uvgenerator/UVGenerator.hx @@ -37,7 +37,8 @@ class UVGenerator { 'uv_fileno', 'uv_open_osfhandle', 'uv_print_all_handles', 'uv_print_active_handles', 'uv_os_environ', 'uv_os_free_environ', 'uv_setup_args', 'uv_get_process_title', 'uv_set_process_title', 'uv_tcp_open', 'uv_udp_open', 'uv_socketpair', 'uv_buf_init', - 'uv_inet_ntop', 'uv_inet_pton', 'uv_loop_configure']; // TODO: don't skip uv_loop_configure + 'uv_inet_ntop', 'uv_inet_pton', 'uv_loadavg', 'uv_cpu_info', 'uv_interface_addressses', + 'uv_loop_configure']; // TODO: don't skip uv_loop_configure static final skipStructs = ['uv_process_options_t', 'uv_utsname_t', 'uv_env_item_t', 'uv_dir_t', 'uv_interface_address_t', 'uv_cpu_info_t']; static final skipStructFields = ['f_spare[4]', 'phys_addr[6]']; @@ -245,7 +246,7 @@ class UVGenerator { static function mapHLArg(a:TypeAndName):String { var type = mapHLType(a.type); if(a.name.endsWith(']')) { - type = isUvBuf(a.type) ? '${type}_ARR' : '_REF($type)'; + type = isUvBuf(a.type) ? '${type}_ARRAY' : '_REF($type)'; } return type; } diff --git a/other/uvsample/MiscSample.hx b/other/uvsample/MiscSample.hx index af3660bdd..81743db0c 100644 --- a/other/uvsample/MiscSample.hx +++ b/other/uvsample/MiscSample.hx @@ -14,17 +14,6 @@ class MiscSample { print('RUsage: ' + Misc.getRUsage()); print('Pid: ' + Misc.getPid()); print('PPid: ' + Misc.getPPid()); - print('Cpu infos:\n ' + Misc.cpuInfo().map(Std.string).join('\n ')); - print('Inteface addresses:\n ' + Misc.interfaceAddresses().map(i -> { - Std.string({ - name:i.name, - physAddr:i.physAddr, - isInternal:i.isInternal, - address:i.address.name(), - netmask:i.netmask.name(), - }); - }).join('\n ')); - print('Load avg: ' + Misc.loadAvg()); print('Home dir: ' + Misc.homeDir()); print('Passwd: ' + Misc.getPasswd()); print('Free mem: ' + Misc.getFreeMemory()); From d9f7035570b42afe39a04e332f51fd7ee843f253 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Wed, 1 Sep 2021 21:51:04 +0300 Subject: [PATCH 089/117] finished misc --- libs/uv/uv.c | 97 +++++++++++++++++++++++++++++--- libs/uv/uv_generated.c | 20 ++++++- other/uvgenerator/UV.hx.header | 19 +++++++ other/uvgenerator/UVGenerator.hx | 68 ++++++++++++++++------ other/uvsample/MiscSample.hx | 21 ++++++- 5 files changed, 196 insertions(+), 29 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 977eaa2a0..e02436e7b 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -87,8 +87,6 @@ typedef struct sockaddr_storage uv_sockaddr_storage; static void on_uv_walk_cb( uv_handle_t* handle, void* arg ) { } - static void on_uv_random_cb( uv_random_t* r, int status, void* buf, size_t buflen ) { - } // } #define UV_ALLOC(t) ((t*)malloc(sizeof(t))) @@ -118,6 +116,12 @@ typedef struct sockaddr_storage uv_sockaddr_storage; } \ DEFINE_PRIM(hl_return, c_struct##_##field, hl_struct); +#define DEFINE_PRIM_C_FIELD_REF(hl_return,c_return,hl_struct,c_struct,field) \ + HL_PRIM c_return HL_NAME(c_struct##_##field)( struct c_struct *s ) { \ + return &s->field; \ + } \ + DEFINE_PRIM(hl_return, c_struct##_##field, hl_struct); + #define DEFINE_PRIM_UV_FIELD(hl_return,c_return,hl_struct,uv_name,field) \ HL_PRIM c_return HL_NAME(uv_name##_##field)( uv_##uv_name##_t *s ) { \ return (c_return)s->field; \ @@ -126,7 +130,7 @@ typedef struct sockaddr_storage uv_sockaddr_storage; #define DEFINE_PRIM_UV_FIELD_REF(hl_return,c_return,hl_struct,uv_name,field) \ HL_PRIM c_return HL_NAME(uv_name##_##field)( uv_##uv_name##_t *s ) { \ - return &s->field; \ + return (c_return)&s->field; \ } \ DEFINE_PRIM(hl_return, uv_name##_##field, hl_struct); @@ -137,6 +141,12 @@ typedef struct sockaddr_storage uv_sockaddr_storage; } \ DEFINE_PRIM(hl_type, pointer_to_##uv_name, _POINTER); +#define DEFINE_PRIM_TO_POINTER(hl_type,uv_name) \ + HL_PRIM void *HL_NAME(pointer_of_##uv_name)( uv_##uv_name##_t *v ) { \ + return v; \ + } \ + DEFINE_PRIM(_POINTER, pointer_of_##uv_name, hl_type); + #define UV_SET_DATA(h,new_data) \ if( h->data != new_data ) { \ if( h->data ) \ @@ -426,6 +436,11 @@ HL_PRIM vbyte **HL_NAME(alloc_char_array)( int length ) { } DEFINE_PRIM(_REF(_BYTES), alloc_char_array, _I32); +HL_PRIM void *HL_NAME(pointer_of_bytes)( vbyte *bytes ) { + return bytes; +} +DEFINE_PRIM(_POINTER, pointer_of_bytes, _BYTES); + DEFINE_PRIM_FREE(_REF(_BYTES), char_array); // Buf @@ -573,6 +588,8 @@ DEFINE_PRIM_FREE(_LOOP, loop); DEFINE_PRIM_FREE(_SOCKADDR_STORAGE, sockaddr_storage); +DEFINE_PRIM_C_FIELD(_I32, int, _SOCKADDR_STORAGE, sockaddr_storage, ss_family); + HL_PRIM struct sockaddr_storage *HL_NAME(alloc_sockaddr_storage)() { return UV_ALLOC(struct sockaddr_storage); } @@ -583,10 +600,15 @@ HL_PRIM int HL_NAME(sockaddr_storage_size)() { } DEFINE_PRIM(_I32, sockaddr_storage_size, _NO_ARG); -HL_PRIM struct sockaddr *HL_NAME(sockaddr_of_storage)( struct sockaddr_storage *addr ) { - return (struct sockaddr *)addr; -} -DEFINE_PRIM(_SOCKADDR, sockaddr_of_storage, _SOCKADDR_STORAGE); +#define DEFINE_PRIM_OF_SOCKADDR_STORAGE(hl_type, name) \ + HL_PRIM struct name *HL_NAME(name##_of_storage)( struct sockaddr_storage *addr ) { \ + return (struct name *)addr; \ + } \ + DEFINE_PRIM(hl_type, name##_of_storage, _SOCKADDR_STORAGE); + +DEFINE_PRIM_OF_SOCKADDR_STORAGE(_SOCKADDR, sockaddr); +DEFINE_PRIM_OF_SOCKADDR_STORAGE(_SOCKADDR_IN, sockaddr_in); +DEFINE_PRIM_OF_SOCKADDR_STORAGE(_SOCKADDR_IN6, sockaddr_in6); HL_PRIM struct sockaddr_storage *HL_NAME(sockaddr_to_storage)( struct sockaddr *addr ) { if( !addr ) @@ -654,6 +676,16 @@ HL_PRIM int HL_NAME(address_family_to_af)( int family ) { } DEFINE_PRIM(_I32, address_family_to_af, _I32) +HL_PRIM int HL_NAME(address_family_of_af)( int af_family ) { + switch( af_family ) { + case AF_UNSPEC: return HL_UV_UNSPEC; + case AF_INET: return HL_UV_INET; + case AF_INET6: return HL_UV_INET6; + default: return af_family; + } +} +DEFINE_PRIM(_I32, address_family_of_af, _I32) + //see hl.uv.SockAddr.SocketType #define HL_UV_STREAM -1 #define HL_UV_DGRAM -2 @@ -1020,6 +1052,57 @@ DEFINE_PRIM_VERSION(suffix, UV_VERSION_SUFFIX, _BYTES, vbyte *); DEFINE_PRIM_FREE(_RUSAGE, rusage); DEFINE_PRIM_FREE(_TIMEVAL64, timeval64); +HL_PRIM uv_cpu_info_t *HL_NAME(cpu_info_get)( uv_cpu_info_t *infos, int index ) { + return &infos[index]; +} +DEFINE_PRIM(_CPU_INFO, cpu_info_get, _CPU_INFO _I32); + +HL_PRIM uv_interface_address_t *HL_NAME(interface_address_get)( uv_interface_address_t *addresses, int index ) { + return &addresses[index]; +} +DEFINE_PRIM(_INTERFACE_ADDRESS, interface_address_get, _INTERFACE_ADDRESS _I32); + +#define DEFINE_PRIM_INTERFACE_ADDRESS_ADDR(field) \ + HL_PRIM uv_sockaddr_storage *HL_NAME(interface_address_##field)( uv_interface_address_t *addr ) { \ + uv_sockaddr_storage *result = UV_ALLOC(uv_sockaddr_storage); \ + memcpy(result, &addr->field, sizeof(addr->field)); \ + return result; \ + } \ + DEFINE_PRIM(_SOCKADDR_STORAGE, interface_address_##field, _INTERFACE_ADDRESS); + +DEFINE_PRIM_INTERFACE_ADDRESS_ADDR(address); +DEFINE_PRIM_INTERFACE_ADDRESS_ADDR(netmask); + +HL_PRIM char *HL_NAME(interface_address_phys_addr)( uv_interface_address_t *addr ) { + return addr->phys_addr; +} +DEFINE_PRIM(_BYTES, interface_address_phys_addr, _INTERFACE_ADDRESS); + +HL_PRIM varray *HL_NAME(loadavg_array)() { + double avg[3]; + uv_loadavg(avg); + varray *a = hl_alloc_array(&hlt_f64, 3); + hl_aptr(a,double)[0] = avg[0]; + hl_aptr(a,double)[1] = avg[1]; + hl_aptr(a,double)[2] = avg[2]; + return a; +} +DEFINE_PRIM(_ARR, loadavg_array, _NO_ARG); + +DEFINE_PRIM_ALLOC(_UTSNAME, utsname); +DEFINE_PRIM_FREE(_UTSNAME, utsname); +DEFINE_PRIM_UV_FIELD_REF(_BYTES, vbyte *, _UTSNAME, utsname, sysname); +DEFINE_PRIM_UV_FIELD_REF(_BYTES, vbyte *, _UTSNAME, utsname, release); +DEFINE_PRIM_UV_FIELD_REF(_BYTES, vbyte *, _UTSNAME, utsname, version); +DEFINE_PRIM_UV_FIELD_REF(_BYTES, vbyte *, _UTSNAME, utsname, machine); + +DEFINE_PRIM_ALLOC(_RANDOM, random); + +static void on_uv_random_cb( uv_random_t* r, int status, void* buf, size_t buflen ) { + vclosure *c = DATA(uv_req_cb_data_t *, r)->callback; + hl_call1(void, c, int, status); +} + // auto-generated libuv bindings #include "uv_generated.c" diff --git a/libs/uv/uv_generated.c b/libs/uv/uv_generated.c index 9206de2fc..baf067427 100644 --- a/libs/uv/uv_generated.c +++ b/libs/uv/uv_generated.c @@ -285,6 +285,19 @@ DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _RUSAGE, rusage, ru_nsignals); DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _RUSAGE, rusage, ru_nvcsw); DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _RUSAGE, rusage, ru_nivcsw); DEFINE_PRIM_ALLOC(_RUSAGE, rusage); +DEFINE_PRIM_UV_FIELD(_BYTES, char*, _CPU_INFO, cpu_info, model); +DEFINE_PRIM_UV_FIELD(_I32, int, _CPU_INFO, cpu_info, speed); +DEFINE_PRIM_UV_FIELD_REF(_CPU_TIMES, uv_cpu_times_t *, _CPU_INFO, cpu_info, cpu_times); +DEFINE_PRIM_ALLOC(_CPU_INFO, cpu_info); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _CPU_TIMES, cpu_times, user); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _CPU_TIMES, cpu_times, nice); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _CPU_TIMES, cpu_times, sys); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _CPU_TIMES, cpu_times, idle); +DEFINE_PRIM_UV_FIELD(_U64, uint64_t, _CPU_TIMES, cpu_times, irq); +DEFINE_PRIM_ALLOC(_CPU_TIMES, cpu_times); +DEFINE_PRIM_UV_FIELD(_BYTES, char*, _INTERFACE_ADDRESS, interface_address, name); +DEFINE_PRIM_UV_FIELD(_I32, int, _INTERFACE_ADDRESS, interface_address, is_internal); +DEFINE_PRIM_ALLOC(_INTERFACE_ADDRESS, interface_address); DEFINE_PRIM_UV_FIELD(_BYTES, char*, _PASSWD, passwd, username); DEFINE_PRIM_UV_FIELD(_I64, long, _PASSWD, passwd, uid); DEFINE_PRIM_UV_FIELD(_I64, long, _PASSWD, passwd, gid); @@ -298,6 +311,7 @@ DEFINE_PRIM(_I32, uptime, _REF(_F64)); DEFINE_PRIM(_I32, getrusage, _RUSAGE); DEFINE_PRIM(_I32, os_getpid, _NO_ARG); DEFINE_PRIM(_I32, os_getppid, _NO_ARG); +DEFINE_PRIM(_I32, cpu_info, _REF(_CPU_INFO) _REF(_I32)); DEFINE_PRIM(_VOID, free_cpu_info, _CPU_INFO _I32); DEFINE_PRIM(_I32, interface_addresses, _REF(_INTERFACE_ADDRESS) _REF(_I32)); DEFINE_PRIM(_VOID, free_interface_addresses, _INTERFACE_ADDRESS _I32); @@ -326,10 +340,10 @@ DEFINE_PRIM(_I32, os_getpriority, _I32 _REF(_I32)); DEFINE_PRIM(_I32, os_setpriority, _I32 _I32); DEFINE_PRIM(_I32, os_uname, _UTSNAME); DEFINE_PRIM(_I32, gettimeofday, _TIMEVAL64); -HL_PRIM int HL_NAME(random_with_cb)( uv_loop_t* loop, uv_random_t* req, void* buf, size_t buflen, unsigned int flags ) { - return uv_random(loop, req, buf, buflen, flags, on_uv_random_cb); +HL_PRIM int HL_NAME(random_with_cb)( uv_loop_t* loop, uv_random_t* req, void* buf, size_t buflen, unsigned int flags, bool use_uv_random_cb ) { + return uv_random(loop, req, buf, buflen, flags, use_uv_random_cb?on_uv_random_cb:NULL); } -DEFINE_PRIM(_I32, random_with_cb, _LOOP _RANDOM _POINTER _U64 _U32); +DEFINE_PRIM(_I32, random_with_cb, _LOOP _RANDOM _POINTER _U64 _U32 _BOOL); DEFINE_PRIM(_VOID, sleep, _U32); DEFINE_PRIM(_I32, pipe_init, _LOOP _PIPE _I32); DEFINE_PRIM(_I32, pipe_open, _PIPE _FILE); diff --git a/other/uvgenerator/UV.hx.header b/other/uvgenerator/UV.hx.header index de282bffd..5cdc25449 100644 --- a/other/uvgenerator/UV.hx.header +++ b/other/uvgenerator/UV.hx.header @@ -137,6 +137,7 @@ extern class UV { static public function alloc_char_array(length:Int):Ref; static public function free_char_array(a:Ref):Void; static public function free_bytes(bytes:Bytes):Void; + static public function pointer_of_bytes(bytes:Bytes):Pointer; static public function translate_uv_error(uvErrno:Int):UVError; static public function translate_to_uv_error(errno:Int):Int; static public function translate_sys_signal(sigNum:Int):SigNum; @@ -155,9 +156,12 @@ extern class UV { static public function alloc_tcp():UvTcpTStar; static public function alloc_sockaddr_storage():CSockaddrStorageStar; static public function sockaddr_storage_size():Int; + static public function sockaddr_storage_ss_family(addr:CSockaddrStorageStar):Int; static public function free_sockaddr_storage(addr:CSockaddrStorageStar):Void; static public function sockaddr_of_storage(addr:CSockaddrStorageStar):CSockaddrStar; static public function sockaddr_to_storage(addr:CSockaddrStar):CSockaddrStorageStar; + static public function sockaddr_in_of_storage(addr:CSockaddrStorageStar):CSockaddrInStar; + static public function sockaddr_in6_of_storage(addr:CSockaddrStorageStar):CSockaddrIn6Star; static public function alloc_udp():UvUdpTStar; static public function alloc_udp_send():UvUdpSendTStar; static public function alloc_pipe():UvPipeTStar; @@ -172,6 +176,7 @@ extern class UV { static public function alloc_shutdown():UvShutdownTStar; static public function alloc_write():UvWriteTStar; static public function alloc_connect():UvConnectTStar; + static public function address_family_of_af(afFamily:Int):AddressFamily; static public function address_family_to_af(family:AddressFamily):Int; static public function address_family_to_pf(family:AddressFamily):Int; static public function addrinfo_ai_family(ai:CAddrinfoStar):AddressFamily; @@ -205,6 +210,20 @@ extern class UV { static public function version_suffix():Bytes; static public function version_is_release():Bool; static public function free_rusage(rusage:UvRusageTStar):Void; + static public function free_timeval64(tv:UvTimeval64TStar):Void; + static public function cpu_info_get(infos:UvCpuInfoTStar, index:Int):UvCpuInfoTStar; + static public function interface_address_get(addresses:UvInterfaceAddressTStar, index:Int):UvInterfaceAddressTStar; + static public function interface_address_address(addr:UvInterfaceAddressTStar):CSockaddrStorageStar; + static public function interface_address_netmask(addr:UvInterfaceAddressTStar):CSockaddrStorageStar; + static public function interface_address_phys_addr(addr:UvInterfaceAddressTStar):Bytes; + static public function loadavg_array():NativeArray; + static public function alloc_utsname():UvUtsnameTStar; + static public function free_utsname(u:UvUtsnameTStar):Void; + static public function utsname_sysname(u:UvUtsnameTStar):Bytes; + static public function utsname_release(u:UvUtsnameTStar):Bytes; + static public function utsname_version(u:UvUtsnameTStar):Bytes; + static public function utsname_machine(u:UvUtsnameTStar):Bytes; + static public function alloc_random():UvRandomTStar; // Auto generated content : diff --git a/other/uvgenerator/UVGenerator.hx b/other/uvgenerator/UVGenerator.hx index 4d76187bb..e33526a35 100644 --- a/other/uvgenerator/UVGenerator.hx +++ b/other/uvgenerator/UVGenerator.hx @@ -19,6 +19,7 @@ typedef FunctionSignature = { } typedef StructSignature = { + var type:String; var name:String; var fields:Array; } @@ -37,12 +38,12 @@ class UVGenerator { 'uv_fileno', 'uv_open_osfhandle', 'uv_print_all_handles', 'uv_print_active_handles', 'uv_os_environ', 'uv_os_free_environ', 'uv_setup_args', 'uv_get_process_title', 'uv_set_process_title', 'uv_tcp_open', 'uv_udp_open', 'uv_socketpair', 'uv_buf_init', - 'uv_inet_ntop', 'uv_inet_pton', 'uv_loadavg', 'uv_cpu_info', 'uv_interface_addressses', + 'uv_inet_ntop', 'uv_inet_pton', 'uv_loadavg', 'uv_interface_addressses', 'uv_loop_configure']; // TODO: don't skip uv_loop_configure static final skipStructs = ['uv_process_options_t', 'uv_utsname_t', 'uv_env_item_t', 'uv_dir_t', - 'uv_interface_address_t', 'uv_cpu_info_t']; + 'uv_stdio_container_t']; static final skipStructFields = ['f_spare[4]', 'phys_addr[6]']; - static final allowNoCallback = ['uv_fs_cb']; + static final allowNoCallback = ['uv_fs_cb', 'uv_random_cb']; static final predefinedHxTypes = new Map(); static final hxTypesToGenerate = new Map(); @@ -81,8 +82,8 @@ class UVGenerator { cFile.writeString(cStr); hxFile.writeString(hxBinding(sig)); } else if(line.startsWith('typedef struct ') && line.endsWith('{')) { - for(sig in parseStruct(lines)) { - if(skipStructs.contains(sig.name)) + for(sig in parseStruct(line, lines)) { + if(sig.name == null || skipStructs.contains(sig.name)) continue; cFile.writeString(cStructBinding(sig)); hxFile.writeString(hxStructBinding(sig)); @@ -136,9 +137,24 @@ class UVGenerator { return new Path(new Path(new Path(generatorPath).dir).dir).dir; } - static function parseStruct(lines:Array):Array { + static function parseStructType(firstLine:String):Null { + var start = firstLine.indexOf('struct '); + if(start < 0) + return null; + start += 'struct '.length; + var end = firstLine.indexOf(' ', start); + if(end < 0) + return null; + var name = firstLine.substring(start, end); + if(name.endsWith('_s')) + name = name.substr(0, name.length - 2) + '_t'; + return name; + } + + static function parseStruct(firstLine:String, lines:Array):Array { var result = []; var fields = []; + var type = parseStructType(firstLine); var name = null; while(lines.length > 0) { var line = lines.shift(); @@ -151,10 +167,17 @@ class UVGenerator { line = line.substr(0, commentPos) + line.substr(line.indexOf('*/') + 2); line = line.trim(); //leave unions for manual handling - if(line.startsWith('union ')) + if(line.startsWith('union {')) { + parseStruct(line, lines); continue; + } if(line.startsWith('struct ') && line.endsWith('{')) { - result = result.concat(parseStruct(lines)); + var sub = parseStruct(line, lines); + if(sub.length > 0) { + result = result.concat(sub); + fields.push({name:sub[0].name, type:sub[0].type}); + sub[0].name = sub[0].type; + } } else { var splitPos = line.lastIndexOf(' '); if(line.endsWith(';')) @@ -167,7 +190,7 @@ class UVGenerator { } } } - result.push({name:name, fields:fields}); + result.unshift({type:type, name:name, fields:fields}); return result; } @@ -223,6 +246,7 @@ class UVGenerator { case 'uv_pid_t': '_I32'; case 'uv_buf_t': '_BUF'; case 'uv_dirent_type_t': '_I32'; + case 'cpu_times*': '_CPU_TIMES'; case _ if(type.endsWith('**')): '_REF(' + mapHLType(type.substr(0, type.length - 1)) + ')'; case _ if(type.startsWith('uv_')): @@ -385,14 +409,22 @@ class UVGenerator { return lines.join('\n') + '\n'; } - static function validateStructName(name:String):String { - if(!name.startsWith('uv_') || !name.endsWith('_t')) - throw new Skip('Struct $name does not conform naming pattern.'); - return name.substring('uv_'.length, name.length - '_t'.length); + static function detectStruct(name:String):{name:String, definePrim:String} { + if(name.startsWith('uv_') && name.endsWith('_t')) { + return { + name: name.substring('uv_'.length, name.length - '_t'.length), + definePrim: 'DEFINE_PRIM_UV_FIELD' + } + } else { + return { + name: name, + definePrim: 'DEFINE_PRIM_C_FIELD' + } + } } static function cStructBinding(sig:StructSignature):String { - var name = validateStructName(sig.name); + var s = detectStruct(sig.name); var cStruct = sig.name + '*'; var hlStruct = mapHLType(cStruct); var result = sig.fields @@ -400,17 +432,17 @@ class UVGenerator { .map(f -> { if(structFieldNeedsRef(f.type)) { var hlType = mapHLType(f.type + '*'); - 'DEFINE_PRIM_UV_FIELD_REF($hlType, ${f.type} *, $hlStruct, $name, ${f.name});'; + '${s.definePrim}_REF($hlType, ${f.type} *, $hlStruct, ${s.name}, ${f.name});'; } else { - 'DEFINE_PRIM_UV_FIELD(${mapHLType(f.type)}, ${f.type}, $hlStruct, $name, ${f.name});'; + '${s.definePrim}(${mapHLType(f.type)}, ${f.type}, $hlStruct, ${s.name}, ${f.name});'; } }); - result.push('DEFINE_PRIM_ALLOC($hlStruct, $name);'); + result.push('DEFINE_PRIM_ALLOC($hlStruct, ${s.name});'); return result.join('\n') + '\n'; } static function hxStructBinding(sig:StructSignature):String { - var name = validateStructName(sig.name); + var name = detectStruct(sig.name).name; var cStruct = sig.name + '*'; var hxStruct = mapHXType(cStruct); var result = sig.fields diff --git a/other/uvsample/MiscSample.hx b/other/uvsample/MiscSample.hx index 81743db0c..b09e39820 100644 --- a/other/uvsample/MiscSample.hx +++ b/other/uvsample/MiscSample.hx @@ -14,7 +14,23 @@ class MiscSample { print('RUsage: ' + Misc.getRUsage()); print('Pid: ' + Misc.getPid()); print('PPid: ' + Misc.getPPid()); + print('Cpu infos:\n ' + Misc.cpuInfo().map(Std.string).join('\n ')); + print('Inteface addresses:\n ' + Misc.interfaceAddresses().map(i -> { + Std.string({ + name:i.name, + physAddr:i.physAddr, + isInternal:i.isInternal, + address:i.address.name(), + netmask:i.netmask.name(), + }); + }).join('\n ')); + print('Load avg: ' + Misc.loadAvg()); + var addr = Misc.ip4Addr('127.0.0.1', 80); + print('Ip4Addr: ' + addr); + print('IpName: ' + Misc.ipName(addr)); + print('Cwd: ' + Misc.cwd()); print('Home dir: ' + Misc.homeDir()); + print('Temp dir: ' + Misc.tmpDir()); print('Passwd: ' + Misc.getPasswd()); print('Free mem: ' + Misc.getFreeMemory()); print('Total mem: ' + Misc.getTotalMemory()); @@ -27,9 +43,12 @@ class MiscSample { print('Uname: ' + Misc.uname()); print('Time of day: ' + Misc.getTimeOfDay()); var buf = new Bytes(20); + Misc.randomSync(buf, 20, 0); + print('Sync random bytes hex: ' + haxe.io.Bytes.ofData(new haxe.io.BytesData(buf, 20)).toHex()); + var buf = new Bytes(20); Misc.random(Thread.current().events, buf, 20, 0, e -> switch e { case UV_NOERR: - print('Random bytes hex: ' + haxe.io.Bytes.ofData(new haxe.io.BytesData(buf, 20)).toHex()); + print('Async random bytes hex: ' + haxe.io.Bytes.ofData(new haxe.io.BytesData(buf, 20)).toHex()); case _: throw new UVException(e); }); From cdc58327d9616ca58580dd3b9949e8c5c23e1c0a Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Wed, 1 Sep 2021 21:52:36 +0300 Subject: [PATCH 090/117] libuv 1.42 version requirement --- libs/uv/uv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index e02436e7b..6f35feb8b 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -7,8 +7,8 @@ # include #endif -#if (UV_VERSION_MAJOR <= 0) -# error "libuv1-dev required, uv version 0.x found" +#if (UV_VERSION_MAJOR <= 0 || UV_VERSION_MINOR < 42) +# error "libuv 1.42 or newer required" #endif // Common macros From 22509a49b9599f55de66f0ad07f470dcda563774 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Wed, 1 Sep 2021 22:51:01 +0300 Subject: [PATCH 091/117] rework SockAddr --- libs/uv/uv.c | 20 ++++++++++++++------ libs/uv/uv_generated.c | 4 ---- other/uvgenerator/UV.hx.header | 3 ++- other/uvgenerator/UVGenerator.hx | 2 +- other/uvsample/MiscSample.hx | 3 --- other/uvsample/PipeSample.hx | 2 +- other/uvsample/TcpSample.hx | 4 ++-- other/uvsample/UdpSample.hx | 4 ++-- 8 files changed, 22 insertions(+), 20 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 6f35feb8b..c59b22dfa 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -83,12 +83,6 @@ typedef struct sockaddr_in uv_sockaddr_in; typedef struct sockaddr_in6 uv_sockaddr_in6; typedef struct sockaddr_storage uv_sockaddr_storage; -// TODO { - static void on_uv_walk_cb( uv_handle_t* handle, void* arg ) { - } - -// } - #define UV_ALLOC(t) ((t*)malloc(sizeof(t))) #define DATA(t,h) ((t)h->data) @@ -588,6 +582,20 @@ DEFINE_PRIM_FREE(_LOOP, loop); DEFINE_PRIM_FREE(_SOCKADDR_STORAGE, sockaddr_storage); +HL_PRIM vdynamic *HL_NAME(sockaddr_storage_port)( uv_sockaddr_storage *addr ) { + UV_CHECK_NULL(addr,NULL); + int port; + if( addr->ss_family == AF_INET ) { + port = ntohs(((uv_sockaddr_in *)addr)->sin_port); + } else if( addr->ss_family == AF_INET6 ) { + port = ntohs(((uv_sockaddr_in6 *)addr)->sin6_port); + } else { + return NULL; + } + return hl_make_dyn(&port, &hlt_i32); +} +DEFINE_PRIM(_NULL(_I32), sockaddr_storage_port, _SOCKADDR_STORAGE); + DEFINE_PRIM_C_FIELD(_I32, int, _SOCKADDR_STORAGE, sockaddr_storage, ss_family); HL_PRIM struct sockaddr_storage *HL_NAME(alloc_sockaddr_storage)() { diff --git a/libs/uv/uv_generated.c b/libs/uv/uv_generated.c index baf067427..415055ba5 100644 --- a/libs/uv/uv_generated.c +++ b/libs/uv/uv_generated.c @@ -254,10 +254,6 @@ DEFINE_PRIM(_I32, backend_fd, _LOOP); DEFINE_PRIM(_I32, backend_timeout, _LOOP); DEFINE_PRIM(_U64, now, _LOOP); DEFINE_PRIM(_VOID, update_time, _LOOP); -HL_PRIM void HL_NAME(walk_with_cb)( uv_loop_t* loop, void* arg ) { - uv_walk(loop, on_uv_walk_cb, arg); -} -DEFINE_PRIM(_VOID, walk_with_cb, _LOOP _POINTER); DEFINE_PRIM(_I32, loop_fork, _LOOP); DEFINE_PRIM(_POINTER, loop_get_data, _LOOP); DEFINE_PRIM(_POINTER, loop_set_data, _LOOP _POINTER); diff --git a/other/uvgenerator/UV.hx.header b/other/uvgenerator/UV.hx.header index 5cdc25449..bbf02508f 100644 --- a/other/uvgenerator/UV.hx.header +++ b/other/uvgenerator/UV.hx.header @@ -157,6 +157,7 @@ extern class UV { static public function alloc_sockaddr_storage():CSockaddrStorageStar; static public function sockaddr_storage_size():Int; static public function sockaddr_storage_ss_family(addr:CSockaddrStorageStar):Int; + static public function sockaddr_storage_port(addr:CSockaddrStorageStar):Null; static public function free_sockaddr_storage(addr:CSockaddrStorageStar):Void; static public function sockaddr_of_storage(addr:CSockaddrStorageStar):CSockaddrStar; static public function sockaddr_to_storage(addr:CSockaddrStar):CSockaddrStorageStar; @@ -182,7 +183,7 @@ extern class UV { static public function addrinfo_ai_family(ai:CAddrinfoStar):AddressFamily; static public function addrinfo_ai_socktype(ai:CAddrinfoStar):SocketType; static public function addrinfo_ai_protocol(ai:CAddrinfoStar):Int; - static public function addrinfo_ai_addr(ai:CAddrinfoStar):SockAddr; + static public function addrinfo_ai_addr(ai:CAddrinfoStar):CSockaddrStorageStar; static public function addrinfo_ai_canonname(ai:CAddrinfoStar):Bytes; static public function addrinfo_ai_next(ai:CAddrinfoStar):Null; static public function nameinfo_flags_to_native(ai:NameInfoFlags):Int; diff --git a/other/uvgenerator/UVGenerator.hx b/other/uvgenerator/UVGenerator.hx index e33526a35..ecf06e6d3 100644 --- a/other/uvgenerator/UVGenerator.hx +++ b/other/uvgenerator/UVGenerator.hx @@ -39,7 +39,7 @@ class UVGenerator { 'uv_os_environ', 'uv_os_free_environ', 'uv_setup_args', 'uv_get_process_title', 'uv_set_process_title', 'uv_tcp_open', 'uv_udp_open', 'uv_socketpair', 'uv_buf_init', 'uv_inet_ntop', 'uv_inet_pton', 'uv_loadavg', 'uv_interface_addressses', - 'uv_loop_configure']; // TODO: don't skip uv_loop_configure + 'uv_walk', 'uv_loop_configure']; // TODO: don't skip uv_loop_configure static final skipStructs = ['uv_process_options_t', 'uv_utsname_t', 'uv_env_item_t', 'uv_dir_t', 'uv_stdio_container_t']; static final skipStructFields = ['f_spare[4]', 'phys_addr[6]']; diff --git a/other/uvsample/MiscSample.hx b/other/uvsample/MiscSample.hx index b09e39820..9dd5e0203 100644 --- a/other/uvsample/MiscSample.hx +++ b/other/uvsample/MiscSample.hx @@ -25,9 +25,6 @@ class MiscSample { }); }).join('\n ')); print('Load avg: ' + Misc.loadAvg()); - var addr = Misc.ip4Addr('127.0.0.1', 80); - print('Ip4Addr: ' + addr); - print('IpName: ' + Misc.ipName(addr)); print('Cwd: ' + Misc.cwd()); print('Home dir: ' + Misc.homeDir()); print('Temp dir: ' + Misc.tmpDir()); diff --git a/other/uvsample/PipeSample.hx b/other/uvsample/PipeSample.hx index f7de8e5cc..44fb4eefe 100644 --- a/other/uvsample/PipeSample.hx +++ b/other/uvsample/PipeSample.hx @@ -48,7 +48,7 @@ class PipeSample { client.readStart((e, data, bytesRead) -> switch e { case UV_NOERR: print('incoming request: ' + data.toBytes(bytesRead).toString()); - var addr = SockAddr.ipv4('93.184.216.34', 80); //http://example.com + var addr = Ip4Addr('93.184.216.34', 80); //http://example.com tcp.connect(addr, handle(() -> { print('tcp connected to ' + addr); client.write2(data, bytesRead, tcp, handle(() -> print('tcp sent'))); diff --git a/other/uvsample/TcpSample.hx b/other/uvsample/TcpSample.hx index 3f7acf166..c5eeab262 100644 --- a/other/uvsample/TcpSample.hx +++ b/other/uvsample/TcpSample.hx @@ -39,7 +39,7 @@ class TcpSample { } var loop = Thread.current().events; var server = Tcp.init(loop, INET); - server.bind(SockAddr.ipv4('0.0.0.0', PORT)); + server.bind(Ip4Addr('0.0.0.0', PORT)); server.listen(32, handle(() -> { var client = Tcp.init(loop); server.accept(client); @@ -67,7 +67,7 @@ class TcpSample { } var loop = Thread.current().events; var client = Tcp.init(loop, INET); - client.connect(SockAddr.ipv4('127.0.0.1', PORT), handle(() -> { + client.connect(Ip4Addr('127.0.0.1', PORT), handle(() -> { print('connected to ' + client.getPeerName()); var data = Bytes.ofString('Hello, world!').getData(); client.write(data.bytes, data.length, handle(() -> { diff --git a/other/uvsample/UdpSample.hx b/other/uvsample/UdpSample.hx index 7ea61f958..930980a44 100644 --- a/other/uvsample/UdpSample.hx +++ b/other/uvsample/UdpSample.hx @@ -20,7 +20,7 @@ class UdpSample { } var loop = Thread.current().events; var udp = Udp.init(loop, INET,true); - udp.bind(SockAddr.ipv4('0.0.0.0', PORT)); + udp.bind(Ip4Addr('0.0.0.0', PORT)); var cnt = 0; udp.recvStart((e, data, bytesRead, addr, flags) -> switch e { case UV_NOERR: @@ -49,7 +49,7 @@ class UdpSample { } var udp = Udp.init(Thread.current().events, INET); var data = Bytes.ofString('Hello, UDP!'); - udp.send(data.getData(), data.length, SockAddr.ipv4('127.0.0.1', PORT), e -> switch e { + udp.send(data.getData(), data.length, Ip4Addr('127.0.0.1', PORT), e -> switch e { case UV_NOERR: print('Message sent'); udp.close(() -> print('Done')); From c081d065906e73377c0c30af91750c6568770997 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Thu, 2 Sep 2021 10:56:19 +0300 Subject: [PATCH 092/117] fs --- libs/uv/uv.c | 44 ++++++++++++++++++++++++++++++++-- other/uvgenerator/UV.hx.header | 3 +++ other/uvsample/FileSample.hx | 2 +- 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index c59b22dfa..8edea521a 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -985,8 +985,9 @@ static void on_uv_fs_cb( uv_fs_t *r ) { hl_call1(void, c, uv_fs_t *, r); } -DEFINE_PRIM_OF_POINTER(_DIR,dir); -DEFINE_PRIM_FREE(_DIR,dir); +DEFINE_PRIM_OF_POINTER(_STATFS, statfs); +DEFINE_PRIM_OF_POINTER(_DIR, dir); +DEFINE_PRIM_FREE(_DIR, dir); HL_PRIM void HL_NAME(dir_init)( uv_dir_t *dir, int num_entries ) { dir->nentries = num_entries; @@ -1002,6 +1003,45 @@ DEFINE_PRIM(_DIRENT, dir_dirent, _DIR _I32); DEFINE_PRIM_UV_FIELD(_I32, int, _DIR, dir, nentries); DEFINE_PRIM_FREE(_DIRENT, dirent); +HL_PRIM int HL_NAME(translate_to_sys_file_open_flag)( int hx_flag ) { + switch( hx_flag ) { + case 0: return UV_FS_O_APPEND; + case 1: return UV_FS_O_CREAT; + case 2: return UV_FS_O_DIRECT; + case 3: return UV_FS_O_DIRECTORY; + case 4: return UV_FS_O_DSYNC; + case 5: return UV_FS_O_EXCL; + case 6: return UV_FS_O_EXLOCK; + case 7: return UV_FS_O_FILEMAP; + case 8: return UV_FS_O_NOATIME; + case 9: return UV_FS_O_NOCTTY; + case 10: return UV_FS_O_NOFOLLOW; + case 11: return UV_FS_O_NONBLOCK; + case 12: return UV_FS_O_RANDOM; + case 13: return UV_FS_O_RDONLY; + case 14: return UV_FS_O_RDWR; + case 15: return UV_FS_O_SEQUENTIAL; + case 16: return UV_FS_O_SHORT_LIVED; + case 17: return UV_FS_O_SYMLINK; + case 18: return UV_FS_O_SYNC; + case 19: return UV_FS_O_TEMPORARY; + case 20: return UV_FS_O_TRUNC; + case 21: return UV_FS_O_WRONLY; + default: hl_error("Unknown file open flag index: %d", hx_flag); + } +} +DEFINE_PRIM(_I32, translate_to_sys_file_open_flag, _I32); + +HL_PRIM varray *HL_NAME(statfs_f_spare)( uv_statfs_t *stat ) { + varray *a = hl_alloc_array(&hlt_i64, 4); + hl_aptr(a,double)[0] = stat->f_spare[0]; + hl_aptr(a,double)[1] = stat->f_spare[1]; + hl_aptr(a,double)[2] = stat->f_spare[2]; + hl_aptr(a,double)[3] = stat->f_spare[3]; + return a; +} +DEFINE_PRIM(_ARR, statfs_f_spare, _STATFS); + // Tty DEFINE_PRIM_ALLOC(_TTY, tty); diff --git a/other/uvgenerator/UV.hx.header b/other/uvgenerator/UV.hx.header index bbf02508f..ecc4b46db 100644 --- a/other/uvgenerator/UV.hx.header +++ b/other/uvgenerator/UV.hx.header @@ -225,6 +225,9 @@ extern class UV { static public function utsname_version(u:UvUtsnameTStar):Bytes; static public function utsname_machine(u:UvUtsnameTStar):Bytes; static public function alloc_random():UvRandomTStar; + static public function translate_to_sys_file_open_flag(fileOpenFlagIndex:Int):Int; + static public function pointer_to_statfs(ptr:Pointer):UvStatfsTStar; + static public function statfs_f_spare(statfs:UvStatfsTStar):NativeArray; // Auto generated content : diff --git a/other/uvsample/FileSample.hx b/other/uvsample/FileSample.hx index 86155b319..d5e10f514 100644 --- a/other/uvsample/FileSample.hx +++ b/other/uvsample/FileSample.hx @@ -174,7 +174,7 @@ class FileSample { Log.print('fstat on $path...'); File.open(loop, path, [O_CREAT(420)], (e, file) -> handle(() -> { file.fstat(loop, (e, fstat) -> handle(() -> { - Log.print('got fstat'); + Log.print('got fstat: $fstat'); file.close(loop, handle(() -> { Log.print('stat on $path'); File.stat(loop, path, (e, stat) -> handle(() -> { From 965f4f458b32f7f32ee9dc82bcf3cda51aa03ad1 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Thu, 2 Sep 2021 17:03:41 +0300 Subject: [PATCH 093/117] update DirSample for sync functions --- other/uvsample/DirSample.hx | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/other/uvsample/DirSample.hx b/other/uvsample/DirSample.hx index f62d9b6c7..c24cf111d 100644 --- a/other/uvsample/DirSample.hx +++ b/other/uvsample/DirSample.hx @@ -1,3 +1,4 @@ +import hl.uv.DirSync; import hl.uv.Misc; import hl.uv.Dir; import hl.uv.UVException; @@ -5,10 +6,28 @@ import sys.thread.Thread; class DirSample { public static function main() { - var loop = Thread.current().events; var path = Misc.tmpDir(); - inline function print(msg) - Log.print('DIR: $msg'); + + runSync(path); + runAsync(path); + } + + static inline function print(msg) { + Log.print('DIR: $msg'); + } + + static function runSync(path:String) { + print('SYNC functions:'); + var dir = DirSync.open(path); + var entries = dir.sync.read(3); + for(i in 0...entries.length) + print('\t${entries[i]}'); + dir.sync.close(); + } + + static function runAsync(path:String) { + var loop = Thread.current().events; + print('ASYNC functions:'); Dir.open(loop, path, (e, dir) -> switch e { case UV_NOERR: dir.read(loop, 3, (e, entries) -> switch e { From 7e9018c3a1e2d4b39b0b67644d90c6a8bfc6577a Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Thu, 2 Sep 2021 19:09:28 +0300 Subject: [PATCH 094/117] FileSync sample --- other/uvsample/DirSample.hx | 1 - other/uvsample/FileSample.hx | 4 - other/uvsample/FileSyncSample.hx | 268 +++++++++++++++++++++++++++++++ other/uvsample/UVSample.hx | 1 + 4 files changed, 269 insertions(+), 5 deletions(-) create mode 100644 other/uvsample/FileSyncSample.hx diff --git a/other/uvsample/DirSample.hx b/other/uvsample/DirSample.hx index c24cf111d..791235d7a 100644 --- a/other/uvsample/DirSample.hx +++ b/other/uvsample/DirSample.hx @@ -7,7 +7,6 @@ import sys.thread.Thread; class DirSample { public static function main() { var path = Misc.tmpDir(); - runSync(path); runAsync(path); } diff --git a/other/uvsample/FileSample.hx b/other/uvsample/FileSample.hx index d5e10f514..3838015ff 100644 --- a/other/uvsample/FileSample.hx +++ b/other/uvsample/FileSample.hx @@ -1,8 +1,4 @@ -import hl.uv.Dir; -import sys.io.Process; -import hl.uv.Loop; import haxe.Constraints.Function; -import haxe.xml.Access; import hl.I64; import haxe.io.Bytes; import hl.uv.UVError; diff --git a/other/uvsample/FileSyncSample.hx b/other/uvsample/FileSyncSample.hx new file mode 100644 index 000000000..f01290723 --- /dev/null +++ b/other/uvsample/FileSyncSample.hx @@ -0,0 +1,268 @@ +import haxe.Constraints.Function; +import hl.I64; +import haxe.io.Bytes; +import hl.uv.UVError; +import haxe.PosInfos; +import hl.uv.Misc; +import hl.uv.UVException; +import sys.thread.Thread; +import hl.uv.File; +import hl.uv.FileSync; +import FileSample.Actions; + +class FileSyncSample { + public static function main() { + var actions:Actions = [ + createWriteSyncReadUnlink, + mkdirRenameRmdir, + mkdtempRmdir, + mkstempUnlink, + statFStat, + statFs, + truncate, + copyFile, + // sendFile, // Throws EBADF and idk why + access, + chmod, + utime, + linkSymlinkReadLinkRealPath, + chown, + ]; + actions.next(); + } + + static function createFile(path:String, content:Bytes, ?pos:PosInfos) { + try { + var file = FileSync.open(path, [O_CREAT(420),O_TRUNC,O_WRONLY]); + file.sync.write(content.getData(), content.length, I64.ofInt(0)); + file.sync.close(); + } catch(e:UVException) { + throw new UVException(e.error, pos.fileName + ':' + pos.lineNumber + ': ' + e.error.toString(), e); + } + } + + static function readFile(path:String):Bytes { + var file = FileSync.open(path, [O_RDONLY]); + var buf = new hl.Bytes(10240); + var bytesRead = file.sync.read(buf, 10240, I64.ofInt(0)); + file.sync.close(); + return buf.toBytes(bytesRead.toInt()); + } + + static function deleteFiles(files:Array) { + for(path in files) + FileSync.unlink(path); + } + + static function createWriteSyncReadUnlink(actions:Actions) { + var path = Misc.tmpDir() + '/test-file'; + Log.print('Creating $path for writing...'); + var file = FileSync.open(path, [O_CREAT(420), O_WRONLY]); + Log.print('Writing...'); + var data = Bytes.ofString('Hello, world!'); + var bytesWritten = file.sync.write(data.getData(), data.length, I64.ofInt(0)); + Log.print('$bytesWritten bytes written: $data'); + Log.print('fsync...'); + file.sync.fsync(); + Log.print('fdatasync...'); + file.sync.fdataSync(); + file.sync.close(); + Log.print('closed $path'); + readUnlink(path, actions); + } + + static function readUnlink(path:String, actions:Actions) { + Log.print('Opening $path for reading...'); + var file = FileSync.open(path, [O_RDONLY]); + Log.print('Reading...'); + var buf = new hl.Bytes(1024); + var bytesRead = file.sync.read(buf, 1024, I64.ofInt(0)); + Log.print('$bytesRead bytes read: ' + buf.toBytes(bytesRead.toInt())); + file.sync.close(); + Log.print('closed $path'); + unlink(path, actions); + } + + static function unlink(path:String, actions:Actions) { + Log.print('Unlinking $path...'); + FileSync.unlink(path); + actions.next(); + } + + static function mkdirRenameRmdir(actions:Actions) { + var path = Misc.tmpDir() + '/test-dir'; + var newPath = Misc.tmpDir() + '/test-dir2'; + Log.print('Creating directory $path...'); + FileSync.mkdir(path, 511); + Log.print('Renaming $path to $newPath...'); + FileSync.rename(path, newPath); + Log.print('Removing directory $newPath...'); + FileSync.rmdir(newPath); + Log.print('Done'); + actions.next(); + } + + static function mkdtempRmdir(actions:Actions) { + var tpl = Misc.tmpDir() + '/test-dir-XXXXXX'; + Log.print('Creating temp directory with tpl $tpl...'); + var path = FileSync.mkdtemp(tpl); + Log.print('Removing directory $path...'); + FileSync.rmdir(path); + Log.print('Done'); + actions.next(); + } + + static function mkstempUnlink(actions:Actions) { + var tpl = Misc.tmpDir() + '/test-file-XXXXXX'; + Log.print('Creating temp file with tpl $tpl...'); + var tmp = FileSync.mkstemp(tpl); + Log.print('Closing ${tmp.path}...'); + tmp.file.sync.close(); + Log.print('Unlinking ${tmp.path}...'); + FileSync.unlink(tmp.path); + Log.print('Done'); + actions.next(); + } + + static function statFStat(actions:Actions) { + var path = Misc.tmpDir() + '/test-file'; + Log.print('fstat on $path...'); + var file = FileSync.open(path, [O_CREAT(420)]); + var fstat = file.sync.fstat(); + Log.print('got fstat: $fstat'); + file.sync.close(); + Log.print('stat on $path'); + var stat = FileSync.stat(path); + Log.print('got stat: $stat'); + // TODO: jit error on I64 == I64 + // var ok = stat.dev == fstat.dev; + // && stat.mode == fstat.mode + // && stat.nlink == fstat.nlink + // && stat.uid == fstat.uid + // && stat.gid == fstat.gid + // && stat.rdev == fstat.rdev + // && stat.ino == fstat.ino + // && stat.size == fstat.size + // && stat.blksize == fstat.blksize + // && stat.blocks == fstat.blocks + // && stat.flags == fstat.flags + // && stat.gen == fstat.gen; + // Log.print('fstat equals stat: $ok'); + deleteFiles([path]); + Log.print('Done'); + actions.next(); + } + + static function statFs(actions:Actions) { + Log.print('statfs on .'); + var stat = FileSync.statFs('.'); + Log.print('got statfs: $stat'); + Log.print('Done'); + actions.next(); + } + + static function truncate(actions:Actions) { + var path = Misc.tmpDir() + '/test-file-truncate'; + var content = '1234567890'; + Log.print('Writing content for truncation at $path: $content'); + createFile(path, Bytes.ofString(content)); + var file = FileSync.open(path, [O_WRONLY]); + Log.print('truncating at 5...'); + file.sync.ftruncate(I64.ofInt(5)); + file.sync.close(); + var data = readFile(path); + Log.print('Content after truncation (length=${data.length}): $data'); + deleteFiles([path]); + Log.print('Done'); + actions.next(); + } + + static function copyFile(actions:Actions) { + var path = Misc.tmpDir() + '/test-file-copy'; + var newPath = '$path-copy'; + createFile(path, Bytes.ofString('123')); + Log.print('Copy $path to $newPath'); + FileSync.copyFile(path, newPath, [EXCL]); + deleteFiles([path, newPath]); + Log.print('Done'); + actions.next(); + } + + static function sendFile(actions:Actions) { + var path = Misc.tmpDir() + '/test-file-send'; + var newPath = '$path-copy'; + createFile(path, Bytes.ofString('12345678')); + var src = FileSync.open(path, [O_RDONLY]); + var dst = FileSync.open(newPath, [O_CREAT(420), O_WRONLY]); + Log.print('sendFile from $path to $newPath...'); + var outOffset = src.sync.sendFile(dst, I64.ofInt(0), I64.ofInt(20)); + Log.print('sendfile stopped at $outOffset'); + src.sync.close(); + dst.sync.close(); + deleteFiles([path, newPath]); + Log.print('Done'); + actions.next(); + } + + static function access(actions:Actions) { + var path = Misc.tmpDir(); + Log.print('Checking write permissions on $path...'); + FileSync.access(path, [W_OK]); + Log.print('Done'); + actions.next(); + } + + static function chmod(actions:Actions) { + var path = Misc.tmpDir() + '/test-file-chmod'; + createFile(path, Bytes.ofString('123')); + Log.print('chmod on $path...'); + FileSync.chmod(path, 420); + deleteFiles([path]); + Log.print('Done'); + actions.next(); + } + + static function utime(actions:Actions) { + var path = Misc.tmpDir() + '/test-file-utime'; + createFile(path, Bytes.ofString('123')); + Log.print('utime on $path...'); + FileSync.utime(path, Date.now().getTime(), Date.now().getTime()); + deleteFiles([path]); + Log.print('Done'); + actions.next(); + } + + static function linkSymlinkReadLinkRealPath(actions:Actions) { + var path = Misc.tmpDir() + '/test-file-l'; + var newPath = Misc.tmpDir() + '/test-file-link'; + createFile(path, Bytes.ofString('123')); + Log.print('link $path to $newPath...'); + FileSync.link(path, newPath); + deleteFiles([newPath]); + Log.print('symlink $path to $newPath...'); + FileSync.symlink(path, newPath, [SYMLINK_JUNCTION]); + Log.print('readlink at $newPath...'); + var target = FileSync.readLink(newPath); + Log.print('Link content: $target'); + var real = FileSync.readLink(newPath); + Log.print('Real path of $newPath: $real'); + deleteFiles([path, newPath]); + Log.print('Done'); + actions.next(); + } + + static function chown(actions:Actions) { + if(Sys.systemName() == 'Windows') { + actions.next(); + return; + } + + var path = Misc.tmpDir() + '/test-file-chown'; + createFile(path, Bytes.ofString('')); + Log.print('chown on $path...'); + FileSync.chown(path, -1, -1); + deleteFiles([path]); + Log.print('Done'); + actions.next(); + } +} \ No newline at end of file diff --git a/other/uvsample/UVSample.hx b/other/uvsample/UVSample.hx index ef8556c7d..02375963e 100644 --- a/other/uvsample/UVSample.hx +++ b/other/uvsample/UVSample.hx @@ -9,6 +9,7 @@ class UVSample { // UdpSample.main(); // PipeSample.main(); // ProcessSample.main(); + // FileSyncSample.main(); // FileSample.main(); // DirSample.main(); // FsEventSample.main(); From 4fb4b4cd6007d8b3992d3307c6d4e9f6ff482c1d Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 3 Sep 2021 10:10:55 +0300 Subject: [PATCH 095/117] [ci] install automake & libtool --- other/azure-pipelines/build-linux.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/other/azure-pipelines/build-linux.yml b/other/azure-pipelines/build-linux.yml index 131023cf0..fe131b0d1 100644 --- a/other/azure-pipelines/build-linux.yml +++ b/other/azure-pipelines/build-linux.yml @@ -34,7 +34,8 @@ jobs: cmake \ make \ gcc \ - autotools-dev \ + automake \ + libtool \ libz-dev \ zlib1g-dev \ libpng-dev \ From 95aa1d6f54403abab73d06c3ea70f0cd619f03b7 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 3 Sep 2021 10:25:59 +0300 Subject: [PATCH 096/117] [ci] install automake & lobtool for another arch too --- other/azure-pipelines/build-linux.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/other/azure-pipelines/build-linux.yml b/other/azure-pipelines/build-linux.yml index fe131b0d1..f8a0b4c92 100644 --- a/other/azure-pipelines/build-linux.yml +++ b/other/azure-pipelines/build-linux.yml @@ -66,7 +66,8 @@ jobs: cmake \ make \ gcc-multilib \ - autotools-dev \ + automake \ + libtool \ libz-dev:${{ parameters.arch }} \ zlib1g-dev:${{ parameters.arch }} \ libpng-dev:${{ parameters.arch }} \ From 1444e2d24a7ed1524645b481fa16ee9defed069e Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 3 Sep 2021 10:29:33 +0300 Subject: [PATCH 097/117] [ci] check passwd --- other/azure-pipelines/build-linux.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/other/azure-pipelines/build-linux.yml b/other/azure-pipelines/build-linux.yml index f8a0b4c92..452aa1723 100644 --- a/other/azure-pipelines/build-linux.yml +++ b/other/azure-pipelines/build-linux.yml @@ -48,6 +48,7 @@ jobs: neko \ curl \ ca-certificates + cat /etc/passwd curl -fsSL -o "libuv-v1.42.0.tar.gz" --retry 3 https://dist.libuv.org/dist/v1.42.0/libuv-v1.42.0.tar.gz tar -xf libuv-v1.42.0.tar.gz cd libuv-v1.42.0 From d3ac523a39aab6254789c2e29c4f07fcf1d43f68 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 3 Sep 2021 11:21:06 +0300 Subject: [PATCH 098/117] [ci] don't run make check --- other/azure-pipelines/build-linux.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/other/azure-pipelines/build-linux.yml b/other/azure-pipelines/build-linux.yml index 452aa1723..9f930e200 100644 --- a/other/azure-pipelines/build-linux.yml +++ b/other/azure-pipelines/build-linux.yml @@ -48,14 +48,12 @@ jobs: neko \ curl \ ca-certificates - cat /etc/passwd curl -fsSL -o "libuv-v1.42.0.tar.gz" --retry 3 https://dist.libuv.org/dist/v1.42.0/libuv-v1.42.0.tar.gz tar -xf libuv-v1.42.0.tar.gz cd libuv-v1.42.0 sh autogen.sh ./configure make - make check sudo make install displayName: Install dependencies - ${{ if not(eq(parameters.arch, '')) }}: @@ -87,7 +85,6 @@ jobs: sh autogen.sh ./configure make - make check sudo make install displayName: Install dependencies - template: install-haxe-snapshot.yml From dce25a4aeb4e0f6a5cc153a1d167b8923f8532fa Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 3 Sep 2021 12:03:16 +0300 Subject: [PATCH 099/117] refactor samples --- other/uvsample/AllSample.hx | 23 +++++ other/uvsample/CheckSample.hx | 6 +- other/uvsample/DirSample.hx | 23 +---- other/uvsample/DirSyncSample.hx | 13 +++ other/uvsample/DnsSample.hx | 8 +- other/uvsample/FileSample.hx | 154 +++++++++++++++---------------- other/uvsample/FileSyncSample.hx | 150 +++++++++++++++--------------- other/uvsample/FsEventSample.hx | 8 +- other/uvsample/FsPollSample.hx | 10 +- other/uvsample/IdleSample.hx | 6 +- other/uvsample/MiscSample.hx | 8 +- other/uvsample/PipeSample.hx | 22 ++--- other/uvsample/PrepareSample.hx | 6 +- other/uvsample/ProcessSample.hx | 10 +- other/uvsample/SignalSample.hx | 8 +- other/uvsample/TcpSample.hx | 13 ++- other/uvsample/TtySample.hx | 9 +- other/uvsample/UVSample.hx | 40 ++++---- other/uvsample/UdpSample.hx | 18 ++-- other/uvsample/VersionSample.hx | 8 +- 20 files changed, 274 insertions(+), 269 deletions(-) create mode 100644 other/uvsample/AllSample.hx create mode 100644 other/uvsample/DirSyncSample.hx diff --git a/other/uvsample/AllSample.hx b/other/uvsample/AllSample.hx new file mode 100644 index 000000000..46af3be73 --- /dev/null +++ b/other/uvsample/AllSample.hx @@ -0,0 +1,23 @@ +class AllSample extends UVSample { + public function run() { + new CheckSample().run(); + new PrepareSample().run(); + new IdleSample().run(); + new TcpSample().run(); + new DnsSample().run(); + new UdpSample().run(); + new ProcessSample().run(); + new FileSyncSample().run(); + new FileSample().run(); + new DirSyncSample().run(); + new DirSample().run(); + new FsEventSample().run(); + new FsPollSample().run(); + new MiscSample().run(); + new TtySample().run(); + new SignalSample().run(); + new VersionSample().run(); + // This one requires manual execution + new PipeSample().run(); + } +} \ No newline at end of file diff --git a/other/uvsample/CheckSample.hx b/other/uvsample/CheckSample.hx index 62330bf5b..a8509aed4 100644 --- a/other/uvsample/CheckSample.hx +++ b/other/uvsample/CheckSample.hx @@ -3,8 +3,8 @@ import hl.uv.Timer; import hl.uv.Check; import sys.thread.Thread; -class CheckSample { - public static function main() { +class CheckSample extends UVSample { + public function run() { var loop = Thread.current().events; var timer = Timer.init(loop); timer.start(() -> { @@ -14,7 +14,7 @@ class CheckSample { var check = Check.init(loop); check.start(() -> { - Log.print('Check'); + print('Check'); check.stop(); check.close(); }); diff --git a/other/uvsample/DirSample.hx b/other/uvsample/DirSample.hx index 791235d7a..d76fd166f 100644 --- a/other/uvsample/DirSample.hx +++ b/other/uvsample/DirSample.hx @@ -4,27 +4,10 @@ import hl.uv.Dir; import hl.uv.UVException; import sys.thread.Thread; -class DirSample { - public static function main() { - var path = Misc.tmpDir(); - runSync(path); - runAsync(path); - } - - static inline function print(msg) { - Log.print('DIR: $msg'); - } +class DirSample extends UVSample { - static function runSync(path:String) { - print('SYNC functions:'); - var dir = DirSync.open(path); - var entries = dir.sync.read(3); - for(i in 0...entries.length) - print('\t${entries[i]}'); - dir.sync.close(); - } - - static function runAsync(path:String) { + public function run() { + var path = Misc.tmpDir(); var loop = Thread.current().events; print('ASYNC functions:'); Dir.open(loop, path, (e, dir) -> switch e { diff --git a/other/uvsample/DirSyncSample.hx b/other/uvsample/DirSyncSample.hx new file mode 100644 index 000000000..ba3ee7baf --- /dev/null +++ b/other/uvsample/DirSyncSample.hx @@ -0,0 +1,13 @@ +import hl.uv.DirSync; +import hl.uv.Misc; + +class DirSyncSample extends UVSample { + public function run() { + var path = Misc.tmpDir(); + var dir = DirSync.open(path); + var entries = dir.sync.read(3); + for(i in 0...entries.length) + print('\t${entries[i]}'); + dir.sync.close(); + } +} \ No newline at end of file diff --git a/other/uvsample/DnsSample.hx b/other/uvsample/DnsSample.hx index 82039903c..9ad254774 100644 --- a/other/uvsample/DnsSample.hx +++ b/other/uvsample/DnsSample.hx @@ -3,20 +3,20 @@ import hl.uv.UVException; import sys.thread.Thread; import hl.uv.Dns; -class DnsSample { - public static function main() { +class DnsSample extends UVSample { + public function run() { var loop = Thread.current().events; Dns.getAddrInfo(loop, 'haxe.org', 'http', { flags: AI_CANONNAME, family: INET }, (e, infos) -> switch e { case UV_NOERR: for(i in infos) { - Log.print('getAddrInfo: addr ${i.addr}, canonname ${i.canonName}'); + print('getAddrInfo: addr ${i.addr}, canonname ${i.canonName}'); if(i.canonName != null) { Dns.getNameInfo(loop, i.addr, NI_NAMEREQD, (e, name, service) -> switch e { case UV_NOERR: - Log.print('getNameInfo: host $name, service $service'); + print('getNameInfo: host $name, service $service'); case _: throw new UVException(e); } diff --git a/other/uvsample/FileSample.hx b/other/uvsample/FileSample.hx index 3838015ff..7fede007e 100644 --- a/other/uvsample/FileSample.hx +++ b/other/uvsample/FileSample.hx @@ -12,16 +12,16 @@ abstract Actions(Array) from Array { public function next() { var fn = this.shift(); if(fn != null) { - Log.print('-----------'); + print('-----------'); fn(this); } } } -class FileSample { +class FileSample extends UVSample { static final loop = Thread.current().events; - public static function main() { + public function run() { var actions:Actions = [ createWriteSyncReadUnlink, mkdirRenameRmdir, @@ -41,14 +41,14 @@ class FileSample { actions.next(); } - static function handle(success:()->Void, ?p:PosInfos):(e:UVError)->Void { + function handle(success:()->Void, ?p:PosInfos):(e:UVError)->Void { return e -> switch e { case UV_NOERR: success(); case _: throw new UVException(e, p.fileName + ':' + p.lineNumber + ': ' + e.toString()); } } - static function createFile(path:String, content:Bytes, callback:()->Void, ?pos:PosInfos) { + function createFile(path:String, content:Bytes, callback:()->Void, ?pos:PosInfos) { File.open(loop, path, [O_CREAT(420),O_TRUNC,O_WRONLY], (e, file) -> handle(() -> { file.write(loop, content.getData(), content.length, I64.ofInt(0), (e, bytesWritten) -> handle(() -> { file.close(loop, handle(callback, pos)); @@ -56,7 +56,7 @@ class FileSample { }, pos)(e)); } - static function readFile(path:String, callback:(data:Bytes)->Void) { + function readFile(path:String, callback:(data:Bytes)->Void) { File.open(loop, path, [O_RDONLY], (e, file) -> handle(() -> { var buf = new hl.Bytes(10240); file.read(loop, buf, 10240, I64.ofInt(0), (e, bytesRead) -> handle(() -> { @@ -67,7 +67,7 @@ class FileSample { })(e)); } - static function deleteFiles(files:Array, callback:()->Void) { + function deleteFiles(files:Array, callback:()->Void) { var finished = 0; for(path in files) { File.unlink(loop, path, handle(() -> { @@ -78,20 +78,20 @@ class FileSample { } } - static function createWriteSyncReadUnlink(actions:Actions) { + function createWriteSyncReadUnlink(actions:Actions) { var path = Misc.tmpDir() + '/test-file'; - Log.print('Creating $path for writing...'); + print('Creating $path for writing...'); File.open(loop, path, [O_CREAT(420), O_WRONLY], (e, file) -> handle(() -> { - Log.print('Writing...'); + print('Writing...'); var data = Bytes.ofString('Hello, world!'); file.write(loop, data.getData(), data.length, I64.ofInt(0), (e, bytesWritten) -> handle(() -> { - Log.print('$bytesWritten bytes written: $data'); - Log.print('fsync...'); + print('$bytesWritten bytes written: $data'); + print('fsync...'); file.fsync(loop, handle(() -> { - Log.print('fdatasync...'); + print('fdatasync...'); file.fdataSync(loop, handle(() -> { file.close(loop, handle(() -> { - Log.print('closed $path'); + print('closed $path'); readUnlink(path, actions); })); })); @@ -100,81 +100,81 @@ class FileSample { })(e)); } - static function readUnlink(path:String, actions:Actions) { - Log.print('Opening $path for reading...'); + function readUnlink(path:String, actions:Actions) { + print('Opening $path for reading...'); File.open(loop, path, [O_RDONLY], (e, file) -> handle(() -> { - Log.print('Reading...'); + print('Reading...'); var buf = new hl.Bytes(1024); file.read(loop, buf, 1024, I64.ofInt(0), (e, bytesRead) -> handle(() -> { - Log.print('$bytesRead bytes read: ' + buf.toBytes(bytesRead.toInt())); + print('$bytesRead bytes read: ' + buf.toBytes(bytesRead.toInt())); file.close(loop, handle(() -> { - Log.print('closed $path'); + print('closed $path'); unlink(path, actions); })); })(e)); })(e)); } - static function unlink(path:String, actions:Actions) { - Log.print('Unlinking $path...'); + function unlink(path:String, actions:Actions) { + print('Unlinking $path...'); File.unlink(loop, path, handle(() -> { actions.next(); })); } - static function mkdirRenameRmdir(actions:Actions) { + function mkdirRenameRmdir(actions:Actions) { var path = Misc.tmpDir() + '/test-dir'; var newPath = Misc.tmpDir() + '/test-dir2'; - Log.print('Creating directory $path...'); + print('Creating directory $path...'); File.mkdir(loop, path, 511, handle(() -> { - Log.print('Renaming $path to $newPath...'); + print('Renaming $path to $newPath...'); File.rename(loop, path, newPath, handle(() -> { - Log.print('Removing directory $newPath...'); + print('Removing directory $newPath...'); File.rmdir(loop, newPath, handle(() -> { - Log.print('Done'); + print('Done'); actions.next(); })); })); })); } - static function mkdtempRmdir(actions:Actions) { + function mkdtempRmdir(actions:Actions) { var tpl = Misc.tmpDir() + '/test-dir-XXXXXX'; - Log.print('Creating temp directory with tpl $tpl...'); + print('Creating temp directory with tpl $tpl...'); File.mkdtemp(loop, tpl, (e, path) -> handle(() -> { - Log.print('Removing directory $path...'); + print('Removing directory $path...'); File.rmdir(loop, path, handle(() -> { - Log.print('Done'); + print('Done'); actions.next(); })); })(e)); } - static function mkstempUnlink(actions:Actions) { + function mkstempUnlink(actions:Actions) { var tpl = Misc.tmpDir() + '/test-file-XXXXXX'; - Log.print('Creating temp file with tpl $tpl...'); + print('Creating temp file with tpl $tpl...'); File.mkstemp(loop, tpl, (e, file, path) -> handle(() -> { - Log.print('Closing $path...'); + print('Closing $path...'); file.close(loop, handle(() -> { - Log.print('Unlinking $path...'); + print('Unlinking $path...'); File.unlink(loop, path, handle(() -> { - Log.print('Done'); + print('Done'); actions.next(); })); })); })(e)); } - static function statFStat(actions:Actions) { + function statFStat(actions:Actions) { var path = Misc.tmpDir() + '/test-file'; - Log.print('fstat on $path...'); + print('fstat on $path...'); File.open(loop, path, [O_CREAT(420)], (e, file) -> handle(() -> { file.fstat(loop, (e, fstat) -> handle(() -> { - Log.print('got fstat: $fstat'); + print('got fstat: $fstat'); file.close(loop, handle(() -> { - Log.print('stat on $path'); + print('stat on $path'); File.stat(loop, path, (e, stat) -> handle(() -> { - Log.print('got stat: $stat'); + print('got stat: $stat'); // TODO: jit error on I64 == I64 // var ok = stat.dev == fstat.dev; // && stat.mode == fstat.mode @@ -188,9 +188,9 @@ class FileSample { // && stat.blocks == fstat.blocks // && stat.flags == fstat.flags // && stat.gen == fstat.gen; - // Log.print('fstat equals stat: $ok'); + // print('fstat equals stat: $ok'); deleteFiles([path], () -> { - Log.print('Done'); + print('Done'); actions.next(); }); })(e)); @@ -199,28 +199,28 @@ class FileSample { })(e)); } - static function statFs(actions:Actions) { - Log.print('statfs on .'); + function statFs(actions:Actions) { + print('statfs on .'); File.statFs(loop, '.', (e, stat) -> handle(() -> { - Log.print('got statfs: $stat'); - Log.print('Done'); + print('got statfs: $stat'); + print('Done'); actions.next(); })(e)); } - static function truncate(actions:Actions) { + function truncate(actions:Actions) { var path = Misc.tmpDir() + '/test-file-truncate'; var content = '1234567890'; - Log.print('Writing content for truncation at $path: $content'); + print('Writing content for truncation at $path: $content'); createFile(path, Bytes.ofString(content), () -> { File.open(loop, path, [O_WRONLY], (e, file) -> handle(() -> { - Log.print('truncating at 5...'); + print('truncating at 5...'); file.ftruncate(loop, I64.ofInt(5), handle(() -> { file.close(loop, handle(() -> { readFile(path, data -> { - Log.print('Content after truncation (length=${data.length}): $data'); + print('Content after truncation (length=${data.length}): $data'); deleteFiles([path], () -> { - Log.print('Done'); + print('Done'); actions.next(); }); }); @@ -230,33 +230,33 @@ class FileSample { }); } - static function copyFile(actions:Actions) { + function copyFile(actions:Actions) { var path = Misc.tmpDir() + '/test-file-copy'; var newPath = '$path-copy'; createFile(path, Bytes.ofString('123'), () -> { - Log.print('Copy $path to $newPath'); + print('Copy $path to $newPath'); File.copyFile(loop, path, newPath, [EXCL], handle(() -> { deleteFiles([path, newPath], () -> { - Log.print('Done'); + print('Done'); actions.next(); }); })); }); } - static function sendFile(actions:Actions) { + function sendFile(actions:Actions) { var path = Misc.tmpDir() + '/test-file-send'; var newPath = '$path-copy'; createFile(path, Bytes.ofString('12345678'), () -> { File.open(loop, path, [O_RDONLY], (e, src) -> handle(() -> { File.open(loop, newPath, [O_CREAT(420), O_WRONLY], (e, dst) -> handle(() -> { - Log.print('sendFile from $path to $newPath...'); + print('sendFile from $path to $newPath...'); src.sendFile(loop, dst, I64.ofInt(0), I64.ofInt(20), (e, outOffset) -> handle(() -> { - Log.print('sendfile stopped at $outOffset'); + print('sendfile stopped at $outOffset'); src.close(loop, handle(() -> { dst.close(loop, handle(() -> { deleteFiles([path, newPath], () -> { - Log.print('Done'); + print('Done'); actions.next(); }); })); @@ -267,57 +267,57 @@ class FileSample { }); } - static function access(actions:Actions) { + function access(actions:Actions) { var path = Misc.tmpDir(); - Log.print('Checking write permissions on $path...'); + print('Checking write permissions on $path...'); File.access(loop, path, [W_OK], handle(() -> { - Log.print('Done'); + print('Done'); actions.next(); })); } - static function chmod(actions:Actions) { + function chmod(actions:Actions) { var path = Misc.tmpDir() + '/test-file-chmod'; createFile(path, Bytes.ofString('123'), () -> { - Log.print('chmod on $path...'); + print('chmod on $path...'); File.chmod(loop, path, 420, handle(() -> { deleteFiles([path], () -> { - Log.print('Done'); + print('Done'); actions.next(); }); })); }); } - static function utime(actions:Actions) { + function utime(actions:Actions) { var path = Misc.tmpDir() + '/test-file-utime'; createFile(path, Bytes.ofString('123'), () -> { - Log.print('utime on $path...'); + print('utime on $path...'); File.utime(loop, path, Date.now().getTime(), Date.now().getTime(), handle(() -> { deleteFiles([path], () -> { - Log.print('Done'); + print('Done'); actions.next(); }); })); }); } - static function linkSymlinkReadLinkRealPath(actions:Actions) { + function linkSymlinkReadLinkRealPath(actions:Actions) { var path = Misc.tmpDir() + '/test-file-l'; var newPath = Misc.tmpDir() + '/test-file-link'; createFile(path, Bytes.ofString('123'), () -> { - Log.print('link $path to $newPath...'); + print('link $path to $newPath...'); File.link(loop, path, newPath, handle(() -> { deleteFiles([newPath], () -> { - Log.print('symlink $path to $newPath...'); + print('symlink $path to $newPath...'); File.symlink(loop, path, newPath, [SYMLINK_JUNCTION], handle(() -> { - Log.print('readlink at $newPath...'); + print('readlink at $newPath...'); File.readLink(loop, newPath, (e, target) -> handle(() -> { - Log.print('Link content: $target'); + print('Link content: $target'); File.readLink(loop, newPath, (e, real) -> handle(() -> { - Log.print('Real path of $newPath: $real'); + print('Real path of $newPath: $real'); deleteFiles([path, newPath], () -> { - Log.print('Done'); + print('Done'); actions.next(); }); })(e)); @@ -328,7 +328,7 @@ class FileSample { }); } - static function chown(actions:Actions) { + function chown(actions:Actions) { if(Sys.systemName() == 'Windows') { actions.next(); return; @@ -336,10 +336,10 @@ class FileSample { var path = Misc.tmpDir() + '/test-file-chown'; createFile(path, Bytes.ofString(''), () -> { - Log.print('chown on $path...'); + print('chown on $path...'); File.chown(loop, path, -1, -1, handle(() -> { deleteFiles([path], () -> { - Log.print('Done'); + print('Done'); actions.next(); }); })); diff --git a/other/uvsample/FileSyncSample.hx b/other/uvsample/FileSyncSample.hx index f01290723..82b8b7584 100644 --- a/other/uvsample/FileSyncSample.hx +++ b/other/uvsample/FileSyncSample.hx @@ -10,8 +10,8 @@ import hl.uv.File; import hl.uv.FileSync; import FileSample.Actions; -class FileSyncSample { - public static function main() { +class FileSyncSample extends UVSample { + public function run() { var actions:Actions = [ createWriteSyncReadUnlink, mkdirRenameRmdir, @@ -31,7 +31,7 @@ class FileSyncSample { actions.next(); } - static function createFile(path:String, content:Bytes, ?pos:PosInfos) { + function createFile(path:String, content:Bytes, ?pos:PosInfos) { try { var file = FileSync.open(path, [O_CREAT(420),O_TRUNC,O_WRONLY]); file.sync.write(content.getData(), content.length, I64.ofInt(0)); @@ -41,7 +41,7 @@ class FileSyncSample { } } - static function readFile(path:String):Bytes { + function readFile(path:String):Bytes { var file = FileSync.open(path, [O_RDONLY]); var buf = new hl.Bytes(10240); var bytesRead = file.sync.read(buf, 10240, I64.ofInt(0)); @@ -49,91 +49,91 @@ class FileSyncSample { return buf.toBytes(bytesRead.toInt()); } - static function deleteFiles(files:Array) { + function deleteFiles(files:Array) { for(path in files) FileSync.unlink(path); } - static function createWriteSyncReadUnlink(actions:Actions) { + function createWriteSyncReadUnlink(actions:Actions) { var path = Misc.tmpDir() + '/test-file'; - Log.print('Creating $path for writing...'); + print('Creating $path for writing...'); var file = FileSync.open(path, [O_CREAT(420), O_WRONLY]); - Log.print('Writing...'); + print('Writing...'); var data = Bytes.ofString('Hello, world!'); var bytesWritten = file.sync.write(data.getData(), data.length, I64.ofInt(0)); - Log.print('$bytesWritten bytes written: $data'); - Log.print('fsync...'); + print('$bytesWritten bytes written: $data'); + print('fsync...'); file.sync.fsync(); - Log.print('fdatasync...'); + print('fdatasync...'); file.sync.fdataSync(); file.sync.close(); - Log.print('closed $path'); + print('closed $path'); readUnlink(path, actions); } - static function readUnlink(path:String, actions:Actions) { - Log.print('Opening $path for reading...'); + function readUnlink(path:String, actions:Actions) { + print('Opening $path for reading...'); var file = FileSync.open(path, [O_RDONLY]); - Log.print('Reading...'); + print('Reading...'); var buf = new hl.Bytes(1024); var bytesRead = file.sync.read(buf, 1024, I64.ofInt(0)); - Log.print('$bytesRead bytes read: ' + buf.toBytes(bytesRead.toInt())); + print('$bytesRead bytes read: ' + buf.toBytes(bytesRead.toInt())); file.sync.close(); - Log.print('closed $path'); + print('closed $path'); unlink(path, actions); } - static function unlink(path:String, actions:Actions) { - Log.print('Unlinking $path...'); + function unlink(path:String, actions:Actions) { + print('Unlinking $path...'); FileSync.unlink(path); actions.next(); } - static function mkdirRenameRmdir(actions:Actions) { + function mkdirRenameRmdir(actions:Actions) { var path = Misc.tmpDir() + '/test-dir'; var newPath = Misc.tmpDir() + '/test-dir2'; - Log.print('Creating directory $path...'); + print('Creating directory $path...'); FileSync.mkdir(path, 511); - Log.print('Renaming $path to $newPath...'); + print('Renaming $path to $newPath...'); FileSync.rename(path, newPath); - Log.print('Removing directory $newPath...'); + print('Removing directory $newPath...'); FileSync.rmdir(newPath); - Log.print('Done'); + print('Done'); actions.next(); } - static function mkdtempRmdir(actions:Actions) { + function mkdtempRmdir(actions:Actions) { var tpl = Misc.tmpDir() + '/test-dir-XXXXXX'; - Log.print('Creating temp directory with tpl $tpl...'); + print('Creating temp directory with tpl $tpl...'); var path = FileSync.mkdtemp(tpl); - Log.print('Removing directory $path...'); + print('Removing directory $path...'); FileSync.rmdir(path); - Log.print('Done'); + print('Done'); actions.next(); } - static function mkstempUnlink(actions:Actions) { + function mkstempUnlink(actions:Actions) { var tpl = Misc.tmpDir() + '/test-file-XXXXXX'; - Log.print('Creating temp file with tpl $tpl...'); + print('Creating temp file with tpl $tpl...'); var tmp = FileSync.mkstemp(tpl); - Log.print('Closing ${tmp.path}...'); + print('Closing ${tmp.path}...'); tmp.file.sync.close(); - Log.print('Unlinking ${tmp.path}...'); + print('Unlinking ${tmp.path}...'); FileSync.unlink(tmp.path); - Log.print('Done'); + print('Done'); actions.next(); } - static function statFStat(actions:Actions) { + function statFStat(actions:Actions) { var path = Misc.tmpDir() + '/test-file'; - Log.print('fstat on $path...'); + print('fstat on $path...'); var file = FileSync.open(path, [O_CREAT(420)]); var fstat = file.sync.fstat(); - Log.print('got fstat: $fstat'); + print('got fstat: $fstat'); file.sync.close(); - Log.print('stat on $path'); + print('stat on $path'); var stat = FileSync.stat(path); - Log.print('got stat: $stat'); + print('got stat: $stat'); // TODO: jit error on I64 == I64 // var ok = stat.dev == fstat.dev; // && stat.mode == fstat.mode @@ -147,111 +147,111 @@ class FileSyncSample { // && stat.blocks == fstat.blocks // && stat.flags == fstat.flags // && stat.gen == fstat.gen; - // Log.print('fstat equals stat: $ok'); + // print('fstat equals stat: $ok'); deleteFiles([path]); - Log.print('Done'); + print('Done'); actions.next(); } - static function statFs(actions:Actions) { - Log.print('statfs on .'); + function statFs(actions:Actions) { + print('statfs on .'); var stat = FileSync.statFs('.'); - Log.print('got statfs: $stat'); - Log.print('Done'); + print('got statfs: $stat'); + print('Done'); actions.next(); } - static function truncate(actions:Actions) { + function truncate(actions:Actions) { var path = Misc.tmpDir() + '/test-file-truncate'; var content = '1234567890'; - Log.print('Writing content for truncation at $path: $content'); + print('Writing content for truncation at $path: $content'); createFile(path, Bytes.ofString(content)); var file = FileSync.open(path, [O_WRONLY]); - Log.print('truncating at 5...'); + print('truncating at 5...'); file.sync.ftruncate(I64.ofInt(5)); file.sync.close(); var data = readFile(path); - Log.print('Content after truncation (length=${data.length}): $data'); + print('Content after truncation (length=${data.length}): $data'); deleteFiles([path]); - Log.print('Done'); + print('Done'); actions.next(); } - static function copyFile(actions:Actions) { + function copyFile(actions:Actions) { var path = Misc.tmpDir() + '/test-file-copy'; var newPath = '$path-copy'; createFile(path, Bytes.ofString('123')); - Log.print('Copy $path to $newPath'); + print('Copy $path to $newPath'); FileSync.copyFile(path, newPath, [EXCL]); deleteFiles([path, newPath]); - Log.print('Done'); + print('Done'); actions.next(); } - static function sendFile(actions:Actions) { + function sendFile(actions:Actions) { var path = Misc.tmpDir() + '/test-file-send'; var newPath = '$path-copy'; createFile(path, Bytes.ofString('12345678')); var src = FileSync.open(path, [O_RDONLY]); var dst = FileSync.open(newPath, [O_CREAT(420), O_WRONLY]); - Log.print('sendFile from $path to $newPath...'); + print('sendFile from $path to $newPath...'); var outOffset = src.sync.sendFile(dst, I64.ofInt(0), I64.ofInt(20)); - Log.print('sendfile stopped at $outOffset'); + print('sendfile stopped at $outOffset'); src.sync.close(); dst.sync.close(); deleteFiles([path, newPath]); - Log.print('Done'); + print('Done'); actions.next(); } - static function access(actions:Actions) { + function access(actions:Actions) { var path = Misc.tmpDir(); - Log.print('Checking write permissions on $path...'); + print('Checking write permissions on $path...'); FileSync.access(path, [W_OK]); - Log.print('Done'); + print('Done'); actions.next(); } - static function chmod(actions:Actions) { + function chmod(actions:Actions) { var path = Misc.tmpDir() + '/test-file-chmod'; createFile(path, Bytes.ofString('123')); - Log.print('chmod on $path...'); + print('chmod on $path...'); FileSync.chmod(path, 420); deleteFiles([path]); - Log.print('Done'); + print('Done'); actions.next(); } - static function utime(actions:Actions) { + function utime(actions:Actions) { var path = Misc.tmpDir() + '/test-file-utime'; createFile(path, Bytes.ofString('123')); - Log.print('utime on $path...'); + print('utime on $path...'); FileSync.utime(path, Date.now().getTime(), Date.now().getTime()); deleteFiles([path]); - Log.print('Done'); + print('Done'); actions.next(); } - static function linkSymlinkReadLinkRealPath(actions:Actions) { + function linkSymlinkReadLinkRealPath(actions:Actions) { var path = Misc.tmpDir() + '/test-file-l'; var newPath = Misc.tmpDir() + '/test-file-link'; createFile(path, Bytes.ofString('123')); - Log.print('link $path to $newPath...'); + print('link $path to $newPath...'); FileSync.link(path, newPath); deleteFiles([newPath]); - Log.print('symlink $path to $newPath...'); + print('symlink $path to $newPath...'); FileSync.symlink(path, newPath, [SYMLINK_JUNCTION]); - Log.print('readlink at $newPath...'); + print('readlink at $newPath...'); var target = FileSync.readLink(newPath); - Log.print('Link content: $target'); + print('Link content: $target'); var real = FileSync.readLink(newPath); - Log.print('Real path of $newPath: $real'); + print('Real path of $newPath: $real'); deleteFiles([path, newPath]); - Log.print('Done'); + print('Done'); actions.next(); } - static function chown(actions:Actions) { + function chown(actions:Actions) { if(Sys.systemName() == 'Windows') { actions.next(); return; @@ -259,10 +259,10 @@ class FileSyncSample { var path = Misc.tmpDir() + '/test-file-chown'; createFile(path, Bytes.ofString('')); - Log.print('chown on $path...'); + print('chown on $path...'); FileSync.chown(path, -1, -1); deleteFiles([path]); - Log.print('Done'); + print('Done'); actions.next(); } } \ No newline at end of file diff --git a/other/uvsample/FsEventSample.hx b/other/uvsample/FsEventSample.hx index eb9084ca2..3971e50c6 100644 --- a/other/uvsample/FsEventSample.hx +++ b/other/uvsample/FsEventSample.hx @@ -6,16 +6,16 @@ import hl.uv.FsEvent; import hl.uv.UVException; import sys.thread.Thread; -class FsEventSample { - public static function main() { +class FsEventSample extends UVSample { + public function run() { var loop = Thread.current().events; var event = FsEvent.init(loop); var path = Misc.tmpDir() + '/test-file-fsevent'; File.saveContent(path, 'Hello, world'); event.start(path, null, (e, path, events) -> switch e { case UV_NOERR: - Log.print('FS event monitors ${event.getPath()}'); - Log.print('FS event {change:${events.change}, rename:${events.rename}} on $path'); + print('FS event monitors ${event.getPath()}'); + print('FS event {change:${events.change}, rename:${events.rename}} on $path'); event.stop(); event.close(); case _: diff --git a/other/uvsample/FsPollSample.hx b/other/uvsample/FsPollSample.hx index 3feeab2e2..822011013 100644 --- a/other/uvsample/FsPollSample.hx +++ b/other/uvsample/FsPollSample.hx @@ -6,17 +6,17 @@ import hl.uv.FsPoll; import hl.uv.UVException; import sys.thread.Thread; -class FsPollSample { - public static function main() { +class FsPollSample extends UVSample { + public function run() { var loop = Thread.current().events; var poll = FsPoll.init(loop); var path = Misc.tmpDir() + '/test-file'; File.saveContent(path, 'Hello, world'); poll.start(path, 1000, (e, previous, current) -> switch e { case UV_NOERR: - Log.print('FS Poll at $path:'); - Log.print('\tprev: $previous'); - Log.print('\tcurr: $current'); + print('FS Poll at $path:'); + print('\tprev: $previous'); + print('\tcurr: $current'); FileSystem.deleteFile(path); poll.stop(); poll.close(); diff --git a/other/uvsample/IdleSample.hx b/other/uvsample/IdleSample.hx index b5d04ecce..00493f796 100644 --- a/other/uvsample/IdleSample.hx +++ b/other/uvsample/IdleSample.hx @@ -3,8 +3,8 @@ import hl.uv.Timer; import hl.uv.Idle; import sys.thread.Thread; -class IdleSample { - public static function main() { +class IdleSample extends UVSample { + public function run() { var loop = Thread.current().events; var timer = Timer.init(loop); timer.start(() -> { @@ -14,7 +14,7 @@ class IdleSample { var idle = Idle.init(loop); idle.start(() -> { - Log.print('Idle'); + print('Idle'); idle.stop(); idle.close(); }); diff --git a/other/uvsample/MiscSample.hx b/other/uvsample/MiscSample.hx index 9dd5e0203..265e59151 100644 --- a/other/uvsample/MiscSample.hx +++ b/other/uvsample/MiscSample.hx @@ -3,12 +3,8 @@ import hl.uv.UVException; import sys.thread.Thread; import hl.uv.Misc; -class MiscSample { - static function print(msg:String) { - Log.print('MiscSample: $msg'); - } - - public static function main() { +class MiscSample extends UVSample { + public function run() { print('Resident set memory: ' + Misc.residentSetMemory()); print('Uptime: ' + Misc.uptime()); print('RUsage: ' + Misc.getRUsage()); diff --git a/other/uvsample/PipeSample.hx b/other/uvsample/PipeSample.hx index 44fb4eefe..b16a2d653 100644 --- a/other/uvsample/PipeSample.hx +++ b/other/uvsample/PipeSample.hx @@ -10,18 +10,18 @@ import haxe.PosInfos; import sys.thread.Thread; import haxe.Timer; -class PipeSample { +class PipeSample extends UVSample { static var NAME = 'testpipe'; - static public function main() { + public function run() { NAME = Misc.tmpDir() + '/' + NAME; #if CLIENT - Log.print('Running PipeSample client...'); - client(); + print('Running PipeSample client...'); + client(); #else - Log.print('Running PipeSample server...'); - Log.print('waiting for connections'); - server(); + print('Running PipeSample server...'); + print('waiting for connections'); + server(); #end } @@ -35,8 +35,8 @@ class PipeSample { static function server() { if(FileSystem.exists(NAME)) FileSystem.deleteFile(NAME); - function print(msg:String) - Log.print('SERVER: $msg'); + function print(msg:String, ?p:PosInfos) + print('SERVER: $msg', p); var loop = Thread.current().events; var server = Pipe.init(loop); server.bind(NAME); @@ -69,8 +69,8 @@ class PipeSample { } static function client() { - function print(msg:String) - Log.print('CLIENT: $msg'); + function print(msg:String, ?p:PosInfos) + print('CLIENT: $msg', p); var loop = Thread.current().events; var client = Pipe.init(loop, true); client.connect(NAME, handle(() -> { diff --git a/other/uvsample/PrepareSample.hx b/other/uvsample/PrepareSample.hx index 88c39e440..9abc142c7 100644 --- a/other/uvsample/PrepareSample.hx +++ b/other/uvsample/PrepareSample.hx @@ -3,8 +3,8 @@ import hl.uv.Timer; import hl.uv.Prepare; import sys.thread.Thread; -class PrepareSample { - public static function main() { +class PrepareSample extends UVSample { + public function run() { var loop = Thread.current().events; var timer = Timer.init(loop); timer.start(() -> { @@ -14,7 +14,7 @@ class PrepareSample { var prepare = Prepare.init(loop); prepare.start(() -> { - Log.print('Prepare'); + print('Prepare'); prepare.stop(); prepare.close(); }); diff --git a/other/uvsample/ProcessSample.hx b/other/uvsample/ProcessSample.hx index c409ea364..ac77523da 100644 --- a/other/uvsample/ProcessSample.hx +++ b/other/uvsample/ProcessSample.hx @@ -1,8 +1,8 @@ import hl.uv.Process; import sys.thread.Thread; -class ProcessSample { - public static function main() { +class ProcessSample extends UVSample { + public function run() { // Process.disableStdioInheritance(); var env = Sys.environment(); @@ -18,14 +18,14 @@ class ProcessSample { stdio: [INHERIT, INHERIT, INHERIT], env: env, onExit: (p, exitCode, termSignal) -> { - Log.print('process finished: exitCode $exitCode, termSignal $termSignal'); - p.close(() -> Log.print('process closed')); + print('process finished: exitCode $exitCode, termSignal $termSignal'); + p.close(() -> print('process closed')); }, } var args = [cmd]; // args.push('non-existent-path'); var p = Process.spawn(Thread.current().events, cmd, args, opt); - Log.print('pid ${p.pid}'); + print('pid ${p.pid}'); // p.kill(SIGINT); // Process.killPid(p.pid, SIGINT); } diff --git a/other/uvsample/SignalSample.hx b/other/uvsample/SignalSample.hx index 7a1491913..d02af1b83 100644 --- a/other/uvsample/SignalSample.hx +++ b/other/uvsample/SignalSample.hx @@ -1,13 +1,13 @@ import hl.uv.Signal; import sys.thread.Thread; -class SignalSample { - public static function main() { +class SignalSample extends UVSample { + public function run() { var loop = Thread.current().events; var signal = Signal.init(loop); - Log.print('SignalSample: waiting for Ctrl^C...'); + print('waiting for Ctrl^C...'); signal.start(SIGINT, () -> { - Log.print('SignalSample: got Ctrl^C'); + print('got Ctrl^C'); signal.stop(); signal.close(); }); diff --git a/other/uvsample/TcpSample.hx b/other/uvsample/TcpSample.hx index c5eeab262..b7c5bb5ee 100644 --- a/other/uvsample/TcpSample.hx +++ b/other/uvsample/TcpSample.hx @@ -7,23 +7,22 @@ import sys.thread.Thread; import hl.uv.Tcp; import hl.uv.SockAddr; -class TcpSample { +class TcpSample extends UVSample { static inline var PORT = 22001; - public static function main() { - Log.print('Running TcpSample...'); + public function run() { server(); Timer.delay(client,100); } - static function handle(success:()->Void, ?p:PosInfos):(e:UVError)->Void { + function handle(success:()->Void, ?p:PosInfos):(e:UVError)->Void { return e -> switch e { case UV_NOERR: success(); case _: throw new UVException(e, p.fileName + ':' + p.lineNumber + ': ' + e.toString()); } } - static function shutdownAndClose(tcp:Tcp, print:(msg:String)->Void, ?onClose:()->Void) { + function shutdownAndClose(tcp:Tcp, print:(msg:String)->Void, ?onClose:()->Void) { tcp.shutdown(_ -> { tcp.close(() -> { print('connection closed'); @@ -33,7 +32,7 @@ class TcpSample { }); } - static function server() { + function server() { function print(msg:String) { Log.print('SERVER: $msg'); } @@ -61,7 +60,7 @@ class TcpSample { })); } - static function client() { + function client() { function print(msg:String) { Log.print('CLIENT: $msg'); } diff --git a/other/uvsample/TtySample.hx b/other/uvsample/TtySample.hx index 0fddfc08a..40217a127 100644 --- a/other/uvsample/TtySample.hx +++ b/other/uvsample/TtySample.hx @@ -3,18 +3,15 @@ import hl.uv.Tty; import hl.uv.UVException; import sys.thread.Thread; -class TtySample { - static function print(msg:String) { - Log.print('TtySample: $msg'); - } - - public static function main() { +class TtySample extends UVSample { + public function run() { print('opening tty...'); var tty = Tty.init(Thread.current().events, File.stdout); print('setting mode...'); tty.setMode(TTY_MODE_NORMAL); print('window size: ' + tty.getWinSize()); Tty.resetMode(); + //VTermState works on windows only // var state = Tty.getVTermState(); // print('vterm state: ' + state); // print('changing vterm state...'); diff --git a/other/uvsample/UVSample.hx b/other/uvsample/UVSample.hx index 02375963e..8033a1c65 100644 --- a/other/uvsample/UVSample.hx +++ b/other/uvsample/UVSample.hx @@ -1,26 +1,20 @@ -class UVSample { +import haxe.PosInfos; + +abstract class UVSample { static function main() { - run(); - // CheckSample.main(); - // PrepareSample.main(); - // IdleSample.main(); - // TcpSample.main(); - // DnsSample.main(); - // UdpSample.main(); - // PipeSample.main(); - // ProcessSample.main(); - // FileSyncSample.main(); - // FileSample.main(); - // DirSample.main(); - // FsEventSample.main(); - // FsPollSample.main(); - // MiscSample.main(); - // TtySample.main(); - // SignalSample.main(); - // VersionSample.main(); + pickSample(); + } + + public function new() {} + + abstract public function run():Void; + + public function print(msg:String, ?pos:PosInfos) { + var sampleName = pos.fileName.substr(0, pos.fileName.length - '.hx'.length); + Log.print('${pos.fileName}:${pos.lineNumber}: $msg'); } - macro static public function run() { + macro static public function pickSample() { var sample = haxe.macro.Context.definedValue('SAMPLE'); if(sample == null || sample == '') sample = Sys.getEnv('SAMPLE'); @@ -30,6 +24,10 @@ class UVSample { Sys.exit(1); } sample += 'Sample'; - return macro $i{sample}.main(); + var cls = switch haxe.macro.TypeTools.toComplexType(haxe.macro.Context.getType(sample)) { + case TPath(tp): tp; + case _: haxe.macro.Context.error('Unsupported sample type', haxe.macro.Context.currentPos()); + } + return macro new $cls().run(); } } \ No newline at end of file diff --git a/other/uvsample/UdpSample.hx b/other/uvsample/UdpSample.hx index 930980a44..95947cf8c 100644 --- a/other/uvsample/UdpSample.hx +++ b/other/uvsample/UdpSample.hx @@ -1,3 +1,4 @@ +import haxe.PosInfos; import haxe.Timer; import haxe.io.Bytes; import hl.uv.UVException; @@ -5,18 +6,17 @@ import hl.uv.SockAddr; import hl.uv.Udp; import sys.thread.Thread; -class UdpSample { +class UdpSample extends UVSample { static inline var PORT = 22002; - public static function main() { - Log.print('Running UdpSample...'); + public function run() { server(); Timer.delay(client,100); } - static function server() { - function print(msg:String) { - Log.print('RECEIVER: $msg'); + function server() { + function print(msg:String, ?p:PosInfos) { + this.print('RECEIVER: $msg', p); } var loop = Thread.current().events; var udp = Udp.init(loop, INET,true); @@ -43,9 +43,9 @@ class UdpSample { }); } - static function client() { - function print(msg:String) { - Log.print('SENDER: $msg'); + function client() { + function print(msg:String, ?p:PosInfos) { + this.print('SENDER: $msg', p); } var udp = Udp.init(Thread.current().events, INET); var data = Bytes.ofString('Hello, UDP!'); diff --git a/other/uvsample/VersionSample.hx b/other/uvsample/VersionSample.hx index 0a4aaca0e..892730e2c 100644 --- a/other/uvsample/VersionSample.hx +++ b/other/uvsample/VersionSample.hx @@ -2,12 +2,8 @@ import hl.uv.Version; import hl.uv.UVException; import sys.thread.Thread; -class VersionSample { - static function print(msg:String) { - Log.print('VersionSample: $msg'); - } - - public static function main() { +class VersionSample extends UVSample { + public function run() { print('string ' + Version.string()); print('major ' + Version.major); print('minor ' + Version.minor); From c9cb7c4c22a60064ccf218d62ef45f01f12aa731 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 3 Sep 2021 16:55:18 +0300 Subject: [PATCH 100/117] fix SockAddr --- libs/uv/uv.c | 2210 +------------------------------- other/uvgenerator/UV.hx.header | 3 +- 2 files changed, 5 insertions(+), 2208 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 8edea521a..f4bcfc8f0 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -407,10 +407,6 @@ static int errno_hl2uv( int uv_errno ) { } } -static int hl_uv_errno( int result ) { - return result < 0 ? errno_uv2hl(result) : 0; -} - HL_PRIM int HL_NAME(translate_uv_error)( int uv_errno ) { return uv_errno < 0 ? errno_uv2hl(uv_errno) : 0; } @@ -582,21 +578,9 @@ DEFINE_PRIM_FREE(_LOOP, loop); DEFINE_PRIM_FREE(_SOCKADDR_STORAGE, sockaddr_storage); -HL_PRIM vdynamic *HL_NAME(sockaddr_storage_port)( uv_sockaddr_storage *addr ) { - UV_CHECK_NULL(addr,NULL); - int port; - if( addr->ss_family == AF_INET ) { - port = ntohs(((uv_sockaddr_in *)addr)->sin_port); - } else if( addr->ss_family == AF_INET6 ) { - port = ntohs(((uv_sockaddr_in6 *)addr)->sin6_port); - } else { - return NULL; - } - return hl_make_dyn(&port, &hlt_i32); -} -DEFINE_PRIM(_NULL(_I32), sockaddr_storage_port, _SOCKADDR_STORAGE); - DEFINE_PRIM_C_FIELD(_I32, int, _SOCKADDR_STORAGE, sockaddr_storage, ss_family); +DEFINE_PRIM_C_FIELD(_I32, int, _SOCKADDR_IN, sockaddr_in, sin_port); +DEFINE_PRIM_C_FIELD(_I32, int, _SOCKADDR_IN6, sockaddr_in6, sin6_port); HL_PRIM struct sockaddr_storage *HL_NAME(alloc_sockaddr_storage)() { return UV_ALLOC(struct sockaddr_storage); @@ -1152,2192 +1136,4 @@ static void on_uv_random_cb( uv_random_t* r, int status, void* buf, size_t bufle } // auto-generated libuv bindings -#include "uv_generated.c" - - - -// TODO: remove everything below - -#define _CALLB _FUN(_VOID,_NO_ARG) -#define _TIMESPEC_OBJ _OBJ(_I64 _I64) - -#define EVT_CLOSE 1 - -#define EVT_STREAM_READ 0 -#define EVT_STREAM_LISTEN 2 - -#define EVT_CONNECT 0 // connect_t - -#define EVT_MAX 2 // !!!!!!!!!!!!!! - -typedef struct { - vclosure *events[EVT_MAX + 1]; - void *data; -} events_data; - -#define UV_DATA(h) ((events_data*)((h)->data)) -#define UV_ALLOC_REQ(t,r,c) \ - t *r = UV_ALLOC(t); \ - req_init_hl_data((uv_req_t *)r); \ - if( c ) \ - req_register_callback((uv_req_t *)r,c,0); -#define UV_CHECK_ERROR(action,cleanup,fail_return) \ - int __result__ = action; \ - if(__result__ < 0) { \ - cleanup; \ - hx_error(__result__); \ - return fail_return; \ - } -#define UV_GET_CLOSURE(c,holder,callback_idx,fail_msg) \ - events_data *__ev__ = UV_DATA(holder); \ - vclosure *c = __ev__ ? __ev__->events[callback_idx] : NULL; \ - if( !c ) \ - hl_fatal(fail_msg); -#define UV_COPY_DATA(buf,holder,buffer,length) \ - events_data *__ev__ = UV_DATA(holder); \ - __ev__->data = malloc(length); \ - memcpy(__ev__->data,buffer,length); \ - uv_buf_t buf = uv_buf_init(__ev__->data, length); - -hl_type hlt_sockaddr = { HABSTRACT, {USTR("uv_sockaddr_storage")} }; - -static void dyn_set_i64( vdynamic *obj, int field, int64 value ) { - vdynamic *v = hl_alloc_dynamic(&hlt_i64); - v->v.i64 = value; - hl_dyn_setp(obj, field, &hlt_dyn, v); -} - -static int bytes_geti32( vbyte *b, int pos ) { - return b[pos] | (b[pos + 1] << 8) | (b[pos + 2] << 16) | (b[pos + 3] << 24); -} - -// exceptions - -static vclosure *c_exception; - -HL_PRIM void HL_NAME(init_exception)( vclosure *c ) { - c_exception = c; -} -DEFINE_PRIM(_VOID, init_exception, _FUN(_DYN, _I32)); - -static void hx_error(int uv_errno) { - if( c_exception ) { - vdynamic *exc = hl_call1(vdynamic *, c_exception, int, errno_uv2hl(uv_errno)); - hl_throw(exc); - } else { - hl_error("%s", hl_to_utf16(uv_err_name(uv_errno))); - } -} - -// Request - -static events_data *req_init_hl_data( uv_req_t *r ) { - events_data *d = hl_gc_alloc_raw(sizeof(events_data)); - memset(d,0,sizeof(events_data)); - hl_add_root(&r->data); - r->data = d; - return d; -} - -static void req_register_callback( uv_req_t *r, vclosure *c, int event_kind ) { - if( !r ) - hl_fatal("Missing req"); - if( !r->data ) - hl_fatal("Missing req data"); - UV_DATA(r)->events[event_kind] = c; -} - -static void req_clear_callback( uv_req_t *r, int event_kind ) { - req_register_callback(r,NULL,event_kind); -} - -static void free_req( uv_req_t *r ) { - events_data *ev = UV_DATA(r); - if( ev ) { - req_clear_callback(r, 0); - if( ev->data ) { - free(ev->data); - hl_remove_root(&r->data); - } - r->data = NULL; - } - free(r); -} - -static void free_fs_req( uv_fs_t *r ) { - events_data *ev = UV_DATA(r); - if( ev ) { - req_clear_callback((uv_req_t *)r, 0); - if( ev->data ) { - free(ev->data); - hl_remove_root(&r->data); - } - r->data = NULL; - } - uv_fs_req_cleanup(r); -} - -HL_PRIM void HL_NAME(cancel_wrap)( uv_req_t *r ) { - UV_CHECK_NULL(r,); - UV_CHECK_ERROR(uv_cancel(r),,); - free_req(r); -} -DEFINE_PRIM(_VOID, cancel_wrap, _REQ); - -// HANDLE - -static events_data *handle_init_hl_data( uv_handle_t *h ) { - events_data *d = hl_gc_alloc_raw(sizeof(events_data)); - memset(d,0,sizeof(events_data)); - hl_add_root(&h->data); - h->data = d; - return d; -} - -static void handle_register_callback( uv_handle_t *h, vclosure *c, int event_kind ) { - if( !h ) - hl_fatal("Missing handle"); - if( !h->data ) - hl_fatal("Missing handle data"); - UV_DATA(h)->events[event_kind] = c; -} - -static void handle_clear_callback( uv_handle_t *h, int event_kind ) { - handle_register_callback(h,NULL,event_kind); -} - -static void on_close( uv_handle_t *h ) { - events_data *ev = UV_DATA(h); - if( ev ) { - vclosure *c = ev ? ev->events[EVT_CLOSE] : NULL; - if( c ) { - hl_call0(void, c); - handle_clear_callback(h, EVT_CLOSE); - } - if( ev->data ) { - free(ev->data); - hl_remove_root(&h->data); - } - h->data = NULL; - } - free(h); -} - -static void free_handle( uv_handle_t *h ) { - uv_close(h, on_close); -} - -HL_PRIM void HL_NAME(close_wrap)( uv_handle_t *h, vclosure *c ) { - UV_CHECK_NULL(h,); - handle_register_callback(h, c, EVT_CLOSE); - free_handle(h); -} -DEFINE_PRIM(_VOID, close_wrap, _HANDLE _CALLB); - -HL_PRIM bool HL_NAME(is_active_wrap)( uv_handle_t *h ) { - UV_CHECK_NULL(h,false); - return uv_is_active(h) != 0; -} -DEFINE_PRIM(_BOOL, is_active_wrap, _HANDLE); - -HL_PRIM bool HL_NAME(is_closing_wrap)( uv_handle_t *h ) { - UV_CHECK_NULL(h,false); - return uv_is_closing(h) != 0; -} -DEFINE_PRIM(_BOOL, is_closing_wrap, _HANDLE); - -HL_PRIM bool HL_NAME(has_ref_wrap)( uv_handle_t *h ) { - UV_CHECK_NULL(h,false); - return uv_has_ref(h) != 0; -} -DEFINE_PRIM(_BOOL, has_ref_wrap, _HANDLE); - -HL_PRIM void HL_NAME(ref_wrap)( uv_handle_t *h ) { - UV_CHECK_NULL(h,); - uv_ref(h); -} -DEFINE_PRIM(_VOID, ref_wrap, _HANDLE); - -HL_PRIM void HL_NAME(unref_wrap)( uv_handle_t *h ) { - UV_CHECK_NULL(h,); - uv_unref(h); -} -DEFINE_PRIM(_VOID, unref_wrap, _HANDLE); - -// STREAM - -static void on_shutdown( uv_shutdown_t *r, int status ) { - UV_GET_CLOSURE(c,r,0,"No callback in shutdown request"); - hl_call1(void, c, int, errno_uv2hl(status)); - free_req((uv_req_t *)r); -} - -HL_PRIM void HL_NAME(shutdown_wrap)( uv_stream_t *h, vclosure *c ) { - UV_CHECK_NULL(h,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_shutdown_t,r,c) - UV_CHECK_ERROR(uv_shutdown(r, h, on_shutdown),free_req((uv_req_t *)r),); -} -DEFINE_PRIM(_VOID, shutdown_wrap, _HANDLE _FUN(_VOID,_I32)); - -static void on_listen( uv_stream_t *h, int status ) { - UV_GET_CLOSURE(c,h,EVT_STREAM_LISTEN,"No listen callback in stream handle"); - hl_call1(void, c, int, errno_uv2hl(status)); -} - -HL_PRIM void HL_NAME(listen_wrap)( uv_stream_t *h, int backlog, vclosure *c ) { - UV_CHECK_NULL(h,); - UV_CHECK_NULL(c,); - handle_register_callback((uv_handle_t*)h,c,EVT_STREAM_LISTEN); - UV_CHECK_ERROR(uv_listen(h, backlog, on_listen),handle_clear_callback((uv_handle_t*)h,EVT_STREAM_LISTEN),); -} -DEFINE_PRIM(_VOID, listen_wrap, _HANDLE _I32 _FUN(_VOID,_I32)); - -HL_PRIM void HL_NAME(accept_wrap)( uv_stream_t *h, uv_stream_t *client ) { - UV_CHECK_NULL(h,); - UV_CHECK_NULL(client,); - UV_CHECK_ERROR(uv_accept(h, client),,); -} -DEFINE_PRIM(_VOID, accept_wrap, _HANDLE _HANDLE); - -static void on_alloc( uv_handle_t* h, size_t size, uv_buf_t *buf ) { - *buf = uv_buf_init(malloc(size), (int)size); -} - -static void on_read( uv_stream_t *h, ssize_t nread, const uv_buf_t *buf ) { - UV_GET_CLOSURE(c,h,EVT_STREAM_READ,"No listen callback in stream handle"); - if( nread < 0 ) { - hl_call3(void, c, int, errno_uv2hl(nread), vbyte *, NULL, int, 0); - } else { - hl_call3(void, c, int, 0, vbyte *, (vbyte *)buf->base, int, nread); - } - free(buf->base); -} - -HL_PRIM void HL_NAME(read_start_wrap)( uv_stream_t *h, vclosure *c ) { - UV_CHECK_NULL(h,); - UV_CHECK_NULL(c,); - handle_register_callback((uv_handle_t*)h,c,EVT_STREAM_READ); - UV_CHECK_ERROR(uv_read_start(h,on_alloc,on_read), handle_clear_callback((uv_handle_t*)h,EVT_STREAM_READ),); -} -DEFINE_PRIM(_VOID, read_start_wrap, _HANDLE _FUN(_VOID,_I32 _BYTES _I32)); - -HL_PRIM void HL_NAME(read_stop_wrap)( uv_stream_t *h ) { - UV_CHECK_NULL(h,); - handle_clear_callback((uv_handle_t*)h,EVT_STREAM_READ); - uv_read_stop(h); -} -DEFINE_PRIM(_VOID, read_stop_wrap, _HANDLE); - -static void on_write( uv_write_t *r, int status ) { - UV_GET_CLOSURE(c,r,0,"No callback in write request"); - hl_call1(void, c, int, hl_uv_errno(status)); - free_req((uv_req_t *)r); -} - -HL_PRIM void HL_NAME(write_wrap)( uv_stream_t *h, vbyte *b, int length, vclosure *c ) { - UV_CHECK_NULL(h,); - UV_CHECK_NULL(b,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_write_t,r,c); - UV_COPY_DATA(buf,r,b,length); - UV_CHECK_ERROR(uv_write(r,h,&buf,1,on_write),free_req((uv_req_t *)r),); -} -DEFINE_PRIM(_VOID, write_wrap, _HANDLE _BYTES _I32 _FUN(_VOID,_I32)); - -HL_PRIM int HL_NAME(try_write_wrap)( uv_stream_t *h, vbyte *b, int length ) { - UV_CHECK_NULL(h,0); - UV_CHECK_NULL(b,0); - uv_buf_t buf = uv_buf_init((char *)b, length); - UV_CHECK_ERROR(uv_try_write(h,&buf,1),,0); - return __result__; -} -DEFINE_PRIM(_I32, try_write_wrap, _HANDLE _BYTES _I32); - -HL_PRIM void HL_NAME(write2_wrap)( uv_stream_t *h, vbyte *b, int length, uv_stream_t *send_handle, vclosure *c ) { - UV_CHECK_NULL(h,); - UV_CHECK_NULL(b,); - UV_CHECK_NULL(send_handle,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_write_t,r,c); - UV_COPY_DATA(buf,r,b,length); - UV_CHECK_ERROR(uv_write2(r,h,&buf,1,send_handle,on_write),free_req((uv_req_t *)r),); -} -DEFINE_PRIM(_VOID, write2_wrap, _HANDLE _BYTES _I32 _HANDLE _FUN(_VOID,_I32)); - -HL_PRIM bool HL_NAME(is_readable_wrap)( uv_stream_t *h ) { - UV_CHECK_NULL(h,false); - return uv_is_readable(h) != 0; -} -DEFINE_PRIM(_BOOL, is_readable_wrap, _HANDLE); - -HL_PRIM bool HL_NAME(is_writable_wrap)( uv_stream_t *h ) { - UV_CHECK_NULL(h,false); - return uv_is_writable(h) != 0; -} -DEFINE_PRIM(_BOOL, is_writable_wrap, _HANDLE); - -// Timer -HL_PRIM uv_timer_t *HL_NAME(timer_init_wrap)( uv_loop_t *loop ) { - UV_CHECK_NULL(loop,NULL); - uv_timer_t *t = UV_ALLOC(uv_timer_t); - UV_CHECK_ERROR(uv_timer_init(loop,t),free(t),NULL); - handle_init_hl_data((uv_handle_t*)t); - return t; -} -DEFINE_PRIM(_HANDLE, timer_init_wrap, _LOOP); - -static void on_timer( uv_timer_t *h ) { - UV_GET_CLOSURE(c,h,0,"No callback in timer handle"); - hl_call0(void, c); -} - -// TODO: change `timeout` and `repeat` to uint64 -HL_PRIM void HL_NAME(timer_start_wrap)(uv_timer_t *t, vclosure *c, int timeout, int repeat) { - UV_CHECK_NULL(t,); - UV_CHECK_NULL(c,); - handle_register_callback((uv_handle_t*)t,c,0); - UV_CHECK_ERROR( - uv_timer_start(t,on_timer, (uint64_t)timeout, (uint64_t)repeat), - handle_clear_callback((uv_handle_t*)t,0), - ); -} -DEFINE_PRIM(_VOID, timer_start_wrap, _HANDLE _FUN(_VOID,_NO_ARG) _I32 _I32); - -HL_PRIM void HL_NAME(timer_stop_wrap)(uv_timer_t *t) { - UV_CHECK_NULL(t,); - handle_clear_callback((uv_handle_t*)t,0); - UV_CHECK_ERROR(uv_timer_stop(t),,); -} -DEFINE_PRIM(_VOID, timer_stop_wrap, _HANDLE); - -HL_PRIM void HL_NAME(timer_again_wrap)(uv_timer_t *t) { - UV_CHECK_NULL(t,); - UV_CHECK_ERROR(uv_timer_again(t),,); -} -DEFINE_PRIM(_VOID, timer_again_wrap, _HANDLE); - -HL_PRIM int HL_NAME(timer_get_due_in_wrap)(uv_timer_t *t) { - UV_CHECK_NULL(t,0); - return (int)uv_timer_get_due_in(t); //TODO: change to uint64_t -} -DEFINE_PRIM(_I32, timer_get_due_in_wrap, _HANDLE); - -HL_PRIM int HL_NAME(timer_get_repeat_wrap)(uv_timer_t *t) { - UV_CHECK_NULL(t,0); - return (int)uv_timer_get_repeat(t); //TODO: change to uint64_t -} -DEFINE_PRIM(_I32, timer_get_repeat_wrap, _HANDLE); - -//TODO: change `value` to uint64_t -HL_PRIM int HL_NAME(timer_set_repeat_wrap)(uv_timer_t *t, int value) { - UV_CHECK_NULL(t,0); - uv_timer_set_repeat(t, (uint64_t)value); - return value; -} -DEFINE_PRIM(_I32, timer_set_repeat_wrap, _HANDLE _I32); - -// Async - -static void on_async( uv_async_t *h ) { - UV_GET_CLOSURE(c,h,0,"No callback in async handle"); - hl_call1(void, c, uv_async_t *, h); -} - -HL_PRIM uv_async_t *HL_NAME(async_init_wrap)( uv_loop_t *loop, vclosure *c ) { - UV_CHECK_NULL(loop,NULL); - UV_CHECK_NULL(c,NULL); - uv_async_t *a = UV_ALLOC(uv_async_t); - UV_CHECK_ERROR(uv_async_init(loop,a,on_async),free(a),NULL); - handle_init_hl_data((uv_handle_t*)a); - handle_register_callback((uv_handle_t*)a,c,0); - return a; -} -DEFINE_PRIM(_HANDLE, async_init_wrap, _LOOP _FUN(_VOID,_HANDLE)); - -HL_PRIM void HL_NAME(async_send_wrap)( uv_async_t *a ) { - UV_CHECK_NULL(a,); - UV_CHECK_ERROR(uv_async_send(a),,); -} -DEFINE_PRIM(_VOID, async_send_wrap, _HANDLE); - -// Idle - -static void on_idle( uv_idle_t *h ) { - UV_GET_CLOSURE(c,h,0,"No callback in idle handle"); - hl_call0(void, c); -} - -HL_PRIM uv_idle_t *HL_NAME(idle_init_wrap)( uv_loop_t *loop ) { - UV_CHECK_NULL(loop,NULL); - uv_idle_t *h = UV_ALLOC(uv_idle_t); - UV_CHECK_ERROR(uv_idle_init(loop,h),free(h),NULL); - handle_init_hl_data((uv_handle_t*)h); - return h; -} -DEFINE_PRIM(_HANDLE, idle_init_wrap, _LOOP); - -HL_PRIM void HL_NAME(idle_start_wrap)( uv_idle_t *h, vclosure *c ) { - UV_CHECK_NULL(h,); - UV_CHECK_NULL(c,); - handle_register_callback((uv_handle_t*)h,c,0); - uv_idle_start(h, on_idle); -} -DEFINE_PRIM(_VOID, idle_start_wrap, _HANDLE _FUN(_VOID,_NO_ARG)); - -HL_PRIM void HL_NAME(idle_stop_wrap)( uv_idle_t *h ) { - UV_CHECK_NULL(h,); - uv_idle_stop(h); -} -DEFINE_PRIM(_VOID, idle_stop_wrap, _HANDLE); - -// Prepare - -static void on_prepare( uv_prepare_t *h ) { - UV_GET_CLOSURE(c,h,0,"No callback in prepare handle"); - hl_call0(void, c); -} - -HL_PRIM uv_prepare_t *HL_NAME(prepare_init_wrap)( uv_loop_t *loop ) { - UV_CHECK_NULL(loop,NULL); - uv_prepare_t *h = UV_ALLOC(uv_prepare_t); - UV_CHECK_ERROR(uv_prepare_init(loop,h),free(h),NULL); - handle_init_hl_data((uv_handle_t*)h); - return h; -} -DEFINE_PRIM(_HANDLE, prepare_init_wrap, _LOOP); - -HL_PRIM void HL_NAME(prepare_start_wrap)( uv_prepare_t *h, vclosure *c ) { - UV_CHECK_NULL(h,); - UV_CHECK_NULL(c,); - handle_register_callback((uv_handle_t*)h,c,0); - uv_prepare_start(h, on_prepare); -} -DEFINE_PRIM(_VOID, prepare_start_wrap, _HANDLE _FUN(_VOID,_NO_ARG)); - -HL_PRIM void HL_NAME(prepare_stop_wrap)( uv_prepare_t *h ) { - UV_CHECK_NULL(h,); - uv_prepare_stop(h); -} -DEFINE_PRIM(_VOID, prepare_stop_wrap, _HANDLE); - -// Check - -static void on_check( uv_check_t *h ) { - UV_GET_CLOSURE(c,h,0,"No callback in check handle"); - hl_call0(void, c); -} - -HL_PRIM uv_check_t *HL_NAME(check_init_wrap)( uv_loop_t *loop ) { - UV_CHECK_NULL(loop,NULL); - uv_check_t *h = UV_ALLOC(uv_check_t); - UV_CHECK_ERROR(uv_check_init(loop,h),free(h),NULL); - handle_init_hl_data((uv_handle_t*)h); - return h; -} -DEFINE_PRIM(_HANDLE, check_init_wrap, _LOOP); - -HL_PRIM void HL_NAME(check_start_wrap)( uv_check_t *h, vclosure *c ) { - UV_CHECK_NULL(h,); - UV_CHECK_NULL(c,); - handle_register_callback((uv_handle_t*)h,c,0); - uv_check_start(h, on_check); -} -DEFINE_PRIM(_VOID, check_start_wrap, _HANDLE _FUN(_VOID,_NO_ARG)); - -HL_PRIM void HL_NAME(check_stop_wrap)( uv_check_t *h ) { - UV_CHECK_NULL(h,); - uv_check_stop(h); -} -DEFINE_PRIM(_VOID, check_stop_wrap, _HANDLE); - -// Signal - -static void on_signal( uv_signal_t *h, int signum ) { - UV_GET_CLOSURE(c,h,0,"No callback in signal handle"); - hl_call1(void, c, int, uv_translate_sys_signal(signum)); -} - -static void on_signal_oneshot( uv_signal_t *h, int signum ) { - UV_GET_CLOSURE(c,h,0,"No callback in signal handle"); - handle_clear_callback((uv_handle_t *)h,0); - hl_call1(void, c, int, uv_translate_sys_signal(signum)); -} - -HL_PRIM uv_signal_t *HL_NAME(signal_init_wrap)( uv_loop_t *loop ) { - UV_CHECK_NULL(loop,NULL); - uv_signal_t *h = UV_ALLOC(uv_signal_t); - UV_CHECK_ERROR(uv_signal_init(loop,h),free(h),NULL); - handle_init_hl_data((uv_handle_t*)h); - return h; -} -DEFINE_PRIM(_HANDLE, signal_init_wrap, _LOOP); - -HL_PRIM void HL_NAME(signal_start_wrap)( uv_signal_t *h, int signum, vclosure *c ) { - UV_CHECK_NULL(h,); - UV_CHECK_NULL(c,); - handle_register_callback((uv_handle_t*)h,c,0); - UV_CHECK_ERROR(uv_signal_start(h, on_signal, uv_translate_to_sys_signal(signum)),handle_clear_callback((uv_handle_t *)h,0),); -} -DEFINE_PRIM(_VOID, signal_start_wrap, _HANDLE _I32 _FUN(_VOID,_I32)); - -HL_PRIM void HL_NAME(signal_start_oneshot_wrap)( uv_signal_t *h, int signum, vclosure *c ) { - UV_CHECK_NULL(h,); - UV_CHECK_NULL(c,); - handle_register_callback((uv_handle_t*)h,c,0); - UV_CHECK_ERROR(uv_signal_start_oneshot(h, on_signal_oneshot, uv_translate_to_sys_signal(signum)),handle_clear_callback((uv_handle_t *)h,0),); -} -DEFINE_PRIM(_VOID, signal_start_oneshot_wrap, _HANDLE _I32 _FUN(_VOID,_I32)); - -HL_PRIM void HL_NAME(signal_stop_wrap)( uv_signal_t *h ) { - UV_CHECK_NULL(h,); - UV_CHECK_ERROR(uv_signal_stop(h),,); -} -DEFINE_PRIM(_VOID, signal_stop_wrap, _HANDLE); - -HL_PRIM int HL_NAME(signal_get_sigNum_wrap)(uv_signal_t *h) { - UV_CHECK_NULL(h,0); - return uv_translate_sys_signal(h->signum); -} -DEFINE_PRIM(_I32, signal_get_sigNum_wrap, _HANDLE); - -// Sockaddr - -/** - * Convert `hl.uv.SockAddr.AddressFamily` values to corresponding AF_* values - */ -static int address_family(int hx_address_family) { - switch( hx_address_family ) { - case -1: return AF_UNSPEC; - case -2: return AF_INET; - case -3: return AF_INET6; - default: return hx_address_family; - } -} - -HL_PRIM uv_sockaddr_storage *HL_NAME(ip4_addr_wrap)( vstring *ip, int port ) { - UV_CHECK_NULL(ip,NULL); - uv_sockaddr_storage *addr = UV_ALLOC(uv_sockaddr_storage); //register in hl gc? - UV_CHECK_ERROR(uv_ip4_addr(hl_to_utf8(ip->bytes), port, (uv_sockaddr_in *)addr),free(addr),NULL); - return addr; -} -DEFINE_PRIM(_SOCKADDR_STORAGE, ip4_addr_wrap, _STRING _I32); - -HL_PRIM uv_sockaddr_storage *HL_NAME(ip6_addr_wrap)( vstring *ip, int port ) { - UV_CHECK_NULL(ip,NULL); - uv_sockaddr_storage *addr = UV_ALLOC(uv_sockaddr_storage); - UV_CHECK_ERROR(uv_ip6_addr(hl_to_utf8(ip->bytes), port, (uv_sockaddr_in6 *)addr),free(addr),NULL); - return addr; -} -DEFINE_PRIM(_SOCKADDR_STORAGE, ip6_addr_wrap, _STRING _I32); - -HL_PRIM vdynamic *HL_NAME(sockaddr_get_port)( uv_sockaddr_storage *addr ) { - UV_CHECK_NULL(addr,NULL); - int port; - if( addr->ss_family == AF_INET ) { - port = ntohs(((uv_sockaddr_in *)addr)->sin_port); - } else if( addr->ss_family == AF_INET6 ) { - port = ntohs(((uv_sockaddr_in6 *)addr)->sin6_port); - } else { - return NULL; - } - return hl_make_dyn(&port, &hlt_i32); -} -DEFINE_PRIM(_NULL(_I32), sockaddr_get_port, _SOCKADDR_STORAGE); - -HL_PRIM uv_sockaddr_storage *HL_NAME(sockaddr_cast_ptr)( vdynamic *ptr ) { - UV_CHECK_NULL(ptr,NULL); - if( ptr->t->kind != HABSTRACT || 0 != memcmp(ptr->t->abs_name,USTR("uv_sockaddr_storage"),38) ) - hl_error("Invalid usage of hl.uv.SockAddr.castPtr()"); - return (uv_sockaddr_storage *)ptr->v.ptr; -} -DEFINE_PRIM(_SOCKADDR_STORAGE, sockaddr_cast_ptr, _DYN); - -//How to return vstring instead of vbyte? -HL_PRIM vbyte *HL_NAME(ip_name_wrap)( uv_sockaddr_storage *addr ) { - UV_CHECK_NULL(addr,NULL); - vbyte *dst = hl_alloc_bytes(128); - if( addr->ss_family == AF_INET ) { - UV_CHECK_ERROR(uv_ip4_name((uv_sockaddr_in *)addr, (char *)dst, 128),,NULL); //free bytes? - } else if( addr->ss_family == AF_INET6 ) { - UV_CHECK_ERROR(uv_ip6_name((uv_sockaddr_in6 *)addr, (char *)dst, 128),,NULL); - } else { - return NULL; - } - return dst; -} -DEFINE_PRIM(_BYTES, ip_name_wrap, _SOCKADDR_STORAGE); - -// TCP - -HL_PRIM uv_tcp_t *HL_NAME(tcp_init_wrap)( uv_loop_t *loop, vdynamic *domain ) { - UV_CHECK_NULL(loop,NULL); - uv_tcp_t *h = UV_ALLOC(uv_tcp_t); - if( !domain ) { - UV_CHECK_ERROR(uv_tcp_init(loop,h),free(h),NULL); - } else { - int d = address_family(domain->v.i); - UV_CHECK_ERROR(uv_tcp_init_ex(loop,h,d),free_handle((uv_handle_t *)h),NULL); - } - handle_init_hl_data((uv_handle_t*)h); - return h; -} -DEFINE_PRIM(_HANDLE, tcp_init_wrap, _LOOP _NULL(_I32)); - -HL_PRIM void HL_NAME(tcp_nodelay_wrap)( uv_tcp_t *h, bool enable ) { - UV_CHECK_NULL(h,); - UV_CHECK_ERROR(uv_tcp_nodelay(h,enable?1:0),,); -} -DEFINE_PRIM(_VOID, tcp_nodelay_wrap, _HANDLE _BOOL); - -HL_PRIM void HL_NAME(tcp_keepalive_wrap)( uv_tcp_t *h, bool enable, int delay ) { - UV_CHECK_NULL(h,); - UV_CHECK_ERROR(uv_tcp_keepalive(h,enable?1:0,delay),,); -} -DEFINE_PRIM(_VOID, tcp_keepalive_wrap, _HANDLE _BOOL _I32); - -HL_PRIM void HL_NAME(tcp_simultaneous_accepts_wrap)( uv_tcp_t *h, bool enable ) { - UV_CHECK_NULL(h,); - UV_CHECK_ERROR(uv_tcp_simultaneous_accepts(h,enable?1:0),,); -} -DEFINE_PRIM(_VOID, tcp_simultaneous_accepts_wrap, _HANDLE _BOOL); - -HL_PRIM void HL_NAME(tcp_bind_wrap)( uv_tcp_t *h, uv_sockaddr_storage *addr, vdynamic *ipv6_only ) { - UV_CHECK_NULL(h,); - UV_CHECK_NULL(addr,); - int flags = ipv6_only && ipv6_only->v.b ? UV_TCP_IPV6ONLY : 0; - UV_CHECK_ERROR(uv_tcp_bind(h,(uv_sockaddr *)addr,flags),,); -} -DEFINE_PRIM(_VOID, tcp_bind_wrap, _HANDLE _SOCKADDR_STORAGE _NULL(_BOOL)); - -HL_PRIM uv_sockaddr_storage *HL_NAME(tcp_getsockname_wrap)( uv_tcp_t *h ) { - UV_CHECK_NULL(h,NULL); - uv_sockaddr_storage *addr = UV_ALLOC(uv_sockaddr_storage); - int size = sizeof(uv_sockaddr_storage); - UV_CHECK_ERROR(uv_tcp_getsockname(h,(uv_sockaddr *)addr,&size),free(addr),NULL); - return addr; -} -DEFINE_PRIM(_SOCKADDR_STORAGE, tcp_getsockname_wrap, _HANDLE); - -HL_PRIM uv_sockaddr_storage *HL_NAME(tcp_getpeername_wrap)( uv_tcp_t *h ) { - UV_CHECK_NULL(h,NULL); - uv_sockaddr_storage *addr = UV_ALLOC(uv_sockaddr_storage); - int size = sizeof(uv_sockaddr_storage); - UV_CHECK_ERROR(uv_tcp_getpeername(h,(uv_sockaddr *)addr,&size),free(addr),NULL); - return addr; -} -DEFINE_PRIM(_SOCKADDR_STORAGE, tcp_getpeername_wrap, _HANDLE); - -static void on_connect( uv_connect_t *r, int status ) { - UV_GET_CLOSURE(c,r,0,"No callback in connect request"); - hl_call1(void, c, int, errno_uv2hl(status)); - free_req((uv_req_t *)r); -} - -HL_PRIM void HL_NAME(tcp_connect_wrap)( uv_tcp_t *h, uv_sockaddr_storage *addr, vclosure *c ) { - UV_CHECK_NULL(h,); - UV_CHECK_NULL(addr,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_connect_t,r,c); - UV_CHECK_ERROR(uv_tcp_connect(r, h,(uv_sockaddr *)addr,on_connect),free_req((uv_req_t *)r),); -} -DEFINE_PRIM(_VOID, tcp_connect_wrap, _HANDLE _SOCKADDR_STORAGE _FUN(_VOID,_I32)); - -HL_PRIM void HL_NAME(tcp_close_reset_wrap)( uv_tcp_t *h, vclosure *c ) { - UV_CHECK_NULL(h,); - handle_register_callback((uv_handle_t *)h, c, EVT_CLOSE); - uv_tcp_close_reset(h, on_close); -} -DEFINE_PRIM(_VOID, tcp_close_reset_wrap, _HANDLE _CALLB); - -// PIPE - -HL_PRIM uv_pipe_t *HL_NAME(pipe_init_wrap)( uv_loop_t *loop, vdynamic *ipc ) { - UV_CHECK_NULL(loop,NULL); - uv_pipe_t *h = UV_ALLOC(uv_pipe_t); - UV_CHECK_ERROR(uv_pipe_init(loop,h,(ipc&&ipc->v.b)),free(h),NULL); - handle_init_hl_data((uv_handle_t*)h); - return h; -} -DEFINE_PRIM(_HANDLE, pipe_init_wrap, _LOOP _NULL(_BOOL)); - -HL_PRIM void HL_NAME(pipe_bind_wrap)( uv_pipe_t *h, vstring *name ) { - UV_CHECK_NULL(h,); - UV_CHECK_NULL(name,); - UV_CHECK_ERROR(uv_pipe_bind(h,hl_to_utf8(name->bytes)),,); -} -DEFINE_PRIM(_VOID, pipe_bind_wrap, _HANDLE _STRING); - -static vbyte *pipe_getname( uv_pipe_t *h, int (*fn)(const uv_pipe_t *h, char *buf, size_t *size) ) { - UV_CHECK_NULL(h,NULL); - size_t size = 256; - char *buf = NULL; - int result = UV_ENOBUFS; - while (result == UV_ENOBUFS) { - if( buf ) - free(buf); - buf = malloc(size); - result = fn(h,buf,&size); - } - vbyte *name = hl_alloc_bytes(size); - memcpy(name,buf,size); - free(buf); - UV_CHECK_ERROR(result,,NULL); //free bytes? - return name; -} - -HL_PRIM vbyte *HL_NAME(pipe_getsockname_wrap)( uv_pipe_t *h ) { - return pipe_getname(h, uv_pipe_getsockname); -} -DEFINE_PRIM(_BYTES, pipe_getsockname_wrap, _HANDLE); - -HL_PRIM vbyte *HL_NAME(pipe_getpeername_wrap)( uv_pipe_t *h ) { - return pipe_getname(h, uv_pipe_getpeername); -} -DEFINE_PRIM(_BYTES, pipe_getpeername_wrap, _HANDLE); - -HL_PRIM void HL_NAME(pipe_connect_wrap)( uv_pipe_t *h, vstring *name, vclosure *c ) { - UV_CHECK_NULL(h,); - UV_CHECK_NULL(name,); - UV_CHECK_NULL(c,); - uv_connect_t *r = UV_ALLOC(uv_connect_t); - req_init_hl_data((uv_req_t *)r); - req_register_callback((uv_req_t *)r,c,0); - uv_pipe_connect(r, h,hl_to_utf8(name->bytes),on_connect); -} -DEFINE_PRIM(_VOID, pipe_connect_wrap, _HANDLE _STRING _FUN(_VOID,_I32)); - -HL_PRIM void HL_NAME(pipe_pending_instances_wrap)( uv_pipe_t *h, int count ) { - UV_CHECK_NULL(h,); - uv_pipe_pending_instances(h, count); -} -DEFINE_PRIM(_VOID, pipe_pending_instances_wrap, _HANDLE _I32); - -HL_PRIM int HL_NAME(pipe_pending_count_wrap)( uv_pipe_t *h ) { - UV_CHECK_NULL(h,0); - UV_CHECK_ERROR(uv_pipe_pending_count(h),,0); - return __result__; -} -DEFINE_PRIM(_I32, pipe_pending_count_wrap, _HANDLE); - -HL_PRIM int HL_NAME(pipe_pending_type_wrap)( uv_pipe_t *h ) { - UV_CHECK_NULL(h,0); - UV_CHECK_ERROR(uv_pipe_pending_type(h),,0); - switch( __result__ ) { - case UV_ASYNC: return 1; - case UV_CHECK: return 2; - case UV_FS_EVENT: return 3; - case UV_FS_POLL: return 4; - case UV_HANDLE: return 5; - case UV_IDLE: return 6; - case UV_NAMED_PIPE: return 7; - case UV_POLL: return 8; - case UV_PREPARE: return 9; - case UV_PROCESS: return 10; - case UV_STREAM: return 11; - case UV_TCP: return 12; - case UV_TIMER: return 13; - case UV_TTY: return 14; - case UV_UDP: return 15; - case UV_SIGNAL: return 16; - case UV_FILE: return 17; - case UV_UNKNOWN_HANDLE: - default: - return 0; - } -} -DEFINE_PRIM(_I32, pipe_pending_type_wrap, _HANDLE); - -// Process - -static void on_process_exit(uv_process_t *h, int64_t exit_status, int term_signal) { - events_data *ev = UV_DATA(h); - vclosure *c = ev ? ev->events[0] : NULL; - if( c ) { - hl_call3(void, c, uv_process_t *, h, int64, exit_status, int, uv_translate_sys_signal(term_signal)); - handle_clear_callback((uv_handle_t *)h, 0); - } -} - -HL_PRIM uv_process_t *HL_NAME(spawn_wrap)( uv_loop_t *loop, vstring *file, varray *args, - vclosure *on_exit, varray *stdio, varray *env, vstring *cwd, vdynamic *uid, - vdynamic *gid, vdynamic *detached, vdynamic *windowsVerbatimArguments, vdynamic *windowsHide, - vdynamic *windowsHideConsole, vdynamic *windowsHideGui ) { - UV_CHECK_NULL(loop,NULL); - UV_CHECK_NULL(file,NULL); - UV_CHECK_NULL(args,NULL); - - uv_process_t *h = UV_ALLOC(uv_process_t); - - uv_process_options_t options = {0}; - options.file = hl_to_utf8(file->bytes); - options.exit_cb = on_process_exit; - - options.args = malloc(sizeof(char *) * (args->size + 1)); - for (int i = 0; i < args->size; i++) - options.args[i] = hl_to_utf8(hl_aptr(args, vstring *)[i]->bytes); - options.args[args->size] = NULL; - - if( env ) { - options.env = malloc(sizeof(char *) * (env->size + 1)); - for (int i = 0; i < env->size; i++) - options.env[i] = hl_to_utf8(hl_aptr(env, vstring *)[i]->bytes); - options.env[env->size] = NULL; - } - if( stdio ) { - options.stdio_count = stdio->size; - options.stdio = malloc(sizeof(uv_stdio_container_t) * stdio->size); - for (int i = 0; i < stdio->size; i++) { - venum *io = hl_aptr(stdio, venum *)[i]; - if( !io ) { - options.stdio[i].flags = UV_IGNORE; - continue; - } - // On Haxe side: enum ProcessStdio - stdio_pipe *cfg; - switch( io->index ) { - case 0: // IGNORE - options.stdio[i].flags = UV_IGNORE; - break; - case 1: // INHERIT - options.stdio[i].flags = UV_INHERIT_FD; - options.stdio[i].data.fd = i; - break; - case 2: // FD(fd:StdioFd) - options.stdio[i].flags = UV_INHERIT_FD; - options.stdio[i].data.fd = ((stdio_fd *)io)->fd; - break; - case 3: // PIPE - cfg = (stdio_pipe *)io; - UV_CHECK_NULL(cfg->pipe,NULL); - options.stdio[i].flags = UV_CREATE_PIPE; - switch( cfg->permissions ) { - case 1: options.stdio[i].flags |= UV_READABLE_PIPE; break; - case 2: options.stdio[i].flags |= UV_WRITABLE_PIPE; break; - case 3: - default: options.stdio[i].flags |= UV_READABLE_PIPE | UV_WRITABLE_PIPE; break; - } - if( cfg->nonBlock && cfg->nonBlock->v.b ) - options.stdio[i].flags |= UV_OVERLAPPED_PIPE; - options.stdio[i].data.stream = (uv_stream_t *)cfg->pipe; - break; - case 4: // STREAM - UV_CHECK_NULL(((stdio_stream *)io)->stream,NULL); - options.stdio[i].flags = UV_INHERIT_STREAM; - options.stdio[i].data.stream = ((stdio_stream *)io)->stream; - break; - default: - options.stdio[i].flags = UV_IGNORE; - break; - } - } - } - - if( cwd ) - options.cwd = hl_to_utf8(cwd->bytes); - if(uid) { - options.uid = uid->v.i; - options.flags |= UV_PROCESS_SETUID; - } - if(gid) { - options.gid = gid->v.i; - options.flags |= UV_PROCESS_SETGID; - } - if(detached && detached->v.b) - options.flags |= UV_PROCESS_DETACHED; - if(windowsVerbatimArguments && windowsVerbatimArguments->v.b) - options.flags |= UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS; - if(windowsHide && windowsHide->v.b) - options.flags |= UV_PROCESS_WINDOWS_HIDE; - if(windowsHideConsole && windowsHideConsole->v.b) - options.flags |= UV_PROCESS_WINDOWS_HIDE_CONSOLE; - if(windowsHideGui && windowsHideGui->v.b) - options.flags |= UV_PROCESS_WINDOWS_HIDE_GUI; - - UV_CHECK_ERROR(uv_spawn(loop,h,&options),free(h),NULL); // free options? - handle_init_hl_data((uv_handle_t*)h); - if( on_exit ) - handle_register_callback((uv_handle_t *)h, on_exit, 0); - return h; -} -DEFINE_PRIM(_HANDLE, spawn_wrap, _LOOP _STRING _ARR _FUN(_VOID, _HANDLE _I64 _I32) _ARR _ARR _STRING _NULL(_I32) _NULL(_I32) _NULL(_BOOL) _NULL(_BOOL) _NULL(_BOOL) _NULL(_BOOL) _NULL(_BOOL)); - -HL_PRIM int HL_NAME(process_pid)( uv_process_t *h ) { - return h->pid; -} -DEFINE_PRIM(_I32, process_pid, _HANDLE); - -HL_PRIM void HL_NAME(process_kill_wrap)( uv_process_t *h, int signum ) { - UV_CHECK_NULL(h,); - UV_CHECK_ERROR(uv_process_kill(h, uv_translate_to_sys_signal(signum)),,); -} -DEFINE_PRIM(_VOID, process_kill_wrap, _HANDLE _I32); - -HL_PRIM void HL_NAME(kill_wrap)( int pid, int signum ) { - UV_CHECK_ERROR(uv_kill(pid, uv_translate_to_sys_signal(signum)),,); -} -DEFINE_PRIM(_VOID, kill_wrap, _I32 _I32); - -// UDP - -HL_PRIM uv_udp_t *HL_NAME(udp_init_wrap)( uv_loop_t *loop, vdynamic *domain, vdynamic *recvmmsg ) { - UV_CHECK_NULL(loop,NULL); - uv_udp_t *h = UV_ALLOC(uv_udp_t); - if( !domain && !recvmmsg ) { - UV_CHECK_ERROR(uv_udp_init(loop,h),free(h),NULL); - } else { - int flags = 0; - if( domain ) - flags |= address_family(domain->v.i); - if( recvmmsg && recvmmsg->v.b ) - flags |= UV_UDP_RECVMMSG; - UV_CHECK_ERROR(uv_udp_init_ex(loop,h,flags),free_handle((uv_handle_t *)h),NULL); - } - handle_init_hl_data((uv_handle_t*)h); - return h; -} -DEFINE_PRIM(_HANDLE, udp_init_wrap, _LOOP _NULL(_I32) _NULL(_BOOL)); - -HL_PRIM void HL_NAME(udp_bind_wrap)( uv_udp_t *h, uv_sockaddr_storage *addr, vdynamic *ipv6_only, vdynamic *reuse_addr ) { - UV_CHECK_NULL(h,); - UV_CHECK_NULL(addr,); - int flags = 0; - if( ipv6_only && ipv6_only->v.b ) - flags |= UV_UDP_IPV6ONLY; - if( reuse_addr && reuse_addr->v.b ) - flags |= UV_UDP_REUSEADDR; - UV_CHECK_ERROR(uv_udp_bind(h,(uv_sockaddr *)addr,flags),,); -} -DEFINE_PRIM(_VOID, udp_bind_wrap, _HANDLE _SOCKADDR_STORAGE _NULL(_BOOL) _NULL(_BOOL)); - -HL_PRIM void HL_NAME(udp_connect_wrap)( uv_udp_t *h, uv_sockaddr_storage *addr ) { - UV_CHECK_NULL(h,); - UV_CHECK_ERROR(uv_udp_connect(h,(uv_sockaddr *)addr),,); -} -DEFINE_PRIM(_VOID, udp_connect_wrap, _HANDLE _SOCKADDR_STORAGE _NULL(_BOOL)); - -HL_PRIM uv_sockaddr_storage *HL_NAME(udp_getsockname_wrap)( uv_udp_t *h ) { - UV_CHECK_NULL(h,NULL); - uv_sockaddr_storage *addr = UV_ALLOC(uv_sockaddr_storage); - int size = sizeof(uv_sockaddr_storage); - UV_CHECK_ERROR(uv_udp_getsockname(h,(uv_sockaddr *)addr,&size),free(addr),NULL); - return addr; -} -DEFINE_PRIM(_SOCKADDR_STORAGE, udp_getsockname_wrap, _HANDLE); - -HL_PRIM uv_sockaddr_storage *HL_NAME(udp_getpeername_wrap)( uv_udp_t *h ) { - UV_CHECK_NULL(h,NULL); - uv_sockaddr_storage *addr = UV_ALLOC(uv_sockaddr_storage); - int size = sizeof(uv_sockaddr_storage); - UV_CHECK_ERROR(uv_udp_getpeername(h,(uv_sockaddr *)addr,&size),free(addr),NULL); - return addr; -} -DEFINE_PRIM(_SOCKADDR_STORAGE, udp_getpeername_wrap, _HANDLE); - -static uv_membership udp_membership( int hx_membership ) { - switch( hx_membership ) { - case 0: return UV_LEAVE_GROUP; - case 1: return UV_JOIN_GROUP; - default: - hl_fatal("Unexpected UdpMembership value"); - return hx_membership; - } -} - -HL_PRIM void HL_NAME(udp_set_membership_wrap)( uv_udp_t *h, vstring *multicast_addr, vstring *interface_addr, int membership ) { - UV_CHECK_NULL(h,); - UV_CHECK_NULL(multicast_addr,); - UV_CHECK_NULL(interface_addr,); - char *m_addr = hl_to_utf8(multicast_addr->bytes); - char *i_addr = hl_to_utf8(interface_addr->bytes); - uv_membership m = udp_membership(membership); - UV_CHECK_ERROR(uv_udp_set_membership(h,m_addr,i_addr,m),,); -} -DEFINE_PRIM(_VOID, udp_set_membership_wrap, _HANDLE _STRING _STRING _STRING _I32); - -HL_PRIM void HL_NAME(udp_set_source_membership_wrap)( uv_udp_t *h, vstring *multicast_addr, vstring *interface_addr, vstring *source_addr, int membership ) { - UV_CHECK_NULL(h,); - UV_CHECK_NULL(multicast_addr,); - UV_CHECK_NULL(interface_addr,); - UV_CHECK_NULL(source_addr,); - char *m_addr = hl_to_utf8(multicast_addr->bytes); - char *i_addr = hl_to_utf8(interface_addr->bytes); - char *s_addr = hl_to_utf8(source_addr->bytes); - uv_membership m = udp_membership(membership); - UV_CHECK_ERROR(uv_udp_set_source_membership(h,m_addr,i_addr,s_addr,m),,); -} -DEFINE_PRIM(_VOID, udp_set_source_membership_wrap, _HANDLE _STRING _STRING _STRING _I32); - -HL_PRIM void HL_NAME(udp_set_multicast_loop_wrap)( uv_udp_t *h, bool on ) { - UV_CHECK_NULL(h,); - UV_CHECK_ERROR(uv_udp_set_multicast_loop(h,on),,); -} -DEFINE_PRIM(_VOID, udp_set_multicast_loop_wrap, _HANDLE _BOOL); - -HL_PRIM void HL_NAME(udp_set_multicast_ttl_wrap)( uv_udp_t *h, int ttl ) { - UV_CHECK_NULL(h,); - UV_CHECK_ERROR(uv_udp_set_multicast_ttl(h,ttl),,); -} -DEFINE_PRIM(_VOID, udp_set_multicast_ttl_wrap, _HANDLE _I32); - -HL_PRIM void HL_NAME(udp_set_multicast_interface_wrap)( uv_udp_t *h, vstring *interface_addr ) { - UV_CHECK_NULL(h,); - UV_CHECK_NULL(interface_addr,); - UV_CHECK_ERROR(uv_udp_set_multicast_interface(h,hl_to_utf8(interface_addr->bytes)),,); -} -DEFINE_PRIM(_VOID, udp_set_multicast_interface_wrap, _HANDLE _STRING); - -HL_PRIM void HL_NAME(udp_set_broadcast_wrap)( uv_udp_t *h, bool on ) { - UV_CHECK_NULL(h,); - UV_CHECK_ERROR(uv_udp_set_broadcast(h,on),,); -} -DEFINE_PRIM(_VOID, udp_set_broadcast_wrap, _HANDLE _BOOL); - -HL_PRIM void HL_NAME(udp_set_ttl_wrap)( uv_udp_t *h, int ttl ) { - UV_CHECK_NULL(h,); - UV_CHECK_ERROR(uv_udp_set_ttl(h,ttl),,); -} -DEFINE_PRIM(_VOID, udp_set_ttl_wrap, _HANDLE _I32); - -static void on_udp_send( uv_udp_send_t *r, int status ) { - UV_GET_CLOSURE(c,r,0,"No callback in udp send request"); - hl_call1(void, c, int, hl_uv_errno(status)); - free_req((uv_req_t *)r); -} - -HL_PRIM void HL_NAME(udp_send_wrap)( uv_udp_t *h, vbyte *data, int length, uv_sockaddr_storage *addr, vclosure *c ) { - UV_CHECK_NULL(h,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_udp_send_t,r,c); - events_data *d = UV_DATA(r); - uv_buf_t buf; - d->data = malloc(length); - memcpy(d->data,data,length); - buf.base = d->data; - buf.len = length; - UV_CHECK_ERROR(uv_udp_send(r,h,&buf,1,(uv_sockaddr *)addr,on_udp_send),free_req((uv_req_t *)r),); -} -DEFINE_PRIM(_VOID, udp_send_wrap, _HANDLE _BYTES _I32 _SOCKADDR_STORAGE _FUN(_VOID,_I32)); - -HL_PRIM int HL_NAME(udp_try_send_wrap)( uv_udp_t *h, vbyte *data, int length, uv_sockaddr_storage *addr ) { - UV_CHECK_NULL(h,0); - uv_buf_t buf = uv_buf_init((char *)data, length); - UV_CHECK_ERROR(uv_udp_try_send(h,&buf,1,(uv_sockaddr *)addr),,0); - return __result__; -} -DEFINE_PRIM(_I32, udp_try_send_wrap, _HANDLE _BYTES _I32 _SOCKADDR_STORAGE); - -static void on_udp_recv( uv_udp_t *h, ssize_t nread, const uv_buf_t *buf, const uv_sockaddr *src_addr, unsigned flags ) { - UV_GET_CLOSURE(c,h,0,"No recv callback in udp handle"); - - uv_sockaddr_storage *addr = NULL; - if( src_addr ) { - addr = UV_ALLOC(uv_sockaddr_storage); - memcpy(addr, src_addr, sizeof(uv_sockaddr_storage)); - } - - vdynamic *hx_flags = (vdynamic*)hl_alloc_dynobj(); - hl_dyn_seti(hx_flags, hl_hash_utf8("mmsgChunk"), &hlt_bool, 0 != (flags & UV_UDP_MMSG_CHUNK)); - hl_dyn_seti(hx_flags, hl_hash_utf8("mmsgFree"), &hlt_bool, 0 != (flags & UV_UDP_MMSG_FREE)); - hl_dyn_seti(hx_flags, hl_hash_utf8("partial"), &hlt_bool, 0 != (flags & UV_UDP_PARTIAL)); - - if( nread < 0 ) { - hl_call5(void, c, int, errno_uv2hl(nread), vbyte *, NULL, int, 0, uv_sockaddr_storage *, addr, vdynamic *, hx_flags); - } else { - hl_call5(void, c, int, 0, vbyte *, (vbyte *)buf->base, int, nread, uv_sockaddr_storage *, addr, vdynamic *, hx_flags); - } - if( (nread <= 0 && !addr) || (flags & UV_UDP_MMSG_FREE) ) - free(buf->base); -} - -HL_PRIM void HL_NAME(udp_recv_start_wrap)( uv_udp_t *h, vclosure *c ) { - UV_CHECK_NULL(h,); - UV_CHECK_NULL(c,); - handle_register_callback((uv_handle_t *)h,c,0); - UV_CHECK_ERROR(uv_udp_recv_start(h,on_alloc,on_udp_recv),handle_clear_callback((uv_handle_t*)h,0),); -} -DEFINE_PRIM(_VOID, udp_recv_start_wrap, _HANDLE _FUN(_VOID,_I32 _BYTES _I32 _SOCKADDR_STORAGE _DYN)); - -HL_PRIM bool HL_NAME(udp_using_recvmmsg_wrap)( uv_udp_t *h ) { - UV_CHECK_NULL(h,false); - UV_CHECK_ERROR(uv_udp_using_recvmmsg(h),,false); - return __result__ == 1; -} -DEFINE_PRIM(_BOOL, udp_using_recvmmsg_wrap, _HANDLE); - -HL_PRIM void HL_NAME(udp_recv_stop_wrap)( uv_udp_t *h ) { - UV_CHECK_NULL(h,); - UV_CHECK_ERROR(uv_udp_recv_stop(h),,); -} -DEFINE_PRIM(_VOID, udp_recv_stop_wrap, _HANDLE); - -HL_PRIM int HL_NAME(udp_get_send_queue_size_wrap)( uv_udp_t *h ) { - UV_CHECK_NULL(h,0); - return uv_udp_get_send_queue_size(h); -} -DEFINE_PRIM(_I32, udp_get_send_queue_size_wrap, _HANDLE); - -HL_PRIM int HL_NAME(udp_get_send_queue_count_wrap)( uv_udp_t *h ) { - UV_CHECK_NULL(h,0); - return uv_udp_get_send_queue_count(h); -} -DEFINE_PRIM(_I32, udp_get_send_queue_count_wrap, _HANDLE); - -// DNS - -static void on_getaddrinfo( uv_getaddrinfo_t *r, int status, struct addrinfo *res ) { - UV_GET_CLOSURE(c,r,0,"No callback in getaddrinfo request"); - - int count = 0; - struct addrinfo *current = res; - while( current ) { - ++count; - current = current->ai_next; - } - - varray *addresses = hl_alloc_array(&hlt_dyn, count); - current = res; - int hfamily = hl_hash_utf8("family"); - int hsockType = hl_hash_utf8("sockType"); - int hprotocol = hl_hash_utf8("protocol"); - int haddr = hl_hash_utf8("addr"); - int hcanonName = hl_hash_utf8("canonName"); - int i = 0; - vdynamic *entry; - while( current ) { - entry = (vdynamic *)hl_alloc_dynobj(); - uv_sockaddr_storage *addr = UV_ALLOC(uv_sockaddr_storage); - memcpy(addr, current->ai_addr, current->ai_addrlen); - hl_dyn_setp(entry,haddr,&hlt_sockaddr,addr); - hl_dyn_seti(entry,hprotocol,&hlt_i32,current->ai_protocol); - if( current->ai_canonname ) { - vbyte *canonname = hl_copy_bytes((vbyte *)current->ai_canonname, strlen(current->ai_canonname)); - hl_dyn_setp(entry,hcanonName,&hlt_bytes,canonname); - } - int family; - switch( current->ai_family ) { - case PF_UNSPEC: family = -1; break; - case PF_INET: family = -2; break; - case PF_INET6: family = -3; break; - default: family = current->ai_family; break; - } - hl_dyn_seti(entry,hfamily,&hlt_i32,family); - int socktype; - switch( current->ai_socktype ) { - case SOCK_STREAM: socktype = -1; break; - case SOCK_DGRAM: socktype = -2; break; - case SOCK_RAW: socktype = -3; break; - default: socktype = current->ai_socktype; break; - } - hl_dyn_seti(entry,hsockType,&hlt_i32,socktype); - hl_aptr(addresses, vdynamic*)[i] = entry; - current = current->ai_next; - ++i; - } - freeaddrinfo(res); - - hl_call2(void,c,int,errno_uv2hl(status),varray *,addresses); - free_req((uv_req_t *)r); -} - -HL_PRIM void HL_NAME(getaddrinfo_wrap)( uv_loop_t *l, vstring *name, vstring *service, vdynamic *flags, - vdynamic *family, vdynamic *socktype, vdynamic *protocol, vclosure *c ) { - UV_CHECK_NULL(l,); - UV_CHECK_NULL(( name || service ),); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_getaddrinfo_t,r,c); - char *c_name = name ? hl_to_utf8(name->bytes) : NULL; - char *c_service = service ? hl_to_utf8(service->bytes) : NULL; - struct addrinfo hints = {0}; - if( family ) - switch( family->v.i ) { - case -1: hints.ai_family = PF_UNSPEC; break; - case -2: hints.ai_family = PF_INET; break; - case -3: hints.ai_family = PF_INET6; break; - default: hints.ai_family = family->v.i; break; - } - if( socktype ) - switch( socktype->v.i ) { - case -1: hints.ai_socktype = SOCK_STREAM; break; - case -2: hints.ai_socktype = SOCK_DGRAM; break; - case -3: hints.ai_socktype = SOCK_RAW; break; - default: hints.ai_socktype = socktype->v.i; break; - } - if( protocol ) - hints.ai_protocol = protocol->v.i; - if( flags ) { - if( hl_dyn_geti(flags,hl_hash_utf8("passive"),&hlt_bool) ) - hints.ai_flags |= AI_PASSIVE; - if( hl_dyn_geti(flags,hl_hash_utf8("canonName"),&hlt_bool) ) - hints.ai_flags |= AI_CANONNAME; - if( hl_dyn_geti(flags,hl_hash_utf8("numericHost"),&hlt_bool) ) - hints.ai_flags |= AI_NUMERICHOST; - if( hl_dyn_geti(flags,hl_hash_utf8("numericServ"),&hlt_bool) ) - hints.ai_flags |= AI_NUMERICSERV; - if( hl_dyn_geti(flags,hl_hash_utf8("v4Mapped"),&hlt_bool) ) - hints.ai_flags |= AI_V4MAPPED; - if( hl_dyn_geti(flags,hl_hash_utf8("all"),&hlt_bool) ) - hints.ai_flags |= AI_ALL; - if( hl_dyn_geti(flags,hl_hash_utf8("addrConfig"),&hlt_bool) ) - hints.ai_flags |= AI_ADDRCONFIG; - } - UV_CHECK_ERROR(uv_getaddrinfo(l,r,on_getaddrinfo,c_name,c_service,&hints),free_req((uv_req_t *)r),); -} -DEFINE_PRIM(_VOID, getaddrinfo_wrap, _LOOP _STRING _STRING _DYN _NULL(_I32) _NULL(_I32) _NULL(_I32) _FUN(_VOID,_I32 _ARR)); - -static void on_getnameinfo( uv_getnameinfo_t *r, int status, const char *hostname, const char *service ) { - UV_GET_CLOSURE(c,r,0,"No callback in getnameinfo request"); - vbyte * bhost = NULL; - if( hostname ) - bhost = hl_copy_bytes((const vbyte *)hostname, strlen(hostname)); - vbyte * bservice = NULL; - if( service ) - bservice = hl_copy_bytes((const vbyte *)service, strlen(service)); - hl_call3(void,c,int,errno_uv2hl(status),vbyte *,bhost,vbyte *,bservice); - free_req((uv_req_t *)r); -} - -HL_PRIM void HL_NAME(getnameinfo_wrap)( uv_loop_t *l, uv_sockaddr_storage *addr, vdynamic *namereqd, vdynamic *dgram, - vdynamic *nofqdn, vdynamic *numerichost, vdynamic *numericserv, vclosure *c ) { - UV_CHECK_NULL(l,); - UV_CHECK_NULL(addr,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_getnameinfo_t,r,c); - int flags = 0; - if( namereqd && namereqd->v.b ) - flags |= NI_NAMEREQD; - if( dgram && dgram->v.b ) - flags |= NI_DGRAM; - if( nofqdn && nofqdn->v.b ) - flags |= NI_NOFQDN; - if( numerichost && numerichost->v.b ) - flags |= NI_NUMERICHOST; - if( numericserv && numericserv->v.b ) - flags |= NI_NUMERICSERV; - UV_CHECK_ERROR(uv_getnameinfo(l,r,on_getnameinfo,(uv_sockaddr *)addr,flags),free_req((uv_req_t *)r),); -} -DEFINE_PRIM(_VOID, getnameinfo_wrap, _LOOP _SOCKADDR_STORAGE _NULL(_BOOL) _NULL(_BOOL) _NULL(_BOOL) _NULL(_BOOL) _NULL(_BOOL) _FUN(_VOID,_I32 _BYTES _BYTES)); - -// loop - -HL_PRIM uv_loop_t *HL_NAME(loop_init_wrap)( ) { - uv_loop_t *loop = UV_ALLOC(uv_loop_t); - UV_CHECK_ERROR(uv_loop_init(loop),free(loop),NULL); - return loop; -} -DEFINE_PRIM(_LOOP, loop_init_wrap, _NO_ARG); - -// DEFINE_PRIM(_LOOP, default_loop, _NO_ARG); - -HL_PRIM void HL_NAME(loop_close_wrap)( uv_loop_t *loop ) { - UV_CHECK_NULL(loop,); - UV_CHECK_ERROR(uv_loop_close(loop),,); -} -DEFINE_PRIM(_VOID, loop_close_wrap, _LOOP); - -HL_PRIM bool HL_NAME(run_wrap)( uv_loop_t *loop, int mode ) { - UV_CHECK_NULL(loop,false); - return uv_run(loop, mode) != 0; -} -DEFINE_PRIM(_BOOL, run_wrap, _LOOP _I32); - -HL_PRIM bool HL_NAME(loop_alive_wrap)( uv_loop_t *loop ) { - UV_CHECK_NULL(loop,false); - return uv_loop_alive(loop) != 0; -} -DEFINE_PRIM(_BOOL, loop_alive_wrap, _LOOP); - -HL_PRIM void HL_NAME(stop_wrap)( uv_loop_t *loop ) { - UV_CHECK_NULL(loop,); - uv_stop(loop); -} -DEFINE_PRIM(_VOID, stop_wrap, _LOOP); - -// File system - -static void on_fs_open( uv_fs_t *r ) { - UV_GET_CLOSURE(c,r,0,"No callback in fs_open request"); - hl_call2(void, c, int, hl_uv_errno(r->result), int, r->result); - free_req((uv_req_t *)r); -} - -HL_PRIM void HL_NAME(fs_open_wrap)( uv_loop_t *loop, vstring *path, varray *flags, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(flags,); - UV_CHECK_NULL(c,); - - int i_flags = 0; - int mode = 0; - for( int i=0; isize; i++ ){ - venum *flag = hl_aptr(flags, venum*)[i]; - if( !flag ) { - continue; - } - switch( flag->index ) { - case 0: i_flags |= UV_FS_O_APPEND; break; - case 1: - i_flags |= UV_FS_O_CREAT; - mode = ((struct {hl_type *t;int index;int mode;} *)flag)->mode; - break; - case 2: i_flags |= UV_FS_O_DIRECT; break; - case 3: i_flags |= UV_FS_O_DIRECTORY; break; - case 4: i_flags |= UV_FS_O_DSYNC; break; - case 5: i_flags |= UV_FS_O_EXCL; break; - case 6: i_flags |= UV_FS_O_EXLOCK; break; - case 7: i_flags |= UV_FS_O_FILEMAP; break; - case 8: i_flags |= UV_FS_O_NOATIME; break; - case 9: i_flags |= UV_FS_O_NOCTTY; break; - case 10: i_flags |= UV_FS_O_NOFOLLOW; break; - case 11: i_flags |= UV_FS_O_NONBLOCK; break; - case 12: i_flags |= UV_FS_O_RANDOM; break; - case 13: i_flags |= UV_FS_O_RDONLY; break; - case 14: i_flags |= UV_FS_O_RDWR; break; - case 15: i_flags |= UV_FS_O_SEQUENTIAL; break; - case 16: i_flags |= UV_FS_O_SHORT_LIVED; break; - case 17: i_flags |= UV_FS_O_SYMLINK; break; - case 18: i_flags |= UV_FS_O_SYNC; break; - case 19: i_flags |= UV_FS_O_TEMPORARY; break; - case 20: i_flags |= UV_FS_O_TRUNC; break; - case 21: i_flags |= UV_FS_O_WRONLY; break; - } - } - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_open(loop,r,hl_to_utf8(path->bytes),i_flags,mode,on_fs_open),free_req((uv_req_t *)r),); -} -DEFINE_PRIM(_VOID, fs_open_wrap, _LOOP _STRING _ARR _FUN(_VOID,_I32 _I32)); - -static void on_fs_common( uv_fs_t *r ) { - UV_GET_CLOSURE(c,r,0,"No callback in fs request"); - hl_call1(void,c,int,hl_uv_errno(r->result)); - free_fs_req(r); -} - -static void on_fs_bytes_handled( uv_fs_t *r ) { - UV_GET_CLOSURE(c,r,0,"No callback in fs request"); - hl_call2(void,c,int,hl_uv_errno(r->result),int64,r->result<0?0:r->result); - free_fs_req(r); -} - -static void on_fs_path( uv_fs_t *r ) { - UV_GET_CLOSURE(c,r,0,"No callback in fs request"); - hl_call2(void,c,int,hl_uv_errno(r->result),vbyte *,(vbyte *)(r->result<0?NULL:r->path)); - free_fs_req(r); -} - -static void on_fs_bytes( uv_fs_t *r ) { - UV_GET_CLOSURE(c,r,0,"No callback in fs request"); - hl_call2(void,c,int,hl_uv_errno(r->result),vbyte *,(vbyte *)(r->result<0?NULL:r->ptr)); - free_fs_req(r); -} - -HL_PRIM void HL_NAME(fs_close_wrap)( uv_file file, uv_loop_t *loop, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_close(loop,r,file,on_fs_common),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_close_wrap, _I32 _LOOP _FUN(_VOID,_I32)); - -HL_PRIM void HL_NAME(fs_write_wrap)( uv_file file, uv_loop_t *loop, vbyte *data, int length, int64 offset, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(data,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_COPY_DATA(buf,r,data,length); - UV_CHECK_ERROR(uv_fs_write(loop,r,file,&buf,1,offset,on_fs_bytes_handled),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_write_wrap, _I32 _LOOP _BYTES _I32 _I64 _FUN(_VOID,_I32 _I64)); - -HL_PRIM void HL_NAME(fs_read_wrap)( uv_file file, uv_loop_t *loop, vbyte *buf, int length, int64 offset, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(buf,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - uv_buf_t b = uv_buf_init((char *)buf, length); - UV_CHECK_ERROR(uv_fs_read(loop,r,file,&b,1,offset,on_fs_bytes_handled),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_read_wrap, _I32 _LOOP _BYTES _I32 _I64 _FUN(_VOID,_I32 _I64)); - -HL_PRIM void HL_NAME(fs_unlink_wrap)( uv_loop_t *loop, vstring *path, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(path,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_unlink(loop,r,hl_to_utf8(path->bytes),on_fs_common),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_unlink_wrap, _LOOP _STRING _FUN(_VOID,_I32)); - -HL_PRIM void HL_NAME(fs_mkdir_wrap)( uv_loop_t *loop, vstring *path, int mode, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(path,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_mkdir(loop,r,hl_to_utf8(path->bytes),mode,on_fs_common),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_mkdir_wrap, _LOOP _STRING _I32 _FUN(_VOID,_I32)); - -HL_PRIM void HL_NAME(fs_mkdtemp_wrap)( uv_loop_t *loop, vstring *tpl, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(tpl,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_mkdtemp(loop,r,hl_to_utf8(tpl->bytes),on_fs_path),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_mkdtemp_wrap, _LOOP _STRING _FUN(_VOID,_I32 _BYTES)); - -static void on_fs_mkstemp( uv_fs_t *r ) { - UV_GET_CLOSURE(c,r,0,"No callback in fs_mkstemp request"); - hl_call3(void,c,int,hl_uv_errno(r->result),int,r->result,vbyte *,(vbyte *)(r->result<0?NULL:r->path)); - free_fs_req(r); -} - -HL_PRIM void HL_NAME(fs_mkstemp_wrap)( uv_loop_t *loop, vstring *tpl, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(tpl,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_mkstemp(loop,r,hl_to_utf8(tpl->bytes),on_fs_mkstemp),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_mkstemp_wrap, _LOOP _STRING _FUN(_VOID,_I32 _I32 _BYTES)); - -HL_PRIM void HL_NAME(fs_rmdir_wrap)( uv_loop_t *loop, vstring *path, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(path,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_rmdir(loop,r,hl_to_utf8(path->bytes),on_fs_common),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_rmdir_wrap, _LOOP _STRING _FUN(_VOID,_I32)); - -static void on_fs_opendir( uv_fs_t *r ) { - UV_GET_CLOSURE(c,r,0,"No callback in fs_opendir request"); - hl_call2(void,c,int,hl_uv_errno(r->result),uv_dir_t *,(r->result<0?NULL:r->ptr)); - free_fs_req(r); -} - -HL_PRIM void HL_NAME(fs_opendir_wrap)( uv_loop_t *loop, vstring *path, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(path,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_opendir(loop,r,hl_to_utf8(path->bytes),on_fs_opendir),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_opendir_wrap, _LOOP _STRING _FUN(_VOID,_I32 _DIR)); - -HL_PRIM void HL_NAME(fs_closedir_wrap)( uv_dir_t *dir, uv_loop_t *loop, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(dir,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_closedir(loop,r,dir,on_fs_common),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_closedir_wrap, _DIR _LOOP _FUN(_VOID,_I32)); - -static void on_fs_readdir( uv_fs_t *r ) { - UV_GET_CLOSURE(c,r,0,"No callback in fs_readdir request"); - if( r->result < 0 ) { - hl_call2(void,c,int,hl_uv_errno(r->result),vdynamic *,NULL); - } else { - uv_dir_t *dir = r->ptr; - varray *entries = hl_alloc_array(&hlt_dyn, r->result); - int hash_type = hl_hash_utf8("type"); - int hash_name = hl_hash_utf8("name"); - for(int i = 0; i < r->result; i++) { - vdynamic *entry = (vdynamic*)hl_alloc_dynobj(); - - int entry_type = 0; - switch( dir->dirents[i].type ) { - case UV_DIRENT_UNKNOWN: entry_type = 1; break; - case UV_DIRENT_FILE: entry_type = 2; break; - case UV_DIRENT_DIR: entry_type = 3; break; - case UV_DIRENT_LINK: entry_type = 4; break; - case UV_DIRENT_FIFO: entry_type = 5; break; - case UV_DIRENT_SOCKET: entry_type = 6; break; - case UV_DIRENT_CHAR: entry_type = 7; break; - case UV_DIRENT_BLOCK: entry_type = 8; break; - } - hl_dyn_seti(entry, hash_type, &hlt_i32, entry_type); - - const char *c_name = dir->dirents[i].name; - vbyte *name = hl_copy_bytes((const vbyte *)c_name, strlen(c_name)); - hl_dyn_setp(entry, hash_name, &hlt_bytes, name); - - hl_aptr(entries,vdynamic *)[i] = entry; - } - hl_call2(void,c,int,0,varray *,entries); - } - free_fs_req(r); -} - -HL_PRIM void HL_NAME(fs_readdir_wrap)( uv_dir_t *dir, uv_loop_t *loop, int num_entries, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(dir,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - dir->nentries = num_entries; - dir->dirents = malloc(sizeof(uv_dirent_t) * num_entries); - UV_CHECK_ERROR(uv_fs_readdir(loop,r,dir,on_fs_readdir),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_readdir_wrap, _DIR _LOOP _I32 _FUN(_VOID,_I32 _ARR)); - -static vdynamic *alloc_timespec_dyn( const uv_timespec_t *spec ) { - vdynamic *obj = (vdynamic*)hl_alloc_dynobj(); - hl_dyn_seti(obj, hl_hash_utf8("sec"), &hlt_i64, (int64)spec->tv_sec); - hl_dyn_seti(obj, hl_hash_utf8("nsec"), &hlt_i64, (int64)spec->tv_nsec); - return obj; -} - -static vdynamic *alloc_stat_dyn( const uv_stat_t *stat ) { - vdynamic *obj = (vdynamic*)hl_alloc_dynobj(); - dyn_set_i64(obj, hl_hash_utf8("dev"), stat->st_dev); - dyn_set_i64(obj, hl_hash_utf8("mode"), stat->st_mode); - dyn_set_i64(obj, hl_hash_utf8("nlink"), stat->st_nlink); - dyn_set_i64(obj, hl_hash_utf8("uid"), stat->st_uid); - dyn_set_i64(obj, hl_hash_utf8("gid"), stat->st_gid); - dyn_set_i64(obj, hl_hash_utf8("rdev"), stat->st_rdev); - dyn_set_i64(obj, hl_hash_utf8("ino"), stat->st_ino); - dyn_set_i64(obj, hl_hash_utf8("size"), stat->st_size); - dyn_set_i64(obj, hl_hash_utf8("blksize"), stat->st_blksize); - dyn_set_i64(obj, hl_hash_utf8("blocks"), stat->st_blocks); - dyn_set_i64(obj, hl_hash_utf8("flags"), stat->st_flags); - dyn_set_i64(obj, hl_hash_utf8("gen"), stat->st_gen); - hl_dyn_setp(obj, hl_hash_utf8("atim"), &hlt_dyn, alloc_timespec_dyn(&stat->st_atim)); - hl_dyn_setp(obj, hl_hash_utf8("mtim"), &hlt_dyn, alloc_timespec_dyn(&stat->st_mtim)); - hl_dyn_setp(obj, hl_hash_utf8("ctim"), &hlt_dyn, alloc_timespec_dyn(&stat->st_ctim)); - hl_dyn_setp(obj, hl_hash_utf8("birthtim"), &hlt_dyn, alloc_timespec_dyn(&stat->st_birthtim)); - return obj; -} - -static void on_fs_stat( uv_fs_t *r ) { - UV_GET_CLOSURE(c,r,0,"No callback in fs stat request"); - if( r->result < 0 ) { - hl_call2(void,c,int,hl_uv_errno(r->result),vdynamic *,NULL); - } else { - hl_call2(void,c,int,0,vdynamic *,alloc_stat_dyn(&r->statbuf)); - } - free_fs_req(r); -} - -HL_PRIM void HL_NAME(fs_stat_wrap)( uv_loop_t *loop, vstring *path, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(path,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_stat(loop,r,hl_to_utf8(path->bytes),on_fs_stat),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_stat_wrap, _LOOP _STRING _FUN(_VOID,_I32 _DYN)); - -HL_PRIM void HL_NAME(fs_fstat_wrap)( uv_file file, uv_loop_t *loop, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_fstat(loop,r,file,on_fs_stat),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_fstat_wrap, _I32 _LOOP _FUN(_VOID,_I32 _DYN)); - -HL_PRIM void HL_NAME(fs_lstat_wrap)( uv_loop_t *loop, vstring *path, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(path,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_lstat(loop,r,hl_to_utf8(path->bytes),on_fs_stat),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_lstat_wrap, _LOOP _STRING _FUN(_VOID,_I32 _DYN)); - -static void on_fs_statfs( uv_fs_t *r ) { - UV_GET_CLOSURE(c,r,0,"No callback in statfs request"); - events_data *ev = UV_DATA(r); - if( r->result < 0 ) { - hl_call2(void,c,int,hl_uv_errno(r->result),vdynamic *,NULL); - } else { - uv_statfs_t *stat = r->ptr; - vdynamic *obj = (vdynamic *)hl_alloc_dynobj(); - dyn_set_i64(obj, hl_hash_utf8("type"), stat->f_type); - dyn_set_i64(obj, hl_hash_utf8("bsize"), stat->f_bsize); - dyn_set_i64(obj, hl_hash_utf8("blocks"), stat->f_blocks); - dyn_set_i64(obj, hl_hash_utf8("bfree"), stat->f_bfree); - dyn_set_i64(obj, hl_hash_utf8("bavail"), stat->f_bavail); - dyn_set_i64(obj, hl_hash_utf8("files"), stat->f_files); - dyn_set_i64(obj, hl_hash_utf8("ffree"), stat->f_ffree); - - varray *spare = hl_alloc_array(&hlt_i64, 4); - for (int i = 0; i < 4; i++) - hl_aptr(spare,int64)[i] = stat->f_spare[i]; - hl_dyn_setp(obj, hl_hash_utf8("spare"), &hlt_array, spare); - - hl_call2(void,c,int,0,vdynamic *,obj); - } - ev->data = NULL; - free_fs_req(r); -} - -HL_PRIM void HL_NAME(fs_statfs_wrap)( uv_loop_t *loop, vstring *path, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(path,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_statfs(loop,r,hl_to_utf8(path->bytes),on_fs_statfs),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_statfs_wrap, _LOOP _STRING _FUN(_VOID,_I32 _DYN)); - -HL_PRIM void HL_NAME(fs_rename_wrap)( uv_loop_t *loop, vstring *path, vstring *new_path, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(path,); - UV_CHECK_NULL(new_path,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_rename(loop,r,hl_to_utf8(path->bytes),hl_to_utf8(new_path->bytes),on_fs_common),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_rename_wrap, _LOOP _STRING _STRING _FUN(_VOID,_I32)); - -HL_PRIM void HL_NAME(fs_fsync_wrap)( uv_file file, uv_loop_t *loop, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_fsync(loop,r,file,on_fs_common),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_fsync_wrap, _I32 _LOOP _FUN(_VOID,_I32)); - -HL_PRIM void HL_NAME(fs_fdatasync_wrap)( uv_file file, uv_loop_t *loop, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_fdatasync(loop,r,file,on_fs_common),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_fdatasync_wrap, _I32 _LOOP _FUN(_VOID,_I32)); - -HL_PRIM void HL_NAME(fs_ftruncate_wrap)( uv_file file, uv_loop_t *loop, int64 offset, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_ftruncate(loop,r,file,offset,on_fs_common),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_ftruncate_wrap, _I32 _LOOP _I64 _FUN(_VOID,_I32)); - -HL_PRIM void HL_NAME(fs_copyfile_wrap)( uv_loop_t *loop, vstring *path, vstring *new_path, varray_bytes *flags, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(path,); - UV_CHECK_NULL(new_path,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - int i_flags = 0; - if( flags ) { - for(int i = 0; i < flags->length; i++) { - switch( bytes_geti32(flags->bytes, i * 4) ) { - case 1: i_flags |= UV_FS_COPYFILE_EXCL; break; - case 2: i_flags |= UV_FS_COPYFILE_FICLONE; break; - case 3: i_flags |= UV_FS_COPYFILE_FICLONE_FORCE; break; - } - } - } - UV_CHECK_ERROR(uv_fs_copyfile(loop,r,hl_to_utf8(path->bytes),hl_to_utf8(new_path->bytes),i_flags,on_fs_common),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_copyfile_wrap, _LOOP _STRING _STRING _ARRBYTES _FUN(_VOID,_I32)); - -HL_PRIM void HL_NAME(fs_sendfile_wrap)( uv_file src, uv_loop_t *loop, uv_file dst, int64 in_offset, int64 length, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_sendfile(loop,r,src,dst,in_offset,length,on_fs_bytes_handled),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_sendfile_wrap, _I32 _LOOP _I32 _I64 _I64 _FUN(_VOID,_I32 _I64)); - -HL_PRIM void HL_NAME(fs_access_wrap)( uv_loop_t *loop, vstring *path, varray_bytes *mode, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(path,); - UV_CHECK_NULL(mode,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - int i_mode = 0; - if( mode ) { - for(int i = 0; i < mode->length; i++) { - switch( bytes_geti32(mode->bytes, i * 4) ) { - case 0: i_mode |= F_OK; break; - case 1: i_mode |= R_OK; break; - case 2: i_mode |= W_OK; break; - case 3: i_mode |= X_OK; break; - } - } - } - UV_CHECK_ERROR(uv_fs_access(loop,r,hl_to_utf8(path->bytes),i_mode,on_fs_common),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_access_wrap, _LOOP _STRING _ARRBYTES _FUN(_VOID,_I32)); - -HL_PRIM void HL_NAME(fs_chmod_wrap)( uv_loop_t *loop, vstring *path, int mode, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(path,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_chmod(loop,r,hl_to_utf8(path->bytes),mode,on_fs_common),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_chmod_wrap, _LOOP _STRING _I32 _FUN(_VOID,_I32)); - -HL_PRIM void HL_NAME(fs_fchmod_wrap)( uv_file file, uv_loop_t *loop, int mode, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_fchmod(loop,r,file,mode,on_fs_common),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_fchmod_wrap, _I32 _LOOP _I32 _FUN(_VOID,_I32)); - -HL_PRIM void HL_NAME(fs_utime_wrap)( uv_loop_t *loop, vstring *path, double atime, double mtime, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(path,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_utime(loop,r,hl_to_utf8(path->bytes),atime,mtime,on_fs_common),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_utime_wrap, _LOOP _STRING _F64 _F64 _FUN(_VOID,_I32)); - -HL_PRIM void HL_NAME(fs_futime_wrap)( uv_file file, uv_loop_t *loop, double atime, double mtime, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_futime(loop,r,file,atime,mtime,on_fs_common),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_futime_wrap, _I32 _LOOP _F64 _F64 _FUN(_VOID,_I32)); - -HL_PRIM void HL_NAME(fs_lutime_wrap)( uv_loop_t *loop, vstring *path, double atime, double mtime, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(path,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_lutime(loop,r,hl_to_utf8(path->bytes),atime,mtime,on_fs_common),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_lutime_wrap, _LOOP _STRING _F64 _F64 _FUN(_VOID,_I32)); - -HL_PRIM void HL_NAME(fs_link_wrap)( uv_loop_t *loop, vstring *path, vstring *link, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(path,); - UV_CHECK_NULL(link,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_link(loop,r,hl_to_utf8(path->bytes),hl_to_utf8(link->bytes),on_fs_common),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_link_wrap, _LOOP _STRING _STRING _FUN(_VOID,_I32)); - -HL_PRIM void HL_NAME(fs_symlink_wrap)( uv_loop_t *loop, vstring *path, vstring *link, varray_bytes *flags, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(path,); - UV_CHECK_NULL(link,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - int i_flags = 0; - if( flags ) { - for(int i = 0; i < flags->length; i++) { - switch( bytes_geti32(flags->bytes, i * 4) ) { - case 0: i_flags |= UV_FS_SYMLINK_DIR; break; - case 1: i_flags |= UV_FS_SYMLINK_JUNCTION; break; - } - } - } - UV_CHECK_ERROR(uv_fs_symlink(loop,r,hl_to_utf8(path->bytes),hl_to_utf8(link->bytes),i_flags,on_fs_common),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_symlink_wrap, _LOOP _STRING _STRING _ARRBYTES _FUN(_VOID,_I32)); - -HL_PRIM void HL_NAME(fs_readlink_wrap)( uv_loop_t *loop, vstring *path, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(path,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_readlink(loop,r,hl_to_utf8(path->bytes),on_fs_bytes),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_readlink_wrap, _LOOP _STRING _FUN(_VOID,_I32 _BYTES)); - -HL_PRIM void HL_NAME(fs_realpath_wrap)( uv_loop_t *loop, vstring *path, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(path,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_realpath(loop,r,hl_to_utf8(path->bytes),on_fs_bytes),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_realpath_wrap, _LOOP _STRING _FUN(_VOID,_I32 _BYTES)); - -HL_PRIM void HL_NAME(fs_chown_wrap)( uv_loop_t *loop, vstring *path, int uid, int gid, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(path,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_chown(loop,r,hl_to_utf8(path->bytes),uid,gid,on_fs_common),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_chown_wrap, _LOOP _STRING _I32 _I32 _FUN(_VOID,_I32)); - -HL_PRIM void HL_NAME(fs_fchown_wrap)( uv_file file, uv_loop_t *loop, int uid, int gid, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_fchown(loop,r,file,uid,gid,on_fs_common),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_fchown_wrap, _I32 _LOOP _I32 _I32 _FUN(_VOID,_I32)); - -HL_PRIM void HL_NAME(fs_lchown_wrap)( uv_loop_t *loop, vstring *path, int uid, int gid, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(path,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_fs_t,r,c); - UV_CHECK_ERROR(uv_fs_lchown(loop,r,hl_to_utf8(path->bytes),uid,gid,on_fs_common),free_fs_req(r),); -} -DEFINE_PRIM(_VOID, fs_lchown_wrap, _LOOP _STRING _I32 _I32 _FUN(_VOID,_I32)); - -// Fs event - -HL_PRIM uv_fs_event_t *HL_NAME(fs_event_init_wrap)( uv_loop_t *loop ) { - UV_CHECK_NULL(loop,NULL); - uv_fs_event_t *h = UV_ALLOC(uv_fs_event_t); - UV_CHECK_ERROR(uv_fs_event_init(loop,h),free(h),NULL); - handle_init_hl_data((uv_handle_t*)h); - return h; -} -DEFINE_PRIM(_HANDLE, fs_event_init_wrap, _LOOP); - -static void on_fs_event( uv_fs_event_t *h, const char *filename, int events, int status ) { - UV_GET_CLOSURE(c,h,0,"No callback in fs_event handle"); - if( status < 0 ) { - hl_call3(void,c,int,errno_uv2hl(status),vbyte *,NULL,varray *,NULL); - } else { - int size = (0 != (UV_RENAME & events)) + (0 != (UV_CHANGE & events)); - varray *a_events = hl_alloc_array(&hlt_i32, size); - int i = 0; - if( UV_RENAME & events ) - hl_aptr(a_events,int)[i++] = UV_RENAME; - if( UV_CHANGE & events ) - hl_aptr(a_events,int)[i++] = UV_CHANGE; - vbyte *path = hl_copy_bytes((const vbyte *)filename, strlen(filename)); - hl_call3(void,c,int,0,vbyte *,path,varray *,a_events); - } -} - -HL_PRIM void HL_NAME(fs_event_start_wrap)( uv_fs_event_t *h, vstring *path, varray_bytes *flags, vclosure *c ) { - UV_CHECK_NULL(h,); - UV_CHECK_NULL(path,); - UV_CHECK_NULL(c,); - handle_register_callback((uv_handle_t*)h,c,0); - unsigned int i_flags = 0; - if( flags ) { - for(int i = 0; i < flags->length; i++) { - switch( bytes_geti32(flags->bytes, i * 4) ) { - case 1: i_flags |= UV_FS_EVENT_WATCH_ENTRY; break; - case 2: i_flags |= UV_FS_EVENT_STAT; break; - case 3: i_flags |= UV_FS_EVENT_RECURSIVE; break; - } - } - } - UV_CHECK_ERROR(uv_fs_event_start(h,on_fs_event,hl_to_utf8(path->bytes),i_flags),handle_clear_callback((uv_handle_t *)h,0),); -} -DEFINE_PRIM(_VOID, fs_event_start_wrap, _HANDLE _STRING _ARRBYTES _FUN(_VOID,_I32 _BYTES _ARR)); - -HL_PRIM void HL_NAME(fs_event_stop_wrap)( uv_fs_event_t *h ) { - UV_CHECK_NULL(h,); - UV_CHECK_ERROR(uv_fs_event_stop(h),,); -} -DEFINE_PRIM(_VOID, fs_event_stop_wrap, _HANDLE); - -// Fs poll - -HL_PRIM uv_fs_poll_t *HL_NAME(fs_poll_init_wrap)( uv_loop_t *loop ) { - UV_CHECK_NULL(loop,NULL); - uv_fs_poll_t *h = UV_ALLOC(uv_fs_poll_t); - UV_CHECK_ERROR(uv_fs_poll_init(loop,h),free(h),NULL); - handle_init_hl_data((uv_handle_t*)h); - return h; -} -DEFINE_PRIM(_HANDLE, fs_poll_init_wrap, _LOOP); - -static void on_fs_poll( uv_fs_poll_t *h, int status, const uv_stat_t *prev, const uv_stat_t *curr ) { - UV_GET_CLOSURE(c,h,0,"No callback in fs_poll handle"); - hl_call3(void,c,int,0,vdynamic *,(prev?alloc_stat_dyn(prev):NULL),vdynamic *,(curr?alloc_stat_dyn(curr):NULL)); -} - -HL_PRIM void HL_NAME(fs_poll_start_wrap)( uv_fs_poll_t *h, vstring *path, int interval, vclosure *c ) { - UV_CHECK_NULL(h,); - UV_CHECK_NULL(path,); - UV_CHECK_NULL(c,); - handle_register_callback((uv_handle_t*)h,c,0); - UV_CHECK_ERROR(uv_fs_poll_start(h,on_fs_poll,hl_to_utf8(path->bytes),interval),handle_clear_callback((uv_handle_t *)h,0),); -} -DEFINE_PRIM(_VOID, fs_poll_start_wrap, _HANDLE _STRING _I32 _FUN(_VOID,_I32 _DYN _DYN)); - -HL_PRIM void HL_NAME(fs_poll_stop_wrap)( uv_fs_poll_t *h ) { - UV_CHECK_NULL(h,); - UV_CHECK_ERROR(uv_fs_poll_stop(h),,); -} -DEFINE_PRIM(_VOID, fs_poll_stop_wrap, _HANDLE); - -// Miscellaneous - -HL_PRIM int64 HL_NAME(resident_set_memory_wrap)() { - size_t rss; - UV_CHECK_ERROR(uv_resident_set_memory(&rss),,0); - return rss; -} -DEFINE_PRIM(_I64, resident_set_memory_wrap, _NO_ARG); - -HL_PRIM double HL_NAME(uptime_wrap)() { - double uptime; - UV_CHECK_ERROR(uv_uptime(&uptime),,0); - return uptime; -} -DEFINE_PRIM(_F64, uptime_wrap, _NO_ARG); - -static vdynamic *alloc_timeval_dyn( const uv_timeval_t *tv ) { - vdynamic *obj = (vdynamic*)hl_alloc_dynobj(); - hl_dyn_seti(obj, hl_hash_utf8("sec"), &hlt_i64, (int64)tv->tv_sec); - hl_dyn_seti(obj, hl_hash_utf8("usec"), &hlt_i64, (int64)tv->tv_usec); - return obj; -} - -HL_PRIM vdynamic *HL_NAME(getrusage_wrap)() { - uv_rusage_t rusage = {0}; - UV_CHECK_ERROR(uv_getrusage(&rusage),,NULL); - vdynamic *obj = (vdynamic *)hl_alloc_dynobj(); - hl_dyn_setp(obj, hl_hash_utf8("utime"), &hlt_dyn, alloc_timeval_dyn(&rusage.ru_utime)); - hl_dyn_setp(obj, hl_hash_utf8("stime"), &hlt_dyn, alloc_timeval_dyn(&rusage.ru_stime)); - dyn_set_i64(obj, hl_hash_utf8("maxrss"), rusage.ru_maxrss); - dyn_set_i64(obj, hl_hash_utf8("ixrss"), rusage.ru_ixrss); - dyn_set_i64(obj, hl_hash_utf8("idrss"), rusage.ru_idrss); - dyn_set_i64(obj, hl_hash_utf8("isrss"), rusage.ru_isrss); - dyn_set_i64(obj, hl_hash_utf8("minflt"), rusage.ru_minflt); - dyn_set_i64(obj, hl_hash_utf8("majflt"), rusage.ru_majflt); - dyn_set_i64(obj, hl_hash_utf8("nswap"), rusage.ru_nswap); - dyn_set_i64(obj, hl_hash_utf8("inblock"), rusage.ru_inblock); - dyn_set_i64(obj, hl_hash_utf8("oublock"), rusage.ru_oublock); - dyn_set_i64(obj, hl_hash_utf8("msgsnd"), rusage.ru_msgsnd); - dyn_set_i64(obj, hl_hash_utf8("msgrcv"), rusage.ru_msgrcv); - dyn_set_i64(obj, hl_hash_utf8("nsignals"), rusage.ru_nsignals); - dyn_set_i64(obj, hl_hash_utf8("nvcsw"), rusage.ru_nvcsw); - dyn_set_i64(obj, hl_hash_utf8("nivcsw"), rusage.ru_nivcsw); - return obj; -} -DEFINE_PRIM(_DYN, getrusage_wrap, _NO_ARG); - -HL_PRIM varray *HL_NAME(cpu_info_wrap)() { - uv_cpu_info_t *infos; - int count; - UV_CHECK_ERROR(uv_cpu_info(&infos, &count),,NULL); - int hash_user = hl_hash_utf8("user"); - int hash_nice = hl_hash_utf8("nice"); - int hash_sys = hl_hash_utf8("sys"); - int hash_idle = hl_hash_utf8("idle"); - int hash_irq = hl_hash_utf8("irq"); - int hash_model = hl_hash_utf8("model"); - int hash_speed = hl_hash_utf8("speed"); - int hash_cpuTimes = hl_hash_utf8("cpuTimes"); - varray *a = hl_alloc_array(&hlt_dyn, count); - for(int i = 0; i < count; i++) { - vdynamic *times = (vdynamic *)hl_alloc_dynobj(); - dyn_set_i64(times, hash_user, infos[i].cpu_times.user); - dyn_set_i64(times, hash_nice, infos[i].cpu_times.nice); - dyn_set_i64(times, hash_sys, infos[i].cpu_times.sys); - dyn_set_i64(times, hash_idle, infos[i].cpu_times.idle); - dyn_set_i64(times, hash_irq, infos[i].cpu_times.irq); - - vdynamic *info = (vdynamic *)hl_alloc_dynobj(); - hl_dyn_setp(info, hash_model, &hlt_bytes, hl_copy_bytes((vbyte *)infos[i].model, strlen(infos[i].model) + 1)); - hl_dyn_seti(info, hash_speed, &hlt_i32, infos[i].speed); - hl_dyn_setp(info, hash_cpuTimes, &hlt_dyn, times); - - hl_aptr(a,vdynamic *)[i] = info; - } - uv_free_cpu_info(infos, count); - return a; -} -DEFINE_PRIM(_ARR, cpu_info_wrap, _NO_ARG); - -HL_PRIM varray *HL_NAME(interface_addresses_wrap)() { - uv_interface_address_t *addresses; - int count; - UV_CHECK_ERROR(uv_interface_addresses(&addresses, &count),,NULL); - int hash_name = hl_hash_utf8("name"); - int hash_physAddr = hl_hash_utf8("physAddr"); - int hash_isInternal = hl_hash_utf8("isInternal"); - int hash_address = hl_hash_utf8("address"); - int hash_netmask = hl_hash_utf8("netmask"); - varray *a = hl_alloc_array(&hlt_dyn, count); - for(int i = 0; i < count; i++) { - vdynamic *info = (vdynamic *)hl_alloc_dynobj(); - - hl_dyn_setp(info, hash_name, &hlt_bytes, hl_copy_bytes((vbyte *)addresses[i].name, strlen(addresses[i].name))); - hl_dyn_setp(info, hash_physAddr, &hlt_bytes, hl_copy_bytes((vbyte *)addresses[i].phys_addr, 6)); - hl_dyn_seti(info, hash_isInternal, &hlt_bool, addresses[i].is_internal); - - uv_sockaddr_storage *addr = UV_ALLOC(uv_sockaddr_storage); - memcpy(addr, &addresses[i].address, sizeof(addresses[i].address)); - hl_dyn_setp(info, hash_address, &hlt_sockaddr, addr); - - uv_sockaddr_storage *mask = UV_ALLOC(uv_sockaddr_storage); - memcpy(mask, &addresses[i].netmask, sizeof(addresses[i].netmask)); - hl_dyn_setp(info, hash_netmask, &hlt_sockaddr, mask); - - hl_aptr(a,vdynamic *)[i] = info; - } - uv_free_interface_addresses(addresses, count); - return a; -} -DEFINE_PRIM(_ARR, interface_addresses_wrap, _NO_ARG); - -HL_PRIM varray *HL_NAME(loadavg_wrap)() { - double avg[3]; - uv_loadavg(avg); - varray *a = hl_alloc_array(&hlt_f64, 3); - hl_aptr(a,double)[0] = avg[0]; - hl_aptr(a,double)[1] = avg[1]; - hl_aptr(a,double)[2] = avg[2]; - return a; -} -DEFINE_PRIM(_ARR, loadavg_wrap, _NO_ARG); - -static vbyte *os_str( int (*fn)(char *buf, size_t *size) ) { - size_t size = 256; - char *buf = NULL; - int result = UV_ENOBUFS; - while (result == UV_ENOBUFS) { - if( buf ) - free(buf); - buf = malloc(size); - result = fn(buf,&size); - } - vbyte *path = hl_alloc_bytes(size); - memcpy(path,buf,size); - free(buf); - UV_CHECK_ERROR(result,,NULL); // free bytes? - return path; -} - -HL_PRIM vbyte *HL_NAME(os_homedir_wrap)() { - return os_str(uv_os_homedir); -} -DEFINE_PRIM(_BYTES, os_homedir_wrap, _NO_ARG); - -HL_PRIM vbyte *HL_NAME(os_tmpdir_wrap)() { - return os_str(uv_os_tmpdir); -} -DEFINE_PRIM(_BYTES, os_tmpdir_wrap, _NO_ARG); - -HL_PRIM vdynamic *HL_NAME(os_getpasswd_wrap)() { - uv_passwd_t p; - UV_CHECK_ERROR(uv_os_get_passwd(&p),,NULL); - vdynamic *obj = (vdynamic *)hl_alloc_dynobj(); - hl_dyn_setp(obj, hl_hash_utf8("username"), &hlt_bytes, hl_copy_bytes((vbyte *)p.username, strlen(p.username))); - hl_dyn_setp(obj, hl_hash_utf8("homedir"), &hlt_bytes, hl_copy_bytes((vbyte *)p.homedir, strlen(p.homedir))); - dyn_set_i64(obj, hl_hash_utf8("uid"), p.uid); - dyn_set_i64(obj, hl_hash_utf8("gid"), p.gid); - if( p.shell ) - hl_dyn_setp(obj, hl_hash_utf8("shell"), &hlt_bytes, hl_copy_bytes((vbyte *)p.shell, strlen(p.shell))); - uv_os_free_passwd(&p); - return obj; -} -DEFINE_PRIM(_DYN, os_getpasswd_wrap, _NO_ARG); - -HL_PRIM vbyte *HL_NAME(os_gethostname_wrap)() { - return os_str(uv_os_gethostname); -} -DEFINE_PRIM(_BYTES, os_gethostname_wrap, _NO_ARG); - -HL_PRIM int HL_NAME(os_getpriority_wrap)( int pid ) { - int priority; - UV_CHECK_ERROR(uv_os_getpriority(pid,&priority),,0); - return priority; -} -DEFINE_PRIM(_I32, os_getpriority_wrap, _I32); - -HL_PRIM void HL_NAME(os_setpriority_wrap)( int pid, int priority ) { - UV_CHECK_ERROR(uv_os_setpriority(pid,priority),,); -} -DEFINE_PRIM(_VOID, os_setpriority_wrap, _I32 _I32); - -HL_PRIM vdynamic *HL_NAME(os_uname_wrap)() { - uv_utsname_t u; - UV_CHECK_ERROR(uv_os_uname(&u),,NULL); - vdynamic *obj = (vdynamic *)hl_alloc_dynobj(); - hl_dyn_setp(obj, hl_hash_utf8("machine"), &hlt_bytes, hl_copy_bytes((vbyte *)u.machine, strlen(u.machine))); - hl_dyn_setp(obj, hl_hash_utf8("release"), &hlt_bytes, hl_copy_bytes((vbyte *)u.release, strlen(u.release))); - hl_dyn_setp(obj, hl_hash_utf8("sysname"), &hlt_bytes, hl_copy_bytes((vbyte *)u.sysname, strlen(u.sysname))); - hl_dyn_setp(obj, hl_hash_utf8("version"), &hlt_bytes, hl_copy_bytes((vbyte *)u.version, strlen(u.version))); - return obj; -} -DEFINE_PRIM(_DYN, os_uname_wrap, _NO_ARG); - -HL_PRIM vdynamic *HL_NAME(gettimeofday_wrap)() { - uv_timeval64_t t; - UV_CHECK_ERROR(uv_gettimeofday(&t),,NULL); - vdynamic *obj = (vdynamic *)hl_alloc_dynobj(); - dyn_set_i64(obj, hl_hash_utf8("sec"), t.tv_sec); - dyn_set_i64(obj, hl_hash_utf8("usec"), t.tv_usec); - return obj; -} -DEFINE_PRIM(_DYN, gettimeofday_wrap, _NO_ARG); - -static void on_random( uv_random_t* r, int status, void* buf, size_t buflen ) { - UV_GET_CLOSURE(c,r,0,"No callback in random request"); - hl_call1(void,c,int,errno_uv2hl(status)); -} - -HL_PRIM void HL_NAME(random_wrap)( uv_loop_t *loop, vbyte *buf, int length, int flags, vclosure *c ) { - UV_CHECK_NULL(loop,); - UV_CHECK_NULL(buf,); - UV_CHECK_NULL(c,); - UV_ALLOC_REQ(uv_random_t,r,c); - UV_CHECK_ERROR(uv_random(loop,r,buf,length,flags,on_random),free_req((uv_req_t *)r),); -} -DEFINE_PRIM(_VOID, random_wrap, _LOOP _BYTES _I32 _I32 _FUN(_VOID,_I32)); - -// Tty - -HL_PRIM uv_tty_t *HL_NAME(tty_init_wrap)( uv_loop_t *loop, int fd ) { - UV_CHECK_NULL(loop,NULL); - uv_tty_t *h = UV_ALLOC(uv_tty_t); - UV_CHECK_ERROR(uv_tty_init(loop,h,fd,0),free_handle((uv_handle_t *)h),NULL); - handle_init_hl_data((uv_handle_t *)h); - return h; -} -DEFINE_PRIM(_HANDLE, tty_init_wrap, _LOOP _I32); - -HL_PRIM void HL_NAME(tty_set_mode_wrap)( uv_tty_t *h, int mode ) { - UV_CHECK_NULL(h,); - uv_tty_mode_t uv_mode = UV_TTY_MODE_NORMAL; - switch( mode ) { - case 0: uv_mode = UV_TTY_MODE_NORMAL; break; - case 1: uv_mode = UV_TTY_MODE_RAW; break; - case 2: uv_mode = UV_TTY_MODE_IO; break; - } - UV_CHECK_ERROR(uv_tty_set_mode(h,uv_mode),,); -} -DEFINE_PRIM(_VOID, tty_set_mode_wrap, _HANDLE _I32); - -HL_PRIM void HL_NAME(tty_reset_mode_wrap)() { - UV_CHECK_ERROR(uv_tty_reset_mode(),,); -} -DEFINE_PRIM(_VOID, tty_reset_mode_wrap, _NO_ARG); - -HL_PRIM vdynamic *HL_NAME(tty_get_winsize_wrap)( uv_tty_t *h ) { - UV_CHECK_NULL(h,NULL); - int width; - int height; - UV_CHECK_ERROR(uv_tty_get_winsize(h,&width,&height),,NULL); - vdynamic *obj = (vdynamic *)hl_alloc_dynobj(); - hl_dyn_seti(obj, hl_hash_utf8("width"), &hlt_i32, width); - hl_dyn_seti(obj, hl_hash_utf8("height"), &hlt_i32, height); - return obj; -} -DEFINE_PRIM(_DYN, tty_get_winsize_wrap, _HANDLE); - -HL_PRIM void HL_NAME(tty_set_vterm_state_wrap)( int state ) { - uv_tty_vtermstate_t uv_state = UV_TTY_SUPPORTED; - switch( state ) { - case 0: uv_state = UV_TTY_SUPPORTED; break; - case 1: uv_state = UV_TTY_UNSUPPORTED; break; - } - uv_tty_set_vterm_state(uv_state); -} -DEFINE_PRIM(_VOID, tty_set_vterm_state_wrap, _I32); - -HL_PRIM int HL_NAME(tty_get_vterm_state_wrap)() { - uv_tty_vtermstate_t state; - UV_CHECK_ERROR(uv_tty_get_vterm_state(&state),,UV_TTY_SUPPORTED); - switch( state ) { - case UV_TTY_SUPPORTED: return 0; - case UV_TTY_UNSUPPORTED: - default: return 1; - } -} -DEFINE_PRIM(_I32, tty_get_vterm_state_wrap, _NO_ARG); +#include "uv_generated.c" \ No newline at end of file diff --git a/other/uvgenerator/UV.hx.header b/other/uvgenerator/UV.hx.header index ecc4b46db..624e35751 100644 --- a/other/uvgenerator/UV.hx.header +++ b/other/uvgenerator/UV.hx.header @@ -157,7 +157,8 @@ extern class UV { static public function alloc_sockaddr_storage():CSockaddrStorageStar; static public function sockaddr_storage_size():Int; static public function sockaddr_storage_ss_family(addr:CSockaddrStorageStar):Int; - static public function sockaddr_storage_port(addr:CSockaddrStorageStar):Null; + static public function sockaddr_in_sin_port(addr:CSockaddrInStar):Int; + static public function sockaddr_in6_sin6_port(addr:CSockaddrIn6Star):Int; static public function free_sockaddr_storage(addr:CSockaddrStorageStar):Void; static public function sockaddr_of_storage(addr:CSockaddrStorageStar):CSockaddrStar; static public function sockaddr_to_storage(addr:CSockaddrStar):CSockaddrStorageStar; From 108181c0b8a0030441074da8a300da7a3a0b72b7 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 3 Sep 2021 16:56:44 +0300 Subject: [PATCH 101/117] improve samples --- other/uvsample/AllSample.hx | 1 - other/uvsample/FileSample.hx | 4 ++-- other/uvsample/FsPollSample.hx | 2 +- other/uvsample/Log.hx | 12 ++++++++++-- other/uvsample/PipeSample.hx | 19 +++++++++---------- other/uvsample/TcpSample.hx | 4 ++-- other/uvsample/UVSample.hx | 3 +-- 7 files changed, 25 insertions(+), 20 deletions(-) diff --git a/other/uvsample/AllSample.hx b/other/uvsample/AllSample.hx index 46af3be73..97497a35d 100644 --- a/other/uvsample/AllSample.hx +++ b/other/uvsample/AllSample.hx @@ -17,7 +17,6 @@ class AllSample extends UVSample { new TtySample().run(); new SignalSample().run(); new VersionSample().run(); - // This one requires manual execution new PipeSample().run(); } } \ No newline at end of file diff --git a/other/uvsample/FileSample.hx b/other/uvsample/FileSample.hx index 7fede007e..d81487e1f 100644 --- a/other/uvsample/FileSample.hx +++ b/other/uvsample/FileSample.hx @@ -9,10 +9,10 @@ import sys.thread.Thread; import hl.uv.File; abstract Actions(Array) from Array { - public function next() { + public function next(?p:PosInfos) { var fn = this.shift(); if(fn != null) { - print('-----------'); + Log.print('-----------', p); fn(this); } } diff --git a/other/uvsample/FsPollSample.hx b/other/uvsample/FsPollSample.hx index 822011013..7e469aa3c 100644 --- a/other/uvsample/FsPollSample.hx +++ b/other/uvsample/FsPollSample.hx @@ -12,7 +12,7 @@ class FsPollSample extends UVSample { var poll = FsPoll.init(loop); var path = Misc.tmpDir() + '/test-file'; File.saveContent(path, 'Hello, world'); - poll.start(path, 1000, (e, previous, current) -> switch e { + poll.start(path, 100, (e, previous, current) -> switch e { case UV_NOERR: print('FS Poll at $path:'); print('\tprev: $previous'); diff --git a/other/uvsample/Log.hx b/other/uvsample/Log.hx index c2926c5a5..eaf2209d7 100644 --- a/other/uvsample/Log.hx +++ b/other/uvsample/Log.hx @@ -1,7 +1,15 @@ +import haxe.PosInfos; + class Log { static final T0 = haxe.Timer.stamp(); - public static function print( msg : String ) { - Sys.println("["+Std.int((haxe.Timer.stamp() - T0) * 100)+"] "+msg); + public static function print(msg:String, ?pos:PosInfos) { + var slashPos = pos.fileName.lastIndexOf('/'); + if(slashPos < 0) + slashPos = pos.fileName.lastIndexOf('\\'); + var namePos = slashPos < 0 ? 0 : slashPos + 1; + var sampleName = pos.fileName.substr(namePos); + var stamp = Std.int((haxe.Timer.stamp() - T0) * 100); + Sys.println('[$stamp] $sampleName:${pos.lineNumber}: $msg'); } } \ No newline at end of file diff --git a/other/uvsample/PipeSample.hx b/other/uvsample/PipeSample.hx index b16a2d653..6900fb7ff 100644 --- a/other/uvsample/PipeSample.hx +++ b/other/uvsample/PipeSample.hx @@ -15,14 +15,13 @@ class PipeSample extends UVSample { public function run() { NAME = Misc.tmpDir() + '/' + NAME; - #if CLIENT + print('Running PipeSample server...'); + print('waiting for connections'); + server(); + haxe.Timer.delay(() -> { print('Running PipeSample client...'); client(); - #else - print('Running PipeSample server...'); - print('waiting for connections'); - server(); - #end + }, 200); } static function handle(success:()->Void, ?p:PosInfos):(e:UVError)->Void { @@ -32,11 +31,11 @@ class PipeSample extends UVSample { } } - static function server() { + function server() { if(FileSystem.exists(NAME)) FileSystem.deleteFile(NAME); function print(msg:String, ?p:PosInfos) - print('SERVER: $msg', p); + this.print('SERVER: $msg', p); var loop = Thread.current().events; var server = Pipe.init(loop); server.bind(NAME); @@ -68,9 +67,9 @@ class PipeSample extends UVSample { })); } - static function client() { + function client() { function print(msg:String, ?p:PosInfos) - print('CLIENT: $msg', p); + this.print('CLIENT: $msg', p); var loop = Thread.current().events; var client = Pipe.init(loop, true); client.connect(NAME, handle(() -> { diff --git a/other/uvsample/TcpSample.hx b/other/uvsample/TcpSample.hx index b7c5bb5ee..230b8a25c 100644 --- a/other/uvsample/TcpSample.hx +++ b/other/uvsample/TcpSample.hx @@ -34,7 +34,7 @@ class TcpSample extends UVSample { function server() { function print(msg:String) { - Log.print('SERVER: $msg'); + this.print('SERVER: $msg'); } var loop = Thread.current().events; var server = Tcp.init(loop, INET); @@ -62,7 +62,7 @@ class TcpSample extends UVSample { function client() { function print(msg:String) { - Log.print('CLIENT: $msg'); + this.print('CLIENT: $msg'); } var loop = Thread.current().events; var client = Tcp.init(loop, INET); diff --git a/other/uvsample/UVSample.hx b/other/uvsample/UVSample.hx index 8033a1c65..352d35536 100644 --- a/other/uvsample/UVSample.hx +++ b/other/uvsample/UVSample.hx @@ -10,8 +10,7 @@ abstract class UVSample { abstract public function run():Void; public function print(msg:String, ?pos:PosInfos) { - var sampleName = pos.fileName.substr(0, pos.fileName.length - '.hx'.length); - Log.print('${pos.fileName}:${pos.lineNumber}: $msg'); + Log.print(msg, pos); } macro static public function pickSample() { From 41a5635a87aa417f39f35e5e6c1829444dcf9cd9 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 3 Sep 2021 17:54:42 +0300 Subject: [PATCH 102/117] fixed gc handling of requests' and handles' data --- libs/uv/uv.c | 6 +++--- other/uvsample/SignalSample.hx | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index f4bcfc8f0..3dcbcf472 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -145,9 +145,9 @@ typedef struct sockaddr_storage uv_sockaddr_storage; if( h->data != new_data ) { \ if( h->data ) \ hl_remove_root(h->data); \ - if( new_data ) \ - hl_add_root(new_data); \ h->data = new_data; \ + if( h->data ) \ + hl_add_root(&h->data); \ } // Errors @@ -495,7 +495,7 @@ static void on_uv_alloc_cb( uv_handle_t* h, size_t size, uv_buf_t *buf ) { vclosure *c = DATA(uv_handle_data_with_alloc_t *, h)->onAlloc; hl_call2(void, c, uv_buf_t *, buf, int, (int)size); if( buf->base ) - hl_add_root(buf->base); + hl_add_root(&buf->base); } // Request diff --git a/other/uvsample/SignalSample.hx b/other/uvsample/SignalSample.hx index d02af1b83..827b0586a 100644 --- a/other/uvsample/SignalSample.hx +++ b/other/uvsample/SignalSample.hx @@ -1,3 +1,5 @@ +import hl.uv.Process; +import hl.uv.Misc; import hl.uv.Signal; import sys.thread.Thread; @@ -11,5 +13,7 @@ class SignalSample extends UVSample { signal.stop(); signal.close(); }); + var selfPid = Misc.getPid(); + Process.killPid(selfPid, SIGINT); } } \ No newline at end of file From 003365561be63bbadcfe9ab22618911c2475c953 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 3 Sep 2021 18:05:48 +0300 Subject: [PATCH 103/117] [ci][cmake] run all uv samples --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 88a3109ad..18ae735c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -247,6 +247,7 @@ if(BUILD_TESTING) add_custom_command(OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test/uvsample/uvsample.c COMMAND ${HAXE_COMPILER} + -D SAMPLE=All -hl ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test/uvsample/uvsample.c -cp ${CMAKE_SOURCE_DIR}/other/uvsample -main UVSample ) From 9e917352781215ddc2c13bad7cf167b5f7f840fb Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 3 Sep 2021 19:09:45 +0300 Subject: [PATCH 104/117] [ci] uname --- other/azure-pipelines/build-linux.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/other/azure-pipelines/build-linux.yml b/other/azure-pipelines/build-linux.yml index 9f930e200..fba07d0c3 100644 --- a/other/azure-pipelines/build-linux.yml +++ b/other/azure-pipelines/build-linux.yml @@ -86,6 +86,7 @@ jobs: ./configure make sudo make install + uname -a displayName: Install dependencies - template: install-haxe-snapshot.yml parameters: From dca431e089c7c6b03d6ea7bc06c3353f715d4265 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 3 Sep 2021 19:20:57 +0300 Subject: [PATCH 105/117] [ci] properly configure libuv for i386 --- other/azure-pipelines/build-linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/other/azure-pipelines/build-linux.yml b/other/azure-pipelines/build-linux.yml index fba07d0c3..52b3c478b 100644 --- a/other/azure-pipelines/build-linux.yml +++ b/other/azure-pipelines/build-linux.yml @@ -83,7 +83,7 @@ jobs: tar -xf libuv-v1.42.0.tar.gz cd libuv-v1.42.0 sh autogen.sh - ./configure + ./configure --host=i686-linux-gnu "CFLAGS=-m32" "CXXFLAGS=-m32" "LDFLAGS=-m32" make sudo make install uname -a From 58bd2198b3a985f35941ca7cfbe83cc2526a939d Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 3 Sep 2021 19:33:45 +0300 Subject: [PATCH 106/117] [ci][cmake] disable building UVSample until HaxeFoundation/haxe#10342 is merged --- CMakeLists.txt | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 18ae735c8..7961f16d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -245,26 +245,28 @@ if(BUILD_TESTING) ##################### # uvsample.c - add_custom_command(OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test/uvsample/uvsample.c - COMMAND ${HAXE_COMPILER} - -D SAMPLE=All - -hl ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test/uvsample/uvsample.c - -cp ${CMAKE_SOURCE_DIR}/other/uvsample -main UVSample - ) - add_executable(uvsample - ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test/uvsample/uvsample.c - ) - set_target_properties(uvsample - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test/uvsample - ) - target_include_directories(uvsample - PRIVATE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test/uvsample - ) - target_link_libraries(uvsample - libhl - uv.hdll - ) + # TODO: uncomment when https://github.com/HaxeFoundation/haxe/pull/10342 is merged + + #add_custom_command(OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test/uvsample/uvsample.c + # COMMAND ${HAXE_COMPILER} + # -D SAMPLE=All + # -hl ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test/uvsample/uvsample.c + # -cp ${CMAKE_SOURCE_DIR}/other/uvsample -main UVSample + #) + #add_executable(uvsample + # ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test/uvsample/uvsample.c + #) + #set_target_properties(uvsample + # PROPERTIES + # RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test/uvsample + #) + #target_include_directories(uvsample + # PRIVATE ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test/uvsample + #) + #target_link_libraries(uvsample + # libhl + # uv.hdll + #) ##################### # Tests From 5cee57851cde15fa1b252a808ff464d339624b82 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 3 Sep 2021 19:49:31 +0300 Subject: [PATCH 107/117] [ci][cmake] disable building UVSample until HaxeFoundation/haxe#10342 is merged --- CMakeLists.txt | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7961f16d5..b4ecf573d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -189,14 +189,17 @@ if(BUILD_TESTING) ##################### # uvsample.hl - add_custom_command(OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test/uvsample.hl - COMMAND ${HAXE_COMPILER} - -hl ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test/uvsample.hl - -cp ${CMAKE_SOURCE_DIR}/other/uvsample -main UVSample - ) - add_custom_target(uvsample.hl ALL - DEPENDS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test/uvsample.hl - ) + # TODO: uncomment when https://github.com/HaxeFoundation/haxe/pull/10342 is merged + + #add_custom_command(OUTPUT ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test/uvsample.hl + # COMMAND ${HAXE_COMPILER} + # -D SAMPLE=All + # -hl ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test/uvsample.hl + # -cp ${CMAKE_SOURCE_DIR}/other/uvsample -main UVSample + #) + #add_custom_target(uvsample.hl ALL + # DEPENDS ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test/uvsample.hl + #) ##################### # hello.c From 3513cc920c00e69ccb885ec819074b8373c4a8bc Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 3 Sep 2021 20:57:20 +0300 Subject: [PATCH 108/117] [ci] tests clenaup --- CMakeLists.txt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b4ecf573d..e3f22f848 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -280,18 +280,20 @@ if(BUILD_TESTING) add_test(NAME threads.hl COMMAND hl ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test/threads.hl ) - add_test(NAME uvsample.hl - COMMAND hl ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test/uvsample.hl - ) + # TODO: uncomment when https://github.com/HaxeFoundation/haxe/pull/10342 is merged + #add_test(NAME uvsample.hl + # COMMAND hl ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/test/uvsample.hl + #) add_test(NAME hello COMMAND hello ) add_test(NAME threads COMMAND threads ) - add_test(NAME uvsample - COMMAND uvsample - ) + # TODO: uncomment when https://github.com/HaxeFoundation/haxe/pull/10342 is merged + #add_test(NAME uvsample + # COMMAND uvsample + #) add_test(NAME version COMMAND hl --version ) From 687d8bfb6bc0c8f754fdf7e8d0699a883fef3a96 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 3 Sep 2021 21:04:02 +0300 Subject: [PATCH 109/117] [ci] list all libuv sources for in cmake for win --- libs/uv/CMakeLists.txt | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/libs/uv/CMakeLists.txt b/libs/uv/CMakeLists.txt index e7aa2a1f3..47217624d 100644 --- a/libs/uv/CMakeLists.txt +++ b/libs/uv/CMakeLists.txt @@ -6,28 +6,45 @@ if(WIN32) target_sources(uv.hdll PRIVATE ${INCLUDES_BASE_DIR}/libuv/src/fs-poll.c + ${INCLUDES_BASE_DIR}/libuv/src/heap-inl.h + ${INCLUDES_BASE_DIR}/libuv/src/idna.c + ${INCLUDES_BASE_DIR}/libuv/src/idna.h ${INCLUDES_BASE_DIR}/libuv/src/inet.c + ${INCLUDES_BASE_DIR}/libuv/src/queue.h + ${INCLUDES_BASE_DIR}/libuv/src/random.c + ${INCLUDES_BASE_DIR}/libuv/src/strscpy.c + ${INCLUDES_BASE_DIR}/libuv/src/strscpy.h ${INCLUDES_BASE_DIR}/libuv/src/threadpool.c + ${INCLUDES_BASE_DIR}/libuv/src/timer.c ${INCLUDES_BASE_DIR}/libuv/src/uv-common.c + ${INCLUDES_BASE_DIR}/libuv/src/uv-common.h + ${INCLUDES_BASE_DIR}/libuv/src/uv-data-getter-setters.c ${INCLUDES_BASE_DIR}/libuv/src/version.c ${INCLUDES_BASE_DIR}/libuv/src/win/async.c + ${INCLUDES_BASE_DIR}/libuv/src/win/atomicops-inl.h ${INCLUDES_BASE_DIR}/libuv/src/win/core.c + ${INCLUDES_BASE_DIR}/libuv/src/win/detect-wakeup.c ${INCLUDES_BASE_DIR}/libuv/src/win/dl.c ${INCLUDES_BASE_DIR}/libuv/src/win/error.c - ${INCLUDES_BASE_DIR}/libuv/src/win/fs-event.c ${INCLUDES_BASE_DIR}/libuv/src/win/fs.c + ${INCLUDES_BASE_DIR}/libuv/src/win/fs-event.c + ${INCLUDES_BASE_DIR}/libuv/src/win/fs-fd-hash-inl.h ${INCLUDES_BASE_DIR}/libuv/src/win/getaddrinfo.c ${INCLUDES_BASE_DIR}/libuv/src/win/getnameinfo.c ${INCLUDES_BASE_DIR}/libuv/src/win/handle.c + ${INCLUDES_BASE_DIR}/libuv/src/win/handle-inl.h + ${INCLUDES_BASE_DIR}/libuv/src/win/internal.h ${INCLUDES_BASE_DIR}/libuv/src/win/loop-watcher.c ${INCLUDES_BASE_DIR}/libuv/src/win/pipe.c ${INCLUDES_BASE_DIR}/libuv/src/win/poll.c - ${INCLUDES_BASE_DIR}/libuv/src/win/process-stdio.c ${INCLUDES_BASE_DIR}/libuv/src/win/process.c + ${INCLUDES_BASE_DIR}/libuv/src/win/process-stdio.c ${INCLUDES_BASE_DIR}/libuv/src/win/req.c + ${INCLUDES_BASE_DIR}/libuv/src/win/req-inl.h ${INCLUDES_BASE_DIR}/libuv/src/win/signal.c ${INCLUDES_BASE_DIR}/libuv/src/win/snprintf.c ${INCLUDES_BASE_DIR}/libuv/src/win/stream.c + ${INCLUDES_BASE_DIR}/libuv/src/win/stream-inl.h ${INCLUDES_BASE_DIR}/libuv/src/win/tcp.c ${INCLUDES_BASE_DIR}/libuv/src/win/thread.c ${INCLUDES_BASE_DIR}/libuv/src/win/timer.c @@ -35,7 +52,9 @@ if(WIN32) ${INCLUDES_BASE_DIR}/libuv/src/win/udp.c ${INCLUDES_BASE_DIR}/libuv/src/win/util.c ${INCLUDES_BASE_DIR}/libuv/src/win/winapi.c + ${INCLUDES_BASE_DIR}/libuv/src/win/winapi.h ${INCLUDES_BASE_DIR}/libuv/src/win/winsock.c + ${INCLUDES_BASE_DIR}/libuv/src/win/winsock.h ) else() find_package(LibUV) From 3cadfef10f3b3f18c1c0a6daa8f1b3d15a8af461 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 3 Sep 2021 22:08:55 +0300 Subject: [PATCH 110/117] [ci] removed .h files from cmake --- libs/uv/CMakeLists.txt | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/libs/uv/CMakeLists.txt b/libs/uv/CMakeLists.txt index 47217624d..5c3d28957 100644 --- a/libs/uv/CMakeLists.txt +++ b/libs/uv/CMakeLists.txt @@ -6,45 +6,34 @@ if(WIN32) target_sources(uv.hdll PRIVATE ${INCLUDES_BASE_DIR}/libuv/src/fs-poll.c - ${INCLUDES_BASE_DIR}/libuv/src/heap-inl.h ${INCLUDES_BASE_DIR}/libuv/src/idna.c - ${INCLUDES_BASE_DIR}/libuv/src/idna.h ${INCLUDES_BASE_DIR}/libuv/src/inet.c - ${INCLUDES_BASE_DIR}/libuv/src/queue.h ${INCLUDES_BASE_DIR}/libuv/src/random.c ${INCLUDES_BASE_DIR}/libuv/src/strscpy.c - ${INCLUDES_BASE_DIR}/libuv/src/strscpy.h ${INCLUDES_BASE_DIR}/libuv/src/threadpool.c ${INCLUDES_BASE_DIR}/libuv/src/timer.c ${INCLUDES_BASE_DIR}/libuv/src/uv-common.c - ${INCLUDES_BASE_DIR}/libuv/src/uv-common.h ${INCLUDES_BASE_DIR}/libuv/src/uv-data-getter-setters.c ${INCLUDES_BASE_DIR}/libuv/src/version.c ${INCLUDES_BASE_DIR}/libuv/src/win/async.c - ${INCLUDES_BASE_DIR}/libuv/src/win/atomicops-inl.h ${INCLUDES_BASE_DIR}/libuv/src/win/core.c ${INCLUDES_BASE_DIR}/libuv/src/win/detect-wakeup.c ${INCLUDES_BASE_DIR}/libuv/src/win/dl.c ${INCLUDES_BASE_DIR}/libuv/src/win/error.c ${INCLUDES_BASE_DIR}/libuv/src/win/fs.c ${INCLUDES_BASE_DIR}/libuv/src/win/fs-event.c - ${INCLUDES_BASE_DIR}/libuv/src/win/fs-fd-hash-inl.h ${INCLUDES_BASE_DIR}/libuv/src/win/getaddrinfo.c ${INCLUDES_BASE_DIR}/libuv/src/win/getnameinfo.c ${INCLUDES_BASE_DIR}/libuv/src/win/handle.c - ${INCLUDES_BASE_DIR}/libuv/src/win/handle-inl.h - ${INCLUDES_BASE_DIR}/libuv/src/win/internal.h ${INCLUDES_BASE_DIR}/libuv/src/win/loop-watcher.c ${INCLUDES_BASE_DIR}/libuv/src/win/pipe.c ${INCLUDES_BASE_DIR}/libuv/src/win/poll.c ${INCLUDES_BASE_DIR}/libuv/src/win/process.c ${INCLUDES_BASE_DIR}/libuv/src/win/process-stdio.c ${INCLUDES_BASE_DIR}/libuv/src/win/req.c - ${INCLUDES_BASE_DIR}/libuv/src/win/req-inl.h ${INCLUDES_BASE_DIR}/libuv/src/win/signal.c ${INCLUDES_BASE_DIR}/libuv/src/win/snprintf.c ${INCLUDES_BASE_DIR}/libuv/src/win/stream.c - ${INCLUDES_BASE_DIR}/libuv/src/win/stream-inl.h ${INCLUDES_BASE_DIR}/libuv/src/win/tcp.c ${INCLUDES_BASE_DIR}/libuv/src/win/thread.c ${INCLUDES_BASE_DIR}/libuv/src/win/timer.c @@ -52,9 +41,7 @@ if(WIN32) ${INCLUDES_BASE_DIR}/libuv/src/win/udp.c ${INCLUDES_BASE_DIR}/libuv/src/win/util.c ${INCLUDES_BASE_DIR}/libuv/src/win/winapi.c - ${INCLUDES_BASE_DIR}/libuv/src/win/winapi.h ${INCLUDES_BASE_DIR}/libuv/src/win/winsock.c - ${INCLUDES_BASE_DIR}/libuv/src/win/winsock.h ) else() find_package(LibUV) From db3e8479f9209be0dfc1e91682c18b6dfe251d67 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Fri, 3 Sep 2021 22:46:39 +0300 Subject: [PATCH 111/117] [ci] added missing entries in uv.vcxproj --- libs/uv/uv.vcxproj | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/libs/uv/uv.vcxproj b/libs/uv/uv.vcxproj index 48be5f27d..0dd0d1dcd 100644 --- a/libs/uv/uv.vcxproj +++ b/libs/uv/uv.vcxproj @@ -247,24 +247,30 @@ + + + + + + - + - + @@ -281,9 +287,12 @@ + + + From 0131d1a26899d96546c45481e5897d4d3a0921a1 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Sat, 4 Sep 2021 14:50:40 +0300 Subject: [PATCH 112/117] [ci] and uv.vcxproj.filters --- libs/uv/uv.vcxproj.filters | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/libs/uv/uv.vcxproj.filters b/libs/uv/uv.vcxproj.filters index c681ebd02..293b3109c 100644 --- a/libs/uv/uv.vcxproj.filters +++ b/libs/uv/uv.vcxproj.filters @@ -5,15 +5,30 @@ libuv + + libuv + libuv + + libuv + + + libuv + libuv + + libuv + libuv + + libuv + libuv @@ -23,6 +38,9 @@ libuv + + libuv + libuv @@ -105,15 +123,24 @@ libuv + + libuv + libuv + + libuv + libuv libuv + + libuv + libuv From 31c853ec1a917a80ae52a64ffca29f3ce826cf92 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Sat, 4 Sep 2021 15:06:48 +0300 Subject: [PATCH 113/117] [win] removed defines HANDLE_DATA_FIELDS and REQ_DATA_FIELDS --- libs/uv/uv.c | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/libs/uv/uv.c b/libs/uv/uv.c index 3dcbcf472..01879a1b0 100644 --- a/libs/uv/uv.c +++ b/libs/uv/uv.c @@ -455,26 +455,24 @@ DEFINE_PRIM_UV_FIELD(_U64, int64, _BUF_ARRAY, buf, len); // Handle -#define HANDLE_DATA_FIELDS \ - hl_type *t; \ - uv_handle_t *_h; \ - vclosure *onClose; - -#define HANDLE_DATA_WITH_ALLOC_FIELDS \ - HANDLE_DATA_FIELDS; \ - vclosure *onAlloc; - typedef struct { - HANDLE_DATA_FIELDS; + hl_type *t; + uv_handle_t *_h; + vclosure *onClose; } uv_handle_data_t; typedef struct { - HANDLE_DATA_FIELDS; + hl_type *t; + uv_handle_t *_h; + vclosure *onClose; vclosure *callback; } uv_handle_cb_data_t; typedef struct { - HANDLE_DATA_WITH_ALLOC_FIELDS; + hl_type *t; + uv_handle_t *_h; + vclosure *onClose; + vclosure *onAlloc; } uv_handle_data_with_alloc_t; #define _HANDLE_DATA _OBJ(_HANDLE _FUN(_VOID,_NO_ARG)) @@ -500,16 +498,14 @@ static void on_uv_alloc_cb( uv_handle_t* h, size_t size, uv_buf_t *buf ) { // Request -#define REQ_DATA_FIELDS \ - hl_type *t; \ - uv_req_t *_r; - typedef struct { - REQ_DATA_FIELDS + hl_type *t; + uv_req_t *_r; } uv_req_data_t; typedef struct { - REQ_DATA_FIELDS + hl_type *t; + uv_req_t *_r; vclosure *callback; } uv_req_cb_data_t; @@ -753,7 +749,10 @@ static void on_uv_getnameinfo_cb( uv_getnameinfo_t *r, int status, const char *h // Stream typedef struct { - HANDLE_DATA_WITH_ALLOC_FIELDS; + hl_type *t; + uv_handle_t *_h; + vclosure *onClose; + vclosure *onAlloc; vclosure *onConnection; vclosure *onRead; } uv_stream_data_t; @@ -800,7 +799,10 @@ DEFINE_PRIM_ALLOC(_PIPE, pipe); // UDP typedef struct { - HANDLE_DATA_WITH_ALLOC_FIELDS; + hl_type *t; + uv_handle_t *_h; + vclosure *onClose; + vclosure *onAlloc; vclosure *onRecv; } uv_udp_data_t; @@ -856,7 +858,9 @@ DEFINE_PRIM(_I32, translate_sys_signal, _I32); // Process typedef struct { - HANDLE_DATA_FIELDS; + hl_type *t; + uv_handle_t *_h; + vclosure *onClose; vclosure *onExit; } uv_process_data_t; From 5a86a74090c21a2af16c99a89c923b27636fa71a Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Sat, 4 Sep 2021 15:34:00 +0300 Subject: [PATCH 114/117] removed old files from libuv --- include/libuv/include/tree.h | 768 -------------------------- include/libuv/include/uv-errno.h | 418 -------------- include/libuv/include/uv-threadpool.h | 37 -- include/libuv/include/uv-version.h | 43 -- include/libuv/include/uv-win.h | 649 ---------------------- include/libuv/src/win/req.c | 25 - include/libuv/src/win/timer.c | 195 ------- libs/uv/CMakeLists.txt | 2 - libs/uv/uv.vcxproj | 2 - libs/uv/uv.vcxproj.filters | 6 - 10 files changed, 2145 deletions(-) delete mode 100644 include/libuv/include/tree.h delete mode 100644 include/libuv/include/uv-errno.h delete mode 100644 include/libuv/include/uv-threadpool.h delete mode 100644 include/libuv/include/uv-version.h delete mode 100644 include/libuv/include/uv-win.h delete mode 100644 include/libuv/src/win/req.c delete mode 100644 include/libuv/src/win/timer.c diff --git a/include/libuv/include/tree.h b/include/libuv/include/tree.h deleted file mode 100644 index f936416e3..000000000 --- a/include/libuv/include/tree.h +++ /dev/null @@ -1,768 +0,0 @@ -/*- - * Copyright 2002 Niels Provos - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef UV_TREE_H_ -#define UV_TREE_H_ - -#ifndef UV__UNUSED -# if __GNUC__ -# define UV__UNUSED __attribute__((unused)) -# else -# define UV__UNUSED -# endif -#endif - -/* - * This file defines data structures for different types of trees: - * splay trees and red-black trees. - * - * A splay tree is a self-organizing data structure. Every operation - * on the tree causes a splay to happen. The splay moves the requested - * node to the root of the tree and partly rebalances it. - * - * This has the benefit that request locality causes faster lookups as - * the requested nodes move to the top of the tree. On the other hand, - * every lookup causes memory writes. - * - * The Balance Theorem bounds the total access time for m operations - * and n inserts on an initially empty tree as O((m + n)lg n). The - * amortized cost for a sequence of m accesses to a splay tree is O(lg n); - * - * A red-black tree is a binary search tree with the node color as an - * extra attribute. It fulfills a set of conditions: - * - every search path from the root to a leaf consists of the - * same number of black nodes, - * - each red node (except for the root) has a black parent, - * - each leaf node is black. - * - * Every operation on a red-black tree is bounded as O(lg n). - * The maximum height of a red-black tree is 2lg (n+1). - */ - -#define SPLAY_HEAD(name, type) \ -struct name { \ - struct type *sph_root; /* root of the tree */ \ -} - -#define SPLAY_INITIALIZER(root) \ - { NULL } - -#define SPLAY_INIT(root) do { \ - (root)->sph_root = NULL; \ -} while (/*CONSTCOND*/ 0) - -#define SPLAY_ENTRY(type) \ -struct { \ - struct type *spe_left; /* left element */ \ - struct type *spe_right; /* right element */ \ -} - -#define SPLAY_LEFT(elm, field) (elm)->field.spe_left -#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right -#define SPLAY_ROOT(head) (head)->sph_root -#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) - -/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ -#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ - SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ - SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ - (head)->sph_root = tmp; \ -} while (/*CONSTCOND*/ 0) - -#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ - SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ - SPLAY_LEFT(tmp, field) = (head)->sph_root; \ - (head)->sph_root = tmp; \ -} while (/*CONSTCOND*/ 0) - -#define SPLAY_LINKLEFT(head, tmp, field) do { \ - SPLAY_LEFT(tmp, field) = (head)->sph_root; \ - tmp = (head)->sph_root; \ - (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ -} while (/*CONSTCOND*/ 0) - -#define SPLAY_LINKRIGHT(head, tmp, field) do { \ - SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ - tmp = (head)->sph_root; \ - (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ -} while (/*CONSTCOND*/ 0) - -#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ - SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ - SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field); \ - SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ - SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ -} while (/*CONSTCOND*/ 0) - -/* Generates prototypes and inline functions */ - -#define SPLAY_PROTOTYPE(name, type, field, cmp) \ -void name##_SPLAY(struct name *, struct type *); \ -void name##_SPLAY_MINMAX(struct name *, int); \ -struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ -struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ - \ -/* Finds the node with the same key as elm */ \ -static __inline struct type * \ -name##_SPLAY_FIND(struct name *head, struct type *elm) \ -{ \ - if (SPLAY_EMPTY(head)) \ - return(NULL); \ - name##_SPLAY(head, elm); \ - if ((cmp)(elm, (head)->sph_root) == 0) \ - return (head->sph_root); \ - return (NULL); \ -} \ - \ -static __inline struct type * \ -name##_SPLAY_NEXT(struct name *head, struct type *elm) \ -{ \ - name##_SPLAY(head, elm); \ - if (SPLAY_RIGHT(elm, field) != NULL) { \ - elm = SPLAY_RIGHT(elm, field); \ - while (SPLAY_LEFT(elm, field) != NULL) { \ - elm = SPLAY_LEFT(elm, field); \ - } \ - } else \ - elm = NULL; \ - return (elm); \ -} \ - \ -static __inline struct type * \ -name##_SPLAY_MIN_MAX(struct name *head, int val) \ -{ \ - name##_SPLAY_MINMAX(head, val); \ - return (SPLAY_ROOT(head)); \ -} - -/* Main splay operation. - * Moves node close to the key of elm to top - */ -#define SPLAY_GENERATE(name, type, field, cmp) \ -struct type * \ -name##_SPLAY_INSERT(struct name *head, struct type *elm) \ -{ \ - if (SPLAY_EMPTY(head)) { \ - SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ - } else { \ - int __comp; \ - name##_SPLAY(head, elm); \ - __comp = (cmp)(elm, (head)->sph_root); \ - if(__comp < 0) { \ - SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field); \ - SPLAY_RIGHT(elm, field) = (head)->sph_root; \ - SPLAY_LEFT((head)->sph_root, field) = NULL; \ - } else if (__comp > 0) { \ - SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field); \ - SPLAY_LEFT(elm, field) = (head)->sph_root; \ - SPLAY_RIGHT((head)->sph_root, field) = NULL; \ - } else \ - return ((head)->sph_root); \ - } \ - (head)->sph_root = (elm); \ - return (NULL); \ -} \ - \ -struct type * \ -name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ -{ \ - struct type *__tmp; \ - if (SPLAY_EMPTY(head)) \ - return (NULL); \ - name##_SPLAY(head, elm); \ - if ((cmp)(elm, (head)->sph_root) == 0) { \ - if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ - (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ - } else { \ - __tmp = SPLAY_RIGHT((head)->sph_root, field); \ - (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ - name##_SPLAY(head, elm); \ - SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ - } \ - return (elm); \ - } \ - return (NULL); \ -} \ - \ -void \ -name##_SPLAY(struct name *head, struct type *elm) \ -{ \ - struct type __node, *__left, *__right, *__tmp; \ - int __comp; \ - \ - SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL; \ - __left = __right = &__node; \ - \ - while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \ - if (__comp < 0) { \ - __tmp = SPLAY_LEFT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if ((cmp)(elm, __tmp) < 0){ \ - SPLAY_ROTATE_RIGHT(head, __tmp, field); \ - if (SPLAY_LEFT((head)->sph_root, field) == NULL) \ - break; \ - } \ - SPLAY_LINKLEFT(head, __right, field); \ - } else if (__comp > 0) { \ - __tmp = SPLAY_RIGHT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if ((cmp)(elm, __tmp) > 0){ \ - SPLAY_ROTATE_LEFT(head, __tmp, field); \ - if (SPLAY_RIGHT((head)->sph_root, field) == NULL) \ - break; \ - } \ - SPLAY_LINKRIGHT(head, __left, field); \ - } \ - } \ - SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ -} \ - \ -/* Splay with either the minimum or the maximum element \ - * Used to find minimum or maximum element in tree. \ - */ \ -void name##_SPLAY_MINMAX(struct name *head, int __comp) \ -{ \ - struct type __node, *__left, *__right, *__tmp; \ - \ - SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL; \ - __left = __right = &__node; \ - \ - while (1) { \ - if (__comp < 0) { \ - __tmp = SPLAY_LEFT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if (__comp < 0){ \ - SPLAY_ROTATE_RIGHT(head, __tmp, field); \ - if (SPLAY_LEFT((head)->sph_root, field) == NULL) \ - break; \ - } \ - SPLAY_LINKLEFT(head, __right, field); \ - } else if (__comp > 0) { \ - __tmp = SPLAY_RIGHT((head)->sph_root, field); \ - if (__tmp == NULL) \ - break; \ - if (__comp > 0) { \ - SPLAY_ROTATE_LEFT(head, __tmp, field); \ - if (SPLAY_RIGHT((head)->sph_root, field) == NULL) \ - break; \ - } \ - SPLAY_LINKRIGHT(head, __left, field); \ - } \ - } \ - SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ -} - -#define SPLAY_NEGINF -1 -#define SPLAY_INF 1 - -#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) -#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) -#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) -#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) -#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ - : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) -#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ - : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) - -#define SPLAY_FOREACH(x, name, head) \ - for ((x) = SPLAY_MIN(name, head); \ - (x) != NULL; \ - (x) = SPLAY_NEXT(name, head, x)) - -/* Macros that define a red-black tree */ -#define RB_HEAD(name, type) \ -struct name { \ - struct type *rbh_root; /* root of the tree */ \ -} - -#define RB_INITIALIZER(root) \ - { NULL } - -#define RB_INIT(root) do { \ - (root)->rbh_root = NULL; \ -} while (/*CONSTCOND*/ 0) - -#define RB_BLACK 0 -#define RB_RED 1 -#define RB_ENTRY(type) \ -struct { \ - struct type *rbe_left; /* left element */ \ - struct type *rbe_right; /* right element */ \ - struct type *rbe_parent; /* parent element */ \ - int rbe_color; /* node color */ \ -} - -#define RB_LEFT(elm, field) (elm)->field.rbe_left -#define RB_RIGHT(elm, field) (elm)->field.rbe_right -#define RB_PARENT(elm, field) (elm)->field.rbe_parent -#define RB_COLOR(elm, field) (elm)->field.rbe_color -#define RB_ROOT(head) (head)->rbh_root -#define RB_EMPTY(head) (RB_ROOT(head) == NULL) - -#define RB_SET(elm, parent, field) do { \ - RB_PARENT(elm, field) = parent; \ - RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ - RB_COLOR(elm, field) = RB_RED; \ -} while (/*CONSTCOND*/ 0) - -#define RB_SET_BLACKRED(black, red, field) do { \ - RB_COLOR(black, field) = RB_BLACK; \ - RB_COLOR(red, field) = RB_RED; \ -} while (/*CONSTCOND*/ 0) - -#ifndef RB_AUGMENT -#define RB_AUGMENT(x) do {} while (0) -#endif - -#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ - (tmp) = RB_RIGHT(elm, field); \ - if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \ - RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ - } \ - RB_AUGMENT(elm); \ - if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ - if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ - RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ - else \ - RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ - } else \ - (head)->rbh_root = (tmp); \ - RB_LEFT(tmp, field) = (elm); \ - RB_PARENT(elm, field) = (tmp); \ - RB_AUGMENT(tmp); \ - if ((RB_PARENT(tmp, field))) \ - RB_AUGMENT(RB_PARENT(tmp, field)); \ -} while (/*CONSTCOND*/ 0) - -#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ - (tmp) = RB_LEFT(elm, field); \ - if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \ - RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ - } \ - RB_AUGMENT(elm); \ - if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field)) != NULL) { \ - if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ - RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ - else \ - RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ - } else \ - (head)->rbh_root = (tmp); \ - RB_RIGHT(tmp, field) = (elm); \ - RB_PARENT(elm, field) = (tmp); \ - RB_AUGMENT(tmp); \ - if ((RB_PARENT(tmp, field))) \ - RB_AUGMENT(RB_PARENT(tmp, field)); \ -} while (/*CONSTCOND*/ 0) - -/* Generates prototypes and inline functions */ -#define RB_PROTOTYPE(name, type, field, cmp) \ - RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) -#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ - RB_PROTOTYPE_INTERNAL(name, type, field, cmp, UV__UNUSED static) -#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ -attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ -attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ -attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ -attr struct type *name##_RB_INSERT(struct name *, struct type *); \ -attr struct type *name##_RB_FIND(struct name *, struct type *); \ -attr struct type *name##_RB_NFIND(struct name *, struct type *); \ -attr struct type *name##_RB_NEXT(struct type *); \ -attr struct type *name##_RB_PREV(struct type *); \ -attr struct type *name##_RB_MINMAX(struct name *, int); \ - \ - -/* Main rb operation. - * Moves node close to the key of elm to top - */ -#define RB_GENERATE(name, type, field, cmp) \ - RB_GENERATE_INTERNAL(name, type, field, cmp,) -#define RB_GENERATE_STATIC(name, type, field, cmp) \ - RB_GENERATE_INTERNAL(name, type, field, cmp, UV__UNUSED static) -#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ -attr void \ -name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ -{ \ - struct type *parent, *gparent, *tmp; \ - while ((parent = RB_PARENT(elm, field)) != NULL && \ - RB_COLOR(parent, field) == RB_RED) { \ - gparent = RB_PARENT(parent, field); \ - if (parent == RB_LEFT(gparent, field)) { \ - tmp = RB_RIGHT(gparent, field); \ - if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ - RB_COLOR(tmp, field) = RB_BLACK; \ - RB_SET_BLACKRED(parent, gparent, field); \ - elm = gparent; \ - continue; \ - } \ - if (RB_RIGHT(parent, field) == elm) { \ - RB_ROTATE_LEFT(head, parent, tmp, field); \ - tmp = parent; \ - parent = elm; \ - elm = tmp; \ - } \ - RB_SET_BLACKRED(parent, gparent, field); \ - RB_ROTATE_RIGHT(head, gparent, tmp, field); \ - } else { \ - tmp = RB_LEFT(gparent, field); \ - if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ - RB_COLOR(tmp, field) = RB_BLACK; \ - RB_SET_BLACKRED(parent, gparent, field); \ - elm = gparent; \ - continue; \ - } \ - if (RB_LEFT(parent, field) == elm) { \ - RB_ROTATE_RIGHT(head, parent, tmp, field); \ - tmp = parent; \ - parent = elm; \ - elm = tmp; \ - } \ - RB_SET_BLACKRED(parent, gparent, field); \ - RB_ROTATE_LEFT(head, gparent, tmp, field); \ - } \ - } \ - RB_COLOR(head->rbh_root, field) = RB_BLACK; \ -} \ - \ -attr void \ -name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, \ - struct type *elm) \ -{ \ - struct type *tmp; \ - while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ - elm != RB_ROOT(head)) { \ - if (RB_LEFT(parent, field) == elm) { \ - tmp = RB_RIGHT(parent, field); \ - if (RB_COLOR(tmp, field) == RB_RED) { \ - RB_SET_BLACKRED(tmp, parent, field); \ - RB_ROTATE_LEFT(head, parent, tmp, field); \ - tmp = RB_RIGHT(parent, field); \ - } \ - if ((RB_LEFT(tmp, field) == NULL || \ - RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) && \ - (RB_RIGHT(tmp, field) == NULL || \ - RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) { \ - RB_COLOR(tmp, field) = RB_RED; \ - elm = parent; \ - parent = RB_PARENT(elm, field); \ - } else { \ - if (RB_RIGHT(tmp, field) == NULL || \ - RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) { \ - struct type *oleft; \ - if ((oleft = RB_LEFT(tmp, field)) \ - != NULL) \ - RB_COLOR(oleft, field) = RB_BLACK; \ - RB_COLOR(tmp, field) = RB_RED; \ - RB_ROTATE_RIGHT(head, tmp, oleft, field); \ - tmp = RB_RIGHT(parent, field); \ - } \ - RB_COLOR(tmp, field) = RB_COLOR(parent, field); \ - RB_COLOR(parent, field) = RB_BLACK; \ - if (RB_RIGHT(tmp, field)) \ - RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK; \ - RB_ROTATE_LEFT(head, parent, tmp, field); \ - elm = RB_ROOT(head); \ - break; \ - } \ - } else { \ - tmp = RB_LEFT(parent, field); \ - if (RB_COLOR(tmp, field) == RB_RED) { \ - RB_SET_BLACKRED(tmp, parent, field); \ - RB_ROTATE_RIGHT(head, parent, tmp, field); \ - tmp = RB_LEFT(parent, field); \ - } \ - if ((RB_LEFT(tmp, field) == NULL || \ - RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) && \ - (RB_RIGHT(tmp, field) == NULL || \ - RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) { \ - RB_COLOR(tmp, field) = RB_RED; \ - elm = parent; \ - parent = RB_PARENT(elm, field); \ - } else { \ - if (RB_LEFT(tmp, field) == NULL || \ - RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) { \ - struct type *oright; \ - if ((oright = RB_RIGHT(tmp, field)) \ - != NULL) \ - RB_COLOR(oright, field) = RB_BLACK; \ - RB_COLOR(tmp, field) = RB_RED; \ - RB_ROTATE_LEFT(head, tmp, oright, field); \ - tmp = RB_LEFT(parent, field); \ - } \ - RB_COLOR(tmp, field) = RB_COLOR(parent, field); \ - RB_COLOR(parent, field) = RB_BLACK; \ - if (RB_LEFT(tmp, field)) \ - RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK; \ - RB_ROTATE_RIGHT(head, parent, tmp, field); \ - elm = RB_ROOT(head); \ - break; \ - } \ - } \ - } \ - if (elm) \ - RB_COLOR(elm, field) = RB_BLACK; \ -} \ - \ -attr struct type * \ -name##_RB_REMOVE(struct name *head, struct type *elm) \ -{ \ - struct type *child, *parent, *old = elm; \ - int color; \ - if (RB_LEFT(elm, field) == NULL) \ - child = RB_RIGHT(elm, field); \ - else if (RB_RIGHT(elm, field) == NULL) \ - child = RB_LEFT(elm, field); \ - else { \ - struct type *left; \ - elm = RB_RIGHT(elm, field); \ - while ((left = RB_LEFT(elm, field)) != NULL) \ - elm = left; \ - child = RB_RIGHT(elm, field); \ - parent = RB_PARENT(elm, field); \ - color = RB_COLOR(elm, field); \ - if (child) \ - RB_PARENT(child, field) = parent; \ - if (parent) { \ - if (RB_LEFT(parent, field) == elm) \ - RB_LEFT(parent, field) = child; \ - else \ - RB_RIGHT(parent, field) = child; \ - RB_AUGMENT(parent); \ - } else \ - RB_ROOT(head) = child; \ - if (RB_PARENT(elm, field) == old) \ - parent = elm; \ - (elm)->field = (old)->field; \ - if (RB_PARENT(old, field)) { \ - if (RB_LEFT(RB_PARENT(old, field), field) == old) \ - RB_LEFT(RB_PARENT(old, field), field) = elm; \ - else \ - RB_RIGHT(RB_PARENT(old, field), field) = elm; \ - RB_AUGMENT(RB_PARENT(old, field)); \ - } else \ - RB_ROOT(head) = elm; \ - RB_PARENT(RB_LEFT(old, field), field) = elm; \ - if (RB_RIGHT(old, field)) \ - RB_PARENT(RB_RIGHT(old, field), field) = elm; \ - if (parent) { \ - left = parent; \ - do { \ - RB_AUGMENT(left); \ - } while ((left = RB_PARENT(left, field)) != NULL); \ - } \ - goto color; \ - } \ - parent = RB_PARENT(elm, field); \ - color = RB_COLOR(elm, field); \ - if (child) \ - RB_PARENT(child, field) = parent; \ - if (parent) { \ - if (RB_LEFT(parent, field) == elm) \ - RB_LEFT(parent, field) = child; \ - else \ - RB_RIGHT(parent, field) = child; \ - RB_AUGMENT(parent); \ - } else \ - RB_ROOT(head) = child; \ -color: \ - if (color == RB_BLACK) \ - name##_RB_REMOVE_COLOR(head, parent, child); \ - return (old); \ -} \ - \ -/* Inserts a node into the RB tree */ \ -attr struct type * \ -name##_RB_INSERT(struct name *head, struct type *elm) \ -{ \ - struct type *tmp; \ - struct type *parent = NULL; \ - int comp = 0; \ - tmp = RB_ROOT(head); \ - while (tmp) { \ - parent = tmp; \ - comp = (cmp)(elm, parent); \ - if (comp < 0) \ - tmp = RB_LEFT(tmp, field); \ - else if (comp > 0) \ - tmp = RB_RIGHT(tmp, field); \ - else \ - return (tmp); \ - } \ - RB_SET(elm, parent, field); \ - if (parent != NULL) { \ - if (comp < 0) \ - RB_LEFT(parent, field) = elm; \ - else \ - RB_RIGHT(parent, field) = elm; \ - RB_AUGMENT(parent); \ - } else \ - RB_ROOT(head) = elm; \ - name##_RB_INSERT_COLOR(head, elm); \ - return (NULL); \ -} \ - \ -/* Finds the node with the same key as elm */ \ -attr struct type * \ -name##_RB_FIND(struct name *head, struct type *elm) \ -{ \ - struct type *tmp = RB_ROOT(head); \ - int comp; \ - while (tmp) { \ - comp = cmp(elm, tmp); \ - if (comp < 0) \ - tmp = RB_LEFT(tmp, field); \ - else if (comp > 0) \ - tmp = RB_RIGHT(tmp, field); \ - else \ - return (tmp); \ - } \ - return (NULL); \ -} \ - \ -/* Finds the first node greater than or equal to the search key */ \ -attr struct type * \ -name##_RB_NFIND(struct name *head, struct type *elm) \ -{ \ - struct type *tmp = RB_ROOT(head); \ - struct type *res = NULL; \ - int comp; \ - while (tmp) { \ - comp = cmp(elm, tmp); \ - if (comp < 0) { \ - res = tmp; \ - tmp = RB_LEFT(tmp, field); \ - } \ - else if (comp > 0) \ - tmp = RB_RIGHT(tmp, field); \ - else \ - return (tmp); \ - } \ - return (res); \ -} \ - \ -/* ARGSUSED */ \ -attr struct type * \ -name##_RB_NEXT(struct type *elm) \ -{ \ - if (RB_RIGHT(elm, field)) { \ - elm = RB_RIGHT(elm, field); \ - while (RB_LEFT(elm, field)) \ - elm = RB_LEFT(elm, field); \ - } else { \ - if (RB_PARENT(elm, field) && \ - (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ - elm = RB_PARENT(elm, field); \ - else { \ - while (RB_PARENT(elm, field) && \ - (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ - elm = RB_PARENT(elm, field); \ - elm = RB_PARENT(elm, field); \ - } \ - } \ - return (elm); \ -} \ - \ -/* ARGSUSED */ \ -attr struct type * \ -name##_RB_PREV(struct type *elm) \ -{ \ - if (RB_LEFT(elm, field)) { \ - elm = RB_LEFT(elm, field); \ - while (RB_RIGHT(elm, field)) \ - elm = RB_RIGHT(elm, field); \ - } else { \ - if (RB_PARENT(elm, field) && \ - (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ - elm = RB_PARENT(elm, field); \ - else { \ - while (RB_PARENT(elm, field) && \ - (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ - elm = RB_PARENT(elm, field); \ - elm = RB_PARENT(elm, field); \ - } \ - } \ - return (elm); \ -} \ - \ -attr struct type * \ -name##_RB_MINMAX(struct name *head, int val) \ -{ \ - struct type *tmp = RB_ROOT(head); \ - struct type *parent = NULL; \ - while (tmp) { \ - parent = tmp; \ - if (val < 0) \ - tmp = RB_LEFT(tmp, field); \ - else \ - tmp = RB_RIGHT(tmp, field); \ - } \ - return (parent); \ -} - -#define RB_NEGINF -1 -#define RB_INF 1 - -#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) -#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) -#define RB_FIND(name, x, y) name##_RB_FIND(x, y) -#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) -#define RB_NEXT(name, x, y) name##_RB_NEXT(y) -#define RB_PREV(name, x, y) name##_RB_PREV(y) -#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) -#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) - -#define RB_FOREACH(x, name, head) \ - for ((x) = RB_MIN(name, head); \ - (x) != NULL; \ - (x) = name##_RB_NEXT(x)) - -#define RB_FOREACH_FROM(x, name, y) \ - for ((x) = (y); \ - ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ - (x) = (y)) - -#define RB_FOREACH_SAFE(x, name, head, y) \ - for ((x) = RB_MIN(name, head); \ - ((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \ - (x) = (y)) - -#define RB_FOREACH_REVERSE(x, name, head) \ - for ((x) = RB_MAX(name, head); \ - (x) != NULL; \ - (x) = name##_RB_PREV(x)) - -#define RB_FOREACH_REVERSE_FROM(x, name, y) \ - for ((x) = (y); \ - ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ - (x) = (y)) - -#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ - for ((x) = RB_MAX(name, head); \ - ((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \ - (x) = (y)) - -#endif /* UV_TREE_H_ */ diff --git a/include/libuv/include/uv-errno.h b/include/libuv/include/uv-errno.h deleted file mode 100644 index 53f30296c..000000000 --- a/include/libuv/include/uv-errno.h +++ /dev/null @@ -1,418 +0,0 @@ -/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#ifndef UV_ERRNO_H_ -#define UV_ERRNO_H_ - -#include - -#define UV__EOF (-4095) -#define UV__UNKNOWN (-4094) - -#define UV__EAI_ADDRFAMILY (-3000) -#define UV__EAI_AGAIN (-3001) -#define UV__EAI_BADFLAGS (-3002) -#define UV__EAI_CANCELED (-3003) -#define UV__EAI_FAIL (-3004) -#define UV__EAI_FAMILY (-3005) -#define UV__EAI_MEMORY (-3006) -#define UV__EAI_NODATA (-3007) -#define UV__EAI_NONAME (-3008) -#define UV__EAI_OVERFLOW (-3009) -#define UV__EAI_SERVICE (-3010) -#define UV__EAI_SOCKTYPE (-3011) -#define UV__EAI_BADHINTS (-3013) -#define UV__EAI_PROTOCOL (-3014) - -/* Only map to the system errno on non-Windows platforms. It's apparently - * a fairly common practice for Windows programmers to redefine errno codes. - */ -#if defined(E2BIG) && !defined(_WIN32) -# define UV__E2BIG (-E2BIG) -#else -# define UV__E2BIG (-4093) -#endif - -#if defined(EACCES) && !defined(_WIN32) -# define UV__EACCES (-EACCES) -#else -# define UV__EACCES (-4092) -#endif - -#if defined(EADDRINUSE) && !defined(_WIN32) -# define UV__EADDRINUSE (-EADDRINUSE) -#else -# define UV__EADDRINUSE (-4091) -#endif - -#if defined(EADDRNOTAVAIL) && !defined(_WIN32) -# define UV__EADDRNOTAVAIL (-EADDRNOTAVAIL) -#else -# define UV__EADDRNOTAVAIL (-4090) -#endif - -#if defined(EAFNOSUPPORT) && !defined(_WIN32) -# define UV__EAFNOSUPPORT (-EAFNOSUPPORT) -#else -# define UV__EAFNOSUPPORT (-4089) -#endif - -#if defined(EAGAIN) && !defined(_WIN32) -# define UV__EAGAIN (-EAGAIN) -#else -# define UV__EAGAIN (-4088) -#endif - -#if defined(EALREADY) && !defined(_WIN32) -# define UV__EALREADY (-EALREADY) -#else -# define UV__EALREADY (-4084) -#endif - -#if defined(EBADF) && !defined(_WIN32) -# define UV__EBADF (-EBADF) -#else -# define UV__EBADF (-4083) -#endif - -#if defined(EBUSY) && !defined(_WIN32) -# define UV__EBUSY (-EBUSY) -#else -# define UV__EBUSY (-4082) -#endif - -#if defined(ECANCELED) && !defined(_WIN32) -# define UV__ECANCELED (-ECANCELED) -#else -# define UV__ECANCELED (-4081) -#endif - -#if defined(ECHARSET) && !defined(_WIN32) -# define UV__ECHARSET (-ECHARSET) -#else -# define UV__ECHARSET (-4080) -#endif - -#if defined(ECONNABORTED) && !defined(_WIN32) -# define UV__ECONNABORTED (-ECONNABORTED) -#else -# define UV__ECONNABORTED (-4079) -#endif - -#if defined(ECONNREFUSED) && !defined(_WIN32) -# define UV__ECONNREFUSED (-ECONNREFUSED) -#else -# define UV__ECONNREFUSED (-4078) -#endif - -#if defined(ECONNRESET) && !defined(_WIN32) -# define UV__ECONNRESET (-ECONNRESET) -#else -# define UV__ECONNRESET (-4077) -#endif - -#if defined(EDESTADDRREQ) && !defined(_WIN32) -# define UV__EDESTADDRREQ (-EDESTADDRREQ) -#else -# define UV__EDESTADDRREQ (-4076) -#endif - -#if defined(EEXIST) && !defined(_WIN32) -# define UV__EEXIST (-EEXIST) -#else -# define UV__EEXIST (-4075) -#endif - -#if defined(EFAULT) && !defined(_WIN32) -# define UV__EFAULT (-EFAULT) -#else -# define UV__EFAULT (-4074) -#endif - -#if defined(EHOSTUNREACH) && !defined(_WIN32) -# define UV__EHOSTUNREACH (-EHOSTUNREACH) -#else -# define UV__EHOSTUNREACH (-4073) -#endif - -#if defined(EINTR) && !defined(_WIN32) -# define UV__EINTR (-EINTR) -#else -# define UV__EINTR (-4072) -#endif - -#if defined(EINVAL) && !defined(_WIN32) -# define UV__EINVAL (-EINVAL) -#else -# define UV__EINVAL (-4071) -#endif - -#if defined(EIO) && !defined(_WIN32) -# define UV__EIO (-EIO) -#else -# define UV__EIO (-4070) -#endif - -#if defined(EISCONN) && !defined(_WIN32) -# define UV__EISCONN (-EISCONN) -#else -# define UV__EISCONN (-4069) -#endif - -#if defined(EISDIR) && !defined(_WIN32) -# define UV__EISDIR (-EISDIR) -#else -# define UV__EISDIR (-4068) -#endif - -#if defined(ELOOP) && !defined(_WIN32) -# define UV__ELOOP (-ELOOP) -#else -# define UV__ELOOP (-4067) -#endif - -#if defined(EMFILE) && !defined(_WIN32) -# define UV__EMFILE (-EMFILE) -#else -# define UV__EMFILE (-4066) -#endif - -#if defined(EMSGSIZE) && !defined(_WIN32) -# define UV__EMSGSIZE (-EMSGSIZE) -#else -# define UV__EMSGSIZE (-4065) -#endif - -#if defined(ENAMETOOLONG) && !defined(_WIN32) -# define UV__ENAMETOOLONG (-ENAMETOOLONG) -#else -# define UV__ENAMETOOLONG (-4064) -#endif - -#if defined(ENETDOWN) && !defined(_WIN32) -# define UV__ENETDOWN (-ENETDOWN) -#else -# define UV__ENETDOWN (-4063) -#endif - -#if defined(ENETUNREACH) && !defined(_WIN32) -# define UV__ENETUNREACH (-ENETUNREACH) -#else -# define UV__ENETUNREACH (-4062) -#endif - -#if defined(ENFILE) && !defined(_WIN32) -# define UV__ENFILE (-ENFILE) -#else -# define UV__ENFILE (-4061) -#endif - -#if defined(ENOBUFS) && !defined(_WIN32) -# define UV__ENOBUFS (-ENOBUFS) -#else -# define UV__ENOBUFS (-4060) -#endif - -#if defined(ENODEV) && !defined(_WIN32) -# define UV__ENODEV (-ENODEV) -#else -# define UV__ENODEV (-4059) -#endif - -#if defined(ENOENT) && !defined(_WIN32) -# define UV__ENOENT (-ENOENT) -#else -# define UV__ENOENT (-4058) -#endif - -#if defined(ENOMEM) && !defined(_WIN32) -# define UV__ENOMEM (-ENOMEM) -#else -# define UV__ENOMEM (-4057) -#endif - -#if defined(ENONET) && !defined(_WIN32) -# define UV__ENONET (-ENONET) -#else -# define UV__ENONET (-4056) -#endif - -#if defined(ENOSPC) && !defined(_WIN32) -# define UV__ENOSPC (-ENOSPC) -#else -# define UV__ENOSPC (-4055) -#endif - -#if defined(ENOSYS) && !defined(_WIN32) -# define UV__ENOSYS (-ENOSYS) -#else -# define UV__ENOSYS (-4054) -#endif - -#if defined(ENOTCONN) && !defined(_WIN32) -# define UV__ENOTCONN (-ENOTCONN) -#else -# define UV__ENOTCONN (-4053) -#endif - -#if defined(ENOTDIR) && !defined(_WIN32) -# define UV__ENOTDIR (-ENOTDIR) -#else -# define UV__ENOTDIR (-4052) -#endif - -#if defined(ENOTEMPTY) && !defined(_WIN32) -# define UV__ENOTEMPTY (-ENOTEMPTY) -#else -# define UV__ENOTEMPTY (-4051) -#endif - -#if defined(ENOTSOCK) && !defined(_WIN32) -# define UV__ENOTSOCK (-ENOTSOCK) -#else -# define UV__ENOTSOCK (-4050) -#endif - -#if defined(ENOTSUP) && !defined(_WIN32) -# define UV__ENOTSUP (-ENOTSUP) -#else -# define UV__ENOTSUP (-4049) -#endif - -#if defined(EPERM) && !defined(_WIN32) -# define UV__EPERM (-EPERM) -#else -# define UV__EPERM (-4048) -#endif - -#if defined(EPIPE) && !defined(_WIN32) -# define UV__EPIPE (-EPIPE) -#else -# define UV__EPIPE (-4047) -#endif - -#if defined(EPROTO) && !defined(_WIN32) -# define UV__EPROTO (-EPROTO) -#else -# define UV__EPROTO (-4046) -#endif - -#if defined(EPROTONOSUPPORT) && !defined(_WIN32) -# define UV__EPROTONOSUPPORT (-EPROTONOSUPPORT) -#else -# define UV__EPROTONOSUPPORT (-4045) -#endif - -#if defined(EPROTOTYPE) && !defined(_WIN32) -# define UV__EPROTOTYPE (-EPROTOTYPE) -#else -# define UV__EPROTOTYPE (-4044) -#endif - -#if defined(EROFS) && !defined(_WIN32) -# define UV__EROFS (-EROFS) -#else -# define UV__EROFS (-4043) -#endif - -#if defined(ESHUTDOWN) && !defined(_WIN32) -# define UV__ESHUTDOWN (-ESHUTDOWN) -#else -# define UV__ESHUTDOWN (-4042) -#endif - -#if defined(ESPIPE) && !defined(_WIN32) -# define UV__ESPIPE (-ESPIPE) -#else -# define UV__ESPIPE (-4041) -#endif - -#if defined(ESRCH) && !defined(_WIN32) -# define UV__ESRCH (-ESRCH) -#else -# define UV__ESRCH (-4040) -#endif - -#if defined(ETIMEDOUT) && !defined(_WIN32) -# define UV__ETIMEDOUT (-ETIMEDOUT) -#else -# define UV__ETIMEDOUT (-4039) -#endif - -#if defined(ETXTBSY) && !defined(_WIN32) -# define UV__ETXTBSY (-ETXTBSY) -#else -# define UV__ETXTBSY (-4038) -#endif - -#if defined(EXDEV) && !defined(_WIN32) -# define UV__EXDEV (-EXDEV) -#else -# define UV__EXDEV (-4037) -#endif - -#if defined(EFBIG) && !defined(_WIN32) -# define UV__EFBIG (-EFBIG) -#else -# define UV__EFBIG (-4036) -#endif - -#if defined(ENOPROTOOPT) && !defined(_WIN32) -# define UV__ENOPROTOOPT (-ENOPROTOOPT) -#else -# define UV__ENOPROTOOPT (-4035) -#endif - -#if defined(ERANGE) && !defined(_WIN32) -# define UV__ERANGE (-ERANGE) -#else -# define UV__ERANGE (-4034) -#endif - -#if defined(ENXIO) && !defined(_WIN32) -# define UV__ENXIO (-ENXIO) -#else -# define UV__ENXIO (-4033) -#endif - -#if defined(EMLINK) && !defined(_WIN32) -# define UV__EMLINK (-EMLINK) -#else -# define UV__EMLINK (-4032) -#endif - -/* EHOSTDOWN is not visible on BSD-like systems when _POSIX_C_SOURCE is - * defined. Fortunately, its value is always 64 so it's possible albeit - * icky to hard-code it. - */ -#if defined(EHOSTDOWN) && !defined(_WIN32) -# define UV__EHOSTDOWN (-EHOSTDOWN) -#elif defined(__APPLE__) || \ - defined(__DragonFly__) || \ - defined(__FreeBSD__) || \ - defined(__NetBSD__) || \ - defined(__OpenBSD__) -# define UV__EHOSTDOWN (-64) -#else -# define UV__EHOSTDOWN (-4031) -#endif - -#endif /* UV_ERRNO_H_ */ diff --git a/include/libuv/include/uv-threadpool.h b/include/libuv/include/uv-threadpool.h deleted file mode 100644 index 9708ebdd5..000000000 --- a/include/libuv/include/uv-threadpool.h +++ /dev/null @@ -1,37 +0,0 @@ -/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -/* - * This file is private to libuv. It provides common functionality to both - * Windows and Unix backends. - */ - -#ifndef UV_THREADPOOL_H_ -#define UV_THREADPOOL_H_ - -struct uv__work { - void (*work)(struct uv__work *w); - void (*done)(struct uv__work *w, int status); - struct uv_loop_s* loop; - void* wq[2]; -}; - -#endif /* UV_THREADPOOL_H_ */ diff --git a/include/libuv/include/uv-version.h b/include/libuv/include/uv-version.h deleted file mode 100644 index 08ad0edaa..000000000 --- a/include/libuv/include/uv-version.h +++ /dev/null @@ -1,43 +0,0 @@ -/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#ifndef UV_VERSION_H -#define UV_VERSION_H - - /* - * Versions with the same major number are ABI stable. API is allowed to - * evolve between minor releases, but only in a backwards compatible way. - * Make sure you update the -soname directives in configure.ac - * and uv.gyp whenever you bump UV_VERSION_MAJOR or UV_VERSION_MINOR (but - * not UV_VERSION_PATCH.) - */ - -#define UV_VERSION_MAJOR 1 -#define UV_VERSION_MINOR 9 -#define UV_VERSION_PATCH 1 -#define UV_VERSION_IS_RELEASE 1 -#define UV_VERSION_SUFFIX "" - -#define UV_VERSION_HEX ((UV_VERSION_MAJOR << 16) | \ - (UV_VERSION_MINOR << 8) | \ - (UV_VERSION_PATCH)) - -#endif /* UV_VERSION_H */ diff --git a/include/libuv/include/uv-win.h b/include/libuv/include/uv-win.h deleted file mode 100644 index a75dba8d1..000000000 --- a/include/libuv/include/uv-win.h +++ /dev/null @@ -1,649 +0,0 @@ -/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#ifndef _WIN32_WINNT -# define _WIN32_WINNT 0x0502 -#endif - -#if !defined(_SSIZE_T_) && !defined(_SSIZE_T_DEFINED) -typedef intptr_t ssize_t; -# define _SSIZE_T_ -# define _SSIZE_T_DEFINED -#endif - -#include - -#if defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) -typedef struct pollfd { - SOCKET fd; - short events; - short revents; -} WSAPOLLFD, *PWSAPOLLFD, *LPWSAPOLLFD; -#endif - -#ifndef LOCALE_INVARIANT -# define LOCALE_INVARIANT 0x007f -#endif - -#include -#include -#include - -#include -#include -#include - -#if defined(_MSC_VER) && _MSC_VER < 1600 -# include "stdint-msvc2008.h" -#else -# include -#endif - -#include "tree.h" -#include "uv-threadpool.h" - -#define MAX_PIPENAME_LEN 256 - -#ifndef S_IFLNK -# define S_IFLNK 0xA000 -#endif - -/* Additional signals supported by uv_signal and or uv_kill. The CRT defines - * the following signals already: - * - * #define SIGINT 2 - * #define SIGILL 4 - * #define SIGABRT_COMPAT 6 - * #define SIGFPE 8 - * #define SIGSEGV 11 - * #define SIGTERM 15 - * #define SIGBREAK 21 - * #define SIGABRT 22 - * - * The additional signals have values that are common on other Unix - * variants (Linux and Darwin) - */ -#define SIGHUP 1 -#define SIGKILL 9 -#define SIGWINCH 28 - -/* The CRT defines SIGABRT_COMPAT as 6, which equals SIGABRT on many */ -/* unix-like platforms. However MinGW doesn't define it, so we do. */ -#ifndef SIGABRT_COMPAT -# define SIGABRT_COMPAT 6 -#endif - -/* - * Guids and typedefs for winsock extension functions - * Mingw32 doesn't have these :-( - */ -#ifndef WSAID_ACCEPTEX -# define WSAID_ACCEPTEX \ - {0xb5367df1, 0xcbac, 0x11cf, \ - {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}} - -# define WSAID_CONNECTEX \ - {0x25a207b9, 0xddf3, 0x4660, \ - {0x8e, 0xe9, 0x76, 0xe5, 0x8c, 0x74, 0x06, 0x3e}} - -# define WSAID_GETACCEPTEXSOCKADDRS \ - {0xb5367df2, 0xcbac, 0x11cf, \ - {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}} - -# define WSAID_DISCONNECTEX \ - {0x7fda2e11, 0x8630, 0x436f, \ - {0xa0, 0x31, 0xf5, 0x36, 0xa6, 0xee, 0xc1, 0x57}} - -# define WSAID_TRANSMITFILE \ - {0xb5367df0, 0xcbac, 0x11cf, \ - {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}} - - typedef BOOL PASCAL (*LPFN_ACCEPTEX) - (SOCKET sListenSocket, - SOCKET sAcceptSocket, - PVOID lpOutputBuffer, - DWORD dwReceiveDataLength, - DWORD dwLocalAddressLength, - DWORD dwRemoteAddressLength, - LPDWORD lpdwBytesReceived, - LPOVERLAPPED lpOverlapped); - - typedef BOOL PASCAL (*LPFN_CONNECTEX) - (SOCKET s, - const struct sockaddr* name, - int namelen, - PVOID lpSendBuffer, - DWORD dwSendDataLength, - LPDWORD lpdwBytesSent, - LPOVERLAPPED lpOverlapped); - - typedef void PASCAL (*LPFN_GETACCEPTEXSOCKADDRS) - (PVOID lpOutputBuffer, - DWORD dwReceiveDataLength, - DWORD dwLocalAddressLength, - DWORD dwRemoteAddressLength, - LPSOCKADDR* LocalSockaddr, - LPINT LocalSockaddrLength, - LPSOCKADDR* RemoteSockaddr, - LPINT RemoteSockaddrLength); - - typedef BOOL PASCAL (*LPFN_DISCONNECTEX) - (SOCKET hSocket, - LPOVERLAPPED lpOverlapped, - DWORD dwFlags, - DWORD reserved); - - typedef BOOL PASCAL (*LPFN_TRANSMITFILE) - (SOCKET hSocket, - HANDLE hFile, - DWORD nNumberOfBytesToWrite, - DWORD nNumberOfBytesPerSend, - LPOVERLAPPED lpOverlapped, - LPTRANSMIT_FILE_BUFFERS lpTransmitBuffers, - DWORD dwFlags); - - typedef PVOID RTL_SRWLOCK; - typedef RTL_SRWLOCK SRWLOCK, *PSRWLOCK; -#endif - -typedef int (WSAAPI* LPFN_WSARECV) - (SOCKET socket, - LPWSABUF buffers, - DWORD buffer_count, - LPDWORD bytes, - LPDWORD flags, - LPWSAOVERLAPPED overlapped, - LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine); - -typedef int (WSAAPI* LPFN_WSARECVFROM) - (SOCKET socket, - LPWSABUF buffers, - DWORD buffer_count, - LPDWORD bytes, - LPDWORD flags, - struct sockaddr* addr, - LPINT addr_len, - LPWSAOVERLAPPED overlapped, - LPWSAOVERLAPPED_COMPLETION_ROUTINE completion_routine); - -#ifndef _NTDEF_ - typedef LONG NTSTATUS; - typedef NTSTATUS *PNTSTATUS; -#endif - -#ifndef RTL_CONDITION_VARIABLE_INIT - typedef PVOID CONDITION_VARIABLE, *PCONDITION_VARIABLE; -#endif - -typedef struct _AFD_POLL_HANDLE_INFO { - HANDLE Handle; - ULONG Events; - NTSTATUS Status; -} AFD_POLL_HANDLE_INFO, *PAFD_POLL_HANDLE_INFO; - -typedef struct _AFD_POLL_INFO { - LARGE_INTEGER Timeout; - ULONG NumberOfHandles; - ULONG Exclusive; - AFD_POLL_HANDLE_INFO Handles[1]; -} AFD_POLL_INFO, *PAFD_POLL_INFO; - -#define UV_MSAFD_PROVIDER_COUNT 3 - - -/** - * It should be possible to cast uv_buf_t[] to WSABUF[] - * see http://msdn.microsoft.com/en-us/library/ms741542(v=vs.85).aspx - */ -typedef struct uv_buf_t { - ULONG len; - char* base; -} uv_buf_t; - -typedef int uv_file; -typedef SOCKET uv_os_sock_t; -typedef HANDLE uv_os_fd_t; - -typedef HANDLE uv_thread_t; - -typedef HANDLE uv_sem_t; - -typedef CRITICAL_SECTION uv_mutex_t; - -/* This condition variable implementation is based on the SetEvent solution - * (section 3.2) at http://www.cs.wustl.edu/~schmidt/win32-cv-1.html - * We could not use the SignalObjectAndWait solution (section 3.4) because - * it want the 2nd argument (type uv_mutex_t) of uv_cond_wait() and - * uv_cond_timedwait() to be HANDLEs, but we use CRITICAL_SECTIONs. - */ - -typedef union { - CONDITION_VARIABLE cond_var; - struct { - unsigned int waiters_count; - CRITICAL_SECTION waiters_count_lock; - HANDLE signal_event; - HANDLE broadcast_event; - } fallback; -} uv_cond_t; - -typedef union { - struct { - unsigned int num_readers_; - CRITICAL_SECTION num_readers_lock_; - HANDLE write_semaphore_; - } state_; - /* TODO: remove me in v2.x. */ - struct { - SRWLOCK unused_; - } unused1_; - /* TODO: remove me in v2.x. */ - struct { - uv_mutex_t unused1_; - uv_mutex_t unused2_; - } unused2_; -} uv_rwlock_t; - -typedef struct { - unsigned int n; - unsigned int count; - uv_mutex_t mutex; - uv_sem_t turnstile1; - uv_sem_t turnstile2; -} uv_barrier_t; - -typedef struct { - DWORD tls_index; -} uv_key_t; - -#define UV_ONCE_INIT { 0, NULL } - -typedef struct uv_once_s { - unsigned char ran; - HANDLE event; -} uv_once_t; - -/* Platform-specific definitions for uv_spawn support. */ -typedef unsigned char uv_uid_t; -typedef unsigned char uv_gid_t; - -typedef struct uv__dirent_s { - int d_type; - char d_name[1]; -} uv__dirent_t; - -#define HAVE_DIRENT_TYPES -#define UV__DT_DIR UV_DIRENT_DIR -#define UV__DT_FILE UV_DIRENT_FILE -#define UV__DT_LINK UV_DIRENT_LINK -#define UV__DT_FIFO UV_DIRENT_FIFO -#define UV__DT_SOCKET UV_DIRENT_SOCKET -#define UV__DT_CHAR UV_DIRENT_CHAR -#define UV__DT_BLOCK UV_DIRENT_BLOCK - -/* Platform-specific definitions for uv_dlopen support. */ -#define UV_DYNAMIC FAR WINAPI -typedef struct { - HMODULE handle; - char* errmsg; -} uv_lib_t; - -RB_HEAD(uv_timer_tree_s, uv_timer_s); - -#define UV_LOOP_PRIVATE_FIELDS \ - /* The loop's I/O completion port */ \ - HANDLE iocp; \ - /* The current time according to the event loop. in msecs. */ \ - uint64_t time; \ - /* Tail of a single-linked circular queue of pending reqs. If the queue */ \ - /* is empty, tail_ is NULL. If there is only one item, */ \ - /* tail_->next_req == tail_ */ \ - uv_req_t* pending_reqs_tail; \ - /* Head of a single-linked list of closed handles */ \ - uv_handle_t* endgame_handles; \ - /* The head of the timers tree */ \ - struct uv_timer_tree_s timers; \ - /* Lists of active loop (prepare / check / idle) watchers */ \ - uv_prepare_t* prepare_handles; \ - uv_check_t* check_handles; \ - uv_idle_t* idle_handles; \ - /* This pointer will refer to the prepare/check/idle handle whose */ \ - /* callback is scheduled to be called next. This is needed to allow */ \ - /* safe removal from one of the lists above while that list being */ \ - /* iterated over. */ \ - uv_prepare_t* next_prepare_handle; \ - uv_check_t* next_check_handle; \ - uv_idle_t* next_idle_handle; \ - /* This handle holds the peer sockets for the fast variant of uv_poll_t */ \ - SOCKET poll_peer_sockets[UV_MSAFD_PROVIDER_COUNT]; \ - /* Counter to keep track of active tcp streams */ \ - unsigned int active_tcp_streams; \ - /* Counter to keep track of active udp streams */ \ - unsigned int active_udp_streams; \ - /* Counter to started timer */ \ - uint64_t timer_counter; \ - /* Threadpool */ \ - void* wq[2]; \ - uv_mutex_t wq_mutex; \ - uv_async_t wq_async; - -#define UV_REQ_TYPE_PRIVATE \ - /* TODO: remove the req suffix */ \ - UV_ACCEPT, \ - UV_FS_EVENT_REQ, \ - UV_POLL_REQ, \ - UV_PROCESS_EXIT, \ - UV_READ, \ - UV_UDP_RECV, \ - UV_WAKEUP, \ - UV_SIGNAL_REQ, - -#define UV_REQ_PRIVATE_FIELDS \ - union { \ - /* Used by I/O operations */ \ - struct { \ - OVERLAPPED overlapped; \ - size_t queued_bytes; \ - } io; \ - } u; \ - struct uv_req_s* next_req; - -#define UV_WRITE_PRIVATE_FIELDS \ - int ipc_header; \ - uv_buf_t write_buffer; \ - HANDLE event_handle; \ - HANDLE wait_handle; - -#define UV_CONNECT_PRIVATE_FIELDS \ - /* empty */ - -#define UV_SHUTDOWN_PRIVATE_FIELDS \ - /* empty */ - -#define UV_UDP_SEND_PRIVATE_FIELDS \ - /* empty */ - -#define UV_PRIVATE_REQ_TYPES \ - typedef struct uv_pipe_accept_s { \ - UV_REQ_FIELDS \ - HANDLE pipeHandle; \ - struct uv_pipe_accept_s* next_pending; \ - } uv_pipe_accept_t; \ - \ - typedef struct uv_tcp_accept_s { \ - UV_REQ_FIELDS \ - SOCKET accept_socket; \ - char accept_buffer[sizeof(struct sockaddr_storage) * 2 + 32]; \ - HANDLE event_handle; \ - HANDLE wait_handle; \ - struct uv_tcp_accept_s* next_pending; \ - } uv_tcp_accept_t; \ - \ - typedef struct uv_read_s { \ - UV_REQ_FIELDS \ - HANDLE event_handle; \ - HANDLE wait_handle; \ - } uv_read_t; - -#define uv_stream_connection_fields \ - unsigned int write_reqs_pending; \ - uv_shutdown_t* shutdown_req; - -#define uv_stream_server_fields \ - uv_connection_cb connection_cb; - -#define UV_STREAM_PRIVATE_FIELDS \ - unsigned int reqs_pending; \ - int activecnt; \ - uv_read_t read_req; \ - union { \ - struct { uv_stream_connection_fields } conn; \ - struct { uv_stream_server_fields } serv; \ - } stream; - -#define uv_tcp_server_fields \ - uv_tcp_accept_t* accept_reqs; \ - unsigned int processed_accepts; \ - uv_tcp_accept_t* pending_accepts; \ - LPFN_ACCEPTEX func_acceptex; - -#define uv_tcp_connection_fields \ - uv_buf_t read_buffer; \ - LPFN_CONNECTEX func_connectex; - -#define UV_TCP_PRIVATE_FIELDS \ - SOCKET socket; \ - int delayed_error; \ - union { \ - struct { uv_tcp_server_fields } serv; \ - struct { uv_tcp_connection_fields } conn; \ - } tcp; - -#define UV_UDP_PRIVATE_FIELDS \ - SOCKET socket; \ - unsigned int reqs_pending; \ - int activecnt; \ - uv_req_t recv_req; \ - uv_buf_t recv_buffer; \ - struct sockaddr_storage recv_from; \ - int recv_from_len; \ - uv_udp_recv_cb recv_cb; \ - uv_alloc_cb alloc_cb; \ - LPFN_WSARECV func_wsarecv; \ - LPFN_WSARECVFROM func_wsarecvfrom; - -#define uv_pipe_server_fields \ - int pending_instances; \ - uv_pipe_accept_t* accept_reqs; \ - uv_pipe_accept_t* pending_accepts; - -#define uv_pipe_connection_fields \ - uv_timer_t* eof_timer; \ - uv_write_t ipc_header_write_req; \ - int ipc_pid; \ - uint64_t remaining_ipc_rawdata_bytes; \ - struct { \ - void* queue[2]; \ - int queue_len; \ - } pending_ipc_info; \ - uv_write_t* non_overlapped_writes_tail; \ - uv_mutex_t readfile_mutex; \ - volatile HANDLE readfile_thread; - -#define UV_PIPE_PRIVATE_FIELDS \ - HANDLE handle; \ - WCHAR* name; \ - union { \ - struct { uv_pipe_server_fields } serv; \ - struct { uv_pipe_connection_fields } conn; \ - } pipe; - -/* TODO: put the parser states in an union - TTY handles are always */ -/* half-duplex so read-state can safely overlap write-state. */ -#define UV_TTY_PRIVATE_FIELDS \ - HANDLE handle; \ - union { \ - struct { \ - /* Used for readable TTY handles */ \ - /* TODO: remove me in v2.x. */ \ - HANDLE unused_; \ - uv_buf_t read_line_buffer; \ - HANDLE read_raw_wait; \ - /* Fields used for translating win keystrokes into vt100 characters */ \ - char last_key[8]; \ - unsigned char last_key_offset; \ - unsigned char last_key_len; \ - WCHAR last_utf16_high_surrogate; \ - INPUT_RECORD last_input_record; \ - } rd; \ - struct { \ - /* Used for writable TTY handles */ \ - /* utf8-to-utf16 conversion state */ \ - unsigned int utf8_codepoint; \ - unsigned char utf8_bytes_left; \ - /* eol conversion state */ \ - unsigned char previous_eol; \ - /* ansi parser state */ \ - unsigned char ansi_parser_state; \ - unsigned char ansi_csi_argc; \ - unsigned short ansi_csi_argv[4]; \ - COORD saved_position; \ - WORD saved_attributes; \ - } wr; \ - } tty; - -#define UV_POLL_PRIVATE_FIELDS \ - SOCKET socket; \ - /* Used in fast mode */ \ - SOCKET peer_socket; \ - AFD_POLL_INFO afd_poll_info_1; \ - AFD_POLL_INFO afd_poll_info_2; \ - /* Used in fast and slow mode. */ \ - uv_req_t poll_req_1; \ - uv_req_t poll_req_2; \ - unsigned char submitted_events_1; \ - unsigned char submitted_events_2; \ - unsigned char mask_events_1; \ - unsigned char mask_events_2; \ - unsigned char events; - -#define UV_TIMER_PRIVATE_FIELDS \ - RB_ENTRY(uv_timer_s) tree_entry; \ - uint64_t due; \ - uint64_t repeat; \ - uint64_t start_id; \ - uv_timer_cb timer_cb; - -#define UV_ASYNC_PRIVATE_FIELDS \ - struct uv_req_s async_req; \ - uv_async_cb async_cb; \ - /* char to avoid alignment issues */ \ - char volatile async_sent; - -#define UV_PREPARE_PRIVATE_FIELDS \ - uv_prepare_t* prepare_prev; \ - uv_prepare_t* prepare_next; \ - uv_prepare_cb prepare_cb; - -#define UV_CHECK_PRIVATE_FIELDS \ - uv_check_t* check_prev; \ - uv_check_t* check_next; \ - uv_check_cb check_cb; - -#define UV_IDLE_PRIVATE_FIELDS \ - uv_idle_t* idle_prev; \ - uv_idle_t* idle_next; \ - uv_idle_cb idle_cb; - -#define UV_HANDLE_PRIVATE_FIELDS \ - uv_handle_t* endgame_next; \ - unsigned int flags; - -#define UV_GETADDRINFO_PRIVATE_FIELDS \ - struct uv__work work_req; \ - uv_getaddrinfo_cb getaddrinfo_cb; \ - void* alloc; \ - WCHAR* node; \ - WCHAR* service; \ - /* The addrinfoW field is used to store a pointer to the hints, and */ \ - /* later on to store the result of GetAddrInfoW. The final result will */ \ - /* be converted to struct addrinfo* and stored in the addrinfo field. */ \ - struct addrinfoW* addrinfow; \ - struct addrinfo* addrinfo; \ - int retcode; - -#define UV_GETNAMEINFO_PRIVATE_FIELDS \ - struct uv__work work_req; \ - uv_getnameinfo_cb getnameinfo_cb; \ - struct sockaddr_storage storage; \ - int flags; \ - char host[NI_MAXHOST]; \ - char service[NI_MAXSERV]; \ - int retcode; - -#define UV_PROCESS_PRIVATE_FIELDS \ - struct uv_process_exit_s { \ - UV_REQ_FIELDS \ - } exit_req; \ - BYTE* child_stdio_buffer; \ - int exit_signal; \ - HANDLE wait_handle; \ - HANDLE process_handle; \ - volatile char exit_cb_pending; - -#define UV_FS_PRIVATE_FIELDS \ - struct uv__work work_req; \ - int flags; \ - DWORD sys_errno_; \ - union { \ - /* TODO: remove me in 0.9. */ \ - WCHAR* pathw; \ - int fd; \ - } file; \ - union { \ - struct { \ - int mode; \ - WCHAR* new_pathw; \ - int file_flags; \ - int fd_out; \ - unsigned int nbufs; \ - uv_buf_t* bufs; \ - int64_t offset; \ - uv_buf_t bufsml[4]; \ - } info; \ - struct { \ - double atime; \ - double mtime; \ - } time; \ - } fs; - -#define UV_WORK_PRIVATE_FIELDS \ - struct uv__work work_req; - -#define UV_FS_EVENT_PRIVATE_FIELDS \ - struct uv_fs_event_req_s { \ - UV_REQ_FIELDS \ - } req; \ - HANDLE dir_handle; \ - int req_pending; \ - uv_fs_event_cb cb; \ - WCHAR* filew; \ - WCHAR* short_filew; \ - WCHAR* dirw; \ - char* buffer; - -#define UV_SIGNAL_PRIVATE_FIELDS \ - RB_ENTRY(uv_signal_s) tree_entry; \ - struct uv_req_s signal_req; \ - unsigned long pending_signum; - -#ifndef F_OK -#define F_OK 0 -#endif -#ifndef R_OK -#define R_OK 4 -#endif -#ifndef W_OK -#define W_OK 2 -#endif -#ifndef X_OK -#define X_OK 1 -#endif diff --git a/include/libuv/src/win/req.c b/include/libuv/src/win/req.c deleted file mode 100644 index 111cc5e28..000000000 --- a/include/libuv/src/win/req.c +++ /dev/null @@ -1,25 +0,0 @@ -/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include - -#include "uv.h" -#include "internal.h" diff --git a/include/libuv/src/win/timer.c b/include/libuv/src/win/timer.c deleted file mode 100644 index 27ca7716a..000000000 --- a/include/libuv/src/win/timer.c +++ /dev/null @@ -1,195 +0,0 @@ -/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -#include -#include - -#include "uv.h" -#include "internal.h" -#include "tree.h" -#include "handle-inl.h" - - -/* The number of milliseconds in one second. */ -#define UV__MILLISEC 1000 - - -void uv_update_time(uv_loop_t* loop) { - uint64_t new_time = uv__hrtime(UV__MILLISEC); - assert(new_time >= loop->time); - loop->time = new_time; -} - - -static int uv_timer_compare(uv_timer_t* a, uv_timer_t* b) { - if (a->due < b->due) - return -1; - if (a->due > b->due) - return 1; - /* - * compare start_id when both has the same due. start_id is - * allocated with loop->timer_counter in uv_timer_start(). - */ - if (a->start_id < b->start_id) - return -1; - if (a->start_id > b->start_id) - return 1; - return 0; -} - - -RB_GENERATE_STATIC(uv_timer_tree_s, uv_timer_s, tree_entry, uv_timer_compare); - - -int uv_timer_init(uv_loop_t* loop, uv_timer_t* handle) { - uv__handle_init(loop, (uv_handle_t*) handle, UV_TIMER); - handle->timer_cb = NULL; - handle->repeat = 0; - - return 0; -} - - -void uv_timer_endgame(uv_loop_t* loop, uv_timer_t* handle) { - if (handle->flags & UV__HANDLE_CLOSING) { - assert(!(handle->flags & UV_HANDLE_CLOSED)); - uv__handle_close(handle); - } -} - - -static uint64_t get_clamped_due_time(uint64_t loop_time, uint64_t timeout) { - uint64_t clamped_timeout; - - clamped_timeout = loop_time + timeout; - if (clamped_timeout < timeout) - clamped_timeout = (uint64_t) -1; - - return clamped_timeout; -} - - -int uv_timer_start(uv_timer_t* handle, uv_timer_cb timer_cb, uint64_t timeout, - uint64_t repeat) { - uv_loop_t* loop = handle->loop; - uv_timer_t* old; - - if (timer_cb == NULL) - return UV_EINVAL; - - if (uv__is_active(handle)) - uv_timer_stop(handle); - - handle->timer_cb = timer_cb; - handle->due = get_clamped_due_time(loop->time, timeout); - handle->repeat = repeat; - uv__handle_start(handle); - - /* start_id is the second index to be compared in uv__timer_cmp() */ - handle->start_id = handle->loop->timer_counter++; - - old = RB_INSERT(uv_timer_tree_s, &loop->timers, handle); - assert(old == NULL); - - return 0; -} - - -int uv_timer_stop(uv_timer_t* handle) { - uv_loop_t* loop = handle->loop; - - if (!uv__is_active(handle)) - return 0; - - RB_REMOVE(uv_timer_tree_s, &loop->timers, handle); - uv__handle_stop(handle); - - return 0; -} - - -int uv_timer_again(uv_timer_t* handle) { - /* If timer_cb is NULL that means that the timer was never started. */ - if (!handle->timer_cb) { - return UV_EINVAL; - } - - if (handle->repeat) { - uv_timer_stop(handle); - uv_timer_start(handle, handle->timer_cb, handle->repeat, handle->repeat); - } - - return 0; -} - - -void uv_timer_set_repeat(uv_timer_t* handle, uint64_t repeat) { - assert(handle->type == UV_TIMER); - handle->repeat = repeat; -} - - -uint64_t uv_timer_get_repeat(const uv_timer_t* handle) { - assert(handle->type == UV_TIMER); - return handle->repeat; -} - - -DWORD uv__next_timeout(const uv_loop_t* loop) { - uv_timer_t* timer; - int64_t delta; - - /* Check if there are any running timers - * Need to cast away const first, since RB_MIN doesn't know what we are - * going to do with this return value, it can't be marked const - */ - timer = RB_MIN(uv_timer_tree_s, &((uv_loop_t*)loop)->timers); - if (timer) { - delta = timer->due - loop->time; - if (delta >= UINT_MAX - 1) { - /* A timeout value of UINT_MAX means infinite, so that's no good. */ - return UINT_MAX - 1; - } else if (delta < 0) { - /* Negative timeout values are not allowed */ - return 0; - } else { - return (DWORD)delta; - } - } else { - /* No timers */ - return INFINITE; - } -} - - -void uv_process_timers(uv_loop_t* loop) { - uv_timer_t* timer; - - /* Call timer callbacks */ - for (timer = RB_MIN(uv_timer_tree_s, &loop->timers); - timer != NULL && timer->due <= loop->time; - timer = RB_MIN(uv_timer_tree_s, &loop->timers)) { - - uv_timer_stop(timer); - uv_timer_again(timer); - timer->timer_cb((uv_timer_t*) timer); - } -} diff --git a/libs/uv/CMakeLists.txt b/libs/uv/CMakeLists.txt index 5c3d28957..53c76c4dd 100644 --- a/libs/uv/CMakeLists.txt +++ b/libs/uv/CMakeLists.txt @@ -30,13 +30,11 @@ if(WIN32) ${INCLUDES_BASE_DIR}/libuv/src/win/poll.c ${INCLUDES_BASE_DIR}/libuv/src/win/process.c ${INCLUDES_BASE_DIR}/libuv/src/win/process-stdio.c - ${INCLUDES_BASE_DIR}/libuv/src/win/req.c ${INCLUDES_BASE_DIR}/libuv/src/win/signal.c ${INCLUDES_BASE_DIR}/libuv/src/win/snprintf.c ${INCLUDES_BASE_DIR}/libuv/src/win/stream.c ${INCLUDES_BASE_DIR}/libuv/src/win/tcp.c ${INCLUDES_BASE_DIR}/libuv/src/win/thread.c - ${INCLUDES_BASE_DIR}/libuv/src/win/timer.c ${INCLUDES_BASE_DIR}/libuv/src/win/tty.c ${INCLUDES_BASE_DIR}/libuv/src/win/udp.c ${INCLUDES_BASE_DIR}/libuv/src/win/util.c diff --git a/libs/uv/uv.vcxproj b/libs/uv/uv.vcxproj index 0dd0d1dcd..13da7bc22 100644 --- a/libs/uv/uv.vcxproj +++ b/libs/uv/uv.vcxproj @@ -271,13 +271,11 @@ - - diff --git a/libs/uv/uv.vcxproj.filters b/libs/uv/uv.vcxproj.filters index 293b3109c..73debb1c4 100644 --- a/libs/uv/uv.vcxproj.filters +++ b/libs/uv/uv.vcxproj.filters @@ -77,9 +77,6 @@ libuv - - libuv - libuv @@ -95,9 +92,6 @@ libuv - - libuv - libuv From 0730a57a78fd012f4a725ccc5fb056387273f69a Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Mon, 6 Sep 2021 22:37:53 +0300 Subject: [PATCH 115/117] finally --- include/libuv/src/win/core.c | 2 +- include/libuv/src/win/getaddrinfo.c | 2 +- include/libuv/src/win/pipe.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/libuv/src/win/core.c b/include/libuv/src/win/core.c index e53a0f8e2..b5958e75e 100644 --- a/include/libuv/src/win/core.c +++ b/include/libuv/src/win/core.c @@ -31,7 +31,7 @@ #include "uv.h" #include "internal.h" -#include "queue.h" +#include "../queue.h" #include "handle-inl.h" #include "heap-inl.h" #include "req-inl.h" diff --git a/include/libuv/src/win/getaddrinfo.c b/include/libuv/src/win/getaddrinfo.c index dfab860a7..c9df0c3f2 100644 --- a/include/libuv/src/win/getaddrinfo.c +++ b/include/libuv/src/win/getaddrinfo.c @@ -24,7 +24,7 @@ #include "uv.h" #include "internal.h" #include "req-inl.h" -#include "idna.h" +#include "../idna.h" /* EAI_* constants. */ #include diff --git a/include/libuv/src/win/pipe.c b/include/libuv/src/win/pipe.c index 88ba99bbc..c95bf6de3 100644 --- a/include/libuv/src/win/pipe.c +++ b/include/libuv/src/win/pipe.c @@ -29,7 +29,7 @@ #include "internal.h" #include "req-inl.h" #include "stream-inl.h" -#include "uv-common.h" +#include "../uv-common.h" #include "uv.h" #include From baec999e383871cbdc67a60f01d696f7626faa16 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Mon, 6 Sep 2021 22:49:40 +0300 Subject: [PATCH 116/117] one more --- include/libuv/src/win/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/libuv/src/win/core.c b/include/libuv/src/win/core.c index b5958e75e..a2634f062 100644 --- a/include/libuv/src/win/core.c +++ b/include/libuv/src/win/core.c @@ -32,7 +32,7 @@ #include "uv.h" #include "internal.h" #include "../queue.h" -#include "handle-inl.h" +#include "../handle-inl.h" #include "heap-inl.h" #include "req-inl.h" From a76a7f9f6b9018ceb732b84eeabb9327ff5bb524 Mon Sep 17 00:00:00 2001 From: Aleksandr Kuzmenko Date: Mon, 6 Sep 2021 22:56:53 +0300 Subject: [PATCH 117/117] oops --- include/libuv/src/win/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/libuv/src/win/core.c b/include/libuv/src/win/core.c index a2634f062..735b259b8 100644 --- a/include/libuv/src/win/core.c +++ b/include/libuv/src/win/core.c @@ -32,8 +32,8 @@ #include "uv.h" #include "internal.h" #include "../queue.h" -#include "../handle-inl.h" -#include "heap-inl.h" +#include "handle-inl.h" +#include "../heap-inl.h" #include "req-inl.h" /* uv_once initialization guards */

;+s0)O?4jMGXN_fjD^yY!L#d=Q=9Dh2|l}AL`Pw{cu#w0 zP`t01ks?AT=Kr1$H$$F-=byj>x~$lfEO2D0?02A}Y__+oFx@xbj#lhPEmGa%(73p_ zH;fxhqsbMWg;H-xoj7antxcLQD>M z9Kgy$cUQ!`TvYBMn`W%dZF1G*lg0E*b-t@++tN3M=TZ+5-qe~wIDW;U8;vj}J=C@N zDUzV#CnK=wJxOf!2KE$cz{nUSu$~t$6PQFe*oF|aEb@c=8htAI?vlS-QIIA%Ss8z( zWqZ4|XL&0Lj(9P>!y!)g-i!Dx!J72oFsi4b|7uUZmKDy4bp$SoXQZ4z8L$mE=XTML z+2OG7)Jbf`PQ*B-!ovYiY(ksT_G~xmpakWmPkF%e-q<+!e7hpr%7DwH%$D@xoE7EN z8I92aV&*N6V7kYo`MeB+U|amQ4rh32{%yeX8R-;rlX4!KlVYc8)Ncrwbt;$TBiZ9u z?EbjD6&{bUVN(*fELP-o#jXt}sD+)N7;ra4@w`2|UBavG_e< z>BBz}V~hFj}&?URH6l zVia6$65TV^@~%PZRC#T&<_@}s*}43`C&v8d<29LX^KPOaE{$Yv{ysnJuW03LlOB{G zDg;(6MoG$g?p>HE8?dVryvU{%R$Dy29mMfy5RTD=k$uwQ?eFh6T&{;jUiC!IFML8f zzG<_yRW$8Wi;ZurW_~g;{$53$%9+N^;Q#RekL0ss2A~crJ>Tq~*YBKE*R9Vd)IPS5 zz%Iq?HJ-Jvl$X>(I5QOKm3@v&vG$+_CjWTd762$taR9{*IjXlB_X$z=Y;*|V_!1ft zf&cLA!&6_?r7T*2OEhNfcFS*HA=lk4hO;((!(AiPUZ1hAqMiHr?i0hWW6h6_EjG9F zJ=1_s%r{&~ok92c37(49c(zKV4|*)ES-j&k8A^Xa$;;-|IP-C=IbZmarEnw#aYDq`ry#2OF>eKu+N}U?2k8{P_!v>J?=vMMMV#N9pka)aJ_Z4OBMtE_ ziLO8*k_?sLngXZb$sItZib^kQqeG2&Er=b?j{Z|IY$g>q$TMOf)Cd zp;=#pT?lP_+#loh7%t!^XZJ4r>FWgUhOW=@ZXU|qZ()bqt%>b9>`lN+JE0>^`bZ1L z7lVCD@gCoe>4V$FhGb<^a@Us3PZ>eC{f}u9)C-W#n#%~O&wX#|uX*VcTw#)TNO>{{ z1RH7}!Xl?e4q1o*npgO+ni###GBh;~-#Jfc%7p)&Am0CY> zv9ODINnHEeeH^^HF|EA7h`04P{^Q@rs|Bt#tC&1x*A?H6Ivf~DD0tv8w z<)7WcV+3XROLn#OzJ@@8YTw0WOkNqS$?9FElR%)I5~6Di?0#g#h^I%xhC@G19t2O? z926QBe?-r;f+#0-n@1QEdnl-p)$MEvL%M8t?d$nCH2rMlvZ z!ZtH_uw6l-qfqeZLkdDOjz|;s(1>_(8QOb(#(R&WY>9s#F_+>E6ZD4~O)__FudBBG z7Eh8k^=F`}zDHyktBZ3A4|q>G_X2bw@-AZT4L(nY zSX3MR!?NCj`456To9Nbw?1SxeqbZ%L&Yf|~N`&U3!LReFa%tht%%O~kItcodZ(fNp z#49Z2DLwoziITs5i;Zt0qYOdb66p`!I`lUHcIKHgdSsBr3wG8AvpH zUrs0GWlM67T4HO(7zF(QM3G674Ah~DkfMiBykAT)b2yn7k=M5U%hr01bm;ob34HO~ z8saCTxo4?UyRVUh;4|h)Zj?cC};Oq{FA>2|GSzS_pX%_eUAK!?| zPh4$hXIgS}gF4!U{uqw${uwKv<(>fkbkXM){Zl(qvdcal%AF2YyI8B*-krGH-Z#1? zdtDS|9mgANPYanyrfBk@dA@Fj#cUT%Y!_F>jxC>i=oxzx z1u50UvA)Rw>H-56(xW4-hUGzNe?E4f(c%6VUo}j*sL_f)u0xO!IL4ILn15sprf28R zh4W}xJ^(VTDGgI-=1cfVQafI|#3Cx%dk-QO) z^~gxbnFv3ji@8a;?Daq)Sud|$X1a@tO=vIaok@3nODK#yzJboOsWuT2 zt*<`+lW|=8m!4^8xH#?U-Z%fuY|k)><2^nq^xG{C&_7Vu`^pup+S2Nm2hGL5g3>YR zJt2AGDuK4VY8cS^&OADNSUKbeAf1YnJ<@XwlfB!*oYKYQHMcxkqsuyNLpyBN6T5cvbB-JV6TP^t|1Z6`^6g`_@Q&FwGZml#qq`f?vhOgmmO-) z`!n5Lx&XPVKHNW=caIIl1{2d+@N9S!Xy{(3MFI8UkLD^V_n;Ycn+NtunDT4&b-h{U zFI=q@3j*d%<54dubH-8g<33?!t%&`zr1Kr@SF^VoBEYI3*rg!M&)SYZr zsQp9gb;xs2JVEmr3B}97iyC1D<|lhdBUbb3yA``9OnwRL*acD>_cnbIPad%|b46eC zY{6Wip~|VFMW%}OhT_7umO+nALY-&7HU{bEm|Gi__D3u>$x40UDJQ5}4&!hx&&F$Z z&^8b>v7h>g(~&?a0#kM!WH#`w zwd_mBeUIFkGC1E_a@FD#{?llV?IvO=I`z4s91Q~uj!tE5`eHdTg35G}AuSyJu9%#o zIYN*)?S}A8OZksjaO0_}5wr3PV__Ip>jkDXp`e?+D&HJ;vW3JvN#m?75m;1DpjR(Qfm z!4YybwO+pTOG;G3_oeQqDrLWWw{&aoJwPt7iYWgIvDHPuj+%b2Vi*ay7s}FR+~wuE z&U>hLp^(<3`Al1N&lFRezH3!8tZcMB^n{5ksC3~DN7Of-AH{;$eGA4rf5kyH%{p?u zAyD@0kR(3yq3rFLEb~%BPoovh}*5;i8vkXNpCswDj zKFC=#ws?O1rANsyaiq_?bUq?$w^|l6cbGCxmy4+EwTh~q%y>l^YJ<5PN))XJu?f8y zArtaeMkG##BZk)tR$8Dj4@?PjFW36_yP08rVGOV+z%T)RqlwgiCqZsu1OHjvE%gOP-15@b8*A=NxK?6v0{oC= zUN`%t!4i;#24;JYF#Mz=I+T7=B(+o3}`_x8FvMK#7N>E1q%h>DVs@iGl^R|v* z_dNrPYcusWhTXEN5ZFZ=Ay`|@O#&#NqHS$XYt^@HBl%960p~net5wdBja{Z`i+_X0 zQ|?-U&mrOz2?A~LzaA6t$ibzVo>4t5KYlR`*p;#+7XLQBQQY|myC)<_bjdJxA`dGf zQ>70Eb*R#m4|a)HESNn1Yt&~;0q-2ZHX@21eLiMmk2 zckKIQ7bK$W_YH&TaD_MGyOTiNTObquzLJLK7arW!)9{hj`x@{h zJSQHR#eQII91o9P6Vz@Q?&F`jBCE^*oBaV878Y!om52!sIb!W)<)0c(jTUA9QP?>l zOwV0P@4F%e2}x0@r0(R?hEEVm-W&oRWs9ln)5x1ru{QA@L)%b3{F?mG^C;(nSU&5p z)K2}?+d7@R5NRvLyT>$s0o6HK?F%OMW_9@-t3E8&APgEBc?37Hu42qSs~0{F^3+nwBGEic-v`GyD>*oM@F8o0V^Sh7=L5^tL{+3 zpjiz%q&7s63RVq{xWi?g)vHng16w`y+=2RzMYCG@EsbPdPLj=2hv>`041Wa#rJM*M zNlaC}k`-BSf-<)_PO*v-7gj@aQ48um^*pqwMMVfEVs?`^UR`1paNv;(USz{f8D1yZ zFXq?di?Z_cuZ$5BZ*O!b+c?Tp-`py0Wrr4#B!<{F=tD4jIAOt^sg1M_kH+rAF~50Wu{DEHT~Y53Rgr%Q2P1;WtFPC)L@72Fw$A&AiG zWC49i3LAsn_+Vo_>)-hD#9G~px*M}&TH%MLUT&`K zvTbS=>pHs`6y56F2Z4A_3-w z*6rQewRmI_i6dj%M98r`CImk`UkJ}_bj5&t=loLb43iCjjysC5f|gP* zN;H`|Wt1GnHB!t=O%&TGO+xym#oIJZtha1%>B^47JM?Lj>)P#Ze?9Iq&9^wnY}S$g zY}}m)o}4g+tGhe0{|hDzdY;JmYOLj-n!ui&Uq^bYp@Zvu5_es-w(PvX7{Rscua3i4 zznF$HW_Tz|uxm^xLTHAl+wj+bKF>(S0MQUE=II)3Lg+REA&u9B4t6!GZ>_3ib87!M z(V|Z#Nya^(LMX-SK@;njNU<6OR8mt-B@A|I+*{GW@_7pJljId!Z9ZaYBGMf};dm3H zs*Q%HH}s172I{2OYr+OZu3#yIf84nD)!LVJd5mbNubgz zv7)9(M=YH=R?&HVCLm}~Hzz92$UoA5`Ru@SIDDVlGA~r}V>y^bzcGNlu-vamF7yVB z3e2vG0n-Es6k_k*avQh!TUSPE{_3L5o+LJ9 z_EChvr_;DO;_PG{uN!;KjZXkpqAE-$(6{=K-i~KOTEC-rw~@wf*!7+Z66`%wJ-Ye( z?SG%kT%Ga$3hAVLV(T8kCiNZqnv6qyZGGgsRZP3HB75%$McTP;b$bSE+_r;W)|8y< zE!8qHW@LuomkTjH`|@V~uFt&_8nn{wWyHG_L!7$jCM>Il+jk$)Y(FpX@{TVDsq3(L z5DcO1INS=}We^vPX^=B(UE>Sg?mK%O%x|1u`KF=e8BX3#4YJSAMi&zac)7p^{KXPC zcNA{jdl>4ZX& zZf8Na8Rccct-4bS@$O)BLtu5kvJe7AO`1iF;J3A3DHlIJoZ%|=;f!4Jh6U|*fj|1d zIzq%twY`{4XHE0-5-}Zs*t%~$;65FxrJ3mA;-NuL+yI;r9zDsJN}}ZUgxI;hwft;) z=ZATr%RInh#eSXe;O^x}@8Ta`&He#907AuYVQDqFJpX;_EE0;>qNJiyAP|SZI<8)~ z;QfDD(u8NEsF`rhlPl|Ec1NqfkEos}$*MsJp{iL)VXhh#!hYc{VhHv@$4hG0>R1%v z#*Ez(E`r27yD%@6X&k05+8Qcc!wC&`sbfjJxkz%GAccgrJGQSgX}Dj!{DU{@g7LnRQ@MCry;>IQw8HAr6$ z0)3>h*sR*=JeuJN!KcPG-_=nfAFN)HTw+V>mTQr+@?{dKA2DiCJ6~$k@iz{%&yL&T zZul0d=;!&cOn%^O_ke1&^=+yB4K|W-!+-;5e#^|cRyl3{oM}C<8O*k7o8CW8Z*iq2F4;FdhH&KG zYJT4bkaTW`SqPZ~mp&z7iN&@Bn)m@_rbk6aA2cb-(l5X#Ftif2$5ORMkiD!w(d??$ zMOi;HR!JZ3%|TO*P$uvVt>C#kVgbT9tSw^B2OB~ydlG5STUcmsC&8PG4b2jrb(Ef= zIu~)8DA?t7ZfASXl$p-7en$_W9k3DQyII4^yp1;&wdLlG`G-pz$4ZbbPyK~bLUnUb z|MRMhwBZ_7LJ;xOsN?)o*V6fwo_6@c@ORPUF00TZYOyZAn9K=KZbyRl|50c^5}`eG zVP09=X4}&=7kCygL1PImi>4zx_I`<(F=D`u zE?IT)hGweLZ@JfJmpuh>c+;k^1GGj6+3(e14}DZt@ls2lSL$R{XK{A(Bi1=P*1Dgx zdc9`joAg$C>g>aNuRz^=p6I@}?J{;vJGV_ET6pS#_-vK?#zYLhN=^Tr4ODvld!tU& z)uP);*t!&+{kH{!%OA5l-=DEpC(pdY@gI=;q?E5AtTo9&CP(*>V{d8)c>GRX&YEDw zjFuB7I#m)*Sdo5f!LJ}FVZp2>gP{QL=}N+A=D>t3L?vOJO;8VW6qGVqL5zDo-y*(caxTv~E7bU9#F%OYtzw!k zIS6;5qWPMgrbRc;^9!IfMn{&E4vmF|Mwn9r2>e$Gn!9-aLE_rK$)JfvON7a`Qli5o zu76}*rei(mCVD|$JYch#+;&a6c)o1yBE9hA@>b$C63^I9l%6}saJ)Tlp0fxL7OWhg zPeAyYY{x(6sWTQh3uQsL^h@(M;^kCxRdLp)L>+q4WcM*N{>TTof-&13t>}4NLwRHf zhd;Pqq*E7F9TKg_%G?YvBb&P6WCQ#J-Z*RUlsl0^l*wD-KKSgFGz?T4ZpqD!`GZZW z8@j4!TGF4r0te8Z87Oo<;+2t;HEdkq5G(#{HbUB3+?@fMgBSx-Sdu6qpZRXRPKeYS z`~A^sh|*8}+rW}S*+A~WAyI#wzzJ5N0mW9W_1^%n` zdm(yDkX}^GhJNRf#)X#Q1O{lQ-X`pU16r<#sq%JB&h5ZgbUyJKxMvwvQ|6 z{P%^yBus7z2~P}7M8=~kIAB-BI4-yYpmYvZ3`w4ALT9UAoTZwF@9kRN8N55RUX?R~ zz|Ao1XKI+kVMBpu`iTd!CRXFxR)@U#GE}Vimi0N*KH*KBzj#D)5C;OkB5)y~DcDm%qP zqvFoCrPGU(c8h=gKd!#TpXopTm!wrfB^Db~ic0J&g!hDE+VWVl=~&unOlfN?pR2v)VhAHb?gAT;&u1h_z1HPo(4b_v{eQF3>o@|`Q0brvG(%q1}B5Kz%EArGPA@%TjG^z(OQKBeV|}#&t=tN zMq9s-*N@QrWUj24YQA!)_SlVPA&SDv>yZm%nEdj_vmR@Gc$*0NAJ|Ne`n_Mp?36A-#_#tHc$`R(Y*n9*UdS_+Dnr2 z#0MA0if-d0imGQ=TXy&D22f|bT%9V(R#txwY4wMOscqzS!1e5H>Upv_tF04Hy6vJE z&e-WCayKAkL72nJjx#Y!Q}Ur+x$ZXu;E@X(K|)81sHmRaIYJKcN3gDcAhc(3bEMj< z(C*KToZ6|cOS`OnTP?M=z5QpUU6#&R71!)iO!)~-aVDNpng;FzUo9*XyWF}uoKac< zr*2FYuukTg0K@HmQo>P*4ydKGmJMAZdu2n6MC3CTsRX@5veMaPwndIOV&yNnU&Ek- zloG575(hG{lKbz2c6Tp5`1{YoLDjMcGKr}|8 zh;E4;kr0MyDcV1802CH9!@1@%OVk`p;r~**Um+B1>b*c6z@=j1%)}%+2+;^bNITMz zKlTh9G(%xfkp9j1^9Ow9UK+(Z!ntZg6?WjCU47Ss0~-O^^j`*rKSI+yyYpvQ_8_#* z-g15Y{3EO4JUt-zHfW_j!P6lF^!iuol~nHUHB~oJghEaKCHl}CUF|-~a7TS9e`ay_ z-Vf|Aw@JMx<72kFJGXgHq9u0c{Bl>DQ1_!k~c++?`5h8MoHg5`)TLNlWm~xG%fDCwjv^G>#Gm*nw2f~u?BrT z8N!~p_p?hRtDo%`2n%>CcKH&Cr?^`lS+G&R#mUZ(-DB`5S5_+ZyL7>_yi^g#FkeMS z*g|SL&jmcyPJYdcEupFF(e6obQ|)GqV{vW5GkJ)hORTdnZ?>|sKV6}=6GdG8Dcull z56D*98OfX%w_@YF5SNx%R#Oa&8vF|;?P3N&$!9jM@uw_;DdaK#UM(b%buyK_ucE>` zCCOfEwCuye(O6@IM!y+mNO;z9i44jZI_cFb&hsW4Yuk7PNdZvo)`fKyzCY`B_lJyu z$Yex7u5|+R4VtZdpP{KRE2}ya=5hW2X}2=4wxugb*1rz*|g*%3c>H` zT$(nPzZSgi%m+4Yr z=3b<|)G-KN*GA3$-K4)^j=C3WhuAXeS?*dcXInS8s@%n+kbZe(qtfRMEd+{+D7-6o?ZWUd|FeL7*IY2 z@F^DFH>;Kbi_5gE(+~75V?*f+>YHNp)_sW7yn61f{`LE7UDXqxAEM>eXYL^EZw1U^ z1dyHVy-?Am@1aJzme|JQ0l(7t!mXxRcLyv)!`iY}F-+dqSyU*L` zA_F?fhl+xOs171T+*8$!vuM3`g>;f4`uW0ZHN6?39f`7tm3AjEimznacNfl+iVY|h zkmf|twFR2NGvP~bfe<*1$zw+T6bYw-z`AO(9NHO29UzjSsltD$IRxNwMhEEWLpod^ z;p>QoF)({AT0R8qO_LIFI6Bq-7;MXaDv?N49c2sw$VsMHNwzZJ8S77i7{q`R(f4}e z@GL+rnz1LPmNF*G1pg0gCjgk?ny?B?;^mh6gjn@+U8)6`)Rkq&c1?Sp`A>CA( ziu_jPST^|N%<`FaU1qHiDH)}JIsm4jG*UigIk9;BPO*=D*6Z9KA)FBPAqv78H?y9@`rDz0x77gzoDv7Q-D)cH8}elcqT)MGylD|0`CP)a3IN`IA#FZkcq}> zjm@hK3rckV*XfAl!v!8}dzPG)c1e)1J+skXH6!7mR<`>(4*K%0Sf`WUuiA^F>gN*F zAx;H;6MAVHXSQ<2y+i%8?0mEM#ZT1Ii>t>*+>qVsGniG#y2wupdL2(|F>^issUCv; z5|VjD4~5)+&PLat-d$#sbJ`v_|ID(3N=e?7D0_e-~(*1o_s9v(BDl5l9YHC6>+A5;*85|X%9 z3ZJw|$4g=JRQ(;x)^e>NpNcGaLg4TMeh`yxD8lsLcS#X_T>%br_5FFE|CMqzkA(;e zX|=kJE`kK{`cI2U&Z>;N2C<3i5LUWGO(&yVe?tuVNSmNs(9e8`A+dmx*#jxT%Ugc$ zN_LN=si9~}+8dD#4KqnQzdc4DwGB{iU?Ki*+hPl#nz5VD>UAQ)e9q*x5q{RnmuQXl# z&XKX{4(Y>3==Ak}PiNoVvRt<-jX>B1sI>XHi^ob~K`o9^Dw7*mY6nhhZ0dJlAJp5Q% zIiW#_+XIZ7=(PnE@A;l7VH7WH>luUxy(6gd1i^W!M`6L#Yt zo%9DF({hNe-*xB81G?6*9kyX*Ha(LtXE$~<{Z7Yk0CnoJjMsRfM(6|{pT&b92-i@>Z^t$%l%`S}q3{`0-4-1fJtx)tb)O+#NF5PP$tA-SwnQLXkH2*^Yd z;zy12@3HGp=4f|AS6ZH9iA-tW(d>ux+0Cztqgs{S4YQTOD|`!hI6Ec3T$`GoN6#mr zP8&m!#NJi3ub|fe0eh5@VE2Jx-4gWT@leX)(cnvIjn_W}NA@A*Z)B~l$9+&Wh!rVV ze6_nEs_1rFF>68hmborR-@e{S50V-z1r2iwS-F55W9yrqP1A7D)9jUdZ8dhjckD{Q zZeBuDgcnn`u#eI%i)301tLue#eRo`H#-j2ed(8QDq1ctz829}^J-a`OA)|+r4Bj;S zS&vJu@K%H$u(IY4%jq*tW9`~M;2UNR8EhsAY>I$+TDm5Fors4#Cr-P9F15e4TgHGC z#N5}=tTXKs)MGW@tK%EAu@D7tFE@pwi3)m?v;CxD&c8|K{>lde7(~aN_E?TWxDpVp zb|b@|sS$y2MCa-YeQNJ6iEivqB3DMJh4~jZkR?~Mq3D_6j&3-*VT>cC3tRdwj>7;D z8EAH@BqjrVq$a{Z6zV?t&no(Xm}-eZs7zez_rU4hUE9cokLxncL&Xm}K6PN>gt-Eh zM1h}OIz1J6wyXVhyVV8f(`+sS3y@Lcnj9M#KC{r#=u259T%y5^p*_-L>BH3OI z6uRVX6FxLKJl5s)IhIX4%FO?uL&^LuRX%YB=q;gp@F#H$MJNkclP%X`Q;IT6tDh`4 zSZv9MuNSG5PNmJR`~KUq@blGQf%t7rTkJSt0Lj$`XeAWte%;sk>e%F`@7!-*BDJ;f zMFW>Bn^6ch?5commvot%7q5+wat(`F?YS6xm_r82{BS@7Aa&r)!-$=ebsLY`S`f8l z(`s_X`K&BAzKIYlE?S(9t8xsJo}9*3=Ri$EsS}GO-jzbu=^?8}87B|Zf4=WQpLbw9 z_)#|CQuRF4&%W5=`qGaWaBy92!dUKlwf5;bW3A!LzS-p-KjJGuFD3Mqcp$H_u=&-) zUc3EYA8mW%bNhPpo5G4&Ckk}+ekf=AlU=5GF7iXIznrH`1!7ONk!c8gz+*eLWd*Kw zTHyPOa#L-hOIu@Vph;+b+|A^l!EkNVZ&NxD4af*Q{-Q(-yIS7sweWz5y!>YU){_K# z$4BNL4BU=q>oVR-PQOq%HtaIp5KEE_i;&3@Tx^s~GKe6XD%Kl|#2kv>ca8TgVnBUc zo#nC0guc2L?Svpk!jVuzb*6qn4JXfeX7w`Cl`X_09v!_tgkTG6l}FC07;uP?bV_E) zBq#GE<8*@cMF#4|ZsO>#O}hUl5Ow5CI%lJ6w#Tl$sr_vYQyH>m66-Vn$^MNUS*ji% z-EqvOj;ar{zw-!?sZjOA7zCC@c?YGcYEi&Ty0faFIE;cXBXXty=1rEK^qv=k1XKG* zS|vHa%y}bhqDZ(Q79294a1kD)X3OR+#cw@Q1pcEm0DPqRv7%7CNl2tVaM~{_3R{&y z%NEM0NY-DeVQH_f(|me*7drr(Xm@FE&haDYu$wo^%5>%hS=TsyqO%(Ol+67edGR<( zhQx0B3(J=w-t<*A9U4ZhZI8jZbNrkuY4cZ9Y_P{v`ZLTk{d|vGr1u?xFGe<8c}vg` zv;%&4XSD|a8!4m}aSZR+9ki?4CpTW@px??J@X%&x04ZS=jyhk`~TeBw@&u^UNOK`m7A1rS`S?C_1qdqvtjZnwP#n)AI8`G^}R4? zloq;vcN{1YkHfw`HyJB-yk0j|RmcVIw`(Z&8oypc>qsc6!J;uHl2czD_}+#^X_+%HS_Y_#p0%K!M_`xXvZA*AdRq z>D<(dc6`kPWU z6oljENlG-yFgl>V{J&lSI5YB0+lb`h7H5B4DNzarC+u%;cX^Y3nzz(-NOuhn315f| z6bfM~d+wT8f&^pM@|J4Z{U(j*f<{7NrEhKfVb^;;nwaHsK|M1Q-9?FS+h&E9mw#07mXgs zF+9pB--LxN?XveyrY+a!MqB1;e!Y8E*qfFbA(+KFW4b=1qp~k?eV?8>i)5~erNQK) z`)A}y5+SUgDow<=d!D|R-kOGS-{+xf<^}+|*aR+ZYdCyO6T&rT(7$}ty`()akzLD- zna*%-%;O$0M;OmTKCTPu*5FHC>=Y`184Jgj&ac1Ua0#=4=mt84*&ujBN}aXCCl#F3 za$4MshE7lFno=x~2`XBwxpKqZZe65&z2{=k?!xqymC!$Exjvu3;OH#uge^SZF9Vx-EjbmNWW2?Q3`Ep{I}FY zdWWG0d)#blIK&scmRe?iyD=ZbXOF2p9C+~?{-Ay?c4GgGl;fI{TEf#}*l2IKfd5b% zBer+4xGq%V+Y?Jj810ly+0&Mg)x^f>@OZYlP2%MGW?Pdi+W7$ds3c&3t z#W+tS-1qlBGg;0^1WuoIa{US+7Kl*LD!1`Rm%H3l=)5keOPFaXg%B7o^R(CHvVMvp zIwQVfI1>|2Ajt?R)i4^jD_IFnq4vpFT8lxaPe*HW4=0;PsKMM>Dz(j^>CsjZQy`K} zVVxAAat^LGOU8DL#R^X2a|gs3{_T(k|ESK5?A zKXdqGCcLKelQqg0zJWns_GMkEV|GZ$-Pnz?66 z0o4-y)sgVn>GuO7!os>|dR94BI+T}|6I)X|nba4<1fPbvlC9tOSVS`!L0%R#68(`T zStedPg6I}Ra=s;c=ulRK)Dz1g*P*)vKC?DErsL3E$Vrz;icfP&UkWszi@Jg>C1mwl zZ^^18(XrejpB33HNCzYw6^`9TSs?tlOY-0D>z?V*dM3W1%ND12USis?Yb#g|9KE?N+1FiWZUxv-Y5onXh3}Qi-^x9b3za%$FAWz~~G% zYobCwN8QF6to7WfVN>1QWgb7qHom16axKXZe}oK7g#Y;qeShjieMIqsv#EJlWDYxK z%IAZ8Hu8r)#$6{j0lM=VCeVu0W0sb-4i*rCr5EOq^ncl1sR=;!BmHgIu& zS8?w#j$XW`A*u_og?H8J{(yCr5`u&b6xr=Z!SKtxk?z3zeI8xEB>PvEvoQ^A^z+l7 zoEXUvyg&ly)58jmMY5$MEW%;;%zaLKj3vB|N!%J~hai6a`iha6B+J&1vGcG;(Af}K zN7(cI&3nJaNGyXg3VB~hZh8^yI#5rS!)Pge_5G^~@Zl$@z$R4cG9Pb*^cJvRXl4$K zr>X$kpw5NVZj@En6JVB_N5l#()q!2{Nkt93!)yL%S>`k%$tXV-UcqtH-pm!e7&tsH zy_|NjZdN2TXar;DPYQPg?kmt+h}&ZdbG}*ztw#e8wm~zDWch~KIaNCjh1vxFO9p6f z19%dDq>%&=8FGx+UC;^gz^a|BE0l4L&;%UzF@KV5usI?l#hlh%dqx!=#%k^^eW*@U z-Lz}`tW$OTL6rHguE_o*T+JD{SYiV~1bICDCUSf>F03od3ccP1g*@_Tmg^eBlZw(S zY|?FOif)$KV4S)HQEMEy_As4J)5p?(#`aaL3;!jl=;w@%8nC2;k+ceKZrpB>FqJFV zjfz<)q+%-8zG0Npdb`&E1zam+$-2!C@Egn#T^JWe864in>oIepTphs0!u^BX&eZis zV-)yEC~<5)%rwbsV(lZV*ac11;eJW&h*QbZN!FeJcrQmYQ>0e!?xPj?9mb3EmA5M> zOZrmt;8rmN&=LC;2jFa5oMS|D4>JHboe@rL~=VmFqTeg!Ta`|4`r+ zo1L14vgCAs=(A+Eo!u%fq}9!tzTk{m9r<0OJ)b{~y!TqYK|Y~s3BSkyDi7(vXFl=n zN*whtVDgXQuy(X3fDr#h$n&GoC#9NTwNkEx%ssZzd@Lp+qh@U~8Y0Dm)3BkQrI z@EPRpF4t`ok=5DqbO*n1keO~^sEiKSO>`J{nX(dKa=Mp~*0Tkx z`hBv7650AQ3unESJ41)!E2ZV&Xx)*XK$EqT*rW~IE(KG%bj=hlz=Sa>5h}VvdIdGl zj?g0QUZ01n0NBo9lq7=8w{V69I%@l$Wp>}1#}tYUbXs+{(%#n5e1^RBT>R9b?mWqT zmuyxhvO@W~)NjjJg|rD4*=KOYjkLK@jTzsXwDEdrZ+>y=qI&Av{mjSz0aGB%)FAIr z%`@!cV~Z-B1>Px5`E8-OGhZ;4y~4R-J#sHQH0`mRDBL*UJvaldh`BBy-Y%fqyWtEE zyb)nYa563urmWBd+?%^^k=(u%Db)O5>rtMTYsq3%+Q*-EMa#=r=N{tNY{TQp2M`-= zcmg*gx8%@ccsi z#yBY~S?4Ltb-k8)Oa&g4;XQF^M?wM7y}r)@D;jd#6a+i6*J-iqr0`IpiF`mS4DVGw zh8Y7k1??>J?B8$y?YUPq`AOT495|Ga+=nHks@3Lp&u5fOy#r3JHVGs8yDr~N_IT?t zGRbwuh1vi3PG^OrccTwIqBQ=s(m6XaNC~|o-sQ~gU(E5(-j9e}_F6&gOa9=ae5C|5~}e)0xKUPWJKj_$gWD&FPnalHiGw zjg^#mX==xdu-y46P?=*aWk-%U`_%=IJ1K+l_+g2w!ss`AqQlzf8cvM%=ECdJH4wT^ z;9a%hR|Ibw@{9y`Kz+VjNF=NAnWRr`BWL=YgP8jKg%J+8p8Zyv1ic??*De!AqZ*@( z=^~k9BT@(llsA7K)HxOhM@2{+MPG`0{71ERgy{8ix+rW-DA_Y0I$EVWwmo-FAAMP% zbjl>>yq8jY$cUs}&=BNH`AwW|)K)F4QC6NJT^=pz+I4&Bq$w8O=q`Wp<7z}gB=VqM zYN(1AqocDWTDV16Ld^E<8Pcn1`|v<H0G~X?Z~I*Cj8>h$`-kn^ zg!+D}GIC(-_x&rs1LL{xqWWGP3-^vc8=Lgk&U0B-grLgQ^qR!G@J;S~-s}^)VEVTT z$YD2VO)=Q4@mKnM?l^P9adXXh#(1ZEVM@@56@tjcBW>82A)^+7YO zSJNV&mcP!F+O&6Neu?@x>eq73lh>;^`ils_iqlp!m6(dIE6~VUU5Lg2!h_kYlsMQop#B3dr?SxwFF>)oA$b zS-`BBKh>x&G8Q3GSJ`q_xvWdL4j&CkIi@YwXp?s*lNVGZyVKvP-^m(`wDO!&T~vmV z?%2TPQis=6@u0=>o6syn*Ot7LE6P$Kv+Z~c>6OO@{XLx7ts-M*$Gmo(UhdBvKZXsp;}8mw0+7Es@B2`QZQJVN zDByfhp0%4nRIeM$%h%`ef=gfM9bi?6Ljo>wviyO=m_wPu+VzXcz4^iCo00|_KP8-4 zWzL$zD`@?!R}h8iyOMT8$R$c_-?7O7@az?5-1-B)w|^wZ&Eq2o#_)GrNN(^^)Ow;1 zD(VzWYiJMt)%5vRK1rf64!hC`=xEBoGNL=8we>j3n9RX3wG44ta4?^hX|dZuZ$Cbs z<2G6mF3gy<+K0RH-0zJ7I{J%89f>pw|RT zs=;5e>RyVxuGzu0Gi`lXE{)VKFYKAWEe*nz3jsm;R1Rs9BK5wBNQ-Hk^tuP)3GN`Ix=~hX8 zne=X-J%6ylRKAj<=@vZ#fuG-G?8sw?BSO=GO6euUG#OPV+~8 z%6=5Y*9`NiqG-%29i@$%i57#SV*65eKHmYDuac};{ei`kOF#_YX#_?c@^h zJLj`5w2oOJyu9gz!*~U1c?+&XG_oqGiTx%GRQtXIJ-$3z55=?B_m`ocn!i2mhJYLR<>v8x0h$q7p>E5I< zhU=3Z z!F=h9$pGTP1MSiB)yet>W*GGEu@Izq)=}#3{V#4!eqE0`zH}LXlWBB5^Ar8QgXce! zQ|UPoE;_3UnbHcqGjd>P-tO7IG+)#DRo;Dy)1ApmW6HOEfAFSD+=)Pb;k3zvqy?cr z@ty1fy!Sa~6lLEsLJ2VuY?3~jD`vt5;qGn`qXbl z^s|IeVS$aL+RlFHrW8`RNjk$nbj+}bGa+7mYWmBoUr>lvd^MxXI)3v+_LIfwvNQ+u zZ-k@B=vod``ArOz0ej!O+jT8z@HvX2z0+f@7HT@iz8K7^eWci=UQkp@;C9|QR53uk z8r?un%kw*xH9Kk_qlt=_-rAY?-&z2as)vpyT1~}(F!UE@)(~#V1fWoHVjTsuQ>+3n zYa-J&%XAItslf}d51Rb8>Dteo*G!!fBkz0etG#2UGWHJJTlr{tRxEs}l@-&_V7bg3 z91&*5sc{FDulqZKG_;r|_aVuWLF{s${kV+=bAK>uG2doGPr9tE556^OGxwk22ygv-_M{ ztz;ZPAJ1;%AcG%!$_*ZuI3J)HQClWMi*KmAE*Um^Gedb(}FHX-i{M3qKCezfprpycwy;A)7!Am}Ep{T&!2zq+HZGO;|B?V;=?@ zv_rcZ`qxndr+^hZm|B3>T4UYz;iZ>#tj^Fq5L)Vk$ciujQ6}QioF?_Z3fq5WBIcE} z$iY`-u` zm!yB6pJ1rZ$7KF83Y;zIBgVX#^P(5n1{}vVY?4qmhHp1683t_6#h&fc+U-=07(Xy4 ztksPf*^K&&`{zlulYZ|nRdjZZG;-Y0Z&X_=FwF5QZznlojq93zJ4Va?N`b&SAbi~B zkLT45;s>3D9`Fwj1pZc?&}XW5B#OT8ZmIBkeFori@INDMy6x@5KfSussr%UdMZ%B3 z8TT(bfhpJ$d)WLNs07NXtoHR}XFCGsYO5!}Gdxg{IBT%2p()>{K7eHkoJ=-!e|yMk z-v3YFEXh$S<`q=SkvcWxIU6?_S~R(KR*x*NRq6X4e{ulm=iW?R=fGg$U^bPGi$+r; z(0nU9TU~Q7Loq-p?`gCor%iJ()pV454FbOu;m|-cVpG;X^?Wu~G6fh8tEf63#j?n% zo@$w{{wYR%q`*ItS%Qu}q?681dBFNb4498DVVFoZ%L39Ku=HoR3$IH$Eq9t@5gHUq zx(}QA2@|wYDWSz;4S%$X0K*0IhksciVo?5}H$WtGOMU+H&tCiOz1b;S`Nn-Zt%k}^ zsH7e1oT0-%ssFQ93DQkkX5_cQ6E~hqh^7!`O)1|J7ZJtQF{xwi_on}j0p!mDL{kQTK2XWJ{%ERH=4~l$r$Dl_l@a*Gt z>hmX}Ov)d>7#@=kO1FdAqtz0wnt;=b>Zk^jPSiP(x@DtkBDU3P@upBQR+#zqfNI#i zn&RENj}BXM{oh0`?j!ID17+T2{`37u`1(CCYCc^9ao@yalp&@uc;UhL^bZV9_Mhgm z)cQVm`fOsyj)AO@-TueM=$_Hu3qg+27FW*6g(t~}X_t7fV zH2NP{tXR97!^q7kzn0dqPWx-S%SA(hqJKKPOc8Rw>Ldd8a6H z#0BHIJN6*5|B2EizNq^z$T+Rl_jp63oj73&teRd$i!Aj@z?4y*lAn_GXa$i@`-%DmYWR?$gr04&g4T&xbSH}>nWNgh z&@55J^7?zQyp1C&F@xVP+b>Y(7`iY@lM;7jkyPM}b>XIHaUDRIkc8C`9M0}P4&1?? zJ&>IQWl1R@eX+WZ9g!F>iYX9b;4~xR-l42#NQc@FWV|E=3Qob>FwjjhV>Z;^^nL=c zJ!=R?&Vz(4^<$8b9wU{k=$MhS)uF??Dp5m!Y@7(+6a9{Wb-g(HlIw{7+u#U-CZ{K& zmGmksaPm}H*WD$d@Y2R_SyPXIzPDjRBg$5ff2T{E+?ICN8rweXRX!a4q?D(%92WV= zCWxHUYJMvJ++8r<6l<(8ENV^m_`7Qa4CsIa{$5odC7d&?@~8E(pO+)+I^#{{qZPizcY`!7D}ifs9SmDW4BPpbZyJvqKmdn%?JKkcD(m(O(*4x?9~(o#>9 zEsZ)mDa_2-6Br*oNZKYg`Ci=YO& zr)xT*nF5Y7@dO+MxGc1)2x!MhHg}tqMz)qeK7xS)!YDU;84(G)oNh}kd!D~5>#5YU zj4&hrnZ+w*?DxAgxh%^F$?Oj6Fc^m$X^J(d>KDn8O*O?dkx$Y2@jWw10w(~FuH%e^ zK_-VXqCFIwDzd6D;G$id@h2(VcFl2_$xl`rkB@^ z0lQy++HRxXikYf6Gvh3MKPd^mvmj~jK6}kE{70M2UWF4!Br#e^GFM8{E~uYzO;Qq4 zppheTNg(3Vi2|_2=9)=&iPWlg=3s$FeUmy<=UZ6 ztDSbqw2aFGX)H;3kEqWhasdo@HNi)kbAHkR<_AKzs+vXHGJ;q4+$^>2)rjDa@s z%y5LK?(Kh9i^M>)*rAFZ|ATwn2|9cj_@RuP@Z7Bz#xm))(n;^&xaVo_`1yAwodlES zf1mOU`T3aLB`KHf_X8*Z_{Fi@-s{&BELHkds8P1GB3yf_u0R+J8jI$%>txn5=2L4% zTtp;og4S&HW=@x40xJU&Fh~i*!1)Y`26lU(qbX{+&!guiT$@JzDe@T}wlvTJSMc!v z6MY_|fpTT*H(!>i<3<;V2LIZZv`QMf&Yzc>J`Mz-W+iQ(q$q{aO<_*0sX8qEMBxzH zD8L59bI)~|6iquhlB2|-9nON6F-T5~JR}0fpv0;31U%ETmt67buzvl@SZ(fWOWq95 zana-RBF!eb0G=vh8_>Vt(@e=vfVjG+10o=uCQEKOQ&f}~BoiP8(z@6Sp!r%hXv+(z z28h(-GyEg}PqxX3Y)cUay$F2fx0NLhLZp+XiqynRW8R$Zd}Vd}-rzm2_{pcGKMH;9 zuib0XE?~b)p1Nk85GxUwQh!U8_a`bUs?LAYxn|5A#{DAs&kVl!b%KM|%x|-v=vtE2 zmmcfgs+HE9b>R_O$YVuaf7XRx$MfnrS+>mGsKZ~h2R#bCL4PN!H?TJxcjxXbUW9FX zING?+DvfWNO^FGmDruzU4#jxpL1$O2Q!H}?;xv!8QsroF8RD@AK<+k;cT zwxLp36(W=NMQMrP@;-VewNF&6F0Ug-6abdzR7#*XrH&0+d#C!OM1{wS>8+FjMnnYC znGOTaVt%dJdp_YE6t2$fX%2DTm#eD^(h&9l?l7Z=tMtMhjAJb zvP3pbixyAULQ`V~NS>$%W>g5GA|*%Unzn3}JmnKfzef{G^_o8b&Cqa2zgzd#iVz2s z1aC8h#2pr2_x3Xiq?l4(T7^uq?5=ZBctMbcZ~%^=eEo75YDs@&7Bh5Ao~3b#G%qUo zI#Gx6L{cqe!|#|%Z8o+qA_Fdm@~?kzc5gQ^4?ekc7Dv(@iC^wifphcH#dZAh@>Mn$ zdO{)+;CdVKtm+tv83-JZJRaa)haKMh(Mfw%=Ov){rLrb^|8w~MKgovh>&UqvxfA~P zoHm>-j@>1un0DR?8#%Kt=2rJzyt9;N-gA8s+=;vXqp&ep^|g(J5m>w$@x4We*FJKm z-Um?`)FMuRj@8x}dI6DV$3c|0w`*Rs+$0E=xYrjihU#BXCd{~n$K$l|OZdL)tVs0( zZa-I^h_!7Hl}6?aw}0uLGIJsRGSNetHQ~f&b`p&eeU+v-#n2A_iF*xNE?w#M8xDLx z`vp$|>mfwlbf901O&nGvomD7pp!lAO$~;lRbD~8$AupfHIN7CLjOuCVpJaVVgmp_j zinc7T!O?)_wf)vFt46IJoXG5HJEV1RtvmTN)(ijjh5yu#LlwR0D79y(hY6CFs+OPw z>uSIt5&srl0TGn2ey4%N^L8o^Sa>~IWUqq?J_MSYX_t}rf7|tV>0?yGwQ%1@=8|$5 z4GSH(XIQu+U#o^>n|{7b$fHh~J=%$4&}ZsJT&Z3|x-bX5{&m?D{bps^y2_xL&TA04 zzYcJ~WHB7J_NJIs8vetwp*P7!gb|mbrbkW480ApJExR#{*B8N`VFLbd|1sF4Vn9XC zqD#_DBu=vde;()F|49U4lODVX%Ip((vubypN7pbe4_Uz%?8S!uX4nFzJ9TIEM`tLU zF{{CF&q%`Ub*?J zh?^{(^*<*h0al~h6LGx93SeO4I`F6Wd(V@5Mmtxv^PPdu2Ni5gRw}OFhUM#2++Nb( z0sr&+J?h?53l*>E$);+kZ`RT8vZ7j(w9q!BW7w5jP39#eEwa!gwlYsL6S_2hZTwN& zd4b|~c-MG3CwEy5UB!8lO31P!n)MzaX#YG5k)M)QbjJMHg6<_?oOR2;u3DfM)^N;k zzV@7dF4vK_SNysxGl z`h~5jZK6}w-wU;J4*RF?uZqOil*<%Jn`)h0i_qqDrdW7Va+Umjy=z7E2aVLzl{Sm> z61$w#XShZ{*`406;O=0fr2>IP7D@uHweT~NL`b@|L>0-&8IVFU0+?qBc~3QCtkIf7 zYY6UZ_oZvl^u~a~vm#8|>po^z9Q>$zg64Lw80puP5d;{>$6=~rA&K;nhHdb$#5vAbF!k?#u37iwi znO`{LlF@yxBzIP-M2J%Kbx6bgj5`W7LLfzWd`vi-WWB@r_@FWZ1_ywKBpnFXRp9XE zf#MhD3#j;s-zou@ERWy|Gv|pMLq#|6>W8lR@M3o_j;{BQE|sK3M5yt=x{*yc4D8uqHiCeWv+v@UB->d5TJUu9;E(%pY-T zS%jDc-X(7`eIpm&N2)7x8yK&6ivJqXT~PL|V#>XY?h^_M!n6!53UNkPqDq4Fl=V)e z5{+drItnR4EBceeOXd56~Y(hJtZlb29m5>{g#v{${8c0qEo z^CWmmP>ktaqrwTg)I?@Vi@O_p-Tq_GzAB$#$RG zg@2Re^KsPkXn5Hv=vXiFxJv2Hz@a+Ec?3&1Wan&*K&Bn9i}x(XotUsiGh_D#K1w~* zI>_%)JrU{4RYt^ku^OCk)fubwMSWb`<@sW6Z+*$z!BC zBOl=E*ZcVoPL$^KNCE$tMirSF5wP39b3&$Vt!*6RPNS;X!U3D z!`J)l8Q)6k9{ickhXy~air2-w*ix=$U-%MYY+l0tK4@}p;k)dV9r6Bl*+qu52kOVG zGRS+RjPE^R^~3%7B~vvxLsbPEx~Dw}4*-@yVHcl0 zX^ov8ko1qc8Tn&t>Rm!5Uq3g#q1?0EP82#{>i@3JwF(7sb#a}j_~2$ z6Zj?noOlIWV#;)1`P|KyNeX%DC?~N=lj0s!*1j9+tj*uRPCI-8>8r&yAF zkNnV2DDA3tOhCM~BhS9*d)LPLOLXKTN?FSzZBcClR-U|0`yM;wtL{HV>x-Kq!cSKS zTOsPbRQXHa=D+|^Q@{RDQNnBvO zxF0einIzG`>BqsJ4#>#6Q|M9-Z?y2U%e9xWF9gEBT57sMh7kB#oGI8FMOLno`8S;5 zYjgl!=E~&9Nv3o}zd}UFyhbnIb)iuh`}tqYJTDbMCY|M>x{ieQ@Jp1o)Qq-BjuJG8 zOa>45taNs$Tn6X{C-nK3L2QayccHST^LN4z&nwkB^%+@d)$ka2BZLB6C5wYm3I0TS zF9u#}U5BohLa_ZK@m8FH4k-sUMA?VLt~ImiRZbGMJfN;u38VlwHC;L*>BMz9!Z=hq zWS7zb4D)~4BxrZA3)or5Rk4=mZQ)D!j-sG|)v2a_>&{xu|2bIwl-1cU%lx;oV}9~| z6}bpPVLok8>a%ygVb<8%{Cu{11uN>r zeXYuS&t6c{TEB}`%$6VOvl2aVYiUi`fHQQzVmhc-s^fiS@E&*A;gZ%`xs%6F5!%W_ z(E4ilnn;JJOV(e#26H1*MWNQeRJ`~THA|Y+iPi+FBQ@H3`Zs~Hn(Ot?jg>;!$c%zb z`u6Hx0RZC_&iF)KlBycAXYUX*#SZSx6+>jheOZHZj;6z)aMlmOoyf2w>1N08@!7|V zdnS5+G?so0T&bSWEvaKwu{JT`0VvMcLw)27LP#}`d))6c+0GuUJ2YkRHd#P^;rtFt)ep5+0=RiVu7r->wYbHS0qcwAUr z01Hrx`GAsLj|uZT+&>#SkN(kje;y0LXP)ez>F?c(?UTnLxL!`=jj_M(i$12^e-<2oej+_QIu605?V)oYPBn_90LGHWA z&97tiTPw1G4uQ)xJD&?lFII!;HFITVpLh))r2^ews-Vl`IzD;|KyKhm&#>mRk~VuJ z6~yLEYUCa>n7X{T6iXsZCgA=qvwQ-9K)}A;5`Pr{v2ggKC2yaT5=|R@l5eiOshzVz zky5#4=^sa$#TcgA&e51eOdO|*j&2z!*McbBwW!v_fzdEh62iNXD~ z4{0v&`n1YDMErX04)=R zSyLweP&zlfHq(gD4qpRVgdb~mhQ-L(#=8>lA3m!$x~RYLRHRt!S|qU8!x|LCl-;+Qx8FZ7CTlOx233g!yXV9g{I()@G&3iLj_+_(3n^+ zcyGlDs<1~5vko{?B?kI|Fa$PA8iWb#H>c~pGlE+`unI7C+PmAKhs$qNn?YgQ& z9Aa&W<(1__2^ymIg8H_LwK4Lc7pC5NO?P7eSb=7zjrLRs_l5NIuE&susaH_~;328n z-g}-y_NslvZ!xh>Olz2*C7KD%mB-%#itGu0gBv<4=Y2rQu9&<_;xlYU#a2z=;J`EQZ9l zFP@w=gE1RDyH_i`hjL!rMEW}~%Pms7$E4F#*{rko)pms0Ot*z>v6<)rlCvxS zkoua9s%vBNH-k;W)~a}T5QW<*p-Wg0fpuWF#2p9XR4{JSyQEN>M_WN&2N^fzGC{vN z4O3u;Az&rD>%+-gJHHZmIL#$t{K9~l^_p;6&8V0ap9GTsUtFDuJCq9??<=7a&Pkcd zGL_im^v-WHic4I#%?eR3CS`=kt|awDqG0TjD2V@mN3@A3}c_oFq@e> z_qor#&%O6gc%SEe-uL_a{l4GNcl{u@|8SxY%f@Ux1*m7e@NuiQchD->BFdJbrUM)v zqba%lq^tLuFPq}o0F@7VhL%03UM?%jwJxc$tT=XXK*v-u9K4VP$wD_7y7(;7l;A8U z#Sf8($DIUxO1C1P+*9oX3y4tc0X!Ru!<6k;_laEkyZ`^+XI#kH&bvhIx_w-hi~VGq zuv&tF(+lBp%pGUpdkQaetwg{7_LmrLl|0=3?fTUlUshYb`>*+JGV9nwWq$WQOwJ|j zxR`USoVBOJu={H1j-j}h*|Q8M{`$0ovlKhMc=v+|n1&GHg$KNZm}n zPP2sxdf97O*vM9!sANDuWNy(B9eKTnAfg;VN`^JJj7Eggk+}4*i zL$bwmf;kUr<+sewz%cqz&C{G^KP`fEO<&z~31-O}pc$dWsQm_j$9H0b=9)TS;7A8n z&4oMVx%4B9PmO0?q#v9OG=h-t< z^u7AG?`CzJ|I1hYS5Ina_MoE5u}N zQ;TX*g_-rB(J(AnR=1%|zYI6zUU8{trmvu?^L2tI*rGL2p-m5T&wFNQ_9N7x^LW@} zR+&al*MX^p(CDaAwi0s;Js-PwlFpt!p|Q2XRkH!aQ0PD~zq`Pwas9Ot#MLvYAk;$@ z^g)je-8$DwdjUtJf!5_>SI6P5WmDnAS>v+v*DPg^uBDdW7^$viK9g1d|E=Uo&ExP0?kgrY#=K$9IFZluvS$9&m?C;vC?nvjxS*3M&>w$0^r-|C->QTqC{8yj6 zy(V)Ott9OX`RJGSA6Wkto&3cb=m*H{A>wEKoc@`8^4Fz?lk-lt{Z+o;r{Y0)cxTOm znpqNt^Q#)06A};nFz9^AX|1l zm}Ta7kh;Y&OIC#i1ub8B@bwFMP)SM^m1?S5^C;|Wy&1N7MmXWggt z-^5g15EC&2bUmXTG0&#ejO;ej9X_Du@@jvjiH z?UK9yA1q4mJO5X7t{wheOj|2)(!_CeDZHiW(Ns*H4zxpb zsp-hda?JX8!C{i|`wQ9?e_nnK)yqALXkYlJ>PoYh*4P_#b7lQ+C;;u9;TLa&x?iVo zn(&zKZY+tzt#;-uGIk{=FXWLdn>8@bVS-%^{?}Cc3h=WDD!!!sP;o+!TTGh$ZPP&Z z!|(A$g0ngW_L3RF9=g(W{9s*S;>bapGx%uw0aZ&7~x3x}P*vATe^jW=5uXKCGBTD-s#R+?HDhht3IP;x;___38NU3X%Yv`@ha@p@5 z{BQTx{~E3QzZze6et!GL7oUoIs}r0eW*#0{rK18`0=?yWz;!2AWpm{UBsG4 zirXKKBw^j^QMd1p)46}*IQ{ZFLZx4|d5ToYkBY2efj)8z_M`Ix@hToU)es&;4R<_6 z4Ik-cOZ4GiGq!6F0xsaTJG<7>fDpF4s4Z-@9%u&ybxwU}fhIXk4w98m?3rg2uU2ta&IRgW8bEuU zznyCFcY%y31_0g*+IP>OmeSwygl*9jyxO7OINxJ#T0A*@8cSc4y-D}K!91xv4 z9aR-fM_hFK{OH)6Vs6_nz1ojN+_?38Hn$@`4LV5(&A`90)v}`Q!-!wux-o%6jGQmH zoQwhTqm}T~`3}1&D^o4Ztof%|2RGJT)sE%AGqOlHon>~UB)V-pVEEmnraegDLAIz) z5p9OY+Xyl=BCL9spu8^QNFOfMa{KCqnfBca(Rpeht;hboH`iIQS`8_5%YeZPDb`Lt zO}1fl))$=1pc=JnCnXt-s>?@uZ+%d8T|O3T3U=W2J?x=to@F-g(26Fz)FXuYv?uk# z=TQ0MLI00EOtuP`>5NkLCv3;u+jU!hC@ikf6mx0lHw}u zj2+ac9=)+|ciElnWwRkcg-Hhr)j_nIck|!f5^wk2)2sm>zd;}o0>yd6NsM@48Dj3& z(1`x;dW1Hr_sZ%r6Tkd2eX)MxAvtSveDm)Tnz8JyY(QBv=HQ2|LJf0W{?McD$cg_Y z18(MT-24$^KNLZ266&Y}>=xSe!cN}eZiw-n*XtYsw^>8y!eG1pLT$Z3EwT`jzaevJ zF2ny;IfO~Z$>kr-O|kq%_P(>=asx*pLh#n~jARpcrbvC&KiXwws*dQ(_M$i98JqP@8<0&pf<1@bMCmoyp9q&5)dnc#;$6BT@yiAKqb zfM%?uBH+?i(S2IkxYIB$p+WMgI@u0A*Y2^&@N*SpQ%rqq9$7Dlq-=B16KH9et+dwek`co|jD^5)Rr|hc9b5tIy+M=GsnJG4Li+#XnkQ zrDjIRP8bny2DCG!p`-x~n7WZBM4L;7-7>NcJi@o8sLouC)W#`~^?iC5U z$v)^AIlB)VrwB*%=31mm3YGR`K*XFCnmll7Nw{KSd7-K=@Q>Lup{1M%! z+T{QHY7?(3_cUd{#Yy^|_Xlil{|H|EvaRs^Rd%T7j8G-uz!~jT$D%Rh-~Rir1P{F3 zQTqqV(O&T8{6FmnCHtQ)f9HS7a0u(z&*$?CDxbt|>TR2>Tsv0hQ$AUFufN{EFgke+ zl`?9cQew-ZO3Ic2MlG@;v{}7L(?<8_a=@>)tPvZ@L_lwXb4SQH0Q+RBdaa829d{4E znTB)iiYFvqZr5@R*@kzE7)sW+Ld+~lpIs#EpaQcf`qe12#)+B_Zs+-U=;U?5cYX#x z9kht{#F0ZND}$5m4;c@l^*u#T275mjnDPt6bdUKu1{9pmJw?8!=5MI5{q)M?f}o6W z-PTRXU`#tMp@^Wo~@_*`W>I^Eja5ePhd51=}o z3W`XooQbXGa_;mqjp)GE}oW?Ic zEY*Z>bpa2Km*N9WWTUsW#0yiql51p$UbO2_W)dE+)O!;tccF)~H#Z6OhL^;JWrc?O zT)6XQ6FO)RJ#v76xd+MgUxbTiYXmU>Y$LC8i_Hsek|#^c%I4N9mW=Ov)R_Yx8MfQ> zG-f}_)4v2X6y%lwx1wCMVW1xF`L+J>g-+>BhMu0UZ+7HI75ROk3bd@^lSL9$k$k~@5w!^ z1m2Y2M5(Fx!Ee9s`lH7G%V7L}Zk#H%3PRt_+y9JS9xpoiV*i&ax#dF2f6qrdM?D=M z{?>RNa@%F~*4f)Lk6ItMPPE@UaHsa>L!H;NKS&+3_D3K2VrFX0)#$~>;Eal z*UxMGDoAc0kw0p4B9RK0`o;<5_gAl-iu^CCSd`OH>k#3Gk5 z<{Wlr3tix&0YGs&I&yAjNxiEX?2rK}Fyrr0}yiSv0=vd`rJCAf~V15mrB8HeF#g}-7*GFwmU z4XbKKa1wD+_Yvp=_>}SH#J{Am9*XdT(t?-rt2|@D$26e7P__{`VQwJh{2L9%mA}5` z;$$1_)vZ|9CA#LhI_sh8pwSSp-UjC!F$z!Jsrssv|CtQ)AM?mHHxyK2ASDPMvl7ae zAw6c;y*R-l+gHjPqDew*sd2b#9K57cnN|efN?}*w(9JcAIzS%_^h|i$ri4Xt!qN#d z0$IXaxKe4O7+m*(l^NFPizMlzG1e^LRuh6O8{E@rt*G4f-x1HHGcw|c#u(rvK*B|rP1hFGk%rnXVkPn)lpJ~mDHog=^Vlborl z!tmJNwrjVMl{46*E*fTr+7>mA2_U&EVe17rcE(3Pb-`hBlxq7JtDmoBkg8JG(4N!K%FGt|h0FU7?{+3)hZ1*= z`qo6k2wu~4j}&bzg!y}*7VkE(=|~I7aH&0#fi)di8(_F6nMbIB9NXuN!SMdOz{(f4QXXmr5 zQ8zV$qba|1cNw0^`YZ5=cSiBTCvvmH`ZbR$+5VZA#Q!^4~|DYZY99W&WekPBXu zu6w1BBup643SLtZ!b+YRZj5B`Sxhm5{S_x9bl74Ta~_Nyv(PoyBP_mfW3D}>?XO&+ zcLvQEzLgj-riTAU>rx2fqGStuo3$>QoK%oOWRys5iC$&1q`eVIk_*zg5hBfvpZXTK zE@Q96z_K}STm;ivx1s9U(k;k;=4Y~bncs**W;y-?=5sf&g+u{XJ3}G_tF$@r3%EybPiX{Zy^Hj_ zY6#Yd*TX4?6A7U`E%Mk)wg_YF@HQBKA*dOM>^LGRzY=Hvho#$D)fWw>64tqEQQDTB z7SoXi2CF86+a)^bC(Wgk8Zzouq%M3O@(KJ`{Eqx9Q@06O;iB7sWQ*@nsl}uSh7fOva3sr2c5s)GJVYJnek}P zb?Ieyhn?$7G(C+i&(3nA_QB{3+~YCIu|hKRj8Pkrb#R#$(&wv*^H9R+NMvQcRt6Cr z{+=@nS4$SuLC(Q5F?O0=7KegiGK%FecB=EC(cn<%$WYhD3wv6(2NbAk!xOXl#-Jh@ zNx<`zgMY%8WC=)OyEla{OkoZ!-E|HJ)D_#SVvyR%{9^UGTv8%#wx=5;gS%q$wymmk>fmCGA=@dV1{JK z{#@%_0o>lzJbZqJ_iszr{d=`f-m*TGUIOxDl(yH#mNfDhPBRN72i$OB^7)l(6U#5b zxv`TM!PytG>yZM)&!B**l5<^xV_|Mi*)iCndZXoZIL{SEC@!k-8n^gldc;SFo>gi@ z%H%1OA5&1@X|fUf687NJ0gZYms%kIO^zxpa3`#5{Vj`pw)j$|k#;EiZnenx&IU>f=+&Ljy*(SLe zbxY_A({j!z!%|bI9L0aI3$H^R4A+rSOq{v7R&1ZT@wegqp=EcMPto013GHn9Xa{B33 z`K5ZxDTS-I*%w{;1M6y3<}7Y<>3dj0&m*f3=>c$~U10%ATC*6Tc#M&>UqE zfpeg+*6x}+#nEJ?irdR1Zsb9&H&Oh&*TmZ)GtuapCrBX@8SLt^t;+-~=uX=&4qE$Y z#SQXogia?FpuO8CfnC37lOB#+DQMfd){D;zr5y577;oI!Yj{3^S0f2wMvy4ZVau-c z?6KbrawYG1%OVu{)Mk6YaLwv3Xwo2%`yBsH4<-)sEUBCxtE0>`1TX0rYK9@{a*gEg z#_ofZ-s~r%yZqi?sB5#8?su27S4nMU^(po(6r`ybIQc(W{FtWd1g)N~a7$j)!$OeF z?V&!i{a#82br+yhf_TMOUC20)?}B$w-!m8;*Kl(#@yD4bZ||w>+`kvLdspYtrq!@1 zOEe=DqKQEYyq6O_*>{~5mP0}6|91H3KJXX(p(|KmZGCY@3AOxnw2x8#Xk$HC)3e-+qe_XN0qmtuCn`rtC9wt5O*58td=ohK0$;o?rj zkfH>U{bR9)y%KNjVJ&**mE?g1=kwYxX-K>!whdj)uDt(p9Wne$8o_mAiJfUx3WuI& z=ycwdO+WvH1c~QLB7e+9i4YOONhneE;$hu>9PhG$T;AYw*h74JvfXIgb8Vq-w z>b)RRx~buvb+ZnTbsGs*?1d(Bfrrc^1UuA|&lAI&0jskO=WspiYm#u5zLm?QpAfCj$jz-~1+rxJ!W5MzF%K~7?`~qa6aye7Mk;b0==y-~*E+2usm&9(% zl^l~?bOAF=EtSSiCzgi==zrWa6C1i(XE&plM?i+hIxCY2?|zEAl$}8d8jtEr$jX;0XJe8~^ zq~b&E#ht~42p^8`nKAJA7uDhlsFzk^rp+w>YvzU3z+<&nEt_|X^R)<^*T)5$3(73h#?Be9D*dWvmaQuf^LXvwnwWF&J|*~3=C{N@atJL%vpSY%MTf$x2LL&Gt9V{Mu~GYK#=+1b zaU5af#*C`o&*Fy-U092!t>Oy%#PTL^bAqT?G0<<0r z`TeOuXHK)`btL}|A55O?o7F&lJJ;_Xe`?EfKu=8+9%DJ?1QV2}8f<;QX`bBj`B*Xc zA?N}1QfOJgMOmr*y0_8b0IKXqZB3^Opx&?U!I#4D#2`|p*QS+uuc(AsOab7O7=CMS zM&m;4#KVIQK(n{@Whd~;yi1W93>uL2VQN48TQa-Mi%I|;TPY%nMrR0EsAyLdhcm0Y3kMWOelc{bVo zfeu!DuPUc0LTF~LFXZb-{M3DBxZ$pMy6jKah33}>Y{&RVdlx;+HD2I?KSRxcozuy< zj^=pCnes9to(2DuVV$5L8Az94HrC|D)rCc>;@&Ovz9SR=#kd^!`y=H~`7chN-$s0_ z#AYnD{ss?P7N-n9$@xzHt9YaA?d-Ke+CMrm`)$jjGR}XSe*9&ReC^@$yW+M*PxSDP zTU;?`(6e3Uw;GsCk(3#vySp~~g4Py+jM*A-Vs4>E#=M!8o02W`GML)e0%-JK-yZT3 z9FFv-N0~xqViJMSsUX(I90MY@BWXP4O7lQsUXDTpxyMVJlu|E9r2}vby~?s#+#Y2wLMQA$mZ7rZLlY zYByX4Sn>VW`-`{hi6ODyPIHw<7kWzU6L`%5X=w)`{Yd;^Lhr>1O{;dw^t;1UmnODH zuUv4=Q6thWRD~g1&JT5}{0ii3#WnWm*zG4v8#bGLIlM&GytGreM?rozS&52ux|lS$ zaL!sI5|E;(!h(xW>JQ~wf1hbTK%iu1$qkQLXpxhjZ@g!;gH+IE#Typ`#4(vhHWCCf z$oM(3<3V6lhp8la)no0RgmfEtcPgY^lmW-g!(ftrC5dS6O`5|=eC>=)w(TaS^vm3D zn}>t*Q?;$(t;n}WO1VHKrl#%#&f`A{aoiF5Pj`LC$Q84Q2#WqW|KT8w^QF8BTG#O8 z(B`ejR=QiUe;c_AZ?t+|5TI+aIOL zio_+$+^;oP)wd>y*(o8l`QSbGb(F(gQ6|ZOwQn^ z8E=>V&X)uT6DHO&%Y7<_V9Qyv2%m545*`b?H~dUiAMKa=3zaH&O!9J-#+ruYcV@lK zueX;_LN&%D`r^!VbQKZ(>k2PfFx;tT3b~yT?wYdh@^Y1g5fzi7dlYil)X3J}0@cF6Xv1XQ(7NTkm<-feS)i*r*({)krwZgF=|2^R*xW%4;C|QP*zo($ovuNh$ zBI2+61}<_0sFkmub}8H0CCqQ_*s%!tm6@pfZ>^TkieS`DE(z|oQNLPqHtpc*@s6|8 zfPd~Y!an!XE(K72I|%kj)2{xh0tBx3Or zt@QBcWXNg$TS{w2%<(#%_&C4^b~0$8dZN}#@R}+Gkg8p{$s|<*OSI(fgSJ&)B^P<&F<>y1s^e0+(P=HanVGkExn3!G?;v6_>M~mE!M*x5ETw!fPvw z&Ovirqp;C`^orF+sG+T#3n7{)VHIhfH$L{qSWRyn`65x1SrPp+{Z*x3lhttJF`t}} zBtOHN$Yg(Uz0u;awyvRwGgs7r8aGmXCD-o&nHG-ctEdbq3siw!}-h;mYG+4Stt;cL|eZoSGWtNnTzYPhu0 zY8?Hdb*zC~csfzA;OxMO{rjDZgND?4OON_{K#yR#p76TZVzZA(sAq;AEkk+S=a8zC zat>CQk`538`=dk(qnZDk1)yk?CGIM2(Ay*enZ|Ed_AE@zIfBBM180;0Ac!bL+JOJD z8W5YYygFez``1=b*(7vKA7qM5(4W_ZRbKfT^dG!85tn3?3J`BAF}}q|UnD%v9k9Z_ zURCyB*90`>c8{OgxP0(T#CLeb?t(tJ4I`#%WO=ci9nbVADUsh%O9}Sl-IB!p4Z}>; zl1h;a->Y`3ic`%0!A=VsNEw_Njb72Shs@~ubi0zqFSuukb7~{2_uPaNdIm6V648KJ z!yl^}hnuiIB1d5oZqFdx?3jba;`2Fss%Ul~V3|X0oZl2C2+G>^{(7*%+bKGs2lR6{ z#~lhPi)b;-@T*$4-AONa98UX`T4n-aMa)g|q7vLAWx7@qxs;#VGyI2XBR&bLMvl8xr6;3XlxKtKaos4P2DNfjscepG`c9*l4x5ifR5cmB|_%t8Jp3$5zBw)P~eNN~3Z z&E0xO^eU?E0p`07-)l(l@cXaE{F~lI9-YS*BEPz1BR-iRMT;C{o_^xv8LcFmT6p>$%PAEfLB|#Npa}p((D>ZzTLXnSH{pBkuPjV~wqp z3Rd8y(w=gdQdEYyX{6zs?rvJ0b~6l><^2r{v#t;vAb$%nlzkRV2vYXkH|%(21M7*` zX8!&h_)sUh>0JFVKuUo8~-Bo?i5P`1%*vmXc*V>RT;1c z56kZdnyc}<%M<3jae_7>Vj_VwzRcz8UdC+T-S@Kobn~OXOXlTgGNTBA=gkPbQ9qZV zVnvVsMw@=9s4!8{eljb&+<-V0_c*ehR=hKmKQ$5&n ztC7F?Gsa$4pgA3F@TE&Rp^Tr|XMpU|t&h;7xc5N8c^mTxT4|^_$t|FT_64HR*lVYS z5B?6S(R4k3Ip3}SkObr;?!-}*LYA}=ZSQ+(41Q9HGRoI zpcASYn1v1hHMo?>!Xs<>QZz5HHIlk$th<`!?H?S0lEa6b#c9`V4uQ7uEg+CNB9^XW zv4&i+EHop%HqMBmw7+p#;=Y~Q&zVssxTJlACfnN2Z|H-TvBx~Af7l?p^sxdi`AJUL z?`83Q@*M7-!*{Gv_C28M2R(}prM3`ZE!54ye06=UY5;V}bfoMTIl^ZyS)2F_^klD= zA?@{ht2#%(!&!FO`EH`AA)wrjO zEDSK06xiqHb}n*bQ{s0F&qpx@wWS53g~rstV|viecwq%H;I2PdgO3A6Zr*vekk-Ju zd@J?jKe4D+pX1EBhE!FvH!M#IPa+n{NB%9{`RJ^>q6#Kz?*|R+Vb584TZbYxD1w)l zCM%9r$WSTEBRVWB=0&%Cu-BeWD4ivF%6OZJj^o>76rTGy%x%ejXJ1fjk@ZN!Nduo*KU}+- z82`TRgcTRz*#lkAj9K8eMXooGzbR+-M2%i|4(eWA_%djON45GJ^OPW0j56M~oyS$t zdd3n=O)2jypQik|Q30Mu1d3nd>iK%Vv(9s4qk__o5tVI_n&aYwxb{CLD?ZL=9ekwh zMn$|Upr?U?krl7QV#1MCU4E-s)#|K1s{p#@1B1+v`zf+eaS*rp47(Yr8-RaNcY}ws}{4ETb|UP;z}OFr@RGb>a4tC zznQfD&B$s2K}JVl8$|M9>D0uX@#egg(SSS81;Oy*lUP?gFC4n6$7tN0K{&nQ z4xevpB#jp2)QCIHz+tt4n_46$T0ADWdv=kfuuYMFBn4cOZQOP%);jXB)?WO8T^MaF z=OYP=3@zCMa`VWO0jN0c-~SD1@O!l_ATN>wmF>Jg@RAdgds*ck4xO3ik$V$6fxf|( zU7S&qOQbhwGj&VdUnx6&C{a9!F0(i14d0vM0$0<5YQmR=Nt!mV?}@VbeW#(7?Imz= zRQOKSPOcv9qL~}R#vHlFTF}eb15L-i&KqQu`F3tY|IDp_XcnnpIhr4&6;qXZ_a{wB zYenW|3KIs;*aNXG!(%Dv^%q8E~Ji&{=$9jcgEs@9UCQh>{|DRh@rI#t}w ztfFaN-u6vi9;%z*7?XP>7V6G!-fn$${W3DO!w}!9D9@}r9$ls-T}VFV?8)Eu`>Kj& z!=Vp*kKuoIag?WH+Uy}`jgNMf>Zxx%<_A_^#Wi-8)vpaU8=?Z=9t`}a zjlutez)F={k1fF&MPZ?1o?Wswl(HCw-|TY`!C*>>6fuf%LKn`f0aGum>+BYP7}ty- zKLp-I3uuJ?Ek0S@l9E|(6*OXv_$O%7W&|zM=lyIAaCD+OcvhQ=j0R5CHvh9BuvqTpH<@L*fTwTgdF8y%Iy6g5 z>^TNg=}|`G>%)yC<(bv;KIQ#a*$RC02SfA*Gi-$FWUuXtt{S;4>FcU8S-HRUdk|^y z@nCk4i$9hr>!_|a;meM0Jqh)L*7U@w>8jAK8sF%!g4%!pMo10V%LoYFosb;D` zIot902T@@`fBN*0<*7*`m&oTh@O|Sa6)RmqUYWh0c)Aq}tnD?{Pl!PuIwUx}K#U%f z#7SpLPm#f18C_HjcPj7*H~_rG_A(@JHeo-l1uKIYI0Q*RS{3O7=ZqtJ!KPECkz_s5 zkR*e!UP}r?%?*ke+)m-w9j^0~AYAYUXN-yN54?mLl&Ur^qDc6<8+~w{H)Qv>t#_O( z=Q`${-{2ja@^j!h6|*<>>5PLX;d`izry~H3hODDG%LA>96kN$u{f^;?^YH<{k~LO3 zs(3$Q2~FX5+_qT)+dyP``{D8Ry6|aXr^d^t;Bq@?a8pIfM78n;w#S1; zNH&1)&aLRNl=vYE?FO7XElJ_%IZ0vkFHuIB#Ck zXgno_NO)aKxDIv>;N*{Ug4^PM=IMZc;j#y|e*(Iu3?aYR!XjN;D{{o~DykU)SH=CR zxP$1mR%sPRvJxP(aevKj8vT>nQ$1NO?v^K9SK_s`=t(-D$S5Y;UKY!IL8b5i+4cf& z947VYnH)i{D3cO%#kWbdUeelPk2Y)b)cc0-^D-ub+c``K*Evh@sQ8_7t;gT@bI1C^ zQ}+^VAX?)!MF|x~Rn3)MXD~CU`H6Yc(vKBc_Jjgr*yLOL=#XaNa*CkHIk zEnC2PT#57{GnRAyW$ZK$q>als8`QqwnjWtuhIwA(Y_`)P!{FvG_|7j~R@r5xdRhSCRwL2SIOFWCksV{~iLaQJYc=ifGB?DKyd2Nnunxa&vjA z7FooK!X6<8J`f%gops$Gp$zdLIhy)P=0Z+ap;FS|9z(T7p(j_eX`mB5RO)9!Etr*a z-E{d7)Z_Pcj~U%$z0Djr{y;Ht;I3@OxFet|Y4JvRRTq}Oy)O;C2DMMgg>)#CGIcw% z59ZlQAP`qO&h`a88N#LZTc~n}-pI43HGRg+-_89Wfmk$wC@GES(b!r z8#McH6|0=4oSb}2!(JbGq-K5bgYj~$DFzh21v;fStiQlOks)rm>yCtgTJ|T^&Ey(A zYpHko&0h2hhscZ^34E{>XG7>jsfQF?4QOG6(Nmb?bJfGyrWBjzUU8(B? z8DOl=g+&mzabjW89#WZ!?b(i-S<-}aJ{%&(+&ja%SkTWuMchVs21LUqR}f5+l+6)i zgr(`F;X@#hnJYq4&KShGI9M^1(uw@q7E3a6fr#;E*P>n_(X;l18veUhSP|cM4KAeE zrh8mvdXPnlq)aHPgOgy^;8(jo;zq@nwLa=NEoo8Y%dvjEw)i+%<~#eMq%8(GiR^Jp z(MBfR0Ji|8#;WY&UgG~2c9dypcuTWBN?wNzJB1Y_H>|#U4sl_4gL} z>+VI0KgLKO@42s`dIT4WeCDNZ=?)2OmZ?SI*wfSQD)z-qrB9k|O@mFc%fV-P6)#>5 zDF(KHQ04BI6#Y+@8O*a@C2Q7&p95U>qulE2U;B;|npfdXM0PlTHxXdVV11Xg?Blz7 zcoA$Dzs~?T`9CFF*m6zy=b+6g;iHfsK{yn-_9w1BA74wd4g0nAtNlwgtiLAwJlJg9 zM}@~YSY>)`T*3c-N>D_g3t4w4Vj0mfN7dxgeIGFpUoMn&Rc}0+d|1#IYa6ybI-eQW zx-h5MIJ`MKhSWUbolvm!n$gNP70T8vi=Bg7%ZAQ*X*u`C`9~aCc_b$y@gDYNAN$A0 zz09IKA)RPj)@Ou1?#Ds!4NW<-IM8uT8@jGr)k2nrRx44Jc9M9=5kGiC`iogss7$9G zPQV2Ex?aQYV-)4}X3*rW;P1sJ+xnp|D~nn6f0SzOXG&s3>40qR!*(k`qDM@6ylPje zNf_C;+5j%e%Zi9eFKFoR25MMC<88tn=*ghNHl{pdRsGo%OmFj;ed(C)M~LYM*I%L` z*7tTNSrrZz)8W?vUX|U3M-J2;^xje^J)4!1SW;VQwye;roPKJ~s~f%ABTH2`F@H;=_(67tDC9<8L!2 z^PXP~O3LB87QD$Nj;L*^r5D0S>l@};GeA;~FTikQvWmdfu@&Ep@Nl`pe>@q*Z{t;0 zk~vWdfh$ic^fwKUHRe)(+UJV1b3q0x8r!}Q;M+RSv45$;7Ki+@zCh7QIcD3fUuS59 zKh<(K7}cgFmp+dE+0b*nw~D~741$$&dh#p(z}B>ie97SxvnL2tF{nkG1PSq9W zOBp=}1~=re3$3jniRa*Z{slG6JmCGT<&{aJ+Xk1c{XYcV3F)|&8&lwT^a8l9lHHsL zDPI=%KUn>YGq7IhP;J9?pR+D2aL7jRT8Z%#y$g)7(uIxh{54q{`zQ<|>cH|zK+c{~ zXOMux8HM2AQ_^vdn}lVXG7st`z_rgbm9%B%LRb;-FFK=}^u=eYQOy(fCvNT~-Ghcl zpfV?E8Ci+aR2aG^a8X0z3+JeTFyP41FC-o?f*JUWhO4n&%7m3?`!6k$a%B`aFx#%w ztt5g7A0pFT$INzNOPS}UNqAvkQ}4NA+Q@I_Z>n$+x^8>37dZwPv5zI1kb#nQh;t}R z+>gJ97@y-!YTe!!M)K=`s?aZ~98fLrtEJD*bwKKVa0kOFtnOO>`5g@h7k=?I8)#K? zOJNPDQ4Du&?z3^|(DD}9ZLI{m(@msEARy@&(YLuc``%7ZO*=&GIsB4tT=hY1UXqug z-mr31*7AJ&#ZtlO+!7V#%s6A(tu)tLK9hf5Yh~3IncyaFE@m9D4i7eR3tOgG21*>&TPBxpX&Q`)y_=)T(omGxOd3h_ngV1ITHXAwZab%ud62B{U_`HUNIr2!|%tE5p>HJ*Zy`EJJ zubUqFctXWQ(^6we^GI2Yj32s?0D9f7hMOL^6GK_*od@Fifrz5JvuaM9cjjH3Xz>$b zZn?OFidf|=RA>)*0C6K#+74Nhx%IrBFBnl6`hM-|nw6|e-zZQ%3^mT zjZ`u<{4v9fwJXHznufV7ShaONdg_v4%I}G{ULN;->-;u(B$8xs(?GO*rQW`TeS?m1 ziw|q){U0>F`B##Q_rI-KTA5N=k+WsW>21n!2xqWNsZ2c`&&gRzElqKrPywe*%>k9l z3>D4NI!-yy^PD3&;XFWsVF`+cg!tw2to8f@_u_|pubaL1YhTx$sr8C+^G`Ez2E#8P zZchruD#$M#WRNdB+%~!bG*0erbkojtF#4WTXymz-@8<*1q~TnJv|#7 z5Q%<+|5N6G+6!r7N{iDJvBTjfEbo1(*?61$OU{;^E_>Kly#0|I+}%Gz>O2&@4Wkz? z5<^N+^kG>c?Y_L4roGVz*p(oSc7AGwi^AcX{$Gt#IT7zWtZDBZ{aQmB*ZwQX zYX9|mELc(t*aL9tAlMz$)NyhZ$z#143xmI?EqE8cV#Fge418^bWNJBrkKEoKc2u2N|iQmLraPR)0YC2PB?z)JDmt#V~9CEGVtuc!L0Y zY%%lFK5r@uGq~LYf>wfa3~g0@`FEn{sy)U6N3PFYBl1B}6@9_Jo1Y)M#0^`vbyq&+ zH%;FzH-x*Mw!YDaPr3PAJwjWR()Wg4ubrxHvK|_MyyUh2s@5OD! z3{XhxFnc5SwXHeP*%c`fIejQTUTm#X7Q1U;4q;D4=d8`eS_l7+i%v$M#`hm9sM@l{ z*5a_pjO zHaZSX{Ci*7svrJLZEx${E`)8zIsOQ7OIzy73H&k`LJM?pRztu*{(tA6WwwwMWYqbBG6HO=$Jxg)5LnJ|gr(&3u)}du0 zFqcpX-{>v(GwAky)6lyeGp87OJm52qw&wZEeqH&4?{?02PlC<7Fk=t*-_0}wZp>Je~OGOMy8c58?>(- z3|UwoHsv)l_7D8mecZ{zEN)&2v_Y;^f?%0vyI5V>V@T-E8D7@|le!#boj7*wa*VWu zY{q)6#G|#UO7%gBMXvdhnXOgLwOP~(A;eFRff9DyDZN1fN3DD*>`Jxb(%#fvaIne! zOj`qcZ^_}Oe>UE(d74{C5S{+BzqHm1(gpdpb1ur|Q-8%k>dgLr_fI6+`QB?!+6||m zOVDttTDxlfW@3h1^Y)+NffKVMQ$KR<<#BZSkGEF0Xcpz}ES9p^!wu&eH#u*5DtBMs zMvkE}vV&4`9z#F4{JuGpGT4J>QFl@d4K;4{+{)q$$^PD&TMqg~2*a3GV36+Wrc;dUss?W-_k5Gt#n^ z=%U+)l*OQsfj(YSZPfCpM?z7%`@{W#?{w!s)bwA4?%JKVOlt{^@;l!c^DFl#WX<>T z_+uvC@qhE7EYeo>PVn9Yca33JGFl7o8?xrGKIm?~!bN(aOI4^Xd0-8<@2W=-y*6=w z`DIR;VMz7cRh6WGbFj#&5Oi%l>Lgs((UtC=c~#<$0_v zotw!nzZA3gpgeiTDM&qOW-g&<{FOJZW^h6szP59MGK=R0p+idzvydV=B$ph@F^Fk#rr(07-_R zM>YM>N9gRYin5Ju5>N>9f2+F2{d@&WpA$E@ZPg`yJ6DJmU-W$JVP&Cw#M#@bO_vLc2u2Q)w|(wB zu&8nO61lG5aQ(A1j9Kb;`XVx2uco`X?gX%%We#$j^G}MCbn9}4pkF-wDO^lKOf zZxnvg-rtA6cA~P59MlY6MScb5Cd{yVfGs2o;1W62(r8EDq(C(|ow~I2fc))!~|#V?wE>)x+t@Hqm9K9;8%rdL&!y z*AkX!ILOgiNW~ zabGX;C50kC*u(b;vLN(2-sp4INLxYa}5JdF?P*OU9<*`(^qD|oCT)zih zFH0oZn$H(gzhqs0A~DkkQyQDA)UK4FHD2-`-p(LsxU}&{fqrGp()iR^_&-TAPrj-` zmUF?c)?aimPtQZviqiZFJ)br6Y9w{!7Y9P(C z&Z*0W67Re!X$${r$5GuB!ZG%{i$KS0+1nY7#-VyO3!&{P-Jp2B0xdJ3`^G7DtySf@ zR=83z`}uJ=f+7xT|1~MI`uxVu`A*BzSXEr$d3k4G z1reT-<~!N0pcZl0ZxcEA%E#LcHVBSDh`*HBXQ^P;qKC3fDFSAvM(&{XB?;679r~AM zgh-`_!UfW|w z4jR}wBpzKT0+`*cB|Z2lPL%tP^;U5%w{ZIe+i75e9uy98W@RIJ zN^lR?aZ&b4f%uQ~Mn{eipL1KZgG7+MK*}D$A}c~Xt4&33nG<|;^dNLm?#Wi(QRA=@ zD+Th+u(FF0YxhBXdYToNT7pZSYRFH{vPRyrB5pOVO8us4Ly_5T=oj*Oy>gui#2-xe zpZUTlA8yRW70z926Ig(1)OWf7@(yj9^mg>8PHmb+&7n37oZx7iLOkNP8AuS~i~)3z zl0iYX&}jofIHq5mY$D~x?!M~nQbz?!!lI>!1`(XjI_QcuDkW%c5CB`5@=y8{uh;|Q zAGI>x<+le%d3Ph0653U#_H2HrQ!zS6y9ce8B4!`FvmCgWeiVP>&i3CyT5*>;uE2QT zugDUi_SbL!Jy}p$R#v)8$#Rcij*BoiM0i{wOj)wi=EZx#aUUh~S~6qO#^~F(bQRaK zWPDoFSCiW4Meq#bKplP;2R-fRWdaD-n#;aD7c9i8{E(jj^I`W~$&x(s;lXxCAKys#^SHzETtzZ-bpbX`dbCq8%g4KI#u4ljNw4!AF<`;6S?tcBpG zM5STf{_h!Yn%67bMtH64*Fz;g(yH8sDm`**MD^CoD$@LPeM`@z$$-#|!s{KmoZ0B< z@3wm0-%C}hr6-I1m}UhJ8RTzp4o19HqV&V#;0o)WG}|_NHTij@qsWsVNwCe1!Sl_* zk8Q?aI@a5(QjE&}G-&uML?GwMjKSac3=Et_x>eU_lrc&5VXzaZ#U3Bn9}adIUL`oGTQB)9Eu5C?`rFWOzcjotxX?T;<;RD5r@9c<$BI)hYXyBa{`;2H)nyer!}qNP?5oC=qzbn_tffcEFVHjT#{KT^rj@nACB6 zPVW5Ebk5JXvv}yPNf_*v25i={fao-q_>KjlsRzYLv>R^O9yb|%yqUprIMh53$Ziu@ zX-puWw6hpDt2QZx)#19=59tfshMT+d^=oA}z87!seyQ^X3TJ)bgi2`DlG5kSt(Cc;1d7a%D7y!@2&{3Y1V(W;3(l$r11H1EgwB z!nX%5yd~q7n;^N|Z9>a4U7b++ZI{52leh$@Kr?_*zGA8+!D$<|0sBJ?TvDk+KtP^5p@{(c1lLExO&7uDNUn|Y>~@xwUNIKB^RhQva6p_XYYfa z_(TJaT0KyJ^#0Zak@S0UUAx{?VKbin-qJU6LlwhFV+)^dV#(6uNw=}fp5%SJ*= z71d=S=Ax6XnFRCFgJg>ATb1r@nCC>OmIXu!hdj**hw2UA=G}Gj!z#T!Mzy+)3iQIG$-tD z6PTOCtA4+FdNX!~YANQsVRlI`<`%+B=h$tfmZKNt^1)Y+*jk`bndt*1D(0E2r*T(s z7=wWdz)?(oAK@l3{wC2n`;@ekmUPa&CXS`*-lX*8K%a*}&8vI2^|;rNN-WQvD2b#v zWB$;ILh3WZLYe?ex%- z>M0m+`)-0Jp*A3JP*j@x9P;;O)phPpP$Um;Xt1x#ps-n18+DhutMJyP-E+=(SlW4u5|Jb@%KHdYOUedFc^GC_=gv% zwBh)OxQcR{Qa@iUwx~<&Yz0>H-=Ij5UML|=Y6fWjq8#~kUZHWVV91-waYV4H-K5&b zo_}JZg&-y73Tajy<)rV$oDWhJlsV1lF&ke$b?U-f-blk3qmcc%1GZmd{{+$glu!p+ zVVSkjOOwWzGZkUIC5Z(@GO!ib|97x0i^6cq9yVjxy+AA~8h%7^clLUh>w0YWtjqV6 ziR?P--u7=6B$7cwqO7!AJlv>lw!L367dacs|0xST?-4a)lJT}3H3e<*uQ8EaQ*2L# z_dW5ElHl{#HI@dx#Ax0hxB`RJZrA0B&>2|tZCuQ506is=LEpJ=|U9m4>x zog2gy10AlAtVs&`1`seV=`6o!SsLX&9lBo#7^t$$l7WV&XoMimFKn%*>kSm*3&GeI z5~*pN_Bwg#nx2=fH+enyn=t3iuVtlf9u^sZgR80YHbZ?Y_^gma2L;W2RvXGmseILf7x6M1SrbLA=rbJ8CdlDEaSFSVXDH( z%_ZaZFE`isxc^Ew6?&J^pt`*9510`Y%27z}Rm<2?ZM{`a^=z*g;AqF651nq<)L44e zUQwC#VozqD_Ln{?0MSY1bb5IE;O>b6{X`#)h!d0Ihynot27g@9+20$>pAa8s9V;H~ zU`eI&nU#2vQ^e5dYi%n7UxhPmjd*vQxF$s8CJ6kAmA`63F4As#2d-QJmEaOeA<#fB zdP2<8{&Pwhx>NXo`uPkYDk&+AvwllVa?)z9PNUPCS z5U+)W2hF?>yY{xd4|!<(^W?AVfvYqWBvc?d}We)>?llO8f7IC7}IP(?d3^0rJb?B9PowvryZD(mV%d-y%~-|tyU zESO|;>)YB{&{T1}(Fv@Z9#G1ixvk7--s4TWe)~@$M{zKuUf$`zux&I95|mwX&>L!2 z|C0${*sak0#sK3cpuLuXK@HSaUzfYf4} z^G^KRolJyD8_GVFwbQ*Y9JOz8$vc>zo^=se2Y;p z_8;aZsXBWdOc=E0>`&8H*MPc_2R3o8%KsPyN=3VHBb?Oy`Z_##0u9wl>oF2KUagp` z&D#8ev+MP>**eieI8#7JY9m2_=Q-~Z8e)n=C);eHgi8Kt|>a;HnI?wgo9a^)N*;P?X1(gW#*i{8Cj8r3ND0BOjq?ueL5Q9>@w%O?Oo2I+L5>Viq?uY6jeK@qB zBvS$I(J>2*{@R$Zqwp0d)EgVrS|zC!bM~~8Xj;mhOXH_xa;V2q>c9ietgy6`0g2UZ zW;-XawQW4Ho`3kYxRgkz979FhX}zJkCA-v-rcgJNujO$h;q_H}*%A@2x_!DyH(nE? zjWH2!031sAE!24~_~R}G@Z0};t+P+VKp&))l{zwBOHo!(YCmaQ83!0RW5?(cEMLVY zeb25;Jm~4C88Sj1=_ig}Ks`IGLkg;RG@wr@e2}N9&?l}SdGT;p&ViCj;0t=~;GbWH zGTyWYyQf6Rm!(#YY#QAOy|;VXr|)D1WpPfB08ZG^lp{DK6cqCz(GaT2q7kIN?=*H( zU*EurMy>N1drP_E;bKRWYo5C15@bGaK2d2=uOIOBP8-q#LWbn?Oq5{+ekDP|@625B zr~dY&crdlp8#P`k>py)9IN{LPAR`MIUf1CNc%xMhf)gIs21{%2%O7;ZfTBUBk9Zj( z0uU2NRg&v5LQU49D1`;>n2d5Xt14q`Pw{|`f&ZJ91~pg((xCdoO^!FsbjzhI0<~CA zcucC%V_&^IXaC{G7m~V?KQ;17`;~El)Q&!q`KajJ*5C8!z_yumtqJSH-EP~4CEkPO zCspk!zL|!E0k55jiHEzxo%&g9{bqXPkd^2D->|*41h>2Ma>8kGX|3Ovbh4ZY_X)w; z$0eCVAE|eOJP%&Bx;x(%2?V|V$&YQ!tgNhKJ+?9Awy?%4k)sE#gBr&6VWdEQas7|1 zw6hLYpX$$(TID+cx@Pa%!!4}GptSWh2Dq{0ZN-xN3T|xaSCOpse%Av;!pkF+wW4SO zaL|+}azWD*Qqe& zSUYyNdu44M>k@P*3p+~WnM~leRxRZ?LAm(wgm#+m?6#M_LQTt0D>!f9`bCVA z!K5de3dEX#6PH&LOvSgR7u1az-a4VvkV0@k0qLsfb*#@GU6S6P(2|J*T-gh`$D~Tx zvOlpRA3h!Z1Knvsd^b`-O)c-SRQMe4+pc7L@~uJT>LszuxT3P2!)}^}LY4PE*^)m{ z>Qfa1sv`**`g_hPp;0LW?An=T$Zg+&{(ic0LRK368=>xIapOmMnD$|BYTns>W|bvS zfV7X7Q>f|oVLcuhLl~JDWqVLxh2*7Nl9NaFsLJJj+DMXUVRx$_SSL~?uPaFzGm-hE z5=$tDoTvc<$~dfn`aLwE=ahaue8~7{apc5)>VlZaozrfylQca$F&QI;xVU~3MsAcjmhaaVTm}cc23zq9G%M@-BHb$6iDJvo5OSqW`LR?%l-J>uG`LM=FXe-t78HFR zXV7>#4#YC|6C4MJrOm)ngMQr}a}1hnX=;XeR$nDT4R?-`2m5SJ_mfNYUCfA= zYkp-E@9T}HEKAJ%0BNmN#XN3)dyMyHSETH3QqAmX!;S5+rop1?Jpo30G|`?OwS*mR ztKG&*R*3ELvEz|=i$+!r8p38bzmZ4u5gQ+AHoi4V3=2HdTAdbO{Q6lD#p2#3(>bSK zzBrGdzfTq=mZ{MDtYp(EWAybM`T)7{Y8;Wr%y>&s+onE`ng)lz58ZCS2*cNIo8nxL z;X$mwy(r!$oKet25wDRS;q6I=HF$=3Wj^v|*`kTd6SPM;srLf