From 8c95919b32970ea1cd1926a8af896e5cc3e08b94 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 23 Feb 2015 23:49:39 -0800 Subject: [PATCH] Move watchdog/runtime interaction into Environment. --- sourcepawn/jit/environment.cpp | 53 +++++++++++++++++++++++++++- sourcepawn/jit/environment.h | 34 +++++++++++++++++- sourcepawn/jit/plugin-runtime.cpp | 9 ++--- sourcepawn/jit/watchdog_timer.cpp | 30 ++++++++-------- sourcepawn/jit/watchdog_timer.h | 6 +++- sourcepawn/jit/x86/jit_x86.cpp | 57 ++----------------------------- sourcepawn/jit/x86/jit_x86.h | 23 +++---------- 7 files changed, 118 insertions(+), 94 deletions(-) diff --git a/sourcepawn/jit/environment.cpp b/sourcepawn/jit/environment.cpp index 34ce296d..ea779f96 100644 --- a/sourcepawn/jit/environment.cpp +++ b/sourcepawn/jit/environment.cpp @@ -63,7 +63,7 @@ Environment::Initialize() { api_v1_ = new SourcePawnEngine(); api_v2_ = new SourcePawnEngine2(); - watchdog_timer_ = new WatchdogTimer(); + watchdog_timer_ = new WatchdogTimer(this); if ((code_pool_ = Knight::KE_CreateCodeCache()) == nullptr) return false; @@ -181,3 +181,54 @@ Environment::FreeCode(void *code) { Knight::KE_FreeCode(code_pool_, code); } + +void +Environment::RegisterRuntime(PluginRuntime *rt) +{ + mutex_.AssertCurrentThreadOwns(); + runtimes_.append(rt); +} + +void +Environment::DeregisterRuntime(PluginRuntime *rt) +{ + mutex_.AssertCurrentThreadOwns(); + runtimes_.remove(rt); +} + +void +Environment::PatchAllJumpsForTimeout() +{ + mutex_.AssertCurrentThreadOwns(); + for (ke::InlineList::iterator iter = runtimes_.begin(); iter != runtimes_.end(); iter++) { + PluginRuntime *rt = *iter; + for (size_t i = 0; i < rt->NumJitFunctions(); i++) { + CompiledFunction *fun = rt->GetJitFunction(i); + uint8_t *base = reinterpret_cast(fun->GetEntryAddress()); + + for (size_t j = 0; j < fun->NumLoopEdges(); j++) { + const LoopEdge &e = fun->GetLoopEdge(j); + int32_t diff = intptr_t(g_Jit.TimeoutStub()) - intptr_t(base + e.offset); + *reinterpret_cast(base + e.offset - 4) = diff; + } + } + } +} + +void +Environment::UnpatchAllJumpsFromTimeout() +{ + mutex_.AssertCurrentThreadOwns(); + for (ke::InlineList::iterator iter = runtimes_.begin(); iter != runtimes_.end(); iter++) { + PluginRuntime *rt = *iter; + for (size_t i = 0; i < rt->NumJitFunctions(); i++) { + CompiledFunction *fun = rt->GetJitFunction(i); + uint8_t *base = reinterpret_cast(fun->GetEntryAddress()); + + for (size_t j = 0; j < fun->NumLoopEdges(); j++) { + const LoopEdge &e = fun->GetLoopEdge(j); + *reinterpret_cast(base + e.offset - 4) = e.disp32; + } + } + } +} diff --git a/sourcepawn/jit/environment.h b/sourcepawn/jit/environment.h index 94b094f4..40ddd46d 100644 --- a/sourcepawn/jit/environment.h +++ b/sourcepawn/jit/environment.h @@ -15,7 +15,10 @@ #include #include // Replace with am-cxx later. +#include +#include #include "code-allocator.h" +#include "plugin-runtime.h" class PluginRuntime; @@ -59,6 +62,15 @@ class Environment : public ISourcePawnEnvironment void *AllocateCode(size_t size); void FreeCode(void *code); + // Runtime management. + void RegisterRuntime(PluginRuntime *rt); + void DeregisterRuntime(PluginRuntime *rt); + void PatchAllJumpsForTimeout(); + void UnpatchAllJumpsFromTimeout(); + ke::Mutex *lock() { + return &mutex_; + } + // Helpers. void SetProfiler(IProfilingTool *profiler) { profiler_ = profiler; @@ -89,6 +101,21 @@ class Environment : public ISourcePawnEnvironment return watchdog_timer_; } + // These are indicators used for the watchdog timer. + uintptr_t FrameId() const { + return frame_id_; + } + bool RunningCode() const { + return invoke_depth_ != 0; + } + void EnterInvoke() { + if (invoke_depth_++ == 0) + frame_id_++; + } + void LeaveInvoke() { + invoke_depth_--; + } + private: bool Initialize(); @@ -96,6 +123,7 @@ class Environment : public ISourcePawnEnvironment ke::AutoPtr api_v1_; ke::AutoPtr api_v2_; ke::AutoPtr watchdog_timer_; + ke::Mutex mutex_; IDebugListener *debugger_; IProfilingTool *profiler_; @@ -103,6 +131,10 @@ class Environment : public ISourcePawnEnvironment bool profiling_enabled_; Knight::KeCodeCache *code_pool_; + ke::InlineList runtimes_; + + uintptr_t frame_id_; + uintptr_t invoke_depth_; }; class EnterProfileScope @@ -121,6 +153,6 @@ class EnterProfileScope } }; -} +} // namespace sp #endif // _include_sourcepawn_vm_environment_h_ diff --git a/sourcepawn/jit/plugin-runtime.cpp b/sourcepawn/jit/plugin-runtime.cpp index 1aa0962b..7afe77b3 100644 --- a/sourcepawn/jit/plugin-runtime.cpp +++ b/sourcepawn/jit/plugin-runtime.cpp @@ -17,6 +17,7 @@ #include "plugin-runtime.h" #include "x86/jit_x86.h" #include "sp_vm_basecontext.h" +#include "environment.h" #include "md5/md5.h" @@ -48,8 +49,8 @@ PluginRuntime::PluginRuntime() memset(m_CodeHash, 0, sizeof(m_CodeHash)); memset(m_DataHash, 0, sizeof(m_DataHash)); - ke::AutoLock lock(g_Jit.Mutex()); - g_Jit.RegisterRuntime(this); + ke::AutoLock lock(Environment::get()->lock()); + Environment::get()->RegisterRuntime(this); } PluginRuntime::~PluginRuntime() @@ -58,9 +59,9 @@ PluginRuntime::~PluginRuntime() // runtimes. It is not enough to ensure that the unlinking of the runtime is // protected; we cannot delete functions or code while the watchdog might be // executing. Therefore, the entire destructor is guarded. - ke::AutoLock lock(g_Jit.Mutex()); + ke::AutoLock lock(Environment::get()->lock()); - g_Jit.DeregisterRuntime(this); + Environment::get()->DeregisterRuntime(this); for (uint32_t i = 0; i < m_plugin.num_publics; i++) delete m_PubFuncs[i]; diff --git a/sourcepawn/jit/watchdog_timer.cpp b/sourcepawn/jit/watchdog_timer.cpp index f1d2a81b..54373b73 100644 --- a/sourcepawn/jit/watchdog_timer.cpp +++ b/sourcepawn/jit/watchdog_timer.cpp @@ -15,15 +15,17 @@ // You should have received a copy of the GNU General Public License // along with SourcePawn. If not, see . #include "watchdog_timer.h" -#include "x86/jit_x86.h" #include +#include "environment.h" +//#include "x86/jit_x86.h" -WatchdogTimer::WatchdogTimer() - : terminate_(false), - mainthread_(ke::GetCurrentThreadId()), - last_frame_id_(0), - second_timeout_(false), - timedout_(false) +WatchdogTimer::WatchdogTimer(Environment *env) + : env_(env), + terminate_(false), + mainthread_(ke::GetCurrentThreadId()), + last_frame_id_(0), + second_timeout_(false), + timedout_(false) { } @@ -68,7 +70,7 @@ WatchdogTimer::Run() ke::AutoLock lock(&cv_); // Initialize the frame id, so we don't have to wait longer on startup. - last_frame_id_ = g_Jit.FrameId(); + last_frame_id_ = env_->FrameId(); while (!terminate_) { ke::WaitResult rv = cv_.Wait(timeout_ms_ / 2); @@ -89,8 +91,8 @@ WatchdogTimer::Run() // Note that it's okay if these two race: it's just a heuristic, and // worst case, we'll miss something that might have timed out but // ended up resuming. - uintptr_t frame_id = g_Jit.FrameId(); - if (frame_id != last_frame_id_ || !g_Jit.RunningCode()) { + uintptr_t frame_id = env_->FrameId(); + if (frame_id != last_frame_id_ || !env_->RunningCode()) { last_frame_id_ = frame_id; second_timeout_ = false; continue; @@ -105,7 +107,7 @@ WatchdogTimer::Run() { // Prevent the JIT from linking or destroying runtimes and functions. - ke::AutoLock lock(g_Jit.Mutex()); + ke::AutoLock lock(env_->lock()); // Set the timeout notification bit. If this is detected before any patched // JIT backedges are reached, the main thread will attempt to acquire the @@ -115,7 +117,7 @@ WatchdogTimer::Run() // Patch all jumps. This can race with the main thread's execution since // all code writes are 32-bit integer instruction operands, which are // guaranteed to be atomic on x86. - g_Jit.PatchAllJumpsForTimeout(); + env_->PatchAllJumpsForTimeout(); } // The JIT will be free to compile new functions while we wait, but it will @@ -141,8 +143,8 @@ WatchdogTimer::NotifyTimeoutReceived() // notification, and is therefore blocked. We take the JIT lock // anyway for sanity. { - ke::AutoLock lock(g_Jit.Mutex()); - g_Jit.UnpatchAllJumpsFromTimeout(); + ke::AutoLock lock(env_->lock()); + env_->UnpatchAllJumpsFromTimeout(); } timedout_ = false; diff --git a/sourcepawn/jit/watchdog_timer.h b/sourcepawn/jit/watchdog_timer.h index 7891bc22..e7242241 100644 --- a/sourcepawn/jit/watchdog_timer.h +++ b/sourcepawn/jit/watchdog_timer.h @@ -23,12 +23,14 @@ namespace sp { +class Environment; + typedef bool (*WatchdogCallback)(); class WatchdogTimer : public ke::IRunnable { public: - WatchdogTimer(); + WatchdogTimer(Environment *env); ~WatchdogTimer(); bool Initialize(size_t timeout_ms); @@ -43,6 +45,8 @@ class WatchdogTimer : public ke::IRunnable void Run(); private: + Environment *env_; + bool terminate_; size_t timeout_ms_; ke::ThreadId mainthread_; diff --git a/sourcepawn/jit/x86/jit_x86.cpp b/sourcepawn/jit/x86/jit_x86.cpp index 09c5c698..9c362e9a 100644 --- a/sourcepawn/jit/x86/jit_x86.cpp +++ b/sourcepawn/jit/x86/jit_x86.cpp @@ -1928,7 +1928,7 @@ JITX86::CompileFunction(PluginRuntime *prt, cell_t pcode_offs, int *err) // Grab the lock before linking code in, since the watchdog timer will look // at this list on another thread. - ke::AutoLock lock(g_Jit.Mutex()); + ke::AutoLock lock(Environment::get()->lock()); prt->AddJittedFunction(fun); return fun; @@ -2031,62 +2031,11 @@ JITX86::InvokeFunction(PluginRuntime *runtime, CompiledFunction *fn, cell_t *res JIT_EXECUTE pfn = (JIT_EXECUTE)m_pJitEntry; - if (level_++ == 0) - frame_id_++; + Environment::get()->EnterInvoke(); int err = pfn(ctx, runtime->plugin()->memory, fn->GetEntryAddress()); - level_--; + Environment::get()->LeaveInvoke(); *result = ctx->rval; return err; } -void -JITX86::RegisterRuntime(PluginRuntime *rt) -{ - mutex_.AssertCurrentThreadOwns(); - runtimes_.append(rt); -} - -void -JITX86::DeregisterRuntime(PluginRuntime *rt) -{ - mutex_.AssertCurrentThreadOwns(); - runtimes_.remove(rt); -} - -void -JITX86::PatchAllJumpsForTimeout() -{ - mutex_.AssertCurrentThreadOwns(); - for (ke::InlineList::iterator iter = runtimes_.begin(); iter != runtimes_.end(); iter++) { - PluginRuntime *rt = *iter; - for (size_t i = 0; i < rt->NumJitFunctions(); i++) { - CompiledFunction *fun = rt->GetJitFunction(i); - uint8_t *base = reinterpret_cast(fun->GetEntryAddress()); - - for (size_t j = 0; j < fun->NumLoopEdges(); j++) { - const LoopEdge &e = fun->GetLoopEdge(j); - int32_t diff = intptr_t(m_pJitTimeout) - intptr_t(base + e.offset); - *reinterpret_cast(base + e.offset - 4) = diff; - } - } - } -} - -void -JITX86::UnpatchAllJumpsFromTimeout() -{ - mutex_.AssertCurrentThreadOwns(); - for (ke::InlineList::iterator iter = runtimes_.begin(); iter != runtimes_.end(); iter++) { - PluginRuntime *rt = *iter; - for (size_t i = 0; i < rt->NumJitFunctions(); i++) { - CompiledFunction *fun = rt->GetJitFunction(i); - uint8_t *base = reinterpret_cast(fun->GetEntryAddress()); - - for (size_t j = 0; j < fun->NumLoopEdges(); j++) { - const LoopEdge &e = fun->GetLoopEdge(j); - *reinterpret_cast(base + e.offset - 4) = e.disp32; - } - } - } -} diff --git a/sourcepawn/jit/x86/jit_x86.h b/sourcepawn/jit/x86/jit_x86.h index de1e98c7..e48d8de7 100644 --- a/sourcepawn/jit/x86/jit_x86.h +++ b/sourcepawn/jit/x86/jit_x86.h @@ -26,7 +26,6 @@ #include "sp_vm_basecontext.h" #include "compiled-function.h" #include "opcodes.h" -#include using namespace SourcePawn; @@ -163,33 +162,19 @@ class JITX86 ICompilation *ApplyOptions(ICompilation *_IN, ICompilation *_OUT); int InvokeFunction(PluginRuntime *runtime, CompiledFunction *fn, cell_t *result); - void RegisterRuntime(PluginRuntime *rt); - void DeregisterRuntime(PluginRuntime *rt); - void PatchAllJumpsForTimeout(); - void UnpatchAllJumpsFromTimeout(); - + void *TimeoutStub() const { + return m_pJitTimeout; + } + public: ExternalAddress GetUniversalReturn() { return ExternalAddress(m_pJitReturn); } - uintptr_t FrameId() const { - return frame_id_; - } - bool RunningCode() const { - return level_ != 0; - } - ke::Mutex *Mutex() { - return &mutex_; - } private: void *m_pJitEntry; /* Entry function */ void *m_pJitReturn; /* Universal return address */ void *m_pJitTimeout; /* Universal timeout address */ - ke::InlineList runtimes_; - uintptr_t frame_id_; - uintptr_t level_; - ke::Mutex mutex_; }; const Register pri = eax;