2010-04-25 00:31:27 +00:00
/*
2010-04-24 21:37:39 +00:00
* Copyright ( C ) 2007 - 2009 Gabest
* http : //www.gabest.org
*
* This Program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 , or ( at your option )
* any later version .
2010-04-25 00:31:27 +00:00
*
2010-04-24 21:37:39 +00:00
* 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 General Public License for more details .
2010-04-25 00:31:27 +00:00
*
2010-04-24 21:37:39 +00:00
* You should have received a copy of the GNU General Public License
* along with GNU Make ; see the file COPYING . If not , write to
2010-04-25 00:31:27 +00:00
* the Free Software Foundation , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
2010-04-24 21:37:39 +00:00
* http : //www.gnu.org/copyleft/gpl.html
*
*/
# pragma once
2011-02-19 03:36:30 +00:00
# ifdef _WINDOWS
2010-04-24 21:37:39 +00:00
class GSThread
{
DWORD m_ThreadId ;
HANDLE m_hThread ;
2011-02-19 03:36:30 +00:00
static DWORD WINAPI StaticThreadProc ( void * lpParam ) ;
protected :
virtual void ThreadProc ( ) = 0 ;
void CreateThread ( ) ;
void CloseThread ( ) ;
public :
GSThread ( ) ;
virtual ~ GSThread ( ) ;
} ;
class GSCritSec
{
CRITICAL_SECTION m_cs ;
public :
GSCritSec ( ) { InitializeCriticalSection ( & m_cs ) ; }
~ GSCritSec ( ) { DeleteCriticalSection ( & m_cs ) ; }
void Lock ( ) { EnterCriticalSection ( & m_cs ) ; }
bool TryLock ( ) { return TryEnterCriticalSection ( & m_cs ) = = TRUE ; }
void Unlock ( ) { LeaveCriticalSection ( & m_cs ) ; }
} ;
2011-12-20 14:33:28 +00:00
class GSEvent
2011-02-19 03:36:30 +00:00
{
protected :
HANDLE m_hEvent ;
public :
2011-12-20 14:33:28 +00:00
GSEvent ( bool manual = false , bool initial = false ) { m_hEvent = CreateEvent ( NULL , manual , initial , NULL ) ; }
~ GSEvent ( ) { CloseHandle ( m_hEvent ) ; }
2011-02-19 03:36:30 +00:00
void Set ( ) { SetEvent ( m_hEvent ) ; }
2011-12-20 14:33:28 +00:00
void Reset ( ) { ResetEvent ( m_hEvent ) ; }
2011-02-19 03:36:30 +00:00
bool Wait ( ) { return WaitForSingleObject ( m_hEvent , INFINITE ) = = WAIT_OBJECT_0 ; }
} ;
2011-03-27 03:12:12 +00:00
2011-02-19 03:36:30 +00:00
# else
# include <pthread.h>
2011-02-19 10:27:10 +00:00
# include <semaphore.h>
2011-02-19 03:36:30 +00:00
class GSThread
{
pthread_attr_t m_thread_attr ;
pthread_t m_thread ;
static void * StaticThreadProc ( void * param ) ;
2010-04-24 21:37:39 +00:00
protected :
virtual void ThreadProc ( ) = 0 ;
void CreateThread ( ) ;
void CloseThread ( ) ;
public :
GSThread ( ) ;
virtual ~ GSThread ( ) ;
} ;
2011-02-19 03:36:30 +00:00
class GSCritSec
{
pthread_mutexattr_t m_mutex_attr ;
pthread_mutex_t m_mutex ;
public :
GSCritSec ( )
{
pthread_mutexattr_init ( & m_mutex_attr ) ;
pthread_mutexattr_settype ( & m_mutex_attr , PTHREAD_MUTEX_RECURSIVE_NP ) ;
pthread_mutex_init ( & m_mutex , & m_mutex_attr ) ;
}
~ GSCritSec ( )
{
pthread_mutex_destroy ( & m_mutex ) ;
pthread_mutexattr_destroy ( & m_mutex_attr ) ;
}
void Lock ( ) { pthread_mutex_lock ( & m_mutex ) ; }
bool TryLock ( ) { return pthread_mutex_trylock ( & m_mutex ) = = 0 ; }
void Unlock ( ) { pthread_mutex_unlock ( & m_mutex ) ; }
} ;
2011-12-20 14:33:28 +00:00
class GSEvent
2011-02-19 03:36:30 +00:00
{
protected :
2011-02-19 10:27:10 +00:00
sem_t m_sem ;
2011-02-19 03:36:30 +00:00
public :
2011-12-20 14:33:28 +00:00
GSEvent ( ) { sem_init ( & m_sem , 0 , 0 ) ; }
~ GSEvent ( ) { sem_destroy ( & m_sem ) ; }
2011-02-19 03:36:30 +00:00
2011-02-19 10:27:10 +00:00
void Set ( ) { sem_post ( & m_sem ) ; }
bool Wait ( ) { return sem_wait ( & m_sem ) = = 0 ; }
2011-02-19 03:36:30 +00:00
} ;
# endif
class GSAutoLock
{
protected :
GSCritSec * m_cs ;
public :
GSAutoLock ( GSCritSec * cs ) { m_cs = cs ; m_cs - > Lock ( ) ; }
~ GSAutoLock ( ) { m_cs - > Unlock ( ) ; }
} ;
2011-12-20 14:33:28 +00:00
class GSEventSpin
{
protected :
volatile long m_sync ;
volatile bool m_manual ;
public :
GSEventSpin ( bool manual = false , bool initial = false ) { m_sync = initial ? 1 : 0 ; m_manual = manual ; }
~ GSEventSpin ( ) { }
void Set ( ) { _interlockedbittestandset ( & m_sync , 0 ) ; }
void Reset ( ) { _interlockedbittestandreset ( & m_sync , 0 ) ; }
bool Wait ( )
{
if ( m_manual ) while ( ! m_sync ) _mm_pause ( ) ;
else while ( ! _interlockedbittestandreset ( & m_sync , 0 ) ) _mm_pause ( ) ;
return true ;
}
} ;
2011-12-22 14:36:54 +00:00
template < class T > class GSJobQueue : private GSThread
{
protected :
int m_count ;
queue < T > m_queue ;
volatile bool m_exit ;
2011-12-24 15:02:48 +00:00
struct { GSCritSec lock ; GSEvent notempty ; volatile long count ; } m_ev ;
2011-12-22 14:36:54 +00:00
# ifdef _WINDOWS
struct { SRWLOCK lock ; CONDITION_VARIABLE notempty , empty ; bool available ; } m_cv ;
2011-12-23 02:49:27 +00:00
HMODULE m_kernel32 ;
typedef void ( WINAPI * InitializeConditionVariablePtr ) ( CONDITION_VARIABLE * ConditionVariable ) ;
typedef void ( WINAPI * WakeConditionVariablePtr ) ( CONDITION_VARIABLE * ConditionVariable ) ;
typedef void ( WINAPI * WakeAllConditionVariablePtr ) ( CONDITION_VARIABLE * ConditionVariable ) ;
typedef void ( WINAPI * SleepConditionVariableSRWPtr ) ( CONDITION_VARIABLE * ConditionVariable , SRWLOCK * SRWLock , DWORD dwMilliseconds , ULONG Flags ) ;
typedef void ( WINAPI * InitializeSRWLockPtr ) ( SRWLOCK * SRWLock ) ;
typedef void ( WINAPI * AcquireSRWLockExclusivePtr ) ( SRWLOCK * SRWLock ) ;
typedef void ( WINAPI * ReleaseSRWLockExclusivePtr ) ( SRWLOCK * SRWLock ) ;
InitializeConditionVariablePtr pInitializeConditionVariable ;
WakeConditionVariablePtr pWakeConditionVariable ;
WakeAllConditionVariablePtr pWakeAllConditionVariable ;
SleepConditionVariableSRWPtr pSleepConditionVariableSRW ;
InitializeSRWLockPtr pInitializeSRWLock ; ;
AcquireSRWLockExclusivePtr pAcquireSRWLockExclusive ;
ReleaseSRWLockExclusivePtr pReleaseSRWLockExclusive ;
2011-12-26 22:30:59 +00:00
# elif defined(_LINUX)
struct { pthread_mutex_t lock ; pthread_cond_t notempty , empty ; bool available ; } m_cv ;
2011-12-22 14:36:54 +00:00
# endif
void ThreadProc ( )
{
if ( m_cv . available )
{
2011-12-26 22:30:59 +00:00
# ifdef _WINDOWS
2011-12-23 02:49:27 +00:00
pAcquireSRWLockExclusive ( & m_cv . lock ) ;
2011-12-22 14:36:54 +00:00
while ( true )
{
while ( m_queue . empty ( ) )
{
2011-12-23 02:49:27 +00:00
pSleepConditionVariableSRW ( & m_cv . notempty , & m_cv . lock , INFINITE , 0 ) ;
2011-12-22 14:36:54 +00:00
2011-12-23 02:49:27 +00:00
if ( m_exit ) { pReleaseSRWLockExclusive ( & m_cv . lock ) ; return ; }
2011-12-22 14:36:54 +00:00
}
2011-12-24 15:02:48 +00:00
{
2011-12-25 07:26:42 +00:00
// NOTE: this is scoped because we must make sure the last item is no longer around when Wait detects an empty queue
2011-12-24 15:02:48 +00:00
T item = m_queue . front ( ) ;
2011-12-22 14:36:54 +00:00
2011-12-24 15:02:48 +00:00
pReleaseSRWLockExclusive ( & m_cv . lock ) ;
2011-12-22 14:36:54 +00:00
2011-12-24 15:02:48 +00:00
Process ( item ) ;
2011-12-22 14:36:54 +00:00
2011-12-24 15:02:48 +00:00
pAcquireSRWLockExclusive ( & m_cv . lock ) ;
2011-12-22 14:36:54 +00:00
2011-12-24 15:02:48 +00:00
m_queue . pop ( ) ;
}
2011-12-22 14:36:54 +00:00
if ( m_queue . empty ( ) )
{
2011-12-23 02:49:27 +00:00
pWakeConditionVariable ( & m_cv . empty ) ;
2011-12-22 14:36:54 +00:00
}
}
2011-12-26 22:30:59 +00:00
# elif defined(_LINUX)
pthread_mutex_lock ( & m_cv . lock ) ;
while ( true )
{
while ( m_queue . empty ( ) )
{
pthread_cond_wait ( & m_cv . notempty , & m_cv . lock ) ;
if ( m_exit ) { pthread_mutex_unlock ( & m_cv . lock ) ; return ; }
}
{
// NOTE: this is scoped because we must make sure the last item is no longer around when Wait detects an empty queue
T item = m_queue . front ( ) ;
pthread_mutex_unlock ( & m_cv . lock ) ;
Process ( item ) ;
pthread_mutex_lock ( & m_cv . lock ) ;
m_queue . pop ( ) ;
}
if ( m_queue . empty ( ) )
{
pthread_cond_signal ( & m_cv . empty ) ;
}
}
# endif
2011-12-22 14:36:54 +00:00
}
else
{
2011-12-24 15:02:48 +00:00
m_ev . lock . Lock ( ) ;
while ( true )
2011-12-22 14:36:54 +00:00
{
2011-12-24 15:02:48 +00:00
while ( m_queue . empty ( ) )
{
m_ev . lock . Unlock ( ) ;
m_ev . notempty . Wait ( ) ;
if ( m_exit ) { return ; }
m_ev . lock . Lock ( ) ;
}
2011-12-22 14:36:54 +00:00
{
2011-12-24 15:02:48 +00:00
// NOTE: this is scoped because we must make sure the last item is no longer around when Wait detects an empty queue
2011-12-22 14:36:54 +00:00
2011-12-24 15:02:48 +00:00
T item = m_queue . front ( ) ;
2011-12-22 14:36:54 +00:00
2011-12-24 15:02:48 +00:00
m_ev . lock . Unlock ( ) ;
2011-12-22 14:36:54 +00:00
Process ( item ) ;
2011-12-24 15:02:48 +00:00
m_ev . lock . Lock ( ) ;
2011-12-22 14:36:54 +00:00
2011-12-24 15:02:48 +00:00
m_queue . pop ( ) ;
2011-12-22 14:36:54 +00:00
}
2011-12-24 15:02:48 +00:00
_InterlockedDecrement ( & m_ev . count ) ;
2011-12-22 14:36:54 +00:00
}
}
}
public :
GSJobQueue ( )
: m_count ( 0 )
, m_exit ( false )
{
2011-12-24 15:02:48 +00:00
m_ev . count = 0 ;
2011-12-23 02:49:27 +00:00
# ifdef _WINDOWS
2011-12-22 14:36:54 +00:00
m_cv . available = false ;
2011-12-23 02:49:27 +00:00
m_kernel32 = LoadLibrary ( " kernel32.dll " ) ;
2011-12-22 14:36:54 +00:00
2011-12-23 02:49:27 +00:00
pInitializeConditionVariable = ( InitializeConditionVariablePtr ) GetProcAddress ( m_kernel32 , " InitializeConditionVariable " ) ;
pWakeConditionVariable = ( WakeConditionVariablePtr ) GetProcAddress ( m_kernel32 , " WakeConditionVariable " ) ;
pWakeAllConditionVariable = ( WakeAllConditionVariablePtr ) GetProcAddress ( m_kernel32 , " WakeAllConditionVariable " ) ;
pSleepConditionVariableSRW = ( SleepConditionVariableSRWPtr ) GetProcAddress ( m_kernel32 , " SleepConditionVariableSRW " ) ;
pInitializeSRWLock = ( InitializeSRWLockPtr ) GetProcAddress ( m_kernel32 , " InitializeSRWLock " ) ;
pAcquireSRWLockExclusive = ( AcquireSRWLockExclusivePtr ) GetProcAddress ( m_kernel32 , " AcquireSRWLockExclusive " ) ;
pReleaseSRWLockExclusive = ( ReleaseSRWLockExclusivePtr ) GetProcAddress ( m_kernel32 , " ReleaseSRWLockExclusive " ) ;
2011-12-22 14:36:54 +00:00
2011-12-23 02:49:27 +00:00
if ( pInitializeConditionVariable ! = NULL )
2011-12-22 14:36:54 +00:00
{
2011-12-23 02:49:27 +00:00
pInitializeSRWLock ( & m_cv . lock ) ;
pInitializeConditionVariable ( & m_cv . notempty ) ;
pInitializeConditionVariable ( & m_cv . empty ) ;
2011-12-22 14:36:54 +00:00
m_cv . available = true ;
}
2011-12-26 22:30:59 +00:00
# elif defined(_LINUX)
m_cv . available = true ;
// FIXME attribute
pthread_cond_init ( & m_cv . notempty , NULL ) ;
pthread_cond_init ( & m_cv . empty , NULL ) ;
pthread_mutex_init ( & m_cv . lock , NULL ) ;
2011-12-22 14:36:54 +00:00
# endif
CreateThread ( ) ;
}
virtual ~ GSJobQueue ( )
{
m_exit = true ;
if ( m_cv . available )
{
2011-12-26 22:30:59 +00:00
# ifdef _WINDOWS
2011-12-23 02:49:27 +00:00
pWakeConditionVariable ( & m_cv . notempty ) ;
2011-12-26 22:30:59 +00:00
# elif defined(_LINUX)
pthread_cond_signal ( & m_cv . notempty ) ;
pthread_mutex_destroy ( & m_cv . lock ) ;
pthread_cond_destroy ( & m_cv . notempty ) ;
pthread_cond_destroy ( & m_cv . empty ) ;
# endif
2011-12-22 14:36:54 +00:00
}
else
{
m_ev . notempty . Set ( ) ;
}
2011-12-26 22:30:59 +00:00
# ifdef _WINDOWS
2011-12-23 02:49:27 +00:00
if ( m_kernel32 ! = NULL )
{
FreeLibrary ( m_kernel32 ) ; // lol, decrement the refcount anyway
}
2011-12-22 14:36:54 +00:00
# endif
2011-12-26 22:30:59 +00:00
2011-12-22 14:36:54 +00:00
}
int GetCount ( ) const
{
return m_count ;
}
virtual void Push ( const T & item )
{
if ( m_cv . available )
{
2011-12-26 22:30:59 +00:00
# ifdef _WINDOWS
2011-12-23 02:49:27 +00:00
pAcquireSRWLockExclusive ( & m_cv . lock ) ;
2011-12-22 14:36:54 +00:00
m_queue . push ( item ) ;
2011-12-23 02:49:27 +00:00
pReleaseSRWLockExclusive ( & m_cv . lock ) ;
2011-12-22 14:36:54 +00:00
2011-12-23 02:49:27 +00:00
pWakeConditionVariable ( & m_cv . notempty ) ;
2011-12-26 22:30:59 +00:00
# elif defined(_LINUX)
pthread_mutex_lock ( & m_cv . lock ) ;
m_queue . push ( item ) ;
pthread_mutex_unlock ( & m_cv . lock ) ;
pthread_cond_signal ( & m_cv . notempty ) ;
# endif
2011-12-22 14:36:54 +00:00
}
else
{
GSAutoLock l ( & m_ev . lock ) ;
m_queue . push ( item ) ;
2011-12-24 15:02:48 +00:00
_InterlockedIncrement ( & m_ev . count ) ;
2011-12-22 14:36:54 +00:00
m_ev . notempty . Set ( ) ;
}
m_count + + ;
}
virtual void Wait ( )
{
if ( m_cv . available )
{
2011-12-26 22:30:59 +00:00
# ifdef _WINDOWS
2011-12-23 02:49:27 +00:00
pAcquireSRWLockExclusive ( & m_cv . lock ) ;
2011-12-22 14:36:54 +00:00
while ( ! m_queue . empty ( ) )
{
2011-12-23 02:49:27 +00:00
pSleepConditionVariableSRW ( & m_cv . empty , & m_cv . lock , INFINITE , 0 ) ;
2011-12-22 14:36:54 +00:00
}
2011-12-23 02:49:27 +00:00
pReleaseSRWLockExclusive ( & m_cv . lock ) ;
2011-12-26 22:30:59 +00:00
# elif defined(_LINUX)
pthread_mutex_lock ( & m_cv . lock ) ;
while ( ! m_queue . empty ( ) )
{
pthread_cond_wait ( & m_cv . empty , & m_cv . lock ) ;
}
pthread_mutex_unlock ( & m_cv . lock ) ;
# endif
2011-12-22 14:36:54 +00:00
}
else
{
2011-12-24 15:02:48 +00:00
// NOTE: it is the safest to have our own counter because m_queue.pop() might decrement its own before the last item runs out of its scope and gets destroyed (implementation dependent)
2011-12-22 14:36:54 +00:00
2011-12-24 15:02:48 +00:00
while ( m_ev . count > 0 ) _mm_pause ( ) ;
2011-12-22 14:36:54 +00:00
}
m_count + + ;
}
virtual void Process ( T & item ) = 0 ;
} ;