Implement a watchdog timer for scripts that take too long to execute (bug 5837, r=fyren).
--HG-- extra : rebase_source : ffacb38457eca581660ce8f15c444ad828b7fedd
This commit is contained in:
@@ -64,6 +64,11 @@ class Assembler
|
||||
return pos_ - buffer_;
|
||||
}
|
||||
|
||||
// Current offset into the code stream.
|
||||
uint32_t pc() const {
|
||||
return uint32_t(pos_ - buffer_);
|
||||
}
|
||||
|
||||
protected:
|
||||
void writeByte(uint8_t byte) {
|
||||
write<uint8_t>(byte);
|
||||
@@ -125,11 +130,6 @@ class Assembler
|
||||
return int32_t(pos_ - buffer_);
|
||||
}
|
||||
|
||||
// pc is the unsigned version of position().
|
||||
uint32_t pc() const {
|
||||
return uint32_t(pos_ - buffer_);
|
||||
}
|
||||
|
||||
protected:
|
||||
void assertCanWrite(size_t bytes) {
|
||||
assert(pos_ + bytes <= end_);
|
||||
|
||||
@@ -640,6 +640,10 @@ class AssemblerX86 : public Assembler
|
||||
emit2(0xdc, 0xc0 + src.code);
|
||||
}
|
||||
|
||||
void jmp32(Label *dest) {
|
||||
emit1(0xe9);
|
||||
emitJumpTarget(dest);
|
||||
}
|
||||
void jmp(Label *dest) {
|
||||
int8_t d8;
|
||||
if (canEmitSmallJump(dest, &d8)) {
|
||||
@@ -655,6 +659,10 @@ class AssemblerX86 : public Assembler
|
||||
void jmp(const Operand &target) {
|
||||
emit1(0xff, 4, target);
|
||||
}
|
||||
void j32(ConditionCode cc, Label *dest) {
|
||||
emit2(0x0f, 0x80 + uint8_t(cc));
|
||||
emitJumpTarget(dest);
|
||||
}
|
||||
void j(ConditionCode cc, Label *dest) {
|
||||
int8_t d8;
|
||||
if (canEmitSmallJump(dest, &d8)) {
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* vim: set sts=2 ts=8 sw=2 tw=99 noet :
|
||||
* =============================================================================
|
||||
* SourcePawn
|
||||
* Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved.
|
||||
* =============================================================================
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License, version 3.0, as published by the
|
||||
* Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* As a special exception, AlliedModders LLC gives you permission to link the
|
||||
* code of this program (as well as its derivative works) to "Half-Life 2," the
|
||||
* "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
|
||||
* by the Valve Corporation. You must obey the GNU General Public License in
|
||||
* all respects for all other code used. Additionally, AlliedModders LLC grants
|
||||
* this exception to all derivative works. AlliedModders LLC defines further
|
||||
* exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
|
||||
* or <http://www.sourcemod.net/license.php>.
|
||||
*/
|
||||
#ifndef _include_sourcepawn_inline_list_h_
|
||||
#define _include_sourcepawn_inline_list_h_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
template <typename T> class InlineList;
|
||||
|
||||
template <typename T>
|
||||
class InlineListNode
|
||||
{
|
||||
friend class InlineList<T>;
|
||||
|
||||
public:
|
||||
InlineListNode()
|
||||
: next_(NULL),
|
||||
prev_(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
InlineListNode(InlineListNode *next, InlineListNode *prev)
|
||||
: next_(next),
|
||||
prev_(prev)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
InlineListNode *next_;
|
||||
InlineListNode *prev_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class InlineList
|
||||
{
|
||||
typedef InlineListNode<T> Node;
|
||||
|
||||
Node head_;
|
||||
|
||||
public:
|
||||
InlineList()
|
||||
: head_(&head_, &head_)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
class iterator
|
||||
{
|
||||
friend class InlineList;
|
||||
Node *iter_;
|
||||
|
||||
public:
|
||||
iterator(Node *iter)
|
||||
: iter_(iter)
|
||||
{
|
||||
}
|
||||
|
||||
iterator & operator ++() {
|
||||
iter_ = iter_->next;
|
||||
return *iter_;
|
||||
}
|
||||
iterator operator ++(int) {
|
||||
iterator old(*this);
|
||||
iter_ = iter_->next_;
|
||||
return old;
|
||||
}
|
||||
T * operator *() {
|
||||
return static_cast<T *>(iter_);
|
||||
}
|
||||
T * operator ->() {
|
||||
return static_cast<T *>(iter_);
|
||||
}
|
||||
bool operator !=(const iterator &where) const {
|
||||
return iter_ != where.iter_;
|
||||
}
|
||||
bool operator ==(const iterator &where) const {
|
||||
return iter_ == where.iter_;
|
||||
}
|
||||
iterator prev() const {
|
||||
iterator p(iter_->prev_);
|
||||
return p;
|
||||
}
|
||||
iterator next() const {
|
||||
iterator p(iter_->next_);
|
||||
return p;
|
||||
}
|
||||
};
|
||||
|
||||
iterator begin() {
|
||||
return iterator(head_.next_);
|
||||
}
|
||||
|
||||
iterator end() {
|
||||
return iterator(&head_);
|
||||
}
|
||||
|
||||
void erase(Node *t) {
|
||||
t->prev_->next_ = t->next_;
|
||||
t->next_->prev_ = t->prev_;
|
||||
}
|
||||
|
||||
void insert(Node *t) {
|
||||
t->prev_ = head_.prev_;
|
||||
t->next_ = &head_;
|
||||
head_.prev_->next_ = t;
|
||||
head_.prev_ = t;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // _include_sourcepawn_inline_list_h_
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
// vim: set ts=8 sts=2 sw=2 tw=99 et:
|
||||
//
|
||||
// This file is part of SourcePawn.
|
||||
//
|
||||
// SourcePawn is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// SourcePawn is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with SourcePawn. If not, see <http://www.gnu.org/licenses/>.
|
||||
#ifndef _include_sourcepawn_thread_posix_h_
|
||||
#define _include_sourcepawn_thread_posix_h_
|
||||
|
||||
#include <pthread.h>
|
||||
#include <sys/time.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#if defined(__linux__)
|
||||
# include <sys/prctl.h>
|
||||
#endif
|
||||
|
||||
namespace ke {
|
||||
|
||||
class Mutex : public Lockable
|
||||
{
|
||||
public:
|
||||
Mutex() {
|
||||
#if !defined(NDEBUG)
|
||||
int rv =
|
||||
#endif
|
||||
pthread_mutex_init(&mutex_, NULL);
|
||||
assert(rv == 0);
|
||||
}
|
||||
~Mutex() {
|
||||
pthread_mutex_destroy(&mutex_);
|
||||
}
|
||||
|
||||
bool DoTryLock() {
|
||||
return pthread_mutex_trylock(&mutex_) == 0;
|
||||
}
|
||||
|
||||
void DoLock() {
|
||||
pthread_mutex_lock(&mutex_);
|
||||
}
|
||||
|
||||
void DoUnlock() {
|
||||
pthread_mutex_unlock(&mutex_);
|
||||
}
|
||||
|
||||
pthread_mutex_t *raw() {
|
||||
return &mutex_;
|
||||
}
|
||||
|
||||
private:
|
||||
pthread_mutex_t mutex_;
|
||||
};
|
||||
|
||||
// Currently, this class only supports single-listener CVs.
|
||||
class ConditionVariable : public Lockable
|
||||
{
|
||||
public:
|
||||
ConditionVariable() {
|
||||
#if !defined(NDEBUG)
|
||||
int rv =
|
||||
#endif
|
||||
pthread_cond_init(&cv_, NULL);
|
||||
assert(rv == 0);
|
||||
}
|
||||
~ConditionVariable() {
|
||||
pthread_cond_destroy(&cv_);
|
||||
}
|
||||
|
||||
bool DoTryLock() {
|
||||
return mutex_.DoTryLock();
|
||||
}
|
||||
void DoLock() {
|
||||
mutex_.DoLock();
|
||||
}
|
||||
void DoUnlock() {
|
||||
mutex_.DoUnlock();
|
||||
}
|
||||
|
||||
void Notify() {
|
||||
AssertCurrentThreadOwns();
|
||||
pthread_cond_signal(&cv_);
|
||||
}
|
||||
|
||||
WaitResult Wait(size_t timeout_ms) {
|
||||
AssertCurrentThreadOwns();
|
||||
|
||||
struct timespec ts;
|
||||
if (clock_gettime(CLOCK_REALTIME, &ts) == -1)
|
||||
return Wait_Error;
|
||||
|
||||
ts.tv_sec += timeout_ms / 1000;
|
||||
ts.tv_nsec += (timeout_ms % 1000) * 1000000;
|
||||
if (ts.tv_nsec >= 1000000000) {
|
||||
ts.tv_sec++;
|
||||
ts.tv_nsec -= 1000000000;
|
||||
}
|
||||
|
||||
DebugSetUnlocked();
|
||||
int rv = pthread_cond_timedwait(&cv_, mutex_.raw(), &ts);
|
||||
DebugSetLocked();
|
||||
|
||||
if (rv == ETIMEDOUT)
|
||||
return Wait_Timeout;
|
||||
if (rv == 0)
|
||||
return Wait_Signaled;
|
||||
return Wait_Error;
|
||||
}
|
||||
|
||||
WaitResult Wait() {
|
||||
AssertCurrentThreadOwns();
|
||||
|
||||
DebugSetUnlocked();
|
||||
int rv = pthread_cond_wait(&cv_, mutex_.raw());
|
||||
DebugSetLocked();
|
||||
|
||||
if (rv == 0)
|
||||
return Wait_Signaled;
|
||||
return Wait_Error;
|
||||
}
|
||||
|
||||
private:
|
||||
Mutex mutex_;
|
||||
pthread_cond_t cv_;
|
||||
};
|
||||
|
||||
class Thread
|
||||
{
|
||||
struct ThreadData {
|
||||
IRunnable *run;
|
||||
char name[17];
|
||||
};
|
||||
public:
|
||||
Thread(IRunnable *run, const char *name = NULL) {
|
||||
ThreadData *data = new ThreadData;
|
||||
data->run = run;
|
||||
snprintf(data->name, sizeof(data->name), "%s", name ? name : "");
|
||||
|
||||
initialized_ = (pthread_create(&thread_, NULL, Main, data) == 0);
|
||||
if (!initialized_)
|
||||
delete data;
|
||||
}
|
||||
|
||||
bool Succeeded() const {
|
||||
return initialized_;
|
||||
}
|
||||
|
||||
void Join() {
|
||||
if (!Succeeded())
|
||||
return;
|
||||
pthread_join(thread_, NULL);
|
||||
}
|
||||
|
||||
private:
|
||||
static void *Main(void *arg) {
|
||||
AutoPtr<ThreadData> data((ThreadData *)arg);
|
||||
|
||||
if (data->name[0]) {
|
||||
#if defined(__linux__)
|
||||
prctl(PR_SET_NAME, (unsigned long)data->name);
|
||||
#elif defined(__APPLE__)
|
||||
int (*fn)(const char *) = (int (*)(const char *))dlsym(RTLD_DEFAULT, "pthread_setname_np");
|
||||
if (fn)
|
||||
fn(data->name);
|
||||
#endif
|
||||
}
|
||||
data->run->Run();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
private:
|
||||
bool initialized_;
|
||||
pthread_t thread_;
|
||||
};
|
||||
|
||||
} // namespace ke
|
||||
|
||||
#endif // _include_sourcepawn_thread_posix_h_
|
||||
|
||||
@@ -0,0 +1,241 @@
|
||||
// vim: set ts=8 sts=2 sw=2 tw=99 et:
|
||||
//
|
||||
// This file is part of SourcePawn.
|
||||
//
|
||||
// SourcePawn is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// SourcePawn is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with SourcePawn. If not, see <http://www.gnu.org/licenses/>.
|
||||
#ifndef _include_sourcepawn_threads_
|
||||
#define _include_sourcepawn_threads_
|
||||
|
||||
#include <assert.h>
|
||||
#if defined(_MSC_VER)
|
||||
# include <windows.h>
|
||||
#else
|
||||
# include <pthread.h>
|
||||
#endif
|
||||
#include <ke_utility.h>
|
||||
|
||||
// Thread primitives for SourcePawn.
|
||||
//
|
||||
// -- Mutexes --
|
||||
//
|
||||
// A Lockable is a mutual exclusion primitive. It can be owned by at most one
|
||||
// thread at a time, and ownership blocks any other thread from taking taking
|
||||
// ownership. Ownership must be acquired and released on the same thread.
|
||||
// Lockables are not re-entrant.
|
||||
//
|
||||
// While a few classes support the Lockable interface, the simplest Lockable
|
||||
// object that can be instantiated is a Mutex.
|
||||
//
|
||||
// -- Condition Variables --
|
||||
//
|
||||
// A ConditionVariable provides mutually exclusive access based on a
|
||||
// condition ocurring. CVs provide two capabilities: Wait(), which will block
|
||||
// until the condition is triggered, and Notify(), which signals any blocking
|
||||
// thread that the condition has occurred.
|
||||
//
|
||||
// Condition variables have an underlying mutex lock. This lock must be
|
||||
// acquired before calling Wait() or Notify(). It is automatically released
|
||||
// once Wait begins blocking. This operation is atomic with respect to other
|
||||
// threads and the mutex. For example, it is not possible for the lock to be
|
||||
// acquired by another thread in between unlocking and blocking. Since Notify
|
||||
// also requires the lock to be acquired, there is no risk of an event
|
||||
// accidentally dissipating into thin air because it was sent before the other
|
||||
// thread began blocking.
|
||||
//
|
||||
// When Wait() returns, the lock is automatically re-acquired. This operation
|
||||
// is NOT atomic. In between waking up and re-acquiring the lock, another
|
||||
// thread may steal the lock and issue another event. Applications must
|
||||
// account for this. For example, a message pump should check that there are no
|
||||
// messages left to process before blocking again.
|
||||
//
|
||||
// -- Threads --
|
||||
//
|
||||
// A Thread object, when created, spawns a new thread with the given callback
|
||||
// (the callbacks must implement IRunnable). Threads have one method of
|
||||
// interest, Join(), which will block until the thread's execution finishes.
|
||||
// Deleting a thread object will free any operating system resources associated
|
||||
// with that thread, if the thread has finished executing.
|
||||
//
|
||||
// Threads can fail to spawn; make sure to check Succeeded().
|
||||
//
|
||||
|
||||
namespace ke {
|
||||
|
||||
// Abstraction for accessing the current thread.
|
||||
#if defined(_MSC_VER)
|
||||
typedef HANDLE ThreadId;
|
||||
|
||||
static inline ThreadId GetCurrentThreadId()
|
||||
{
|
||||
return GetCurrentThread();
|
||||
}
|
||||
#else
|
||||
typedef pthread_t ThreadId;
|
||||
|
||||
static inline ThreadId GetCurrentThreadId()
|
||||
{
|
||||
return pthread_self();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Classes which use non-reentrant, same-thread lock/unlock semantics should
|
||||
// inherit from this and implement DoLock/DoUnlock.
|
||||
class Lockable
|
||||
{
|
||||
public:
|
||||
Lockable()
|
||||
{
|
||||
#if !defined(NDEBUG)
|
||||
owner_ = 0;
|
||||
#endif
|
||||
}
|
||||
virtual ~Lockable() {
|
||||
}
|
||||
|
||||
bool TryLock() {
|
||||
if (DoTryLock()) {
|
||||
DebugSetLocked();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Lock() {
|
||||
assert(Owner() != GetCurrentThreadId());
|
||||
DoLock();
|
||||
DebugSetLocked();
|
||||
}
|
||||
|
||||
void Unlock() {
|
||||
assert(Owner() == GetCurrentThreadId());
|
||||
DebugSetUnlocked();
|
||||
DoUnlock();
|
||||
}
|
||||
|
||||
void AssertCurrentThreadOwns() const {
|
||||
assert(Owner() == GetCurrentThreadId());
|
||||
}
|
||||
#if !defined(NDEBUG)
|
||||
bool Locked() const {
|
||||
return owner_ != 0;
|
||||
}
|
||||
ThreadId Owner() const {
|
||||
return owner_;
|
||||
}
|
||||
#endif
|
||||
|
||||
virtual bool DoTryLock() = 0;
|
||||
virtual void DoLock() = 0;
|
||||
virtual void DoUnlock() = 0;
|
||||
|
||||
protected:
|
||||
void DebugSetUnlocked() {
|
||||
#if !defined(NDEBUG)
|
||||
owner_ = 0;
|
||||
#endif
|
||||
}
|
||||
void DebugSetLocked() {
|
||||
#if !defined(NDEBUG)
|
||||
owner_ = GetCurrentThreadId();
|
||||
#endif
|
||||
}
|
||||
|
||||
protected:
|
||||
#if !defined(NDEBUG)
|
||||
ThreadId owner_;
|
||||
#endif
|
||||
};
|
||||
|
||||
// RAII for automatically locking and unlocking an object.
|
||||
class AutoLock
|
||||
{
|
||||
public:
|
||||
AutoLock(Lockable *lock)
|
||||
: lock_(lock)
|
||||
{
|
||||
lock_->Lock();
|
||||
}
|
||||
~AutoLock() {
|
||||
lock_->Unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
Lockable *lock_;
|
||||
};
|
||||
|
||||
class AutoTryLock
|
||||
{
|
||||
public:
|
||||
AutoTryLock(Lockable *lock)
|
||||
{
|
||||
lock_ = lock->TryLock() ? lock : NULL;
|
||||
}
|
||||
~AutoTryLock() {
|
||||
if (lock_)
|
||||
lock_->Unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
Lockable *lock_;
|
||||
};
|
||||
|
||||
// RAII for automatically unlocking and relocking an object.
|
||||
class AutoUnlock
|
||||
{
|
||||
public:
|
||||
AutoUnlock(Lockable *lock)
|
||||
: lock_(lock)
|
||||
{
|
||||
lock_->Unlock();
|
||||
}
|
||||
~AutoUnlock() {
|
||||
lock_->Lock();
|
||||
}
|
||||
|
||||
private:
|
||||
Lockable *lock_;
|
||||
};
|
||||
|
||||
enum WaitResult {
|
||||
// Woke up because something happened.
|
||||
Wait_Signaled,
|
||||
|
||||
// Woke up because nothing happened and a timeout was specified.
|
||||
Wait_Timeout,
|
||||
|
||||
// Woke up, but because of an error.
|
||||
Wait_Error
|
||||
};
|
||||
|
||||
// This must be implemented in order to spawn a new thread.
|
||||
class IRunnable
|
||||
{
|
||||
public:
|
||||
virtual ~IRunnable() {
|
||||
}
|
||||
|
||||
virtual void Run() = 0;
|
||||
};
|
||||
|
||||
} // namespace ke
|
||||
|
||||
// Include the actual thread implementations.
|
||||
#if defined(_MSC_VER)
|
||||
# include "ke_thread_windows.h"
|
||||
#else
|
||||
# include "ke_thread_posix.h"
|
||||
#endif
|
||||
|
||||
#endif // _include_sourcepawn_threads_
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
// vim: set ts=8 sts=2 sw=2 tw=99 et:
|
||||
//
|
||||
// This file is part of SourcePawn.
|
||||
//
|
||||
// SourcePawn is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// SourcePawn is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with SourcePawn. If not, see <http://www.gnu.org/licenses/>.
|
||||
#ifndef _include_sourcepawn_thread_windows_h_
|
||||
#define _include_sourcepawn_thread_windows_h_
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
namespace ke {
|
||||
|
||||
class CriticalSection : public Lockable
|
||||
{
|
||||
public:
|
||||
CriticalSection() {
|
||||
InitializeCriticalSection(&cs_);
|
||||
}
|
||||
~CriticalSection() {
|
||||
DeleteCriticalSection(&cs_);
|
||||
}
|
||||
|
||||
bool DoTryLock() {
|
||||
return !!TryEnterCriticalSection(&cs_);
|
||||
}
|
||||
void DoLock() {
|
||||
EnterCriticalSection(&cs_);
|
||||
}
|
||||
|
||||
void DoUnlock() {
|
||||
LeaveCriticalSection(&cs_);
|
||||
}
|
||||
|
||||
private:
|
||||
CRITICAL_SECTION cs_;
|
||||
};
|
||||
|
||||
typedef CriticalSection Mutex;
|
||||
|
||||
// Currently, this class only supports single-listener CVs.
|
||||
class ConditionVariable : public Lockable
|
||||
{
|
||||
public:
|
||||
ConditionVariable() {
|
||||
event_ = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
}
|
||||
~ConditionVariable() {
|
||||
CloseHandle(event_);
|
||||
}
|
||||
|
||||
bool DoTryLock() {
|
||||
return cs_.DoTryLock();
|
||||
}
|
||||
void DoLock() {
|
||||
cs_.DoLock();
|
||||
}
|
||||
void DoUnlock() {
|
||||
cs_.DoUnlock();
|
||||
}
|
||||
|
||||
void Notify() {
|
||||
AssertCurrentThreadOwns();
|
||||
SetEvent(event_);
|
||||
}
|
||||
|
||||
WaitResult Wait(size_t timeout_ms) {
|
||||
// This will assert if the lock has not been acquired. We don't need to be
|
||||
// atomic here, like pthread_cond_wait, because the event bit will stick
|
||||
// until reset by a wait function.
|
||||
Unlock();
|
||||
DWORD rv = WaitForSingleObject(event_, timeout_ms);
|
||||
Lock();
|
||||
|
||||
if (rv == WAIT_TIMEOUT)
|
||||
return Wait_Timeout;
|
||||
if (rv == WAIT_FAILED)
|
||||
return Wait_Error;
|
||||
return Wait_Signaled;
|
||||
}
|
||||
|
||||
WaitResult Wait() {
|
||||
return Wait(INFINITE);
|
||||
}
|
||||
|
||||
private:
|
||||
CriticalSection cs_;
|
||||
HANDLE event_;
|
||||
};
|
||||
|
||||
class Thread
|
||||
{
|
||||
public:
|
||||
Thread(IRunnable *run, const char *name = NULL) {
|
||||
thread_ = CreateThread(NULL, 0, Main, run, 0, NULL);
|
||||
}
|
||||
~Thread() {
|
||||
if (!thread_)
|
||||
return;
|
||||
CloseHandle(thread_);
|
||||
}
|
||||
|
||||
bool Succeeded() const {
|
||||
return !!thread_;
|
||||
}
|
||||
|
||||
void Join() {
|
||||
if (!Succeeded())
|
||||
return;
|
||||
WaitForSingleObject(thread_, INFINITE);
|
||||
}
|
||||
|
||||
private:
|
||||
static DWORD WINAPI Main(LPVOID arg) {
|
||||
((IRunnable *)arg)->Run();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#pragma pack(push, 8)
|
||||
struct ThreadNameInfo {
|
||||
DWORD dwType;
|
||||
LPCSTR szName;
|
||||
DWORD dwThreadID;
|
||||
DWORD dwFlags;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
private:
|
||||
HANDLE thread_;
|
||||
};
|
||||
|
||||
} // namespace ke
|
||||
|
||||
#endif // _include_sourcepawn_thread_windows_h_
|
||||
@@ -48,34 +48,45 @@ static const size_t kKB = 1024;
|
||||
static const size_t kMB = 1024 * kKB;
|
||||
static const size_t kGB = 1024 * kMB;
|
||||
|
||||
template <typename T> T
|
||||
ReturnAndVoid(T &t)
|
||||
{
|
||||
T saved = t;
|
||||
t = T();
|
||||
return saved;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class AutoFree
|
||||
class AutoPtr
|
||||
{
|
||||
T *t_;
|
||||
|
||||
public:
|
||||
AutoFree()
|
||||
AutoPtr()
|
||||
: t_(NULL)
|
||||
{
|
||||
}
|
||||
AutoFree(T *t)
|
||||
explicit AutoPtr(T *t)
|
||||
: t_(t)
|
||||
{
|
||||
}
|
||||
~AutoFree() {
|
||||
free(t_);
|
||||
~AutoPtr() {
|
||||
delete t_;
|
||||
}
|
||||
T *take() {
|
||||
T *t = t_;
|
||||
t_ = NULL;
|
||||
return t;
|
||||
return ReturnAndVoid(t_);
|
||||
}
|
||||
T *operator *() const {
|
||||
return t_;
|
||||
}
|
||||
T *operator ->() const {
|
||||
return t_;
|
||||
}
|
||||
operator T *() const {
|
||||
return t_;
|
||||
}
|
||||
void operator =(T *t) {
|
||||
if (t_)
|
||||
free(t_);
|
||||
delete t_;
|
||||
t_ = t;
|
||||
}
|
||||
};
|
||||
@@ -289,14 +300,6 @@ Max(const T &t1, const T &t2)
|
||||
return t1 > t2 ? t1 : t2;
|
||||
}
|
||||
|
||||
template <typename T> T
|
||||
ReturnAndVoid(T &t)
|
||||
{
|
||||
T saved = t;
|
||||
t = T();
|
||||
return saved;
|
||||
}
|
||||
|
||||
#define OFFSETOF(Class, Member) reinterpret_cast<size_t>(&((Class *)NULL)->Member)
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
/** SourcePawn Engine API Version */
|
||||
#define SOURCEPAWN_ENGINE_API_VERSION 4
|
||||
#define SOURCEPAWN_ENGINE2_API_VERSION 4
|
||||
#define SOURCEPAWN_ENGINE2_API_VERSION 5
|
||||
|
||||
#if !defined SOURCEMOD_BUILD
|
||||
#define SOURCEMOD_BUILD
|
||||
@@ -1286,6 +1286,15 @@ namespace SourcePawn
|
||||
* @return New runtime, or NULL if not enough memory.
|
||||
*/
|
||||
virtual IPluginRuntime *CreateEmptyRuntime(const char *name, uint32_t memory) =0;
|
||||
|
||||
/**
|
||||
* @brief Initiates the watchdog timer with the specified timeout
|
||||
* length. This cannot be called more than once.
|
||||
*
|
||||
* @param timeout Timeout, in ms.
|
||||
* @return True on success, false on failure.
|
||||
*/
|
||||
virtual bool InstallWatchdogTimer(size_t timeout_ms) =0;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -87,6 +87,7 @@ typedef uint32_t funcid_t; /**< Function index code */
|
||||
#define SP_ERROR_CODE_TOO_NEW 27 /**< Code is too new for this VM */
|
||||
#define SP_ERROR_OUT_OF_MEMORY 28 /**< Out of memory */
|
||||
#define SP_ERROR_INTEGER_OVERFLOW 29 /**< Integer overflow (-INT_MIN / -1) */
|
||||
#define SP_ERROR_TIMEOUT 30 /**< Timeout */
|
||||
//Hey you! Update the string table if you add to the end of me! */
|
||||
|
||||
/**********************************************
|
||||
|
||||
Reference in New Issue
Block a user