From ffedd175233975f3ca9ac940df9883898d5ace25 Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Wed, 3 Jul 2024 13:21:45 +0200 Subject: Renamed set_debug_threadname → lane_threadname (can also read the current name now) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGES | 3 ++- docs/index.html | 4 ++-- src/lane.cpp | 48 +++++++++++++++++++++++++++++++----------------- src/lane.h | 14 ++++++++++++++ src/tracker.cpp | 2 +- tests/basic.lua | 12 ++++++------ tests/cancel.lua | 2 +- tests/deadlock.lua | 2 +- tests/fibonacci.lua | 4 ++-- tests/track_lanes.lua | 8 ++++---- 10 files changed, 64 insertions(+), 35 deletions(-) diff --git a/CHANGES b/CHANGES index 9b87eda..b6967ea 100644 --- a/CHANGES +++ b/CHANGES @@ -24,6 +24,7 @@ CHANGE 2: BGe 11-Jun-24 - lane function body must return a non-nil first value on success if lane is waited upon with lane:join(). - lanes.sleep() accept a new argument "indefinitely" to block forever (until hard cancellation is received). - lanes.gen() is stricter wrt base libraries (can raise an error if it doesn't exist in the Lua flavor it's built against). + - function set_debug_threadname() available inside a Lane is renamed lane_threadname(); can now both read and write the name. - Lindas: - providing "auto" as name when constructing a Linda cause Lanes to provide a name built from the source location of the construction. - specifying a group to lanes.linda() is mandatory when Lanes is configured with user Keepers. @@ -35,7 +36,7 @@ CHANGE 2: BGe 11-Jun-24 - deep userdata are an acceptable key to send data into (for example, another linda). - Lane generator settings: - error_trace_level added. Replaces the global verbose_errors setting. - - name added. Can be used to set the name early (before the lane body calls set_debug_threadname()). + - name added. Can be used to set the name early (before the lane body calls lane_threadname()). * Internal changes - Lanes is implemented in C++20: thread, condition_variable, mutex, string_view, variant, and more! - Almost all platform-specific code is gone (only a small bit for thread priority and affinity remains). diff --git a/docs/index.html b/docs/index.html index 0cdf954..4fd7447 100644 --- a/docs/index.html +++ b/docs/index.html @@ -736,7 +736,7 @@ string - Name of the lane. If "auto", name is built from ar.short_src:ar.linedefined. Can be changed later from the inside of the lane with set_debug_threadname() (see below). + Name of the lane. If "auto", name is built from ar.short_src:ar.linedefined. Can be changed later from the inside of the lane with lane_threadname() (see below). @@ -773,7 +773,7 @@

