352 lines
8.6 KiB
C++
352 lines
8.6 KiB
C++
/******************************************************************************/
|
|
/* THREADING - INLINE CODE ****************************************************/
|
|
/******************************************************************************/
|
|
|
|
#ifndef _H_LW_LIB_INLINE_THREADING
|
|
#define _H_LW_LIB_INLINE_THREADING
|
|
#include <lw/lib/Threading.hh>
|
|
namespace lw {
|
|
|
|
|
|
/*= T_ReadWriteMutex =========================================================*/
|
|
|
|
inline T_ReadWriteMutex::T_ReadWriteMutex( )
|
|
: state_( 0 )
|
|
{ }
|
|
|
|
inline T_ReadWriteMutex::~T_ReadWriteMutex( )
|
|
{
|
|
assert( state_ == 0 );
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
inline void T_ReadWriteMutex::lock( )
|
|
{
|
|
T_ExclusiveLock lock( mutex_ );
|
|
writerBlock_.wait( lock , [this]( ) {
|
|
return ( state_ & C_WLOCKED_ ) == 0;
|
|
} );
|
|
state_ |= C_WLOCKED_;
|
|
readerBlock_.wait( lock , [this]( ) {
|
|
return ( state_ & C_READERS_ ) == 0;
|
|
} );
|
|
}
|
|
|
|
inline bool T_ReadWriteMutex::try_lock( )
|
|
{
|
|
T_ExclusiveLock lock( mutex_ , std::try_to_lock );
|
|
if ( lock.owns_lock( ) && state_ == 0 ) {
|
|
state_ = C_WLOCKED_;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
inline void T_ReadWriteMutex::unlock( )
|
|
{
|
|
T_ScopeLock lock( mutex_ );
|
|
assert( ( state_ & C_WLOCKED_ ) != 0 );
|
|
state_ = 0;
|
|
writerBlock_.notify_all( );
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
inline void T_ReadWriteMutex::lock_shared( )
|
|
{
|
|
T_ExclusiveLock lock( mutex_ );
|
|
writerBlock_.wait( lock , [this]( ) {
|
|
return state_ < C_READERS_;
|
|
} );
|
|
state_ ++;
|
|
}
|
|
|
|
inline bool T_ReadWriteMutex::try_lock_shared( )
|
|
{
|
|
T_ExclusiveLock lock( mutex_ , std::try_to_lock );
|
|
if ( !lock.owns_lock( ) && state_ < C_READERS_ ) {
|
|
state_ ++;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
inline void T_ReadWriteMutex::unlock_shared( )
|
|
{
|
|
T_ScopeLock lock( mutex_ );
|
|
assert( ( state_ & C_READERS_ ) != 0 );
|
|
auto previous( state_ -- );
|
|
if ( previous == ( C_WLOCKED_ & 1 ) ) {
|
|
readerBlock_.notify_one( );
|
|
} else if ( previous == C_READERS_ ) {
|
|
writerBlock_.notify_one( );
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
inline void T_ReadWriteMutex::upgradeLock( )
|
|
{
|
|
T_ExclusiveLock lock( mutex_ );
|
|
assert( ( state_ & C_READERS_ ) != 0 );
|
|
state_ --;
|
|
writerBlock_.wait( lock , [this]( ) {
|
|
return ( state_ & C_WLOCKED_ ) == 0;
|
|
} );
|
|
state_ |= C_WLOCKED_;
|
|
readerBlock_.wait( lock , [this]( ) {
|
|
return ( state_ & C_READERS_ ) == 0;
|
|
} );
|
|
}
|
|
|
|
|
|
/*= T_ReadLock ===============================================================*/
|
|
|
|
inline T_ReadLock::T_ReadLock( ) noexcept
|
|
: std::shared_lock< T_ReadWriteMutex >( )
|
|
{ }
|
|
|
|
inline T_ReadLock::T_ReadLock( T_ReadLock&& other )
|
|
: std::shared_lock< T_ReadWriteMutex >( std::move( other ) )
|
|
{ }
|
|
|
|
inline T_ReadLock::T_ReadLock( T_ReadWriteMutex& m )
|
|
: std::shared_lock< T_ReadWriteMutex >( m )
|
|
{ }
|
|
|
|
inline T_ReadLock::T_ReadLock( T_ReadWriteMutex& m , std::defer_lock_t t )
|
|
: std::shared_lock< T_ReadWriteMutex >( m , t )
|
|
{ }
|
|
|
|
inline T_ReadLock::T_ReadLock( T_ReadWriteMutex& m , std::try_to_lock_t t )
|
|
: std::shared_lock< T_ReadWriteMutex >( m , t )
|
|
{ }
|
|
|
|
inline T_ReadLock::T_ReadLock( T_ReadWriteMutex& m , std::adopt_lock_t t )
|
|
: std::shared_lock< T_ReadWriteMutex >( m , t )
|
|
{ }
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
inline T_WriteLock T_ReadLock::upgrade( )
|
|
{
|
|
assert( owns_lock( ) );
|
|
RP_ReadWriteMutex mutex( release( ) );
|
|
mutex->upgradeLock( );
|
|
return T_WriteLock( *mutex , std::adopt_lock );
|
|
}
|
|
|
|
|
|
/*= T_RingBuffer =============================================================*/
|
|
|
|
template< typename T >
|
|
inline T_RingBuffer< T >::T_RingBuffer( uint32_t expand )
|
|
: expand_( expand ) , data_( nullptr ) , allocated_( 0 ) ,
|
|
used_( 0 ) , readPos_( 0 )
|
|
{
|
|
assert( expand );
|
|
}
|
|
|
|
template< typename T >
|
|
inline T_RingBuffer< T >::T_RingBuffer( T_RingBuffer< T > const& other )
|
|
: expand_( other.expand_ ) , allocated_( other.allocated_ ) ,
|
|
used_( other.used_ ) , readPos_( 0 )
|
|
{
|
|
if ( allocated_ ) {
|
|
data_ = reinterpret_cast< T* >( ::operator new( sizeof( T ) * allocated_ ) );
|
|
for ( uint32_t i = 0 ; i < other.used_ ; i ++ ) {
|
|
const auto sidx( ( i + other.readPos_ ) % allocated_ );
|
|
new ( reinterpret_cast< char* >( &data_[ i ] ) ) T( other.data_[ sidx ] );
|
|
}
|
|
} else {
|
|
data_ = nullptr;
|
|
}
|
|
}
|
|
|
|
template< typename T >
|
|
inline T_RingBuffer< T >::T_RingBuffer( T_RingBuffer< T >&& other ) noexcept
|
|
: T_RingBuffer( other.growth( ) )
|
|
{
|
|
swap( *this , other );
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
template< typename T >
|
|
inline T_RingBuffer< T >::~T_RingBuffer( )
|
|
{
|
|
free( );
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
template< typename T >
|
|
inline T_RingBuffer< T >& T_RingBuffer< T >::operator =( T_RingBuffer< T > const& other )
|
|
{
|
|
free( );
|
|
|
|
expand_ = other.expand_;
|
|
allocated_ = other.allocated_;
|
|
used_ = other.used_;
|
|
readPos_ = 0;
|
|
if ( allocated_ ) {
|
|
data_ = reinterpret_cast< T* >( ::operator new( sizeof( T ) * allocated_ ) );
|
|
for ( uint32_t i = 0 ; i < other.used_ ; i ++ ) {
|
|
const auto sidx( ( i + other.readPos_ ) % allocated_ );
|
|
new ( reinterpret_cast< char* >( &data_[ i ] ) ) T( other.data_[ sidx ] );
|
|
}
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
template< typename T >
|
|
inline T_RingBuffer< T >& T_RingBuffer< T >::operator =( T_RingBuffer< T >&& other ) noexcept
|
|
{
|
|
free( );
|
|
expand_ = other.expand_;
|
|
swap( *this , other );
|
|
return *this;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
template< typename T >
|
|
inline void swap( T_RingBuffer< T >& lhs , T_RingBuffer< T >& rhs ) noexcept
|
|
{
|
|
using std::swap;
|
|
swap( lhs.expand_ , rhs.expand_ );
|
|
swap( lhs.data_ , rhs.data_ );
|
|
swap( lhs.allocated_ , rhs.allocated_ );
|
|
swap( lhs.used_ , rhs.used_ );
|
|
swap( lhs.readPos_ , rhs.readPos_ );
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
template< typename T >
|
|
inline void T_RingBuffer< T >::free( )
|
|
{
|
|
for ( uint32_t i = 0 ; i < used_ ; i ++ ) {
|
|
const auto idx( ( readPos_ + i ) % allocated_ );
|
|
data_[ idx ].~T( );
|
|
}
|
|
::operator delete( (void*) data_ );
|
|
data_ = nullptr;
|
|
allocated_ = 0;
|
|
used_ = 0;
|
|
readPos_ = 0;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
template< typename T >
|
|
inline uint32_t T_RingBuffer< T >::size( ) const
|
|
{
|
|
return used_;
|
|
}
|
|
|
|
template< typename T >
|
|
inline uint32_t T_RingBuffer< T >::capacity( ) const
|
|
{
|
|
return allocated_;
|
|
}
|
|
|
|
template< typename T >
|
|
inline uint32_t T_RingBuffer< T >::growth( ) const
|
|
{
|
|
return expand_;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
template< typename T >
|
|
inline bool T_RingBuffer< T >::readNext( T& output )
|
|
{
|
|
if ( used_ == 0 ) {
|
|
return false;
|
|
}
|
|
output = std::move( data_[ readPos_ ] );
|
|
data_[ readPos_ ].~T( );
|
|
readPos_ = ( readPos_ + 1 ) % allocated_;
|
|
used_ --;
|
|
return true;
|
|
}
|
|
|
|
template< typename T >
|
|
inline bool T_RingBuffer< T >::readAll( T_Array< T >& output )
|
|
{
|
|
if ( used_ == 0 ) {
|
|
return false;
|
|
}
|
|
for ( uint32_t i = 0 ; i < used_ ; i ++ ) {
|
|
output.add( std::move( data_[ ( readPos_ + i ) % allocated_ ] ) );
|
|
}
|
|
for ( uint32_t i = 0 ; i < used_ ; i ++ ) {
|
|
data_[ ( readPos_ + i ) % allocated_ ].~T( );
|
|
}
|
|
used_ = 0;
|
|
return true;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
template< typename T >
|
|
inline void T_RingBuffer< T >::put( T const& input )
|
|
{
|
|
if ( used_ == allocated_ ) {
|
|
expand( );
|
|
}
|
|
const auto idx( ( readPos_ + used_ ) % allocated_ );
|
|
new ( reinterpret_cast< char* >( &data_[ idx ] ) ) T( input );
|
|
used_ ++;
|
|
}
|
|
|
|
template< typename T >
|
|
inline void T_RingBuffer< T >::put( T&& input )
|
|
{
|
|
if ( used_ == allocated_ ) {
|
|
expand( );
|
|
}
|
|
const auto idx( ( readPos_ + used_ ) % allocated_ );
|
|
new ( reinterpret_cast< char* >( &data_[ idx ] ) ) T( std::move( input ) );
|
|
used_ ++;
|
|
}
|
|
|
|
template< typename T >
|
|
template< typename... ArgTypes >
|
|
inline void T_RingBuffer< T >::putNew( ArgTypes&&... arguments )
|
|
{
|
|
if ( used_ == allocated_ ) {
|
|
expand( );
|
|
}
|
|
const auto idx( ( readPos_ + used_ ) % allocated_ );
|
|
new ( reinterpret_cast< char* >( &data_[ idx ] ) ) T( std::forward< ArgTypes >( arguments ) ... );
|
|
used_ ++;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
template< typename T >
|
|
inline void T_RingBuffer< T >::expand( )
|
|
{
|
|
const auto nsz( allocated_ + expand_ );
|
|
T* nData( reinterpret_cast< T* >( ::operator new( sizeof( T ) * nsz ) ) );
|
|
for ( uint32_t i = 0 ; i < used_ ; i ++ ) {
|
|
const auto idx( ( readPos_ + i ) % allocated_ );
|
|
new( reinterpret_cast< char* >( &nData[ i ] ) ) T( std::move( data_[ idx ] ) );
|
|
data_[ idx ].~T( );
|
|
}
|
|
|
|
using std::swap;
|
|
swap( data_ , nData );
|
|
allocated_ = nsz;
|
|
readPos_ = 0;
|
|
::operator delete( (void*) nData );
|
|
}
|
|
|
|
|
|
|
|
}
|
|
#endif // _H_LW_LIB_INLINE_THREADING
|