The
CMclKernel class, encapsulates the basic functionality of a kernel object. This is an abstract base class (it has a pure virtual destructor) so that instances of this class cannot be created. Only instances of classes derived from CMclKernel can be created, namely CMclThread, CMclMutex, CMclSemaphore, and CMclEvent, which we will cover shortly. There is a lot of functionality implemented in this class which is common to all of the kernel object wrappers. This keeps all the common code in one place and the derived classes only implement their own special behaviors. The header file for CMclKernel is shown in Example 6-9, while its C++ source code file is shown in Example 6-10.Example 6-9. CMclKernel.h: Kernel object abstract base class header file
//
// FILE: CMclKernel.h
//
// Copyright (c) 1997 by Aaron Michael Cohen and Mike Woodring
//
/////////////////////////////////////////////////////////////////////////
#ifndef __CMCLKERNEL_H__
#define __CMCLKERNEL_H__
#include "CMclGlobal.h"
#include "CMclWaitableObject.h"
class CMclKernel : public CMclWaitableObject {
protected:
HANDLE m_hHandle;
DWORD m_dwStatus;
protected:
// constructor...
CMclKernel();
// error handling...
void ThrowError( DWORD dwStatus);
public:
// destructor is virtual to make CMclKernel an abstract base class...
virtual ~CMclKernel() = 0;
// read the creation status of the internal kernel object...
DWORD Status(void) const;
// wait on the current kernel object...
DWORD Wait( DWORD dwMilliseconds);
// wait on the current object and one other...
DWORD WaitForTwo( CMclWaitableObject &rCMclWaitableObject, BOOL bWaitAll, DWORD dwMilliseconds);
// get the internal handle...
HANDLE GetHandle(void) const;
// another way to get the internal handle...
operator HANDLE() const;
private:
// these functions have no implementation since they can
// never be called...
// copying and passing by copy are not allowed...
// this prevents confusion of internal object ownership...
CMclKernel(CMclKernel & rhs);
// assigning one object to another is not allowed,
// this prevents confusion of internal object ownership...
CMclKernel & operator= (CMclKernel & rhs);
};
#endif
Example 6-10. CMclKernel.cpp: Kernel object abstract base class implementation file
//
// FILE: CMclKernel.cpp
//
// Copyright (c) 1997 by Aaron Michael Cohen
//
/////////////////////////////////////////////////////////////////////////
#include "CMclKernel.h"
#include "CMclAutoPtr.h"
CMclKernel::CMclKernel() {
m_hHandle = NULL;
m_dwStatus = ERROR_INVALID_HANDLE;
}
CMclKernel::~CMclKernel() {
if (CMclIsValidHandle(m_hHandle)) {
::CloseHandle(m_hHandle);
m_hHandle = NULL;
}
}
void CMclKernel::ThrowError( DWORD dwStatus) {
CMclThrowError(dwStatus);
}
DWORD CMclKernel::Status(void) const {
return m_dwStatus;
}
DWORD CMclKernel::Wait( DWORD dwMilliseconds) {
return ::WaitForSingleObject( m_hHandle, dwMilliseconds);
}
// wait on the current object and one other...
DWORD CMclKernel::WaitForTwo( CMclWaitableObject &rCMclWaitableObject, BOOL bWaitAll, DWORD dwMilliseconds) {
HANDLE handles[2];
// the current object...
handles[0] = m_hHandle;
// the parameter object...
handles[1] = rCMclWaitableObject.GetHandle();
// wait for the objects...
return ::WaitForMultipleObjects( 2, handles, bWaitAll, dwMilliseconds);
}
HANDLE CMclKernel::GetHandle(void) const {
if (this != NULL)
return m_hHandle;
else
return NULL;
}
CMclKernel::operator HANDLE() const {
return GetHandle();
}
First, the
CMclKernel object constructor initializes the m_hHandle and m_dwStatus members to known values. These will be overwritten by the derived class constructors. The m_hHandle member will store the handle to the internally created kernel object, and the m_dwStatus will store the status of the creation of the kernel object. It is necessary to check this value if the library has been compiled with exceptions turned off. Any errors encountered during object construction will be stored here. The list of possible error codes can be found in the winerror.h system header file.The destructor checks to see if a valid handle is stored in
m_hHandle and if so, closes it. This is a very important part of cleaning up after using kernel objects, and the CMcl class library handles it here in the base class destructor for all of the derived kernel object wrappers. This "close on destroy" operation ensures that all of the internal kernel object handles are closed when a global or automatic object goes out of scope or a dynamically created object is explicitly deleted. Having wrapper classes which clean up after themselves streamlines multithreaded code significantly.The GetHandle function checks that it has been called with a valid pointer and returns the internal handle. The operator
HANDLE is simply provided as a convenience, to make it easy to pass the internal handle to Win32 API function when required. This should not be needed very often if you take full advantage of the CMcl library.The two wait functions, Wait and WaitForTwo, are the most interesting. The simple wait member function Wait is a wrapper around the WaitForSingleObject Win32 API function. The result of the wait operation is returned to the caller.
The WaitForTwo member function is a great convenience. With a single function call it replaces most of the occasions where you need to fill an array of handles and call WaitForMultipleObjects. It returns the same values as the API call, using the current object as
WAIT_OBJECT_0 and the rCMclKernel reference as (WAIT_OBJECT_0 + 1). The bWaitAll parameter and dwMilliseconds are used exactly like those in WaitForMultipleObjects.