//+-------------------------------------------------------------------------- // // Copyright (c) 1997-1999 Microsoft Corporation // // File: jobmgr.h // // Contents: // // History: // //--------------------------------------------------------------------------- #ifndef __WORKMANAGER_H__ #define __WORKMANAGER_H__ #include #include #include "tlsstl.h" #include "dbgout.h" #include "locks.h" #include "tlsassrt.h" #include "license.h" #include "tlsapip.h" #include "tlspol.h" // // Default cancel timeout is 5 seconds // #define DEFAULT_RPCCANCEL_TIMEOUT 5 // // Default interval time is 15 mins. // #define DEFAULT_WORK_INTERVAL 15*60*1000 // // Default shutdown wait time // #define DEFAULT_SHUTDOWN_TIME 60*2*1000 // // Max. Number of concurrent Jobs // #define DEFAULT_NUM_CONCURRENTJOB 50 // // // #define WORKMANAGER_TIMER_PERIOD_TIMER 0xFFFFFFFF // see RtlUpdateTimer() #define WORKMANAGER_WAIT_FOREVER INFINITE #define CLASS_PRIVATE #define CLASS_STATIC class CWorkManager; class CWorkObject; #ifdef __TEST_WORKMGR__ #define DBGCONSOLE GetStdHandle(STD_OUTPUT_HANDLE) #else #define DBGCONSOLE NULL #endif //-------------------------------------------------------------- // // Work Object initialization function, each work object // must supply its own initialization routine to work // manager. // typedef enum { JOBDURATION_UNKNOWN=0, JOBDURATION_RUNONCE, // Run Once Work JOBDURATION_SESSION, // Session Job JOBDURATION_PERSISTENT // Persistent Job } JOBDURATION; #define JOB_SHORT_LIVE 0x00000001 #define JOB_INCLUDES_IO 0x00000002 #define JOB_LONG_RUNNING 0x00000004 #define WORK_TYPE_UNKNOWN 0x00000000 #ifndef AllocateMemory #define AllocateMemory(size) \ LocalAlloc(LPTR, size) #endif #ifndef FreeMemory #define FreeMemory(ptr) \ if(ptr) \ { \ LocalFree(ptr); \ ptr=NULL; \ } #endif #ifndef ReallocateMemory #define ReallocateMemory(ptr, size) \ LocalReAlloc(ptr, size, LMEM_ZEROINIT) #endif //------------------------------------------------------ // class MyCSemaphore { private: HANDLE m_semaphore; long m_TryEntry; long m_Acquired; long m_Max; public: MyCSemaphore() : m_semaphore(NULL), m_TryEntry(0), m_Acquired(0), m_Max(0) {} //-------------------------------------------------- const long GetTryEntryCount() { return m_TryEntry; } //-------------------------------------------------- const long GetAcquiredCount() { return m_Acquired; } //-------------------------------------------------- const long GetMaxCount() { return m_Max; } //-------------------------------------------------- BOOL Init( LONG lInitCount, LONG lMaxCount ) /*++ --*/ { m_semaphore=CreateSemaphore( NULL, lInitCount, lMaxCount, NULL ); m_Max = lMaxCount; m_TryEntry = 0; m_Acquired = 0; TLSASSERT(m_semaphore != NULL); return m_semaphore != NULL; } //-------------------------------------------------- ~MyCSemaphore() { TLSASSERT(m_Acquired == 0); TLSASSERT(m_TryEntry == 0); if(m_semaphore) { CloseHandle(m_semaphore); } } //-------------------------------------------------- BOOL AcquireEx( HANDLE hHandle, DWORD dwWaitTime=INFINITE, BOOL bAlertable=FALSE ) /*++ --*/ { BOOL bSuccess = TRUE; DWORD dwStatus; HANDLE hHandles[] = {m_semaphore, hHandle}; TLSASSERT(IsGood() == TRUE); if(hHandle == NULL || hHandle == INVALID_HANDLE_VALUE) { SetLastError(ERROR_INVALID_PARAMETER); bSuccess = FALSE; } else { InterlockedIncrement(&m_TryEntry); dwStatus = WaitForMultipleObjectsEx( sizeof(hHandles)/sizeof(hHandles[0]), hHandles, FALSE, dwWaitTime, bAlertable ); if(dwStatus == WAIT_OBJECT_0) { InterlockedIncrement(&m_Acquired); } else { bSuccess = FALSE; } InterlockedDecrement(&m_TryEntry); } return bSuccess; } //-------------------------------------------------- DWORD Acquire( DWORD dwWaitTime=INFINITE, BOOL bAlertable=FALSE ) /*++ --*/ { DWORD dwStatus; TLSASSERT(IsGood() == TRUE); InterlockedIncrement(&m_TryEntry); dwStatus = WaitForSingleObjectEx( m_semaphore, dwWaitTime, bAlertable ); if(dwStatus == WAIT_OBJECT_0) { InterlockedIncrement(&m_Acquired); } InterlockedDecrement(&m_TryEntry); return dwStatus; } //-------------------------------------------------- BOOL Release( long count=1 ) /*++ --*/ { BOOL bSuccess; TLSASSERT(IsGood() == TRUE); bSuccess = ReleaseSemaphore( m_semaphore, count, NULL ); if(bSuccess == TRUE) { InterlockedDecrement(&m_Acquired); } return bSuccess; } //-------------------------------------------------- BOOL IsGood() /*++ --*/ { return m_semaphore != NULL; } //-------------------------------------------------- const HANDLE GetHandle() { return m_semaphore; } }; //------------------------------------------------------------- // // Pure virtual base class for CWorkManager to store persistent // work object. // typedef enum { ENDPROCESSINGJOB_RETURN=0, // unable to process job, wait for next term. ENDPROCESSINGJOB_SUCCESS, // job completed. ENDPROCESSINGJOB_ERROR // error in processing this job } ENDPROCESSINGJOB_CODE; class CWorkStorage { friend class CWorkManager; protected: CWorkManager* m_pWkMgr; public: CWorkStorage( CWorkManager* pWkMgr=NULL ) : m_pWkMgr(pWkMgr) {} ~CWorkStorage() {} //--------------------------------------------------- CWorkManager* GetWorkManager() { return m_pWkMgr; } //--------------------------------------------------- virtual BOOL Startup( IN CWorkManager* pWkMgr ) /*++ --*/ { if(pWkMgr != NULL) { m_pWkMgr = pWkMgr; } else { SetLastError(ERROR_INVALID_PARAMETER); } return pWkMgr != NULL; } //--------------------------------------------------- virtual BOOL Shutdown() = 0; virtual BOOL AddJob( IN DWORD dwTime, // relative to current time IN CWorkObject* ptr // Pointer to work object ) = 0; //virtual BOOL //JobEnumBegin( // DWORD dwLowScheduleTime=0, // DWORD dwHighScheduleTime=0 //) = 0; // // Return time to next job virtual DWORD GetNextJobTime() = 0; // // return job to be processed next virtual CWorkObject* GetNextJob(PDWORD pdwTime) = 0; // // Inform storage that we are processing this job virtual BOOL BeginProcessingJob( IN CWorkObject* pJob ) = 0; // Inform storage that this job has completed virtual BOOL EndProcessingJob( IN ENDPROCESSINGJOB_CODE opCode, IN DWORD dwOriginalScheduledTime, IN CWorkObject* pJob ) = 0; //virtual BOOL //JobEnumEnd() = 0; virtual DWORD GetNumJobs() = 0; }; //------------------------------------------------------------- // typedef struct _ScheduleJob { DWORD m_ulScheduleTime; // absolute time CWorkObject* m_pWorkObject; } SCHEDULEJOB, *PSCHEDULEJOB, *LPSCHEDULEJOB; inline bool operator<( const struct _ScheduleJob& a, const struct _ScheduleJob& b ) /*++ --*/ { return a.m_ulScheduleTime < b.m_ulScheduleTime; } //------------------------------------------------------------- // // TODO : Re-design our in-memory job as a plugin like persistent // Job. // //------------------------------------------------------------- class CWorkManager { friend class CWorkObject; private: typedef struct { BOOL bProcessInMemory; CWorkManager* pWorkMgr; } WorkManagerProcessContext, *PWorkManagerProcessContext; // // Schedule job might be at the same time, so use multimap // TODO : Need to move this into template. // // All in memory job schedule time are in absolute time // typedef multimap SCHEDULEJOBMAP; SCHEDULEJOBMAP m_Jobs; // schedule jobs. CRWLock m_JobLock; // Schedule Job Lock typedef struct { long m_refCounter; HANDLE m_hThread; } WorkMangerInProcessJob; typedef map INPROCESSINGJOBLIST; CCriticalSection m_InProcessingListLock; INPROCESSINGJOBLIST m_InProcessingList; HANDLE m_hJobInProcessing; // signal if no job, non-signal // if job currently in process HANDLE m_hWorkMgrThread; HANDLE m_hNewJobArrive; HANDLE m_hShutdown; // shutdown timer. HANDLE m_hInStorageWait; // relative time to next schedule job //CCriticalSection m_JobTimeLock; //CMyCounter m_dwNextInStorageJobTime; //CMyCounter m_dwNextInMemoryJobTime; CSafeCounter m_dwNextInStorageJobTime; CSafeCounter m_dwNextInMemoryJobTime; //DWORD m_dwNextInMemoryJobTime; // Absolute time. //DWORD m_dwNextInStorageJobTime; // Absolute time. long m_NumJobInProcess; // // Default interval to process job DWORD m_dwDefaultInterval; // Max. concurrent job, not use DWORD m_dwMaxCurrentJob; MyCSemaphore m_hMaxJobLock; CWorkStorage* m_pPersistentWorkStorage; private: //------------------------------------------------------------- DWORD AddJobToProcessingList( CWorkObject* ptr ); //------------------------------------------------------------- DWORD RemoveJobFromProcessingList( CWorkObject* ptr ); //------------------------------------------------------------- DWORD ProcessScheduledJob(); //------------------------------------------------------------- BOOL SignalJobArrive() { return SetEvent(m_hNewJobArrive); } //------------------------------------------------------------- BOOL WaitForObjectOrShutdown( HANDLE hHandle ); //------------------------------------------------------------- DWORD RunJob( IN CWorkObject* ptr, IN BOOL bImmediate ); //------------------------------------------------------------- void EndProcessingScheduledJob( IN CWorkObject* ptr ) /*++ --*/ { RemoveJobFromProcessingList(ptr); return; } //------------------------------------------------------------- void DeleteAllJobsInMemoryQueue(); //------------------------------------------------------------- void CancelInProcessingJob(); //------------------------------------------------------------- BOOL SignalJobRunning( CWorkObject* ptr ); //------------------------------------------------------------- CWorkObject* GetNextJobInMemoryQueue( PDWORD pulTime ); //------------------------------------------------------------- BOOL RemoveJobFromInMemoryQueue( IN DWORD ulJobTime, IN CWorkObject* ptr ); //------------------------------------------------------------- DWORD AddJobIntoMemoryQueue( DWORD ulTime, CWorkObject* pWork ); //------------------------------------------------------------- BOOL IsShuttingDown() { if(m_hShutdown == NULL) { return TRUE; } return (WaitForSingleObject( m_hShutdown, 0 ) == WAIT_OBJECT_0); } //------------------------------------------------------------- static DWORD WINAPI ProcessInMemoryScheduledJob(PVOID); //------------------------------------------------------------- static DWORD WINAPI ProcessInStorageScheduledJob(PVOID); //------------------------------------------------------------- static unsigned int __stdcall WorkManagerThread(PVOID); //------------------------------------------------------------- static DWORD WINAPI ExecuteWorkObject(PVOID); //------------------------------------------------------------- DWORD GetTimeToNextJob(); //------------------------------------------------------------- void AddJobUpdateInMemoryJobWaitTimer( DWORD dwJobTime ) /*++ --*/ { //m_JobTimeLock.Lock(); if((DWORD)m_dwNextInMemoryJobTime > dwJobTime) { m_dwNextInMemoryJobTime = dwJobTime; } //m_JobTimeLock.UnLock(); return; } //------------------------------------------------------------- void AddJobUpdateInStorageJobWaitTimer( DWORD dwJobTime ) /*++ --*/ { //m_JobTimeLock.Lock(); if((DWORD)m_dwNextInStorageJobTime > dwJobTime) { m_dwNextInStorageJobTime = dwJobTime; } //m_JobTimeLock.UnLock(); return; } //------------------------------------------------------------- BOOL UpdateTimeToNextPersistentJob() /*++ --*/ { BOOL bSuccess = TRUE; // // Work Manager thread are processing storage job, don't // Update the storage job timer. // TLSASSERT(m_pPersistentWorkStorage != NULL); if(m_pPersistentWorkStorage->GetNumJobs() > 0) { m_dwNextInStorageJobTime = m_pPersistentWorkStorage->GetNextJobTime(); } return bSuccess; } //------------------------------------------------------------ BOOL UpdateTimeToNextInMemoryJob() /*++ Must have called m_JobTimeLock.Lock(); --*/ { BOOL bSuccess = TRUE; SCHEDULEJOBMAP::iterator it; m_JobLock.Acquire(READER_LOCK); it = m_Jobs.begin(); if(it != m_Jobs.end()) { m_dwNextInMemoryJobTime = (*it).first; } else { m_dwNextInMemoryJobTime = WORKMANAGER_WAIT_FOREVER; } m_JobLock.Release(READER_LOCK); return bSuccess; } //------------------------------------------------------------- DWORD TranslateJobRunningAttributeToThreadPoolFlag( DWORD dwJobAttribute ) /*++ --*/ { DWORD dwThreadPoolFlag = 0; if(dwJobAttribute & JOB_LONG_RUNNING) { dwThreadPoolFlag |= WT_EXECUTELONGFUNCTION; } else if(dwJobAttribute & JOB_INCLUDES_IO) { dwThreadPoolFlag |= WT_EXECUTEINIOTHREAD; } else { dwThreadPoolFlag = WT_EXECUTEDEFAULT; // = 0 } return dwThreadPoolFlag; } public: //------------------------------------------------ // // Constructor, only initialize member variable, must // invokd Init() // CWorkManager(); //------------------------------------------------ // Destructor. ~CWorkManager(); //------------------------------------------------ // // Startup Work Manager. // DWORD Startup( IN CWorkStorage* pPersistentWorkStorage, IN DWORD dwInterval = DEFAULT_WORK_INTERVAL, IN DWORD dwMaxConcurrentJob=DEFAULT_NUM_CONCURRENTJOB ); //------------------------------------------------ // // Schedule a Job // DWORD ScheduleJob( IN DWORD dwTime, // relative to current time. IN CWorkObject* pJob ); //------------------------------------------------ // // Shutdown WorkManager // void Shutdown(); //------------------------------------------------ // // inline DWORD GetNumberJobInMemoryQueue() { DWORD dwNumJob = 0; m_JobLock.Acquire(READER_LOCK); dwNumJob = m_Jobs.size(); m_JobLock.Release(READER_LOCK); return dwNumJob; } //------------------------------------------------------------- inline DWORD GetNumberJobInStorageQueue() { return m_pPersistentWorkStorage->GetNumJobs(); } //------------------------------------------------------------- DWORD GetNumberJobInProcessing() { DWORD dwNumJobs; m_InProcessingListLock.Lock(); dwNumJobs = m_InProcessingList.size(); m_InProcessingListLock.UnLock(); return dwNumJobs; } //------------------------------------------------------------- DWORD GetTotalNumberJobInQueue() { return GetNumberJobInMemoryQueue() + GetNumberJobInStorageQueue(); } //------------------------------------------------------------- #ifdef DBG void SuspendWorkManagerThread() { SuspendThread(m_hWorkMgrThread); }; void ResumeWorkManagerThread() { ResumeThread(m_hWorkMgrThread); }; #endif }; //------------------------------------------------------------- class CWorkObject { friend class CWorkManager; private: CWorkManager* m_pWkMgr; long m_refCount; // reference counter DWORD m_dwLastRunStatus; // status from last Execute(). BOOL m_bCanBeFree; // TRUE if work manager should call // SelfDestruct(). DWORD m_dwScheduledTime; // time schedule to be processed by // work manager // // Private function invoke only by CWorkManager // long GetReferenceCount(); void IncrementRefCount(); void DecrementRefCount(); void ExecuteWorkObject(); void EndExecuteWorkObject(); //------------------------------------------------------------ // virtual void SetScheduledTime( IN DWORD dwTime ) /*++ Abstract: Set original scheduled processing time, this is call by work manager Parameter: dwTime : absolute scheduled time in second Returns: None. --*/ { m_dwScheduledTime = dwTime; return; } protected: CWorkManager* GetWorkManager() { return m_pWkMgr; } BOOL CanBeDelete() { return m_bCanBeFree; } public: //------------------------------------------------------------ // // Constructor // CWorkObject( IN BOOL bDestructorDelete = TRUE ); //------------------------------------------------------------ // // Destructor // ~CWorkObject() { Cleanup(); } //------------------------------------------------------------ // BOOL IsWorkManagerShuttingDown() { return (m_pWkMgr != NULL) ? m_pWkMgr->IsShuttingDown() : TRUE; } //------------------------------------------------------------ // TODO - quick fix, persistent storage can't assign this. void SetProcessingWorkManager( IN CWorkManager* pWkMgr ) /*++ --*/ { m_pWkMgr = pWkMgr; } //------------------------------------------------------------ // virtual DWORD GetJobRestartTime() /* Abstract: Return suggested re-start time after server has been shutdown/restart, this is used by work storage class only. Parameter: None. Returns: Time in second relative to current time. --*/ { return INFINITE; } //------------------------------------------------------------ // virtual DWORD GetScheduledTime() /*++ Abstract: Get Job's scheduled time. Parameter: None: Returns: Absolute scheduled time in seconds. --*/ { return m_dwScheduledTime; } //------------------------------------------------------------ // // Abstract: // // Initialize work object, similar to constructor. // virtual DWORD Init( IN BOOL bDestructorDelete = TRUE ); //------------------------------------------------------------ // virtual BOOL IsWorkPersistent() /*++ Abstract: Return if this is persistent job - across session. Parameter: None. Returns: TRUE/FALSE. --*/ { return FALSE; } //------------------------------------------------------------ // virtual BOOL IsValid() /*++ Abstract: Return if this object has been properly initialized. Parameter: None: Returns: TRUE/FALSE --*/ { return m_pWkMgr != NULL; } //------------------------------------------------------------ // virtual void Cleanup() /*++ Abstract: Cleanup internal data in this object. Parameter: None. Returns: None. --*/ { InterlockedExchange(&m_refCount, 0); return; } //------------------------------------------------------------ // // Abstract: // // Pure virtual function to return type of work // // Parameter: // // None. // // Returns: // // Derived class dependent. // virtual DWORD GetWorkType() = 0; //------------------------------------------------------------ // virtual BOOL SetWorkType( IN DWORD dwType ) /*++ Abstract: Set the type of work for this object, not call by any of work manager function. Parameter: dwType : Type of work. return: TRUE/FALSE. --*/ { SetLastError(ERROR_INVALID_DATA); return FALSE; } //----------------------------------------------------------- // // Abstract: // // pure virtual function to return work object specific data. // // Parameters: // // ppbData : Pointer to pointer to buffer to receive object // specific work data. // pcbData : Pointer to DWORD to receive size of object specific // work data. // // Returns: // // TRUE/FALSE, all derived class specific. virtual BOOL GetWorkObjectData( OUT PBYTE* ppbData, OUT PDWORD pcbData ) = 0; //----------------------------------------------------------- // // Abstract: // // Pure virtual function for work storage class to assign // storage ID. // // Parameters: // // pbData : Work Storage assigned storage ID. // cbData : size of storage ID. // // Returns: // // TRUE/FALSE, derived class specific. // virtual BOOL SetJobId( IN PBYTE pbData, IN DWORD cbData ) = 0; //----------------------------------------------------------- // // Abstract: // // Pure virtual function to return storage ID assigned by // storage class. // // parameter: // // ppbData : Pointer to pointer to buffer to receive storage ID. // pcbData : size of storage ID. // // Returns: // // TRUE/FALSE, derived class specific. // virtual BOOL GetJobId( OUT PBYTE* ppbData, OUT PDWORD pcbData ) = 0; //------------------------------------------------------------- // // Abstract: // // Virtual function, execute a job. // // Parameters: // // None. // // Returns: // // None // virtual DWORD Execute() = 0; //------------------------------------------------------------- // // Abstract : // // Schedule a job at relative time // // Parameters: // // pftStartTime : Time relative to current system time, if NULL, // Job will be placed infront of job queue // // Return: // // TRUE if successful, FALSE otherwise // // Note: // // Could cause job stavation if set to NULL // virtual DWORD ScheduleJob( IN DWORD StartTime ) /*++ --*/ { TLSASSERT(m_pWkMgr != NULL); return (m_pWkMgr == NULL) ? ERROR_INVALID_DATA : m_pWkMgr->ScheduleJob(StartTime, this); } //---------------------------------------------------------- // // For threadpool function, see thread pool doc. // virtual DWORD GetJobRunningAttribute() { return JOB_INCLUDES_IO | JOB_LONG_RUNNING; } //--------------------------------------------------------------- // // Return suggested schedule time relative to current time // virtual DWORD GetSuggestedScheduledTime() = 0; //-------------------------------------------------------------- // // Get last status return from Execute(). // virtual BOOL GetLastRunStatus() { return m_dwLastRunStatus; } //-------------------------------------------------------------- // // Return TRUE if job can be deleted from queue // virtual BOOL IsJobCompleted() = 0; //------------------------------------------------------------- // // End Job, work manager, after invoke Execute(), calls EndJob() // to inform. work object that job has completed, derived class // should perform internal data cleanup. // virtual void EndJob() = 0; //------------------------------------------------------------- // // Pure virtual function, work manager operates on CWorkObject // so it has no idea the actual class it is running, derive class // should cast the pointer back to its class and delete the pointer // to free up memory associated with object. // virtual BOOL SelfDestruct() = 0; //------------------------------------------------------------- // // Pure virtual, for debugging purpose only. // virtual LPCTSTR GetJobDescription() = 0; //-------------------------------------------------------- virtual void SetJobRetryTimes( IN DWORD dwRetries ) = 0; //-------------------------------------------------------- virtual DWORD GetJobRetryTimes() = 0; //--------------------------------------------------------- virtual void SetJobInterval( IN DWORD dwInterval ) = 0; //--------------------------------------------------------- virtual DWORD GetJobInterval() = 0; //--------------------------------------------------------- virtual void SetJobRestartTime( IN DWORD dwRestartTime ) /*++ --*/ { return; } }; #ifndef __TEST_WORKMGR__ #define TLSDebugOutput #endif //----------------------------------------------------- #ifdef __cplusplus extern "C" { #endif #ifdef __cplusplus } #endif #endif