You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
516 lines
17 KiB
516 lines
17 KiB
|
|
// Ruler
|
|
// 1 2 3 4 5 6 7 8
|
|
//345678901234567890123456789012345678901234567890123456789012345678901234567890
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* The standard layout. */
|
|
/* */
|
|
/* The standard layout for 'cpp' files in this code is as */
|
|
/* follows: */
|
|
/* */
|
|
/* 1. Include files. */
|
|
/* 2. Constants local to the class. */
|
|
/* 3. Data structures local to the class. */
|
|
/* 4. Data initializations. */
|
|
/* 5. Static functions. */
|
|
/* 6. Class functions. */
|
|
/* */
|
|
/* The constructor is typically the first function, class */
|
|
/* member functions appear in alphabetical order with the */
|
|
/* destructor appearing at the end of the file. Any section */
|
|
/* or function this is not required is simply omitted. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
#include "LibraryPCH.hpp"
|
|
|
|
#include "Thread.hpp"
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Constants local to the class. */
|
|
/* */
|
|
/* The thread class keeps track of active threads. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
CONST SBIT32 ThreadsSize = 16;
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Data structures local to the class. */
|
|
/* */
|
|
/* When we start a thread we want to configure it. However, */
|
|
/* we no longer have access to the class information. Hence, */
|
|
/* we need a structure to pass over all of the interesting */
|
|
/* values to the new thread. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
typedef struct
|
|
{
|
|
BOOLEAN Affinity;
|
|
VOLATILE SBIT16 *Cpu;
|
|
NEW_THREAD Function;
|
|
SBIT16 MaxCpus;
|
|
VOID *Parameter;
|
|
BOOLEAN Priority;
|
|
HANDLE Running;
|
|
HANDLE Started;
|
|
THREAD *Thread;
|
|
BOOLEAN Wait;
|
|
}
|
|
SETUP_NEW_THREAD;
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Static functions local to the class. */
|
|
/* */
|
|
/* The static functions used by this class are declared here. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
STATIC VOID CDECL NewThread( VOID *SetupParameter );
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Class constructor. */
|
|
/* */
|
|
/* Create a thread class and initialize it. This call is not */
|
|
/* thread safe and should only be made in a single thread */
|
|
/* environment. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
THREAD::THREAD( VOID ) :
|
|
//
|
|
// Call the constructors for the contained classes.
|
|
//
|
|
Threads( ThreadsSize,NoAlignment,CacheLineSize )
|
|
{
|
|
//
|
|
// The inital configuration.
|
|
//
|
|
Affinity = False;
|
|
Cpu = 0;
|
|
Priority = False;
|
|
Stack = 0;
|
|
|
|
MaxThreads = ThreadsSize;
|
|
ThreadsUsed = 0;
|
|
|
|
//
|
|
// This event is signaled when a thread is
|
|
// running.
|
|
//
|
|
if ( (Running = CreateEvent( NULL, FALSE, FALSE, NULL )) == NULL)
|
|
{ Failure( "Create event in constructor for THREAD" ); }
|
|
|
|
//
|
|
// This event is signaled when a new thread can
|
|
// be started.
|
|
//
|
|
if ( (Started = CreateEvent( NULL, FALSE, TRUE, NULL )) == NULL)
|
|
{ Failure( "Create event in constructor for THREAD" ); }
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* End a thread. */
|
|
/* */
|
|
/* Delete the thread handle from the internal table and then */
|
|
/* terminate the thread. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
VOID THREAD::EndThread( VOID )
|
|
{
|
|
UnregisterThread();
|
|
|
|
_endthread();
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Find a thread. */
|
|
/* */
|
|
/* Find a registered thread in the thread info table. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
SBIT32 THREAD::FindThread( SBIT32 ThreadId )
|
|
{
|
|
REGISTER SBIT32 Count;
|
|
|
|
//
|
|
// Find a thread in the active thread list.
|
|
//
|
|
for ( Count=0;Count < ThreadsUsed;Count ++ )
|
|
{
|
|
if ( ThreadId == Threads[ Count ].ThreadId )
|
|
{ return Count; }
|
|
}
|
|
|
|
return NoThread;
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Start a new thread. */
|
|
/* */
|
|
/* When a new thread is created it executes a special initial */
|
|
/* function which configures it. When control returns to this */
|
|
/* function the thread is terminated. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
STATIC VOID CDECL NewThread( VOID *SetupParameter )
|
|
{
|
|
AUTO SETUP_NEW_THREAD Setup = (*(SETUP_NEW_THREAD*) SetupParameter);
|
|
|
|
//
|
|
// Set the affinity mask to the next processor if requested.
|
|
//
|
|
if ( (Setup.Affinity) && (Setup.MaxCpus > 1) )
|
|
{
|
|
REGISTER DWORD AffinityMask;
|
|
|
|
if ( (*Setup.Cpu) < Setup.MaxCpus )
|
|
{ AffinityMask = (1 << ((*Setup.Cpu) ++)); }
|
|
else
|
|
{
|
|
AffinityMask = 1;
|
|
(*Setup.Cpu) = 1;
|
|
}
|
|
|
|
if ( SetThreadAffinityMask( GetCurrentThread(),AffinityMask ) == 0 )
|
|
{ Failure( "Affinity mask invalid in NewThread()" ); }
|
|
}
|
|
|
|
//
|
|
// Set the priority to 'HIGH' if requested.
|
|
//
|
|
if ( Setup.Priority )
|
|
{ SetThreadPriority( GetCurrentThread(),THREAD_PRIORITY_HIGHEST ); }
|
|
|
|
//
|
|
// The thread is now so add it to the table
|
|
// executiong threads.
|
|
//
|
|
Setup.Thread -> RegisterThread();
|
|
|
|
//
|
|
// Wake up anyone who is waiting.
|
|
//
|
|
if ( Setup.Wait )
|
|
{ SetEvent( Setup.Running ); }
|
|
|
|
SetEvent( Setup.Started );
|
|
|
|
//
|
|
// Call thread function.
|
|
//
|
|
Setup.Function( Setup.Parameter );
|
|
|
|
//
|
|
// The thread function has returned so exit.
|
|
//
|
|
Setup.Thread -> EndThread();
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Register the current thread. */
|
|
/* */
|
|
/* When a thread has created we can add the thread info to */
|
|
/* our internal table. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
VOID THREAD::RegisterThread( VOID )
|
|
{
|
|
REGISTER SBIT32 ThreadId = GetThreadId();
|
|
|
|
//
|
|
// Claim a spinlock so we can update the
|
|
// thread table.
|
|
//
|
|
Spinlock.ClaimLock();
|
|
|
|
if ( FindThread( ThreadId ) == NoThread )
|
|
{
|
|
AUTO HANDLE NewHandle;
|
|
REGISTER HANDLE Process = GetCurrentProcess();
|
|
REGISTER HANDLE Thread = GetCurrentThread();
|
|
|
|
//
|
|
// We need to duplicate the handle so we get
|
|
// a real thread handle and not the pretend
|
|
// ones supplied by NT.
|
|
//
|
|
if
|
|
(
|
|
DuplicateHandle
|
|
(
|
|
Process,
|
|
Thread,
|
|
Process,
|
|
& NewHandle,
|
|
DUPLICATE_SAME_ACCESS,
|
|
False,
|
|
DUPLICATE_SAME_ACCESS
|
|
)
|
|
)
|
|
{
|
|
REGISTER THREAD_INFO *ThreadInfo;
|
|
|
|
//
|
|
// We may need to expand the table if there are
|
|
// a large number of threads.
|
|
//
|
|
while ( ThreadsUsed >= MaxThreads )
|
|
{ Threads.Resize( (MaxThreads *= ExpandStore) ); }
|
|
|
|
//
|
|
// Add the thread handle to the table.
|
|
//
|
|
ThreadInfo = & Threads[ ThreadsUsed ++ ];
|
|
|
|
ThreadInfo -> Handle = NewHandle;
|
|
ThreadInfo -> ThreadId = ThreadId;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We are finished so release the lock.
|
|
//
|
|
Spinlock.ReleaseLock();
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Set thread stack size. */
|
|
/* */
|
|
/* Set thread stack size. This will cause all new threads to */
|
|
/* be created with the selected stack size. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
VOID THREAD::SetThreadStackSize( LONG Stack )
|
|
{
|
|
#ifdef DEBUGGING
|
|
if ( Stack >= 0 )
|
|
{
|
|
#endif
|
|
this -> Stack = Stack;
|
|
#ifdef DEBUGGING
|
|
}
|
|
else
|
|
{ Failure( "Stack size in SetThreadStack()" ); }
|
|
#endif
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Start a new thread. */
|
|
/* */
|
|
/* Start a new thread and configure it as requested by the */
|
|
/* caller. If needed we will set the affinity and priority */
|
|
/* of this thread later. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
BOOLEAN THREAD::StartThread( NEW_THREAD Function,VOID *Parameter,BOOLEAN Wait )
|
|
{
|
|
STATIC SETUP_NEW_THREAD Setup;
|
|
|
|
//
|
|
// Wait for any pending thread creations to
|
|
// complete.
|
|
//
|
|
while
|
|
(
|
|
WaitForSingleObject( Started,INFINITE )
|
|
!=
|
|
WAIT_OBJECT_0
|
|
);
|
|
|
|
//
|
|
// Create a thread activation record.
|
|
//
|
|
Setup.Affinity = Affinity;
|
|
Setup.Cpu = & Cpu;
|
|
Setup.Function = Function;
|
|
Setup.MaxCpus = NumberOfCpus();
|
|
Setup.Parameter = Parameter;
|
|
Setup.Priority = Priority;
|
|
Setup.Running = Running;
|
|
Setup.Started = Started;
|
|
Setup.Thread = this;
|
|
Setup.Wait = Wait;
|
|
|
|
//
|
|
// Call the operating system to start the thread.
|
|
//
|
|
if ( _beginthread( NewThread,(unsigned) Stack,(VOID*) & Setup ) != NULL )
|
|
{
|
|
//
|
|
// Wait for the thread to initialize is needed.
|
|
//
|
|
if ( Wait )
|
|
{
|
|
//
|
|
// Wait for the thread to start to run.
|
|
//
|
|
while
|
|
(
|
|
WaitForSingleObject( Running,INFINITE )
|
|
!=
|
|
WAIT_OBJECT_0
|
|
);
|
|
}
|
|
|
|
return True;
|
|
}
|
|
else
|
|
{ return False; }
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Unregister the current thread. */
|
|
/* */
|
|
/* When a thread has terminated we can delete the thread */
|
|
/* info from our internal table. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
VOID THREAD::UnregisterThread( SBIT32 ThreadId )
|
|
{
|
|
REGISTER SBIT32 Start;
|
|
|
|
//
|
|
// If no 'ThreadId' assume the current thread.
|
|
//
|
|
if ( ThreadId == NoThread )
|
|
{ ThreadId = GetThreadId(); }
|
|
|
|
//
|
|
// Claim a spinlock so we can update the
|
|
// thread table.
|
|
//
|
|
Spinlock.ClaimLock();
|
|
|
|
//
|
|
// Search for the thread info to delete
|
|
// in the table.
|
|
//
|
|
if ( (Start = FindThread( ThreadId )) != NoThread )
|
|
{
|
|
REGISTER SBIT32 Count;
|
|
|
|
//
|
|
// Close the handle to the thread and
|
|
// update the table size.
|
|
//
|
|
CloseHandle( Threads[ Start ].Handle );
|
|
|
|
ThreadsUsed --;
|
|
|
|
//
|
|
// Copy down the remaining thread info.
|
|
//
|
|
for ( Count=Start;Count < ThreadsUsed;Count ++ )
|
|
{ Threads[ Count ] = Threads[ (Count+1) ]; }
|
|
}
|
|
|
|
//
|
|
// We are finished so release the lock.
|
|
//
|
|
Spinlock.ReleaseLock();
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Wait for threads. */
|
|
/* */
|
|
/* Wait for all threads to finish and then return. As this may */
|
|
/* take a while an optional timeout may be supplied. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
BOOLEAN THREAD::WaitForThreads( LONG WaitTime )
|
|
{
|
|
//
|
|
// Claim a spinlock so we can read the
|
|
// thread table.
|
|
//
|
|
Spinlock.ClaimLock();
|
|
|
|
while ( ThreadsUsed > 0 )
|
|
{
|
|
REGISTER HANDLE Handle = (Threads[0].Handle);
|
|
REGISTER SBIT32 ThreadId = (Threads[0].ThreadId);
|
|
REGISTER DWORD Status;
|
|
|
|
//
|
|
// We are finished so release the lock.
|
|
//
|
|
Spinlock.ReleaseLock();
|
|
|
|
//
|
|
// Wait for the first thread in the thread info
|
|
// table to terminate.
|
|
//
|
|
if
|
|
(
|
|
(Status = WaitForSingleObject( Handle,(DWORD) WaitTime ))
|
|
==
|
|
WAIT_TIMEOUT
|
|
)
|
|
{ return False; }
|
|
|
|
//
|
|
// We have woken up the thread must of terminated
|
|
// or the handle is bad in some way. In any case
|
|
// lets delete the handle and try to sleep again
|
|
// if there are any more active threads.
|
|
//
|
|
UnregisterThread( ThreadId );
|
|
|
|
//
|
|
// Claim a spinlock so we can read the
|
|
// thread table.
|
|
//
|
|
Spinlock.ClaimLock();
|
|
}
|
|
|
|
//
|
|
// We are finished so release the lock.
|
|
//
|
|
Spinlock.ReleaseLock();
|
|
|
|
return True;
|
|
}
|
|
|
|
/********************************************************************/
|
|
/* */
|
|
/* Class destructor. */
|
|
/* */
|
|
/* Destory the thread class. This call is not thread safe */
|
|
/* and should only be made in a single thread environment. */
|
|
/* */
|
|
/********************************************************************/
|
|
|
|
THREAD::~THREAD( VOID )
|
|
{
|
|
if
|
|
(
|
|
! CloseHandle( Running )
|
|
||
|
|
! CloseHandle( Started )
|
|
)
|
|
{ Failure( "Event handles in destructor for THREAD" ); }
|
|
}
|