From 8335a534ee23f9da97484f3c67ffbf9829afe3ed Mon Sep 17 00:00:00 2001 From: George Zhao Date: Sat, 20 May 2023 11:36:36 +0800 Subject: [PATCH 01/17] update submodule * LuaJIT v2.1 to HEAD * libuv v1.x to v1.45.0 --- deps/libuv | 2 +- deps/luajit | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/deps/libuv b/deps/libuv index 0c1fa696..96e05543 160000 --- a/deps/libuv +++ b/deps/libuv @@ -1 +1 @@ -Subproject commit 0c1fa696aa502eb749c2c4735005f41ba00a27b8 +Subproject commit 96e05543f53b19d9642b4b0dd73b86ad3cea313e diff --git a/deps/luajit b/deps/luajit index 03080b79..224129a8 160000 --- a/deps/luajit +++ b/deps/luajit @@ -1 +1 @@ -Subproject commit 03080b795aa3496ed62d4a0697c9f4767e7ca7e5 +Subproject commit 224129a8e64bfa219d35cd03055bf03952f167f6 From bdfc804e3e04523e8c355bc465367e33ab677c1a Mon Sep 17 00:00:00 2001 From: George Zhao Date: Sat, 20 May 2023 12:23:42 +0800 Subject: [PATCH 02/17] update lua-compat-5.3 to HEAD --- deps/lua-compat-5.3 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/lua-compat-5.3 b/deps/lua-compat-5.3 index e245d3a1..8f8e4c6a 160000 --- a/deps/lua-compat-5.3 +++ b/deps/lua-compat-5.3 @@ -1 +1 @@ -Subproject commit e245d3a18957e43ef902a59a72c8902e2e4435b9 +Subproject commit 8f8e4c6adb43e107f5902e784ef207dc3c8ca06b From 3f1583ef599628fd5575f4b8c5dc9ffaac17ff11 Mon Sep 17 00:00:00 2001 From: George Zhao Date: Sat, 20 May 2023 12:24:33 +0800 Subject: [PATCH 03/17] update lua to v5.4.6 --- deps/lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deps/lua b/deps/lua index 5d708c3f..64431851 160000 --- a/deps/lua +++ b/deps/lua @@ -1 +1 @@ -Subproject commit 5d708c3f9cae12820e415d4f89c9eacbe2ab964b +Subproject commit 6443185167c77adcc8552a3fee7edab7895db1a9 From 67218ab7c265ee62aa87c6cff80c85b29e563c64 Mon Sep 17 00:00:00 2001 From: George Zhao Date: Sat, 20 May 2023 12:25:34 +0800 Subject: [PATCH 04/17] feat: introduce uv.metrics_info api --- src/luv.c | 3 +++ src/metrics.c | 24 ++++++++++++++++++++++++ tests/test-metrics.lua | 31 +++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 tests/test-metrics.lua diff --git a/src/luv.c b/src/luv.c index 1bbce26d..141ba4c3 100644 --- a/src/luv.c +++ b/src/luv.c @@ -392,6 +392,9 @@ static const luaL_Reg luv_functions[] = { #if LUV_UV_VERSION_GEQ(1, 39, 0) {"metrics_idle_time", luv_metrics_idle_time}, #endif +#if LUV_UV_VERSION_GEQ(1, 45, 0) + {"metrics_info", luv_metrics_info}, +#endif {NULL, NULL} }; diff --git a/src/metrics.c b/src/metrics.c index bbe59411..48253746 100644 --- a/src/metrics.c +++ b/src/metrics.c @@ -25,3 +25,27 @@ static int luv_metrics_idle_time(lua_State* L) { return 1; } #endif + +#if LUV_UV_VERSION_GEQ(1, 45, 0) +static int luv_metrics_info(lua_State *L) { + uv_metrics_t metrics; + int ret = uv_metrics_info(luv_loop(L), &metrics); + if (ret < 0) return luv_error(L, ret); + + lua_newtable(L); + + lua_pushliteral(L, "loop_count"); + lua_pushinteger(L, metrics.loop_count); + lua_rawset(L, -3); + + lua_pushliteral(L, "events"); + lua_pushinteger(L, metrics.events); + lua_rawset(L, -3); + + lua_pushliteral(L, "events_waiting"); + lua_pushinteger(L, metrics.events_waiting); + lua_rawset(L, -3); + + return 1; +} +#endif diff --git a/tests/test-metrics.lua b/tests/test-metrics.lua new file mode 100644 index 00000000..3991837d --- /dev/null +++ b/tests/test-metrics.lua @@ -0,0 +1,31 @@ + +return require('lib/tap')(function (test) + + test("idle time", function (print, p, expect, uv) + local NS_TO_MS = 1000000 + local timeout = 1000 + local timer = uv.new_timer() + local counter = 0 + timer:start(timeout, 0, function() + counter = counter + 1 + local t = uv.hrtime() + + -- Spin for 500 ms to spin loop time out of the delta check. + while uv.hrtime() - t < 600 * NS_TO_MS do end + timer:close() + + local metrics = uv.metrics_info() + p(metrics) + assert(metrics.loop_count > 0) + assert(metrics.events >= 0) + assert(metrics.events_waiting >= 0) + end) + + local metrics = assert(uv.metrics_info()) + p(metrics) + assert(metrics.loop_count >= 0) + assert(metrics.events >= 0) + assert(metrics.events_waiting >= 0) + end, "1.45.0") + +end) From 3edd4c7a3ca112e5cf9632e059ffb6c464a5481c Mon Sep 17 00:00:00 2001 From: George Zhao Date: Sat, 20 May 2023 12:35:08 +0800 Subject: [PATCH 05/17] doc: introduce uv.uv_metrics_info() api --- docs.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs.md b/docs.md index 05ae93a4..d1e9b71d 100644 --- a/docs.md +++ b/docs.md @@ -3738,6 +3738,22 @@ time until calling `loop_configure` with `"metrics_idle_time"`. **Returns:** `number` +### `uv.metrics_info()` + +Get the metrics table from current set of event loop metrics. + +**Returns:** `table` + +The table contains event loop metrics. It is recommended to retrieve these +metrics in a uv_prepare_cb in order to make sure there are no inconsistencies +with the metrics counters. + +- `loop_count` : `integer` +- `events` : `integer` +- `events_waiting` : `integer` + +**Note**: New in libuv version 1.45.0. + --- [luv]: https://github.com/luvit/luv From c2dac650c9fcc0eb1a03aed21be54e7e447c55b0 Mon Sep 17 00:00:00 2001 From: George Zhao Date: Sat, 20 May 2023 12:45:32 +0800 Subject: [PATCH 06/17] ci: fix build with Luarocks --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c6118528..2b9b81ff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -144,7 +144,7 @@ jobs: - name: Build with Luarocks (alternate rockspec) run: | mkdir ${{github.workspace}}/build/lib - cp ${{github.workspace}}/build/deps/libuv/libuv_a.a ${{github.workspace}}/build/lib/libuv.a + cp ${{github.workspace}}/build/deps/libuv/libuv.a ${{github.workspace}}/build/lib/libuv.a cp -a ${{github.workspace}}/deps/libuv/include ${{github.workspace}}/build luarocks make rockspecs/$(ls rockspecs) LIBUV_DIR=${{github.workspace}}/build LUA_COMPAT53_INCDIR=${{github.workspace}}/deps/lua-compat-5.3/c-api test $PWD = `lua -e "print(require'luv'.cwd())"` From 6b0dd14f94ccbb4597f7c25276391ca0bc93b4ad Mon Sep 17 00:00:00 2001 From: George Zhao Date: Sat, 20 May 2023 13:08:58 +0800 Subject: [PATCH 07/17] minor code style update --- src/fs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/fs.c b/src/fs.c index 917f13f1..8ef36145 100644 --- a/src/fs.c +++ b/src/fs.c @@ -508,6 +508,7 @@ static int luv_fs_read(lua_State* L) { // -1 offset means "the current file offset is used and updated" int64_t offset = -1; int ref; + char* data; // both offset and callback are optional if (luv_is_callable(L, 3) && lua_isnoneornil(L, 4)) { ref = luv_check_continuation(L, 3); @@ -516,7 +517,7 @@ static int luv_fs_read(lua_State* L) { offset = luaL_optinteger(L, 3, offset); ref = luv_check_continuation(L, 4); } - char* data = (char*)malloc(len); + data = (char*)malloc(len); if (!data) { luaL_unref(L, LUA_REGISTRYINDEX, ref); return luaL_error(L, "Failure to allocate buffer"); From 34676b29fda556f364eea4d1e4f4082a33bfbf3a Mon Sep 17 00:00:00 2001 From: George Zhao Date: Sat, 20 May 2023 13:45:37 +0800 Subject: [PATCH 08/17] ci: update for valgrind --- .ci/pthread_create.supp | 9 ----- .ci/valgrind_mem.supp | 71 ++++++++++++++++++++++++++++++++++++++++ .github/workflows/ci.yml | 2 +- 3 files changed, 72 insertions(+), 10 deletions(-) delete mode 100644 .ci/pthread_create.supp create mode 100644 .ci/valgrind_mem.supp diff --git a/.ci/pthread_create.supp b/.ci/pthread_create.supp deleted file mode 100644 index 0160dbb0..00000000 --- a/.ci/pthread_create.supp +++ /dev/null @@ -1,9 +0,0 @@ -{ - pthread_create unknown leak - Memcheck:Leak - match-leak-kinds: possible - fun:calloc - fun:allocate_dtv - fun:_dl_allocate_tls - fun:allocate_stack -} \ No newline at end of file diff --git a/.ci/valgrind_mem.supp b/.ci/valgrind_mem.supp new file mode 100644 index 00000000..fae922e1 --- /dev/null +++ b/.ci/valgrind_mem.supp @@ -0,0 +1,71 @@ +{ + luv_push_stats_table + Memcheck:Cond + fun:luv_push_stats_table +} +{ + tostringbuff + Memcheck:Value8 + fun:_itoa_word + fun:__vfprintf_internal + fun:__vsnprintf_internal + fun:snprintf + fun:tostringbuff +} +{ + tostringbuff Conditional jump or move depends on uninitialised value(s) + Memcheck:Cond + fun:_itoa_word + fun:__vfprintf_internal + fun:__vsnprintf_internal + fun:snprintf + fun:tostringbuff +} +{ + addnum2buff Conditional jump or move depends on uninitialised value(s) + Memcheck:Cond + fun:__vfprintf_internal + fun:__vsnprintf_internal + fun:snprintf + fun:tostringbuff + fun:addnum2buff + fun:luaO_pushvfstring + fun:lua_pushfstring + fun:luaL_tolstring + fun:luaB_tostring + fun:precallC + fun:luaD_precall + fun:luaV_execute +} +{ + luv_fs_read + Memcheck:Cond + fun:malloc + fun:luv_fs_read +} +{ + luv_fulfill_req + Memcheck:Cond + fun:luaV_execute + fun:ccall + fun:luaD_callnoyield + fun:f_call + fun:luaD_rawrunprotected + fun:luaD_pcall + fun:lua_pcallk + fun:luv_fulfill_req +} +{ + Conditional jump or move depends on uninitialised value(s) + Memcheck:Cond + fun:luaV_execute + fun:ccall + fun:luaD_callnoyield + fun:f_call + fun:luaD_rawrunprotected + fun:luaD_pcall + fun:lua_pcallk + fun:luv_cfpcall + fun:luv_fulfill_req + fun:luv_fs_cb +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2b9b81ff..0f761285 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,7 +70,7 @@ jobs: - name: Build run: make - name: Test - run: valgrind --suppressions=.ci/pthread_create.supp --error-exitcode=1 --leak-check=full --child-silent-after-fork=yes --keep-debuginfo=yes ./build/lua -e "collectgarbage('setpause', 0); collectgarbage('setstepmul', 10000000000000)" tests/run.lua + run: valgrind --suppressions=.ci/valgrind_mem.supp --error-exitcode=1 --leak-check=full --child-silent-after-fork=yes --keep-debuginfo=yes ./build/lua -e "collectgarbage('setpause', 0); collectgarbage('setstepmul', 10000000000000)" tests/run.lua process-cleanup-test: runs-on: ubuntu-latest From 46a91c9b15be8f51026bdf48b6c8898d3feb654f Mon Sep 17 00:00:00 2001 From: George Zhao Date: Sat, 20 May 2023 15:06:09 +0800 Subject: [PATCH 09/17] ci: valgrind with --track-origins=yes --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0f761285..40362883 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,7 +70,7 @@ jobs: - name: Build run: make - name: Test - run: valgrind --suppressions=.ci/valgrind_mem.supp --error-exitcode=1 --leak-check=full --child-silent-after-fork=yes --keep-debuginfo=yes ./build/lua -e "collectgarbage('setpause', 0); collectgarbage('setstepmul', 10000000000000)" tests/run.lua + run: valgrind --error-exitcode=1 --leak-check=full --child-silent-after-fork=yes --keep-debuginfo=yes --track-origins=yes ./build/lua -e "collectgarbage('setpause', 0); collectgarbage('setstepmul', 10000000000000)" tests/run.lua process-cleanup-test: runs-on: ubuntu-latest From 600a21716dd67a3609e9e1a1664cb52f14f8c1e7 Mon Sep 17 00:00:00 2001 From: George Zhao Date: Sat, 20 May 2023 15:30:40 +0800 Subject: [PATCH 10/17] ci: add suppressions to make valgrind report OK --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 40362883..521f63e7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,7 +70,7 @@ jobs: - name: Build run: make - name: Test - run: valgrind --error-exitcode=1 --leak-check=full --child-silent-after-fork=yes --keep-debuginfo=yes --track-origins=yes ./build/lua -e "collectgarbage('setpause', 0); collectgarbage('setstepmul', 10000000000000)" tests/run.lua + run: valgrind --suppressions=.ci/valgrind_mem.supp --error-exitcode=1 --leak-check=full --child-silent-after-fork=yes --keep-debuginfo=yes --track-origins=yes ./build/lua -e "collectgarbage('setpause', 0); collectgarbage('setstepmul', 10000000000000)" tests/run.lua process-cleanup-test: runs-on: ubuntu-latest From 705f8edeaf8a1f2eb2e96114e102b58787121090 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Fri, 19 May 2023 23:46:22 -0700 Subject: [PATCH 11/17] Add cpumask_size, get_available_memory, thread_getaffinity, thread_setaffinity, thread_getcpu bindings --- docs.md | 70 ++++++++++++++++++++++++++++++++++++ src/luv.c | 9 +++++ src/misc.c | 14 ++++++++ src/thread.c | 82 +++++++++++++++++++++++++++++++++++++++++++ tests/test-misc.lua | 20 ++++++++++- tests/test-thread.lua | 43 +++++++++++++++++++++++ 6 files changed, 237 insertions(+), 1 deletion(-) diff --git a/docs.md b/docs.md index d1e9b71d..b942924d 100644 --- a/docs.md +++ b/docs.md @@ -3286,6 +3286,60 @@ equivalent to the `__eq` metamethod. **Returns:** `boolean` +### `uv.thread_setaffinity(thread, affinity, [get_old_affinity])` + +> method form `thread:setaffinity(affinity, [get_old_affinity])` + +**Parameters:** +- `thread`: `luv_thread_t userdata` +- `affinity`: `table` + - `[1, 2, 3, ..., n]` : `boolean` +- `get_old_affinity`: `boolean` + +Sets the specified thread's affinity setting. `affinity` must be an array-like +table where each of the keys correspond to a CPU number and the values are +booleans that represent whether the `thread` should be eligible to run on that +CPU. The length of the `affinity` table must be greater than or equal to +`uv.cpumask_size()`. If `get_old_affinity` is `true`, the previous affinity +settings for the `thread` will be returned. Otherwise, `true` is returned after +a successful call. + +**Note:** Thread affinity setting is not atomic on Windows. Unsupported on macOS. + +**Returns:** `table` or `boolean` or `fail` +- `[1, 2, 3, ..., n]` : `boolean` + +### `uv.thread_getaffinity(thread, [mask_size])` + +> method form `thread:getaffinity([mask_size])` + +**Parameters:** +- `thread`: `luv_thread_t userdata` +- `mask_size`: `integer` + +Gets the specified thread's affinity setting. + +If `mask_size` is provided, it must be greater than or equal to +`uv.cpumask_size()`. If the `mask_size` parameter is omitted, then the return +of `uv.cpumask_size()` will be used. Returns an array-like table where each of +the keys correspond to a CPU number and the values are booleans that represent +whether the `thread` is eligible to run on that CPU. + +**Note:** Thread affinity getting is not atomic on Windows. Unsupported on macOS. + +**Returns:** `table` or `fail` +- `[1, 2, 3, ..., n]` : `boolean` + +### `uv.thread_getcpu()` + +Gets the CPU number on which the calling thread is running. + +**Note:** The first CPU will be returned as the number 1, not 0. This allows for +the number to correspond with the table keys used in `uv.thread_getaffinity` and +`uv.thread_setaffinity`. + +**Returns:** `integer` or `fail` + ### `uv.thread_self()` Returns the handle for the thread in which this is called. @@ -3373,6 +3427,15 @@ greater than the total system memory. **Returns:** `number` +### `uv.get_available_memory()` + +Gets the amount of free memory that is still available to the process (in +bytes). This differs from `uv.get_free_memory()` in that it takes into account +any limits imposed by the OS. If there is no such constraint, or the constraint +is unknown, the amount returned will be identical to `uv.get_free_memory()`. + +**Returns:** `number` + ### `uv.resident_set_memory()` Returns the resident set size (RSS) for the current process. @@ -3433,6 +3496,13 @@ CPU found. - `idle` : `number` - `irq` : `number` +### `uv.cpumask_size()` + +Returns the maximum size of the mask used for process/thread affinities, or +`ENOTSUP` if affinities are not supported on the current platform. + +**Returns:** `integer` or `fail` + ### `uv.getpid()` **Deprecated:** Please use `uv.os_getpid()` instead. diff --git a/src/luv.c b/src/luv.c index 141ba4c3..79d5dfa4 100644 --- a/src/luv.c +++ b/src/luv.c @@ -372,12 +372,21 @@ static const luaL_Reg luv_functions[] = { {"random", luv_random}, #endif {"sleep", luv_sleep}, +#if LUV_UV_VERSION_GEQ(1, 45, 0) + {"cpumask_size", luv_cpumask_size}, + {"get_available_memory", luv_get_available_memory}, +#endif // thread.c {"new_thread", luv_new_thread}, {"thread_equal", luv_thread_equal}, {"thread_self", luv_thread_self}, {"thread_join", luv_thread_join}, +#if LUV_UV_VERSION_GEQ(1, 45, 0) + {"thread_getaffinity", luv_thread_getaffinity}, + {"thread_setaffinity", luv_thread_setaffinity}, + {"thread_getcpu", luv_thread_getcpu}, +#endif // work.c {"new_work", luv_new_work}, diff --git a/src/misc.c b/src/misc.c index 26be960f..6fe7e7b6 100644 --- a/src/misc.c +++ b/src/misc.c @@ -750,3 +750,17 @@ static int luv_random(lua_State* L) { } } #endif + +#if LUV_UV_VERSION_GEQ(1, 45, 0) +static int luv_cpumask_size(lua_State* L) { + int ret = uv_cpumask_size(); + if (ret < 0) return luv_error(L, ret); + lua_pushinteger(L, ret); + return 1; +} + +static int luv_get_available_memory(lua_State* L) { + lua_pushinteger(L, uv_get_available_memory()); + return 1; +} +#endif diff --git a/src/thread.c b/src/thread.c index 5e780102..b631cfcd 100644 --- a/src/thread.c +++ b/src/thread.c @@ -382,6 +382,84 @@ static int luv_new_thread(lua_State* L) { return 1; } +#if LUV_UV_VERSION_GEQ(1, 45, 0) +static int luv_thread_getaffinity(lua_State* L) { + luv_thread_t* tid = luv_check_thread(L, 1); + int default_mask_size = uv_cpumask_size(); + if (default_mask_size < 0) { + return luv_error(L, default_mask_size); + } + int mask_size = luaL_optinteger(L, 2, default_mask_size); + if (mask_size < default_mask_size) { + return luaL_argerror(L, 2, lua_pushfstring(L, "cpumask size must be >= %d (from cpumask_size()), got %d", default_mask_size, mask_size)); + } + char* cpumask = malloc(mask_size); + int ret = uv_thread_getaffinity(tid, cpumask, mask_size); + if (ret < 0) { + free(cpumask); + return luv_error(L, ret); + } + lua_newtable(L); + for (int i = 0; i < mask_size; i++) { + lua_pushboolean(L, cpumask[i]); + lua_rawseti(L, -2, i + 1); + } + free(cpumask); + return 1; +} + +static int luv_thread_setaffinity(lua_State* L) { + luv_thread_t* tid = luv_check_thread(L, 1); + luaL_checktype(L, 2, LUA_TTABLE); + int get_old_mask = lua_toboolean(L, 3); + int min_mask_size = uv_cpumask_size(); + if (min_mask_size < 0) { + return luv_error(L, min_mask_size); + } + int mask_size = lua_rawlen(L, 2); + if (mask_size < min_mask_size) { + return luaL_argerror(L, 2, lua_pushfstring(L, "cpumask size must be >= %d (from cpumask_size()), got %d", min_mask_size, mask_size)); + } + char* cpumask = malloc(mask_size); + for (int i = 0; i < mask_size; i++) { + lua_rawgeti(L, 2, i+1); + int val = lua_toboolean(L, -1); + cpumask[i] = val; + lua_pop(L, 1); + } + char* oldmask = get_old_mask ? malloc(mask_size) : NULL; + int ret = uv_thread_setaffinity(tid, cpumask, oldmask, mask_size); + // Done with cpumask at this point + free(cpumask); + if (ret < 0) { + if (get_old_mask) free(oldmask); + return luv_error(L, ret); + } + if (get_old_mask) { + lua_newtable(L); + for (int i = 0; i < mask_size; i++) { + lua_pushboolean(L, oldmask[i]); + lua_rawseti(L, -2, i + 1); + } + free(oldmask); + } + else { + lua_pushboolean(L, 1); + } + return 1; +} + +static int luv_thread_getcpu(lua_State* L) { + int ret = uv_thread_getcpu(); + if (ret < 0) return luv_error(L, ret); + // Make the returned value start at 1 to match how getaffinity/setaffinity + // masks are implemented (they use array-like tables so the first + // CPU is index 1). + lua_pushinteger(L, ret + 1); + return 1; +} +#endif + static int luv_thread_join(lua_State* L) { luv_thread_t* tid = luv_check_thread(L, 1); int ret = uv_thread_join(&tid->handle); @@ -414,6 +492,10 @@ static int luv_thread_equal(lua_State* L) { static const luaL_Reg luv_thread_methods[] = { {"equal", luv_thread_equal}, {"join", luv_thread_join}, +#if LUV_UV_VERSION_GEQ(1, 45, 0) + {"getaffinity", luv_thread_getaffinity}, + {"setaffinity", luv_thread_setaffinity}, +#endif {NULL, NULL} }; diff --git a/tests/test-misc.lua b/tests/test-misc.lua index fcacfc5b..7d409a92 100644 --- a/tests/test-misc.lua +++ b/tests/test-misc.lua @@ -23,9 +23,15 @@ return require('lib/tap')(function (test) local constrained = nil if uv.get_constrained_memory then constrained = uv.get_constrained_memory() + assert(constrained >= 0) + end + local available = nil + if uv.get_available_memory then + available = uv.get_available_memory() + assert(available >= 0) end local free = uv.get_free_memory() - p{rss=rss,total=total,free=free, constrained=constrained} + p{rss=rss,total=total,free=free,available=available,constrained=constrained} assert(rss < total) end) @@ -182,4 +188,16 @@ return require('lib/tap')(function (test) end end) + test("uv.cpumask_size", function(print, p, expect, uv) + -- The result can vary per-platform and is only supported on some platforms, + -- so just test that the function exists and behaves coherently. + local size, err = uv.cpumask_size() + p(size, err) + if err then + assert(not size) + else + assert(size >= 0) + end + end, "1.45.0") + end) diff --git a/tests/test-thread.lua b/tests/test-thread.lua index 5491906f..32b77e72 100644 --- a/tests/test-thread.lua +++ b/tests/test-thread.lua @@ -108,4 +108,47 @@ return require('lib/tap')(function (test) collectgarbage('collect') collectgarbage('collect') end) + + test("thread_getcpu", function(print, p, expect, uv) + local cpu, err = uv.thread_getcpu() + if not cpu then + print(err, "skipping") + return + end + -- starts at 1 to match the tables used by getaffinity/setaffinity + assert(cpu >= 1) + end, "1.45.0") + + test("getaffinity, setaffinity", function(print, p, expect, uv) + local mask_size, err = uv.cpumask_size() + if not mask_size then + print(err, "skipping") + return + end + uv.new_thread(function(cpumask_size) + local _uv = require('luv') + local thread = _uv.thread_self() + local affinity = assert(thread:getaffinity()) + assert(#affinity == cpumask_size) + + -- set every cpu's affinity to false except the current cpu + local cur_cpu = _uv.thread_getcpu() + local affinity_to_set = {} + for i=1,cpumask_size do + affinity_to_set[i] = i == cur_cpu + end + local prev_affinity = assert(thread:setaffinity(affinity_to_set, true)) + -- the returned affinity should match the original affinity + assert(#prev_affinity == #affinity) + for i=1,#affinity do + assert(prev_affinity[i] == affinity[i]) + end + + local new_affinity = thread:getaffinity() + assert(#new_affinity == #affinity_to_set) + for i=1,#new_affinity do + assert(new_affinity[i] == affinity_to_set[i]) + end + end, mask_size):join() + end, "1.45.0") end) From 191641f1cd10e1771b3687c3ec2207b896e5b444 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Sat, 20 May 2023 00:47:52 -0700 Subject: [PATCH 12/17] Add clock_gettime binding --- docs.md | 18 ++++++++++++++++++ src/luv.c | 1 + src/misc.c | 18 ++++++++++++++++++ tests/test-misc.lua | 13 +++++++++++++ 4 files changed, 50 insertions(+) diff --git a/docs.md b/docs.md index b942924d..f66b0ae0 100644 --- a/docs.md +++ b/docs.md @@ -3554,6 +3554,24 @@ time between intervals. **Returns:** `number` +### `uv.clock_gettime(clock_id)` + +**Parameters:** +- `clock_id`: `string` + +Obtain the current system time from a high-resolution real-time or monotonic +clock source. `clock_id` can be the string `"monotonic"` or `"realtime"`. + +The real-time clock counts from the UNIX epoch (1970-01-01) and is subject +to time adjustments; it can jump back in time. + +The monotonic clock counts from an arbitrary point in the past and never +jumps back in time. + +**Returns:** `table` or `fail` +- `sec`: `integer` +- `nsec`: `integer` + ### `uv.uptime()` Returns the current system uptime in seconds. diff --git a/src/luv.c b/src/luv.c index 79d5dfa4..ad596faf 100644 --- a/src/luv.c +++ b/src/luv.c @@ -375,6 +375,7 @@ static const luaL_Reg luv_functions[] = { #if LUV_UV_VERSION_GEQ(1, 45, 0) {"cpumask_size", luv_cpumask_size}, {"get_available_memory", luv_get_available_memory}, + {"clock_gettime", luv_clock_gettime}, #endif // thread.c diff --git a/src/misc.c b/src/misc.c index 6fe7e7b6..b29a3ad9 100644 --- a/src/misc.c +++ b/src/misc.c @@ -763,4 +763,22 @@ static int luv_get_available_memory(lua_State* L) { lua_pushinteger(L, uv_get_available_memory()); return 1; } + +// These are the same order as uv_membership which also starts at 0 +static const char *const luv_clock_id_opts[] = { + "monotonic", "realtime", NULL +}; + +static int luv_clock_gettime(lua_State* L) { + uv_clock_id clock_id = (uv_clock_id)luaL_checkoption(L, 1, NULL, luv_clock_id_opts); + uv_timespec64_t timespec; + int ret = uv_clock_gettime(clock_id, ×pec); + if (ret < 0) return luv_error(L, ret); + lua_createtable(L, 0, 2); + lua_pushinteger(L, timespec.tv_sec); + lua_setfield(L, -2, "sec"); + lua_pushinteger(L, timespec.tv_nsec); + lua_setfield(L, -2, "nsec"); + return 1; +} #endif diff --git a/tests/test-misc.lua b/tests/test-misc.lua index 7d409a92..a69bd882 100644 --- a/tests/test-misc.lua +++ b/tests/test-misc.lua @@ -200,4 +200,17 @@ return require('lib/tap')(function (test) end end, "1.45.0") + test("uv.clock_gettime", function(print, p, expect, uv) + for _, clock_id in ipairs({"monotonic", "realtime"}) do + local timespec, err = uv.clock_gettime("monotonic") + p(clock_id, timespec, err) + if err then + assert(not timespec) + else + assert(timespec.sec >= 0) + assert(timespec.nsec >= 0) + end + end + end, "1.45.0") + end) From 1054d71958fd7cd3bf74cf7bc8388acbabeaaf79 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Sat, 20 May 2023 01:00:00 -0700 Subject: [PATCH 13/17] bindcov: Skip uv_os_{get_passwd2,get_group,free_group} for now These functions are public but currently undocumented. Will wait to bind them until they have documentation. --- .ci/bindcov.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.ci/bindcov.sh b/.ci/bindcov.sh index 02d948c7..da718f8f 100755 --- a/.ci/bindcov.sh +++ b/.ci/bindcov.sh @@ -3,6 +3,10 @@ unbound_functions=0 skipped=() +# waiting on these being documented +# https://github.com/libuv/libuv/issues/4007 +skipped+=(uv_os_get_passwd2 uv_os_get_group uv_os_free_group) + # false positives skipped+=(uv_thread_create uv_thread_create_ex) From 6dca7a576083ffd6b11254d44651ce1ae98f2ae8 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Sat, 20 May 2023 01:08:12 -0700 Subject: [PATCH 14/17] appveyor: `uv_a.lib` -> `libuv.lib` Changed in https://github.com/libuv/libuv/pull/2085 --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index e3f141a3..3ef72191 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -64,7 +64,7 @@ for: - luarocks remove luv # Test the alternate rockspec - mkdir build\lib - - cp build.luarocks\deps\libuv\Release\uv_a.lib build\lib\uv.lib + - cp build.luarocks\deps\libuv\Release\libuv.lib build\lib\uv.lib - cp -a deps\libuv\include build - ps: luarocks make rockspecs\$(ls rockspecs) LIBUV_DIR=build LUA_COMPAT53_INCDIR=deps/lua-compat-5.3/c-api CFLAGS="/nologo /MT /O2" - ps: if("$(Get-Location)" -eq $(lua -e "print(require'luv'.cwd())")) { "LuaRocks test OK" } else { "LuaRocks test failed"; exit 1 } From 87e6530bffb903a8e172853530f8ed582448e450 Mon Sep 17 00:00:00 2001 From: George Zhao Date: Sat, 20 May 2023 17:03:04 +0800 Subject: [PATCH 15/17] fix: use lua_pushnumber, avoid to cast to singed --- src/misc.c | 2 +- tests/test-misc.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/misc.c b/src/misc.c index b29a3ad9..548e029f 100644 --- a/src/misc.c +++ b/src/misc.c @@ -760,7 +760,7 @@ static int luv_cpumask_size(lua_State* L) { } static int luv_get_available_memory(lua_State* L) { - lua_pushinteger(L, uv_get_available_memory()); + lua_pushnumber(L, uv_get_available_memory()); return 1; } diff --git a/tests/test-misc.lua b/tests/test-misc.lua index a69bd882..c1811d5b 100644 --- a/tests/test-misc.lua +++ b/tests/test-misc.lua @@ -28,7 +28,7 @@ return require('lib/tap')(function (test) local available = nil if uv.get_available_memory then available = uv.get_available_memory() - assert(available >= 0) + assert(available >= 0, available) end local free = uv.get_free_memory() p{rss=rss,total=total,free=free,available=available,constrained=constrained} From d252331f267b068d4f7c31e694cafb6bed865b1b Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Sat, 20 May 2023 03:19:55 -0700 Subject: [PATCH 16/17] Fix thread_{get,set}affinity passing `luv_thread_t*` to libuv instead of `uv_thread_t*` This happened to work since the uv_thread_t is at the start of the luv_thread_t structs so their pointers happen to be equal, but it's definitely worth fixing. --- src/thread.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/thread.c b/src/thread.c index b631cfcd..2d138fef 100644 --- a/src/thread.c +++ b/src/thread.c @@ -394,7 +394,7 @@ static int luv_thread_getaffinity(lua_State* L) { return luaL_argerror(L, 2, lua_pushfstring(L, "cpumask size must be >= %d (from cpumask_size()), got %d", default_mask_size, mask_size)); } char* cpumask = malloc(mask_size); - int ret = uv_thread_getaffinity(tid, cpumask, mask_size); + int ret = uv_thread_getaffinity(&tid->handle, cpumask, mask_size); if (ret < 0) { free(cpumask); return luv_error(L, ret); @@ -428,7 +428,7 @@ static int luv_thread_setaffinity(lua_State* L) { lua_pop(L, 1); } char* oldmask = get_old_mask ? malloc(mask_size) : NULL; - int ret = uv_thread_setaffinity(tid, cpumask, oldmask, mask_size); + int ret = uv_thread_setaffinity(&tid->handle, cpumask, oldmask, mask_size); // Done with cpumask at this point free(cpumask); if (ret < 0) { From 5ff84fa144893b0d163936eaa846b9b6eeed0f4e Mon Sep 17 00:00:00 2001 From: George Zhao Date: Sat, 20 May 2023 18:50:54 +0800 Subject: [PATCH 17/17] update for rockspec --- rockspecs/luv-scm-0.rockspec | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rockspecs/luv-scm-0.rockspec b/rockspecs/luv-scm-0.rockspec index dcf612a0..6527b5aa 100644 --- a/rockspecs/luv-scm-0.rockspec +++ b/rockspecs/luv-scm-0.rockspec @@ -79,6 +79,9 @@ build = { 'userenv'; 'ws2_32'; 'advapi32'; + 'Dbghelp'; + "Ole32"; + "Shell32"; }; }; };