Contents:
- 1 Thread Synchronization Toolkit
- 2 Implementing a Critical Section: The Optex
- 3 Creating Thread-Safe Datatypes and Inverse Semaphores
- 4 The Single Writer/Multiple Reader Guard (SWMRG)
- 5 Implementing a WaitForMultipleExpressions Function
1 Thread Synchronization Toolkit #
2 Implementing a Critical Section: The Optex #
3 Creating Thread-Safe Datatypes and Inverse Semaphores #
struct SomeDataStruct
{
...
} g_SomeSharedData;
// Create a CResGuard object that protects g_SomeSharedData.
// Note: The constructor initializes the critical section and
// the destructor deletes the critical section.
CResGuard g_rgSomeSharedData;
void AFunction()
{
// This function touches the shared data.
// Protect the resource from being accessed from multiple threads.
CResGuard::CGuard gDummy( g_rgSomeSharedData ); //Enter critical section
// Touch the g_SomeSharedData resource.
...
} // Note: LeaveCriticalSection is called when gDummy goes out of scope.
CInterlockedScalar<BYTE> b = 5; // A thread-safe BYTE
BYTE b2 = 10; // A non-thread-safe BYTE
b2 = b++; // b2=5, b=6
b *= 4; // b=24
b2 = b; // b2=24, b=24
b += b; // b=48
b %= 2; // b=0
CWhenZero<BYTE> b = 0; // A thread-safe BYTE
// Returns immediately because b is 0
WaitForSingleObject( b, INFINITE );
b = 5;
// Returns only if another thread sets b to 0
WaitForSingleObject( b, INFINITE );
CWhenZero<BYTE> b = 5; // A thread-safe BYTE
// Returns immediately because b is not 0
WaitForSingleObject( b.GetNotZeroHandle(), INFINITE );
b = 0;
// Returns only if another thread sets b to not 0
WaitForSingleObject( b.GetNotZeroHandle(), INFINITE );
IntLockTest.cpp
/******************************************************************************
Module: IntLockTest.cpp
Notices: Copyright (c) 2000 Jeffrey Richter
******************************************************************************/
#include "..\CmnHdr.h" /* See Appendix A. */
#include <tchar.h>
#include "Interlocked.h"
///////////////////////////////////////////////////////////////////////////////
// Set to TRUE when worker threads should terminate cleanly.
volatile BOOL g_fQuit = FALSE;
///////////////////////////////////////////////////////////////////////////////
DWORD WINAPI WorkerThread( PVOID pvParam )
{
CWhenZero<BYTE>& bVal = * (CWhenZero<BYTE> *) pvParam;
// Should worker thread terminate
while( !g_fQuit )
{
// Wait for something to do
WaitForSingleObject( bVal.GetNotZeroHandle(), INFINITE );
// If we should quit, quit
if( g_fQuit )
continue;
// Do something
chMB( "Worker thread: We have something to do" );
bVal--; // We're done
// Wait for all worker threads to stop
WaitForSingleObject( bVal, INFINITE );
}
chMB( "Worker thread: terminating" );
return ( 0 );
}
///////////////////////////////////////////////////////////////////////////////
int WINAPI _tWinMain( HINSTANCE hinstExe, HINSTANCE, PTSTR pszCmdLine, int )
{
// Initialize to indicate that NO worker threads have anything to do
CWhenZero<BYTE> bVal = 0;
// Create the worker threads
const int nMaxThreads = 2;
HANDLE hThreads[ nMaxThreads ];
for( int nThread = 0; nThread < nMaxThreads; nThread++ )
{
DWORD dwThreadId;
hThreads[ nThread ] = CreateThread( NULL, 0,
WorkerThread, (PVOID) &bVal, 0, &dwThreadId );
}
int n;
do
{
// Do more work or stop running
n = MessageBox( NULL,
TEXT( "Yes: Give worker threads something to do\nNo: Quit" ),
TEXT( "Primary thread" ), MB_YESNO );
// Tell worker threads that we're quitting
if( n == IDNO )
InterlockedExchangePointer( (PVOID*) &g_fQuit, (PVOID) TRUE );
bVal = nMaxThreads; // Wake the worker threads
if( n == IDYES )
{
// There is work to do, wait for the worker threads to finish
WaitForSingleObject( bVal, INFINITE );
}
}
while( n == IDYES );
// There is no more work to do, the process wants to die.
// Wait for the worker threads to terminate
WaitForMultipleObjects( nMaxThreads, hThreads, TRUE, INFINITE );
// Close the worker thread handles.
for( nThread = 0; nThread < nMaxThreads; nThread++ )
CloseHandle( hThreads[ nThread ] );
// Tell the user that the process is dying
chMB( "Primary thread: terminating" );
return ( 0 );
}
//////////////////////////////// End of File //////////////////////////////////
Interlocked.h
/******************************************************************************
Module: Interlocked.h
Notices: Copyright (c) 2000 Jeffrey Richter
******************************************************************************/
#pragma once
///////////////////////////////////////////////////////////////////////////////
// Instances of this class will be accessed by multiple threads. So,
// all members of this class (except the constructor and destructor)
// must be thread-safe.
class CResGuard
{
public:
CResGuard() { m_lGrdCnt = 0; InitializeCriticalSection( &m_cs ); }
~CResGuard() { DeleteCriticalSection( &m_cs ); }
// IsGuarded is used for debugging
BOOL IsGuarded() const { return ( m_lGrdCnt > 0 ); }
public:
class CGuard
{
public:
CGuard( CResGuard& rg ) : m_rg( rg ) { m_rg.Guard(); };
~CGuard() { m_rg.Unguard(); }
private:
CResGuard& m_rg;
};
private:
void Guard() { EnterCriticalSection( &m_cs ); m_lGrdCnt++; }
void Unguard() { m_lGrdCnt--; LeaveCriticalSection( &m_cs ); }
// Guard/Unguard can only be accessed by the nested CGuard class.
friend class CResGuard::CGuard;
private:
CRITICAL_SECTION m_cs;
long m_lGrdCnt; // # of EnterCriticalSection calls
};
///////////////////////////////////////////////////////////////////////////////
// Instances of this class will be accessed by multiple threads. So,
// all members of this class (except the constructor and destructor)
// must be thread-safe.
template <class TYPE>
class CInterlockedType
{
public: // Public member functions
// Note: Constructors & destructors are always thread-safe
CInterlockedType() {}
CInterlockedType( const TYPE& TVal ) { m_TVal = TVal; }
virtual ~CInterlockedType() {}
// Cast operator to make writing code that uses
// thread-safe data type easier
operator TYPE() const
{
CResGuard::CGuard x( m_rg );
return ( GetVal() );
}
protected: // Protected function to be called by derived class
TYPE& GetVal()
{
chASSERT( m_rg.IsGuarded() );
return ( m_TVal );
}
const TYPE& GetVal() const {
assert( m_rg.IsGuarded() );
return ( m_TVal );
}
TYPE SetVal( const TYPE& TNewVal )
{
chASSERT( m_rg.IsGuarded() );
TYPE& TVal = GetVal();
if( TVal != TNewVal ) {
TYPE TPrevVal = TVal;
TVal = TNewVal;
OnValChanged( TNewVal, TPrevVal );
}
return ( TVal );
}
protected: // Overridable functions
virtual void OnValChanged( const TYPE& TNewVal, const TYPE& TPrevVal ) const
{
// Nothing to do here
}
protected:
// Protected guard for use by derived class functions
mutable CResGuard m_rg;
private: // Private data members
TYPE m_TVal;
};
///////////////////////////////////////////////////////////////////////////////
// Instances of this class will be accessed by multiple threads. So,
// all members of this class (except the constructor and destructor)
// must be thread-safe.
template <class TYPE>
class CInterlockedScalar : protected CInterlockedType<TYPE>
{
public:
CInterlockedScalar( TYPE TVal = 0 ) : CInterlockedType<TYPE>( TVal ) {}
~CInterlockedScalar() { /* Nothing to do */ }
// C++ does not allow operator cast to be inherited.
operator TYPE() const
{
return ( CInterlockedType<TYPE>::operator TYPE() );
}
TYPE operator=( TYPE TVal )
{
CResGuard::CGuard x( m_rg );
return ( SetVal( TVal ) );
}
TYPE operator++(int) // Postfix increment operator
{
CResGuard::CGuard x( m_rg );
TYPE TPrevVal = GetVal();
SetVal( (TYPE) (TPrevVal + 1) );
return ( TPrevVal ); // Return value BEFORE increment
}
TYPE operator--(int) { // Postfix decrement operator.
CResGuard::CGuard x( m_rg );
TYPE TPrevVal = GetVal();
SetVal( (TYPE) (TPrevVal - 1) );
return ( TPrevVal ); // Return value BEFORE decrement
}
TYPE operator += ( TYPE op )
{ CResGuard::CGuard x( m_rg ); return( SetVal( GetVal() + op ) ); }
TYPE operator++()
{ CResGuard::CGuard x( m_rg ); return( SetVal( GetVal() + 1 ) ); }
TYPE operator -= ( TYPE op )
{ CResGuard::CGuard x( m_rg ); return( SetVal( GetVal() - op ) ); }
TYPE operator--()
{ CResGuard::CGuard x( m_rg ); return( SetVal( GetVal() - 1 ) ); }
TYPE operator *= ( TYPE op )
{ CResGuard::CGuard x( m_rg ); return( SetVal( GetVal() * op ) ); }
TYPE operator /= ( TYPE op )
{ CResGuard::CGuard x( m_rg ); return( SetVal( GetVal() / op ) ); }
TYPE operator %= ( TYPE op )
{ CResGuard::CGuard x( m_rg ); return( SetVal( GetVal() % op ) ); }
TYPE operator ^= ( TYPE op )
{ CResGuard::CGuard x( m_rg ); return( SetVal( GetVal() ^ op ) ); }
TYPE operator &= ( TYPE op )
{ CResGuard::CGuard x( m_rg ); return( SetVal( GetVal() & op ) ); }
TYPE operator |= ( TYPE op )
{ CResGuard::CGuard x( m_rg ); return( SetVal( GetVal() | op ) ); }
TYPE operator <<=( TYPE op )
{ CResGuard::CGuard x( m_rg ); return( SetVal( GetVal() << op ) ); }
TYPE operator >>=( TYPE op )
{ CResGuard::CGuard x( m_rg ); return( SetVal( GetVal() >> op ) ); }
};
///////////////////////////////////////////////////////////////////////////////
// Instances of this class will be accessed by multiple threads. So,
// all members of this class (except the constructor and destructor)
// must be thread-safe.
template <class TYPE>
class CWhenZero : public CInterlockedScalar<TYPE>
{
public:
CWhenZero( TYPE TVal = 0, BOOL fManualReset = TRUE )
: CInterlockedScalar<TYPE>( TVal )
{
// The event should be signaled if TVal is 0
m_hevtZero = CreateEvent( NULL, fManualReset, (TVal == 0), NULL );
// The event should be signaled if TVal is NOT 0
m_hevtNotZero = CreateEvent( NULL, fManualReset, (TVal != 0), NULL );
}
~CWhenZero() {
CloseHandle( m_hevtZero );
CloseHandle( m_hevtNotZero );
}
// C++ does not allow operator= to be inherited.
TYPE operator=( TYPE x )
{
return ( CInterlockedScalar<TYPE>::operator=( x ) );
}
// Return handle to event signaled when value is zero
operator HANDLE() const { return ( m_hevtZero ); }
// Return handle to event signaled when value is not zero
HANDLE GetNotZeroHandle() const { return ( m_hevtNotZero ); }
// C++ does not allow operator cast to be inherited.
operator TYPE() const
{
return ( CInterlockedScalar<TYPE>::operator TYPE() );
}
protected:
void OnValChanged( const TYPE& TNewVal, const TYPE& TPrevVal ) const
{
// For best performance, avoid jumping to
// kernel mode if we don't have to
if( (TNewVal == 0) && (TPrevVal != 0) )
{
SetEvent( m_hevtZero );
ResetEvent( m_hevtNotZero );
}
if( (TNewVal != 0) && (TPrevVal == 0) )
{
ResetEvent( m_hevtZero );
SetEvent( m_hevtNotZero );
}
}
private:
HANDLE m_hevtZero; // Signaled when data value is 0
HANDLE m_hevtNotZero; // Signaled when data value is not 0
};
//////////////////////////////// End of File //////////////////////////////////
InterlockedType.rc
// Microsoft Developer Studio generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (U.S.) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
#ifdef _WIN32
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#endif //_WIN32
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_INTERLOCKEDTYPE ICON DISCARDABLE "InterLockedType.ICO"
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE DISCARDABLE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE DISCARDABLE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE DISCARDABLE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED
4 The Single Writer/Multiple Reader Guard (SWMRG) #
5 Implementing a WaitForMultipleExpressions Function #
CategoryTranslation