2009-08-11 12:17:38 +00:00
// *************************************************************************************************
// --------------------------------------
// Copyright (C) 2006-2010 Rajko Stojadinovic
//
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
// *************************************************************************************************
# include "../Common.h"
# include "ThreadPool.h"
# include "../Log.h"
# include "../Util.h"
# include <stdio.h>
# include <stdlib.h>
# if PLATFORM == PLATFORM_WIN32
# include <process.h>
# else
volatile int threadid_count = 0 ;
NativeMutex m_threadIdLock ;
int GenerateThreadId ( )
{
m_threadIdLock . Acquire ( ) ;
int i = + + threadid_count ;
m_threadIdLock . Release ( ) ;
return i ;
}
# endif
CThreadPool ThreadPool ;
CThreadPool : : CThreadPool ( )
{
_threadsExitedSinceLastCheck = 0 ;
_threadsRequestedSinceLastCheck = 0 ;
_threadsEaten = 0 ;
_threadsFreedSinceLastCheck = 0 ;
}
bool CThreadPool : : ThreadExit ( ThreadStruct * t )
{
_mutex . Acquire ( ) ;
// we're definitely no longer active
m_activeThreads . erase ( t ) ;
// do we have to kill off some threads?
if ( _threadsToExit > 0 )
{
// kill us.
- - _threadsToExit ;
+ + _threadsExitedSinceLastCheck ;
if ( t - > DeleteAfterExit )
m_freeThreads . erase ( t ) ;
_mutex . Release ( ) ;
delete t ;
return false ;
}
// enter the "suspended" pool
+ + _threadsExitedSinceLastCheck ;
+ + _threadsEaten ;
std : : set < ThreadStruct * > : : iterator itr = m_freeThreads . find ( t ) ;
if ( itr ! = m_freeThreads . end ( ) )
{
printf ( " Thread %u duplicated with thread %u \n " , ( * itr ) - > ControlInterface . GetId ( ) , t - > ControlInterface . GetId ( ) ) ;
}
m_freeThreads . insert ( t ) ;
_mutex . Release ( ) ;
return true ;
}
void CThreadPool : : ExecuteTask ( ThreadContext * ExecutionTarget )
{
ThreadStruct * t ;
_mutex . Acquire ( ) ;
+ + _threadsRequestedSinceLastCheck ;
- - _threadsEaten ;
// grab one from the pool, if we have any.
if ( m_freeThreads . size ( ) )
{
t = * m_freeThreads . begin ( ) ;
m_freeThreads . erase ( m_freeThreads . begin ( ) ) ;
// execute the task on this thread.
t - > ExecutionTarget = ExecutionTarget ;
// resume the thread, and it should start working.
t - > ControlInterface . Resume ( ) ;
2010-02-13 14:32:04 +00:00
//DEBUG_LOG( format("Thread %1% left the thread pool.") % t->ControlInterface.GetId() );
2009-08-11 12:17:38 +00:00
}
else
{
// creating a new thread means it heads straight to its task.
// no need to resume it :)
t = StartThread ( ExecutionTarget ) ;
}
// add the thread to the active set
2010-02-16 06:52:49 +00:00
stringstream outMsg ;
using namespace std ;
# ifdef __LP64__
# define ptrIntType uint64
2009-08-11 12:17:38 +00:00
# else
2010-02-16 06:52:49 +00:00
# define ptrIntType uint32
2010-02-14 15:35:36 +00:00
# endif
2010-02-16 06:52:49 +00:00
outMsg < < " Thread " < < t - > ControlInterface . GetId ( ) < < " is now executing task at 0x " < < setw ( sizeof ( ExecutionTarget ) * 2 ) < < setfill ( ' 0 ' ) < < hex < < ptrIntType ( ExecutionTarget ) < < dec < < " . " ;
DEBUG_LOG ( outMsg . str ( ) ) ;
2009-08-11 12:17:38 +00:00
m_activeThreads . insert ( t ) ;
_mutex . Release ( ) ;
}
void CThreadPool : : Startup ( uint8 ThreadCount )
{
int i ;
int tcount = ThreadCount ;
for ( i = 0 ; i < tcount ; + + i )
StartThread ( NULL ) ;
2010-02-13 14:32:04 +00:00
DEBUG_LOG ( format ( " ThreadPool Startup, launched %1% threads. " ) % tcount ) ;
2009-08-11 12:17:38 +00:00
}
void CThreadPool : : ShowStats ( )
{
_mutex . Acquire ( ) ;
DEBUG_LOG ( " ============ ThreadPool Status ============= " ) ;
2010-02-13 14:32:04 +00:00
DEBUG_LOG ( format ( " Active Threads: %u " ) % m_activeThreads . size ( ) ) ;
DEBUG_LOG ( format ( " Suspended Threads: %u " ) % m_freeThreads . size ( ) ) ;
DEBUG_LOG ( format ( " Requested-To-Freed Ratio: %.3f%% (%u/%u) " ) % float ( float ( _threadsRequestedSinceLastCheck + 1 ) / float ( _threadsExitedSinceLastCheck + 1 ) * 100.0f ) % _threadsRequestedSinceLastCheck % _threadsExitedSinceLastCheck ) ;
DEBUG_LOG ( format ( " Eaten Count: %d (negative is bad!) " ) % _threadsEaten ) ;
2009-08-11 12:17:38 +00:00
DEBUG_LOG ( " ============================================ " ) ;
_mutex . Release ( ) ;
}
void CThreadPool : : IntegrityCheck ( uint8 ThreadCount )
{
_mutex . Acquire ( ) ;
int32 gobbled = _threadsEaten ;
if ( gobbled < 0 )
{
// this means we requested more threads than we had in the pool last time.
// spawn "gobbled" + THREAD_RESERVE extra threads.
uint32 new_threads = abs ( gobbled ) + ThreadCount ;
_threadsEaten = 0 ;
for ( uint32 i = 0 ; i < new_threads ; + + i )
StartThread ( NULL ) ;
2010-02-13 14:32:04 +00:00
DEBUG_LOG ( format ( " ThreadPool::IntegrityCheck: (gobbled < 0) Spawning %1% threads. " ) % new_threads ) ;
2009-08-11 12:17:38 +00:00
}
else if ( gobbled < ThreadCount )
{
// this means while we didn't run out of threads, we were getting damn low.
// spawn enough threads to keep the reserve amount up.
uint32 new_threads = ( THREAD_RESERVE - gobbled ) ;
for ( uint32 i = 0 ; i < new_threads ; + + i )
StartThread ( NULL ) ;
2010-02-13 14:32:04 +00:00
DEBUG_LOG ( format ( " ThreadPool::IntegrityCheck: (gobbled <= 5) Spawning %1% threads. " ) % new_threads ) ;
2009-08-11 12:17:38 +00:00
}
else if ( gobbled > ThreadCount )
{
// this means we had "excess" threads sitting around doing nothing.
// lets kill some of them off.
uint32 kill_count = ( gobbled - ThreadCount ) ;
KillFreeThreads ( kill_count ) ;
_threadsEaten - = kill_count ;
2010-02-13 14:32:04 +00:00
DEBUG_LOG ( format ( " ThreadPool::IntegrityCheck: (gobbled > 5) Killing %1% threads. " ) % kill_count ) ;
2009-08-11 12:17:38 +00:00
}
else
{
// perfect! we have the ideal number of free threads.
DEBUG_LOG ( " ThreadPool::IntegrityCheck: Perfect! " ) ;
}
_threadsExitedSinceLastCheck = 0 ;
_threadsRequestedSinceLastCheck = 0 ;
_threadsFreedSinceLastCheck = 0 ;
_mutex . Release ( ) ;
}
void CThreadPool : : KillFreeThreads ( uint32 count )
{
2010-02-13 14:32:04 +00:00
DEBUG_LOG ( format ( " ThreadPool Killing %1% excess threads. " ) % count ) ;
2009-08-11 12:17:38 +00:00
_mutex . Acquire ( ) ;
ThreadStruct * t ;
ThreadSet : : iterator itr ;
uint32 i ;
for ( i = 0 , itr = m_freeThreads . begin ( ) ; i < count & & itr ! = m_freeThreads . end ( ) ; + + i , + + itr )
{
t = * itr ;
t - > ExecutionTarget = NULL ;
t - > DeleteAfterExit = true ;
+ + _threadsToExit ;
t - > ControlInterface . Resume ( ) ;
}
_mutex . Release ( ) ;
}
void CThreadPool : : Shutdown ( )
{
_mutex . Acquire ( ) ;
size_t tcount = m_activeThreads . size ( ) + m_freeThreads . size ( ) ; // exit all
2010-02-13 14:32:04 +00:00
DEBUG_LOG ( format ( " ThreadPool Shutting down %1% threads. " ) % tcount ) ;
2009-08-11 12:17:38 +00:00
KillFreeThreads ( ( uint32 ) m_freeThreads . size ( ) ) ;
_threadsToExit + = ( uint32 ) m_activeThreads . size ( ) ;
for ( ThreadSet : : iterator itr = m_activeThreads . begin ( ) ; itr ! = m_activeThreads . end ( ) ; + + itr )
{
if ( ( * itr ) - > ExecutionTarget )
( * itr ) - > ExecutionTarget - > OnShutdown ( ) ;
}
_mutex . Release ( ) ;
for ( ; ; )
{
_mutex . Acquire ( ) ;
if ( m_activeThreads . size ( ) | | m_freeThreads . size ( ) )
{
2010-02-13 14:32:04 +00:00
DEBUG_LOG ( format ( " ThreadPool %1% threads remaining... " ) % ( m_activeThreads . size ( ) + m_freeThreads . size ( ) ) ) ;
2009-08-11 12:17:38 +00:00
_mutex . Release ( ) ;
Sleep ( 1000 ) ;
continue ;
}
break ;
}
}
bool RunThread ( ThreadContext * target )
{
bool res = false ;
res = target - > run ( ) ;
return res ;
}
/* this is the only platform-specific code. */
# if PLATFORM == PLATFORM_WIN32
static unsigned long WINAPI thread_proc ( void * param )
{
ThreadStruct * t = ( ThreadStruct * ) param ;
t - > SetupMutex . Acquire ( ) ;
uint32 tid = t - > ControlInterface . GetId ( ) ;
bool ht = ( t - > ExecutionTarget ! = NULL ) ;
t - > SetupMutex . Release ( ) ;
2010-02-14 15:35:36 +00:00
DEBUG_LOG ( format ( " Thread %1% started. " ) % t - > ControlInterface . GetId ( ) ) ;
2009-08-11 12:17:38 +00:00
for ( ; ; )
{
if ( t - > ExecutionTarget ! = NULL )
{
if ( RunThread ( t - > ExecutionTarget ) )
delete t - > ExecutionTarget ;
t - > ExecutionTarget = NULL ;
}
if ( ! ThreadPool . ThreadExit ( t ) )
{
2010-02-13 14:32:04 +00:00
DEBUG_LOG ( format ( " Thread %1% exiting. " ) % tid ) ;
2009-08-11 12:17:38 +00:00
break ;
}
else
{
if ( ht )
2010-02-13 14:32:04 +00:00
DEBUG_LOG ( format ( " Thread %1% waiting for a new task. " ) % tid ) ;
2009-08-11 12:17:38 +00:00
// enter "suspended" state. when we return, the threadpool will either tell us to fuk off, or to execute a new task.
t - > ControlInterface . Suspend ( ) ;
// after resuming, this is where we will end up. start the loop again, check for tasks, then go back to the threadpool.
}
}
// at this point the t pointer has already been freed, so we can just cleanly exit.
//ExitThread(0);
// not reached
return 0 ;
}
ThreadStruct * CThreadPool : : StartThread ( ThreadContext * ExecutionTarget )
{
SetThreadName ( " Thread Starter " ) ;
HANDLE h ;
ThreadStruct * t = new ThreadStruct ;
t - > DeleteAfterExit = false ;
t - > ExecutionTarget = ExecutionTarget ;
//h = (HANDLE)_beginthreadex(NULL, 0, &thread_proc, (void*)t, 0, NULL);
t - > SetupMutex . Acquire ( ) ;
h = CreateThread ( NULL , 0 , & thread_proc , ( LPVOID ) t , 0 , ( LPDWORD ) & t - > ControlInterface . thread_id ) ;
t - > ControlInterface . Setup ( h ) ;
t - > SetupMutex . Release ( ) ;
return t ;
}
# else
static void * thread_proc ( void * param )
{
ThreadStruct * t = ( ThreadStruct * ) param ;
t - > SetupMutex . Acquire ( ) ;
2010-02-16 06:52:49 +00:00
stringstream strBuf ;
strBuf < < " ThreadPool::Thread " < < t - > ControlInterface . GetId ( ) < < " started. " ;
DEBUG_LOG ( strBuf . str ( ) ) ;
2009-08-11 12:17:38 +00:00
t - > SetupMutex . Release ( ) ;
for ( ; ; )
{
if ( t - > ExecutionTarget ! = NULL )
{
if ( t - > ExecutionTarget - > run ( ) )
delete t - > ExecutionTarget ;
t - > ExecutionTarget = NULL ;
}
if ( ! ThreadPool . ThreadExit ( t ) )
break ;
else
{
// enter "suspended" state. when we return, the threadpool will either tell us to fuk off, or to execute a new task.
t - > ControlInterface . Suspend ( ) ;
// after resuming, this is where we will end up. start the loop again, check for tasks, then go back to the threadpool.
}
}
//pthread_exit(0);
return NULL ;
}
ThreadStruct * CThreadPool : : StartThread ( ThreadContext * ExecutionTarget )
{
pthread_t target ;
ThreadStruct * t = new ThreadStruct ;
t - > ExecutionTarget = ExecutionTarget ;
t - > DeleteAfterExit = false ;
// lock the main mutex, to make sure id generation doesn't get messed up
_mutex . Acquire ( ) ;
t - > SetupMutex . Acquire ( ) ;
pthread_create ( & target , NULL , & thread_proc , ( void * ) t ) ;
t - > ControlInterface . Setup ( target ) ;
t - > SetupMutex . Release ( ) ;
_mutex . Release ( ) ;
return t ;
}
# endif