2025-11-27 16:46:48 +09:00

262 lines
9.8 KiB
C++

// ==========================================================================
// 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<CPtrList, COXEngineCmd*>
{
// 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_