Paranoia2/utils/common/threads.cpp

248 lines
4.3 KiB
C++

/***
*
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
****/
#include "cmdlib.h"
#define NO_THREAD_NAMES
#include "threads.h"
#include <windows.h>
#define THREAD_STACK_SIZE (4096 * 1024) // 4 Mb
#define PACIFIER_STEP 40
#define PACIFIER_REM ( PACIFIER_STEP / 10 )
typedef struct thread_s
{
int number; // threadnum
pfnRunThreads func; // thread func
} thread_t;
static HANDLE g_threadhandles[MAX_THREADS];
static thread_t g_threads[MAX_THREADS];
static int g_dispatch = 0;
static int g_workcount = 0;
static qboolean g_pacifier = false;
static qboolean g_threaded = false;
static pfnThreadWork g_workfunction;
int g_numthreads = -1;
static int g_oldnumthreads;
static int g_oldf = -1;
static bool g_enter;
CRITICAL_SECTION g_crit;
void UpdatePacifier( float percent )
{
int f;
f = (int)(percent * (float)PACIFIER_STEP);
f = bound( g_oldf, f, PACIFIER_STEP );
if( f != g_oldf )
{
for( int i = g_oldf + 1; i <= f; i++ )
{
if(( i % PACIFIER_REM ) == 0 )
{
Msg( "%d%%", ( i / PACIFIER_REM ) * 10 );
}
else
{
if( i != PACIFIER_STEP )
{
Msg( "." );
}
}
}
g_oldf = f;
}
}
void StartPacifier( void )
{
g_oldf = -1;
UpdatePacifier( 0.001f );
}
void EndPacifier( double total )
{
UpdatePacifier( 1.0f );
Msg( " (%.2f secs)\n", total );
}
/*
=============
GetThreadWork
=============
*/
int GetThreadWork( void )
{
int r;
ThreadLock();
if( g_dispatch == g_workcount )
{
ThreadUnlock();
return -1;
}
if( g_pacifier )
UpdatePacifier( (float)g_dispatch / g_workcount );
r = g_dispatch;
g_dispatch++;
ThreadUnlock ();
return r;
}
static void ThreadWorkerFunction( int thread )
{
int work;
while( 1 )
{
work = GetThreadWork ();
if( work == -1 ) break;
g_workfunction( work, thread );
}
}
// This runs in the thread and dispatches a RunThreadsFn call.
static DWORD WINAPI InternalRunThreadsFn( LPVOID pData )
{
thread_t *pThread = (thread_t *)pData;
pThread->func( pThread->number );
return 0;
}
void ThreadSetDefault( void )
{
SYSTEM_INFO info;
if( g_numthreads == -1 )
{
// not set manually
GetSystemInfo( &info );
g_numthreads = info.dwNumberOfProcessors;
}
if( g_numthreads < 1 || g_numthreads > MAX_THREADS )
g_numthreads = 1;
MsgDev( D_REPORT, "%i threads\n", g_numthreads );
g_oldnumthreads = g_numthreads;
}
void ThreadLock( void )
{
if( !g_threaded ) return;
EnterCriticalSection( &g_crit );
if( g_enter ) COM_FatalError( "recursive ThreadLock\n" );
g_enter = true;
}
void ThreadUnlock( void )
{
if( !g_threaded ) return;
if( !g_enter ) COM_FatalError( "ThreadUnlock without lock\n" );
g_enter = false;
LeaveCriticalSection( &g_crit );
}
bool ThreadLocked( void )
{
return g_enter;
}
void ThreadPush( void )
{
g_numthreads = 1;
}
void ThreadPop( void )
{
g_numthreads = g_oldnumthreads;
}
/*
=============
RunThreadsOn
=============
*/
void RunThreadsOn( int workcnt, bool showpacifier, pfnRunThreads func )
{
double start, end;
DWORD dwDummy;
int i;
if( showpacifier )
{
if( g_numthreads == 1 )
Msg( " (single-threaded)\n" );
else Msg( "\n" );
}
start = I_FloatTime();
g_pacifier = showpacifier;
g_workcount = workcnt;
g_dispatch = 0;
if( g_pacifier ) StartPacifier();
if( g_numthreads == 1 )
{
// use same thread
func( 0 );
}
else
{
// run threads in parallel
InitializeCriticalSection( &g_crit );
g_threaded = true;
for( i = 0; i < g_numthreads; i++ )
{
g_threads[i].number = i;
g_threads[i].func = func;
g_threadhandles[i] = CreateThread( NULL, THREAD_STACK_SIZE, InternalRunThreadsFn, &g_threads, 0, &dwDummy );
}
WaitForMultipleObjects( g_numthreads, g_threadhandles, TRUE, INFINITE );
for ( i = 0; i < g_numthreads; i++ )
CloseHandle( g_threadhandles[i] );
DeleteCriticalSection( &g_crit );
g_threaded = false;
}
end = I_FloatTime ();
if( g_pacifier ) EndPacifier( end - start );
}
void RunThreadsOnIndividual( int workcnt, bool showpacifier, pfnThreadWork func )
{
g_workfunction = func;
RunThreadsOn( workcnt, showpacifier, ThreadWorkerFunction );
}
void RunThreadsOnIncremental( int workcnt, bool showpacifier, pfnRunThreads func )
{
RunThreadsOn( workcnt, showpacifier, func );
}