etqw-sdk/source/game/script/Script_Thread.cpp

594 lines
12 KiB
C++
Raw Normal View History

2008-05-29 00:00:00 +00:00
// Copyright (C) 2007 Id Software, Inc.
//
#include "../precompiled.h"
#pragma hdrstop
#if defined( _DEBUG ) && !defined( ID_REDIRECT_NEWDELETE )
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#include "../../decllib/DeclSurfaceType.h"
#include "Script_Thread.h"
#include "Script_Helper.h"
#include "Script_ScriptObject.h"
CLASS_DECLARATION( sdSysCallThread, idThread )
EVENT( EV_Remove, idThread::Event_Remove )
END_CLASS
idThread* idThread::currentThread = NULL;
int idThread::threadIndex = 0;
idLinkList< idThread > idThread::threadList;
idList< int > idThread::threadNumList;
idBlockAlloc< idThread, 16 > idThread::threadAllocator;
/*
================
idThread::CurrentThread
================
*/
idThread *idThread::CurrentThread( void ) {
return currentThread;
}
/*
================
idThread::CurrentThreadNum
================
*/
int idThread::CurrentThreadNum( void ) {
if ( currentThread ) {
return currentThread->GetThreadNum();
} else {
return 0;
}
}
/*
================
idThread::idThread
================
*/
idThread::idThread() {
threadNode.SetOwner( this );
interpreter.Init( reinterpret_cast< idProgram* >( gameLocal.program ) );
interpreter.SetThread( this );
SetName( "unnamedthread" );
if ( g_debugScript.GetBool() ) {
gameLocal.Printf( "%d: create thread (%d) '%s'\n", gameLocal.time, threadNum, threadName.c_str() );
}
}
/*
================
idThread::Init
================
*/
void idThread::Init( idInterpreter *source, const sdProgram::sdFunction* func, int args, bool guiThread ) {
Init();
this->guiThread = guiThread;
interpreter.ThreadCall( source, reinterpret_cast< const function_t* >( func ), args );
if ( g_debugScript.GetBool() ) {
gameLocal.Printf( "%d: create thread (%d) '%s'\n", gameLocal.time, threadNum, threadName.c_str() );
}
}
/*
================
idThread::Finalize
================
*/
void idThread::Finalize( void ) {
idEvent::CancelEvents( this );
threadNode.Remove();
GetAutoNode().Remove();
interpreter.Reset();
if ( g_debugScript.GetBool() ) {
gameLocal.Printf( "%d: end thread (%d) '%s'\n", gameLocal.time, threadNum, threadName.c_str() );
}
threadName.Clear();
if ( currentThread == this ) {
currentThread = NULL;
}
threadNumList.Alloc() = threadNum;
#ifdef _DEBUG
SetName( "__dead__" );
#endif // _DEBUG
}
/*
================
idThread::~idThread
================
*/
idThread::~idThread( void ) {
Finalize();
}
/*
================
idThread::ManualDelete
================
*/
void idThread::ManualDelete( void ) {
interpreter.terminateOnExit = false;
}
/*
================
idThread::AutoDelete
================
*/
void idThread::AutoDelete( void ) {
interpreter.terminateOnExit = true;
}
/*
================
idThread::GetFreeThreadNum
================
*/
int idThread::GetFreeThreadNum( void ) {
if ( !threadNumList.Num() ) {
return ++threadIndex;
}
int index = threadNumList.Num() - 1;
int value = threadNumList[ index ];
threadNumList.RemoveIndex( index );
return value;
}
/*
================
idThread::Init
================
*/
void idThread::Init( void ) {
threadNum = GetFreeThreadNum();
threadNode.AddToEnd( threadList );
creationTime = gameLocal.time;
lastExecuteTime = 0;
manualControl = false;
guiThread = false;
ClearWaitFor();
}
/*
================
idThread::GetThread
================
*/
idThread *idThread::GetThread( int num ) {
for ( idThread* thread = threadList.Next(); thread; thread = thread->threadNode.Next() ) {
if ( thread->GetThreadNum() == num ) {
return thread;
}
}
return NULL;
}
/*
================
idThread::DisplayInfo
================
*/
void idThread::DisplayInfo( void ) {
gameLocal.Printf(
"%12i: '%s'\n"
" File: %s(%d)\n"
" Created: %d (%d ms ago)\n"
" Status: ",
threadNum, threadName.c_str(),
interpreter.CurrentFile(), interpreter.CurrentLine(),
creationTime, gameLocal.time - creationTime );
if ( interpreter.threadDying ) {
gameLocal.Printf( "Dying\n" );
} else if ( interpreter.doneProcessing ) {
gameLocal.Printf(
"Paused since %d (%d ms)\n"
" Reason: ", lastExecuteTime, gameLocal.time - lastExecuteTime );
if ( waitingUntil ) {
gameLocal.Printf( "Waiting until %d (%d ms total wait time)\n", waitingUntil, waitingUntil - lastExecuteTime );
} else {
gameLocal.Printf( "None\n" );
}
} else {
gameLocal.Printf( "Processing\n" );
}
interpreter.DisplayInfo();
gameLocal.Printf( "\n" );
}
/*
================
idThread::ListThreads
================
*/
void idThread::ListThreads( void ) {
int n = 0;
int totalStack = 0;
for ( idThread* thread = threadList.Next(); thread; thread = thread->threadNode.Next(), n++ ) {
gameLocal.Printf( "%3i: %-20s : %s(%d)\n", thread->threadNum, thread->threadName.c_str(), thread->interpreter.CurrentFile(), thread->interpreter.CurrentLine() );
totalStack += thread->interpreter.GetStackSize();
}
gameLocal.Printf( "%d active threads\n", n );
gameLocal.Printf( "%d stack highpoint\n", idInterpreter::s_stackHigh );
}
/*
================
idThread::PruneThreads
================
*/
void idThread::PruneThreads( void ) {
idThread* next = NULL;
for ( idThread* thread = threadList.Next(); thread; thread = next ) {
next = thread->threadNode.Next();
if ( thread->interpreter.terminateOnExit ) {
FreeThread( thread );
continue;
}
}
}
/*
================
idThread::Restart
================
*/
void idThread::Restart( void ) {
// reset the threadIndex
threadIndex = 0;
currentThread = NULL;
threadAllocator.Shutdown();
while ( !threadList.IsListEmpty() ) {
FreeThread( threadList.Next() );
}
threadNumList.Clear();
memset( &trace, 0, sizeof( trace ) );
trace.c.entityNum = ENTITYNUM_NONE;
}
/*
================
idThread::DelayedStart
================
*/
void idThread::DelayedStart( int delay ) {
CancelEvents( &EV_Thread_Execute );
if ( gameLocal.time <= 0 ) {
delay++;
}
if ( guiThread ) {
PostGUIEventMS( &EV_Thread_Execute, delay );
} else {
PostEventMS( &EV_Thread_Execute, delay );
}
}
/*
================
idThread::SetName
================
*/
void idThread::SetName( const char *name ) {
threadName = name;
}
/*
================
idThread::End
================
*/
void idThread::End( void ) {
assert( threadName.Icmp( "__dead__" ) != 0 );
// Tell thread to die. It will exit on its own.
Pause();
interpreter.threadDying = true;
}
/*
================
idThread::EndThread
================
*/
void idThread::EndThread( void ) {
assert( threadName.Icmp( "__dead__" ) != 0 );
interpreter.threadDying = true;
}
/*
================
idThread::KillThread
================
*/
void idThread::KillThread( const char *name ) {
for ( idThread* thread = threadList.Next(); thread; thread = thread->threadNode.Next() ) {
if ( !idStr::Cmp( thread->GetThreadName(), name ) ) {
thread->End();
}
}
}
/*
================
idThread::KillThread
================
*/
void idThread::KillThread( int num ) {
idThread* thread = GetThread( num );
if ( thread != NULL ) {
// Tell thread to die. It will delete itself on it's own.
thread->End();
} else {
gameLocal.Warning( "Couldn't Find Thread '%d'", num );
}
}
/*
================
idThread::Execute
================
*/
bool idThread::Execute( void ) {
idThread *oldThread;
bool done;
int now = gameLocal.time;
if ( guiThread ) {
now = gameLocal.ToGuiTime( now );
}
if ( manualControl && ( waitingUntil > now ) ) {
return false;
}
oldThread = currentThread;
currentThread = this;
lastExecuteTime = now;
ClearWaitFor();
done = interpreter.Execute();
if ( done ) {
End();
if ( interpreter.terminateOnExit ) {
PostEventMS( &EV_Remove, 0 );
}
} else if ( !manualControl ) {
if ( waitingUntil > lastExecuteTime ) {
if ( guiThread ) {
PostGUIEventMS( &EV_Thread_Execute, waitingUntil - lastExecuteTime );
} else {
PostEventMS( &EV_Thread_Execute, waitingUntil - lastExecuteTime );
}
} else if ( waitFrame ) {
waitFrame = false;
if ( guiThread ) {
PostGUIEventMS( &EV_Thread_Execute, NEXT_FRAME_EVENT_TIME );
} else {
PostEventMS( &EV_Thread_Execute, NEXT_FRAME_EVENT_TIME );
}
}
}
currentThread = oldThread;
return done;
}
/*
================
idThread::CallFunction
NOTE: If this is called from within a event called by this thread, the function arguments will be invalid after calling this function.
================
*/
void idThread::CallFunction( const sdProgram::sdFunction* func ) {
ClearWaitFor();
interpreter.EnterFunction( reinterpret_cast< const function_t* >( func ), true );
}
/*
================
idThread::CallFunction
NOTE: If this is called from within a event called by this thread, the function arguments will be invalid after calling this function.
================
*/
void idThread::CallFunction( idScriptObject* object, const sdProgram::sdFunction* func ) {
assert( object );
ClearWaitFor();
interpreter.EnterObjectFunction( object, reinterpret_cast< const function_t* >( func ), true );
}
/*
================
idThread::ClearWaitFor
================
*/
void idThread::ClearWaitFor( void ) {
waitingUntil = 0;
waitFrame = false;
}
/*
================
idThread::Error
================
*/
void idThread::Error( const char* text ) const {
interpreter.Error( "%s", text );
}
/*
================
idThread::Warning
================
*/
void idThread::Warning( const char *fmt, ... ) const {
va_list argptr;
char text[ 1024 ];
va_start( argptr, fmt );
vsprintf( text, fmt, argptr );
va_end( argptr );
interpreter.Warning( "%s", text );
}
/*
================
idThread::CurrentFile
================
*/
const char* idThread::CurrentFile( void ) const {
return interpreter.CurrentFile();
}
/*
================
idThread::CurrentLine
================
*/
int idThread::CurrentLine( void ) const {
return interpreter.CurrentLine();
}
/*
================
idThread::StackTrace
================
*/
void idThread::StackTrace( void ) const {
interpreter.StackTrace();
}
/*
================
idThread::Pause
================
*/
void idThread::Pause( void ) {
ClearWaitFor();
interpreter.doneProcessing = true;
}
/*
================
idThread::WaitMS
================
*/
void idThread::WaitMS( int time ) {
if ( time <= 0 ) {
WaitFrame();
return;
}
Pause();
int now = gameLocal.time;
if ( guiThread ) {
now = gameLocal.ToGuiTime( now );
}
waitingUntil = now + time;
}
/*
================
idThread::Wait
================
*/
void idThread::Wait( float time ) {
WaitMS( SEC2MS( time ) );
}
/*
================
idThread::WaitFrame
================
*/
void idThread::WaitFrame( void ) {
Pause();
// manual control threads don't set waitingUntil so that they can be run again
// that frame if necessary.
if ( !manualControl ) {
waitFrame = true;
}
}
/*
================
idThread::Assert
================
*/
void idThread::Assert( void ) {
#ifdef _DEBUG
AssertFailed( interpreter.CurrentFile(), interpreter.CurrentLine(), "Script assertion" );
#endif // _DEBUG
}
/*
================
idThread::Event_Remove
================
*/
void idThread::Event_Remove( void ) {
OnEventRemove();
FreeThread( this );
}
/*
================
idThread::AllocThread
================
*/
idThread* idThread::AllocThread( void ) {
idThread* thread = threadAllocator.Alloc();
if ( thread->IsDying() ) {
assert( false );
thread->Finalize();
}
thread->Init();
thread->AutoDelete();
return thread;
}
/*
================
idThread::FreeThread
================
*/
void idThread::FreeThread( idThread* thread ) {
thread->Finalize();
threadAllocator.Free( thread );
}