Move watchdog/runtime interaction into Environment.
This commit is contained in:
parent
b406c3d03d
commit
8c95919b32
@ -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<PluginRuntime>::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<uint8_t *>(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<int32_t *>(base + e.offset - 4) = diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Environment::UnpatchAllJumpsFromTimeout()
|
||||
{
|
||||
mutex_.AssertCurrentThreadOwns();
|
||||
for (ke::InlineList<PluginRuntime>::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<uint8_t *>(fun->GetEntryAddress());
|
||||
|
||||
for (size_t j = 0; j < fun->NumLoopEdges(); j++) {
|
||||
const LoopEdge &e = fun->GetLoopEdge(j);
|
||||
*reinterpret_cast<int32_t *>(base + e.offset - 4) = e.disp32;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,10 @@
|
||||
|
||||
#include <sp_vm_api.h>
|
||||
#include <am-utility.h> // Replace with am-cxx later.
|
||||
#include <am-inlinelist.h>
|
||||
#include <am-thread-utils.h>
|
||||
#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<ISourcePawnEngine> api_v1_;
|
||||
ke::AutoPtr<ISourcePawnEngine2> api_v2_;
|
||||
ke::AutoPtr<WatchdogTimer> 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<PluginRuntime> 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_
|
||||
|
@ -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];
|
||||
|
@ -15,15 +15,17 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with SourcePawn. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "watchdog_timer.h"
|
||||
#include "x86/jit_x86.h"
|
||||
#include <string.h>
|
||||
#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;
|
||||
|
@ -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_;
|
||||
|
@ -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<PluginRuntime>::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<uint8_t *>(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<int32_t *>(base + e.offset - 4) = diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
JITX86::UnpatchAllJumpsFromTimeout()
|
||||
{
|
||||
mutex_.AssertCurrentThreadOwns();
|
||||
for (ke::InlineList<PluginRuntime>::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<uint8_t *>(fun->GetEntryAddress());
|
||||
|
||||
for (size_t j = 0; j < fun->NumLoopEdges(); j++) {
|
||||
const LoopEdge &e = fun->GetLoopEdge(j);
|
||||
*reinterpret_cast<int32_t *>(base + e.offset - 4) = e.disp32;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,6 @@
|
||||
#include "sp_vm_basecontext.h"
|
||||
#include "compiled-function.h"
|
||||
#include "opcodes.h"
|
||||
#include <am-thread-utils.h>
|
||||
|
||||
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<PluginRuntime> runtimes_;
|
||||
uintptr_t frame_id_;
|
||||
uintptr_t level_;
|
||||
ke::Mutex mutex_;
|
||||
};
|
||||
|
||||
const Register pri = eax;
|
||||
|
Loading…
Reference in New Issue
Block a user