mirror of
https://github.com/0TheSpy/Seaside.git
synced 2025-01-10 03:18:50 +08:00
1270 lines
38 KiB
C++
1270 lines
38 KiB
C++
#include <limits.h>
|
|
#include "threadtools.h"
|
|
#include "refcount.h"
|
|
#include "utllinkedlist.h"
|
|
#include "utlvector.h"
|
|
#include "functors.h"
|
|
|
|
#include "vstdlib.h"
|
|
|
|
#ifndef JOBTHREAD_H
|
|
#define JOBTHREAD_H
|
|
|
|
#ifdef AddJob
|
|
#undef AddJob
|
|
#undef GetJob
|
|
#endif
|
|
|
|
#ifdef VSTDLIB_DLL_EXPORT
|
|
#define JOB_INTERFACE DLL_EXPORT
|
|
#define JOB_OVERLOAD DLL_GLOBAL_EXPORT
|
|
#define JOB_CLASS DLL_CLASS_EXPORT
|
|
#else
|
|
#define JOB_INTERFACE DLL_IMPORT
|
|
#define JOB_OVERLOAD DLL_GLOBAL_IMPORT
|
|
#define JOB_CLASS DLL_CLASS_IMPORT
|
|
#endif
|
|
|
|
#if defined( _WIN32 )
|
|
#pragma once
|
|
#endif
|
|
|
|
class CJob;
|
|
|
|
enum JobStatusEnum_t
|
|
{
|
|
JOB_OK,
|
|
JOB_STATUS_PENDING,
|
|
JOB_STATUS_INPROGRESS,
|
|
JOB_STATUS_ABORTED,
|
|
JOB_STATUS_UNSERVICED,
|
|
};
|
|
|
|
typedef int JobStatus_t;
|
|
|
|
enum JobFlags_t
|
|
{
|
|
JF_IO = (1 << 0),
|
|
JF_BOOST_THREAD = (1 << 1),
|
|
JF_SERIAL = (1 << 2),
|
|
JF_QUEUE = (1 << 3),
|
|
};
|
|
|
|
enum JobPriority_t
|
|
{
|
|
JP_LOW,
|
|
JP_NORMAL,
|
|
JP_HIGH,
|
|
JP_IMMEDIATE,
|
|
|
|
JP_NUM_PRIORITIES,
|
|
|
|
JP_FRAME = JP_NORMAL,
|
|
JP_FRAME_SEGMENT = JP_HIGH,
|
|
};
|
|
|
|
#define TP_MAX_POOL_THREADS 64
|
|
struct ThreadPoolStartParams_t
|
|
{
|
|
ThreadPoolStartParams_t(bool bIOThreads = false, unsigned nThreads = (unsigned)-1, int* pAffinities = NULL, ThreeState_t fDistribute = TRS_NONE, unsigned nStackSize = (unsigned)-1, int iThreadPriority = SHRT_MIN)
|
|
: bIOThreads(bIOThreads), nThreads(nThreads), nThreadsMax(-1), fDistribute(fDistribute), nStackSize(nStackSize), iThreadPriority(iThreadPriority)
|
|
{
|
|
bExecOnThreadPoolThreadsOnly = false;
|
|
#if defined( DEDICATED ) && IsPlatformLinux()
|
|
bEnableOnLinuxDedicatedServer = false;
|
|
#endif
|
|
|
|
bUseAffinityTable = (pAffinities != NULL) && (fDistribute == TRS_TRUE) && (nThreads != (unsigned)-1);
|
|
if (bUseAffinityTable)
|
|
{
|
|
nThreads = MIN(TP_MAX_POOL_THREADS, nThreads);
|
|
for (unsigned int i = 0; i < nThreads; i++)
|
|
{
|
|
iAffinityTable[i] = pAffinities[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
int nThreads;
|
|
int nThreadsMax;
|
|
ThreeState_t fDistribute;
|
|
int nStackSize;
|
|
int iThreadPriority;
|
|
int iAffinityTable[TP_MAX_POOL_THREADS];
|
|
|
|
bool bIOThreads : 1;
|
|
bool bUseAffinityTable : 1;
|
|
bool bExecOnThreadPoolThreadsOnly : 1;
|
|
#if defined( DEDICATED ) && IsPlatformLinux()
|
|
bool bEnableOnLinuxDedicatedServer : 1;
|
|
#endif
|
|
};
|
|
|
|
typedef bool (*JobFilter_t)(CJob*);
|
|
|
|
enum ThreadPoolMessages_t
|
|
{
|
|
TPM_EXIT,
|
|
TPM_SUSPEND,
|
|
};
|
|
|
|
#ifdef Yield
|
|
#undef Yield
|
|
#endif
|
|
|
|
abstract_class IThreadPool : public IRefCounted
|
|
{
|
|
public:
|
|
virtual ~IThreadPool() {};
|
|
|
|
virtual bool Start(const ThreadPoolStartParams_t& startParams = ThreadPoolStartParams_t()) = 0;
|
|
virtual bool Stop(int timeout = TT_INFINITE) = 0;
|
|
|
|
virtual unsigned GetJobCount() = 0;
|
|
virtual int NumThreads() = 0;
|
|
virtual int NumIdleThreads() = 0;
|
|
|
|
virtual int SuspendExecution() = 0;
|
|
virtual int ResumeExecution() = 0;
|
|
|
|
virtual int YieldWait(CThreadEvent** pEvents, int nEvents, bool bWaitAll = true, unsigned timeout = TT_INFINITE) = 0;
|
|
virtual int YieldWait(CJob**, int nJobs, bool bWaitAll = true, unsigned timeout = TT_INFINITE) = 0;
|
|
virtual void Yield(unsigned timeout) = 0;
|
|
|
|
bool YieldWait(CThreadEvent& event, unsigned timeout = TT_INFINITE);
|
|
bool YieldWait(CJob*, unsigned timeout = TT_INFINITE);
|
|
|
|
virtual void AddJob(CJob*) = 0;
|
|
|
|
virtual void AddFunctor(CFunctor* pFunctor, CJob** ppJob = NULL, const char* pszDescription = NULL, unsigned flags = 0) { AddFunctorInternal(RetAddRef(pFunctor), ppJob, pszDescription, flags); }
|
|
|
|
virtual void ChangePriority(CJob* p, JobPriority_t priority) = 0;
|
|
|
|
int ExecuteAll(JobFilter_t pfnFilter = NULL) { return ExecuteToPriority(JP_LOW, pfnFilter); }
|
|
virtual int ExecuteToPriority(JobPriority_t toPriority, JobFilter_t pfnFilter = NULL) = 0;
|
|
virtual int AbortAll() = 0;
|
|
|
|
virtual void AddPerFrameJob(CJob*) = 0;
|
|
|
|
#define DEFINE_NONMEMBER_ADD_CALL(N) \
|
|
template <typename FUNCTION_RETTYPE FUNC_TEMPLATE_FUNC_PARAMS_##N FUNC_TEMPLATE_ARG_PARAMS_##N> \
|
|
CJob *AddCall(FUNCTION_RETTYPE (*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \
|
|
{ \
|
|
CJob *pJob; \
|
|
if ( !NumIdleThreads() ) \
|
|
{ \
|
|
pJob = GetDummyJob(); \
|
|
FunctorDirectCall( pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ); \
|
|
} \
|
|
else \
|
|
{ \
|
|
AddFunctorInternal( CreateFunctor( pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob ); \
|
|
} \
|
|
\
|
|
return pJob; \
|
|
}
|
|
|
|
#define DEFINE_MEMBER_ADD_CALL(N) \
|
|
template <typename OBJECT_TYPE, typename FUNCTION_CLASS, typename FUNCTION_RETTYPE FUNC_TEMPLATE_FUNC_PARAMS_##N FUNC_TEMPLATE_ARG_PARAMS_##N> \
|
|
CJob *AddCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \
|
|
{ \
|
|
CJob *pJob; \
|
|
if ( !NumIdleThreads() ) \
|
|
{ \
|
|
pJob = GetDummyJob(); \
|
|
FunctorDirectCall( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ); \
|
|
} \
|
|
else \
|
|
{ \
|
|
AddFunctorInternal( CreateFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob ); \
|
|
} \
|
|
\
|
|
return pJob; \
|
|
}
|
|
|
|
#define DEFINE_CONST_MEMBER_ADD_CALL(N) \
|
|
template <typename OBJECT_TYPE, typename FUNCTION_CLASS, typename FUNCTION_RETTYPE FUNC_TEMPLATE_FUNC_PARAMS_##N FUNC_TEMPLATE_ARG_PARAMS_##N> \
|
|
CJob *AddCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const FUNC_ARG_FORMAL_PARAMS_##N ) \
|
|
{ \
|
|
CJob *pJob; \
|
|
if ( !NumIdleThreads() ) \
|
|
{ \
|
|
pJob = GetDummyJob(); \
|
|
FunctorDirectCall( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ); \
|
|
} \
|
|
else \
|
|
{ \
|
|
AddFunctorInternal( CreateFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob ); \
|
|
} \
|
|
\
|
|
return pJob; \
|
|
}
|
|
|
|
#define DEFINE_REF_COUNTING_MEMBER_ADD_CALL(N) \
|
|
template <typename OBJECT_TYPE, typename FUNCTION_CLASS, typename FUNCTION_RETTYPE FUNC_TEMPLATE_FUNC_PARAMS_##N FUNC_TEMPLATE_ARG_PARAMS_##N> \
|
|
CJob *AddRefCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \
|
|
{ \
|
|
CJob *pJob; \
|
|
if ( !NumIdleThreads() ) \
|
|
{ \
|
|
pJob = GetDummyJob(); \
|
|
FunctorDirectCall( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ); \
|
|
} \
|
|
else \
|
|
{ \
|
|
AddFunctorInternal( CreateRefCountingFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob ); \
|
|
} \
|
|
\
|
|
return pJob; \
|
|
}
|
|
|
|
#define DEFINE_REF_COUNTING_CONST_MEMBER_ADD_CALL(N) \
|
|
template <typename OBJECT_TYPE, typename FUNCTION_CLASS, typename FUNCTION_RETTYPE FUNC_TEMPLATE_FUNC_PARAMS_##N FUNC_TEMPLATE_ARG_PARAMS_##N> \
|
|
CJob *AddRefCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const FUNC_ARG_FORMAL_PARAMS_##N ) \
|
|
{ \
|
|
CJob *pJob; \
|
|
if ( !NumIdleThreads() ) \
|
|
{ \
|
|
pJob = GetDummyJob(); \
|
|
FunctorDirectCall( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ); \
|
|
} \
|
|
else \
|
|
{ \
|
|
AddFunctorInternal( CreateRefCountingFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob ); \
|
|
} \
|
|
\
|
|
return pJob; \
|
|
}
|
|
|
|
#define DEFINE_NONMEMBER_QUEUE_CALL(N) \
|
|
template <typename FUNCTION_RETTYPE FUNC_TEMPLATE_FUNC_PARAMS_##N FUNC_TEMPLATE_ARG_PARAMS_##N> \
|
|
CJob *QueueCall(FUNCTION_RETTYPE (*pfnProxied)( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \
|
|
{ \
|
|
CJob *pJob; \
|
|
AddFunctorInternal( CreateFunctor( pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob, NULL, JF_QUEUE ); \
|
|
return pJob; \
|
|
}
|
|
|
|
#define DEFINE_MEMBER_QUEUE_CALL(N) \
|
|
template <typename OBJECT_TYPE, typename FUNCTION_CLASS, typename FUNCTION_RETTYPE FUNC_TEMPLATE_FUNC_PARAMS_##N FUNC_TEMPLATE_ARG_PARAMS_##N> \
|
|
CJob *QueueCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \
|
|
{ \
|
|
CJob *pJob; \
|
|
AddFunctorInternal( CreateFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob, NULL, JF_QUEUE ); \
|
|
return pJob; \
|
|
}
|
|
|
|
#define DEFINE_CONST_MEMBER_QUEUE_CALL(N) \
|
|
template <typename OBJECT_TYPE, typename FUNCTION_CLASS, typename FUNCTION_RETTYPE FUNC_TEMPLATE_FUNC_PARAMS_##N FUNC_TEMPLATE_ARG_PARAMS_##N> \
|
|
CJob *QueueCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const FUNC_ARG_FORMAL_PARAMS_##N ) \
|
|
{ \
|
|
CJob *pJob; \
|
|
AddFunctorInternal( CreateFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob, NULL, JF_QUEUE ); \
|
|
return pJob; \
|
|
}
|
|
|
|
#define DEFINE_REF_COUNTING_MEMBER_QUEUE_CALL(N) \
|
|
template <typename OBJECT_TYPE, typename FUNCTION_CLASS, typename FUNCTION_RETTYPE FUNC_TEMPLATE_FUNC_PARAMS_##N FUNC_TEMPLATE_ARG_PARAMS_##N> \
|
|
CJob *QueueRefCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) FUNC_ARG_FORMAL_PARAMS_##N ) \
|
|
{ \
|
|
CJob *pJob; \
|
|
AddFunctorInternal( CreateRefCountingFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob, NULL, JF_QUEUE ); \
|
|
return pJob; \
|
|
}
|
|
|
|
#define DEFINE_REF_COUNTING_CONST_MEMBER_QUEUE_CALL(N) \
|
|
template <typename OBJECT_TYPE, typename FUNCTION_CLASS, typename FUNCTION_RETTYPE FUNC_TEMPLATE_FUNC_PARAMS_##N FUNC_TEMPLATE_ARG_PARAMS_##N> \
|
|
CJob *QueueRefCall(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ) const FUNC_ARG_FORMAL_PARAMS_##N ) \
|
|
{ \
|
|
CJob *pJob; \
|
|
AddFunctorInternal( CreateRefCountingFunctor( pObject, pfnProxied FUNC_FUNCTOR_CALL_ARGS_##N ), &pJob, NULL, JF_QUEUE ); \
|
|
\
|
|
return pJob; \
|
|
}
|
|
|
|
FUNC_GENERATE_ALL(DEFINE_NONMEMBER_ADD_CALL);
|
|
FUNC_GENERATE_ALL(DEFINE_MEMBER_ADD_CALL);
|
|
FUNC_GENERATE_ALL(DEFINE_CONST_MEMBER_ADD_CALL);
|
|
FUNC_GENERATE_ALL(DEFINE_REF_COUNTING_MEMBER_ADD_CALL);
|
|
FUNC_GENERATE_ALL(DEFINE_REF_COUNTING_CONST_MEMBER_ADD_CALL);
|
|
FUNC_GENERATE_ALL(DEFINE_NONMEMBER_QUEUE_CALL);
|
|
FUNC_GENERATE_ALL(DEFINE_MEMBER_QUEUE_CALL);
|
|
FUNC_GENERATE_ALL(DEFINE_CONST_MEMBER_QUEUE_CALL);
|
|
FUNC_GENERATE_ALL(DEFINE_REF_COUNTING_MEMBER_QUEUE_CALL);
|
|
FUNC_GENERATE_ALL(DEFINE_REF_COUNTING_CONST_MEMBER_QUEUE_CALL);
|
|
|
|
#undef DEFINE_NONMEMBER_ADD_CALL
|
|
#undef DEFINE_MEMBER_ADD_CALL
|
|
#undef DEFINE_CONST_MEMBER_ADD_CALL
|
|
#undef DEFINE_REF_COUNTING_MEMBER_ADD_CALL
|
|
#undef DEFINE_REF_COUNTING_CONST_MEMBER_ADD_CALL
|
|
#undef DEFINE_NONMEMBER_QUEUE_CALL
|
|
#undef DEFINE_MEMBER_QUEUE_CALL
|
|
#undef DEFINE_CONST_MEMBER_QUEUE_CALL
|
|
#undef DEFINE_REF_COUNTING_MEMBER_QUEUE_CALL
|
|
#undef DEFINE_REF_COUNTING_CONST_MEMBER_QUEUE_CALL
|
|
|
|
private:
|
|
virtual void AddFunctorInternal(CFunctor*, CJob** = NULL, const char* pszDescription = NULL, unsigned flags = 0) = 0;
|
|
|
|
friend class CJob;
|
|
|
|
virtual CJob* GetDummyJob() = 0;
|
|
|
|
public:
|
|
virtual void Distribute(bool bDistribute = true, int* pAffinityTable = NULL) = 0;
|
|
|
|
virtual bool Start(const ThreadPoolStartParams_t& startParams, const char* pszNameOverride) = 0;
|
|
|
|
virtual int YieldWaitPerFrameJobs() = 0;
|
|
};
|
|
|
|
JOB_INTERFACE IThreadPool* CreateNewThreadPool();
|
|
JOB_INTERFACE void DestroyThreadPool(IThreadPool* pPool);
|
|
|
|
JOB_INTERFACE void RunThreadPoolTests();
|
|
|
|
JOB_INTERFACE IThreadPool* g_pThreadPool;
|
|
#ifdef _X360
|
|
JOB_INTERFACE IThreadPool* g_pAlternateThreadPool;
|
|
#endif
|
|
|
|
DECLARE_POINTER_HANDLE(ThreadPoolData_t);
|
|
#define JOB_NO_DATA ((ThreadPoolData_t)-1)
|
|
|
|
class CJob : public CRefCounted1<IRefCounted, CRefCountServiceMT>
|
|
{
|
|
public:
|
|
CJob(JobPriority_t priority = JP_NORMAL)
|
|
: m_status(JOB_STATUS_UNSERVICED),
|
|
m_ThreadPoolData(JOB_NO_DATA),
|
|
m_priority(priority),
|
|
m_flags(0),
|
|
m_pThreadPool(NULL),
|
|
m_CompleteEvent(true),
|
|
m_iServicingThread(-1)
|
|
{
|
|
}
|
|
|
|
void SetPriority(JobPriority_t priority) { m_priority = priority; }
|
|
JobPriority_t GetPriority() const { return m_priority; }
|
|
|
|
void SetFlags(unsigned flags) { m_flags = flags; }
|
|
unsigned GetFlags() const { return m_flags; }
|
|
|
|
void SetServiceThread(int iServicingThread) { m_iServicingThread = (char)iServicingThread; }
|
|
int GetServiceThread() const { return m_iServicingThread; }
|
|
void ClearServiceThread() { m_iServicingThread = -1; }
|
|
|
|
bool Executed() const { return (m_status == JOB_OK); }
|
|
bool CanExecute() const { return (m_status == JOB_STATUS_PENDING || m_status == JOB_STATUS_UNSERVICED); }
|
|
bool IsFinished() const { return (m_status != JOB_STATUS_PENDING && m_status != JOB_STATUS_INPROGRESS && m_status != JOB_STATUS_UNSERVICED); }
|
|
JobStatus_t GetStatus() const { return m_status; }
|
|
|
|
bool TryLock() { return m_mutex.TryLock(); }
|
|
void Lock() { m_mutex.Lock(); }
|
|
void Unlock() { m_mutex.Unlock(); }
|
|
|
|
bool WaitForFinish(uint32 dwTimeout = TT_INFINITE) { if (!this) return true; return (!IsFinished()) ? g_pThreadPool->YieldWait(this, dwTimeout) : true; }
|
|
bool WaitForFinishAndRelease(uint32 dwTimeout = TT_INFINITE) { if (!this) return true; bool bResult = WaitForFinish(dwTimeout); Release(); return bResult; }
|
|
CThreadEvent* AccessEvent() { return &m_CompleteEvent; }
|
|
|
|
JobStatus_t Execute();
|
|
JobStatus_t TryExecute();
|
|
JobStatus_t ExecuteAndRelease() { JobStatus_t status = Execute(); Release(); return status; }
|
|
JobStatus_t TryExecuteAndRelease() { JobStatus_t status = TryExecute(); Release(); return status; }
|
|
|
|
JobStatus_t Abort(bool bDiscard = true);
|
|
|
|
virtual char const* Describe() { return "Job"; }
|
|
|
|
private:
|
|
friend class CThreadPool;
|
|
|
|
JobStatus_t m_status;
|
|
JobPriority_t m_priority;
|
|
CThreadMutex m_mutex;
|
|
unsigned char m_flags;
|
|
char m_iServicingThread;
|
|
short m_reserved;
|
|
ThreadPoolData_t m_ThreadPoolData;
|
|
IThreadPool* m_pThreadPool;
|
|
CThreadEvent m_CompleteEvent;
|
|
|
|
#if defined( THREAD_PARENT_STACK_TRACE_ENABLED )
|
|
void* m_ParentStackTrace[THREAD_PARENT_STACK_TRACE_LENGTH];
|
|
#endif
|
|
|
|
private:
|
|
CJob(const CJob& fromRequest);
|
|
void operator=(const CJob& fromRequest);
|
|
|
|
virtual JobStatus_t DoExecute() = 0;
|
|
virtual JobStatus_t DoAbort(bool bDiscard) { return JOB_STATUS_ABORTED; }
|
|
virtual void DoCleanup() {}
|
|
};
|
|
|
|
class CFunctorJob : public CJob
|
|
{
|
|
public:
|
|
CFunctorJob(CFunctor* pFunctor, const char* pszDescription = NULL)
|
|
: m_pFunctor(pFunctor)
|
|
{
|
|
if (pszDescription)
|
|
{
|
|
Q_strncpy(m_szDescription, pszDescription, sizeof(m_szDescription));
|
|
}
|
|
else
|
|
{
|
|
m_szDescription[0] = 0;
|
|
}
|
|
}
|
|
|
|
virtual JobStatus_t DoExecute()
|
|
{
|
|
(*m_pFunctor)();
|
|
return JOB_OK;
|
|
}
|
|
|
|
const char* Describe()
|
|
{
|
|
return m_szDescription;
|
|
}
|
|
|
|
private:
|
|
CRefPtr<CFunctor> m_pFunctor;
|
|
char m_szDescription[16];
|
|
};
|
|
|
|
class CJobSet
|
|
{
|
|
public:
|
|
CJobSet(CJob* pJob = NULL)
|
|
{
|
|
if (pJob)
|
|
{
|
|
m_jobs.AddToTail(pJob);
|
|
}
|
|
}
|
|
|
|
CJobSet(CJob** ppJobs, int nJobs)
|
|
{
|
|
if (ppJobs)
|
|
{
|
|
m_jobs.AddMultipleToTail(nJobs, ppJobs);
|
|
}
|
|
}
|
|
|
|
~CJobSet()
|
|
{
|
|
for (int i = 0; i < m_jobs.Count(); i++)
|
|
{
|
|
m_jobs[i]->Release();
|
|
}
|
|
}
|
|
|
|
void operator+=(CJob* pJob)
|
|
{
|
|
m_jobs.AddToTail(pJob);
|
|
}
|
|
|
|
void operator-=(CJob* pJob)
|
|
{
|
|
m_jobs.FindAndRemove(pJob);
|
|
}
|
|
|
|
void Execute(bool bRelease = true)
|
|
{
|
|
for (int i = 0; i < m_jobs.Count(); i++)
|
|
{
|
|
m_jobs[i]->Execute();
|
|
if (bRelease)
|
|
{
|
|
m_jobs[i]->Release();
|
|
}
|
|
}
|
|
|
|
if (bRelease)
|
|
{
|
|
m_jobs.RemoveAll();
|
|
}
|
|
}
|
|
|
|
void Abort(bool bRelease = true)
|
|
{
|
|
for (int i = 0; i < m_jobs.Count(); i++)
|
|
{
|
|
m_jobs[i]->Abort();
|
|
if (bRelease)
|
|
{
|
|
m_jobs[i]->Release();
|
|
}
|
|
}
|
|
|
|
if (bRelease)
|
|
{
|
|
m_jobs.RemoveAll();
|
|
}
|
|
}
|
|
|
|
void WaitForFinish(bool bRelease = true)
|
|
{
|
|
for (int i = 0; i < m_jobs.Count(); i++)
|
|
{
|
|
m_jobs[i]->WaitForFinish();
|
|
if (bRelease)
|
|
{
|
|
m_jobs[i]->Release();
|
|
}
|
|
}
|
|
|
|
if (bRelease)
|
|
{
|
|
m_jobs.RemoveAll();
|
|
}
|
|
}
|
|
|
|
void WaitForFinish(IThreadPool* pPool, bool bRelease = true)
|
|
{
|
|
pPool->YieldWait(m_jobs.Base(), m_jobs.Count());
|
|
|
|
if (bRelease)
|
|
{
|
|
for (int i = 0; i < m_jobs.Count(); i++)
|
|
{
|
|
m_jobs[i]->Release();
|
|
}
|
|
|
|
m_jobs.RemoveAll();
|
|
}
|
|
}
|
|
|
|
private:
|
|
CUtlVectorFixed<CJob*, 16> m_jobs;
|
|
};
|
|
|
|
#define ThreadExecute g_pThreadPool->QueueCall
|
|
#define ThreadExecuteRef g_pThreadPool->QueueRefCall
|
|
|
|
#define BeginExecuteParallel() do { CJobSet jobSet
|
|
#define EndExecuteParallel() jobSet.WaitForFinish( g_pThreadPool ); } while (0)
|
|
|
|
#define ExecuteParallel jobSet += g_pThreadPool->QueueCall
|
|
#define ExecuteRefParallel jobSet += g_pThreadPool->QueueCallRef
|
|
|
|
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4389)
|
|
#pragma warning(disable:4018)
|
|
#pragma warning(disable:4701)
|
|
|
|
#define DEFINE_NON_MEMBER_ITER_RANGE_PARALLEL(N) \
|
|
template <typename FUNCTION_CLASS, typename FUNCTION_RETTYPE FUNC_TEMPLATE_FUNC_PARAMS_##N FUNC_TEMPLATE_ARG_PARAMS_##N, typename ITERTYPE1, typename ITERTYPE2> \
|
|
void IterRangeParallel(FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( ITERTYPE1, ITERTYPE2 FUNC_SEPARATOR_##N FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ), ITERTYPE1 from, ITERTYPE2 to FUNC_ARG_FORMAL_PARAMS_##N ) \
|
|
{ \
|
|
const int MAX_THREADS = 16; \
|
|
int nIdle = g_pThreadPool->NumIdleThreads(); \
|
|
ITERTYPE1 range = to - from; \
|
|
int nThreads = min( nIdle + 1, range ); \
|
|
if ( nThreads > MAX_THREADS ) \
|
|
{ \
|
|
nThreads = MAX_THREADS; \
|
|
} \
|
|
if ( nThreads < 2 ) \
|
|
{ \
|
|
FunctorDirectCall( pfnProxied, from, to FUNC_FUNCTOR_CALL_ARGS_##N ); \
|
|
} \
|
|
else \
|
|
{ \
|
|
ITERTYPE1 nIncrement = range / nThreads; \
|
|
\
|
|
CJobSet jobSet; \
|
|
while ( --nThreads ) \
|
|
{ \
|
|
ITERTYPE2 thisTo = from + nIncrement; \
|
|
jobSet += g_pThreadPool->AddCall( pfnProxied, from, thisTo FUNC_FUNCTOR_CALL_ARGS_##N ); \
|
|
from = thisTo; \
|
|
} \
|
|
FunctorDirectCall( pfnProxied, from, to FUNC_FUNCTOR_CALL_ARGS_##N ); \
|
|
jobSet.WaitForFinish( g_pThreadPool ); \
|
|
} \
|
|
\
|
|
}
|
|
|
|
FUNC_GENERATE_ALL(DEFINE_NON_MEMBER_ITER_RANGE_PARALLEL);
|
|
|
|
#define DEFINE_MEMBER_ITER_RANGE_PARALLEL(N) \
|
|
template <typename OBJECT_TYPE, typename FUNCTION_CLASS, typename FUNCTION_RETTYPE FUNC_TEMPLATE_FUNC_PARAMS_##N FUNC_TEMPLATE_ARG_PARAMS_##N, typename ITERTYPE1, typename ITERTYPE2> \
|
|
void IterRangeParallel(OBJECT_TYPE *pObject, FUNCTION_RETTYPE ( FUNCTION_CLASS::*pfnProxied )( ITERTYPE1, ITERTYPE2 FUNC_SEPARATOR_##N FUNC_BASE_TEMPLATE_FUNC_PARAMS_##N ), ITERTYPE1 from, ITERTYPE2 to FUNC_ARG_FORMAL_PARAMS_##N ) \
|
|
{ \
|
|
const int MAX_THREADS = 16; \
|
|
int nIdle = g_pThreadPool->NumIdleThreads(); \
|
|
ITERTYPE1 range = to - from; \
|
|
int nThreads = min( nIdle + 1, range ); \
|
|
if ( nThreads > MAX_THREADS ) \
|
|
{ \
|
|
nThreads = MAX_THREADS; \
|
|
} \
|
|
if ( nThreads < 2 ) \
|
|
{ \
|
|
FunctorDirectCall( pObject, pfnProxied, from, to FUNC_FUNCTOR_CALL_ARGS_##N ); \
|
|
} \
|
|
else \
|
|
{ \
|
|
ITERTYPE1 nIncrement = range / nThreads; \
|
|
\
|
|
CJobSet jobSet; \
|
|
while ( --nThreads ) \
|
|
{ \
|
|
ITERTYPE2 thisTo = from + nIncrement; \
|
|
jobSet += g_pThreadPool->AddCall( pObject, pfnProxied, from, thisTo FUNC_FUNCTOR_CALL_ARGS_##N ); \
|
|
from = thisTo; \
|
|
} \
|
|
FunctorDirectCall( pObject, pfnProxied, from, to FUNC_FUNCTOR_CALL_ARGS_##N ); \
|
|
jobSet.WaitForFinish( g_pThreadPool ); \
|
|
} \
|
|
\
|
|
}
|
|
|
|
FUNC_GENERATE_ALL(DEFINE_MEMBER_ITER_RANGE_PARALLEL);
|
|
|
|
|
|
template <typename T>
|
|
class CJobItemProcessor
|
|
{
|
|
public:
|
|
typedef T ItemType_t;
|
|
void Begin() {}
|
|
void End() {}
|
|
};
|
|
|
|
template <typename T>
|
|
class CFuncJobItemProcessor : public CJobItemProcessor<T>
|
|
{
|
|
public:
|
|
void Init(void (*pfnProcess)(T&), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL)
|
|
{
|
|
m_pfnProcess = pfnProcess;
|
|
m_pfnBegin = pfnBegin;
|
|
m_pfnEnd = pfnEnd;
|
|
}
|
|
|
|
void Begin() { if (m_pfnBegin) (*m_pfnBegin)(); }
|
|
void Process(T& item) { (*m_pfnProcess)(item); }
|
|
void End() { if (m_pfnEnd) (*m_pfnEnd)(); }
|
|
|
|
protected:
|
|
void (*m_pfnProcess)(T&);
|
|
void (*m_pfnBegin)();
|
|
void (*m_pfnEnd)();
|
|
};
|
|
|
|
template <typename T, class OBJECT_TYPE, class FUNCTION_CLASS = OBJECT_TYPE >
|
|
class CMemberFuncJobItemProcessor : public CJobItemProcessor<T>
|
|
{
|
|
public:
|
|
void Init(OBJECT_TYPE* pObject, void (FUNCTION_CLASS::* pfnProcess)(T&), void (FUNCTION_CLASS::* pfnBegin)() = NULL, void (FUNCTION_CLASS::* pfnEnd)() = NULL)
|
|
{
|
|
m_pObject = pObject;
|
|
m_pfnProcess = pfnProcess;
|
|
m_pfnBegin = pfnBegin;
|
|
m_pfnEnd = pfnEnd;
|
|
}
|
|
|
|
void Begin() { if (m_pfnBegin) ((*m_pObject).*m_pfnBegin)(); }
|
|
void Process(T& item) { ((*m_pObject).*m_pfnProcess)(item); }
|
|
void End() { if (m_pfnEnd) ((*m_pObject).*m_pfnEnd)(); }
|
|
|
|
protected:
|
|
OBJECT_TYPE* m_pObject;
|
|
|
|
void (FUNCTION_CLASS::* m_pfnProcess)(T&);
|
|
void (FUNCTION_CLASS::* m_pfnBegin)();
|
|
void (FUNCTION_CLASS::* m_pfnEnd)();
|
|
};
|
|
|
|
template <typename T>
|
|
class CLoopFuncJobItemProcessor : public CJobItemProcessor<T>
|
|
{
|
|
public:
|
|
void Init(void (*pfnProcess)(T*, int, int), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL)
|
|
{
|
|
m_pfnProcess = pfnProcess;
|
|
m_pfnBegin = pfnBegin;
|
|
m_pfnEnd = pfnEnd;
|
|
}
|
|
|
|
void Begin() { if (m_pfnBegin) (*m_pfnBegin)(); }
|
|
void Process(T* pContext, int nFirst, int nCount) { (*m_pfnProcess)(pContext, nFirst, nCount); }
|
|
void End() { if (m_pfnEnd) (*m_pfnEnd)(); }
|
|
|
|
protected:
|
|
void (*m_pfnProcess)(T*, int, int);
|
|
void (*m_pfnBegin)();
|
|
void (*m_pfnEnd)();
|
|
};
|
|
|
|
template <typename T, class OBJECT_TYPE, class FUNCTION_CLASS = OBJECT_TYPE >
|
|
class CLoopMemberFuncJobItemProcessor : public CJobItemProcessor<T>
|
|
{
|
|
public:
|
|
void Init(OBJECT_TYPE* pObject, void (FUNCTION_CLASS::* pfnProcess)(T*, int, int), void (FUNCTION_CLASS::* pfnBegin)() = NULL, void (FUNCTION_CLASS::* pfnEnd)() = NULL)
|
|
{
|
|
m_pObject = pObject;
|
|
m_pfnProcess = pfnProcess;
|
|
m_pfnBegin = pfnBegin;
|
|
m_pfnEnd = pfnEnd;
|
|
}
|
|
|
|
void Begin() { if (m_pfnBegin) ((*m_pObject).*m_pfnBegin)(); }
|
|
void Process(T* item, int nFirst, int nCount) { ((*m_pObject).*m_pfnProcess)(item, nFirst, nCount); }
|
|
void End() { if (m_pfnEnd) ((*m_pObject).*m_pfnEnd)(); }
|
|
|
|
protected:
|
|
OBJECT_TYPE* m_pObject;
|
|
|
|
void (FUNCTION_CLASS::* m_pfnProcess)(T*, int, int);
|
|
void (FUNCTION_CLASS::* m_pfnBegin)();
|
|
void (FUNCTION_CLASS::* m_pfnEnd)();
|
|
};
|
|
|
|
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4189)
|
|
|
|
template <typename ITEM_TYPE, class ITEM_PROCESSOR_TYPE, int ID_TO_PREVENT_COMDATS_IN_PROFILES = 1>
|
|
class CParallelProcessor
|
|
{
|
|
public:
|
|
CParallelProcessor()
|
|
{
|
|
m_pItems = m_pLimit = 0;
|
|
}
|
|
|
|
void Run(ITEM_TYPE* pItems, unsigned nItems, int nChunkSize = 1, int nMaxParallel = INT_MAX, IThreadPool* pThreadPool = NULL)
|
|
{
|
|
if (nItems == 0)
|
|
return;
|
|
|
|
#if defined(_X360)
|
|
volatile int ignored = ID_TO_PREVENT_COMDATS_IN_PROFILES;
|
|
#endif
|
|
|
|
m_nChunkSize = nChunkSize;
|
|
if (!pThreadPool)
|
|
{
|
|
pThreadPool = g_pThreadPool;
|
|
}
|
|
|
|
m_pItems = pItems;
|
|
m_pLimit = pItems + nItems;
|
|
|
|
int nJobs = nItems - 1;
|
|
|
|
if (nJobs > nMaxParallel)
|
|
{
|
|
nJobs = nMaxParallel;
|
|
}
|
|
|
|
if (!pThreadPool)
|
|
{
|
|
DoExecute();
|
|
return;
|
|
}
|
|
|
|
int nThreads = pThreadPool->NumThreads();
|
|
if (nJobs > nThreads)
|
|
{
|
|
nJobs = nThreads;
|
|
}
|
|
|
|
if (nJobs > 0)
|
|
{
|
|
CJob** jobs = (CJob**)stackalloc(nJobs * sizeof(CJob**));
|
|
int i = nJobs;
|
|
|
|
while (i--)
|
|
{
|
|
jobs[i] = pThreadPool->QueueCall(this, &CParallelProcessor<ITEM_TYPE, ITEM_PROCESSOR_TYPE, ID_TO_PREVENT_COMDATS_IN_PROFILES>::DoExecute);
|
|
}
|
|
|
|
DoExecute();
|
|
|
|
for (i = 0; i < nJobs; i++)
|
|
{
|
|
jobs[i]->Abort();
|
|
jobs[i]->Release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DoExecute();
|
|
}
|
|
}
|
|
|
|
ITEM_PROCESSOR_TYPE m_ItemProcessor;
|
|
|
|
private:
|
|
void DoExecute()
|
|
{
|
|
if (m_pItems < m_pLimit)
|
|
{
|
|
#if defined(_X360)
|
|
volatile int ignored = ID_TO_PREVENT_COMDATS_IN_PROFILES;
|
|
#endif
|
|
m_ItemProcessor.Begin();
|
|
|
|
ITEM_TYPE* pLimit = m_pLimit;
|
|
|
|
int nChunkSize = m_nChunkSize;
|
|
for (;;)
|
|
{
|
|
ITEM_TYPE* pCurrent = m_pItems.AtomicAdd(nChunkSize);
|
|
ITEM_TYPE* pLast = MIN(pLimit, pCurrent + nChunkSize);
|
|
while (pCurrent < pLast)
|
|
{
|
|
m_ItemProcessor.Process(*pCurrent);
|
|
pCurrent++;
|
|
}
|
|
if (pCurrent >= pLimit)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
m_ItemProcessor.End();
|
|
}
|
|
}
|
|
CInterlockedPtr<ITEM_TYPE> m_pItems;
|
|
ITEM_TYPE* m_pLimit;
|
|
int m_nChunkSize;
|
|
|
|
};
|
|
|
|
#pragma warning(pop)
|
|
|
|
template <typename ITEM_TYPE>
|
|
inline void ParallelProcess(ITEM_TYPE* pItems, unsigned nItems, void (*pfnProcess)(ITEM_TYPE&), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL, int nMaxParallel = INT_MAX)
|
|
{
|
|
CParallelProcessor<ITEM_TYPE, CFuncJobItemProcessor<ITEM_TYPE> > processor;
|
|
processor.m_ItemProcessor.Init(pfnProcess, pfnBegin, pfnEnd);
|
|
processor.Run(pItems, nItems, 1, nMaxParallel);
|
|
}
|
|
|
|
template <typename ITEM_TYPE, typename OBJECT_TYPE, typename FUNCTION_CLASS >
|
|
inline void ParallelProcess(ITEM_TYPE* pItems, unsigned nItems, OBJECT_TYPE* pObject, void (FUNCTION_CLASS::* pfnProcess)(ITEM_TYPE&), void (FUNCTION_CLASS::* pfnBegin)() = NULL, void (FUNCTION_CLASS::* pfnEnd)() = NULL, int nMaxParallel = INT_MAX)
|
|
{
|
|
CParallelProcessor<ITEM_TYPE, CMemberFuncJobItemProcessor<ITEM_TYPE, OBJECT_TYPE, FUNCTION_CLASS> > processor;
|
|
processor.m_ItemProcessor.Init(pObject, pfnProcess, pfnBegin, pfnEnd);
|
|
processor.Run(pItems, nItems, 1, nMaxParallel);
|
|
}
|
|
|
|
template <typename ITEM_TYPE>
|
|
inline void ParallelProcess(IThreadPool* pPool, ITEM_TYPE* pItems, unsigned nItems, void (*pfnProcess)(ITEM_TYPE&), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL, int nMaxParallel = INT_MAX)
|
|
{
|
|
CParallelProcessor<ITEM_TYPE, CFuncJobItemProcessor<ITEM_TYPE> > processor;
|
|
processor.m_ItemProcessor.Init(pfnProcess, pfnBegin, pfnEnd);
|
|
processor.Run(pItems, nItems, 1, nMaxParallel, pPool);
|
|
}
|
|
|
|
template <typename ITEM_TYPE, typename OBJECT_TYPE, typename FUNCTION_CLASS >
|
|
inline void ParallelProcess(IThreadPool* pPool, ITEM_TYPE* pItems, unsigned nItems, OBJECT_TYPE* pObject, void (FUNCTION_CLASS::* pfnProcess)(ITEM_TYPE&), void (FUNCTION_CLASS::* pfnBegin)() = NULL, void (FUNCTION_CLASS::* pfnEnd)() = NULL, int nMaxParallel = INT_MAX)
|
|
{
|
|
CParallelProcessor<ITEM_TYPE, CMemberFuncJobItemProcessor<ITEM_TYPE, OBJECT_TYPE, FUNCTION_CLASS> > processor;
|
|
processor.m_ItemProcessor.Init(pObject, pfnProcess, pfnBegin, pfnEnd);
|
|
processor.Run(pItems, nItems, 1, nMaxParallel, pPool);
|
|
}
|
|
|
|
template <typename ITEM_TYPE>
|
|
inline void ParallelProcessChunks(ITEM_TYPE* pItems, unsigned nItems, void (*pfnProcess)(ITEM_TYPE&), int nChunkSize, int nMaxParallel = INT_MAX)
|
|
{
|
|
CParallelProcessor<ITEM_TYPE, CFuncJobItemProcessor<ITEM_TYPE> > processor;
|
|
processor.m_ItemProcessor.Init(pfnProcess, NULL, NULL);
|
|
processor.Run(pItems, nItems, nChunkSize, nMaxParallel);
|
|
}
|
|
|
|
template <typename ITEM_TYPE, typename OBJECT_TYPE, typename FUNCTION_CLASS >
|
|
inline void ParallelProcessChunks(ITEM_TYPE* pItems, unsigned nItems, OBJECT_TYPE* pObject, void (FUNCTION_CLASS::* pfnProcess)(ITEM_TYPE&), int nChunkSize, int nMaxParallel = INT_MAX)
|
|
{
|
|
CParallelProcessor<ITEM_TYPE, CMemberFuncJobItemProcessor<ITEM_TYPE, OBJECT_TYPE, FUNCTION_CLASS> > processor;
|
|
processor.m_ItemProcessor.Init(pObject, pfnProcess, NULL, NULL);
|
|
processor.Run(pItems, nItems, nChunkSize, nMaxParallel);
|
|
}
|
|
|
|
template <typename ITEM_TYPE, typename OBJECT_TYPE, typename FUNCTION_CLASS >
|
|
inline void ParallelProcessChunks(IThreadPool* pPool, ITEM_TYPE* pItems, unsigned nItems, OBJECT_TYPE* pObject, void (FUNCTION_CLASS::* pfnProcess)(ITEM_TYPE&), int nChunkSize, int nMaxParallel = INT_MAX)
|
|
{
|
|
CParallelProcessor<ITEM_TYPE, CMemberFuncJobItemProcessor<ITEM_TYPE, OBJECT_TYPE, FUNCTION_CLASS> > processor;
|
|
processor.m_ItemProcessor.Init(pObject, pfnProcess, NULL, NULL);
|
|
processor.Run(pItems, nItems, nChunkSize, nMaxParallel, pPool);
|
|
}
|
|
|
|
|
|
template <class CONTEXT_TYPE, class ITEM_PROCESSOR_TYPE>
|
|
class CParallelLoopProcessor
|
|
{
|
|
public:
|
|
CParallelLoopProcessor()
|
|
{
|
|
m_nIndex = m_nLimit = 0;
|
|
m_nChunkCount = 0;
|
|
m_nActive = 0;
|
|
}
|
|
|
|
void Run(CONTEXT_TYPE* pContext, int nBegin, int nItems, int nChunkCount, int nMaxParallel = INT_MAX, IThreadPool* pThreadPool = NULL)
|
|
{
|
|
if (!nItems)
|
|
return;
|
|
|
|
if (!pThreadPool)
|
|
{
|
|
pThreadPool = g_pThreadPool;
|
|
}
|
|
|
|
m_pContext = pContext;
|
|
m_nIndex = nBegin;
|
|
m_nLimit = nBegin + nItems;
|
|
nChunkCount = MAX(MIN(nItems, nChunkCount), 1);
|
|
m_nChunkCount = (nItems + nChunkCount - 1) / nChunkCount;
|
|
int nJobs = (nItems + m_nChunkCount - 1) / m_nChunkCount;
|
|
if (nJobs > nMaxParallel)
|
|
{
|
|
nJobs = nMaxParallel;
|
|
}
|
|
|
|
if (!pThreadPool)
|
|
{
|
|
DoExecute();
|
|
return;
|
|
}
|
|
|
|
int nThreads = pThreadPool->NumThreads();
|
|
if (nJobs > nThreads)
|
|
{
|
|
nJobs = nThreads;
|
|
}
|
|
|
|
if (nJobs > 0)
|
|
{
|
|
CJob** jobs = (CJob**)stackalloc(nJobs * sizeof(CJob**));
|
|
int i = nJobs;
|
|
|
|
while (i--)
|
|
{
|
|
jobs[i] = pThreadPool->QueueCall(this, &CParallelLoopProcessor<CONTEXT_TYPE, ITEM_PROCESSOR_TYPE>::DoExecute);
|
|
}
|
|
|
|
DoExecute();
|
|
|
|
for (i = 0; i < nJobs; i++)
|
|
{
|
|
jobs[i]->Abort();
|
|
jobs[i]->Release();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DoExecute();
|
|
}
|
|
}
|
|
|
|
ITEM_PROCESSOR_TYPE m_ItemProcessor;
|
|
|
|
private:
|
|
void DoExecute()
|
|
{
|
|
m_ItemProcessor.Begin();
|
|
for (;;)
|
|
{
|
|
int nIndex = m_nIndex.AtomicAdd(m_nChunkCount);
|
|
if (nIndex < m_nLimit)
|
|
{
|
|
int nCount = MIN(m_nChunkCount, m_nLimit - nIndex);
|
|
m_ItemProcessor.Process(m_pContext, nIndex, nCount);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
m_ItemProcessor.End();
|
|
--m_nActive;
|
|
}
|
|
|
|
CONTEXT_TYPE* m_pContext;
|
|
CInterlockedInt m_nIndex;
|
|
int m_nLimit;
|
|
int m_nChunkCount;
|
|
CInterlockedInt m_nActive;
|
|
};
|
|
|
|
template < typename CONTEXT_TYPE >
|
|
inline void ParallelLoopProcess(IThreadPool* pPool, CONTEXT_TYPE* pContext, int nStart, int nCount, void (*pfnProcess)(CONTEXT_TYPE*, int, int), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL, int nMaxParallel = INT_MAX)
|
|
{
|
|
CParallelLoopProcessor< CONTEXT_TYPE, CLoopFuncJobItemProcessor< CONTEXT_TYPE > > processor;
|
|
processor.m_ItemProcessor.Init(pfnProcess, pfnBegin, pfnEnd);
|
|
processor.Run(pContext, nStart, nCount, 1, nMaxParallel, pPool);
|
|
}
|
|
|
|
template < typename CONTEXT_TYPE, typename OBJECT_TYPE, typename FUNCTION_CLASS >
|
|
inline void ParallelLoopProcess(IThreadPool* pPool, CONTEXT_TYPE* pContext, int nStart, int nCount, OBJECT_TYPE* pObject, void (FUNCTION_CLASS::* pfnProcess)(CONTEXT_TYPE*, int, int), void (FUNCTION_CLASS::* pfnBegin)() = NULL, void (FUNCTION_CLASS::* pfnEnd)() = NULL, int nMaxParallel = INT_MAX)
|
|
{
|
|
CParallelLoopProcessor< CONTEXT_TYPE, CLoopMemberFuncJobItemProcessor<CONTEXT_TYPE, OBJECT_TYPE, FUNCTION_CLASS> > processor;
|
|
processor.m_ItemProcessor.Init(pObject, pfnProcess, pfnBegin, pfnEnd);
|
|
processor.Run(pContext, nStart, nCount, 1, nMaxParallel, pPool);
|
|
}
|
|
|
|
template < typename CONTEXT_TYPE >
|
|
inline void ParallelLoopProcessChunks(IThreadPool* pPool, CONTEXT_TYPE* pContext, int nStart, int nCount, int nChunkSize, void (*pfnProcess)(CONTEXT_TYPE*, int, int), void (*pfnBegin)() = NULL, void (*pfnEnd)() = NULL, int nMaxParallel = INT_MAX)
|
|
{
|
|
CParallelLoopProcessor< CONTEXT_TYPE, CLoopFuncJobItemProcessor< CONTEXT_TYPE > > processor;
|
|
processor.m_ItemProcessor.Init(pfnProcess, pfnBegin, pfnEnd);
|
|
processor.Run(pContext, nStart, nCount, nChunkSize, nMaxParallel, pPool);
|
|
}
|
|
|
|
template < typename CONTEXT_TYPE, typename OBJECT_TYPE, typename FUNCTION_CLASS >
|
|
inline void ParallelLoopProcessChunks(IThreadPool* pPool, CONTEXT_TYPE* pContext, int nStart, int nCount, int nChunkSize, OBJECT_TYPE* pObject, void (FUNCTION_CLASS::* pfnProcess)(CONTEXT_TYPE*, int, int), void (FUNCTION_CLASS::* pfnBegin)() = NULL, void (FUNCTION_CLASS::* pfnEnd)() = NULL, int nMaxParallel = INT_MAX)
|
|
{
|
|
CParallelLoopProcessor< CONTEXT_TYPE, CLoopMemberFuncJobItemProcessor<CONTEXT_TYPE, OBJECT_TYPE, FUNCTION_CLASS> > processor;
|
|
processor.m_ItemProcessor.Init(pObject, pfnProcess, pfnBegin, pfnEnd);
|
|
processor.Run(pContext, nStart, nCount, nChunkSize, nMaxParallel, pPool);
|
|
}
|
|
|
|
template <class Derived>
|
|
class CParallelProcessorBase
|
|
{
|
|
protected:
|
|
typedef CParallelProcessorBase<Derived> ThisParallelProcessorBase_t;
|
|
typedef Derived ThisParallelProcessorDerived_t;
|
|
|
|
public:
|
|
CParallelProcessorBase()
|
|
{
|
|
m_nActive = 0;
|
|
}
|
|
|
|
protected:
|
|
void Run(int nMaxParallel = INT_MAX, int threadOverride = -1)
|
|
{
|
|
int i = g_pThreadPool->NumIdleThreads();
|
|
|
|
if (nMaxParallel < i)
|
|
{
|
|
i = nMaxParallel;
|
|
}
|
|
|
|
while (i-- > 0)
|
|
{
|
|
if (threadOverride == -1 || i == threadOverride - 1)
|
|
{
|
|
++m_nActive;
|
|
ThreadExecute(this, &ThisParallelProcessorBase_t::DoExecute)->Release();
|
|
}
|
|
}
|
|
|
|
if (threadOverride == -1 || threadOverride == 0)
|
|
{
|
|
++m_nActive;
|
|
DoExecute();
|
|
}
|
|
|
|
while (m_nActive)
|
|
{
|
|
ThreadPause();
|
|
}
|
|
}
|
|
|
|
protected:
|
|
void OnBegin() {}
|
|
bool OnProcess() { return false; }
|
|
void OnEnd() {}
|
|
|
|
private:
|
|
void DoExecute()
|
|
{
|
|
static_cast<Derived*>(this)->OnBegin();
|
|
|
|
while (static_cast<Derived*>(this)->OnProcess())
|
|
continue;
|
|
|
|
static_cast<Derived*>(this)->OnEnd();
|
|
|
|
--m_nActive;
|
|
}
|
|
|
|
CInterlockedInt m_nActive;
|
|
};
|
|
|
|
|
|
|
|
|
|
inline uintp FunctorExecuteThread(void* pParam)
|
|
{
|
|
CFunctor* pFunctor = (CFunctor*)pParam;
|
|
(*pFunctor)();
|
|
pFunctor->Release();
|
|
return 0;
|
|
}
|
|
|
|
inline ThreadHandle_t ThreadExecuteSoloImpl(CFunctor* pFunctor, const char* pszName = NULL);
|
|
|
|
|
|
inline ThreadHandle_t ThreadExecuteSolo(CJob* pJob) { return ThreadExecuteSoloImpl(CreateFunctor(pJob, &CJob::Execute), pJob->Describe()); }
|
|
|
|
template <typename T1>
|
|
inline ThreadHandle_t ThreadExecuteSolo(const char* pszName, T1 a1) { return ThreadExecuteSoloImpl(CreateFunctor(a1), pszName); }
|
|
|
|
template <typename T1, typename T2>
|
|
inline ThreadHandle_t ThreadExecuteSolo(const char* pszName, T1 a1, T2 a2) { return ThreadExecuteSoloImpl(CreateFunctor(a1, a2), pszName); }
|
|
|
|
template <typename T1, typename T2, typename T3>
|
|
inline ThreadHandle_t ThreadExecuteSolo(const char* pszName, T1 a1, T2 a2, T3 a3) { return ThreadExecuteSoloImpl(CreateFunctor(a1, a2, a3), pszName); }
|
|
|
|
template <typename T1, typename T2, typename T3, typename T4>
|
|
inline ThreadHandle_t ThreadExecuteSolo(const char* pszName, T1 a1, T2 a2, T3 a3, T4 a4) { return ThreadExecuteSoloImpl(CreateFunctor(a1, a2, a3, a4), pszName); }
|
|
|
|
template <typename T1, typename T2, typename T3, typename T4, typename T5>
|
|
inline ThreadHandle_t ThreadExecuteSolo(const char* pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5) { return ThreadExecuteSoloImpl(CreateFunctor(a1, a2, a3, a4, a5), pszName); }
|
|
|
|
template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
|
|
inline ThreadHandle_t ThreadExecuteSolo(const char* pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6) { return ThreadExecuteSoloImpl(CreateFunctor(a1, a2, a3, a4, a5, a6), pszName); }
|
|
|
|
template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
|
|
inline ThreadHandle_t ThreadExecuteSolo(const char* pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7) { return ThreadExecuteSoloImpl(CreateFunctor(a1, a2, a3, a4, a5, a6, a7), pszName); }
|
|
|
|
template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
|
|
inline ThreadHandle_t ThreadExecuteSolo(const char* pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8) { return ThreadExecuteSoloImpl(CreateFunctor(a1, a2, a3, a4, a5, a6, a7, a8), pszName); }
|
|
|
|
template <typename T1, typename T2>
|
|
inline ThreadHandle_t ThreadExecuteSoloRef(const char* pszName, T1 a1, T2 a2) { return ThreadExecuteSoloImpl(CreateRefCountingFunctor(a1, a2), pszName); }
|
|
|
|
template <typename T1, typename T2, typename T3>
|
|
inline ThreadHandle_t ThreadExecuteSoloRef(const char* pszName, T1 a1, T2 a2, T3 a3) { return ThreadExecuteSoloImpl(CreateRefCountingFunctor(a1, a2, a3), pszName); }
|
|
|
|
template <typename T1, typename T2, typename T3, typename T4>
|
|
inline ThreadHandle_t ThreadExecuteSoloRef(const char* pszName, T1 a1, T2 a2, T3 a3, T4 a4) { return ThreadExecuteSoloImpl(CreateRefCountingFunctor(a1, a2, a3, a4), pszName); }
|
|
|
|
template <typename T1, typename T2, typename T3, typename T4, typename T5>
|
|
inline ThreadHandle_t ThreadExecuteSoloRef(const char* pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5) { return ThreadExecuteSoloImpl(CreateRefCountingFunctor(a1, a2, a3, a4, a5), pszName); }
|
|
|
|
template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
|
|
inline ThreadHandle_t ThreadExecuteSoloRef(const char* pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6) { return ThreadExecuteSoloImpl(CreateRefCountingFunctor(a1, a2, a3, a4, a5, a6), pszName); }
|
|
|
|
template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
|
|
inline ThreadHandle_t ThreadExecuteSoloRef(const char* pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7) { return ThreadExecuteSoloImpl(CreateRefCountingFunctor(a1, a2, a3, a4, a5, a6, a7), pszName); }
|
|
|
|
template <typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
|
|
inline ThreadHandle_t ThreadExecuteSoloRef(const char* pszName, T1 a1, T2 a2, T3 a3, T4 a4, T5 a5, T6 a6, T7 a7, T8 a8) { return ThreadExecuteSoloImpl(CreateRefCountingFunctor(a1, a2, a3, a4, a5, a6, a7, a8), pszName); }
|
|
|
|
inline bool IThreadPool::YieldWait(CThreadEvent& theEvent, unsigned timeout)
|
|
{
|
|
CThreadEvent* pEvent = &theEvent;
|
|
return (YieldWait(&pEvent, 1, true, timeout) != TW_TIMEOUT);
|
|
}
|
|
|
|
inline bool IThreadPool::YieldWait(CJob* pJob, unsigned timeout)
|
|
{
|
|
return (YieldWait(&pJob, 1, true, timeout) != TW_TIMEOUT);
|
|
}
|
|
|
|
inline JobStatus_t CJob::Execute()
|
|
{
|
|
if (IsFinished())
|
|
{
|
|
return m_status;
|
|
}
|
|
|
|
AUTO_LOCK(m_mutex);
|
|
AddRef();
|
|
|
|
JobStatus_t result;
|
|
|
|
switch (m_status)
|
|
{
|
|
case JOB_STATUS_UNSERVICED:
|
|
case JOB_STATUS_PENDING:
|
|
{
|
|
m_status = JOB_STATUS_INPROGRESS;
|
|
|
|
#if defined( THREAD_PARENT_STACK_TRACE_ENABLED )
|
|
{
|
|
CStackTop_ReferenceParentStack stackTop(m_ParentStackTrace, ARRAYSIZE(m_ParentStackTrace));
|
|
result = m_status = DoExecute();
|
|
}
|
|
#else
|
|
result = m_status = DoExecute();
|
|
#endif
|
|
|
|
DoCleanup();
|
|
m_CompleteEvent.Set();
|
|
break;
|
|
}
|
|
|
|
case JOB_STATUS_INPROGRESS:
|
|
AssertMsg(0, "Mutex Should have protected use while processing");
|
|
case JOB_OK:
|
|
case JOB_STATUS_ABORTED:
|
|
result = m_status;
|
|
break;
|
|
|
|
default:
|
|
AssertMsg(m_status < JOB_OK, "Unknown job state");
|
|
result = m_status;
|
|
}
|
|
|
|
Release();
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
inline JobStatus_t CJob::TryExecute()
|
|
{
|
|
if (!IsFinished() && TryLock())
|
|
{
|
|
Execute();
|
|
Unlock();
|
|
}
|
|
return m_status;
|
|
}
|
|
|
|
inline JobStatus_t CJob::Abort(bool bDiscard)
|
|
{
|
|
if (IsFinished())
|
|
{
|
|
return m_status;
|
|
}
|
|
|
|
AUTO_LOCK(m_mutex);
|
|
AddRef();
|
|
|
|
JobStatus_t result;
|
|
|
|
switch (m_status)
|
|
{
|
|
case JOB_STATUS_UNSERVICED:
|
|
case JOB_STATUS_PENDING:
|
|
{
|
|
result = m_status = DoAbort(bDiscard);
|
|
if (bDiscard)
|
|
DoCleanup();
|
|
m_CompleteEvent.Set();
|
|
}
|
|
break;
|
|
|
|
case JOB_STATUS_ABORTED:
|
|
case JOB_STATUS_INPROGRESS:
|
|
case JOB_OK:
|
|
result = m_status;
|
|
break;
|
|
|
|
default:
|
|
AssertMsg(m_status < JOB_OK, "Unknown job state");
|
|
result = m_status;
|
|
}
|
|
|
|
Release();
|
|
|
|
return result;
|
|
}
|
|
|
|
#endif |