From 6efef88d0c7c155690dc2ac478d52e75da97d147 Mon Sep 17 00:00:00 2001 From: Benoit Germain Date: Mon, 8 Apr 2024 18:26:47 +0200 Subject: C++ migration: lanes.now_secs uses std::chrono::sytem_clock. plus more enum class cleanup. --- src/cancel.cpp | 17 ++++++------ src/lanes.cpp | 74 +++++++++++++++++++++++++------------------------- src/lanes_private.h | 26 ++++++++++++++---- src/linda.cpp | 20 +++++++------- src/threading.cpp | 78 ----------------------------------------------------- src/threading.h | 15 ----------- 6 files changed, 77 insertions(+), 153 deletions(-) (limited to 'src') diff --git a/src/cancel.cpp b/src/cancel.cpp index 6a94343..437a6f0 100644 --- a/src/cancel.cpp +++ b/src/cancel.cpp @@ -33,13 +33,14 @@ THE SOFTWARE. ]]-- */ -#include -#include +#include "cancel.h" + +// #include +//#include +#include "lanes_private.h" #include "threading.h" -#include "cancel.h" #include "tools.h" -#include "lanes_private.h" // ################################################################################################ // ################################################################################################ @@ -103,7 +104,7 @@ static void cancel_hook(lua_State* L, [[maybe_unused]] lua_Debug* ar) // 'wake_lindas_bool': if true, signal any linda the thread is waiting on // instead of waiting for its timeout (if any) // -// Returns: true if the lane was already finished (DONE/ERROR_ST/CANCELLED) or if we +// Returns: true if the lane was already finished (Done/Error/Cancelled) or if we // managed to cancel it. // false if the cancellation timed out, or a kill was needed. // @@ -117,7 +118,7 @@ static CancelResult thread_cancel_soft(Lane* lane_, lua_Duration duration_, bool if (wake_lane_) // wake the thread so that execution returns from any pending linda operation if desired { std::condition_variable* const waiting_on{ lane_->m_waiting_on }; - if (lane_->status == WAITING && waiting_on != nullptr) + if (lane_->m_status == Lane::Waiting && waiting_on != nullptr) { waiting_on->notify_all(); } @@ -135,7 +136,7 @@ static CancelResult thread_cancel_hard(Lane* lane_, lua_Duration duration_, bool if (wake_lane_) // wake the thread so that execution returns from any pending linda operation if desired { std::condition_variable* waiting_on = lane_->m_waiting_on; - if (lane_->status == WAITING && waiting_on != nullptr) + if (lane_->m_status == Lane::Waiting && waiting_on != nullptr) { waiting_on->notify_all(); } @@ -151,7 +152,7 @@ CancelResult thread_cancel(Lane* lane_, CancelOp op_, int hook_count_, lua_Durat { // remember that lanes are not transferable: only one thread can cancel a lane, so no multithreading issue here // We can read 'lane_->status' without locks, but not wait for it (if Posix no PTHREAD_TIMEDJOIN) - if (lane_->status >= DONE) + if (lane_->m_status >= Lane::Done) { // say "ok" by default, including when lane is already done return CancelResult::Cancelled; diff --git a/src/lanes.cpp b/src/lanes.cpp index 4dd9b46..12cd7ef 100644 --- a/src/lanes.cpp +++ b/src/lanes.cpp @@ -126,8 +126,8 @@ bool Lane::waitForCompletion(lua_Duration duration_) std::unique_lock lock{ m_done_mutex }; //std::stop_token token{ m_thread.get_stop_token() }; - //return m_done_signal.wait_for(lock, token, secs_, [this](){ return status >= DONE; }); - return m_done_signal.wait_until(lock, until, [this](){ return status >= DONE; }); + //return m_done_signal.wait_until(lock, token, secs_, [this](){ return m_status >= Lane::Done; }); + return m_done_signal.wait_until(lock, until, [this](){ return m_status >= Lane::Done; }); } static void lane_main(Lane* lane); @@ -848,12 +848,12 @@ static void lane_main(Lane* lane) // wait until the launching thread has finished preparing L lane->m_ready.wait(); int rc{ LUA_ERRRUN }; - if (lane->status == PENDING) // nothing wrong happened during preparation, we can work + if (lane->m_status == Lane::Pending) // nothing wrong happened during preparation, we can work { // At this point, the lane function and arguments are on the stack int const nargs{ lua_gettop(L) - 1 }; DEBUGSPEW_CODE(Universe* U = universe_get(L)); - lane->status = RUNNING; // PENDING -> RUNNING + lane->m_status = Lane::Running; // Pending -> Running // Tie "set_finalizer()" to the state lua_pushcfunction(L, LG_set_finalizer); @@ -924,14 +924,12 @@ static void lane_main(Lane* lane) { // leave results (1..top) or error message + stack trace (1..2) on the stack - master will copy them - enum e_status st = (rc == 0) ? DONE : CANCEL_ERROR.equals(L, 1) ? CANCELLED : ERROR_ST; + Lane::Status st = (rc == LUA_OK) ? Lane::Done : CANCEL_ERROR.equals(L, 1) ? Lane::Cancelled : Lane::Error; - // Posix no PTHREAD_TIMEDJOIN: - // 'm_done_mutex' protects the -> DONE|ERROR_ST|CANCELLED state change - // { + // 'm_done_mutex' protects the -> Done|Error|Cancelled state change std::lock_guard lock{ lane->m_done_mutex }; - lane->status = st; + lane->m_status = st; lane->m_done_signal.notify_one();// wake up master (while 'lane->m_done_mutex' is on) } } @@ -1045,13 +1043,15 @@ LUAG_FUNC(lane_new) lua_State* const m_L; Lane* m_lane{ nullptr }; int const m_gc_cb_idx; + DEBUGSPEW_CODE(Universe* const U); // for DEBUGSPEW only (hence the absence of m_ prefix) public: - OnExit(lua_State* L_, Lane* lane_, int gc_cb_idx_) + OnExit(lua_State* L_, Lane* lane_, int gc_cb_idx_ DEBUGSPEW_COMMA_PARAM(Universe* U_)) : m_L{ L_ } , m_lane{ lane_ } , m_gc_cb_idx{ gc_cb_idx_ } + DEBUGSPEW_COMMA_PARAM(U{ U_ }) {} ~OnExit() @@ -1065,7 +1065,7 @@ LUAG_FUNC(lane_new) CANCEL_ERROR.pushKey(m_lane->L); { std::lock_guard lock{ m_lane->m_done_mutex }; - m_lane->status = CANCELLED; + m_lane->m_status = Lane::Cancelled; m_lane->m_done_signal.notify_one(); // wake up master (while 'lane->m_done_mutex' is on) } // unblock the thread so that it can terminate gracefully @@ -1113,7 +1113,7 @@ LUAG_FUNC(lane_new) m_lane->m_ready.count_down(); m_lane = nullptr; } - } onExit{ L, lane, gc_cb_idx }; + } onExit{ L, lane, gc_cb_idx DEBUGSPEW_COMMA_PARAM(U) }; // launch the thread early, it will sync with a std::latch to parallelize OS thread warmup and L2 preparation DEBUGSPEW_CODE(fprintf(stderr, INDENT_BEGIN "lane_new: launching thread\n" INDENT_END)); lane->startThread(priority); @@ -1311,7 +1311,7 @@ static int lane_gc(lua_State* L) } // We can read 'lane->status' without locks, but not wait for it - if (lane->status < DONE) + if (lane->m_status < Lane::Done) { // still running: will have to be cleaned up later selfdestruct_add(lane); @@ -1358,14 +1358,14 @@ static int lane_gc(lua_State* L) // static char const * thread_status_string(Lane* lane_) { - enum e_status const st{ lane_->status }; // read just once (volatile) + Lane::Status const st{ lane_->m_status }; // read just once (volatile) char const* str = - (st == PENDING) ? "pending" : - (st == RUNNING) ? "running" : // like in 'co.status()' - (st == WAITING) ? "waiting" : - (st == DONE) ? "done" : - (st == ERROR_ST) ? "error" : - (st == CANCELLED) ? "cancelled" : nullptr; + (st == Lane::Pending) ? "pending" : + (st == Lane::Running) ? "running" : // like in 'co.status()' + (st == Lane::Waiting) ? "waiting" : + (st == Lane::Done) ? "done" : + (st == Lane::Error) ? "error" : + (st == Lane::Cancelled) ? "cancelled" : nullptr; return str; } @@ -1406,16 +1406,16 @@ LUAG_FUNC(thread_join) } STACK_CHECK_START_REL(L, 0); - // Thread is DONE/ERROR_ST/CANCELLED; all ours now + // Thread is Done/Error/Cancelled; all ours now int ret{ 0 }; Universe* const U{ lane->U }; // debug_name is a pointer to string possibly interned in the lane's state, that no longer exists when the state is closed // so store it in the userdata uservalue at a key that can't possibly collide securize_debug_threadname(L, lane); - switch (lane->status) + switch (lane->m_status) { - case DONE: + case Lane::Done: { int const n{ lua_gettop(L2) }; // whole L2 stack if ((n > 0) && (luaG_inter_move(U, L2, L, n, LookupMode::LaneBody) != 0)) @@ -1426,7 +1426,7 @@ LUAG_FUNC(thread_join) } break; - case ERROR_ST: + case Lane::Error: { int const n{ lua_gettop(L2) }; STACK_GROW(L, 3); @@ -1440,12 +1440,12 @@ LUAG_FUNC(thread_join) } break; - case CANCELLED: + case Lane::Cancelled: ret = 0; break; default: - DEBUGSPEW_CODE(fprintf(stderr, "Status: %d\n", lane->status)); + DEBUGSPEW_CODE(fprintf(stderr, "Status: %d\n", lane->m_status)); ASSERT_L(false); ret = 0; } @@ -1505,7 +1505,7 @@ LUAG_FUNC(thread_index) lua_pushcfunction(L, LG_thread_join); lua_pushvalue(L, UD); lua_call(L, 1, LUA_MULTRET); // all return values are on the stack, at slots 4+ - switch (lane->status) + switch (lane->m_status) { default: // this is an internal error, we probably never get here @@ -1516,7 +1516,7 @@ LUAG_FUNC(thread_index) raise_lua_error(L); [[fallthrough]]; // fall through if we are killed, as we got nil, "killed" on the stack - case DONE: // got regular return values + case Lane::Done: // got regular return values { int const nvalues{ lua_gettop(L) - 3 }; for (int i = nvalues; i > 0; --i) @@ -1527,7 +1527,7 @@ LUAG_FUNC(thread_index) } break; - case ERROR_ST: // got 3 values: nil, errstring, callstack table + case Lane::Error: // got 3 values: nil, errstring, callstack table // me[-2] could carry the stack table, but even // me[-1] is rather unnecessary (and undocumented); // use ':join()' instead. --AKa 22-Jan-2009 @@ -1538,7 +1538,7 @@ LUAG_FUNC(thread_index) lua_rawset(L, USR); break; - case CANCELLED: + case Lane::Cancelled: // do nothing break; } @@ -1648,13 +1648,17 @@ LUAG_FUNC(threads) */ /* -* secs= now_secs() +* secs = now_secs() * -* Returns the current time, as seconds (millisecond resolution). +* Returns the current time, as seconds. Resolution depends on std::system_clock implementation +* Can't use std::chrono::steady_clock because we need the same baseline as std::mktime */ LUAG_FUNC(now_secs) { - lua_pushnumber(L, now_secs()); + auto const now{ std::chrono::system_clock::now() }; + lua_Duration duration { now.time_since_epoch() }; + + lua_pushnumber(L, duration.count()); return 1; } @@ -1739,10 +1743,6 @@ static const struct luaL_Reg lanes_functions[] = */ static void init_once_LOCKED( void) { -#if (defined PLATFORM_WIN32) || (defined PLATFORM_POCKETPC) - now_secs(); // initialize 'now_secs()' internal offset -#endif - #if (defined PLATFORM_OSX) && (defined _UTILBINDTHREADTOCPU) chudInitialize(); #endif diff --git a/src/lanes_private.h b/src/lanes_private.h index 01d43c0..3ed52fe 100644 --- a/src/lanes_private.h +++ b/src/lanes_private.h @@ -17,6 +17,22 @@ class Lane { public: + /* + Pending: The Lua VM hasn't done anything yet. + Running, Waiting: Thread is inside the Lua VM. If the thread is forcefully stopped, we can't lua_close() the Lua State. + Done, Error, Cancelled: Thread execution is outside the Lua VM. It can be lua_close()d. + */ + enum class Status + { + Pending, + Running, + Waiting, + Done, + Error, + Cancelled + }; + using enum Status; + // the thread std::jthread m_thread; // a latch to wait for the lua_State to be ready @@ -36,16 +52,16 @@ class Lane // M: prepares the state, and reads results // S: while S is running, M must keep out of modifying the state - volatile enum e_status status{ PENDING }; + Status volatile m_status{ Pending }; // - // M: sets to PENDING (before launching) - // S: updates -> RUNNING/WAITING -> DONE/ERROR_ST/CANCELLED + // M: sets to Pending (before launching) + // S: updates -> Running/Waiting -> Done/Error/Cancelled std::condition_variable* volatile m_waiting_on{ nullptr }; // - // When status is WAITING, points on the linda's signal the thread waits on, else nullptr + // When status is Waiting, points on the linda's signal the thread waits on, else nullptr - volatile CancelRequest cancel_request{ CancelRequest::None }; + CancelRequest volatile cancel_request{ CancelRequest::None }; // // M: sets to false, flags true for cancel request // S: reads to see if cancel is requested diff --git a/src/linda.cpp b/src/linda.cpp index ea1410e..fb74abe 100644 --- a/src/linda.cpp +++ b/src/linda.cpp @@ -306,13 +306,13 @@ LUAG_FUNC(linda_send) // storage limit hit, wait until timeout or signalled that we should try again { - enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings + Lane::Status prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings if (lane != nullptr) { // change status of lane to "waiting" - prev_status = lane->status; // RUNNING, most likely - ASSERT_L(prev_status == RUNNING); // but check, just in case - lane->status = WAITING; + prev_status = lane->m_status; // Running, most likely + ASSERT_L(prev_status == Lane::Running); // but check, just in case + lane->m_status = Lane::Waiting; ASSERT_L(lane->m_waiting_on == nullptr); lane->m_waiting_on = &linda->m_read_happened; } @@ -324,7 +324,7 @@ LUAG_FUNC(linda_send) if (lane != nullptr) { lane->m_waiting_on = nullptr; - lane->status = prev_status; + lane->m_status = prev_status; } } } @@ -470,13 +470,13 @@ LUAG_FUNC(linda_receive) // nothing received, wait until timeout or signalled that we should try again { - enum e_status prev_status = ERROR_ST; // prevent 'might be used uninitialized' warnings + Lane::Status prev_status{ Lane::Error }; // prevent 'might be used uninitialized' warnings if (lane != nullptr) { // change status of lane to "waiting" - prev_status = lane->status; // RUNNING, most likely - ASSERT_L(prev_status == RUNNING); // but check, just in case - lane->status = WAITING; + prev_status = lane->m_status; // Running, most likely + ASSERT_L(prev_status == Lane::Running); // but check, just in case + lane->m_status = Lane::Waiting; ASSERT_L(lane->m_waiting_on == nullptr); lane->m_waiting_on = &linda->m_write_happened; } @@ -488,7 +488,7 @@ LUAG_FUNC(linda_receive) if (lane != nullptr) { lane->m_waiting_on = nullptr; - lane->status = prev_status; + lane->m_status = prev_status; } } } diff --git a/src/threading.cpp b/src/threading.cpp index fc20931..bc1852f 100644 --- a/src/threading.cpp +++ b/src/threading.cpp @@ -115,84 +115,6 @@ static void FAIL( char const* funcname, int rc) #endif // win32 build -/* -* Returns millisecond timing (in seconds) for the current time. -* -* Note: This function should be called once in single-threaded mode in Win32, -* to get it initialized. -*/ -time_d now_secs(void) { - -#if defined( PLATFORM_XBOX) || defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC) - /* - * Windows FILETIME values are "100-nanosecond intervals since - * January 1, 1601 (UTC)" (MSDN). Well, we'd want Unix Epoch as - * the offset and it seems, so would they: - * - * - */ - SYSTEMTIME st; - FILETIME ft; - ULARGE_INTEGER uli; - static ULARGE_INTEGER uli_epoch; // Jan 1st 1970 0:0:0 - - if (uli_epoch.HighPart==0) { - st.wYear= 1970; - st.wMonth= 1; // Jan - st.wDay= 1; - st.wHour= st.wMinute= st.wSecond= st.wMilliseconds= 0; - - if (!SystemTimeToFileTime( &st, &ft )) - FAIL( "SystemTimeToFileTime", GetLastError() ); - - uli_epoch.LowPart= ft.dwLowDateTime; - uli_epoch.HighPart= ft.dwHighDateTime; - } - - GetSystemTime( &st ); // current system date/time in UTC - if (!SystemTimeToFileTime( &st, &ft )) - FAIL( "SystemTimeToFileTime", GetLastError() ); - - uli.LowPart= ft.dwLowDateTime; - uli.HighPart= ft.dwHighDateTime; - - /* 'double' has less accuracy than 64-bit int, but if it were to degrade, - * it would do so gracefully. In practice, the integer accuracy is not - * of the 100ns class but just 1ms (Windows XP). - */ -# if 1 - // >= 2.0.3 code - return (double) ((uli.QuadPart - uli_epoch.QuadPart)/10000) / 1000.0; -# elif 0 - // fix from Kriss Daniels, see: - // - // - // "seem to be getting negative numbers from the old version, probably number - // conversion clipping, this fixes it and maintains ms resolution" - // - // This was a bad fix, and caused timer test 5 sec timers to disappear. - // --AKa 25-Jan-2009 - // - return ((double)((signed)((uli.QuadPart/10000) - (uli_epoch.QuadPart/10000)))) / 1000.0; -# else - // <= 2.0.2 code - return (double)(uli.QuadPart - uli_epoch.QuadPart) / 10000000.0; -# endif -#else // !(defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC)) - struct timeval tv; - // { - // time_t tv_sec; /* seconds since Jan. 1, 1970 */ - // suseconds_t tv_usec; /* and microseconds */ - // }; - - int rc = gettimeofday(&tv, nullptr /*time zone not used any more (in Linux)*/); - assert( rc==0 ); - - return ((double)tv.tv_sec) + ((tv.tv_usec)/1000) / 1000.0; -#endif // !(defined( PLATFORM_WIN32) || defined( PLATFORM_POCKETPC)) -} - - /*---=== Threading ===---*/ //--- diff --git a/src/threading.h b/src/threading.h index e9f302a..82c8f52 100644 --- a/src/threading.h +++ b/src/threading.h @@ -5,13 +5,6 @@ #include #include -/* Note: ERROR is a defined entity on Win32 - PENDING: The Lua VM hasn't done anything yet. - RUNNING, WAITING: Thread is inside the Lua VM. If the thread is forcefully stopped, we can't lua_close() the Lua State. - DONE, ERROR_ST, CANCELLED: Thread execution is outside the Lua VM. It can be lua_close()d. -*/ -enum e_status { PENDING, RUNNING, WAITING, DONE, ERROR_ST, CANCELLED }; - #define THREADAPI_WINDOWS 1 #define THREADAPI_PTHREAD 2 @@ -129,14 +122,6 @@ enum e_status { PENDING, RUNNING, WAITING, DONE, ERROR_ST, CANCELLED }; #define THREAD_CALLCONV #endif //THREADAPI == THREADAPI_PTHREAD -/* -* 'time_d': <0.0 for no timeout -* 0.0 for instant check -* >0.0 absolute timeout in secs + ms -*/ -using time_d = double; -time_d now_secs(void); - /*---=== Threading ===--- */ -- cgit v1.2.3-55-g6feb