sourcepawn/jit/watchdog_timer.cpp
author Ruben Gonzalez <drifter01620@gmail.com>
Thu May 22 09:49:27 2014 -0400 (2014-05-22)
changeset 4378 568f4497ef4a
parent 4129 81d532b90c86
permissions -rw-r--r--
Update CS:GO team score gamedata.
     1 // vim: set ts=8 sts=2 sw=2 tw=99 et:
     2 //
     3 // This file is part of SourcePawn.
     4 // 
     5 // SourcePawn is free software: you can redistribute it and/or modify
     6 // it under the terms of the GNU General Public License as published by
     7 // the Free Software Foundation, either version 3 of the License, or
     8 // (at your option) any later version.
     9 // 
    10 // SourcePawn is distributed in the hope that it will be useful,
    11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13 // GNU General Public License for more details.
    14 // 
    15 // You should have received a copy of the GNU General Public License
    16 // along with SourcePawn.  If not, see <http://www.gnu.org/licenses/>.
    17 #include "watchdog_timer.h"
    18 #include "x86/jit_x86.h"
    19 #include <string.h>
    20 
    21 WatchdogTimer g_WatchdogTimer;
    22 
    23 WatchdogTimer::WatchdogTimer()
    24   : terminate_(false),
    25     mainthread_(ke::GetCurrentThreadId()),
    26     last_frame_id_(0),
    27     second_timeout_(false),
    28     timedout_(false)
    29 {
    30 }
    31 
    32 WatchdogTimer::~WatchdogTimer()
    33 {
    34   assert(!thread_);
    35 }
    36 
    37 bool
    38 WatchdogTimer::Initialize(size_t timeout_ms)
    39 {
    40   if (thread_)
    41     return false;
    42 
    43   timeout_ms_ = timeout_ms;
    44 
    45   thread_ = new ke::Thread(this, "SM Watchdog");
    46   if (!thread_->Succeeded())
    47     return false;
    48 
    49   return true;
    50 }
    51 
    52 void
    53 WatchdogTimer::Shutdown()
    54 {
    55   if (terminate_ || !thread_)
    56     return;
    57 
    58   {
    59     ke::AutoLock lock(&cv_);
    60     terminate_ = true;
    61     cv_.Notify();
    62   }
    63   thread_->Join();
    64   thread_ = NULL;
    65 }
    66 
    67 void
    68 WatchdogTimer::Run()
    69 {
    70   ke::AutoLock lock(&cv_);
    71 
    72   // Initialize the frame id, so we don't have to wait longer on startup.
    73   last_frame_id_ = g_Jit.FrameId();
    74 
    75   while (!terminate_) {
    76     ke::WaitResult rv = cv_.Wait(timeout_ms_ / 2);
    77     if (terminate_)
    78       return;
    79 
    80     if (rv == ke::Wait_Error)
    81       return;
    82 
    83     if (rv != ke::Wait_Timeout)
    84       continue;
    85 
    86     // We reached a timeout. If the current frame is not equal to the last
    87     // frame, then we assume the server is still moving enough to process
    88     // frames. We also make sure JIT code is actually on the stack, since
    89     // our concept of frames won't move if JIT code is not running.
    90     //
    91     // Note that it's okay if these two race: it's just a heuristic, and
    92     // worst case, we'll miss something that might have timed out but
    93     // ended up resuming.
    94     uintptr_t frame_id = g_Jit.FrameId();
    95     if (frame_id != last_frame_id_ || !g_Jit.RunningCode()) {
    96       last_frame_id_ = frame_id;
    97       second_timeout_ = false;
    98       continue;
    99     }
   100 
   101     // We woke up with the same frame. We might be timing out. Wait another
   102     // time to see if so.
   103     if (!second_timeout_) {
   104       second_timeout_ = true;
   105       continue;
   106     }
   107 
   108     {
   109       // Prevent the JIT from linking or destroying runtimes and functions.
   110       ke::AutoLock lock(g_Jit.Mutex());
   111 
   112       // Set the timeout notification bit. If this is detected before any patched
   113       // JIT backedges are reached, the main thread will attempt to acquire the
   114       // monitor lock, and block until we call Wait().
   115       timedout_ = true;
   116     
   117       // Patch all jumps. This can race with the main thread's execution since
   118       // all code writes are 32-bit integer instruction operands, which are
   119       // guaranteed to be atomic on x86.
   120       g_Jit.PatchAllJumpsForTimeout();
   121     }
   122 
   123     // The JIT will be free to compile new functions while we wait, but it will
   124     // see the timeout bit set above and immediately bail out.
   125     cv_.Wait();
   126 
   127     second_timeout_ = false;
   128 
   129     // Reset the last frame ID to something unlikely to be chosen again soon.
   130     last_frame_id_--;
   131 
   132     // It's possible that Shutdown() raced with the timeout, and if so, we
   133     // must leave now to prevent a deadlock.
   134     if (terminate_)
   135       return;
   136   }
   137 }
   138 
   139 bool
   140 WatchdogTimer::NotifyTimeoutReceived()
   141 {
   142   // We are guaranteed that the watchdog thread is waiting for our
   143   // notification, and is therefore blocked. We take the JIT lock
   144   // anyway for sanity.
   145   {
   146     ke::AutoLock lock(g_Jit.Mutex());
   147     g_Jit.UnpatchAllJumpsFromTimeout();
   148   }
   149 
   150   timedout_ = false;
   151 
   152   // Wake up the watchdog thread, it's okay to keep processing now.
   153   ke::AutoLock lock(&cv_);
   154   cv_.Notify();
   155   return false;
   156 }
   157 
   158 bool
   159 WatchdogTimer::HandleInterrupt()
   160 {
   161   if (timedout_)
   162     return NotifyTimeoutReceived();
   163   return true;
   164 }