- BORLAND/: Borland C++ 4.52 (chosen over 4.5 by byte-match: CODE/RP/CW32.LIB
is identical to 4.52's install lib). BCC32/TLINK32/TLIB/MAKE run natively on
Win11; CODE/BT/OPT.MAK is the shipped BTL4OPT.EXE's exact flag recipe
(extender = Borland PowerPack DPMI32, not Phar Lap TNT).
- restoration/source410/: the literal 1995-form reconstruction of the missing
BT game source (never mixed into CODE/). Round 1-3 state:
* 6 of 10 surviving original TUs COMPILE CLEAN under the period toolchain
(BTMSSN, BTCNSL, BTSCNRL, BTTEAM, BTL4MODE, BTL4ARND) - first builds
since 1996.
* BT_L4/BTL4APP.CPP pilot reconstruction: 12/12 functions, Fail() lands on
its binary-recorded line 400 exactly.
* BT/BTCNSL.HPP: console wire IDs recovered from the binary's ctors
(Killed=9, Damaged=10, ScoreUpdate=13, DeathWithoutHonor=15 [T1];
TeamScore=12 flagged [T4]).
* MUNGA/: 8 engine-header backfills back-dated from the BT412 WinTesla tree
(VDATA numbering decomp-verified; AUDREND's OpenAL-era virtual removed -
the period compiler is the drift detector).
* Tooling: backdate.py (WinTesla->1995 header transform), compile410.sh
(per-TU verification sweep under authentic OPT.MAK flags).
* README: corrected roadmap - MECH.HPP is the capstone grown with the mech
TU reconstructions; BTREG.CPP green = the header-family milestone.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
500 lines
12 KiB
C++
500 lines
12 KiB
C++
/*------------------------------------------------------------------------*/
|
|
/* */
|
|
/* THREAD.CPP */
|
|
/* */
|
|
/* Copyright (c) 1993, 1994 Borland International */
|
|
/* All Rights Reserved */
|
|
/* */
|
|
/*------------------------------------------------------------------------*/
|
|
|
|
#if !defined( __PROCESS_H )
|
|
#include <process.h>
|
|
#endif
|
|
|
|
#if !defined( __STDLIB_H )
|
|
#include <stdlib.h>
|
|
#endif
|
|
|
|
#if !defined( __CHECKS_H )
|
|
#include <checks.h>
|
|
#endif
|
|
|
|
#if !defined( CLASSLIB_DEFS_H )
|
|
#include <classlib/defs.h>
|
|
#endif
|
|
|
|
#if !defined( CLASSLIB_THREAD_H )
|
|
#include <classlib/thread.h>
|
|
#endif
|
|
|
|
DIAG_DEFINE_GROUP(Threads,1,0);
|
|
|
|
#if defined( BI_PLAT_WIN32 )
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// WIN32
|
|
//
|
|
// Use OS call to close thread handle.
|
|
//
|
|
|
|
inline void InternalCloseHandle( TThread::THandle Handle )
|
|
{
|
|
::CloseHandle(Handle);
|
|
}
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// WIN32
|
|
//
|
|
// Use OS call to suspend thread.
|
|
//
|
|
|
|
inline DWORD InternalSuspendThread( TThread::THandle Handle )
|
|
{
|
|
return ::SuspendThread(Handle);
|
|
}
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// WIN32
|
|
//
|
|
// Use OS call to suspend thread.
|
|
//
|
|
|
|
inline DWORD InternalResumeThread( TThread::THandle Handle )
|
|
{
|
|
return ::ResumeThread(Handle);
|
|
}
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// WIN32
|
|
//
|
|
// Use OS call to wait for thread termination.
|
|
//
|
|
|
|
inline unsigned long InternalWaitForThread( TThread::THandle Handle,
|
|
unsigned long timeout )
|
|
{
|
|
return ::WaitForSingleObject( Handle, timeout );
|
|
}
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// WIN32
|
|
//
|
|
// Use OS call to get thread priority.
|
|
//
|
|
|
|
inline int InternalGetThreadPriority( TThread::THandle Handle )
|
|
{
|
|
return ::GetThreadPriority( Handle );
|
|
}
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// WIN32
|
|
//
|
|
// Use OS call to set thread priority.
|
|
//
|
|
|
|
inline int InternalSetThreadPriority( TThread::THandle Handle, int pri )
|
|
{
|
|
return ::SetThreadPriority( Handle, pri );
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined( BI_PLAT_OS2 )
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// OS/2
|
|
//
|
|
// Don't need to close the handle.
|
|
//
|
|
inline void InternalCloseHandle( TThread::THandle )
|
|
{
|
|
}
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// OS/2
|
|
//
|
|
// Use OS call to suspend thread.
|
|
//
|
|
|
|
inline ULONG InternalSuspendThread( TThread::THandle Handle )
|
|
{
|
|
return ::DosSuspendThread(Handle);
|
|
}
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// OS/2
|
|
//
|
|
// Use OS call to suspend thread.
|
|
//
|
|
|
|
inline ULONG InternalResumeThread( TThread::THandle Handle )
|
|
{
|
|
return ::DosResumeThread(Handle);
|
|
}
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// OS/2
|
|
//
|
|
// Use OS call to wait for thread termination.
|
|
//
|
|
|
|
inline ULONG InternalWaitForThread( TThread::THandle &Handle,
|
|
unsigned long timeout )
|
|
{
|
|
return ::DosWaitThread( &Handle, timeout );
|
|
}
|
|
|
|
#endif
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// TThread constructors
|
|
//
|
|
|
|
TThread::TThread() :
|
|
#if defined( BI_PLAT_WIN32 )
|
|
ThreadId(0),
|
|
#elif defined( BI_PLAT_OS2 )
|
|
Priority(0),
|
|
#endif
|
|
Handle(0),
|
|
Stat(Created),
|
|
TerminationRequested(0)
|
|
{
|
|
}
|
|
|
|
TThread::TThread( const TThread& ) :
|
|
#if defined( BI_PLAT_WIN32 )
|
|
ThreadId(0),
|
|
#elif defined( BI_PLAT_OS2 )
|
|
Priority(0),
|
|
#endif
|
|
Handle(0),
|
|
Stat(Created),
|
|
TerminationRequested(0)
|
|
{
|
|
}
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// TThread assignment operator
|
|
//
|
|
// Used when assigning derived objects. Attempting to
|
|
// assign from a running object is an error, since the
|
|
// data fields in the running object can be changing
|
|
// asynchronously.
|
|
//
|
|
|
|
const TThread& TThread::operator = ( const TThread& thread )
|
|
{
|
|
switch( GetStatus() )
|
|
{
|
|
case Created:
|
|
case Suspended:
|
|
case Finished:
|
|
{
|
|
if( this != &thread )
|
|
{
|
|
Handle = 0;
|
|
#if defined( BI_PLAT_WIN32 )
|
|
ThreadId = 0;
|
|
#elif defined( BI_PLAT_OS2 )
|
|
Priority = 0;
|
|
#endif
|
|
Stat = Created;
|
|
TerminationRequested = 0;
|
|
}
|
|
return *this;
|
|
}
|
|
default:
|
|
throw ThreadError(ThreadError::AssignError);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// TThread destructor
|
|
//
|
|
// If the thread hasn't finished, destroying its control
|
|
// object is an error.
|
|
//
|
|
|
|
TThread::~TThread()
|
|
{
|
|
if( GetStatus() != Finished )
|
|
throw ThreadError(ThreadError::DestroyBeforeExit);
|
|
InternalCloseHandle(Handle);
|
|
}
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// TThread::Start()
|
|
//
|
|
// Starts the thread executing. The actual call depends on the
|
|
// operating system. After the system call we check status.
|
|
//
|
|
|
|
TThread::THandle TThread::Start()
|
|
{
|
|
#if defined( BI_PLAT_WIN32 )
|
|
#if defined( __MT__ )
|
|
Handle = (HANDLE)::_beginthreadNT( &TThread::Execute, 4096, this, 0, 0, &ThreadId );
|
|
#else
|
|
Handle = ::CreateThread( 0, 0, &TThread::Execute, this, 0, &ThreadId );
|
|
#endif
|
|
#else
|
|
#if defined( __MT__ )
|
|
Handle = ::_beginthread( &TThread::Execute, 4096, this );
|
|
#else
|
|
APIRET res =
|
|
::DosCreateThread( &Handle,
|
|
(void (__syscall*)(unsigned long))&TThread::Execute,
|
|
REINTERPRET_CAST(unsigned long,this),
|
|
FALSE,
|
|
4000 );
|
|
#endif
|
|
#endif
|
|
|
|
if( Handle != 0 )
|
|
{
|
|
TRACEX( Threads, 1, "Thread started [id:" << Handle << ']' );
|
|
Stat = Running;
|
|
}
|
|
else
|
|
{
|
|
TRACEX(Threads, 2, "Thread failed to start" );
|
|
Stat = Invalid;
|
|
throw ThreadError(ThreadError::CreationFailure);
|
|
}
|
|
|
|
return Handle;
|
|
}
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// TThread::Suspend()
|
|
//
|
|
// It's an error to try to suspend a thread that
|
|
// hasn't been started or that has already terminated.
|
|
//
|
|
|
|
unsigned long TThread::Suspend()
|
|
{
|
|
switch( GetStatus() )
|
|
{
|
|
case Created:
|
|
TRACEX( Threads, 2, "Illegal thread suspension [id:" << Handle << ']' );
|
|
throw ThreadError(ThreadError::SuspendBeforeRun);
|
|
case Finished:
|
|
TRACEX( Threads, 2, "Illegal thread suspension [id:" << Handle << ']' );
|
|
throw ThreadError(ThreadError::SuspendAfterExit);
|
|
default:
|
|
TRACEX( Threads, 0, "Thread suspended [id:" << Handle << ']' );
|
|
Stat = Suspended;
|
|
return InternalSuspendThread(Handle);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// TThread::Resume()
|
|
//
|
|
// It's an error to try to resume a thread that hasn't
|
|
// been suspended.
|
|
//
|
|
|
|
unsigned long TThread::Resume()
|
|
{
|
|
switch( GetStatus() )
|
|
{
|
|
case Created:
|
|
TRACEX( Threads, 2, "Illegal thread resumption [id:" << Handle << ']' );
|
|
throw ThreadError(ThreadError::ResumeBeforeRun);
|
|
case Running:
|
|
TRACEX( Threads, 2, "Illegal thread resumption [id:" << Handle << ']' );
|
|
throw ThreadError(ThreadError::ResumeDuringRun);
|
|
case Finished:
|
|
throw ThreadError(ThreadError::ResumeAfterExit);
|
|
default:
|
|
TRACEX( Threads, 0, "Thread resumed [id:" << Handle << ']' );
|
|
unsigned long res = InternalResumeThread(Handle);
|
|
if( res == 0 )
|
|
Stat = Running;
|
|
return res;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// TThread::Terminate()
|
|
//
|
|
// Mark the thread for termination.
|
|
//
|
|
|
|
void TThread::Terminate()
|
|
{
|
|
TRACEX( Threads, 1, "Thread termination requested [handle:" << Handle << ']' );
|
|
TerminationRequested = 1;
|
|
}
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// TThread::WaitForExit()
|
|
//
|
|
// Block until the thread terminates.
|
|
//
|
|
// IMPORTANT: the meaning of the 'timeout' parameter is
|
|
// different for NT and OS/2. Under NT it specifies how long
|
|
// to wait for termination. Under OS/2 it specifies whether
|
|
// to wait or to return immediately if the thread hasn't
|
|
// terminated.
|
|
//
|
|
|
|
unsigned long TThread::WaitForExit( unsigned long timeout )
|
|
{
|
|
TRACEX( Threads, 1, "Waiting for thread exit [id:" << Handle << ']' );
|
|
if( Stat == Running )
|
|
return ::InternalWaitForThread( Handle, timeout );
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// TThread::TerminateAndWait()
|
|
//
|
|
// See note for WaitForExit().
|
|
//
|
|
|
|
unsigned long TThread::TerminateAndWait( unsigned long timeout )
|
|
{
|
|
Terminate();
|
|
return WaitForExit( timeout );
|
|
}
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// TThread::SetPriority()
|
|
//
|
|
// Set the thread's priority.
|
|
//
|
|
|
|
int TThread::SetPriority( int pri )
|
|
{
|
|
TRACEX( Threads, 1, "Thread priority changed to " << pri << " [id:" << Handle << ']' );
|
|
#if defined( BI_PLAT_WIN32 )
|
|
return ::SetThreadPriority(Handle,pri);
|
|
#else
|
|
|
|
APIRET res = DosSetPriority( PRTYS_THREAD,
|
|
PRTYC_NOCHANGE,
|
|
pri-Priority,
|
|
Handle );
|
|
if( res != 0 )
|
|
Priority = pri;
|
|
return res;
|
|
#endif
|
|
}
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// TThread::Execute()
|
|
//
|
|
// Run the thread.
|
|
//
|
|
|
|
#if defined( __MT__ )
|
|
void _USERENTRY TThread::Execute( void *thread )
|
|
{
|
|
STATIC_CAST(TThread*,thread)->Run();
|
|
}
|
|
#elif defined( BI_PLAT_WIN32 )
|
|
unsigned long _stdcall TThread::Execute( void *thread )
|
|
{
|
|
return STATIC_CAST(TThread*,thread)->Run();
|
|
}
|
|
#else
|
|
void __stdcall TThread::Execute( unsigned long thread )
|
|
{
|
|
REINTERPRET_CAST(TThread*,thread)->Run();
|
|
}
|
|
#endif
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// TThread::CheckStatus()
|
|
//
|
|
// Call only when Stat claims that the thread is Running.
|
|
//
|
|
|
|
#if defined( BI_PLAT_WIN32 )
|
|
TThread::Status TThread::CheckStatus() const
|
|
{
|
|
DWORD ExitCode;
|
|
::GetExitCodeThread( Handle, &ExitCode );
|
|
if( ExitCode == STILL_ACTIVE )
|
|
return Running;
|
|
else
|
|
return Finished;
|
|
}
|
|
#elif defined( BI_PLAT_OS2 )
|
|
TThread::Status TThread::CheckStatus() const
|
|
{
|
|
if( ::DosWaitThread( CONST_CAST(THandle *,&Handle), DCWW_NOWAIT ) == ERROR_THREAD_NOT_TERMINATED )
|
|
return Running;
|
|
else
|
|
return Finished;
|
|
}
|
|
#endif
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// TThread::ThreadError constructor
|
|
//
|
|
|
|
TThread::ThreadError::ThreadError(ErrorType type) :
|
|
xmsg(MakeString(type)),
|
|
Type(type)
|
|
{
|
|
}
|
|
|
|
//------------------------------------------------
|
|
//
|
|
// TThread::ThreadError::MakeString()
|
|
//
|
|
// Translates an error code into a string.
|
|
//
|
|
|
|
string TThread::ThreadError::MakeString(ErrorType type)
|
|
{
|
|
static char *Names[] =
|
|
{
|
|
"Suspend() before Run()",
|
|
"Resume() before Run()",
|
|
"Resume() during Run()",
|
|
"Suspend() after Exit()",
|
|
"Resume() after Exit()",
|
|
"creation failure",
|
|
"destroyed before Exit()",
|
|
"illegal assignment"
|
|
};
|
|
string Msg;
|
|
Msg.reserve(40);
|
|
Msg = "Error[thread]: ";
|
|
Msg += Names[type];
|
|
return Msg;
|
|
}
|
|
|
|
|