// ========================================================================== // Class Specification : COXThreadEngine // ========================================================================== // Header file : OXOwnThread.h // This software along with its related components, documentation and files ("The Libraries") // is © 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is // governed by a software license agreement ("Agreement"). Copies of the Agreement are // available at The Code Project (www.codeproject.com), as part of the package you downloaded // to obtain this file, or directly from our office. For a copy of the license governing // this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900. // ////////////////////////////////////////////////////////////////////////// // Properties: // NO Abstract class (does not have any objects) // NO Is a Cwnd. // YES Two stage creation (constructor & Initialize()) // NO Has a message map // NO Needs a resource (template) // NO Persistent objects (saveable on disk) // YES Uses exceptions // ////////////////////////////////////////////////////////////////////////// // Description : // CThreadEngine class // You can use the COXThreadEngine for dedicated access to data objects. // It is a good approach to the design of multithreaded applications to prevent // as much as possible that specific data is accessed from more than one thread. // The class provides build-in functionality such as communication techniques // to communicate to and between different engines. // Suppose for example that we want to access a CMyOLEObject from more threads. // *** The first step is to derive a CMyEngine class from the COXThreadEngine base class. // We add the CMyOLEObject as a protected data member of the engine class. // Then we overload the pure virtual function "OnExecuteCmd()". This function will // be called internally by the COXThreadEngine and needs to dispatch the commands // to the appropriated function. Most of the time this can be implemented with a simply // switch structure. // Next, we have to derive a CMyCmd from the COXEngineCmd class to hold your // command parameters and return values. This class will be used internally to queue // multiple calls to the engine object. // Finally we provide a public wrapper function "DoSomething" which queues the CDoSomethingCmd // commands. We instantiate a CDoSomethingCmd object which we pass on to the PostCommand() // function. The PostCommand function will wait until the engine has executed the // command (in this example the command is synchronous). Before we return in this function // we need to release the command object. This object is reference counted and will // be destroyed automatically. If we now want to return a data member of the command // object, we need to copy the value to a temporary variable, because the command object could // be destroyed after we called the Release() function. // // *** Communication techniques: // + Synchronous, queued: create a COXEngineCmd without optional parameters and // post it with the PostCommand() function to the COXThreadEngine // + Synchronous, ASAP: create a COXEngineCmd without optional parameters and // post it with the PostCommand() function with the bASAP parameter set to TRUE // + Asynchronous, queued: create a COXEngineCmd with the first parameter set to FALSE // and post it with the PostCommand() function // + Asynchronous, ASAP: create a COXEngineCmd with the first parameter set to FALSE // and post it with the PostCommand() function with the bASAP parameter set to TRUE // * OPTIONAL for all the techniques, you can use a second parameter in the // constructor of COXEngineCmd if you want to receive a DONE notification. // This can be important for the asynchronous techniques. For this: the requesting // object need to be derived of COXDoneNotifier. You need to pass the this pointer // of the requesting object to the constructor of COXEngineCmd. Your requesting object // will now receive a DoneCommand() call after the execution of the command. // The command itself will be passed on as a parameter. You can use the command type and // the command index to track the initial request. For this, it is important that you // catalogue the index of the command after the construction of it. // Important is that you need to call the Release() function of your command also after // DONE notification. // Remark: // If you forget the call the Release() function of COXEngineCmd you will end up // with memory leaks. // You need to call the COXEngineCmd::Release() function after you posted the command // with the COXThreadEngine::PostCommand() function. And you need to call the Release() // function ones again if you implement a "done notification" for your command. // If the object that requested the command is also running in his own engine, // it can be necessary to post done notification commands to the queue of your requesting // engine. In this case you need to use a own command for this "Post" which takes the first // one, that came with the "done notification" as a data member. In this case you must call // the Release() function of the first command in the destructor of the second one. // Prerequisites (necessary conditions): // ///////////////////////////////////////////////////////////////////////////// #ifndef __OXTHREADENGINE_H_ #define __OXTHREADENGINE_H_ #if _MSC_VER >= 1000 #pragma once #endif // _MSC_VER >= 1000 #include "OXDllExt.h" class COXEngineCmd; class COXThreadEngine; class OX_CLASS_DECL COXDoneNotifier { friend COXThreadEngine; protected: virtual void DoneCommand(COXEngineCmd* pCmd) = 0; }; class OX_CLASS_DECL COXEngineCmd : public CObject { DECLARE_DYNAMIC(COXEngineCmd) friend COXThreadEngine; // Data Members public: DWORD m_dwIndex; // every instantiated command has an unique index protected: COXDoneNotifier* m_pDoneNotifier; // for optional notification after execution HANDLE m_hFinishedEvent; // use for synchronisation of synchrone commands LONG m_nRef; // used for reference counting private: static DWORD m_dwGlobalIndex; // internal cyclic counter // Member Functions public: COXEngineCmd(BOOL bSynchrone = TRUE, COXDoneNotifier* pDoneNotifier = NULL); // --- In : bSynchrone: defines if the command will be executed synchronous or asynchronous // pDoneNotifier: will be notified when the execution of the command is done // --- Out : none // --- Returns : none // --- Effect : Contructor of an egine command void Release(); // --- In : none // --- Out : none // --- Returns : none // --- Effect : decreases the reference count and deletes the object if zero BOOL IsSynchrone() const; // --- In : none // --- Out : none // --- Returns : if the command is synchronous or not // --- Effect : protected: virtual ~COXEngineCmd(); private: }; class OX_CLASS_DECL COXThreadEngine : public CObject { // Data Members public: protected: BOOL m_bEndThread; // thread shall terminate during next thread event BOOL m_bInitialized; // if successfully initialised CWinThread* m_pThread; // thread pointer of the engines thread HANDLE m_hCreatedEvent;// event for initialisation synchronisation HANDLE m_hEndEvent; // event for termination synchronisation HANDLE m_hThreadEvent; // event when a new command is posted DWORD m_nTerinationTimeout; class OX_CLASS_DECL COXEngineCmdList : public CTypedPtrList { // Data Members public: protected: HANDLE m_hMutex; private: // Member functions public: COXEngineCmdList(); virtual ~COXEngineCmdList(); void Lock(); void Unlock(); protected: private: } m_cmdList; private: // Member Functions public: COXThreadEngine(); // --- In : none // --- Out : none // --- Returns : none // --- Effect : construction of engine (still need to be initialised) virtual ~COXThreadEngine(); // --- In : none // --- Out : none // --- Returns : none // --- Effect : destructs the engine (need to be terminated first) BOOL Initialize(); // --- In : none // --- Out : none // --- Returns : if the engine is successfully initialised // --- Effect : start the engines thread (as a result // OnThreadConstruction() will be called) BOOL IsInitialized(); // --- In : none // --- Out : none // --- Returns : if the engine is successfully initialised // --- Effect : void Terminate(); // --- In : none // --- Out : none // --- Returns : none // --- Effect : stops the engines thread (first // OnThreadDestruction() will be called) void PostCommand(COXEngineCmd* pCmd, BOOL bASAP = FALSE); // --- In : pCmd : the command that will be added to the engines queue // bASAP : the command will be executed as soon as possible // when this flag is used the command will be added to // the head instead of the tail of the queue // --- Out : none // --- Returns : none // --- Effect : the command is added to the engines queue and the engine // thread is notified by an event protected: ///////////////////////////////////////////////////////////////////////////// // --- this function must be overloaded in your derived engine virtual void OnExecuteCmd(COXEngineCmd* pCmd) = 0; // --- In: pCmd: the next command in the queue that should be executed // --- Out: none // --- Returns: none // --- Effect: Up to YOU !!!!! // these two function can be overloaded if some specific initialisation and // uninitialisation is needed virtual BOOL OnThreadCreation(); virtual void OnThreadDestruction(); // these two function can be overloaded if some specific initialisation and // uninitialisation is needed virtual void OnThreadEvent(); private: void Run(); static UINT StartThread( LPVOID pParam ); }; #endif // __OXTHREADENGINE_H_