mirror of
https://github.com/ONLYOFFICE/core.git
synced 2026-02-10 18:05:41 +08:00
Add BaseThreadMonitor class
This commit is contained in:
@ -30,25 +30,27 @@ HEADERS += ./kernel_config.h
|
||||
|
||||
# BLOCKER
|
||||
HEADERS += \
|
||||
./../DesktopEditor/graphics/TemporaryCS.h
|
||||
./../DesktopEditor/graphics/TemporaryCS.h
|
||||
|
||||
SOURCES += \
|
||||
./../DesktopEditor/graphics/TemporaryCS.cpp
|
||||
./../DesktopEditor/graphics/TemporaryCS.cpp
|
||||
|
||||
# THREAD
|
||||
core_android:DEFINES += NOT_USE_PTHREAD_CANCEL USE_FILE32API
|
||||
HEADERS += \
|
||||
./../DesktopEditor/graphics/BaseThread.h
|
||||
./../DesktopEditor/graphics/BaseThread.h \
|
||||
./../DesktopEditor/graphics/BaseThreadMonitor.h
|
||||
|
||||
SOURCES += \
|
||||
./../DesktopEditor/graphics/BaseThread.cpp
|
||||
./../DesktopEditor/graphics/BaseThread.cpp \
|
||||
./../DesktopEditor/graphics/BaseThreadMonitor.cpp
|
||||
|
||||
# TIMER
|
||||
HEADERS += \
|
||||
./../DesktopEditor/graphics/Timer.h
|
||||
./../DesktopEditor/graphics/Timer.h
|
||||
|
||||
SOURCES += \
|
||||
./../DesktopEditor/graphics/Timer.cpp
|
||||
./../DesktopEditor/graphics/Timer.cpp
|
||||
|
||||
# PATH
|
||||
HEADERS += ./../DesktopEditor/common/Path.h
|
||||
@ -92,11 +94,11 @@ HEADERS += ./../DesktopEditor/common/ProcessEnv.h
|
||||
SOURCES += ./../DesktopEditor/common/ProcessEnv.cpp
|
||||
|
||||
core_windows {
|
||||
LIBS += -lRpcrt4
|
||||
LIBS += -lShell32
|
||||
LIBS += -lRpcrt4
|
||||
LIBS += -lShell32
|
||||
}
|
||||
|
||||
core_ios {
|
||||
OBJECTIVE_SOURCES += ./../DesktopEditor/common/File_ios.mm
|
||||
LIBS += -framework Foundation
|
||||
OBJECTIVE_SOURCES += ./../DesktopEditor/common/File_ios.mm
|
||||
LIBS += -framework Foundation
|
||||
}
|
||||
|
||||
@ -30,13 +30,13 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#if defined(__ANDROID__) && !defined (NOT_USE_PTHREAD_CANCEL)
|
||||
#if defined(__ANDROID__) && !defined(NOT_USE_PTHREAD_CANCEL)
|
||||
#include <pthread_setcanceltype.h>
|
||||
#endif
|
||||
|
||||
#include "./BaseThread.h"
|
||||
#include "./BaseThreadMonitor.h"
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64) ||defined(_WIN32_WCE)
|
||||
#if defined(_WIN32) || defined(_WIN64) || defined(_WIN32_WCE)
|
||||
|
||||
#include <winbase.h>
|
||||
|
||||
@ -49,203 +49,230 @@
|
||||
|
||||
namespace NSThreads
|
||||
{
|
||||
class CThreadDescriptor
|
||||
{
|
||||
public:
|
||||
CThreadDescriptor()
|
||||
{
|
||||
}
|
||||
virtual ~CThreadDescriptor()
|
||||
{
|
||||
}
|
||||
};
|
||||
class CThreadDescriptor
|
||||
{
|
||||
public:
|
||||
CThreadDescriptor()
|
||||
{
|
||||
}
|
||||
virtual ~CThreadDescriptor()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
ASC_THREAD_ID GetCurrentThreadId()
|
||||
{
|
||||
#if defined(_WIN32) || defined (_WIN64)
|
||||
return ::GetCurrentThreadId();
|
||||
ASC_THREAD_ID GetCurrentThreadId()
|
||||
{
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
return ::GetCurrentThreadId();
|
||||
#else
|
||||
return pthread_self();
|
||||
return pthread_self();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void Sleep(int nMilliseconds)
|
||||
void Sleep(int nMilliseconds)
|
||||
{
|
||||
#if defined(_WIN32) || defined(_WIN64) || defined(_WIN32_WCE)
|
||||
::Sleep((DWORD)nMilliseconds);
|
||||
#else
|
||||
struct timespec tim, tim2;
|
||||
tim.tv_sec = nMilliseconds / 1000;
|
||||
tim.tv_nsec = (nMilliseconds % 1000) * 1000000;
|
||||
tim.tv_sec = nMilliseconds / 1000;
|
||||
tim.tv_nsec = (nMilliseconds % 1000) * 1000000;
|
||||
|
||||
::nanosleep(&tim , &tim2);
|
||||
::nanosleep(&tim, &tim2);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64) || defined(_WIN32_WCE)
|
||||
DWORD WINAPI CBaseThread::__ThreadProc(void* pv)
|
||||
{
|
||||
CBaseThread* pThis = (CBaseThread*)pv;
|
||||
DWORD value = pThis->ThreadProc();
|
||||
if (pThis->m_bIsNeedDestroy)
|
||||
delete pThis;
|
||||
return value;
|
||||
}
|
||||
DWORD WINAPI CBaseThread::__ThreadProc(void* pv)
|
||||
{
|
||||
CBaseThread* pThis = (CBaseThread*)pv;
|
||||
|
||||
class __native_thread : public CThreadDescriptor
|
||||
{
|
||||
friend class CBaseThread;
|
||||
private:
|
||||
HANDLE m_thread = nullptr;
|
||||
CBaseThreadMonitor::Get().Register(pThis);
|
||||
DWORD value = pThis->ThreadProc();
|
||||
CBaseThreadMonitor::Get().Unregister(pThis);
|
||||
|
||||
public:
|
||||
__native_thread() : CThreadDescriptor()
|
||||
{
|
||||
}
|
||||
virtual ~__native_thread()
|
||||
{
|
||||
if (m_thread)
|
||||
{
|
||||
CloseHandle(m_thread);
|
||||
m_thread = NULL;
|
||||
}
|
||||
}
|
||||
};
|
||||
if (pThis->m_bIsNeedDestroy)
|
||||
delete pThis;
|
||||
return value;
|
||||
}
|
||||
|
||||
class __native_thread : public CThreadDescriptor
|
||||
{
|
||||
friend class CBaseThread;
|
||||
|
||||
private:
|
||||
HANDLE m_thread = nullptr;
|
||||
|
||||
public:
|
||||
__native_thread() : CThreadDescriptor()
|
||||
{
|
||||
}
|
||||
virtual ~__native_thread()
|
||||
{
|
||||
if (m_thread)
|
||||
{
|
||||
CloseHandle(m_thread);
|
||||
m_thread = NULL;
|
||||
}
|
||||
}
|
||||
};
|
||||
#else
|
||||
void* CBaseThread::__ThreadProc(void* pv)
|
||||
{
|
||||
void* CBaseThread::__ThreadProc(void* pv)
|
||||
{
|
||||
#ifndef NOT_USE_PTHREAD_CANCEL
|
||||
int old_thread_type;
|
||||
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old_thread_type);
|
||||
int old_thread_type;
|
||||
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old_thread_type);
|
||||
#endif
|
||||
|
||||
CBaseThread* pThis = (CBaseThread*)pv;
|
||||
pThis->ThreadProc();
|
||||
CBaseThread* pThis = (CBaseThread*)pv;
|
||||
|
||||
if (pThis->m_bIsNeedDestroy)
|
||||
delete pThis;
|
||||
CBaseThreadMonitor::Get().Register(pThis);
|
||||
DWORD value = pThis->ThreadProc();
|
||||
CBaseThreadMonitor::Get().Unregister(pThis);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
class __native_thread : public CThreadDescriptor
|
||||
{
|
||||
friend class CBaseThread;
|
||||
private:
|
||||
pthread_t m_thread;
|
||||
if (pThis->m_bIsNeedDestroy)
|
||||
delete pThis;
|
||||
|
||||
public:
|
||||
__native_thread() : CThreadDescriptor()
|
||||
{
|
||||
m_thread = NULL;
|
||||
}
|
||||
virtual ~__native_thread()
|
||||
{
|
||||
}
|
||||
};
|
||||
return NULL;
|
||||
}
|
||||
class __native_thread : public CThreadDescriptor
|
||||
{
|
||||
friend class CBaseThread;
|
||||
|
||||
private:
|
||||
pthread_t m_thread;
|
||||
|
||||
public:
|
||||
__native_thread() : CThreadDescriptor()
|
||||
{
|
||||
m_thread = NULL;
|
||||
}
|
||||
virtual ~__native_thread()
|
||||
{
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
CBaseThread::CBaseThread()
|
||||
{
|
||||
m_hThread = NULL;
|
||||
m_bRunThread = FALSE;
|
||||
m_bSuspend = FALSE;
|
||||
CBaseThread::CBaseThread()
|
||||
{
|
||||
m_hThread = NULL;
|
||||
m_bRunThread = FALSE;
|
||||
m_bSuspend = FALSE;
|
||||
|
||||
m_lError = 0;
|
||||
m_lThreadPriority = 0;
|
||||
m_lError = 0;
|
||||
m_lThreadPriority = 0;
|
||||
|
||||
m_bIsNeedDestroy = false;
|
||||
}
|
||||
CBaseThread::~CBaseThread()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
void CBaseThread::Start(int lPriority)
|
||||
{
|
||||
if (m_bRunThread)
|
||||
return;
|
||||
m_lError = 0;
|
||||
m_bSuspend = FALSE;
|
||||
m_bIsNeedDestroy = false;
|
||||
}
|
||||
CBaseThread::~CBaseThread()
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
void CBaseThread::Start(int lPriority)
|
||||
{
|
||||
if (m_bRunThread)
|
||||
return;
|
||||
m_lError = 0;
|
||||
m_bSuspend = FALSE;
|
||||
|
||||
m_hThread = new __native_thread();
|
||||
m_hThread = new __native_thread();
|
||||
|
||||
m_bRunThread = TRUE;
|
||||
m_bRunThread = TRUE;
|
||||
|
||||
m_bIsExit.store(false);
|
||||
m_bIsExit.store(false);
|
||||
#if defined(_WIN32) || defined(_WIN64) || defined(_WIN32_WCE)
|
||||
DWORD dwTemp;
|
||||
((__native_thread*)m_hThread)->m_thread = CreateThread(NULL, 0, &__ThreadProc, (void*)this, 0, &dwTemp);
|
||||
SetThreadPriority(((__native_thread*)m_hThread)->m_thread, lPriority);
|
||||
DWORD dwTemp;
|
||||
((__native_thread*)m_hThread)->m_thread = CreateThread(NULL, 0, &__ThreadProc, (void*)this, 0, &dwTemp);
|
||||
SetThreadPriority(((__native_thread*)m_hThread)->m_thread, lPriority);
|
||||
#else
|
||||
pthread_create(&((__native_thread*)m_hThread)->m_thread, 0, &__ThreadProc, (void*)this);
|
||||
pthread_create(&((__native_thread*)m_hThread)->m_thread, 0, &__ThreadProc, (void*)this);
|
||||
#endif
|
||||
m_lThreadPriority = lPriority;
|
||||
}
|
||||
void CBaseThread::Suspend()
|
||||
{
|
||||
m_bSuspend = TRUE;
|
||||
}
|
||||
void CBaseThread::Resume()
|
||||
{
|
||||
m_bSuspend = FALSE;
|
||||
}
|
||||
void CBaseThread::Stop()
|
||||
{
|
||||
if (!m_bRunThread)
|
||||
return;
|
||||
m_lThreadPriority = lPriority;
|
||||
}
|
||||
void CBaseThread::Suspend()
|
||||
{
|
||||
m_bSuspend = TRUE;
|
||||
}
|
||||
void CBaseThread::Resume()
|
||||
{
|
||||
m_bSuspend = FALSE;
|
||||
}
|
||||
void CBaseThread::Stop()
|
||||
{
|
||||
if (!m_bRunThread)
|
||||
return;
|
||||
|
||||
m_bIsExit.store(true);
|
||||
m_bRunThread = FALSE;
|
||||
m_bIsExit.store(true);
|
||||
m_bRunThread = FALSE;
|
||||
|
||||
Join();
|
||||
RELEASEOBJECT(m_hThread);
|
||||
}
|
||||
void CBaseThread::StopNoJoin()
|
||||
{
|
||||
m_bRunThread = FALSE;
|
||||
m_bIsExit.store(true);
|
||||
RELEASEOBJECT(m_hThread);
|
||||
}
|
||||
void CBaseThread::DestroyOnFinish()
|
||||
{
|
||||
m_bIsNeedDestroy = true;
|
||||
}
|
||||
Join();
|
||||
RELEASEOBJECT(m_hThread);
|
||||
}
|
||||
void CBaseThread::StopNoJoin()
|
||||
{
|
||||
m_bRunThread = FALSE;
|
||||
m_bIsExit.store(true);
|
||||
RELEASEOBJECT(m_hThread);
|
||||
}
|
||||
void CBaseThread::DestroyOnFinish()
|
||||
{
|
||||
m_bIsNeedDestroy = true;
|
||||
}
|
||||
|
||||
INT CBaseThread::IsSuspended() { return m_bSuspend; }
|
||||
INT CBaseThread::IsRunned() { return m_bRunThread; }
|
||||
int CBaseThread::GetError() { return m_lError; }
|
||||
bool CBaseThread::isAborted() {return m_bIsExit && m_bIsExit.load();}
|
||||
INT CBaseThread::IsSuspended()
|
||||
{
|
||||
return m_bSuspend;
|
||||
}
|
||||
INT CBaseThread::IsRunned()
|
||||
{
|
||||
return m_bRunThread;
|
||||
}
|
||||
int CBaseThread::GetError()
|
||||
{
|
||||
return m_lError;
|
||||
}
|
||||
bool CBaseThread::isAborted()
|
||||
{
|
||||
return m_bIsExit && m_bIsExit.load();
|
||||
}
|
||||
|
||||
CThreadDescriptor* CBaseThread::GetDescriptor() { return m_hThread; }
|
||||
int CBaseThread::GetPriority() { return m_lThreadPriority; }
|
||||
CThreadDescriptor* CBaseThread::GetDescriptor()
|
||||
{
|
||||
return m_hThread;
|
||||
}
|
||||
int CBaseThread::GetPriority()
|
||||
{
|
||||
return m_lThreadPriority;
|
||||
}
|
||||
|
||||
void CBaseThread::CheckSuspend()
|
||||
{
|
||||
while (m_bSuspend && m_bRunThread)
|
||||
NSThreads::Sleep(10);
|
||||
}
|
||||
void CBaseThread::CheckSuspend()
|
||||
{
|
||||
while (m_bSuspend && m_bRunThread)
|
||||
NSThreads::Sleep(10);
|
||||
}
|
||||
|
||||
void CBaseThread::Join()
|
||||
{
|
||||
if (NULL == m_hThread)
|
||||
return;
|
||||
void CBaseThread::Join()
|
||||
{
|
||||
if (NULL == m_hThread)
|
||||
return;
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64) || defined(_WIN32_WCE)
|
||||
WaitForSingleObject(((__native_thread*)m_hThread)->m_thread, INFINITE);
|
||||
WaitForSingleObject(((__native_thread*)m_hThread)->m_thread, INFINITE);
|
||||
#else
|
||||
pthread_join(((__native_thread*)m_hThread)->m_thread, 0);
|
||||
pthread_join(((__native_thread*)m_hThread)->m_thread, 0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void CBaseThread::Cancel()
|
||||
{
|
||||
if (NULL == m_hThread)
|
||||
return;
|
||||
void CBaseThread::Cancel()
|
||||
{
|
||||
if (NULL == m_hThread)
|
||||
return;
|
||||
|
||||
m_bIsExit.store(true);
|
||||
m_bIsExit.store(true);
|
||||
|
||||
m_bRunThread = FALSE;
|
||||
m_bRunThread = FALSE;
|
||||
|
||||
Join();
|
||||
RELEASEOBJECT(m_hThread);
|
||||
}
|
||||
}
|
||||
Join();
|
||||
RELEASEOBJECT(m_hThread);
|
||||
}
|
||||
} // namespace NSThreads
|
||||
|
||||
@ -41,7 +41,7 @@
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) || defined (_WIN64)
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
typedef DWORD ASC_THREAD_ID;
|
||||
#else
|
||||
typedef pthread_t ASC_THREAD_ID;
|
||||
@ -51,58 +51,57 @@ typedef pthread_t ASC_THREAD_ID;
|
||||
|
||||
namespace NSThreads
|
||||
{
|
||||
KERNEL_DECL ASC_THREAD_ID GetCurrentThreadId();
|
||||
KERNEL_DECL ASC_THREAD_ID GetCurrentThreadId();
|
||||
|
||||
KERNEL_DECL void Sleep(int nMilliseconds);
|
||||
KERNEL_DECL void Sleep(int nMilliseconds);
|
||||
|
||||
class CThreadDescriptor;
|
||||
class KERNEL_DECL CBaseThread
|
||||
class CThreadDescriptor;
|
||||
class KERNEL_DECL CBaseThread
|
||||
{
|
||||
protected:
|
||||
CThreadDescriptor* m_hThread;
|
||||
INT m_bRunThread;
|
||||
INT m_bSuspend;
|
||||
CThreadDescriptor* m_hThread;
|
||||
INT m_bRunThread;
|
||||
INT m_bSuspend;
|
||||
|
||||
int m_lError;
|
||||
int m_lThreadPriority;
|
||||
int m_lError;
|
||||
int m_lThreadPriority;
|
||||
|
||||
bool m_bIsNeedDestroy;
|
||||
std::atomic<bool> m_bIsExit{false};
|
||||
bool m_bIsNeedDestroy;
|
||||
std::atomic<bool> m_bIsExit{false};
|
||||
|
||||
public:
|
||||
CBaseThread();
|
||||
virtual ~CBaseThread();
|
||||
CBaseThread();
|
||||
virtual ~CBaseThread();
|
||||
|
||||
public:
|
||||
virtual void Start(int lPriority);
|
||||
virtual void Suspend();
|
||||
virtual void Resume();
|
||||
virtual void Stop();
|
||||
virtual void StopNoJoin();
|
||||
virtual void DestroyOnFinish();
|
||||
virtual void Cancel();
|
||||
virtual void Start(int lPriority);
|
||||
virtual void Suspend();
|
||||
virtual void Resume();
|
||||
virtual void Stop();
|
||||
virtual void StopNoJoin();
|
||||
virtual void DestroyOnFinish();
|
||||
virtual void Cancel();
|
||||
|
||||
INT IsSuspended();
|
||||
INT IsRunned();
|
||||
bool isAborted();
|
||||
int GetError();
|
||||
INT IsSuspended();
|
||||
INT IsRunned();
|
||||
bool isAborted();
|
||||
int GetError();
|
||||
|
||||
CThreadDescriptor* GetDescriptor();
|
||||
int GetPriority();
|
||||
|
||||
virtual void CheckSuspend();
|
||||
CThreadDescriptor* GetDescriptor();
|
||||
int GetPriority();
|
||||
|
||||
virtual void CheckSuspend();
|
||||
|
||||
protected:
|
||||
virtual void Join();
|
||||
virtual void Join();
|
||||
virtual DWORD ThreadProc() = 0;
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64) || defined(_WIN32_WCE)
|
||||
static DWORD WINAPI __ThreadProc(void* pv);
|
||||
static DWORD WINAPI __ThreadProc(void* pv);
|
||||
#else
|
||||
static void* __ThreadProc(void* pv);
|
||||
static void* __ThreadProc(void* pv);
|
||||
#endif
|
||||
|
||||
friend class CBaseThreadMonitor;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
#endif // _BUILD_BASETHREAD_H_
|
||||
|
||||
@ -48,6 +48,12 @@ namespace NSThreads
|
||||
m_oCS.DeleteCriticalSection();
|
||||
}
|
||||
|
||||
CBaseThreadMonitor& CBaseThreadMonitor::Get()
|
||||
{
|
||||
static CBaseThreadMonitor instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool CBaseThreadMonitor::Init(void* receiver)
|
||||
{
|
||||
CTemporaryCS oCS(&m_oCS);
|
||||
@ -55,15 +61,21 @@ namespace NSThreads
|
||||
if (m_bIsInit)
|
||||
return false;
|
||||
|
||||
m_bIsInit = true;
|
||||
m_pReceiver = receiver;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBaseThreadMonitor::Destroy()
|
||||
{
|
||||
CTemporaryCS oCS(&m_oCS);
|
||||
|
||||
if (!m_bIsInit)
|
||||
return false;
|
||||
|
||||
m_bIsInit = false;
|
||||
m_pReceiver = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CBaseThreadMonitor::IsInit()
|
||||
@ -83,6 +95,28 @@ namespace NSThreads
|
||||
|
||||
CBaseThread* CBaseThreadMonitor::GetBaseThread(const ASC_THREAD_ID& nThreadId)
|
||||
{
|
||||
// лист - потому что будет всегда мало записей - и то будет быстрее мапа. двигаем всегда текущий на первое место
|
||||
|
||||
if (m_listThreads.size() == 0)
|
||||
return NULL;
|
||||
|
||||
std::list<CBaseThreadInfo>::iterator i = m_listThreads.begin();
|
||||
|
||||
if (i->ID == nThreadId)
|
||||
return i->Instance;
|
||||
|
||||
i++;
|
||||
while (i != m_listThreads.end())
|
||||
{
|
||||
if (i->ID == nThreadId)
|
||||
{
|
||||
m_listThreads.erase(i);
|
||||
m_listThreads.insert(m_listThreads.begin(), *i);
|
||||
return i->Instance;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -94,11 +128,17 @@ namespace NSThreads
|
||||
|
||||
void CBaseThreadMonitor::Register(CBaseThread* pInstance)
|
||||
{
|
||||
CTemporaryCS oCS(&m_oCS);
|
||||
if (!m_bIsInit)
|
||||
return;
|
||||
m_listThreads.push_back({NSThreads::GetCurrentThreadId(), pInstance});
|
||||
}
|
||||
|
||||
void CBaseThreadMonitor::Unregister(CBaseThread* pInstance)
|
||||
{
|
||||
CTemporaryCS oCS(&m_oCS);
|
||||
if (!m_bIsInit)
|
||||
return;
|
||||
for (std::list<CBaseThreadInfo>::iterator i = m_listThreads.begin(); i != m_listThreads.end(); i++)
|
||||
{
|
||||
if (i->Instance == pInstance)
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
#include "BaseThread.h"
|
||||
#include "TemporaryCS.h"
|
||||
#include <functional>
|
||||
#include <list.h>
|
||||
#include <list>
|
||||
|
||||
namespace NSThreads
|
||||
{
|
||||
@ -80,6 +80,8 @@ namespace NSThreads
|
||||
private:
|
||||
void Register(CBaseThread* pInstance);
|
||||
void Unregister(CBaseThread* pInstance);
|
||||
|
||||
friend class CBaseThread;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user