forked from Alepha/Alepha
Modernize thread code.
This commit is contained in:
130
Thread.cc
Normal file
130
Thread.cc
Normal file
@ -0,0 +1,130 @@
|
||||
static_assert( __cplusplus > 2020'99 );
|
||||
|
||||
#include "Thread.h"
|
||||
|
||||
#include <stop_token>
|
||||
#include <condition_variable>
|
||||
|
||||
#include <Alepha/Utility/StaticValue.h>
|
||||
#include <Alepha/Utility/evaluation_helpers.h>
|
||||
|
||||
namespace Alepha::Hydrogen::detail::Thread_m
|
||||
{
|
||||
namespace
|
||||
{
|
||||
struct ThreadState
|
||||
{
|
||||
std::mutex access;
|
||||
std::stop_source source;
|
||||
std::exception_ptr notification;
|
||||
|
||||
void
|
||||
deliver( std::exception_ptr ptr )
|
||||
{
|
||||
std::lock_guard lock{ access };
|
||||
if( notification ) return; // TODO: Don't swallow blocked interrupts?
|
||||
notification= std::move( ptr );
|
||||
source.request_stop();
|
||||
}
|
||||
|
||||
void
|
||||
interruption_point( std::lock_guard< std::mutex > & )
|
||||
{
|
||||
if( notification == nullptr ) return;
|
||||
source= std::stop_source{};
|
||||
auto next= std::move( notification );
|
||||
notification= nullptr;
|
||||
std::rethrow_exception( next );
|
||||
}
|
||||
|
||||
void
|
||||
interruption_point()
|
||||
{
|
||||
std::lock_guard lock( access );
|
||||
interruption_point( lock );
|
||||
}
|
||||
|
||||
std::stop_token
|
||||
getToken()
|
||||
{
|
||||
std::lock_guard lock( access );
|
||||
interruption_point( lock );
|
||||
return source.get_token();
|
||||
}
|
||||
};
|
||||
|
||||
thread_local Utility::StaticValue< std::shared_ptr< ThreadState > > state;
|
||||
}
|
||||
|
||||
struct ConditionVariable::Impl
|
||||
{
|
||||
std::condition_variable_any cond;
|
||||
};
|
||||
|
||||
ConditionVariable::~ConditionVariable()= default;
|
||||
|
||||
ConditionVariable::ConditionVariable()
|
||||
: pimpl( std::make_unique< Impl >() ) {}
|
||||
|
||||
void
|
||||
ConditionVariable::notify_one()
|
||||
{
|
||||
impl().cond.notify_one();
|
||||
}
|
||||
|
||||
void
|
||||
ConditionVariable::notify_all()
|
||||
{
|
||||
impl().cond.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
ConditionVariable::wait( unique_lock< mutex > &lock )
|
||||
{
|
||||
auto stop= Utility::evaluate <=[]
|
||||
{
|
||||
std::lock_guard lock( state()->access );
|
||||
return state()->source.get_token();
|
||||
};
|
||||
impl().cond.wait( lock, stop, [val= std::uint64_t()] mutable { return val++; } );
|
||||
state()->interruption_point();
|
||||
}
|
||||
|
||||
struct Thread::Impl
|
||||
{
|
||||
std::thread thr;
|
||||
std::weak_ptr< ThreadState > state;
|
||||
};
|
||||
|
||||
Thread::~Thread()= default;
|
||||
|
||||
Thread::Thread( std::function< void () > start )
|
||||
{
|
||||
auto newState= std::make_shared< ThreadState >();
|
||||
std::weak_ptr local= newState;
|
||||
|
||||
auto entry= [newState= std::move( newState ), start= std::move( start )]
|
||||
{
|
||||
state()= std::move( newState );
|
||||
start();
|
||||
};
|
||||
|
||||
pimpl= std::make_unique< Impl >( std::thread{ std::move( entry ) }, std::move( local ) );
|
||||
}
|
||||
|
||||
void
|
||||
Thread::join()
|
||||
{
|
||||
// TODO: Make interruptible.
|
||||
impl().thr.join();
|
||||
}
|
||||
|
||||
void
|
||||
Thread::notify( std::exception_ptr p )
|
||||
{
|
||||
const auto state= impl().state.lock();
|
||||
if( not state ) return;
|
||||
|
||||
state->deliver( std::move( p ) );
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user