Implement a watchdog timer for scripts that take too long to execute (bug 5837, r=fyren).
authorDavid Anderson <dvander@alliedmods.net>
Wed Aug 14 23:54:25 2013 -0700 (2013-08-14)
changeset 40136201c1180e0e
parent 4012 e3e3d9c6eef6
child 4014 c20558a6734e
Implement a watchdog timer for scripts that take too long to execute (bug 5837, r=fyren).
core/sourcemod.cpp
public/jit/assembler.h
public/jit/x86/assembler-x86.h
public/sourcepawn/ke_inline_list.h
public/sourcepawn/ke_thread_posix.h
public/sourcepawn/ke_thread_utils.h
public/sourcepawn/ke_thread_windows.h
public/sourcepawn/ke_utility.h
public/sourcepawn/sp_vm_api.h
public/sourcepawn/sp_vm_types.h
sourcepawn/jit/AMBuilder
sourcepawn/jit/BaseRuntime.cpp
sourcepawn/jit/BaseRuntime.h
sourcepawn/jit/Makefile.shell
sourcepawn/jit/dll_exports.cpp
sourcepawn/jit/engine2.cpp
sourcepawn/jit/engine2.h
sourcepawn/jit/jit_function.cpp
sourcepawn/jit/jit_function.h
sourcepawn/jit/sp_vm_basecontext.cpp
sourcepawn/jit/sp_vm_engine.cpp
sourcepawn/jit/watchdog_timer.cpp
sourcepawn/jit/watchdog_timer.h
sourcepawn/jit/x86/jit_x86.cpp
sourcepawn/jit/x86/jit_x86.h
     1.1 --- a/core/sourcemod.cpp	Wed Aug 14 17:18:02 2013 -0400
     1.2 +++ b/core/sourcemod.cpp	Wed Aug 14 23:54:25 2013 -0700
     1.3 @@ -1,5 +1,5 @@
     1.4  /**
     1.5 - * vim: set ts=4 sw=4 :
     1.6 + * vim: set ts=4 sw=4 tw=99 noet:
     1.7   * =============================================================================
     1.8   * SourceMod
     1.9   * Copyright (C) 2004-2010 AlliedModders LLC.  All rights reserved.
    1.10 @@ -311,6 +311,16 @@
    1.11  	{
    1.12  		extsys->LoadAutoExtension("updater.ext." PLATFORM_LIB_EXT);
    1.13  	}
    1.14 +
    1.15 +	const char *timeout = GetCoreConfigValue("SlowScriptTimeout");
    1.16 +	if (timeout == NULL)
    1.17 +	{
    1.18 +		timeout = "8";
    1.19 +	}
    1.20 +	if (atoi(timeout) != 0)
    1.21 +	{
    1.22 +		g_pSourcePawn2->InstallWatchdogTimer(atoi(timeout) * 1000);
    1.23 +	}
    1.24  }
    1.25  
    1.26  static bool g_LevelEndBarrier = false;
     2.1 --- a/public/jit/assembler.h	Wed Aug 14 17:18:02 2013 -0400
     2.2 +++ b/public/jit/assembler.h	Wed Aug 14 23:54:25 2013 -0700
     2.3 @@ -64,6 +64,11 @@
     2.4      return pos_ - buffer_;
     2.5    }
     2.6  
     2.7 +  // Current offset into the code stream.
     2.8 +  uint32_t pc() const {
     2.9 +    return uint32_t(pos_ - buffer_);
    2.10 +  }
    2.11 +
    2.12   protected:
    2.13    void writeByte(uint8_t byte) {
    2.14      write<uint8_t>(byte);
    2.15 @@ -125,11 +130,6 @@
    2.16      return int32_t(pos_ - buffer_);
    2.17    }
    2.18  
    2.19 -  // pc is the unsigned version of position().
    2.20 -  uint32_t pc() const {
    2.21 -    return uint32_t(pos_ - buffer_);
    2.22 -  }
    2.23 -
    2.24   protected:
    2.25    void assertCanWrite(size_t bytes) {
    2.26      assert(pos_ + bytes <= end_);
     3.1 --- a/public/jit/x86/assembler-x86.h	Wed Aug 14 17:18:02 2013 -0400
     3.2 +++ b/public/jit/x86/assembler-x86.h	Wed Aug 14 23:54:25 2013 -0700
     3.3 @@ -640,6 +640,10 @@
     3.4        emit2(0xdc, 0xc0 + src.code);
     3.5    }
     3.6  
     3.7 +  void jmp32(Label *dest) {
     3.8 +    emit1(0xe9);
     3.9 +    emitJumpTarget(dest);
    3.10 +  }
    3.11    void jmp(Label *dest) {
    3.12      int8_t d8;
    3.13      if (canEmitSmallJump(dest, &d8)) {
    3.14 @@ -655,6 +659,10 @@
    3.15    void jmp(const Operand &target) {
    3.16      emit1(0xff, 4, target);
    3.17    }
    3.18 +  void j32(ConditionCode cc, Label *dest) {
    3.19 +    emit2(0x0f, 0x80 + uint8_t(cc));
    3.20 +    emitJumpTarget(dest);
    3.21 +  }
    3.22    void j(ConditionCode cc, Label *dest) {
    3.23      int8_t d8;
    3.24      if (canEmitSmallJump(dest, &d8)) {
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/public/sourcepawn/ke_inline_list.h	Wed Aug 14 23:54:25 2013 -0700
     4.3 @@ -0,0 +1,137 @@
     4.4 +/**
     4.5 + * vim: set sts=2 ts=8 sw=2 tw=99 noet :
     4.6 + * =============================================================================
     4.7 + * SourcePawn
     4.8 + * Copyright (C) 2004-2009 AlliedModders LLC.  All rights reserved.
     4.9 + * =============================================================================
    4.10 + *
    4.11 + * This program is free software; you can redistribute it and/or modify it under
    4.12 + * the terms of the GNU General Public License, version 3.0, as published by the
    4.13 + * Free Software Foundation.
    4.14 + * 
    4.15 + * This program is distributed in the hope that it will be useful, but WITHOUT
    4.16 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    4.17 + * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    4.18 + * details.
    4.19 + *
    4.20 + * You should have received a copy of the GNU General Public License along with
    4.21 + * this program.  If not, see <http://www.gnu.org/licenses/>.
    4.22 + *
    4.23 + * As a special exception, AlliedModders LLC gives you permission to link the
    4.24 + * code of this program (as well as its derivative works) to "Half-Life 2," the
    4.25 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    4.26 + * by the Valve Corporation.  You must obey the GNU General Public License in
    4.27 + * all respects for all other code used.  Additionally, AlliedModders LLC grants
    4.28 + * this exception to all derivative works.  AlliedModders LLC defines further
    4.29 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    4.30 + * or <http://www.sourcemod.net/license.php>.
    4.31 + */
    4.32 +#ifndef _include_sourcepawn_inline_list_h_
    4.33 +#define _include_sourcepawn_inline_list_h_
    4.34 +
    4.35 +#include <stddef.h>
    4.36 +
    4.37 +template <typename T> class InlineList;
    4.38 +
    4.39 +template <typename T>
    4.40 +class InlineListNode
    4.41 +{
    4.42 +  friend class InlineList<T>;
    4.43 +
    4.44 +  public:
    4.45 +  InlineListNode()
    4.46 +    : next_(NULL),
    4.47 +    prev_(NULL)
    4.48 +  {
    4.49 +  }
    4.50 +
    4.51 +  InlineListNode(InlineListNode *next, InlineListNode *prev)
    4.52 +    : next_(next),
    4.53 +    prev_(prev)
    4.54 +  {
    4.55 +  }
    4.56 +
    4.57 +  protected:
    4.58 +  InlineListNode *next_;
    4.59 +  InlineListNode *prev_;
    4.60 +};
    4.61 +
    4.62 +template <typename T>
    4.63 +class InlineList
    4.64 +{
    4.65 +  typedef InlineListNode<T> Node;
    4.66 +
    4.67 +  Node head_;
    4.68 +
    4.69 + public:
    4.70 +  InlineList()
    4.71 +    : head_(&head_, &head_)
    4.72 +  {
    4.73 +  }
    4.74 +
    4.75 + public:
    4.76 +  class iterator
    4.77 +  {
    4.78 +    friend class InlineList;
    4.79 +    Node *iter_;
    4.80 +
    4.81 +   public:
    4.82 +    iterator(Node *iter)
    4.83 +      : iter_(iter)
    4.84 +    {
    4.85 +    }
    4.86 +
    4.87 +    iterator & operator ++() {
    4.88 +      iter_ = iter_->next;
    4.89 +      return *iter_;
    4.90 +    }
    4.91 +    iterator operator ++(int) {
    4.92 +      iterator old(*this);
    4.93 +      iter_ = iter_->next_;
    4.94 +      return old;
    4.95 +    }
    4.96 +    T * operator *() {
    4.97 +      return static_cast<T *>(iter_);
    4.98 +    }
    4.99 +    T * operator ->() {
   4.100 +      return static_cast<T *>(iter_);
   4.101 +    }
   4.102 +    bool operator !=(const iterator &where) const {
   4.103 +      return iter_ != where.iter_;
   4.104 +    }
   4.105 +    bool operator ==(const iterator &where) const {
   4.106 +      return iter_ == where.iter_;
   4.107 +    }
   4.108 +    iterator prev() const {
   4.109 +      iterator p(iter_->prev_);
   4.110 +      return p;
   4.111 +    }
   4.112 +    iterator next() const {
   4.113 +      iterator p(iter_->next_);
   4.114 +      return p;
   4.115 +    }
   4.116 +  };
   4.117 +
   4.118 +  iterator begin() {
   4.119 +    return iterator(head_.next_);
   4.120 +  }
   4.121 +
   4.122 +  iterator end() {
   4.123 +    return iterator(&head_);
   4.124 +  }
   4.125 +
   4.126 +  void erase(Node *t) {
   4.127 +    t->prev_->next_ = t->next_;
   4.128 +    t->next_->prev_ = t->prev_;
   4.129 +  }
   4.130 +
   4.131 +  void insert(Node *t) {
   4.132 +    t->prev_ = head_.prev_;
   4.133 +    t->next_ = &head_;
   4.134 +    head_.prev_->next_ = t;
   4.135 +    head_.prev_ = t;
   4.136 +  }
   4.137 +};
   4.138 +
   4.139 +#endif // _include_sourcepawn_inline_list_h_
   4.140 +
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/public/sourcepawn/ke_thread_posix.h	Wed Aug 14 23:54:25 2013 -0700
     5.3 @@ -0,0 +1,188 @@
     5.4 +// vim: set ts=8 sts=2 sw=2 tw=99 et:
     5.5 +//
     5.6 +// This file is part of SourcePawn.
     5.7 +// 
     5.8 +// SourcePawn is free software: you can redistribute it and/or modify
     5.9 +// it under the terms of the GNU General Public License as published by
    5.10 +// the Free Software Foundation, either version 3 of the License, or
    5.11 +// (at your option) any later version.
    5.12 +// 
    5.13 +// SourcePawn is distributed in the hope that it will be useful,
    5.14 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
    5.15 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    5.16 +// GNU General Public License for more details.
    5.17 +// 
    5.18 +// You should have received a copy of the GNU General Public License
    5.19 +// along with SourcePawn.  If not, see <http://www.gnu.org/licenses/>.
    5.20 +#ifndef _include_sourcepawn_thread_posix_h_
    5.21 +#define _include_sourcepawn_thread_posix_h_
    5.22 +
    5.23 +#include <pthread.h>
    5.24 +#include <sys/time.h>
    5.25 +#include <errno.h>
    5.26 +#include <stdio.h>
    5.27 +#if defined(__linux__)
    5.28 +# include <sys/prctl.h>
    5.29 +#endif
    5.30 +
    5.31 +namespace ke {
    5.32 +
    5.33 +class Mutex : public Lockable
    5.34 +{
    5.35 + public:
    5.36 +  Mutex() {
    5.37 +#if !defined(NDEBUG)
    5.38 +    int rv =
    5.39 +#endif
    5.40 +      pthread_mutex_init(&mutex_, NULL);
    5.41 +    assert(rv == 0);
    5.42 +  }
    5.43 +  ~Mutex() {
    5.44 +    pthread_mutex_destroy(&mutex_);
    5.45 +  }
    5.46 +
    5.47 +  bool DoTryLock() {
    5.48 +    return pthread_mutex_trylock(&mutex_) == 0;
    5.49 +  }
    5.50 +
    5.51 +  void DoLock() {
    5.52 +    pthread_mutex_lock(&mutex_);
    5.53 +  }
    5.54 +
    5.55 +  void DoUnlock() {
    5.56 +    pthread_mutex_unlock(&mutex_);
    5.57 +  }
    5.58 +  
    5.59 +  pthread_mutex_t *raw() {
    5.60 +    return &mutex_;
    5.61 +  }
    5.62 +
    5.63 + private:
    5.64 +  pthread_mutex_t mutex_;
    5.65 +};
    5.66 +
    5.67 +// Currently, this class only supports single-listener CVs.
    5.68 +class ConditionVariable : public Lockable
    5.69 +{
    5.70 + public:
    5.71 +  ConditionVariable() {
    5.72 +#if !defined(NDEBUG)
    5.73 +    int rv =
    5.74 +#endif
    5.75 +      pthread_cond_init(&cv_, NULL);
    5.76 +    assert(rv == 0);
    5.77 +  }
    5.78 +  ~ConditionVariable() {
    5.79 +    pthread_cond_destroy(&cv_);
    5.80 +  }
    5.81 +
    5.82 +  bool DoTryLock() {
    5.83 +    return mutex_.DoTryLock();
    5.84 +  }
    5.85 +  void DoLock() {
    5.86 +    mutex_.DoLock();
    5.87 +  }
    5.88 +  void DoUnlock() {
    5.89 +    mutex_.DoUnlock();
    5.90 +  }
    5.91 +
    5.92 +  void Notify() {
    5.93 +    AssertCurrentThreadOwns();
    5.94 +    pthread_cond_signal(&cv_);
    5.95 +  }
    5.96 +
    5.97 +  WaitResult Wait(size_t timeout_ms) {
    5.98 +    AssertCurrentThreadOwns();
    5.99 +
   5.100 +    struct timespec ts;
   5.101 +    if (clock_gettime(CLOCK_REALTIME, &ts) == -1)
   5.102 +      return Wait_Error;
   5.103 +
   5.104 +    ts.tv_sec += timeout_ms / 1000;
   5.105 +    ts.tv_nsec += (timeout_ms % 1000) * 1000000;
   5.106 +    if (ts.tv_nsec >= 1000000000) {
   5.107 +      ts.tv_sec++;
   5.108 +      ts.tv_nsec -= 1000000000;
   5.109 +    }
   5.110 +
   5.111 +    DebugSetUnlocked();
   5.112 +    int rv = pthread_cond_timedwait(&cv_, mutex_.raw(), &ts);
   5.113 +    DebugSetLocked();
   5.114 +
   5.115 +    if (rv == ETIMEDOUT)
   5.116 +      return Wait_Timeout;
   5.117 +    if (rv == 0)
   5.118 +      return Wait_Signaled;
   5.119 +    return Wait_Error;
   5.120 +  }
   5.121 +
   5.122 +  WaitResult Wait() {
   5.123 +    AssertCurrentThreadOwns();
   5.124 +
   5.125 +    DebugSetUnlocked();
   5.126 +    int rv = pthread_cond_wait(&cv_, mutex_.raw());
   5.127 +    DebugSetLocked();
   5.128 +
   5.129 +    if (rv == 0)
   5.130 +      return Wait_Signaled;
   5.131 +    return Wait_Error;
   5.132 +  }
   5.133 +
   5.134 + private:
   5.135 +  Mutex mutex_;
   5.136 +  pthread_cond_t cv_;
   5.137 +};
   5.138 +
   5.139 +class Thread
   5.140 +{
   5.141 +  struct ThreadData {
   5.142 +    IRunnable *run;
   5.143 +    char name[17];
   5.144 +  };
   5.145 + public:
   5.146 +  Thread(IRunnable *run, const char *name = NULL) {
   5.147 +    ThreadData *data = new ThreadData;
   5.148 +    data->run = run;
   5.149 +    snprintf(data->name, sizeof(data->name), "%s", name ? name : "");
   5.150 +
   5.151 +    initialized_ = (pthread_create(&thread_, NULL, Main, data) == 0);
   5.152 +    if (!initialized_)
   5.153 +      delete data;
   5.154 +  }
   5.155 +
   5.156 +  bool Succeeded() const {
   5.157 +    return initialized_;
   5.158 +  }
   5.159 +
   5.160 +  void Join() {
   5.161 +    if (!Succeeded())
   5.162 +      return;
   5.163 +    pthread_join(thread_, NULL);
   5.164 +  }
   5.165 +
   5.166 + private:
   5.167 +  static void *Main(void *arg) {
   5.168 +    AutoPtr<ThreadData> data((ThreadData *)arg);
   5.169 +
   5.170 +    if (data->name[0]) {
   5.171 +#if defined(__linux__)
   5.172 +      prctl(PR_SET_NAME, (unsigned long)data->name);
   5.173 +#elif defined(__APPLE__)
   5.174 +      int (*fn)(const char *) = (int (*)(const char *))dlsym(RTLD_DEFAULT, "pthread_setname_np");
   5.175 +      if (fn)
   5.176 +        fn(data->name);
   5.177 +#endif
   5.178 +    }
   5.179 +    data->run->Run();
   5.180 +    return NULL;
   5.181 +  }
   5.182 +
   5.183 + private:
   5.184 +  bool initialized_;
   5.185 +  pthread_t thread_;
   5.186 +};
   5.187 +
   5.188 +} // namespace ke
   5.189 +
   5.190 +#endif // _include_sourcepawn_thread_posix_h_
   5.191 +
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/public/sourcepawn/ke_thread_utils.h	Wed Aug 14 23:54:25 2013 -0700
     6.3 @@ -0,0 +1,241 @@
     6.4 +// vim: set ts=8 sts=2 sw=2 tw=99 et:
     6.5 +//
     6.6 +// This file is part of SourcePawn.
     6.7 +// 
     6.8 +// SourcePawn is free software: you can redistribute it and/or modify
     6.9 +// it under the terms of the GNU General Public License as published by
    6.10 +// the Free Software Foundation, either version 3 of the License, or
    6.11 +// (at your option) any later version.
    6.12 +// 
    6.13 +// SourcePawn is distributed in the hope that it will be useful,
    6.14 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
    6.15 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    6.16 +// GNU General Public License for more details.
    6.17 +// 
    6.18 +// You should have received a copy of the GNU General Public License
    6.19 +// along with SourcePawn.  If not, see <http://www.gnu.org/licenses/>.
    6.20 +#ifndef _include_sourcepawn_threads_
    6.21 +#define _include_sourcepawn_threads_
    6.22 +
    6.23 +#include <assert.h>
    6.24 +#if defined(_MSC_VER)
    6.25 +# include <windows.h>
    6.26 +#else
    6.27 +# include <pthread.h>
    6.28 +#endif
    6.29 +#include <ke_utility.h>
    6.30 +
    6.31 +// Thread primitives for SourcePawn.
    6.32 +//
    6.33 +// -- Mutexes --
    6.34 +//
    6.35 +// A Lockable is a mutual exclusion primitive. It can be owned by at most one
    6.36 +// thread at a time, and ownership blocks any other thread from taking taking
    6.37 +// ownership. Ownership must be acquired and released on the same thread.
    6.38 +// Lockables are not re-entrant.
    6.39 +//
    6.40 +// While a few classes support the Lockable interface, the simplest Lockable
    6.41 +// object that can be instantiated is a Mutex.
    6.42 +//
    6.43 +// -- Condition Variables --
    6.44 +//
    6.45 +// A ConditionVariable provides mutually exclusive access based on a
    6.46 +// condition ocurring. CVs provide two capabilities: Wait(), which will block
    6.47 +// until the condition is triggered, and Notify(), which signals any blocking
    6.48 +// thread that the condition has occurred.
    6.49 +//
    6.50 +// Condition variables have an underlying mutex lock. This lock must be
    6.51 +// acquired before calling Wait() or Notify(). It is automatically released
    6.52 +// once Wait begins blocking. This operation is atomic with respect to other
    6.53 +// threads and the mutex. For example, it is not possible for the lock to be
    6.54 +// acquired by another thread in between unlocking and blocking. Since Notify
    6.55 +// also requires the lock to be acquired, there is no risk of an event
    6.56 +// accidentally dissipating into thin air because it was sent before the other
    6.57 +// thread began blocking.
    6.58 +//
    6.59 +// When Wait() returns, the lock is automatically re-acquired. This operation
    6.60 +// is NOT atomic. In between waking up and re-acquiring the lock, another
    6.61 +// thread may steal the lock and issue another event. Applications must
    6.62 +// account for this. For example, a message pump should check that there are no
    6.63 +// messages left to process before blocking again.
    6.64 +//
    6.65 +// -- Threads --
    6.66 +//
    6.67 +// A Thread object, when created, spawns a new thread with the given callback
    6.68 +// (the callbacks must implement IRunnable). Threads have one method of
    6.69 +// interest, Join(), which will block until the thread's execution finishes.
    6.70 +// Deleting a thread object will free any operating system resources associated
    6.71 +// with that thread, if the thread has finished executing.
    6.72 +//
    6.73 +// Threads can fail to spawn; make sure to check Succeeded().
    6.74 +//
    6.75 +
    6.76 +namespace ke {
    6.77 +
    6.78 +// Abstraction for accessing the current thread.
    6.79 +#if defined(_MSC_VER)
    6.80 +typedef HANDLE ThreadId;
    6.81 +
    6.82 +static inline ThreadId GetCurrentThreadId()
    6.83 +{
    6.84 +  return GetCurrentThread();
    6.85 +}
    6.86 +#else
    6.87 +typedef pthread_t ThreadId;
    6.88 +
    6.89 +static inline ThreadId GetCurrentThreadId()
    6.90 +{
    6.91 +  return pthread_self();
    6.92 +}
    6.93 +#endif
    6.94 +
    6.95 +// Classes which use non-reentrant, same-thread lock/unlock semantics should
    6.96 +// inherit from this and implement DoLock/DoUnlock.
    6.97 +class Lockable
    6.98 +{
    6.99 + public:
   6.100 +  Lockable()
   6.101 +  {
   6.102 +#if !defined(NDEBUG)
   6.103 +    owner_ = 0;
   6.104 +#endif
   6.105 +  }
   6.106 +  virtual ~Lockable() {
   6.107 +  }
   6.108 +
   6.109 +  bool TryLock() {
   6.110 +    if (DoTryLock()) {
   6.111 +      DebugSetLocked();
   6.112 +      return true;
   6.113 +    }
   6.114 +    return false;
   6.115 +  }
   6.116 +
   6.117 +  void Lock() {
   6.118 +    assert(Owner() != GetCurrentThreadId());
   6.119 +    DoLock();
   6.120 +    DebugSetLocked();
   6.121 +  }
   6.122 +
   6.123 +  void Unlock() {
   6.124 +    assert(Owner() == GetCurrentThreadId());
   6.125 +    DebugSetUnlocked();
   6.126 +    DoUnlock();
   6.127 +  }
   6.128 +
   6.129 +  void AssertCurrentThreadOwns() const {
   6.130 +    assert(Owner() == GetCurrentThreadId());
   6.131 +  }
   6.132 +#if !defined(NDEBUG)
   6.133 +  bool Locked() const {
   6.134 +    return owner_ != 0;
   6.135 +  }
   6.136 +  ThreadId Owner() const {
   6.137 +    return owner_;
   6.138 +  }
   6.139 +#endif
   6.140 +
   6.141 +  virtual bool DoTryLock() = 0;
   6.142 +  virtual void DoLock() = 0;
   6.143 +  virtual void DoUnlock() = 0;
   6.144 +
   6.145 + protected:
   6.146 +  void DebugSetUnlocked() {
   6.147 +#if !defined(NDEBUG)
   6.148 +    owner_ = 0;
   6.149 +#endif
   6.150 +  }
   6.151 +  void DebugSetLocked() {
   6.152 +#if !defined(NDEBUG)
   6.153 +    owner_ = GetCurrentThreadId();
   6.154 +#endif
   6.155 +  }
   6.156 +
   6.157 + protected:
   6.158 +#if !defined(NDEBUG)
   6.159 +  ThreadId owner_;
   6.160 +#endif
   6.161 +};
   6.162 +
   6.163 +// RAII for automatically locking and unlocking an object.
   6.164 +class AutoLock
   6.165 +{
   6.166 + public:
   6.167 +  AutoLock(Lockable *lock)
   6.168 +   : lock_(lock)
   6.169 +  {
   6.170 +    lock_->Lock();
   6.171 +  }
   6.172 +  ~AutoLock() {
   6.173 +    lock_->Unlock();
   6.174 +  }
   6.175 +
   6.176 + private:
   6.177 +  Lockable *lock_;
   6.178 +};
   6.179 +
   6.180 +class AutoTryLock
   6.181 +{
   6.182 + public:
   6.183 +  AutoTryLock(Lockable *lock)
   6.184 +  {
   6.185 +    lock_ = lock->TryLock() ? lock : NULL;
   6.186 +  }
   6.187 +  ~AutoTryLock() {
   6.188 +    if (lock_)
   6.189 +      lock_->Unlock();
   6.190 +  }
   6.191 +
   6.192 + private:
   6.193 +  Lockable *lock_;
   6.194 +};
   6.195 +
   6.196 +// RAII for automatically unlocking and relocking an object.
   6.197 +class AutoUnlock
   6.198 +{
   6.199 + public:
   6.200 +  AutoUnlock(Lockable *lock)
   6.201 +   : lock_(lock)
   6.202 +  {
   6.203 +    lock_->Unlock();
   6.204 +  }
   6.205 +  ~AutoUnlock() {
   6.206 +    lock_->Lock();
   6.207 +  }
   6.208 +
   6.209 + private:
   6.210 +  Lockable *lock_;
   6.211 +};
   6.212 +
   6.213 +enum WaitResult {
   6.214 +  // Woke up because something happened.
   6.215 +  Wait_Signaled,
   6.216 +
   6.217 +  // Woke up because nothing happened and a timeout was specified.
   6.218 +  Wait_Timeout,
   6.219 +
   6.220 +  // Woke up, but because of an error.
   6.221 +  Wait_Error
   6.222 +};
   6.223 +
   6.224 +// This must be implemented in order to spawn a new thread.
   6.225 +class IRunnable
   6.226 +{
   6.227 + public:
   6.228 +  virtual ~IRunnable() {
   6.229 +  }
   6.230 +
   6.231 +  virtual void Run() = 0;
   6.232 +};
   6.233 +
   6.234 +} // namespace ke
   6.235 +
   6.236 +// Include the actual thread implementations.
   6.237 +#if defined(_MSC_VER)
   6.238 +# include "ke_thread_windows.h"
   6.239 +#else
   6.240 +# include "ke_thread_posix.h"
   6.241 +#endif
   6.242 +
   6.243 +#endif // _include_sourcepawn_threads_
   6.244 +
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/public/sourcepawn/ke_thread_windows.h	Wed Aug 14 23:54:25 2013 -0700
     7.3 @@ -0,0 +1,144 @@
     7.4 +// vim: set ts=8 sts=2 sw=2 tw=99 et:
     7.5 +//
     7.6 +// This file is part of SourcePawn.
     7.7 +// 
     7.8 +// SourcePawn is free software: you can redistribute it and/or modify
     7.9 +// it under the terms of the GNU General Public License as published by
    7.10 +// the Free Software Foundation, either version 3 of the License, or
    7.11 +// (at your option) any later version.
    7.12 +// 
    7.13 +// SourcePawn is distributed in the hope that it will be useful,
    7.14 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
    7.15 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    7.16 +// GNU General Public License for more details.
    7.17 +// 
    7.18 +// You should have received a copy of the GNU General Public License
    7.19 +// along with SourcePawn.  If not, see <http://www.gnu.org/licenses/>.
    7.20 +#ifndef _include_sourcepawn_thread_windows_h_
    7.21 +#define _include_sourcepawn_thread_windows_h_
    7.22 +
    7.23 +#include <windows.h>
    7.24 +
    7.25 +namespace ke {
    7.26 +
    7.27 +class CriticalSection : public Lockable
    7.28 +{
    7.29 + public:
    7.30 +  CriticalSection() {
    7.31 +    InitializeCriticalSection(&cs_);
    7.32 +  }
    7.33 +  ~CriticalSection() {
    7.34 +    DeleteCriticalSection(&cs_);
    7.35 +  }
    7.36 +
    7.37 +  bool DoTryLock() {
    7.38 +    return !!TryEnterCriticalSection(&cs_);
    7.39 +  }
    7.40 +  void DoLock() {
    7.41 +    EnterCriticalSection(&cs_);
    7.42 +  }
    7.43 +
    7.44 +  void DoUnlock() {
    7.45 +    LeaveCriticalSection(&cs_);
    7.46 +  }
    7.47 +
    7.48 + private:
    7.49 +  CRITICAL_SECTION cs_;
    7.50 +};
    7.51 +
    7.52 +typedef CriticalSection Mutex;
    7.53 +
    7.54 +// Currently, this class only supports single-listener CVs.
    7.55 +class ConditionVariable : public Lockable
    7.56 +{
    7.57 + public:
    7.58 +  ConditionVariable() {
    7.59 +    event_ = CreateEvent(NULL, FALSE, FALSE, NULL);
    7.60 +  }
    7.61 +  ~ConditionVariable() {
    7.62 +    CloseHandle(event_);
    7.63 +  }
    7.64 +
    7.65 +  bool DoTryLock() {
    7.66 +    return cs_.DoTryLock();
    7.67 +  }
    7.68 +  void DoLock() {
    7.69 +    cs_.DoLock();
    7.70 +  }
    7.71 +  void DoUnlock() {
    7.72 +    cs_.DoUnlock();
    7.73 +  }
    7.74 +
    7.75 +  void Notify() {
    7.76 +    AssertCurrentThreadOwns();
    7.77 +    SetEvent(event_);
    7.78 +  }
    7.79 +
    7.80 +  WaitResult Wait(size_t timeout_ms) {
    7.81 +    // This will assert if the lock has not been acquired. We don't need to be
    7.82 +    // atomic here, like pthread_cond_wait, because the event bit will stick
    7.83 +    // until reset by a wait function.
    7.84 +    Unlock();
    7.85 +    DWORD rv = WaitForSingleObject(event_, timeout_ms);
    7.86 +    Lock();
    7.87 +
    7.88 +    if (rv == WAIT_TIMEOUT)
    7.89 +      return Wait_Timeout;
    7.90 +    if (rv == WAIT_FAILED)
    7.91 +      return Wait_Error;
    7.92 +    return Wait_Signaled;
    7.93 +  }
    7.94 +
    7.95 +  WaitResult Wait() {
    7.96 +    return Wait(INFINITE);
    7.97 +  }
    7.98 +
    7.99 + private:
   7.100 +  CriticalSection cs_;
   7.101 +  HANDLE event_;
   7.102 +};
   7.103 +
   7.104 +class Thread
   7.105 +{
   7.106 + public:
   7.107 +  Thread(IRunnable *run, const char *name = NULL) {
   7.108 +    thread_ = CreateThread(NULL, 0, Main, run, 0, NULL);
   7.109 +  }
   7.110 +  ~Thread() {
   7.111 +    if (!thread_)
   7.112 +      return;
   7.113 +    CloseHandle(thread_);
   7.114 +  }
   7.115 +
   7.116 +  bool Succeeded() const {
   7.117 +    return !!thread_;
   7.118 +  }
   7.119 +
   7.120 +  void Join() {
   7.121 +    if (!Succeeded())
   7.122 +      return;
   7.123 +    WaitForSingleObject(thread_, INFINITE);
   7.124 +  }
   7.125 +
   7.126 + private:
   7.127 +  static DWORD WINAPI Main(LPVOID arg) {
   7.128 +    ((IRunnable *)arg)->Run();
   7.129 +    return 0;
   7.130 +  }
   7.131 +
   7.132 +#pragma pack(push, 8)
   7.133 +  struct ThreadNameInfo {
   7.134 +    DWORD dwType;
   7.135 +    LPCSTR szName;
   7.136 +    DWORD dwThreadID;
   7.137 +    DWORD dwFlags;
   7.138 +  };
   7.139 +#pragma pack(pop)
   7.140 +
   7.141 + private:
   7.142 +  HANDLE thread_;
   7.143 +};
   7.144 +
   7.145 +} // namespace ke
   7.146 +
   7.147 +#endif // _include_sourcepawn_thread_windows_h_
     8.1 --- a/public/sourcepawn/ke_utility.h	Wed Aug 14 17:18:02 2013 -0400
     8.2 +++ b/public/sourcepawn/ke_utility.h	Wed Aug 14 23:54:25 2013 -0700
     8.3 @@ -48,34 +48,45 @@
     8.4  static const size_t kMB = 1024 * kKB;
     8.5  static const size_t kGB = 1024 * kMB;
     8.6  
     8.7 +template <typename T> T
     8.8 +ReturnAndVoid(T &t)
     8.9 +{
    8.10 +    T saved = t;
    8.11 +    t = T();
    8.12 +    return saved;
    8.13 +}
    8.14 +
    8.15  template <typename T>
    8.16 -class AutoFree
    8.17 +class AutoPtr
    8.18  {
    8.19      T *t_;
    8.20  
    8.21    public:
    8.22 -    AutoFree()
    8.23 +    AutoPtr()
    8.24        : t_(NULL)
    8.25      {
    8.26      }
    8.27 -    AutoFree(T *t)
    8.28 +    explicit AutoPtr(T *t)
    8.29        : t_(t)
    8.30      {
    8.31      }
    8.32 -    ~AutoFree() {
    8.33 -        free(t_);
    8.34 +    ~AutoPtr() {
    8.35 +        delete t_;
    8.36      }
    8.37      T *take() {
    8.38 -        T *t = t_;
    8.39 -        t_ = NULL;
    8.40 -        return t;
    8.41 +        return ReturnAndVoid(t_);
    8.42      }
    8.43      T *operator *() const {
    8.44          return t_;
    8.45      }
    8.46 +    T *operator ->() const {
    8.47 +        return t_;
    8.48 +    }
    8.49 +    operator T *() const {
    8.50 +        return t_;
    8.51 +    }
    8.52      void operator =(T *t) {
    8.53 -        if (t_)
    8.54 -            free(t_);
    8.55 +        delete t_;
    8.56          t_ = t;
    8.57      }
    8.58  };
    8.59 @@ -289,14 +300,6 @@
    8.60      return t1 > t2 ? t1 : t2;
    8.61  }
    8.62  
    8.63 -template <typename T> T
    8.64 -ReturnAndVoid(T &t)
    8.65 -{
    8.66 -    T saved = t;
    8.67 -    t = T();
    8.68 -    return saved;
    8.69 -}
    8.70 -
    8.71  #define OFFSETOF(Class, Member) reinterpret_cast<size_t>(&((Class *)NULL)->Member)
    8.72  
    8.73  #if defined(_MSC_VER)
     9.1 --- a/public/sourcepawn/sp_vm_api.h	Wed Aug 14 17:18:02 2013 -0400
     9.2 +++ b/public/sourcepawn/sp_vm_api.h	Wed Aug 14 23:54:25 2013 -0700
     9.3 @@ -40,7 +40,7 @@
     9.4  
     9.5  /** SourcePawn Engine API Version */
     9.6  #define SOURCEPAWN_ENGINE_API_VERSION	4
     9.7 -#define SOURCEPAWN_ENGINE2_API_VERSION	4
     9.8 +#define SOURCEPAWN_ENGINE2_API_VERSION	5
     9.9  
    9.10  #if !defined SOURCEMOD_BUILD
    9.11  #define SOURCEMOD_BUILD
    9.12 @@ -1286,6 +1286,15 @@
    9.13  		 * @return			New runtime, or NULL if not enough memory.
    9.14  		 */
    9.15  		virtual IPluginRuntime *CreateEmptyRuntime(const char *name, uint32_t memory) =0;
    9.16 +
    9.17 +		/**
    9.18 +		 * @brief Initiates the watchdog timer with the specified timeout
    9.19 +		 * length. This cannot be called more than once.
    9.20 +		 *
    9.21 +		 * @param timeout	Timeout, in ms.
    9.22 +		 * @return			True on success, false on failure.
    9.23 +		 */
    9.24 +		virtual bool InstallWatchdogTimer(size_t timeout_ms) =0;
    9.25  	};
    9.26  };
    9.27  
    10.1 --- a/public/sourcepawn/sp_vm_types.h	Wed Aug 14 17:18:02 2013 -0400
    10.2 +++ b/public/sourcepawn/sp_vm_types.h	Wed Aug 14 23:54:25 2013 -0700
    10.3 @@ -87,6 +87,7 @@
    10.4  #define SP_ERROR_CODE_TOO_NEW			27	/**< Code is too new for this VM */
    10.5  #define SP_ERROR_OUT_OF_MEMORY			28  /**< Out of memory */
    10.6  #define SP_ERROR_INTEGER_OVERFLOW		29	/**< Integer overflow (-INT_MIN / -1) */
    10.7 +#define SP_ERROR_TIMEOUT				30  /**< Timeout */
    10.8  //Hey you! Update the string table if you add to the end of me! */
    10.9  
   10.10  /**********************************************
    11.1 --- a/sourcepawn/jit/AMBuilder	Wed Aug 14 17:18:02 2013 -0400
    11.2 +++ b/sourcepawn/jit/AMBuilder	Wed Aug 14 23:54:25 2013 -0700
    11.3 @@ -12,6 +12,10 @@
    11.4  compiler['CXXINCLUDES'].append(os.path.join(base, 'public', 'jit', 'x86'))
    11.5  compiler['CXXINCLUDES'].append(os.path.join(base, 'knight', 'shared'))
    11.6  
    11.7 +if AMBuild.target['platform'] == 'linux':
    11.8 +	compiler['POSTLINKFLAGS'].append('-lpthread')
    11.9 +	compiler['POSTLINKFLAGS'].append('-lrt')
   11.10 +
   11.11  extension = AMBuild.AddJob('sourcepawn.jit.x86')
   11.12  binary = Cpp.LibraryBuilder('sourcepawn.jit.x86', AMBuild, extension, compiler)
   11.13  binary.AddSourceFiles('sourcepawn/jit', [
   11.14 @@ -23,6 +27,7 @@
   11.15  	'sp_vm_engine.cpp',
   11.16  	'sp_vm_function.cpp',
   11.17  	'opcodes.cpp',
   11.18 +	'watchdog_timer.cpp',
   11.19  	'x86/jit_x86.cpp',
   11.20  	'zlib/adler32.c',
   11.21  	'zlib/compress.c',
    12.1 --- a/sourcepawn/jit/BaseRuntime.cpp	Wed Aug 14 17:18:02 2013 -0400
    12.2 +++ b/sourcepawn/jit/BaseRuntime.cpp	Wed Aug 14 23:54:25 2013 -0700
    12.3 @@ -37,10 +37,21 @@
    12.4    
    12.5    memset(m_CodeHash, 0, sizeof(m_CodeHash));
    12.6    memset(m_DataHash, 0, sizeof(m_DataHash));
    12.7 +
    12.8 +  ke::AutoLock lock(g_Jit.Mutex());
    12.9 +  g_Jit.RegisterRuntime(this);
   12.10  }
   12.11  
   12.12  BaseRuntime::~BaseRuntime()
   12.13  {
   12.14 +  // The watchdog thread takes the global JIT lock while it patches all
   12.15 +  // runtimes. It is not enough to ensure that the unlinking of the runtime is
   12.16 +  // protected; we cannot delete functions or code while the watchdog might be
   12.17 +  // executing. Therefore, the entire destructor is guarded.
   12.18 +  ke::AutoLock lock(g_Jit.Mutex());
   12.19 +
   12.20 +  g_Jit.DeregisterRuntime(this);
   12.21 +
   12.22    for (uint32_t i = 0; i < m_plugin.num_publics; i++)
   12.23      delete m_PubFuncs[i];
   12.24    delete [] m_PubFuncs;
    13.1 --- a/sourcepawn/jit/BaseRuntime.h	Wed Aug 14 17:18:02 2013 -0400
    13.2 +++ b/sourcepawn/jit/BaseRuntime.h	Wed Aug 14 23:54:25 2013 -0700
    13.3 @@ -4,6 +4,7 @@
    13.4  
    13.5  #include <sp_vm_api.h>
    13.6  #include <ke_vector.h>
    13.7 +#include <ke_inline_list.h>
    13.8  #include "jit_shared.h"
    13.9  #include "sp_vm_function.h"
   13.10  
   13.11 @@ -33,7 +34,9 @@
   13.12  };
   13.13  
   13.14  /* Jit wants fast access to this so we expose things as public */
   13.15 -class BaseRuntime : public SourcePawn::IPluginRuntime
   13.16 +class BaseRuntime
   13.17 +  : public SourcePawn::IPluginRuntime,
   13.18 +    public InlineListNode<BaseRuntime>
   13.19  {
   13.20   public:
   13.21    BaseRuntime();
   13.22 @@ -73,6 +76,13 @@
   13.23      return &m_plugin;
   13.24    }
   13.25  
   13.26 +  size_t NumJitFunctions() const {
   13.27 +    return m_JitFunctions.length();
   13.28 +  }
   13.29 +  JitFunction *GetJitFunction(size_t i) const {
   13.30 +    return m_JitFunctions[i];
   13.31 +  }
   13.32 +
   13.33   private:
   13.34    void SetupFloatNativeRemapping();
   13.35  
   13.36 @@ -103,3 +113,4 @@
   13.37  };
   13.38  
   13.39  #endif //_INCLUDE_SOURCEPAWN_JIT_RUNTIME_H_
   13.40 +
    14.1 --- a/sourcepawn/jit/Makefile.shell	Wed Aug 14 17:18:02 2013 -0400
    14.2 +++ b/sourcepawn/jit/Makefile.shell	Wed Aug 14 23:54:25 2013 -0700
    14.3 @@ -19,6 +19,7 @@
    14.4  	BaseRuntime.cpp		\
    14.5  	jit_function.cpp	\
    14.6  	opcodes.cpp		\
    14.7 +	watchdog_timer.cpp \
    14.8  	md5/md5.cpp			\
    14.9  	zlib/adler32.c		\
   14.10  	zlib/compress.c		\
   14.11 @@ -47,7 +48,7 @@
   14.12  CXX = c++
   14.13  CC = cc
   14.14  
   14.15 -LINK = -m32 -lm
   14.16 +LINK = -m32 -lm -lpthread -lrt
   14.17  
   14.18  INCLUDE = -I. -I.. -I$(SMSDK)/public -I$(SMSDK)/public/jit -I$(SMSDK)/public/jit/x86 \
   14.19  	-I$(SMSDK)/public/sourcepawn -I$(MMSOURCE17)/core/sourcehook -I$(SMSDK)/knight/shared -Ix86
    15.1 --- a/sourcepawn/jit/dll_exports.cpp	Wed Aug 14 17:18:02 2013 -0400
    15.2 +++ b/sourcepawn/jit/dll_exports.cpp	Wed Aug 14 23:54:25 2013 -0700
    15.3 @@ -100,7 +100,7 @@
    15.4  		while (error->GetTraceInfo(&stk_info))
    15.5  		{
    15.6  			fprintf(stderr,
    15.7 -			    "   [%d]  Line %d, %s::%s()",
    15.8 +			    "   [%d]  Line %d, %s::%s()\n",
    15.9  				i++,
   15.10  				stk_info.line,
   15.11  				stk_info.filename,
   15.12 @@ -221,6 +221,7 @@
   15.13  
   15.14  	ShellDebugListener debug;
   15.15  	g_engine1.SetDebugListener(&debug);
   15.16 +	g_engine2.InstallWatchdogTimer(5000);
   15.17  
   15.18  	int errcode = Execute(argv[1]);
   15.19  
    16.1 --- a/sourcepawn/jit/engine2.cpp	Wed Aug 14 17:18:02 2013 -0400
    16.2 +++ b/sourcepawn/jit/engine2.cpp	Wed Aug 14 23:54:25 2013 -0700
    16.3 @@ -1,3 +1,4 @@
    16.4 +// vim: set ts=4 sw=4 tw=99 noet:
    16.5  #include <sourcemod_version.h>
    16.6  #include <stdio.h>
    16.7  #include <stdlib.h>
    16.8 @@ -7,6 +8,7 @@
    16.9  #include "zlib/zlib.h"
   16.10  #include "BaseRuntime.h"
   16.11  #include "sp_vm_engine.h"
   16.12 +#include "watchdog_timer.h"
   16.13  
   16.14  using namespace SourcePawn;
   16.15  
   16.16 @@ -210,3 +212,9 @@
   16.17  	
   16.18  	return rt;
   16.19  }
   16.20 +
   16.21 +bool SourcePawnEngine2::InstallWatchdogTimer(size_t timeout_ms)
   16.22 +{
   16.23 +	return g_WatchdogTimer.Initialize(timeout_ms);
   16.24 +}
   16.25 +
    17.1 --- a/sourcepawn/jit/engine2.h	Wed Aug 14 17:18:02 2013 -0400
    17.2 +++ b/sourcepawn/jit/engine2.h	Wed Aug 14 23:54:25 2013 -0700
    17.3 @@ -26,6 +26,7 @@
    17.4  		bool Initialize();
    17.5  		void Shutdown();
    17.6  		IPluginRuntime *CreateEmptyRuntime(const char *name, uint32_t memory);
    17.7 +		bool InstallWatchdogTimer(size_t timeout_ms);
    17.8  	public:
    17.9  		IProfiler *GetProfiler();
   17.10  	private:
    18.1 --- a/sourcepawn/jit/jit_function.cpp	Wed Aug 14 17:18:02 2013 -0400
    18.2 +++ b/sourcepawn/jit/jit_function.cpp	Wed Aug 14 23:54:25 2013 -0700
    18.3 @@ -1,23 +1,31 @@
    18.4 +// vim: set ts=8 ts=2 sw=2 tw=99 et:
    18.5  #include "jit_function.h"
    18.6  #include "sp_vm_engine.h"
    18.7  #include "jit_x86.h"
    18.8  
    18.9 -JitFunction::JitFunction(void *entry_addr, cell_t pcode_offs)
   18.10 -: m_pEntryAddr(entry_addr), m_PcodeOffs(pcode_offs)
   18.11 +JitFunction::JitFunction(void *entry_addr, cell_t pcode_offs, LoopEdge *edges, uint32_t nedges)
   18.12 +  : m_pEntryAddr(entry_addr),
   18.13 +    m_PcodeOffs(pcode_offs),
   18.14 +    edges_(edges),
   18.15 +    nedges_(nedges)
   18.16  {
   18.17  }
   18.18  
   18.19  JitFunction::~JitFunction()
   18.20  {
   18.21 -	g_Jit.FreeCode(m_pEntryAddr);
   18.22 +  delete [] edges_;
   18.23 +  g_Jit.FreeCode(m_pEntryAddr);
   18.24  }
   18.25  
   18.26 -void *JitFunction::GetEntryAddress()
   18.27 +void *
   18.28 +JitFunction::GetEntryAddress() const
   18.29  {
   18.30 -	return m_pEntryAddr;
   18.31 +  return m_pEntryAddr;
   18.32  }
   18.33  
   18.34 -cell_t JitFunction::GetPCodeAddress()
   18.35 +cell_t
   18.36 +JitFunction::GetPCodeAddress() const
   18.37  {
   18.38 -	return m_PcodeOffs;
   18.39 +  return m_PcodeOffs;
   18.40  }
   18.41 +
    19.1 --- a/sourcepawn/jit/jit_function.h	Wed Aug 14 17:18:02 2013 -0400
    19.2 +++ b/sourcepawn/jit/jit_function.h	Wed Aug 14 23:54:25 2013 -0700
    19.3 @@ -1,19 +1,38 @@
    19.4 +// vim: set ts=8 sts=2 sw=2 tw=99 et:
    19.5  #ifndef _INCLUDE_SOURCEPAWN_JIT2_FUNCTION_H_
    19.6  #define _INCLUDE_SOURCEPAWN_JIT2_FUNCTION_H_
    19.7  
    19.8  #include <sp_vm_types.h>
    19.9 +#include <ke_vector.h>
   19.10 +
   19.11 +struct LoopEdge
   19.12 +{
   19.13 +  uint32_t offset;
   19.14 +  int32_t disp32;
   19.15 +};
   19.16  
   19.17  class JitFunction
   19.18  {
   19.19 -public:
   19.20 -	JitFunction(void *entry_addr, cell_t pcode_offs);
   19.21 -	~JitFunction();
   19.22 -public:
   19.23 -	void *GetEntryAddress();
   19.24 -	cell_t GetPCodeAddress();
   19.25 -private:
   19.26 -	void *m_pEntryAddr;
   19.27 -	cell_t m_PcodeOffs;
   19.28 + public:
   19.29 +  JitFunction(void *entry_addr, cell_t pcode_offs, LoopEdge *edges, uint32_t nedges);
   19.30 +  ~JitFunction();
   19.31 +
   19.32 + public:
   19.33 +  void *GetEntryAddress() const;
   19.34 +  cell_t GetPCodeAddress() const;
   19.35 +  uint32_t NumLoopEdges() const {
   19.36 +    return nedges_;
   19.37 +  }
   19.38 +  const LoopEdge &GetLoopEdge(size_t i) const {
   19.39 +    assert(i < nedges_);
   19.40 +    return edges_[i];
   19.41 +  }
   19.42 +
   19.43 + private:
   19.44 +  void *m_pEntryAddr;
   19.45 +  cell_t m_PcodeOffs;
   19.46 +  LoopEdge *edges_;
   19.47 +  uint32_t nedges_;
   19.48  };
   19.49  
   19.50  #endif //_INCLUDE_SOURCEPAWN_JIT2_FUNCTION_H_
    20.1 --- a/sourcepawn/jit/sp_vm_basecontext.cpp	Wed Aug 14 17:18:02 2013 -0400
    20.2 +++ b/sourcepawn/jit/sp_vm_basecontext.cpp	Wed Aug 14 23:54:25 2013 -0700
    20.3 @@ -36,6 +36,7 @@
    20.4  #include "sp_vm_api.h"
    20.5  #include "sp_vm_basecontext.h"
    20.6  #include "sp_vm_engine.h"
    20.7 +#include "watchdog_timer.h"
    20.8  #include "x86/jit_x86.h"
    20.9  
   20.10  using namespace SourcePawn;
   20.11 @@ -554,6 +555,9 @@
   20.12  
   20.13  	fnid = function->GetFunctionID();
   20.14  
   20.15 +	if (!g_WatchdogTimer.HandleInterrupt())
   20.16 +		return SP_ERROR_TIMEOUT;
   20.17 +
   20.18  	if (fnid & 1)
   20.19  	{	
   20.20  		public_id = fnid >> 1;
   20.21 @@ -674,6 +678,9 @@
   20.22  		}
   20.23  	}
   20.24  
   20.25 +	if (ir == SP_ERROR_TIMEOUT)
   20.26 +		g_WatchdogTimer.NotifyTimeoutReceived();
   20.27 +
   20.28  	if (ir != SP_ERROR_NONE)
   20.29  	{
   20.30  		g_engine1.ReportError(m_pRuntime, ir, m_MsgCache, save_rp);
    21.1 --- a/sourcepawn/jit/sp_vm_engine.cpp	Wed Aug 14 17:18:02 2013 -0400
    21.2 +++ b/sourcepawn/jit/sp_vm_engine.cpp	Wed Aug 14 23:54:25 2013 -0700
    21.3 @@ -1,5 +1,5 @@
    21.4  /**
    21.5 - * vim: set ts=4 :
    21.6 + * vim: set ts=4 sw=4 tw=99 et:
    21.7   * =============================================================================
    21.8   * SourcePawn
    21.9   * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
   21.10 @@ -58,7 +58,7 @@
   21.11  
   21.12  using namespace SourcePawn;
   21.13  
   21.14 -#define ERROR_MESSAGE_MAX		29
   21.15 +#define ERROR_MESSAGE_MAX		30
   21.16  static const char *g_ErrorMsgTable[] = 
   21.17  {
   21.18  	NULL,
   21.19 @@ -90,7 +90,8 @@
   21.20  	"Plugin format is too old",
   21.21  	"Plugin format is too new",
   21.22      "Out of memory",
   21.23 -    "Integer overflow"
   21.24 +    "Integer overflow",
   21.25 +    "Script execution timed out"
   21.26  };
   21.27  
   21.28  const char *SourcePawnEngine::GetErrorString(int error)
    22.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    22.2 +++ b/sourcepawn/jit/watchdog_timer.cpp	Wed Aug 14 23:54:25 2013 -0700
    22.3 @@ -0,0 +1,164 @@
    22.4 +// vim: set ts=8 sts=2 sw=2 tw=99 et:
    22.5 +//
    22.6 +// This file is part of SourcePawn.
    22.7 +// 
    22.8 +// SourcePawn is free software: you can redistribute it and/or modify
    22.9 +// it under the terms of the GNU General Public License as published by
   22.10 +// the Free Software Foundation, either version 3 of the License, or
   22.11 +// (at your option) any later version.
   22.12 +// 
   22.13 +// SourcePawn is distributed in the hope that it will be useful,
   22.14 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
   22.15 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   22.16 +// GNU General Public License for more details.
   22.17 +// 
   22.18 +// You should have received a copy of the GNU General Public License
   22.19 +// along with SourcePawn.  If not, see <http://www.gnu.org/licenses/>.
   22.20 +#include "watchdog_timer.h"
   22.21 +#include "x86/jit_x86.h"
   22.22 +#include <string.h>
   22.23 +
   22.24 +WatchdogTimer g_WatchdogTimer;
   22.25 +
   22.26 +WatchdogTimer::WatchdogTimer()
   22.27 +  : terminate_(false),
   22.28 +    mainthread_(ke::GetCurrentThreadId()),
   22.29 +    last_frame_id_(0),
   22.30 +    second_timeout_(false),
   22.31 +    timedout_(false)
   22.32 +{
   22.33 +}
   22.34 +
   22.35 +WatchdogTimer::~WatchdogTimer()
   22.36 +{
   22.37 +  Shutdown();
   22.38 +}
   22.39 +
   22.40 +bool
   22.41 +WatchdogTimer::Initialize(size_t timeout_ms)
   22.42 +{
   22.43 +  if (thread_)
   22.44 +    return false;
   22.45 +
   22.46 +  timeout_ms_ = timeout_ms;
   22.47 +
   22.48 +  thread_ = new ke::Thread(this, "SM Watchdog");
   22.49 +  if (!thread_->Succeeded())
   22.50 +    return false;
   22.51 +
   22.52 +  return true;
   22.53 +}
   22.54 +
   22.55 +void
   22.56 +WatchdogTimer::Shutdown()
   22.57 +{
   22.58 +  if (terminate_ || !thread_)
   22.59 +    return;
   22.60 +
   22.61 +  {
   22.62 +    ke::AutoLock lock(&cv_);
   22.63 +    terminate_ = true;
   22.64 +    cv_.Notify();
   22.65 +  }
   22.66 +  thread_->Join();
   22.67 +  thread_ = NULL;
   22.68 +}
   22.69 +
   22.70 +void
   22.71 +WatchdogTimer::Run()
   22.72 +{
   22.73 +  ke::AutoLock lock(&cv_);
   22.74 +
   22.75 +  // Initialize the frame id, so we don't have to wait longer on startup.
   22.76 +  last_frame_id_ = g_Jit.FrameId();
   22.77 +
   22.78 +  while (!terminate_) {
   22.79 +    ke::WaitResult rv = cv_.Wait(timeout_ms_ / 2);
   22.80 +    if (terminate_)
   22.81 +      return;
   22.82 +
   22.83 +    if (rv == ke::Wait_Error)
   22.84 +      return;
   22.85 +
   22.86 +    if (rv != ke::Wait_Timeout)
   22.87 +      continue;
   22.88 +
   22.89 +    // We reached a timeout. If the current frame is not equal to the last
   22.90 +    // frame, then we assume the server is still moving enough to process
   22.91 +    // frames. We also make sure JIT code is actually on the stack, since
   22.92 +    // our concept of frames won't move if JIT code is not running.
   22.93 +    //
   22.94 +    // Note that it's okay if these two race: it's just a heuristic, and
   22.95 +    // worst case, we'll miss something that might have timed out but
   22.96 +    // ended up resuming.
   22.97 +    uintptr_t frame_id = g_Jit.FrameId();
   22.98 +    if (frame_id != last_frame_id_ || !g_Jit.RunningCode()) {
   22.99 +      last_frame_id_ = frame_id;
  22.100 +      second_timeout_ = false;
  22.101 +      continue;
  22.102 +    }
  22.103 +
  22.104 +    // We woke up with the same frame. We might be timing out. Wait another
  22.105 +    // time to see if so.
  22.106 +    if (!second_timeout_) {
  22.107 +      second_timeout_ = true;
  22.108 +      continue;
  22.109 +    }
  22.110 +
  22.111 +    {
  22.112 +      // Prevent the JIT from linking or destroying runtimes and functions.
  22.113 +      ke::AutoLock lock(g_Jit.Mutex());
  22.114 +
  22.115 +      // Set the timeout notification bit. If this is detected before any patched
  22.116 +      // JIT backedges are reached, the main thread will attempt to acquire the
  22.117 +      // monitor lock, and block until we call Wait().
  22.118 +      timedout_ = true;
  22.119 +    
  22.120 +      // Patch all jumps. This can race with the main thread's execution since
  22.121 +      // all code writes are 32-bit integer instruction operands, which are
  22.122 +      // guaranteed to be atomic on x86.
  22.123 +      g_Jit.PatchAllJumpsForTimeout();
  22.124 +    }
  22.125 +
  22.126 +    // The JIT will be free to compile new functions while we wait, but it will
  22.127 +    // see the timeout bit set above and immediately bail out.
  22.128 +    cv_.Wait();
  22.129 +
  22.130 +    second_timeout_ = false;
  22.131 +
  22.132 +    // Reset the last frame ID to something unlikely to be chosen again soon.
  22.133 +    last_frame_id_--;
  22.134 +
  22.135 +    // It's possible that Shutdown() raced with the timeout, and if so, we
  22.136 +    // must leave now to prevent a deadlock.
  22.137 +    if (terminate_)
  22.138 +      return;
  22.139 +  }
  22.140 +}
  22.141 +
  22.142 +bool
  22.143 +WatchdogTimer::NotifyTimeoutReceived()
  22.144 +{
  22.145 +  // We are guaranteed that the watchdog thread is waiting for our
  22.146 +  // notification, and is therefore blocked. We take the JIT lock
  22.147 +  // anyway for sanity.
  22.148 +  {
  22.149 +    ke::AutoLock lock(g_Jit.Mutex());
  22.150 +    g_Jit.UnpatchAllJumpsFromTimeout();
  22.151 +  }
  22.152 +
  22.153 +  timedout_ = false;
  22.154 +
  22.155 +  // Wake up the watchdog thread, it's okay to keep processing now.
  22.156 +  ke::AutoLock lock(&cv_);
  22.157 +  cv_.Notify();
  22.158 +  return false;
  22.159 +}
  22.160 +
  22.161 +bool
  22.162 +WatchdogTimer::HandleInterrupt()
  22.163 +{
  22.164 +  if (timedout_)
  22.165 +    return NotifyTimeoutReceived();
  22.166 +  return true;
  22.167 +}
    23.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    23.2 +++ b/sourcepawn/jit/watchdog_timer.h	Wed Aug 14 23:54:25 2013 -0700
    23.3 @@ -0,0 +1,61 @@
    23.4 +// vim: set ts=8 sts=2 sw=2 tw=99 et:
    23.5 +//
    23.6 +// This file is part of SourcePawn.
    23.7 +// 
    23.8 +// SourcePawn is free software: you can redistribute it and/or modify
    23.9 +// it under the terms of the GNU General Public License as published by
   23.10 +// the Free Software Foundation, either version 3 of the License, or
   23.11 +// (at your option) any later version.
   23.12 +// 
   23.13 +// SourcePawn is distributed in the hope that it will be useful,
   23.14 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
   23.15 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   23.16 +// GNU General Public License for more details.
   23.17 +// 
   23.18 +// You should have received a copy of the GNU General Public License
   23.19 +// along with SourcePawn.  If not, see <http://www.gnu.org/licenses/>.
   23.20 +#ifndef _include_sourcepawn_watchdog_timer_posix_h_
   23.21 +#define _include_sourcepawn_watchdog_timer_posix_h_
   23.22 +
   23.23 +#include <stddef.h>
   23.24 +#include <stdint.h>
   23.25 +#include <ke_thread_utils.h>
   23.26 +
   23.27 +typedef bool (*WatchdogCallback)();
   23.28 +
   23.29 +class WatchdogTimer : public ke::IRunnable
   23.30 +{
   23.31 + public:
   23.32 +  WatchdogTimer();
   23.33 +  ~WatchdogTimer();
   23.34 +
   23.35 +  bool Initialize(size_t timeout_ms);
   23.36 +  void Shutdown();
   23.37 +
   23.38 +  // Called from main thread.
   23.39 +  bool NotifyTimeoutReceived();
   23.40 +  bool HandleInterrupt();
   23.41 +
   23.42 + private:
   23.43 +  // Watchdog thread.
   23.44 +  void Run();
   23.45 +
   23.46 + private:
   23.47 +  bool terminate_;
   23.48 +  size_t timeout_ms_;
   23.49 +  ke::ThreadId mainthread_;
   23.50 +
   23.51 +  ke::AutoPtr<ke::Thread> thread_;
   23.52 +  ke::ConditionVariable cv_;
   23.53 +
   23.54 +  // Accessed only on the watchdog thread.
   23.55 +  uintptr_t last_frame_id_;
   23.56 +  bool second_timeout_;
   23.57 +
   23.58 +  // Accessed only on the main thread.
   23.59 +  bool timedout_;
   23.60 +};
   23.61 +
   23.62 +extern WatchdogTimer g_WatchdogTimer;
   23.63 +
   23.64 +#endif // _include_sourcepawn_watchdog_timer_posix_h_
    24.1 --- a/sourcepawn/jit/x86/jit_x86.cpp	Wed Aug 14 17:18:02 2013 -0400
    24.2 +++ b/sourcepawn/jit/x86/jit_x86.cpp	Wed Aug 14 23:54:25 2013 -0700
    24.3 @@ -37,6 +37,7 @@
    24.4  #include "../engine2.h"
    24.5  #include "../BaseRuntime.h"
    24.6  #include "../sp_vm_basecontext.h"
    24.7 +#include "watchdog_timer.h"
    24.8  
    24.9  using namespace Knight;
   24.10  
   24.11 @@ -50,7 +51,7 @@
   24.12  KeCodeCache *g_pCodeCache = NULL;
   24.13  ISourcePawnEngine *engine = &g_engine1;
   24.14  
   24.15 -static inline void *
   24.16 +static inline uint8_t *
   24.17  LinkCode(AssemblerX86 &masm)
   24.18  {
   24.19    if (masm.outOfMemory())
   24.20 @@ -61,7 +62,7 @@
   24.21      return NULL;
   24.22  
   24.23    masm.emitToExecutableMemory(code);
   24.24 -  return code;
   24.25 +  return reinterpret_cast<uint8_t *>(code);
   24.26  }
   24.27  
   24.28  static inline ConditionCode
   24.29 @@ -249,7 +250,7 @@
   24.30      return err;
   24.31  
   24.32    cell_t *base = reinterpret_cast<cell_t *>(rt->plugin()->memory + ctx->hp);
   24.33 -  cell_t offs = GenerateArrayIndirectionVectors(base, argv, argc, autozero);
   24.34 +  cell_t offs = GenerateArrayIndirectionVectors(base, argv, argc, !!autozero);
   24.35    assert(size_t(offs) == cells);
   24.36  
   24.37    argv[argc - 1] = ctx->hp;
   24.38 @@ -314,6 +315,12 @@
   24.39  static int
   24.40  CompileFromThunk(BaseRuntime *runtime, cell_t pcode_offs, void **addrp, char *pc)
   24.41  {
   24.42 +  // If the watchdog timer has declared a timeout, we must process it now,
   24.43 +  // and possibly refuse to compile, since otherwise we will compile a
   24.44 +  // function that is not patched for timeouts.
   24.45 +  if (!g_WatchdogTimer.HandleInterrupt())
   24.46 +    return SP_ERROR_TIMEOUT;
   24.47 +
   24.48    JitFunction *fn = runtime->GetJittedFunctionByOffset(pcode_offs);
   24.49    if (!fn) {
   24.50      int err;
   24.51 @@ -452,13 +459,19 @@
   24.52    emitCallThunks();
   24.53    emitErrorPaths();
   24.54  
   24.55 -  void *code = LinkCode(masm);
   24.56 +  uint8_t *code = LinkCode(masm);
   24.57    if (!code) {
   24.58      *errp = SP_ERROR_OUT_OF_MEMORY;
   24.59      return NULL;
   24.60    }
   24.61  
   24.62 -  return new JitFunction(code, pcode_start_);
   24.63 +  LoopEdge *edges = new LoopEdge[backward_jumps_.length()];
   24.64 +  for (size_t i = 0; i < backward_jumps_.length(); i++) {
   24.65 +    edges[i].offset = backward_jumps_[i];
   24.66 +    edges[i].disp32 = *reinterpret_cast<int32_t *>(code + edges[i].offset - 4);
   24.67 +  }
   24.68 +
   24.69 +  return new JitFunction(code, pcode_start_, edges, backward_jumps_.length());
   24.70  }
   24.71  
   24.72  bool
   24.73 @@ -1228,7 +1241,12 @@
   24.74        Label *target = labelAt(readCell());
   24.75        if (!target)
   24.76          return false;
   24.77 -      __ jmp(target);
   24.78 +      if (target->bound()) {
   24.79 +        __ jmp32(target);
   24.80 +        backward_jumps_.append(masm.pc());
   24.81 +      } else {
   24.82 +        __ jmp(target);
   24.83 +      }
   24.84        break;
   24.85      }
   24.86  
   24.87 @@ -1240,7 +1258,12 @@
   24.88        if (!target)
   24.89          return false;
   24.90        __ testl(pri, pri);
   24.91 -      __ j(cc, target);
   24.92 +      if (target->bound()) {
   24.93 +        __ j32(cc, target);
   24.94 +        backward_jumps_.append(masm.pc());
   24.95 +      } else {
   24.96 +        __ j(cc, target);
   24.97 +      }
   24.98        break;
   24.99      }
  24.100  
  24.101 @@ -1256,7 +1279,12 @@
  24.102          return false;
  24.103        ConditionCode cc = OpToCondition(op);
  24.104        __ cmpl(pri, alt);
  24.105 -      __ j(cc, target);
  24.106 +      if (target->bound()) {
  24.107 +        __ j32(cc, target);
  24.108 +        backward_jumps_.append(masm.pc());
  24.109 +      } else {
  24.110 +        __ j(cc, target);
  24.111 +      }
  24.112        break;
  24.113      }
  24.114  
  24.115 @@ -1755,7 +1783,7 @@
  24.116  typedef int (*JIT_EXECUTE)(sp_context_t *ctx, uint8_t *memory, void *code);
  24.117  
  24.118  static void *
  24.119 -GenerateEntry(void **retp)
  24.120 +GenerateEntry(void **retp, void **timeoutp)
  24.121  {
  24.122    AssemblerX86 masm;
  24.123  
  24.124 @@ -1813,11 +1841,17 @@
  24.125    __ movl(ecx, Operand(ebp, 8 + 4 * 0)); // ret-path expects ecx = ctx
  24.126    __ jmp(&ret);
  24.127  
  24.128 +  Label timeout;
  24.129 +  __ bind(&timeout);
  24.130 +  __ movl(eax, SP_ERROR_TIMEOUT);
  24.131 +  __ jmp(&error);
  24.132 +
  24.133    void *code = LinkCode(masm);
  24.134    if (!code)
  24.135      return NULL;
  24.136  
  24.137    *retp = reinterpret_cast<uint8_t *>(code) + error.offset();
  24.138 +  *timeoutp = reinterpret_cast<uint8_t *>(code) + timeout.offset();
  24.139    return code;
  24.140  }
  24.141  
  24.142 @@ -1841,11 +1875,12 @@
  24.143    m_pJitEntry = NULL;
  24.144  }
  24.145  
  24.146 -bool JITX86::InitializeJIT()
  24.147 +bool
  24.148 +JITX86::InitializeJIT()
  24.149  {
  24.150    g_pCodeCache = KE_CreateCodeCache();
  24.151  
  24.152 -  m_pJitEntry = GenerateEntry(&m_pJitReturn);
  24.153 +  m_pJitEntry = GenerateEntry(&m_pJitReturn, &m_pJitTimeout);
  24.154    if (!m_pJitEntry)
  24.155      return false;
  24.156  
  24.157 @@ -1873,6 +1908,11 @@
  24.158    JitFunction *fun = cc.emit(err);
  24.159    if (!fun)
  24.160      return NULL;
  24.161 +
  24.162 +  // Grab the lock before linking code in, since the watchdog timer will look
  24.163 +  // at this list on another thread.
  24.164 +  ke::AutoLock lock(g_Jit.Mutex());
  24.165 +
  24.166    prt->AddJittedFunction(fun);
  24.167    return fun;
  24.168  }
  24.169 @@ -1919,33 +1959,39 @@
  24.170    return (SPVM_NATIVE_FUNC)LinkCode(masm);
  24.171  }
  24.172  
  24.173 -void JITX86::DestroyFakeNative(SPVM_NATIVE_FUNC func)
  24.174 +void
  24.175 +JITX86::DestroyFakeNative(SPVM_NATIVE_FUNC func)
  24.176  {
  24.177    KE_FreeCode(g_pCodeCache, (void *)func);
  24.178  }
  24.179  
  24.180 -ICompilation *JITX86::StartCompilation()
  24.181 +ICompilation *
  24.182 +JITX86::StartCompilation()
  24.183  {
  24.184    return new CompData;
  24.185  }
  24.186  
  24.187 -ICompilation *JITX86::StartCompilation(BaseRuntime *runtime)
  24.188 +ICompilation *
  24.189 +JITX86::StartCompilation(BaseRuntime *runtime)
  24.190  {
  24.191    return new CompData;
  24.192  }
  24.193  
  24.194 -void CompData::Abort()
  24.195 +void
  24.196 +CompData::Abort()
  24.197  {
  24.198    delete this;
  24.199  }
  24.200  
  24.201 -void JITX86::FreeContextVars(sp_context_t *ctx)
  24.202 +void
  24.203 +JITX86::FreeContextVars(sp_context_t *ctx)
  24.204  {
  24.205    free(((tracker_t *)(ctx->vm[JITVARS_TRACKER]))->pBase);
  24.206    delete (tracker_t *)ctx->vm[JITVARS_TRACKER];
  24.207  }
  24.208  
  24.209 -bool CompData::SetOption(const char *key, const char *val)
  24.210 +bool
  24.211 +CompData::SetOption(const char *key, const char *val)
  24.212  {
  24.213    if (strcmp(key, SP_JITCONF_DEBUG) == 0)
  24.214      return true;
  24.215 @@ -1962,7 +2008,8 @@
  24.216    return false;
  24.217  }
  24.218  
  24.219 -int JITX86::InvokeFunction(BaseRuntime *runtime, JitFunction *fn, cell_t *result)
  24.220 +int
  24.221 +JITX86::InvokeFunction(BaseRuntime *runtime, JitFunction *fn, cell_t *result)
  24.222  {
  24.223    sp_context_t *ctx = runtime->GetBaseContext()->GetCtx();
  24.224  
  24.225 @@ -1970,18 +2017,75 @@
  24.226    ctx->cip = fn->GetPCodeAddress();
  24.227  
  24.228    JIT_EXECUTE pfn = (JIT_EXECUTE)m_pJitEntry;
  24.229 +
  24.230 +  if (level_++ == 0)
  24.231 +    frame_id_++;
  24.232    int err = pfn(ctx, runtime->plugin()->memory, fn->GetEntryAddress());
  24.233 +  level_--;
  24.234  
  24.235    *result = ctx->rval;
  24.236    return err;
  24.237  }
  24.238  
  24.239 -void *JITX86::AllocCode(size_t size)
  24.240 +void *
  24.241 +JITX86::AllocCode(size_t size)
  24.242  {
  24.243    return Knight::KE_AllocCode(g_pCodeCache, size);
  24.244  }
  24.245  
  24.246 -void JITX86::FreeCode(void *code)
  24.247 +void
  24.248 +JITX86::FreeCode(void *code)
  24.249  {
  24.250    KE_FreeCode(g_pCodeCache, code);
  24.251  }
  24.252 +
  24.253 +void
  24.254 +JITX86::RegisterRuntime(BaseRuntime *rt)
  24.255 +{
  24.256 +  mutex_.AssertCurrentThreadOwns();
  24.257 +  runtimes_.insert(rt);
  24.258 +}
  24.259 +
  24.260 +void
  24.261 +JITX86::DeregisterRuntime(BaseRuntime *rt)
  24.262 +{
  24.263 +  mutex_.AssertCurrentThreadOwns();
  24.264 +  runtimes_.erase(rt);
  24.265 +}
  24.266 +
  24.267 +void
  24.268 +JITX86::PatchAllJumpsForTimeout()
  24.269 +{
  24.270 +  mutex_.AssertCurrentThreadOwns();
  24.271 +  for (InlineList<BaseRuntime>::iterator iter = runtimes_.begin(); iter != runtimes_.end(); iter++) {
  24.272 +    BaseRuntime *rt = *iter;
  24.273 +    for (size_t i = 0; i < rt->NumJitFunctions(); i++) {
  24.274 +      JitFunction *fun = rt->GetJitFunction(i);
  24.275 +      uint8_t *base = reinterpret_cast<uint8_t *>(fun->GetEntryAddress());
  24.276 +
  24.277 +      for (size_t j = 0; j < fun->NumLoopEdges(); j++) {
  24.278 +        const LoopEdge &e = fun->GetLoopEdge(j);
  24.279 +        int32_t diff = intptr_t(m_pJitTimeout) - intptr_t(base + e.offset);
  24.280 +        *reinterpret_cast<int32_t *>(base + e.offset - 4) = diff;
  24.281 +      }
  24.282 +    }
  24.283 +  }
  24.284 +}
  24.285 +
  24.286 +void
  24.287 +JITX86::UnpatchAllJumpsFromTimeout()
  24.288 +{
  24.289 +  mutex_.AssertCurrentThreadOwns();
  24.290 +  for (InlineList<BaseRuntime>::iterator iter = runtimes_.begin(); iter != runtimes_.end(); iter++) {
  24.291 +    BaseRuntime *rt = *iter;
  24.292 +    for (size_t i = 0; i < rt->NumJitFunctions(); i++) {
  24.293 +      JitFunction *fun = rt->GetJitFunction(i);
  24.294 +      uint8_t *base = reinterpret_cast<uint8_t *>(fun->GetEntryAddress());
  24.295 +
  24.296 +      for (size_t j = 0; j < fun->NumLoopEdges(); j++) {
  24.297 +        const LoopEdge &e = fun->GetLoopEdge(j);
  24.298 +        *reinterpret_cast<int32_t *>(base + e.offset - 4) = e.disp32;
  24.299 +      }
  24.300 +    }
  24.301 +  }
  24.302 +}
    25.1 --- a/sourcepawn/jit/x86/jit_x86.h	Wed Aug 14 17:18:02 2013 -0400
    25.2 +++ b/sourcepawn/jit/x86/jit_x86.h	Wed Aug 14 23:54:25 2013 -0700
    25.3 @@ -1,34 +1,19 @@
    25.4 -/**
    25.5 - * vim: set ts=4 sw=4 tw=99 et:
    25.6 - * =============================================================================
    25.7 - * SourceMod
    25.8 - * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
    25.9 - * =============================================================================
   25.10 - *
   25.11 - * This program is free software; you can redistribute it and/or modify it under
   25.12 - * the terms of the GNU General Public License, version 3.0, as published by the
   25.13 - * Free Software Foundation.
   25.14 - * 
   25.15 - * This program is distributed in the hope that it will be useful, but WITHOUT
   25.16 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   25.17 - * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
   25.18 - * details.
   25.19 - *
   25.20 - * You should have received a copy of the GNU General Public License along with
   25.21 - * this program.  If not, see <http://www.gnu.org/licenses/>.
   25.22 - *
   25.23 - * As a special exception, AlliedModders LLC gives you permission to link the
   25.24 - * code of this program (as well as its derivative works) to "Half-Life 2," the
   25.25 - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
   25.26 - * by the Valve Corporation.  You must obey the GNU General Public License in
   25.27 - * all respects for all other code used.  Additionally, AlliedModders LLC grants
   25.28 - * this exception to all derivative works.  AlliedModders LLC defines further
   25.29 - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
   25.30 - * or <http://www.sourcemod.net/license.php>.
   25.31 - *
   25.32 - * Version: $Id$
   25.33 - */
   25.34 -
   25.35 +// vim: set ts=8 sts=2 sw=2 tw=99 et:
   25.36 +//
   25.37 +// This file is part of SourcePawn.
   25.38 +// 
   25.39 +// SourcePawn is free software: you can redistribute it and/or modify
   25.40 +// it under the terms of the GNU General Public License as published by
   25.41 +// the Free Software Foundation, either version 3 of the License, or
   25.42 +// (at your option) any later version.
   25.43 +// 
   25.44 +// SourcePawn is distributed in the hope that it will be useful,
   25.45 +// but WITHOUT ANY WARRANTY; without even the implied warranty of
   25.46 +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   25.47 +// GNU General Public License for more details.
   25.48 +// 
   25.49 +// You should have received a copy of the GNU General Public License
   25.50 +// along with SourcePawn.  If not, see <http://www.gnu.org/licenses/>.
   25.51  #ifndef _INCLUDE_SOURCEPAWN_JIT_X86_H_
   25.52  #define _INCLUDE_SOURCEPAWN_JIT_X86_H_
   25.53  
   25.54 @@ -42,6 +27,7 @@
   25.55  #include "sp_vm_basecontext.h"
   25.56  #include "jit_function.h"
   25.57  #include "opcodes.h"
   25.58 +#include <ke_thread_utils.h>
   25.59  
   25.60  using namespace SourcePawn;
   25.61  
   25.62 @@ -155,6 +141,7 @@
   25.63    cell_t *cip_;
   25.64    cell_t *code_end_;
   25.65    Label *jump_map_;
   25.66 +  ke::Vector<size_t> backward_jumps_;
   25.67  
   25.68    // Errors
   25.69    Label error_bounds_;
   25.70 @@ -188,16 +175,36 @@
   25.71    ICompilation *ApplyOptions(ICompilation *_IN, ICompilation *_OUT);
   25.72    int InvokeFunction(BaseRuntime *runtime, JitFunction *fn, cell_t *result);
   25.73  
   25.74 +  void RegisterRuntime(BaseRuntime *rt);
   25.75 +  void DeregisterRuntime(BaseRuntime *rt);
   25.76 +  void PatchAllJumpsForTimeout();
   25.77 +  void UnpatchAllJumpsFromTimeout();
   25.78 +  
   25.79   public:
   25.80    ExternalAddress GetUniversalReturn() {
   25.81 -      return ExternalAddress(m_pJitReturn);
   25.82 +    return ExternalAddress(m_pJitReturn);
   25.83    }
   25.84    void *AllocCode(size_t size);
   25.85    void FreeCode(void *code);
   25.86  
   25.87 +  uintptr_t FrameId() const {
   25.88 +    return frame_id_;
   25.89 +  }
   25.90 +  bool RunningCode() const {
   25.91 +    return level_ != 0;
   25.92 +  }
   25.93 +  ke::Mutex *Mutex() {
   25.94 +    return &mutex_;
   25.95 +  }
   25.96 +
   25.97   private:
   25.98    void *m_pJitEntry;         /* Entry function */
   25.99    void *m_pJitReturn;        /* Universal return address */
  25.100 +  void *m_pJitTimeout;       /* Universal timeout address */
  25.101 +  InlineList<BaseRuntime> runtimes_;
  25.102 +  uintptr_t frame_id_;
  25.103 +  uintptr_t level_;
  25.104 +  ke::Mutex mutex_;
  25.105  };
  25.106  
  25.107  const Register pri = eax;
  25.108 @@ -211,3 +218,4 @@
  25.109  extern JITX86 g_Jit;
  25.110  
  25.111  #endif //_INCLUDE_SOURCEPAWN_JIT_X86_H_
  25.112 +