From f76cb9451129741704c2c8a57eb5ff07b984defb Mon Sep 17 00:00:00 2001 From: David Anderson Date: Tue, 12 May 2020 15:40:39 -0700 Subject: [PATCH] Pare down ThreadSupport and remove ancient thread code. This patch removes almost all of the existing platform-specific ThreadSupport code, as well as code derived from it. It is now implemented on top of C++11 threads and is much simpler. This is the first inclusion of STL in SourceMod. Mac and Windows are allowed to dynamically link to their respective implementations. On Linux, libstdc++ is statically linked, except in the cases where it was already dynamically linked (csgo, blade). IEventSignal has been retained because sourcemod-curl-extension relies on it. As written, it is impossible to use as a condition variable, because the caller does not have access to the underlying mutex. There is no way to make this API safe or non-racy, so extensions relying on it should switch to C++11 threads. ThreadWorker is now pared down and does not interact or inherit from BaseWorker in any way. Basic functionality has been tested. Since it is not used anywhere in SourceMod, or seemingly in any repository on GitHub, it's unclear whether it should even exist. But it has been tested in this patch. This change bumps the minimum macOS version to OS X 10.7, and the minimum C++ standard level to C++14. --- AMBuildScript | 25 +- core/logic/AMBuilder | 8 +- core/logic/{thread => }/BaseWorker.cpp | 0 core/logic/{thread => }/BaseWorker.h | 1 + core/logic/ThreadSupport.cpp | 514 ++++++++++++++++++++++++- core/logic/ThreadSupport.h | 39 +- core/logic/thread/PosixThreads.cpp | 199 ---------- core/logic/thread/PosixThreads.h | 86 ----- core/logic/thread/ThreadWorker.cpp | 218 ----------- core/logic/thread/ThreadWorker.h | 69 ---- core/logic/thread/WinThreads.cpp | 216 ----------- core/logic/thread/WinThreads.h | 88 ----- public/IThreader.h | 14 +- 13 files changed, 534 insertions(+), 943 deletions(-) rename core/logic/{thread => }/BaseWorker.cpp (100%) rename core/logic/{thread => }/BaseWorker.h (96%) delete mode 100644 core/logic/thread/PosixThreads.cpp delete mode 100644 core/logic/thread/PosixThreads.h delete mode 100644 core/logic/thread/ThreadWorker.cpp delete mode 100644 core/logic/thread/ThreadWorker.h delete mode 100644 core/logic/thread/WinThreads.cpp delete mode 100644 core/logic/thread/WinThreads.h diff --git a/AMBuildScript b/AMBuildScript index 1be7732ef..8977f342b 100644 --- a/AMBuildScript +++ b/AMBuildScript @@ -281,8 +281,13 @@ class SMConfig(object): '-msse', '-fvisibility=hidden', ] + + if cxx.version == 'apple-clang-6.0': + cxx.cxxflags += ['-std=c++1y'] + else: + cxx.cxxflags += ['-std=c++14'] + cxx.cxxflags += [ - '-std=c++11', '-fno-exceptions', '-fno-threadsafe-statics', '-Wno-non-virtual-dtor', @@ -386,16 +391,16 @@ class SMConfig(object): cxx.linkflags += ['-static-libgcc'] elif cxx.family == 'clang': cxx.linkflags += ['-lgcc_eh'] + cxx.linkflags += ['-static-libstdc++'] def configure_mac(self, cxx): cxx.defines += ['OSX', '_OSX', 'POSIX', 'KE_ABSOLUTELY_NO_STL'] - cxx.cflags += ['-mmacosx-version-min=10.5'] + cxx.cflags += ['-mmacosx-version-min=10.7'] cxx.linkflags += [ - '-mmacosx-version-min=10.5', - '-lstdc++', - '-stdlib=libstdc++', + '-mmacosx-version-min=10.7', + '-stdlib=libc++', ] - cxx.cxxflags += ['-stdlib=libstdc++'] + cxx.cxxflags += ['-stdlib=libc++'] def configure_windows(self, cxx): cxx.defines += ['WIN32', '_WINDOWS'] @@ -545,9 +550,11 @@ class SMConfig(object): if builder.target.platform in ['linux', 'mac']: compiler.defines += ['NO_HOOK_MALLOC', 'NO_MALLOC_OVERRIDE'] - if sdk.name in ['csgo', 'blade'] and builder.target.platform == 'linux': - compiler.linkflags += ['-lstdc++'] - compiler.defines += ['_GLIBCXX_USE_CXX11_ABI=0'] + if builder.target.platform == 'linux': + if sdk.name in ['csgo', 'blade']: + compiler.linkflags.remove('-static-libstdc++') + compiler.linkflags += ['-lstdc++'] + compiler.defines += ['_GLIBCXX_USE_CXX11_ABI=0'] for path in paths: compiler.cxxincludes += [os.path.join(sdk.path, *path)] diff --git a/core/logic/AMBuilder b/core/logic/AMBuilder index 1415faa46..fa3117eb4 100644 --- a/core/logic/AMBuilder +++ b/core/logic/AMBuilder @@ -35,8 +35,7 @@ for arch in SM.archs: 'smn_maplists.cpp', 'ADTFactory.cpp', 'smn_adt_stack.cpp', - 'thread/ThreadWorker.cpp', - 'thread/BaseWorker.cpp', + 'BaseWorker.cpp', 'ThreadSupport.cpp', 'smn_float.cpp', 'TextParsers.cpp', @@ -90,9 +89,4 @@ for arch in SM.archs: if arch == 'x64': binary.sources += ['PseudoAddrManager.cpp'] - if builder.target.platform == 'windows': - binary.sources += ['thread/WinThreads.cpp'] - else: - binary.sources += ['thread/PosixThreads.cpp'] - SM.binaries += [builder.Add(binary)] diff --git a/core/logic/thread/BaseWorker.cpp b/core/logic/BaseWorker.cpp similarity index 100% rename from core/logic/thread/BaseWorker.cpp rename to core/logic/BaseWorker.cpp diff --git a/core/logic/thread/BaseWorker.h b/core/logic/BaseWorker.h similarity index 96% rename from core/logic/thread/BaseWorker.h rename to core/logic/BaseWorker.h index 0feceb5f5..5e87ed63d 100644 --- a/core/logic/thread/BaseWorker.h +++ b/core/logic/BaseWorker.h @@ -43,6 +43,7 @@ class BaseWorker; class SWThreadHandle : public IThreadHandle { friend class BaseWorker; + friend class CompatWorker; public: SWThreadHandle(IThreadCreator *parent, const ThreadParams *p, IThread *thread); IThread *GetThread(); diff --git a/core/logic/ThreadSupport.cpp b/core/logic/ThreadSupport.cpp index 55fd03275..1c4a35c56 100644 --- a/core/logic/ThreadSupport.cpp +++ b/core/logic/ThreadSupport.cpp @@ -1,5 +1,5 @@ /** - * vim: set ts=4 sw=4 : + * vim: set ts=4 sw=4 tw=99 noet : * ============================================================================= * SourceMod * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved. @@ -29,17 +29,515 @@ * Version: $Id$ */ #include +#include +#include +#include +#include +#include +#include +#include +#include +#include "BaseWorker.h" #include "ThreadSupport.h" #include "common_logic.h" -#if defined PLATFORM_POSIX -#include "thread/PosixThreads.h" -#elif defined PLATFORM_WINDOWS -#include "thread/WinThreads.h" -#endif +static constexpr unsigned int DEFAULT_THINK_TIME_MS = 20; -MainThreader g_MainThreader; -IThreader *g_pThreader = &g_MainThreader; +class CompatWorker final : public IThreadWorker +{ + public: + explicit CompatWorker(IThreadWorkerCallbacks* callbacks); + ~CompatWorker(); + + void MakeThread(IThread *pThread) override; + IThreadHandle *MakeThread(IThread *pThread, ThreadFlags flags) override; + IThreadHandle *MakeThread(IThread *pThread, const ThreadParams *params) override; + void GetPriorityBounds(ThreadPriority &max, ThreadPriority &min) override; + unsigned int RunFrame() override; + bool Pause() override; + bool Unpause() override; + bool Start() override; + bool Stop(bool flush) override; + WorkerState GetStatus(unsigned int *numThreads) override; + void SetMaxThreadsPerFrame(unsigned int threads) override; + void SetThinkTimePerFrame(unsigned int thinktime) override; + + private: + void Flush(); + void Worker(); + void RunWork(SWThreadHandle* handle); + void RunWorkLocked(std::unique_lock* lock, SWThreadHandle* handle); + + private: + IThreadWorkerCallbacks* callbacks_; + WorkerState state_; + std::mutex mutex_; + std::condition_variable work_cv_; + ke::Deque work_; + std::unique_ptr thread_; + std::atomic jobs_per_wakeup_; + std::atomic wait_between_jobs_; +}; + +CompatWorker::CompatWorker(IThreadWorkerCallbacks* callbacks) + : callbacks_(callbacks), + state_(Worker_Stopped), + jobs_per_wakeup_(SM_DEFAULT_THREADS_PER_FRAME), + wait_between_jobs_(DEFAULT_THINK_TIME_MS) +{ +} + +CompatWorker::~CompatWorker() +{ + Stop(false /* ignored */); + Flush(); +} + +bool CompatWorker::Start() +{ + std::lock_guard lock(mutex_); + if (state_ != Worker_Stopped) + return false; + + thread_ = std::make_unique([this]() -> void { + Worker(); + }); + state_ = Worker_Running; + return true; +} + +bool CompatWorker::Stop(bool) +{ + { + std::lock_guard lock(mutex_); + + if (state_ <= Worker_Stopped) + return false; + + state_ = Worker_Stopped; + work_cv_.notify_all(); + } + + thread_->join(); + thread_ = nullptr; + + Flush(); + return true; +} + +bool CompatWorker::Pause() +{ + std::lock_guard lock(mutex_); + if (state_ != Worker_Running) + return false; + + state_ = Worker_Paused; + work_cv_.notify_all(); + return true; +} + +bool CompatWorker::Unpause() +{ + std::lock_guard lock(mutex_); + if (state_ != Worker_Paused) + return false; + + state_ = Worker_Running; + work_cv_.notify_all(); + return true; +} + +void CompatWorker::Flush() +{ + while (!work_.empty()) { + auto handle = work_.popFrontCopy(); + handle->GetThread()->OnTerminate(handle, true); + if (handle->m_params.flags & Thread_AutoRelease) + delete handle; + } +} + +void CompatWorker::Worker() +{ + // Note: this must be first to ensure an ordering between Worker() and + // Start(). It must also be outside of the loop to ensure the lock is + // held across wakeup and retesting the predicates. + std::unique_lock lock(mutex_); + + if (callbacks_) { + lock.unlock(); + callbacks_->OnWorkerStart(this); + lock.lock(); + } + + typedef std::chrono::system_clock Clock; + typedef std::chrono::time_point TimePoint; + + auto can_work = [this]() -> bool { + return state_ == Worker_Running && !work_.empty(); + }; + + ke::Maybe wait; + unsigned int work_in_frame = 0; + for (;;) { + if (state_ == Worker_Stopped) + break; + + if (!can_work()) { + // Wait for work or a Stop. + work_cv_.wait(lock); + continue; + } + + if (wait.isValid()) { + // Wait until the specified time has passed. If we wake up with a + // timeout, then the wait has elapsed, so reset the holder. + if (work_cv_.wait_until(lock, wait.get()) == std::cv_status::timeout) + wait = ke::Nothing(); + continue; + } + + assert(state_ == Worker_Running); + assert(!work_.empty()); + + SWThreadHandle* handle = work_.popFrontCopy(); + RunWorkLocked(&lock, handle); + work_in_frame++; + + // If we've reached our max jobs per "frame", signal that the next + // immediate job must be delayed. We retain the old ThreadWorker + // behavior by checking if the queue has more work. Thus, a delay + // only occurs if two jobs would be processed in the same wakeup. + if (work_in_frame >= jobs_per_wakeup_ && wait_between_jobs_ && can_work()) + wait = ke::Some(Clock::now() + std::chrono::milliseconds(wait_between_jobs_)); + } + + assert(lock.owns_lock()); + + while (!work_.empty()) { + SWThreadHandle* handle = work_.popFrontCopy(); + RunWorkLocked(&lock, handle); + } +} + +unsigned int CompatWorker::RunFrame() +{ + unsigned int nprocessed = 0; + for (unsigned int i = 1; i <= jobs_per_wakeup_; i++) { + SWThreadHandle* handle; + { + std::lock_guard lock(mutex_); + if (work_.empty()) + break; + handle = work_.popFrontCopy(); + } + + RunWork(handle); + nprocessed++; + } + return nprocessed; +} + +void CompatWorker::RunWorkLocked(std::unique_lock* lock, SWThreadHandle* handle) +{ + lock->unlock(); + RunWork(handle); + lock->lock(); +} + +void CompatWorker::RunWork(SWThreadHandle* handle) +{ + bool autorelease = !!(handle->m_params.flags & Thread_AutoRelease); + handle->m_state = Thread_Running; + handle->GetThread()->RunThread(handle); + handle->m_state = Thread_Done; + handle->GetThread()->OnTerminate(handle, false); + if (autorelease) + delete handle; +} + +void CompatWorker::MakeThread(IThread *pThread) +{ + ThreadParams params; + params.flags = Thread_AutoRelease; + MakeThread(pThread, ¶ms); +} + +IThreadHandle *CompatWorker::MakeThread(IThread *pThread, ThreadFlags flags) +{ + ThreadParams params; + params.flags = flags; + return MakeThread(pThread, ¶ms); +} + +IThreadHandle *CompatWorker::MakeThread(IThread *pThread, const ThreadParams *params) +{ + std::lock_guard lock(mutex_); + + ThreadParams def_params; + if (!params) + params = &def_params; + + if (state_ <= Worker_Stopped) + return nullptr; + + SWThreadHandle* handle = new SWThreadHandle(this, params, pThread); + work_.append(handle); + work_cv_.notify_one(); + return handle; +} + +void CompatWorker::GetPriorityBounds(ThreadPriority &max, ThreadPriority &min) +{ + min = ThreadPrio_Normal; + max = ThreadPrio_Normal; +} + +void CompatWorker::SetMaxThreadsPerFrame(unsigned int threads) +{ + jobs_per_wakeup_ = threads; +} + +void CompatWorker::SetThinkTimePerFrame(unsigned int thinktime) +{ + wait_between_jobs_ = thinktime; +} + +WorkerState CompatWorker::GetStatus(unsigned int *numThreads) +{ + std::lock_guard lock(mutex_); + + // This number is meaningless and the status is racy. + if (numThreads) + *numThreads = jobs_per_wakeup_; + return state_; +} + +class CompatThread final : public IThreadHandle +{ +public: + CompatThread(IThread* callbacks, const ThreadParams* params); + + bool WaitForThread() override; + void DestroyThis() override; + IThreadCreator *Parent() override; + void GetParams(ThreadParams *ptparams) override; + ThreadPriority GetPriority() override; + bool SetPriority(ThreadPriority prio) override; + ThreadState GetState() override; + bool Unpause() override; + +private: + void Run(); + +private: + IThread* callbacks_; + ThreadParams params_; + std::unique_ptr thread_; + std::mutex mutex_; + std::condition_variable check_cv_; + std::atomic finished_; +}; + +CompatThread::CompatThread(IThread* callbacks, const ThreadParams* params) + : callbacks_(callbacks), + params_(*params) +{ + if (!(params_.flags & Thread_CreateSuspended)) + Unpause(); +} + +bool CompatThread::Unpause() +{ + std::unique_lock lock(mutex_); + if (thread_) + return false; + + thread_ = std::make_unique([this]() -> void { + Run(); + }); + + return true; +} + +void CompatThread::Run() +{ + // Create an ordering between when the thread runs and when thread_ is assigned. + std::unique_lock lock(mutex_); + + lock.unlock(); + callbacks_->RunThread(this); + finished_ = true; + callbacks_->OnTerminate(this, false); + + if (params_.flags & Thread_AutoRelease) { + // There should be no handles outstanding, so it's safe to self-destruct. + thread_->detach(); + delete this; + } + + lock.lock(); + callbacks_ = nullptr; + check_cv_.notify_all(); +} + +bool CompatThread::WaitForThread() +{ + std::unique_lock lock(mutex_); + for (;;) { + // When done, callbacks are unset. If paused, this will deadlock. + if (!callbacks_) + break; + check_cv_.wait(lock); + } + + thread_->join(); + return true; +} + +ThreadState CompatThread::GetState() +{ + std::unique_lock lock(mutex_); + if (!thread_) + return Thread_Paused; + return finished_ ? Thread_Done : Thread_Running; +} + +void CompatThread::DestroyThis() +{ + delete this; +} + +ThreadPriority CompatThread::GetPriority() +{ + return ThreadPrio_Normal; +} + +bool CompatThread::SetPriority(ThreadPriority prio) +{ + return prio == ThreadPrio_Normal; +} + +IThreadCreator *CompatThread::Parent() +{ + return g_pThreader; +} + +void CompatThread::GetParams(ThreadParams *ptparams) +{ + *ptparams = params_; +} + +class CompatMutex : public IMutex +{ +public: + bool TryLock() { + return mutex_.try_lock(); + } + void Lock() { + mutex_.lock(); + } + void Unlock() { + mutex_.unlock(); + } + void DestroyThis() { + delete this; + } +private: + std::mutex mutex_; +}; + +class CompatThreader final : public IThreader +{ +public: + void MakeThread(IThread *pThread) override; + IThreadHandle *MakeThread(IThread *pThread, ThreadFlags flags) override; + IThreadHandle *MakeThread(IThread *pThread, const ThreadParams *params) override; + void GetPriorityBounds(ThreadPriority &max, ThreadPriority &min) override; + IMutex *MakeMutex() override; + void ThreadSleep(unsigned int ms) override; + IEventSignal *MakeEventSignal() override; + IThreadWorker *MakeWorker(IThreadWorkerCallbacks *hooks, bool threaded) override; + void DestroyWorker(IThreadWorker *pWorker) override; +} sCompatThreader; + +void CompatThreader::MakeThread(IThread *pThread) +{ + ThreadParams params; + params.flags = Thread_AutoRelease; + MakeThread(pThread, ¶ms); +} + +IThreadHandle *CompatThreader::MakeThread(IThread *pThread, ThreadFlags flags) +{ + ThreadParams params; + params.flags = flags; + return MakeThread(pThread, ¶ms); +} + +IThreadHandle *CompatThreader::MakeThread(IThread *pThread, const ThreadParams *params) +{ + ThreadParams def_params; + if (!params) + params = &def_params; + + return new CompatThread(pThread, params); +} + +void CompatThreader::GetPriorityBounds(ThreadPriority &max, ThreadPriority &min) +{ + min = ThreadPrio_Normal; + max = ThreadPrio_Normal; +} + +IMutex *CompatThreader::MakeMutex() +{ + return new CompatMutex(); +} + +void CompatThreader::ThreadSleep(unsigned int ms) +{ + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); +} + +class CompatEventSignal final : public IEventSignal +{ +public: + void Wait() override { + std::unique_lock lock(mutex_); + cv_.wait(lock); + } + void Signal() override { + std::lock_guard lock(mutex_); + cv_.notify_all(); + } + void DestroyThis() override { + delete this; + } + +private: + std::mutex mutex_; + std::condition_variable cv_; +}; + +IEventSignal *CompatThreader::MakeEventSignal() +{ + return new CompatEventSignal(); +} + +IThreadWorker *CompatThreader::MakeWorker(IThreadWorkerCallbacks *hooks, bool threaded) +{ + if (!threaded) + return new BaseWorker(hooks); + return new CompatWorker(hooks); +} + +void CompatThreader::DestroyWorker(IThreadWorker *pWorker) +{ + delete pWorker; +} + +IThreader *g_pThreader = &sCompatThreader; class RegThreadStuff : public SMGlobalClass { diff --git a/core/logic/ThreadSupport.h b/core/logic/ThreadSupport.h index 907c1d3fc..4246f20ff 100644 --- a/core/logic/ThreadSupport.h +++ b/core/logic/ThreadSupport.h @@ -32,49 +32,14 @@ #ifndef _INCLUDE_SOURCEMOD_THREAD_SUPPORT_H #define _INCLUDE_SOURCEMOD_THREAD_SUPPORT_H +#include + #include #include #include using namespace SourceMod; -class CompatMutex : public IMutex -{ -public: - bool TryLock() { - return mutex_.TryLock(); - } - void Lock() { - mutex_.Lock(); - } - void Unlock() { - mutex_.Unlock(); - } - void DestroyThis() { - delete this; - } -private: - ke::Mutex mutex_; -}; - -class CompatCondVar : public IEventSignal -{ -public: - void Wait() { - ke::AutoLock lock(&cv_); - cv_.Wait(); - } - void Signal() { - ke::AutoLock lock(&cv_); - cv_.Notify(); - } - void DestroyThis() { - delete this; - } -private: - ke::ConditionVariable cv_; -}; - extern IThreader *g_pThreader; #endif //_INCLUDE_SOURCEMOD_THREAD_SUPPORT_H diff --git a/core/logic/thread/PosixThreads.cpp b/core/logic/thread/PosixThreads.cpp deleted file mode 100644 index 4932962a4..000000000 --- a/core/logic/thread/PosixThreads.cpp +++ /dev/null @@ -1,199 +0,0 @@ -/** - * vim: set ts=4 sw=4 tw=99 noet: - * ============================================================================= - * SourceMod - * Copyright (C) 2004-2008 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 . - * - * 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 . - * - * Version: $Id$ - */ - -#include -#include "PosixThreads.h" -#include "ThreadWorker.h" - -IThreadWorker *PosixThreader::MakeWorker(IThreadWorkerCallbacks *hooks, bool threaded) -{ - if (threaded) - { - return new ThreadWorker(hooks, this, DEFAULT_THINK_TIME_MS); - } else { - return new BaseWorker(hooks); - } -} - -void PosixThreader::DestroyWorker(IThreadWorker *pWorker) -{ - delete pWorker; -} - -void PosixThreader::ThreadSleep(unsigned int ms) -{ - usleep( ms * 1000 ); -} - -void PosixThreader::GetPriorityBounds(ThreadPriority &max, ThreadPriority &min) -{ - max = ThreadPrio_Normal; - min = ThreadPrio_Normal; -} - -IMutex *PosixThreader::MakeMutex() -{ - return new CompatMutex(); -} - -void PosixThreader::MakeThread(IThread *pThread) -{ - ThreadParams defparams; - - defparams.flags = Thread_AutoRelease; - defparams.prio = ThreadPrio_Normal; - - MakeThread(pThread, &defparams); -} - -IThreadHandle *PosixThreader::MakeThread(IThread *pThread, ThreadFlags flags) -{ - ThreadParams defparams; - - defparams.flags = flags; - defparams.prio = ThreadPrio_Normal; - - return MakeThread(pThread, &defparams); -} - -void PosixThreader::ThreadHandle::Run() -{ - // Wait for an unpause if necessary. - { - ke::AutoLock lock(&m_runlock); - if (m_state == Thread_Paused) - m_runlock.Wait(); - } - - m_run->RunThread(this); - m_state = Thread_Done; - m_run->OnTerminate(this, false); - - if (m_params.flags & Thread_AutoRelease) - delete this; -} - -ThreadParams g_defparams; -IThreadHandle *PosixThreader::MakeThread(IThread *pThread, const ThreadParams *params) -{ - if (params == NULL) - params = &g_defparams; - - ThreadHandle* pHandle = new ThreadHandle(this, pThread, params); - - pHandle->m_thread = new ke::Thread([pHandle]() -> void { - pHandle->Run(); - }, "SourceMod"); - if (!pHandle->m_thread->Succeeded()) { - delete pHandle; - return NULL; - } - - if (!(params->flags & Thread_CreateSuspended)) - pHandle->Unpause(); - - return pHandle; -} - -IEventSignal *PosixThreader::MakeEventSignal() -{ - return new CompatCondVar(); -} - -/****************** -* Thread Handles * -******************/ - -PosixThreader::ThreadHandle::ThreadHandle(IThreader *parent, IThread *run, const ThreadParams *params) : - m_parent(parent), m_params(*params), m_run(run), m_state(Thread_Paused) -{ -} - -PosixThreader::ThreadHandle::~ThreadHandle() -{ -} - -bool PosixThreader::ThreadHandle::WaitForThread() -{ - if (!m_thread) - return false; - - m_thread->Join(); - return true; -} - -ThreadState PosixThreader::ThreadHandle::GetState() -{ - return m_state; -} - -IThreadCreator *PosixThreader::ThreadHandle::Parent() -{ - return m_parent; -} - -void PosixThreader::ThreadHandle::DestroyThis() -{ - if (m_params.flags & Thread_AutoRelease) - return; - - delete this; -} - -void PosixThreader::ThreadHandle::GetParams(ThreadParams *ptparams) -{ - if (!ptparams) - return; - - *ptparams = m_params; -} - -ThreadPriority PosixThreader::ThreadHandle::GetPriority() -{ - return ThreadPrio_Normal; -} - -bool PosixThreader::ThreadHandle::SetPriority(ThreadPriority prio) -{ - return (prio == ThreadPrio_Normal); -} - -bool PosixThreader::ThreadHandle::Unpause() -{ - if (m_state != Thread_Paused) - return false; - - ke::AutoLock lock(&m_runlock); - m_state = Thread_Running; - m_runlock.Notify(); - return true; -} - diff --git a/core/logic/thread/PosixThreads.h b/core/logic/thread/PosixThreads.h deleted file mode 100644 index 764994f92..000000000 --- a/core/logic/thread/PosixThreads.h +++ /dev/null @@ -1,86 +0,0 @@ -/** - * vim: set ts=4 sw=4 tw=99 noet: - * ============================================================================= - * SourceMod - * Copyright (C) 2004-2008 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 . - * - * 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 . - * - * Version: $Id$ - */ - -#ifndef _INCLUDE_POSIXTHREADS_H_ -#define _INCLUDE_POSIXTHREADS_H_ - -#include -#include -#include "IThreader.h" - -using namespace SourceMod; - -class PosixThreader : public IThreader -{ -public: - class ThreadHandle : public IThreadHandle - { - friend class PosixThreader; - public: - ThreadHandle(IThreader *parent, IThread *run, const ThreadParams *params); - virtual ~ThreadHandle(); - public: - virtual bool WaitForThread(); - virtual void DestroyThis(); - virtual IThreadCreator *Parent(); - virtual void GetParams(ThreadParams *ptparams); - virtual ThreadPriority GetPriority(); - virtual bool SetPriority(ThreadPriority prio); - virtual ThreadState GetState(); - virtual bool Unpause(); - public: - void Run(); - protected: - IThreader *m_parent; //Parent handle - ThreadParams m_params; //Current Parameters - IThread *m_run; //Runnable context - ke::AutoPtr m_thread; - ke::ConditionVariable m_runlock; - ThreadState m_state; //internal state - }; -public: - IMutex *MakeMutex(); - void MakeThread(IThread *pThread); - IThreadHandle *MakeThread(IThread *pThread, ThreadFlags flags); - IThreadHandle *MakeThread(IThread *pThread, const ThreadParams *params); - void GetPriorityBounds(ThreadPriority &max, ThreadPriority &min); - void ThreadSleep(unsigned int ms); - IEventSignal *MakeEventSignal(); - IThreadWorker *MakeWorker(IThreadWorkerCallbacks *hooks, bool threaded); - void DestroyWorker(IThreadWorker *pWorker); -}; - -#if defined SM_DEFAULT_THREADER && !defined SM_MAIN_THREADER -#define SM_MAIN_THREADER PosixThreader; -typedef class PosixThreader MainThreader; -#endif - -#endif //_INCLUDE_POSIXTHREADS_H_ diff --git a/core/logic/thread/ThreadWorker.cpp b/core/logic/thread/ThreadWorker.cpp deleted file mode 100644 index 20ce55e30..000000000 --- a/core/logic/thread/ThreadWorker.cpp +++ /dev/null @@ -1,218 +0,0 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * SourceMod - * Copyright (C) 2004-2008 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 . - * - * 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 . - * - * Version: $Id$ - */ - -#include "ThreadWorker.h" - -ThreadWorker::ThreadWorker(IThreadWorkerCallbacks *hooks) : BaseWorker(hooks), - m_Threader(NULL), - me(NULL), - m_think_time(DEFAULT_THINK_TIME_MS) -{ - m_state = Worker_Invalid; -} - -ThreadWorker::ThreadWorker(IThreadWorkerCallbacks *hooks, IThreader *pThreader, unsigned int thinktime) : - BaseWorker(hooks), - m_Threader(pThreader), - me(NULL), - m_think_time(thinktime) -{ - m_state = m_Threader ? Worker_Stopped : Worker_Invalid; -} - -ThreadWorker::~ThreadWorker() -{ - if (m_state != Worker_Stopped || m_state != Worker_Invalid) - Stop(true); - - if (m_ThreadQueue.size()) - Flush(true); -} - -void ThreadWorker::OnTerminate(IThreadHandle *pHandle, bool cancel) -{ - //we don't particularly care - return; -} - -void ThreadWorker::RunThread(IThreadHandle *pHandle) -{ - if (m_pHooks) - m_pHooks->OnWorkerStart(this); - - ke::AutoLock lock(&monitor_); - - while (true) - { - if (m_state == Worker_Paused) - { - // Wait until we're told to wake up. - monitor_.Wait(); - continue; - } - - if (m_state == Worker_Stopped) - { - // We've been told to stop entirely. If we've also been told to - // flush the queue, do that now. - while (!m_ThreadQueue.empty()) - { - // Release the lock since PopThreadFromQueue() will re-acquire it. The - // main thread is blocking anyway. - ke::AutoUnlock unlock(&monitor_); - RunFrame(); - } - assert(m_state == Worker_Stopped); - return; - } - - assert(m_state == Worker_Running); - - // Process one frame. - WorkerState oldstate = m_state; - { - ke::AutoUnlock unlock(&monitor_); - RunFrame(); - } - - // If the state changed, loop back and process the new state. - if (m_state != oldstate) - continue; - - // If the thread queue is now empty, wait for a signal. Otherwise, if - // we're on a delay, wait for either a notification or a timeout to - // process the next item. If the queue has items and we don't have a - // delay, then we just loop around and keep processing. - if (m_ThreadQueue.empty()) - monitor_.Wait(); - else if (m_think_time) - monitor_.Wait(m_think_time); - } - - { - ke::AutoUnlock unlock(&monitor_); - if (m_pHooks) - m_pHooks->OnWorkerStop(this); - } -} - -SWThreadHandle *ThreadWorker::PopThreadFromQueue() -{ - ke::AutoLock lock(&monitor_); - if (m_state <= Worker_Stopped) - return NULL; - - return BaseWorker::PopThreadFromQueue(); -} - -void ThreadWorker::AddThreadToQueue(SWThreadHandle *pHandle) -{ - ke::AutoLock lock(&monitor_); - if (m_state <= Worker_Stopped) - return; - - BaseWorker::AddThreadToQueue(pHandle); - monitor_.Notify(); -} - -WorkerState ThreadWorker::GetStatus(unsigned int *threads) -{ - ke::AutoLock lock(&monitor_); - return BaseWorker::GetStatus(threads); -} - -void ThreadWorker::SetThinkTimePerFrame(unsigned int thinktime) -{ - m_think_time = thinktime; -} - -bool ThreadWorker::Start() -{ - if (m_state == Worker_Invalid && m_Threader == NULL) - return false; - - if (m_state != Worker_Stopped) - return false; - - m_state = Worker_Running; - - ThreadParams pt; - pt.flags = Thread_Default; - pt.prio = ThreadPrio_Normal; - me = m_Threader->MakeThread(this, &pt); - return true; -} - -bool ThreadWorker::Stop(bool flush_cancel) -{ - // Change the state to signal a stop, and then trigger a notify. - { - ke::AutoLock lock(&monitor_); - if (m_state == Worker_Invalid || m_state == Worker_Stopped) - return false; - - m_state = Worker_Stopped; - m_FlushType = flush_cancel; - monitor_.Notify(); - } - - me->WaitForThread(); - //destroy it - me->DestroyThis(); - //flush all remaining events - Flush(true); - - me = NULL; - return true; -} - -bool ThreadWorker::Pause() -{ - if (m_state != Worker_Running) - return false; - - ke::AutoLock lock(&monitor_); - m_state = Worker_Paused; - monitor_.Notify(); - return true; -} - - -bool ThreadWorker::Unpause() -{ - if (m_state != Worker_Paused) - return false; - - ke::AutoLock lock(&monitor_); - m_state = Worker_Running; - monitor_.Notify(); - return true; -} diff --git a/core/logic/thread/ThreadWorker.h b/core/logic/thread/ThreadWorker.h deleted file mode 100644 index dd45f76f1..000000000 --- a/core/logic/thread/ThreadWorker.h +++ /dev/null @@ -1,69 +0,0 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * SourceMod - * Copyright (C) 2004-2008 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 . - * - * 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 . - * - * Version: $Id$ - */ - -#ifndef _INCLUDE_SOURCEMOD_THREADWORKER_H -#define _INCLUDE_SOURCEMOD_THREADWORKER_H - -#include "BaseWorker.h" - -#define DEFAULT_THINK_TIME_MS 20 - -class ThreadWorker : public BaseWorker, public IThread -{ -public: - ThreadWorker(IThreadWorkerCallbacks *hooks); - ThreadWorker(IThreadWorkerCallbacks *hooks, IThreader *pThreader, unsigned int thinktime=DEFAULT_THINK_TIME_MS); - virtual ~ThreadWorker(); -public: //IThread - virtual void OnTerminate(IThreadHandle *pHandle, bool cancel); - virtual void RunThread(IThreadHandle *pHandle); -public: //IWorker - //Controls the worker - virtual bool Pause(); - virtual bool Unpause(); - virtual bool Start(); - virtual bool Stop(bool flush_cancel); - //returns status and number of threads in queue - virtual WorkerState GetStatus(unsigned int *numThreads); - //virtual void SetMaxThreadsPerFrame(unsigned int threads); - virtual void SetThinkTimePerFrame(unsigned int thinktime); -public: //BaseWorker - virtual void AddThreadToQueue(SWThreadHandle *pHandle); - virtual SWThreadHandle *PopThreadFromQueue(); -protected: - IThreader *m_Threader; - IThreadHandle *me; - unsigned int m_think_time; - bool m_FlushType; - ke::ConditionVariable monitor_; -}; - -#endif //_INCLUDE_SOURCEMOD_THREADWORKER_H diff --git a/core/logic/thread/WinThreads.cpp b/core/logic/thread/WinThreads.cpp deleted file mode 100644 index 79cb142e5..000000000 --- a/core/logic/thread/WinThreads.cpp +++ /dev/null @@ -1,216 +0,0 @@ -/** - * vim: set ts=4 sw=4 tw=99 noet: - * ============================================================================= - * SourceMod - * Copyright (C) 2004-2008 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 . - * - * 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 . - * - * Version: $Id$ - */ - -#define _WIN32_WINNT 0x0400 -#include "WinThreads.h" -#include "ThreadWorker.h" - -IThreadWorker *WinThreader::MakeWorker(IThreadWorkerCallbacks *hooks, bool threaded) -{ - if (threaded) - { - return new ThreadWorker(hooks, this, DEFAULT_THINK_TIME_MS); - } else { - return new BaseWorker(hooks); - } -} - -void WinThreader::DestroyWorker(IThreadWorker *pWorker) -{ - delete pWorker; -} - -void WinThreader::ThreadSleep(unsigned int ms) -{ - Sleep((DWORD)ms); -} - -IMutex *WinThreader::MakeMutex() -{ - return new CompatMutex(); -} - -IThreadHandle *WinThreader::MakeThread(IThread *pThread, ThreadFlags flags) -{ - ThreadParams defparams; - - defparams.flags = flags; - defparams.prio = ThreadPrio_Normal; - - return MakeThread(pThread, &defparams); -} - -void WinThreader::MakeThread(IThread *pThread) -{ - ThreadParams defparams; - - defparams.flags = Thread_AutoRelease; - defparams.prio = ThreadPrio_Normal; - - MakeThread(pThread, &defparams); -} - -void WinThreader::ThreadHandle::Run() -{ - // Wait for an unpause if necessary. - { - ke::AutoLock lock(&suspend_); - if (m_state == Thread_Paused) - suspend_.Wait(); - } - - m_run->RunThread(this); - m_state = Thread_Done; - m_run->OnTerminate(this, false); - if (m_params.flags & Thread_AutoRelease) - delete this; -} - -void WinThreader::GetPriorityBounds(ThreadPriority &max, ThreadPriority &min) -{ - max = ThreadPrio_Maximum; - min = ThreadPrio_Minimum; -} - -ThreadParams g_defparams; -IThreadHandle *WinThreader::MakeThread(IThread *pThread, const ThreadParams *params) -{ - if (params == NULL) - params = &g_defparams; - - ThreadHandle* pHandle = new ThreadHandle(this, pThread, params); - - pHandle->m_thread = new ke::Thread([pHandle]() -> void { - pHandle->Run(); - }, "SourceMod"); - if (!pHandle->m_thread->Succeeded()) { - delete pHandle; - return nullptr; - } - - if (pHandle->m_params.prio != ThreadPrio_Normal) - pHandle->SetPriority(pHandle->m_params.prio); - - if (!(params->flags & Thread_CreateSuspended)) - pHandle->Unpause(); - - return pHandle; -} - -IEventSignal *WinThreader::MakeEventSignal() -{ - return new CompatCondVar(); -} - -/****************** - * Thread Handles * - ******************/ - -WinThreader::ThreadHandle::ThreadHandle(IThreader *parent, IThread *run, const ThreadParams *params) : - m_parent(parent), m_run(run), m_params(*params), - m_state(Thread_Paused) -{ -} - -WinThreader::ThreadHandle::~ThreadHandle() -{ -} - -bool WinThreader::ThreadHandle::WaitForThread() -{ - if (!m_thread) - return false; - - m_thread->Join(); - return true; -} - -ThreadState WinThreader::ThreadHandle::GetState() -{ - return m_state; -} - -IThreadCreator *WinThreader::ThreadHandle::Parent() -{ - return m_parent; -} - -void WinThreader::ThreadHandle::DestroyThis() -{ - if (m_params.flags & Thread_AutoRelease) - return; - - delete this; -} - -void WinThreader::ThreadHandle::GetParams(ThreadParams *ptparams) -{ - if (!ptparams) - return; - - *ptparams = m_params; -} - -ThreadPriority WinThreader::ThreadHandle::GetPriority() -{ - return m_params.prio; -} - -bool WinThreader::ThreadHandle::SetPriority(ThreadPriority prio) -{ - BOOL res = FALSE; - - if (prio >= ThreadPrio_Maximum) - res = SetThreadPriority(m_thread->handle(), THREAD_PRIORITY_HIGHEST); - else if (prio <= ThreadPrio_Minimum) - res = SetThreadPriority(m_thread->handle(), THREAD_PRIORITY_LOWEST); - else if (prio == ThreadPrio_Normal) - res = SetThreadPriority(m_thread->handle(), THREAD_PRIORITY_NORMAL); - else if (prio == ThreadPrio_High) - res = SetThreadPriority(m_thread->handle(), THREAD_PRIORITY_ABOVE_NORMAL); - else if (prio == ThreadPrio_Low) - res = SetThreadPriority(m_thread->handle(), THREAD_PRIORITY_BELOW_NORMAL); - - m_params.prio = prio; - - return (res != FALSE); -} - -bool WinThreader::ThreadHandle::Unpause() -{ - if (m_state != Thread_Paused) - return false; - - ke::AutoLock lock(&suspend_); - m_state = Thread_Running; - suspend_.Notify(); - return true; -} diff --git a/core/logic/thread/WinThreads.h b/core/logic/thread/WinThreads.h deleted file mode 100644 index 63916fa49..000000000 --- a/core/logic/thread/WinThreads.h +++ /dev/null @@ -1,88 +0,0 @@ -/** - * vim: set ts=4 : - * ============================================================================= - * SourceMod - * Copyright (C) 2004-2008 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 . - * - * 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 . - * - * Version: $Id$ - */ - -#ifndef _INCLUDE_WINTHREADS_H_ -#define _INCLUDE_WINTHREADS_H_ - -#include -#include -#include -#include "IThreader.h" - -using namespace SourceMod; - -class WinThreader : public IThreader -{ -public: - class ThreadHandle : public IThreadHandle - { - friend class WinThreader; - friend DWORD WINAPI Win32_ThreadGate(LPVOID param); - public: - ThreadHandle(IThreader *parent, IThread *run, const ThreadParams *params); - virtual ~ThreadHandle(); - public: - virtual bool WaitForThread(); - virtual void DestroyThis(); - virtual IThreadCreator *Parent(); - virtual void GetParams(ThreadParams *ptparams); - virtual ThreadPriority GetPriority(); - virtual bool SetPriority(ThreadPriority prio); - virtual ThreadState GetState(); - virtual bool Unpause(); - virtual void Run(); - protected: - IThreader *m_parent; //Parent handle - ke::AutoPtr m_thread; - ThreadParams m_params; //Current Parameters - IThread *m_run; //Runnable context - ThreadState m_state; //internal state - ke::ConditionVariable suspend_; - }; - -public: - IMutex *MakeMutex(); - void MakeThread(IThread *pThread); - IThreadHandle *MakeThread(IThread *pThread, ThreadFlags flags); - IThreadHandle *MakeThread(IThread *pThread, const ThreadParams *params); - void GetPriorityBounds(ThreadPriority &max, ThreadPriority &min); - void ThreadSleep(unsigned int ms); - IEventSignal *MakeEventSignal(); - IThreadWorker *MakeWorker(IThreadWorkerCallbacks *hooks, bool threaded); - void DestroyWorker(IThreadWorker *pWorker); -}; - -#if defined SM_DEFAULT_THREADER && !defined SM_MAIN_THREADER -#define SM_MAIN_THREADER WinThreader; -typedef class WinThreader MainThreader; -#endif - -#endif //_INCLUDE_WINTHREADS_H_ diff --git a/public/IThreader.h b/public/IThreader.h index ed9cb1aee..5953bd274 100644 --- a/public/IThreader.h +++ b/public/IThreader.h @@ -1,5 +1,5 @@ /** - * vim: set ts=4 : + * vim: set ts=4 sw=4 tw=99 noet : * ============================================================================= * SourceMod * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. @@ -273,7 +273,9 @@ namespace SourceMod }; /** - * @brief Describes a simple "condition variable"/signal lock. + * @brief Object that can be used to signal from one thread to another. + * This should not be used and is deprecated. Use C++11 + * std::condition_variable instead, as this version is fundamentally racy. */ class IEventSignal { @@ -286,7 +288,7 @@ namespace SourceMod */ virtual void Wait() =0; - /** + /** * @brief Triggers the signal and resets the signal after triggering. */ virtual void Signal() =0; @@ -326,7 +328,7 @@ namespace SourceMod * @return Number of tasks processed. */ virtual unsigned int RunFrame() =0; - public: + /** * @brief Pauses the worker. * @@ -446,9 +448,9 @@ namespace SourceMod virtual void ThreadSleep(unsigned int ms) =0; /** - * @brief Creates a non-signalled event. + * @brief Deprecated; do not use. * - * @return A new IEventSignal pointer (must be destroyed). + * @return Returns a new IEventSignal. */ virtual IEventSignal *MakeEventSignal() =0;