- Each lane gets a global function set_debug_threadname() that it can use anytime to do as the name says. Supported debuggers are Microsoft Visual Studio (for the C side) and Decoda (for the Lua side).
+ Each lane gets a global function lane_threadname() that it can use anytime to do both read and change the thread name. Supported debuggers are Microsoft Visual Studio (for the C side) and Decoda (for the Lua side).
Change HAVE_DECODA_SUPPORT() in lanesconf.h to enable the Decoda support, that sets a special global variable decoda_name in the lane's state.
The name is stored inside the Lua state registry so that it is available for error reporting. Changing decoda_name doesn't affect this hidden name or the OS thread name reported by MSVC.
When Lanes is initialized by the first lanes.configure() call, "main" is stored in the registry in the same fashion (but decoda_name and the OS thread name are left unchanged).
diff --git a/src/lane.cpp b/src/lane.cpp index dd038a3..8c4062c 100644 --- a/src/lane.cpp +++ b/src/lane.cpp @@ -44,11 +44,12 @@ static constexpr UniqueKey kCachedTostring{ 0xAB5EA23BCEA0C35Cull }; // ################################################################################################# // ################################################################################################# +// lane:get_debug_threadname() static LUAG_FUNC(get_debug_threadname) { Lane* const _lane{ ToLane(L_, 1) }; luaL_argcheck(L_, lua_gettop(L_) == 1, 2, "too many arguments"); - luaG_pushstring(L_, _lane->debugName); + luaG_pushstring(L_, _lane->getDebugName()); return 1; } @@ -78,17 +79,26 @@ static LUAG_FUNC(set_finalizer) // ################################################################################################# +// serves both to read and write the name from the inside of the lane // upvalue #1 is the lane userdata -static LUAG_FUNC(set_debug_threadname) +// this function is exported in a lane's state, therefore it is callable only from inside the Lane's state +static LUAG_FUNC(lane_threadname) { // C s_lane structure is a light userdata upvalue Lane* const _lane{ luaG_tolightuserdata(L_, lua_upvalueindex(1)) }; LUA_ASSERT(L_, L_ == _lane->L); // this function is exported in a lane's state, therefore it is callable only from inside the Lane's state - lua_settop(L_, 1); - STACK_CHECK_START_REL(L_, 0); - _lane->changeDebugName(-1); - STACK_CHECK(L_, 0); - return 0; + if (lua_gettop(L_) == 1) { + lua_settop(L_, 1); + STACK_CHECK_START_REL(L_, 0); + _lane->changeDebugName(-1); + STACK_CHECK(L_, 0); + return 0; + } else if (lua_gettop(L_) == 0) { + luaG_pushstring(L_, _lane->getDebugName()); + return 1; + } else { + raise_luaL_error(L_, "Wrong number of arguments"); + } } // ################################################################################################# @@ -339,7 +349,7 @@ static int thread_index_number(lua_State* L_) lua_replace(L_, -3); // L_: lane n error() "error" lua_pushinteger(L_, 3); // L_: lane n error() "error" 3 lua_call(L_, 2, 0); // error(tostring(errstring), 3) -> doesn't return // L_: lane n - raise_luaL_error(L_, "%s: should not get here!", _lane->debugName.data()); + raise_luaL_error(L_, "%s: should not get here!", _lane->getDebugName().data()); } else { lua_pop(L_, 1); // L_: lane n {uv} } @@ -416,7 +426,7 @@ static LUAG_FUNC(thread_index) lua_call(L_, 1, 1); // L_: mt error() "Unknown key: " "k" lua_concat(L_, 2); // L_: mt error() "Unknown key: " lua_call(L_, 1, 0); // error( "Unknown key: " .. key) -> doesn't return // L_: mt - raise_luaL_error(L_, "%s[%s]: should not get here!", _lane->debugName.data(), luaG_typename(L_, kKey).data()); + raise_luaL_error(L_, "%s[%s]: should not get here!", _lane->getDebugName().data(), luaG_typename(L_, kKey).data()); } } @@ -716,11 +726,11 @@ static void PrepareLaneHelpers(Lane* lane_) tools::PopulateFuncLookupTable(_L, -1, "set_finalizer"); lua_setglobal(_L, "set_finalizer"); - // Tie "set_debug_threadname()" to the state + // Tie "lane_threadname()" to the state // But don't register it in the lookup database because of the Lane pointer upvalue lua_pushlightuserdata(_L, lane_); - lua_pushcclosure(_L, LG_set_debug_threadname, 1); - lua_setglobal(_L, "set_debug_threadname"); + lua_pushcclosure(_L, LG_lane_threadname, 1); + lua_setglobal(_L, "lane_threadname"); // Tie "cancel_test()" to the state lua_pushcfunction(_L, LG_cancel_test); @@ -860,7 +870,7 @@ static LUAG_FUNC(lane_gc) lua_rawget(L_, -2); // L_: ud uservalue gc_cb|nil if (!lua_isnil(L_, -1)) { lua_remove(L_, -2); // L_: ud gc_cb|nil - luaG_pushstring(L_, _lane->debugName); // L_: ud gc_cb name + luaG_pushstring(L_, _lane->getDebugName()); // L_: ud gc_cb name _have_gc_cb = true; } else { lua_pop(L_, 2); // L_: ud @@ -879,8 +889,6 @@ static LUAG_FUNC(lane_gc) } else if (_lane->L) { // no longer accessing the Lua VM: we can close right now _lane->closeState(); - // just in case, but _lane will be freed soon so... - _lane->debugName = std::string_view{ "" }; } // Clean up after a (finished) thread @@ -1003,7 +1011,10 @@ void Lane::changeDebugName(int const nameIdx_) // store a hidden reference in the registry to make sure the string is kept around even if a lane decides to manually change the "decoda_name" global... kLaneNameRegKey.setValue(L, [idx = _nameIdx](lua_State* L_) { lua_pushvalue(L_, idx); }); // L: ... "name" ... // keep a direct pointer on the string - debugName = luaG_tostring(L, _nameIdx); + { + std::lock_guard _guard{ debugNameMutex }; + debugName = luaG_tostring(L, _nameIdx); + } if constexpr (HAVE_DECODA_SUPPORT()) { // to see VM name in Decoda debugger Virtual Machine window lua_pushvalue(L, _nameIdx); // L: ... "name" ... "name" @@ -1119,7 +1130,10 @@ void Lane::securizeDebugName(lua_State* L_) LUA_ASSERT(L_, lua_istable(L_, -1)); // we don't care about the actual key, so long as it's unique and can't collide with anything. lua_newtable(L_); // L_: lane ... {uv} {} - debugName = luaG_pushstring(L_, debugName); // L_: lane ... {uv} {} name + { + std::lock_guard _guard{ debugNameMutex }; + debugName = luaG_pushstring(L_, debugName); // L_: lane ... {uv} {} name + } lua_rawset(L_, -3); // L_: lane ... {uv} lua_pop(L_, 1); // L_: lane STACK_CHECK(L_, 0); diff --git a/src/lane.h b/src/lane.h index 753c230..964c3c0 100644 --- a/src/lane.h +++ b/src/lane.h @@ -89,8 +89,13 @@ class Lane // M: sub-thread OS thread // S: not used + private: + + mutable std::mutex debugNameMutex; std::string_view debugName{ "" }; + public: + Universe* const U{}; lua_State* S{}; // the master state of the lane lua_State* L{}; // the state we run things in (either S or a lua_newthread() state if we run in coroutine mode) @@ -143,12 +148,21 @@ class Lane void changeDebugName(int const nameIdx_); void closeState() { + { + std::lock_guard _guard{ debugNameMutex }; + debugName = std::string_view{ "" }; + } lua_State* _L{ S }; S = nullptr; L = nullptr; lua_close(_L); // this collects our coroutine thread at the same time } [[nodiscard]] std::string_view errorTraceLevelString() const; + [[nodiscard]] std::string_view getDebugName() const + { + std::lock_guard _guard{ debugNameMutex }; + return debugName; + } [[nodiscard]] int pushErrorHandler() const; [[nodiscard]] std::string_view pushErrorTraceLevel(lua_State* L_) const; static void PushMetatable(lua_State* L_); diff --git a/src/tracker.cpp b/src/tracker.cpp index 05902c9..16c9c55 100644 --- a/src/tracker.cpp +++ b/src/tracker.cpp @@ -94,7 +94,7 @@ void LaneTracker::tracking_add(Lane* lane_) while (_lane != TRACKING_END) { // insert a { name='', status='' } tuple, so that several lanes with the same name can't clobber each other lua_createtable(L_, 0, 2); // L_: {} {} - luaG_pushstring(L_, _lane->debugName); // L_: {} {} "name" + luaG_pushstring(L_, _lane->getDebugName()); // L_: {} {} "name" lua_setfield(L_, -2, "name"); // L_: {} {} _lane->pushStatusString(L_); // L_: {} {} "" lua_setfield(L_, -2, "status"); // L_: {} {} diff --git a/tests/basic.lua b/tests/basic.lua index 97783cb..f44c75f 100644 --- a/tests/basic.lua +++ b/tests/basic.lua @@ -76,7 +76,7 @@ end PRINT("\n\n", "---=== Tasking (basic) ===---", "\n\n") local function task(a, b, c) - set_debug_threadname("task("..a..","..b..","..c..")") + lane_threadname("task("..a..","..b..","..c..")") --error "111" -- testing error messages assert(hey) local v=0 @@ -329,7 +329,7 @@ SLEEP(1) PRINT("\n\n", "---=== Stdlib naming ===---", "\n\n") local function dump_g(_x) - set_debug_threadname "dump_g" + lane_threadname "dump_g" assert(print) print("### dumping _G for '" .. _x .. "'") for k, v in pairs(_G) do @@ -339,7 +339,7 @@ local function dump_g(_x) end local function io_os_f(_x) - set_debug_threadname "io_os_f" + lane_threadname "io_os_f" assert(print) print("### checking io and os libs existence for '" .. _x .. "'") assert(io) @@ -348,7 +348,7 @@ local function io_os_f(_x) end local function coro_f(_x) - set_debug_threadname "coro_f" + lane_threadname "coro_f" assert(print) print("### checking coroutine lib existence for '" .. _x .. "'") assert(coroutine) @@ -388,7 +388,7 @@ PRINT("\n\n", "---=== Comms criss cross ===---", "\n\n") -- local tc= lanes_gen("io", {gc_cb = gc_cb}, function(linda, ch_in, ch_out) - set_debug_threadname("criss cross " .. ch_in .. " -> " .. ch_out) + lane_threadname("criss cross " .. ch_in .. " -> " .. ch_out) local function STAGE(str) io.stderr:write(ch_in..": "..str.."\n") linda:send(nil, ch_out, str) @@ -488,7 +488,7 @@ PRINT("\n\n", "---=== :join test ===---", "\n\n") local S= lanes_gen("table", {gc_cb = gc_cb}, function(arg) - set_debug_threadname "join test lane" + lane_threadname "join test lane" set_finalizer(function() end) aux= {} for i, v in ipairs(arg) do diff --git a/tests/cancel.lua b/tests/cancel.lua index 1724e6a..9ee986c 100644 --- a/tests/cancel.lua +++ b/tests/cancel.lua @@ -75,7 +75,7 @@ end local laneBody = function( mode_, payload_) local name = "laneBody("..tostring(mode_)..","..tostring(payload_)..")" - set_debug_threadname( name) + lane_threadname(name) set_finalizer( function( err, stk) if err == lanes.cancel_error then diff --git a/tests/deadlock.lua b/tests/deadlock.lua index bbbda8d..d028e83 100644 --- a/tests/deadlock.lua +++ b/tests/deadlock.lua @@ -22,7 +22,7 @@ if do_extra_stuff then -- lane generator. don't initialize "io" base library so that it is not known in the lane local g = lanes.gen('base,table', function() - set_debug_threadname( "deadlock_lane") + lane_threadname( "deadlock_lane") -- wrapping inside pcall makes the Lanes module unaware that something went wrong print( "In lane 1:", table_unpack{ pcall( linda.receive, linda, 'tmp')}) -- with the bug not fixed, and non-recursive mutexes, we can hang here diff --git a/tests/fibonacci.lua b/tests/fibonacci.lua index 51e7137..7885ae6 100644 --- a/tests/fibonacci.lua +++ b/tests/fibonacci.lua @@ -24,14 +24,14 @@ end local KNOWN= { [0]=0, 1,1,2,3,5,8,13,21,34,55,89,144 } -- dummy function so that we don't error when fib() is launched from the master state -set_debug_threadname = function ( ...) +lane_threadname = function ( ...) end -- -- uint= fib( n_uint ) -- local function fib( n ) - set_debug_threadname( "fib(" .. n .. ")") + lane_threadname( "fib(" .. n .. ")") local lanes = require"lanes" -- local sum diff --git a/tests/track_lanes.lua b/tests/track_lanes.lua index 32f579b..803fae5 100644 --- a/tests/track_lanes.lua +++ b/tests/track_lanes.lua @@ -29,11 +29,11 @@ local sleeper = function( name_, seconds_) end -- print( "entering '" .. name_ .. "'") local lanes = require "lanes" - -- no set_debug_threadname in main thread - if set_debug_threadname + -- no lane_threadname in main thread + if lane_threadname then - -- print( "set_debug_threadname('" .. name_ .. "')") - set_debug_threadname( name_) + -- print( "lane_threadname('" .. name_ .. "')") + lane_threadname( name_) end -- suspend the lane for the specified duration lanes.sleep(seconds_) -- cgit v1.2.3-55-g6feb