diff --git a/Changelog.md b/Changelog.md index 7138c71..e5ace3e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -22,6 +22,7 @@ - ADDED `MP.GetStateMemoryUsage() -> number`: Current memory usage of the current state in bytes - ADDED `MP.GetLuaMemoryUsage() -> number`: Current memory usage of all states combined, in bytes - ADDED `MP.CreateEventTimer(event,interval_ms)`: Replacement for `CreateThread` - calls the event in the given interval +- ADDED `MP.CancelEventTimer(event)`: Cancels all event timers for that event # v2.3.3 diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index 545ae11..9a69113 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -91,9 +91,10 @@ public: } std::set GetEventHandlersForState(const std::string& EventName, TLuaStateId StateId); void CreateEventTimer(const std::string& EventName, TLuaStateId StateId, size_t IntervalMS); + void CancelEventTimers(const std::string& EventName, TLuaStateId StateId); static constexpr const char* BeamMPFnNotFoundError = "BEAMMP_FN_NOT_FOUND"; - + private: void CollectAndInitPlugins(); void InitializePlugin(const fs::path& Folder, const TLuaPluginConfig& Config); @@ -136,7 +137,7 @@ private: }; struct TimedEvent { - const std::chrono::high_resolution_clock::duration Duration {}; + std::chrono::high_resolution_clock::duration Duration {}; std::chrono::high_resolution_clock::time_point LastCompletion {}; std::string EventName; TLuaStateId StateId; diff --git a/src/Common.cpp b/src/Common.cpp index d058de5..375b399 100644 --- a/src/Common.cpp +++ b/src/Common.cpp @@ -159,6 +159,7 @@ std::string ThreadName(bool DebugModeOverride) { } void RegisterThread(const std::string& str) { + beammp_info(str + " is " + std::to_string(gettid())); auto Lock = std::unique_lock(ThreadNameMapMutex); threadNameMap[std::this_thread::get_id()] = str; } diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 81d28c5..d6c8c81 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -44,58 +44,61 @@ void TLuaEngine::operator()() { beammp_lua_error("Calling \"onInit\" on \"" + Future->StateId + "\" failed: " + Future->ErrorMessage); } } - std::queue> ToTrigger; - std::mutex ToTriggerMutex; - std::condition_variable ToTriggerCond; - auto TriggerThread = [&] { + std::queue> ResultsToCheck; + std::recursive_mutex ResultsToCheckMutex; + std::thread ResultCheckThread([&] { while (!mShutdown) { - std::unique_lock Lock(ToTriggerMutex); - ToTriggerCond.wait_for(Lock, std::chrono::milliseconds(10), [&ToTrigger] { return !ToTrigger.empty(); }); - auto Timer = ToTrigger.front(); - ToTrigger.pop(); - auto Handlers = GetEventHandlersForState(Timer.first, Timer.second); - for (auto& Handler : Handlers) { - beammp_ignore(mLuaStates[Timer.second]->EnqueueFunctionCall(Handler, {})); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + std::unique_lock Lock(ResultsToCheckMutex); + if (!ResultsToCheck.empty()) { + auto Res = ResultsToCheck.front(); + ResultsToCheck.pop(); + Lock.unlock(); + + size_t Waited = 0; + while (!Res->Ready) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + Waited++; + if (Waited > 1000) { + beammp_lua_error(Res->Function + " in " + Res->StateId + " took >1s to respond, not printing possible errors"); + } + } + if (Res->Error) { + beammp_lua_error(Res->ErrorMessage); + } } } - }; - std::vector Threads; - Threads.resize(1); - for (auto& Elem : Threads) { - Elem = std::thread(TriggerThread); - } + }); // event loop auto Before = std::chrono::high_resolution_clock::now(); while (!mShutdown) { - std::unique_lock Lock(mTimedEventsMutex); - for (auto& Timer : mTimedEvents) { - if (Timer.Expired()) { - if (Timer.EventName != "Asd") { - beammp_debug("\"" + Timer.EventName + "\" expired"); + { // Timed Events Scope + std::unique_lock Lock(mTimedEventsMutex); + for (auto& Timer : mTimedEvents) { + if (Timer.Expired()) { + Timer.Reset(); + auto Handlers = GetEventHandlersForState(Timer.EventName, Timer.StateId); + std::unique_lock StateLock(mLuaStatesMutex); + std::unique_lock Lock2(ResultsToCheckMutex); + for (auto& Handler : Handlers) { + auto Res = mLuaStates[Timer.StateId]->EnqueueFunctionCall(Handler, {}); + ResultsToCheck.push(Res); + } } - Timer.Reset(); - { - std::unique_lock Lock2(ToTriggerMutex); - ToTrigger.emplace(Timer.EventName, Timer.StateId); - } - ToTriggerCond.notify_one(); } } // sleep for the remaining time to get to 1ms (our atom duration) if (std::chrono::high_resolution_clock::duration Diff; (Diff = std::chrono::high_resolution_clock::now() - Before) - < std::chrono::milliseconds(1)) { - // std::this_thread::sleep_for(Diff); + < std::chrono::milliseconds(10)) { + std::this_thread::sleep_for(Diff); } else { - beammp_debug("Event loop cannot keep up! Currently using " + std::to_string(Threads.size()) + " threads, adding one"); - Threads.push_back(std::thread(TriggerThread)); + beammp_debug("Event loop cannot keep up!"); } Before = std::chrono::high_resolution_clock::now(); } - for (auto& Elem : Threads) { - if (Elem.joinable()) { - Elem.join(); - } + if (ResultCheckThread.joinable()) { + ResultCheckThread.join(); } } @@ -395,8 +398,14 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, std::atomi MPTable.set_function("Sleep", &LuaAPI::MP::Sleep); MPTable.set_function("PrintRaw", &LuaAPI::MP::PrintRaw); MPTable.set_function("CreateEventTimer", [&](const std::string& EventName, size_t IntervalMS) { + if (IntervalMS < 25) { + beammp_warn("Timer for \"" + EventName + "\" on \"" + mStateId + "\" is set to trigger at <25ms, which is likely too fast and won't cancel properly."); + } mEngine->CreateEventTimer(EventName, mStateId, IntervalMS); }); + MPTable.set_function("CancelEventTimer", [&](const std::string& EventName) { + mEngine->CancelEventTimers(EventName, mStateId); + }); MPTable.set_function("Set", &LuaAPI::MP::Set); MPTable.set_function("HttpsGET", [&](const std::string& Host, int Port, const std::string& Target) -> std::tuple { unsigned Status; @@ -547,7 +556,7 @@ void TLuaEngine::StateThreadData::operator()() { } } } - std::this_thread::sleep_for(std::chrono::milliseconds(1)); + std::this_thread::sleep_for(std::chrono::milliseconds(10)); } } @@ -560,7 +569,22 @@ void TLuaEngine::CreateEventTimer(const std::string& EventName, TLuaStateId Stat StateId }; mTimedEvents.push_back(std::move(Event)); - beammp_debug("created event timer for \"" + EventName + "\" on \"" + StateId + " with " + std::to_string(IntervalMS) + "ms interval"); + beammp_trace("created event timer for \"" + EventName + "\" on \"" + StateId + "\" with " + std::to_string(IntervalMS) + "ms interval"); +} + +void TLuaEngine::CancelEventTimers(const std::string& EventName, TLuaStateId StateId) { + std::unique_lock Lock(mTimedEventsMutex); + beammp_trace("cancelling event timer for \"" + EventName + "\" on \"" + StateId + "\""); + for (;;) { + auto Iter = std::find_if(mTimedEvents.begin(), mTimedEvents.end(), [&](const TimedEvent& Event) -> bool { + return Event.EventName == EventName && Event.StateId == StateId; + }); + if (Iter != mTimedEvents.end()) { + mTimedEvents.erase(Iter); + } else { + break; + } + } } void TLuaEngine::StateThreadData::AddPath(const fs::path& Path) {