From fd7b11f436d159550ba12b726a37fb4b4116fe0d Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Wed, 20 Jul 2022 14:31:32 +0200 Subject: [PATCH] fix event loop timing issue The event loop tries to run no faster than every 10ms. If it detects that it goes faster, it would incorrectly calculate the difference, and then wait (what I assume was) way too long or too short. Either way, now it's fixed and it correctly works, even when introducing new lua states. --- Changelog.md | 2 ++ include/Common.h | 7 +++++++ include/THeartbeatThread.h | 1 - include/TLuaEngine.h | 6 ++---- include/TNetwork.h | 1 - include/TPPSMonitor.h | 3 +-- include/TPluginMonitor.h | 1 - src/Common.cpp | 22 +++++++++++++++++++++ src/THeartbeatThread.cpp | 3 +-- src/TLuaEngine.cpp | 40 +++++++++++++++++++------------------- src/TNetwork.cpp | 26 ++++++++++++------------- src/TPPSMonitor.cpp | 3 +-- src/TPluginMonitor.cpp | 5 ++--- src/main.cpp | 2 +- 14 files changed, 71 insertions(+), 51 deletions(-) diff --git a/Changelog.md b/Changelog.md index 6ee3ff8..aa104b3 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,4 @@ + # v3.1.0 - ADDED Tab autocomplete in console, smart tab autocomplete (understands lua tables and types) in the lua console @@ -9,6 +10,7 @@ - FIXED issue with client->server events which contain ':' - FIXED a fatal exception on LuaEngine startup if Resources/Server is a symlink - FIXED onInit not being called on hot-reload +- FIXED incorrect timing calculation of Lua EventTimer loop # v3.0.2 diff --git a/include/Common.h b/include/Common.h index 805739c..c77ffd1 100644 --- a/include/Common.h +++ b/include/Common.h @@ -12,6 +12,7 @@ extern TSentry Sentry; #include #include #include +#include #include #include @@ -96,6 +97,8 @@ public: static void CheckForUpdates(); static std::array VersionStrToInts(const std::string& str); static bool IsOutdated(const Version& Current, const Version& Newest); + static bool IsShuttingDown(); + static void SleepSafeSeconds(size_t Seconds); static void InitializeConsole() { if (!mConsole) { @@ -121,10 +124,14 @@ public: static void SetSubsystemStatus(const std::string& Subsystem, Status status); private: + static void SetShutdown(bool Val); + static inline SystemStatusMap mSystemStatusMap {}; static inline std::mutex mSystemStatusMapMutex {}; static inline std::string mPPS; static inline std::unique_ptr mConsole; + static inline std::shared_mutex mShutdownMtx {}; + static inline bool mShutdown { false }; static inline std::mutex mShutdownHandlersMutex {}; static inline std::deque mShutdownHandlers {}; diff --git a/include/THeartbeatThread.h b/include/THeartbeatThread.h index 1f0ab14..1063be6 100644 --- a/include/THeartbeatThread.h +++ b/include/THeartbeatThread.h @@ -15,7 +15,6 @@ private: std::string GenerateCall(); std::string GetPlayers(); - bool mShutdown = false; TResourceManager& mResourceManager; TServer& mServer; }; diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index f71b9df..2041687 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -187,7 +187,7 @@ private: class StateThreadData : IThreaded { public: - StateThreadData(const std::string& Name, std::atomic_bool& Shutdown, TLuaStateId StateId, TLuaEngine& Engine); + StateThreadData(const std::string& Name, TLuaStateId StateId, TLuaEngine& Engine); StateThreadData(const StateThreadData&) = delete; ~StateThreadData() noexcept { beammp_debug("\"" + mStateId + "\" destroyed"); } [[nodiscard]] std::shared_ptr EnqueueScript(const TLuaChunk& Script); @@ -218,7 +218,6 @@ private: sol::table Lua_FS_ListDirectories(const std::string& Path); std::string mName; - std::atomic_bool& mShutdown; TLuaStateId mStateId; lua_State* mState; std::thread mThread; @@ -247,8 +246,7 @@ private: TNetwork* mNetwork; TServer* mServer; - std::atomic_bool mShutdown { false }; - fs::path mResourceServerPath; + const fs::path mResourceServerPath; std::vector> mLuaPlugins; std::unordered_map> mLuaStates; std::recursive_mutex mLuaStatesMutex; diff --git a/include/TNetwork.h b/include/TNetwork.h index 87e4235..6394306 100644 --- a/include/TNetwork.h +++ b/include/TNetwork.h @@ -32,7 +32,6 @@ private: TServer& mServer; TPPSMonitor& mPPSMonitor; SOCKET mUDPSock {}; - bool mShutdown { false }; TResourceManager& mResourceManager; std::thread mUDPThread; std::thread mTCPThread; diff --git a/include/TPPSMonitor.h b/include/TPPSMonitor.h index 508dfc5..0718f10 100644 --- a/include/TPPSMonitor.h +++ b/include/TPPSMonitor.h @@ -22,6 +22,5 @@ private: TServer& mServer; std::optional> mNetwork { std::nullopt }; - bool mShutdown { false }; int mInternalPPS { 0 }; -}; \ No newline at end of file +}; diff --git a/include/TPluginMonitor.h b/include/TPluginMonitor.h index a617987..2ed77bc 100644 --- a/include/TPluginMonitor.h +++ b/include/TPluginMonitor.h @@ -18,6 +18,5 @@ public: private: std::shared_ptr mEngine; fs::path mPath; - std::atomic_bool mShutdown { false }; std::unordered_map mFileTimes; }; diff --git a/src/Common.cpp b/src/Common.cpp index 11fe8f6..728bbf7 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -25,6 +25,7 @@ void Application::RegisterShutdownHandler(const TShutdownHandler& Handler) { } void Application::GracefullyShutdown() { + SetShutdown(true); static bool AlreadyShuttingDown = false; static uint8_t ShutdownAttempts = 0; if (AlreadyShuttingDown) { @@ -93,6 +94,22 @@ bool Application::IsOutdated(const Version& Current, const Version& Newest) { } } +bool Application::IsShuttingDown() { + std::shared_lock Lock(mShutdownMtx); + return mShutdown; +} + +void Application::SleepSafeSeconds(size_t Seconds) { + // Sleeps for 500 ms, checks if a shutdown occurred, and so forth + for (size_t i = 0; i < Seconds * 2; ++i) { + if (Application::IsShuttingDown()) { + return; + } else { + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } + } +} + TEST_CASE("Application::IsOutdated (version check)") { SUBCASE("Same version") { CHECK(!Application::IsOutdated({ 1, 2, 3 }, { 1, 2, 3 })); @@ -158,6 +175,11 @@ void Application::SetSubsystemStatus(const std::string& Subsystem, Status status mSystemStatusMap[Subsystem] = status; } +void Application::SetShutdown(bool Val) { + std::unique_lock Lock(mShutdownMtx); + mShutdown = Val; +} + TEST_CASE("Application::SetSubsystemStatus") { Application::SetSubsystemStatus("Test", Application::Status::Good); auto Map = Application::GetSubsystemStatuses(); diff --git a/src/THeartbeatThread.cpp b/src/THeartbeatThread.cpp index 5d3d437..edd0ed2 100644 --- a/src/THeartbeatThread.cpp +++ b/src/THeartbeatThread.cpp @@ -20,7 +20,7 @@ void THeartbeatThread::operator()() { static std::chrono::high_resolution_clock::time_point LastNormalUpdateTime = std::chrono::high_resolution_clock::now(); bool isAuth = false; size_t UpdateReminderCounter = 0; - while (!mShutdown) { + while (!Application::IsShuttingDown()) { ++UpdateReminderCounter; Body = GenerateCall(); // a hot-change occurs when a setting has changed, to update the backend of that change. @@ -164,7 +164,6 @@ THeartbeatThread::THeartbeatThread(TResourceManager& ResourceManager, TServer& S Application::RegisterShutdownHandler([&] { Application::SetSubsystemStatus("Heartbeat", Application::Status::ShuttingDown); if (mThread.joinable()) { - mShutdown = true; mThread.join(); } Application::SetSubsystemStatus("Heartbeat", Application::Status::Shutdown); diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 050f416..a4e2d52 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -15,20 +15,18 @@ TLuaEngine* LuaAPI::MP::Engine; -TLuaEngine::TLuaEngine() { +TLuaEngine::TLuaEngine() + : mResourceServerPath(fs::path(Application::Settings.Resource) / "Server") { Application::SetSubsystemStatus("LuaEngine", Application::Status::Starting); LuaAPI::MP::Engine = this; if (!fs::exists(Application::Settings.Resource)) { fs::create_directory(Application::Settings.Resource); } - fs::path Path = fs::path(Application::Settings.Resource) / "Server"; - if (!fs::exists(Path)) { - fs::create_directory(Path); + if (!fs::exists(mResourceServerPath)) { + fs::create_directory(mResourceServerPath); } - mResourceServerPath = Path; Application::RegisterShutdownHandler([&] { Application::SetSubsystemStatus("LuaEngine", Application::Status::ShuttingDown); - mShutdown = true; if (mThread.joinable()) { mThread.join(); } @@ -59,7 +57,7 @@ void TLuaEngine::operator()() { auto ResultCheckThread = std::thread([&] { RegisterThread("ResultCheckThread"); - while (!mShutdown) { + while (!Application::IsShuttingDown()) { std::unique_lock Lock(mResultsToCheckMutex); mResultsToCheckCond.wait_for(Lock, std::chrono::milliseconds(20)); if (!mResultsToCheck.empty()) { @@ -79,10 +77,7 @@ void TLuaEngine::operator()() { }); // event loop auto Before = std::chrono::high_resolution_clock::now(); - while (!mShutdown) { - if (mLuaStates.size() == 0) { - std::this_thread::sleep_for(std::chrono::seconds(100)); - } + while (!Application::IsShuttingDown()) { { // Timed Events Scope std::unique_lock Lock(mTimedEventsMutex); for (auto& Timer : mTimedEvents) { @@ -108,12 +103,18 @@ void TLuaEngine::operator()() { } } } - const auto Expected = std::chrono::milliseconds(10); - if (auto Diff = std::chrono::high_resolution_clock::now() - Before; - Diff < Expected) { - std::this_thread::sleep_for(Expected - Diff); + if (mLuaStates.size() == 0) { + beammp_trace("No Lua states, event loop running extremely sparsely"); + Application::SleepSafeSeconds(10); } else { - beammp_trace("Event loop cannot keep up! Running " + std::to_string(Diff.count()) + "s behind"); + constexpr double NsFactor = 1000000.0; + constexpr double Expected = 10.0; // ms + const auto Diff = (std::chrono::high_resolution_clock::now() - Before).count() / NsFactor; + if (Diff < Expected) { + std::this_thread::sleep_for(std::chrono::nanoseconds(size_t((Expected - Diff) * NsFactor))); + } else { + beammp_tracef("Event loop cannot keep up! Running {}ms behind", Diff); + } } Before = std::chrono::high_resolution_clock::now(); } @@ -326,7 +327,7 @@ void TLuaEngine::EnsureStateExists(TLuaStateId StateId, const std::string& Name, std::unique_lock Lock(mLuaStatesMutex); if (mLuaStates.find(StateId) == mLuaStates.end()) { beammp_debug("Creating lua state for state id \"" + StateId + "\""); - auto DataPtr = std::make_unique(Name, mShutdown, StateId, *this); + auto DataPtr = std::make_unique(Name, StateId, *this); mLuaStates[StateId] = std::move(DataPtr); RegisterEvent("onInit", StateId, "onInit"); if (!DontCallOnInit) { @@ -614,9 +615,8 @@ sol::table TLuaEngine::StateThreadData::Lua_JsonDecode(const std::string& str) { return table; } -TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, std::atomic_bool& Shutdown, TLuaStateId StateId, TLuaEngine& Engine) +TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, TLuaStateId StateId, TLuaEngine& Engine) : mName(Name) - , mShutdown(Shutdown) , mStateId(StateId) , mState(luaL_newstate()) , mEngine(&Engine) { @@ -819,7 +819,7 @@ void TLuaEngine::StateThreadData::RegisterEvent(const std::string& EventName, co void TLuaEngine::StateThreadData::operator()() { RegisterThread("Lua:" + mStateId); - while (!mShutdown) { + while (!Application::IsShuttingDown()) { { // StateExecuteQueue Scope std::unique_lock Lock(mStateExecuteQueueMutex); if (!mStateExecuteQueue.empty()) { diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index 6c654cd..be9b773 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -25,7 +25,6 @@ TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& R Application::RegisterShutdownHandler([&] { Application::SetSubsystemStatus("UDPNetwork", Application::Status::ShuttingDown); if (mUDPThread.joinable()) { - mShutdown = true; mUDPThread.detach(); } Application::SetSubsystemStatus("UDPNetwork", Application::Status::Shutdown); @@ -33,7 +32,6 @@ TNetwork::TNetwork(TServer& Server, TPPSMonitor& PPSMonitor, TResourceManager& R Application::RegisterShutdownHandler([&] { Application::SetSubsystemStatus("TCPNetwork", Application::Status::ShuttingDown); if (mTCPThread.joinable()) { - mShutdown = true; mTCPThread.detach(); } Application::SetSubsystemStatus("TCPNetwork", Application::Status::Shutdown); @@ -68,7 +66,7 @@ void TNetwork::UDPServerMain() { Application::SetSubsystemStatus("UDPNetwork", Application::Status::Good); beammp_info(("Vehicle data network online on port ") + std::to_string(Application::Settings.Port) + (" with a Max of ") + std::to_string(Application::Settings.MaxPlayers) + (" Clients")); - while (!mShutdown) { + while (!Application::IsShuttingDown()) { try { sockaddr_in client {}; std::string Data = UDPRcvFromClient(client); // Receives any data from Socket @@ -152,7 +150,7 @@ void TNetwork::TCPServerMain() { beammp_info("Vehicle event network online"); do { try { - if (mShutdown) { + if (Application::IsShuttingDown()) { beammp_debug("shutdown during TCP wait for accept loop"); break; } @@ -239,16 +237,16 @@ void TNetwork::HandleDownload(SOCKET TCPSock) { }); } -static int get_ip_str(const struct sockaddr *sa, char *strBuf, size_t strBufSize) { - switch(sa->sa_family) { - case AF_INET: - inet_ntop(AF_INET, &(((struct sockaddr_in *)sa)->sin_addr), strBuf, strBufSize); - break; - case AF_INET6: - inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)sa)->sin6_addr), strBuf, strBufSize); - break; - default: - return 1; +static int get_ip_str(const struct sockaddr* sa, char* strBuf, size_t strBufSize) { + switch (sa->sa_family) { + case AF_INET: + inet_ntop(AF_INET, &(((struct sockaddr_in*)sa)->sin_addr), strBuf, strBufSize); + break; + case AF_INET6: + inet_ntop(AF_INET6, &(((struct sockaddr_in6*)sa)->sin6_addr), strBuf, strBufSize); + break; + default: + return 1; } return 0; } diff --git a/src/TPPSMonitor.cpp b/src/TPPSMonitor.cpp index b5b4951..3edd17d 100644 --- a/src/TPPSMonitor.cpp +++ b/src/TPPSMonitor.cpp @@ -10,7 +10,6 @@ TPPSMonitor::TPPSMonitor(TServer& Server) Application::SetSubsystemStatus("PPSMonitor", Application::Status::ShuttingDown); if (mThread.joinable()) { beammp_debug("shutting down PPSMonitor"); - mShutdown = true; mThread.join(); beammp_debug("shut down PPSMonitor"); } @@ -27,7 +26,7 @@ void TPPSMonitor::operator()() { beammp_debug("PPSMonitor starting"); Application::SetSubsystemStatus("PPSMonitor", Application::Status::Good); std::vector> TimedOutClients; - while (!mShutdown) { + while (!Application::IsShuttingDown()) { std::this_thread::sleep_for(std::chrono::seconds(1)); int C = 0, V = 0; if (mServer.ClientCount() == 0) { diff --git a/src/TPluginMonitor.cpp b/src/TPluginMonitor.cpp index 7c84fec..9553f5d 100644 --- a/src/TPluginMonitor.cpp +++ b/src/TPluginMonitor.cpp @@ -17,7 +17,6 @@ TPluginMonitor::TPluginMonitor(const fs::path& Path, std::shared_ptr } Application::RegisterShutdownHandler([this] { - mShutdown = true; if (mThread.joinable()) { mThread.join(); } @@ -30,7 +29,7 @@ void TPluginMonitor::operator()() { RegisterThread("PluginMonitor"); beammp_info("PluginMonitor started"); Application::SetSubsystemStatus("PluginMonitor", Application::Status::Good); - while (!mShutdown) { + while (!Application::IsShuttingDown()) { std::vector ToRemove; for (const auto& Pair : mFileTimes) { try { @@ -61,7 +60,7 @@ void TPluginMonitor::operator()() { } catch (const std::exception& e) { ToRemove.push_back(Pair.first); } - for (size_t i = 0; i < 3 && !mShutdown; ++i) { + for (size_t i = 0; i < 3 && !Application::IsShuttingDown(); ++i) { std::this_thread::sleep_for(std::chrono::seconds(1)); } } diff --git a/src/main.cpp b/src/main.cpp index f3e23f4..4650570 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -69,7 +69,7 @@ int main(int argc, char** argv) { Sentry.LogException(e, _file_basename, _line); MainRet = -1; } - return MainRet; + std::exit(MainRet); } int BeamMPServerMain(MainArguments Arguments) {