forked from Alepha/Alepha
Bring in the mockination work and Truss from old.
It's all a mess -- not in the new unified form and namespace. I need to do a big cleanup pass.
This commit is contained in:
62
Mockination/MockCondition.h
Normal file
62
Mockination/MockCondition.h
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#include <Alepha/Alepha.h>
|
||||||
|
register "Alepha/Mockination/MockCondition.h";
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include <Alepha/Mockination/MockMutex.h>
|
||||||
|
#include <Alepha/ScopedUsage.h>
|
||||||
|
|
||||||
|
namespace Alepha
|
||||||
|
{
|
||||||
|
inline namespace Aluminum
|
||||||
|
{
|
||||||
|
namespace Mockination
|
||||||
|
{
|
||||||
|
class MockCondition
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
Alepha::Truss::mutex access;
|
||||||
|
Alepha::Truss::condition condition;
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline void
|
||||||
|
notify_all()
|
||||||
|
{
|
||||||
|
this->condition.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
notify_one()
|
||||||
|
{
|
||||||
|
this->condition.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
template< typename UniqueLock >
|
||||||
|
inline void
|
||||||
|
wait( UniqueLock &lock )
|
||||||
|
{
|
||||||
|
auto fake_lock= Alepha::use_unique( this->access );
|
||||||
|
lock.unlock();
|
||||||
|
this->condition.wait( fake_lock );
|
||||||
|
lock.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
template< typename UniqueLock, typename Predicate >
|
||||||
|
inline void
|
||||||
|
wait( UniqueLock &lock, Predicate &&predicate )
|
||||||
|
{
|
||||||
|
while( !predicate() )
|
||||||
|
{
|
||||||
|
{
|
||||||
|
auto fake_lock= Alepha::use_unique( this->access );
|
||||||
|
lock.unlock();
|
||||||
|
this->condition.wait( fake_lock );
|
||||||
|
}
|
||||||
|
lock.lock();
|
||||||
|
}
|
||||||
|
assert( predicate() );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
235
Mockination/MockFunction.h
Normal file
235
Mockination/MockFunction.h
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
#include <Alepha/Alepha.h>
|
||||||
|
register "Alepha/Mockination/mockination";
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <Alepha/Meta/functor_traits.h>
|
||||||
|
#include <Alepha/Meta/require_relationship.h>
|
||||||
|
|
||||||
|
#include <Alepha/Truss/function.h>
|
||||||
|
|
||||||
|
namespace Alepha
|
||||||
|
{
|
||||||
|
inline namespace Aluminum
|
||||||
|
{
|
||||||
|
namespace Mockination
|
||||||
|
{
|
||||||
|
template< std::size_t order, std::size_t count >
|
||||||
|
struct overload_count
|
||||||
|
{
|
||||||
|
using type= overload_count;
|
||||||
|
static constexpr std::size_t value= count;
|
||||||
|
};
|
||||||
|
|
||||||
|
// We'd like a `boost::mpl::map`, probably. A metafunction for now.
|
||||||
|
|
||||||
|
template< typename ... Args > struct count_map;
|
||||||
|
|
||||||
|
template< std::size_t order, std::size_t count >
|
||||||
|
struct count_map< overload_count< order, count > >
|
||||||
|
{
|
||||||
|
using entry_type= overload_count< order, count >;
|
||||||
|
};
|
||||||
|
|
||||||
|
template< std::size_t order, std::size_t count, typename ... Args >
|
||||||
|
struct count_map< overload_count< order, count >, Args... >
|
||||||
|
{
|
||||||
|
using entry_type= overload_count< order, count >;
|
||||||
|
};
|
||||||
|
|
||||||
|
//template< template CountMap, std::size_t order_key > struct find_entry;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
template< std::size_t order_key, std::size_t count, typename ... Args >
|
||||||
|
find_entry< count_map< overload_count< order_key, count >, Args... >,
|
||||||
|
{
|
||||||
|
using type=
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template< int id, typename Function > class MockFunctionImpl;
|
||||||
|
|
||||||
|
template< int id, typename Function, typename ... Selection > struct select_overload;
|
||||||
|
|
||||||
|
template< int id, typename Function >
|
||||||
|
struct select_overload< id, Function, Function >
|
||||||
|
{
|
||||||
|
using type= MockFunctionImpl< id, Function >;
|
||||||
|
};
|
||||||
|
|
||||||
|
template< int id, typename Function, typename Selection0, typename ... Selections >
|
||||||
|
struct select_overload< id, Function, Selection0, Selections... >
|
||||||
|
: select_overload< id, Function, Selections... > {};
|
||||||
|
|
||||||
|
template< int id, typename Function, typename ... Selections >
|
||||||
|
struct select_overload< id, Function, Function, Selections... >
|
||||||
|
{
|
||||||
|
using type= MockFunctionImpl< id, Function >;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class skip_execution {};
|
||||||
|
|
||||||
|
|
||||||
|
template< int id, typename ... Args >
|
||||||
|
class MockFunctionImpl< id, void ( Args ... ) >
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static Alepha::Truss::function< void ( Args ... ) > impl;
|
||||||
|
|
||||||
|
using return_type= void;
|
||||||
|
|
||||||
|
template< typename Needed >
|
||||||
|
using overload= typename select_overload< id, Needed, void ( Args... ) >::type;
|
||||||
|
|
||||||
|
MockFunctionImpl()= default;
|
||||||
|
|
||||||
|
static void clear() { impl= nullptr; }
|
||||||
|
|
||||||
|
void
|
||||||
|
operator() ( Args ... args ) const
|
||||||
|
{
|
||||||
|
if( impl == nullptr ) abort();
|
||||||
|
return impl( std::forward< Args >( args )... );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_operation( Alepha::Truss::function< void ( Args ... ) > i )
|
||||||
|
{
|
||||||
|
std::cerr << "Set operation impl..." << std::endl;
|
||||||
|
impl= std::move( i );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_operation_impl( Alepha::Truss::function< void ( Args ... ) > i ) { impl= std::move( i ); }
|
||||||
|
|
||||||
|
static void
|
||||||
|
add_operation( Alepha::Truss::function< void ( Args ... ) > i )
|
||||||
|
{
|
||||||
|
Alepha::Truss::function< void ( Args ... ) > first= impl ? impl : []( Args ... ){};
|
||||||
|
impl= [first, i]( Args... args )
|
||||||
|
{
|
||||||
|
try { i( args... ); } catch( const skip_execution & ) {}
|
||||||
|
first( args... );
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template< int id, typename ... Args >
|
||||||
|
Alepha::Truss::function< void ( Args ... ) > MockFunctionImpl< id, void ( Args ... ) >::impl= nullptr;
|
||||||
|
|
||||||
|
template< int id, typename Rv, typename ... Args >
|
||||||
|
class MockFunctionImpl< id, Rv ( Args ... ) >
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static Alepha::Truss::function< Rv ( Args ... ) > impl;
|
||||||
|
|
||||||
|
using return_type= Rv;
|
||||||
|
|
||||||
|
template< typename Needed >
|
||||||
|
using overload= typename select_overload< id, Needed, Rv ( Args... ) >::type;
|
||||||
|
|
||||||
|
MockFunctionImpl()= default;
|
||||||
|
|
||||||
|
static void clear() { impl= nullptr; }
|
||||||
|
|
||||||
|
Rv operator() ( Args ... args ) const { return impl( std::forward< Args >( args )... ); }
|
||||||
|
|
||||||
|
static void
|
||||||
|
set_result( Rv r )
|
||||||
|
{
|
||||||
|
impl= [r]( Args ... args ){ return r; };
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_operation( Alepha::Truss::function< Rv ( Args ... ) > i ) { impl= std::move( i ); }
|
||||||
|
static void set_operation_impl( Alepha::Truss::function< Rv ( Args ... ) > i ) { impl= std::move( i ); }
|
||||||
|
|
||||||
|
static Rv
|
||||||
|
add_operation( Alepha::Truss::function< Rv ( Args ... ) > i )
|
||||||
|
{
|
||||||
|
impl= [first= impl, i]( Args ... args )
|
||||||
|
{
|
||||||
|
try { return i( args... ); } catch( const skip_execution & ) {}
|
||||||
|
return first( args... );
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template< int id, typename Rv, typename ... Args >
|
||||||
|
Alepha::Truss::function< Rv ( Args... ) > MockFunctionImpl< id, Rv ( Args... ) >::impl= nullptr;
|
||||||
|
|
||||||
|
template< int id, typename ... Funcs > class MockFunction;
|
||||||
|
|
||||||
|
template< int id, typename Rv, typename ... Args >
|
||||||
|
class MockFunction< id, Rv( Args ... ) >
|
||||||
|
: public MockFunctionImpl< id, Rv( Args... ) >
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void clear_all() { MockFunctionImpl< id, Rv( Args... ) >::clear(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template< typename M > struct match;
|
||||||
|
template< typename Class >
|
||||||
|
struct match< void (Class::*) ( std::size_t ) > : std::true_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
template< typename Class >
|
||||||
|
struct match< void (Class::*) ( std::size_t ) const > : std::true_type
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
template< typename T >
|
||||||
|
auto
|
||||||
|
soak_traits( T )//typename std::enable_if< std::is_class< T >::value, T >::type )
|
||||||
|
{
|
||||||
|
return typename Meta::functor_traits< decltype( &T::operator() ) >::type{};
|
||||||
|
}
|
||||||
|
|
||||||
|
template< typename Rv, typename ... Args >
|
||||||
|
Meta::functor_traits< Rv ( Args... ) >
|
||||||
|
soak_traits( Rv ( Args... ) )
|
||||||
|
{
|
||||||
|
return Meta::functor_traits< Rv ( Args... ) >{};
|
||||||
|
}
|
||||||
|
|
||||||
|
template< int id, typename Func0, typename ... Funcs >
|
||||||
|
class MockFunction< id, Func0, Funcs... >
|
||||||
|
: public MockFunctionImpl< id, Func0 >, public MockFunction< id, Funcs... >
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template< typename Needed >
|
||||||
|
using overload= typename select_overload< id, Needed, Func0, Funcs... >::type;
|
||||||
|
|
||||||
|
template< typename Callable >
|
||||||
|
static void
|
||||||
|
set_operation( Callable c )
|
||||||
|
{
|
||||||
|
std::cerr << "Set operation deduced..." << std::endl;
|
||||||
|
auto magic_traits= soak_traits( c );
|
||||||
|
using magic_traits_type= decltype( magic_traits );
|
||||||
|
using traits= typename magic_traits_type::type;
|
||||||
|
using ftype= typename traits::std_function_type;
|
||||||
|
|
||||||
|
static_assert( Meta::require_relationship< std::is_base_of,
|
||||||
|
MockFunctionImpl< id, typename traits::functor_type >,
|
||||||
|
MockFunction >::type::value,
|
||||||
|
"No overload exists in this mock for the specified function type." );
|
||||||
|
MockFunctionImpl< id, typename traits::functor_type >::set_operation_impl( c );
|
||||||
|
}
|
||||||
|
|
||||||
|
template< typename Callable, typename FuncType >
|
||||||
|
static void
|
||||||
|
set_operation( Callable c )
|
||||||
|
{
|
||||||
|
std::cerr << "Set operation forced..." << std::endl;
|
||||||
|
MockFunctionImpl< id, FuncType >::set_operation_impl( Alepha::Truss::function< FuncType >{ c } );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
clear_all()
|
||||||
|
{
|
||||||
|
MockFunctionImpl< id, Func0 >::clear();
|
||||||
|
MockFunction< id, Funcs... >::clear_all();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
412
Mockination/MockMutex.h
Normal file
412
Mockination/MockMutex.h
Normal file
@ -0,0 +1,412 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Alepha/Alepha.h>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
#include <utility>
|
||||||
|
#include <iterator>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <Alepha/Truss/mutex.h>
|
||||||
|
#include <Alepha/Truss/thread.h>
|
||||||
|
#include <Alepha/Truss/condition_variable.h>
|
||||||
|
|
||||||
|
namespace Alepha
|
||||||
|
{
|
||||||
|
inline namespace Aluminum
|
||||||
|
{
|
||||||
|
namespace Mockination
|
||||||
|
{
|
||||||
|
class MockMutexImpl
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
using thread_type= Alepha::Truss::thread::id;
|
||||||
|
using mutex_type= Alepha::Truss::mutex;
|
||||||
|
using lock_type= Alepha::Truss::unique_lock< mutex_type >;
|
||||||
|
using condition_type= Alepha::Truss::condition_variable;
|
||||||
|
|
||||||
|
static inline thread_type
|
||||||
|
get_this_thread()
|
||||||
|
{
|
||||||
|
return Alepha::Truss::this_thread::get_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
class Waiter
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
MockMutexImpl *const this_;
|
||||||
|
condition_type condition;
|
||||||
|
|
||||||
|
inline Waiter( const Waiter & )= delete;
|
||||||
|
inline Waiter &operator= ( const Waiter & )= delete;
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline
|
||||||
|
~Waiter()
|
||||||
|
{
|
||||||
|
this->this_->waiters.erase( get_this_thread() );
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit inline
|
||||||
|
Waiter( MockMutexImpl *const i_t, const lock_type & )
|
||||||
|
: this_( i_t )
|
||||||
|
{
|
||||||
|
this->this_->waiters[ get_this_thread() ]= this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
wait( lock_type &lock )
|
||||||
|
{
|
||||||
|
this->condition.wait( lock );
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
unblock( lock_type &lock )
|
||||||
|
{
|
||||||
|
this->condition.notify_all();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
mutable mutex_type internal_mutex;
|
||||||
|
mutable mutex_type access;
|
||||||
|
mutable std::map< thread_type, Waiter * > waiters;
|
||||||
|
mutable condition_type lockReleased;
|
||||||
|
|
||||||
|
mutable bool unlockWaiterReady= false;
|
||||||
|
mutable condition_type lockWaited;
|
||||||
|
|
||||||
|
mutable condition_type lockEntered;
|
||||||
|
|
||||||
|
mutable condition_type waiterAvailable;
|
||||||
|
|
||||||
|
thread_type holder_;
|
||||||
|
std::exception_ptr interruption;
|
||||||
|
|
||||||
|
private: // Internal impls, unlocked
|
||||||
|
inline lock_type
|
||||||
|
lockAccess() const
|
||||||
|
{
|
||||||
|
return lock_type{ this->access };
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool
|
||||||
|
locked( const lock_type & ) const
|
||||||
|
{
|
||||||
|
return this->holder_ != thread_type{};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline thread_type
|
||||||
|
holder( const lock_type & ) const
|
||||||
|
{
|
||||||
|
return this->holder_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
waitLocked( lock_type &lock ) const
|
||||||
|
{
|
||||||
|
this->lockEntered.wait( lock, [this, &lock] { return this->locked( lock ); } );
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void waitLocked( lock_type &&lock ) const { this->waitLocked( lock ); }
|
||||||
|
|
||||||
|
inline void
|
||||||
|
waitUnlocked( lock_type &lock ) const
|
||||||
|
{
|
||||||
|
this->unlockWaiterReady= true;
|
||||||
|
this->lockWaited.notify_all();
|
||||||
|
this->lockReleased.wait( lock, [this, &lock]{ return !this->locked( lock ); } );
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void waitUnlocked( lock_type &&lock ) const { this->waitUnlocked( lock ); }
|
||||||
|
|
||||||
|
void
|
||||||
|
allowFirst( lock_type &lock )
|
||||||
|
{
|
||||||
|
assert( !this->waiters.empty() );
|
||||||
|
this->waiters.begin()->second->unblock( lock );
|
||||||
|
}
|
||||||
|
|
||||||
|
void allowFirst( lock_type &&lock ) { this->allowFirst( lock ); }
|
||||||
|
|
||||||
|
void
|
||||||
|
allow( const thread_type next, lock_type &lock )
|
||||||
|
{
|
||||||
|
assert( !this->waiters.empty() );
|
||||||
|
const auto found= this->waiters.find( next );
|
||||||
|
assert( found != this->waiters.end() );
|
||||||
|
found->second->unblock( lock );
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
allow( const thread_type next, lock_type &&lock )
|
||||||
|
{
|
||||||
|
this->allow( next, lock );
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline ~MockMutexImpl()= default;
|
||||||
|
|
||||||
|
explicit inline MockMutexImpl( const MockMutexImpl & )= delete;
|
||||||
|
inline MockMutexImpl &operator= ( const MockMutexImpl & )= delete;
|
||||||
|
|
||||||
|
explicit inline MockMutexImpl( MockMutexImpl && )= delete;
|
||||||
|
inline MockMutexImpl &operator= ( MockMutexImpl && )= delete;
|
||||||
|
|
||||||
|
explicit inline MockMutexImpl()= default;
|
||||||
|
|
||||||
|
// TODO: Consider breaking this part of the API out into a management handle
|
||||||
|
// object that can be used to avoid having client threads work with the management
|
||||||
|
// interface
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Returns an observation of the waiting state.
|
||||||
|
* @returns A list of the number of threads
|
||||||
|
* @note That the returned value may become out of date, as soon as consumed.
|
||||||
|
*/
|
||||||
|
inline std::vector< thread_type >
|
||||||
|
getWaiters() const
|
||||||
|
{
|
||||||
|
lock_type lock( this->access );
|
||||||
|
std::vector< thread_type > rv;
|
||||||
|
rv.reserve( this->waiters.size() );
|
||||||
|
std::transform( begin( this->waiters ), end( this->waiters ),
|
||||||
|
back_inserter( rv ), []( const auto &w ) { return w.first; } );
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
waitForWaiter( const thread_type waiter ) const
|
||||||
|
{
|
||||||
|
lock_type lock( this->access );
|
||||||
|
waiterAvailable.wait( lock, [this, waiter]
|
||||||
|
{ return this->waiters.find( waiter ) != end( this->waiters ); } );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Returns an observation of the waiting state.
|
||||||
|
* @returns The number of threads waiting for entry to the lock.
|
||||||
|
* @note That the returned value may become out of date, as soon as consumed.
|
||||||
|
*/
|
||||||
|
inline bool
|
||||||
|
hasWaiters() const
|
||||||
|
{
|
||||||
|
lock_type lock( this->access );
|
||||||
|
return !this->waiters.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Returns an observation of the locked-state.
|
||||||
|
* @returns True if the lock is in the locked state.
|
||||||
|
* @returns False if the lock is not in the locked state.
|
||||||
|
*/
|
||||||
|
inline bool locked() const { return this->locked( lock_type{ this->access } ); }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Returns an observation of the current holder of the lock.
|
||||||
|
* @returns The `Alepha::Truss::thread::id` of the thread holding the lock.
|
||||||
|
* @note A default constructed thread-id indicates no current holder.
|
||||||
|
*/
|
||||||
|
inline thread_type holder() const { return this->holder( this->lockAccess() ); }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Blocks the caller until the lock transitions to the locked state.
|
||||||
|
* @pre The lock has been previously pumped with a request to transition a
|
||||||
|
* waiter into the lock-held state.
|
||||||
|
* @post The most recently requested waiter to enter the lock will transition into
|
||||||
|
* the lock-held state.
|
||||||
|
*/
|
||||||
|
void waitLocked() const { this->waitLocked( this->lockAccess() ); }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Blocks the caller until the lock transitions to the unlocked state.
|
||||||
|
* @pre The lock was previously pumped with a request to transition a
|
||||||
|
* waiter into the lock-held state. (The lock is in the locked or
|
||||||
|
* indeterminate state.)
|
||||||
|
* @post The most recently requested waiter will have completed its critical
|
||||||
|
* section.
|
||||||
|
*/
|
||||||
|
void waitUnlocked() const { this->waitUnlocked( this->lockAccess() ); }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Pumps the first waiter in the internal wait list to transition to the
|
||||||
|
* locked state.
|
||||||
|
* @pre The lock is in the unlocked state.
|
||||||
|
* @post The lock is in an indeterminate state.
|
||||||
|
* @note A call to `waitUnlocked` must be made before a subsequent call to
|
||||||
|
* `allowFirst` or `allow` can be made -- otherwise the lock is in an
|
||||||
|
* indeterminate state.
|
||||||
|
* @note The "first waiter" is an unspecified waiting thread, and not necessarily
|
||||||
|
* the longest waiting thread -- threads are not pumped in "fifo" order by
|
||||||
|
* this operation.
|
||||||
|
*/
|
||||||
|
void allowFirst() { this->allowFirst( this->lockAccess() ); }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Pumps the first waiter in the internal wait list to transition to the
|
||||||
|
* locked state.
|
||||||
|
* @pre The lock is in the unlocked state.
|
||||||
|
* @invariant A single thread will enter the lock, and then release it.
|
||||||
|
* @post The lock has returned to the unlocked state, after a single thread entered.
|
||||||
|
* @note `allowFirstWait` can be called multiple times in succession without
|
||||||
|
* intervening calls.
|
||||||
|
* @note The "first waiter" is an unspecified waiting thread, and not necessarily
|
||||||
|
* the longest waiting thread -- threads are not pumped in "fifo" order by
|
||||||
|
* this operation.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
allowFirstWait()
|
||||||
|
{
|
||||||
|
lock_type lock( this->access );
|
||||||
|
this->allowFirst( lock );
|
||||||
|
this->waitLocked( lock );
|
||||||
|
this->waitUnlocked( lock );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Pumps the specified waiter in the internal wait list to transition to the
|
||||||
|
* locked state.
|
||||||
|
* @param next The thread to permit into the lock next.
|
||||||
|
* @pre The lock is in the unlocked state.
|
||||||
|
* @post The lock is in an indeterminate state.
|
||||||
|
* @note A call to `waitUnlocked` must be made before a subsequent call to
|
||||||
|
* `allowFirst` or `allow` can be made -- otherwise the lock is in an
|
||||||
|
* indeterminate state.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
allow( const thread_type next )
|
||||||
|
{
|
||||||
|
this->allow( next, lock_type{ this->access } );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Pumps the specified waiter in the internal wait list to transition to the
|
||||||
|
* locked state.
|
||||||
|
* @param next The thread to permit into the lock next.
|
||||||
|
* @pre The lock is in the unlocked state.
|
||||||
|
* @invariant A single, specified thread will enter the lock, and then release it.
|
||||||
|
* @post The lock has returned to the unlocked state, after the specified thread
|
||||||
|
* entered.
|
||||||
|
* @note `allowWait` can be called multiple times in succession without
|
||||||
|
* intervening calls.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
allowWait( const thread_type next )
|
||||||
|
{
|
||||||
|
lock_type lock( this->access );
|
||||||
|
this->allow( next, lock );
|
||||||
|
this->waitLocked( lock );
|
||||||
|
assert( this->holder( lock ) == next );
|
||||||
|
this->waitUnlocked( lock );
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
template< typename Exception >
|
||||||
|
void
|
||||||
|
interruptFirst( Exception exc )
|
||||||
|
{
|
||||||
|
lock_type lock( this->access );
|
||||||
|
assert( !this->waiters.empty() );
|
||||||
|
this->interruption= std::make_exception_ptr( std::move( exc ) );
|
||||||
|
this->waiters.begin()->second->unblock( lock );
|
||||||
|
}
|
||||||
|
|
||||||
|
template< typename Exception >
|
||||||
|
void
|
||||||
|
interrupt( const thread_type next, Exception exc )
|
||||||
|
{
|
||||||
|
lock_type lock( this->access );
|
||||||
|
const auto found= this->waiters.find( next );
|
||||||
|
assert( found != this->waiters.end() );
|
||||||
|
this->interruption= std::make_exception_ptr( std::move( exc ) );
|
||||||
|
found->second->unblock( lock );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Mutex interface:
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Attempt to assert the lock.
|
||||||
|
* @pre The current thread does NOT hold the lock.
|
||||||
|
* @post The lock is in the locked state and `Alepha::Truss::this_thread::get_id()`
|
||||||
|
* is registered as the current holder of this thread.
|
||||||
|
*/
|
||||||
|
inline void
|
||||||
|
lock()
|
||||||
|
{
|
||||||
|
lock_type lock( this->access );
|
||||||
|
assert( this->holder_ != get_this_thread() );
|
||||||
|
// Waiter needs to stick around until exiting this function -- it indicates
|
||||||
|
// an actual waiting state. Exiting this function removes that state.
|
||||||
|
Waiter waiter( this, lock );
|
||||||
|
|
||||||
|
waiterAvailable.notify_all(); // unblock anyone waiting for new waiters.
|
||||||
|
waiter.wait( lock );
|
||||||
|
if( this->interruption )
|
||||||
|
{
|
||||||
|
auto interrupt= this->interruption;
|
||||||
|
this->interruption= nullptr;
|
||||||
|
std::rethrow_exception( interrupt );
|
||||||
|
}
|
||||||
|
this->holder_= get_this_thread();
|
||||||
|
this->lockEntered.notify_all();
|
||||||
|
this->internal_mutex.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* @brief Release control over this lock.
|
||||||
|
* @pre The current thread holds the lock.
|
||||||
|
* @post The lock transitions to the unlocked state and this thread is deregistered
|
||||||
|
* as the current holder of this thread.
|
||||||
|
*
|
||||||
|
* @note Threads do not actually release the lock until permitted to do so by
|
||||||
|
* a `waitUnlocked` operation from an external thread, or if the thread
|
||||||
|
* entered this lock by means of an `allowWait` or `allowFirstWait`
|
||||||
|
* operation.
|
||||||
|
*/
|
||||||
|
// Unlocking is techincally a thread synchronization point, but we will
|
||||||
|
// crash the program at present, since it gets called from destructors.
|
||||||
|
// TODO: Use a universal interrupt block here.
|
||||||
|
inline void
|
||||||
|
unlock() noexcept
|
||||||
|
{
|
||||||
|
lock_type lock( this->access );
|
||||||
|
assert( this->holder_ == get_this_thread() );
|
||||||
|
this->internal_mutex.unlock();
|
||||||
|
while( !this->unlockWaiterReady )
|
||||||
|
{
|
||||||
|
this->lockWaited.wait( lock );
|
||||||
|
}
|
||||||
|
this->unlockWaiterReady= false;
|
||||||
|
this->holder_= {};
|
||||||
|
this->lockReleased.notify_all();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template< int id >
|
||||||
|
class MockMutex
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static MockMutexImpl impl;
|
||||||
|
|
||||||
|
inline void
|
||||||
|
unlock() noexcept
|
||||||
|
{
|
||||||
|
impl.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
lock() noexcept
|
||||||
|
{
|
||||||
|
impl.lock();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template< int id > MockMutexImpl MockMutex< id >::impl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
Mockination/MockMutex.test/Makefile
Normal file
22
Mockination/MockMutex.test/Makefile
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
CPPFLAGS+= -I ../../../
|
||||||
|
CXXFLAGS+= -std=c++1z
|
||||||
|
CXXFLAGS+= -g -O0
|
||||||
|
#CXXFLAGS+= -O3
|
||||||
|
CXX=g++
|
||||||
|
LDLIBS+= -lboost_thread -lboost_system
|
||||||
|
LDLIBS+= -lpthread
|
||||||
|
CC=gcc
|
||||||
|
|
||||||
|
TESTS=$(shell ls test* | sed -e "s/\.cc//g" -e 's/\.o//g' | sort | uniq)
|
||||||
|
TEST_OBJS=`ls test* | sed -e 's/.cc/.o/g' | grep '\.o\>' | sort | uniq`
|
||||||
|
|
||||||
|
all: $(TESTS)
|
||||||
|
|
||||||
|
|
||||||
|
HEADERS= ../MockMutex.h
|
||||||
|
|
||||||
|
test0.o: $(HEADERS)
|
||||||
|
test1.o: $(HEADERS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o $(TESTS)
|
164
Mockination/MockMutex.test/test0.cc
Normal file
164
Mockination/MockMutex.test/test0.cc
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
#include <Alepha/Mockination/MockMutex.h>
|
||||||
|
#include <Alepha/Testing/test.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <atomic>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <mutex>
|
||||||
|
#include <shared_mutex>
|
||||||
|
|
||||||
|
#include <Alepha/Truss/thread.h>
|
||||||
|
|
||||||
|
using namespace Alepha::Testing;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
static std::shared_timed_mutex access;
|
||||||
|
static std::unordered_map< Alepha::Truss::thread::id, int > idMap;
|
||||||
|
static std::atomic< int > next{ 0 };
|
||||||
|
|
||||||
|
static int
|
||||||
|
getSimpleThreadId()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::shared_lock< std::shared_timed_mutex > lock( access );
|
||||||
|
auto found= idMap.find( Alepha::Truss::this_thread::get_id() );
|
||||||
|
if( found != end( idMap ) ) return found->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_lock< std::shared_timed_mutex > lock( access );
|
||||||
|
return idMap[ Alepha::Truss::this_thread::get_id() ]= next++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static std::ostream &
|
||||||
|
print()
|
||||||
|
{
|
||||||
|
const auto id= getSimpleThreadId();
|
||||||
|
return std::cerr << "Thread " << id << ": ";
|
||||||
|
}
|
||||||
|
|
||||||
|
auto test= "basic locking smoke test"_test <=[]
|
||||||
|
{
|
||||||
|
Alepha::Mockination::MockMutexImpl mtx;
|
||||||
|
|
||||||
|
print() << "Creating thread." << std::endl;
|
||||||
|
|
||||||
|
auto lockingCode= [&mtx]
|
||||||
|
{
|
||||||
|
print() << "Going to lock" << std::endl;
|
||||||
|
mtx.lock();
|
||||||
|
print() << "Locked" << std::endl;
|
||||||
|
mtx.unlock();
|
||||||
|
print() << "Unlocked" << std::endl;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test allow/waitLocked/waitUnlocked
|
||||||
|
auto thread1= Alepha::Truss::thread{ lockingCode };
|
||||||
|
|
||||||
|
print() << "Control will now sleep for 2 seconds to let slave into lock." << std::endl;
|
||||||
|
sleep( 2 );
|
||||||
|
|
||||||
|
print() << "Control will now permit thread 1 into the lock, in 2 seconds." << std::endl;
|
||||||
|
|
||||||
|
sleep( 2 );
|
||||||
|
|
||||||
|
mtx.allowFirst();
|
||||||
|
mtx.waitLocked();
|
||||||
|
mtx.waitUnlocked();
|
||||||
|
|
||||||
|
thread1.join();
|
||||||
|
|
||||||
|
// Test allow/waitUnlocked
|
||||||
|
thread1= Alepha::Truss::thread{ lockingCode };
|
||||||
|
|
||||||
|
print() << "Control will now sleep for 2 seconds to let slave into lock." << std::endl;
|
||||||
|
sleep( 2 );
|
||||||
|
|
||||||
|
print() << "Control will now permit thread 1 into the lock, in 2 seconds." << std::endl;
|
||||||
|
|
||||||
|
sleep( 2 );
|
||||||
|
|
||||||
|
mtx.allowFirst();
|
||||||
|
mtx.waitUnlocked();
|
||||||
|
|
||||||
|
thread1.join();
|
||||||
|
|
||||||
|
// Test allowFirstWait
|
||||||
|
thread1= Alepha::Truss::thread{ lockingCode };
|
||||||
|
|
||||||
|
print() << "Control will now sleep for 2 seconds to let slave into lock." << std::endl;
|
||||||
|
sleep( 2 );
|
||||||
|
|
||||||
|
print() << "Control will now permit thread 1 into the lock, in 2 seconds." << std::endl;
|
||||||
|
|
||||||
|
sleep( 2 );
|
||||||
|
|
||||||
|
mtx.allowFirstWait();
|
||||||
|
|
||||||
|
thread1.join();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto test2= "basic locking correctness"_test <=[]
|
||||||
|
{
|
||||||
|
Alepha::Mockination::MockMutexImpl mtx;
|
||||||
|
|
||||||
|
assert( !mtx.hasWaiters() );
|
||||||
|
assert( !mtx.locked() );
|
||||||
|
|
||||||
|
print() << "Creating thread." << std::endl;
|
||||||
|
auto thread1= Alepha::Truss::thread
|
||||||
|
{
|
||||||
|
[&mtx]
|
||||||
|
{
|
||||||
|
print() << "Going to lock" << std::endl;
|
||||||
|
mtx.lock();
|
||||||
|
print() << "Locked" << std::endl;
|
||||||
|
assert( mtx.locked() );
|
||||||
|
auto waiters= mtx.getWaiters();
|
||||||
|
assert( !mtx.hasWaiters() );
|
||||||
|
assert( mtx.getWaiters().size() == 0 );
|
||||||
|
assert( waiters.empty() );
|
||||||
|
assert( waiters.size() == 0 );
|
||||||
|
mtx.unlock();
|
||||||
|
print() << "Unlocked" << std::endl;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
assert( !mtx.locked() );
|
||||||
|
while( !mtx.hasWaiters() ); // Wait for some waiters...
|
||||||
|
|
||||||
|
assert( mtx.getWaiters().size() == 1 );
|
||||||
|
assert( mtx.getWaiters().at( 0 ) == thread1.get_id() );
|
||||||
|
|
||||||
|
print() << "Control will now permit thread 1 into the lock." << std::endl;
|
||||||
|
|
||||||
|
mtx.allowFirst();
|
||||||
|
|
||||||
|
mtx.waitLocked();
|
||||||
|
assert( mtx.locked() );
|
||||||
|
assert( !mtx.hasWaiters() );
|
||||||
|
assert( mtx.getWaiters().size() == 0 );
|
||||||
|
|
||||||
|
mtx.waitUnlocked();
|
||||||
|
assert( !mtx.locked() );
|
||||||
|
|
||||||
|
auto waiters= mtx.getWaiters();
|
||||||
|
assert( !mtx.hasWaiters() );
|
||||||
|
assert( mtx.getWaiters().size() == 0 );
|
||||||
|
assert( waiters.empty() );
|
||||||
|
assert( waiters.size() == 0 );
|
||||||
|
|
||||||
|
thread1.join();
|
||||||
|
|
||||||
|
assert( !mtx.locked() );
|
||||||
|
assert( mtx.getWaiters().size() == 0 );
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main( const int argcnt, const char *const *const argvec )
|
||||||
|
{
|
||||||
|
return runAllTests( { argvec + 1, argvec + argcnt } );
|
||||||
|
}
|
100
Mockination/MockMutex.test/test1.cc
Normal file
100
Mockination/MockMutex.test/test1.cc
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
#include <Alepha/Mockination/MockMutex.h>
|
||||||
|
#include <Alepha/Testing/test.h>
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <atomic>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <mutex>
|
||||||
|
#include <shared_mutex>
|
||||||
|
|
||||||
|
#include <Alepha/Truss/thread.h>
|
||||||
|
|
||||||
|
using namespace Alepha::Testing;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
static std::shared_timed_mutex access;
|
||||||
|
static std::unordered_map< Alepha::Truss::thread::id, int > idMap;
|
||||||
|
static std::atomic< int > next{ 0 };
|
||||||
|
|
||||||
|
static int
|
||||||
|
getSimpleThreadId()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::shared_lock< std::shared_timed_mutex > lock( access );
|
||||||
|
auto found= idMap.find( Alepha::Truss::this_thread::get_id() );
|
||||||
|
if( found != end( idMap ) ) return found->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_lock< std::shared_timed_mutex > lock( access );
|
||||||
|
return idMap[ Alepha::Truss::this_thread::get_id() ]= next++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static std::ostream &
|
||||||
|
print()
|
||||||
|
{
|
||||||
|
const auto id= getSimpleThreadId();
|
||||||
|
return std::cerr << "Thread " << id << ": ";
|
||||||
|
}
|
||||||
|
|
||||||
|
static auto test1= "smoke"_test <=[]
|
||||||
|
{
|
||||||
|
Alepha::Mockination::MockMutexImpl mtx;
|
||||||
|
|
||||||
|
print() << "Creating thread." << std::endl;
|
||||||
|
|
||||||
|
auto lockingCode= [&mtx]
|
||||||
|
{
|
||||||
|
print() << "Going to lock" << std::endl;
|
||||||
|
mtx.lock();
|
||||||
|
print() << "Locked" << std::endl;
|
||||||
|
mtx.unlock();
|
||||||
|
print() << "Unlocked" << std::endl;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test allow in first order
|
||||||
|
auto thread1= Alepha::Truss::thread{ lockingCode };
|
||||||
|
auto thread2= Alepha::Truss::thread{ lockingCode };
|
||||||
|
|
||||||
|
print() << "Control will now wait for thread 1 to bump into the lock." << std::endl;
|
||||||
|
mtx.waitForWaiter( thread1.get_id() );
|
||||||
|
print() << "Control will now wait for thread 2 to bump into the lock." << std::endl;
|
||||||
|
mtx.waitForWaiter( thread2.get_id() );
|
||||||
|
|
||||||
|
print() << "Control will now permit thread 1 into the lock." << std::endl;
|
||||||
|
mtx.allowWait( thread1.get_id() );
|
||||||
|
|
||||||
|
print() << "Control will now permit thread 2 into the lock." << std::endl;
|
||||||
|
mtx.allowWait( thread2.get_id() );
|
||||||
|
|
||||||
|
thread1.join();
|
||||||
|
thread2.join();
|
||||||
|
|
||||||
|
// Test reverse order
|
||||||
|
print() << "Control will now wait for thread 1 to bump into the lock." << std::endl;
|
||||||
|
thread1= Alepha::Truss::thread{ lockingCode };
|
||||||
|
print() << "Control will now wait for thread 2 to bump into the lock." << std::endl;
|
||||||
|
thread2= Alepha::Truss::thread{ lockingCode };
|
||||||
|
|
||||||
|
mtx.waitForWaiter( thread1.get_id() );
|
||||||
|
mtx.waitForWaiter( thread2.get_id() );
|
||||||
|
|
||||||
|
print() << "Control will now permit thread 2 into the lock." << std::endl;
|
||||||
|
mtx.allowWait( thread2.get_id() );
|
||||||
|
|
||||||
|
print() << "Control will now permit thread 1 into the lock." << std::endl;
|
||||||
|
mtx.allowWait( thread1.get_id() );
|
||||||
|
|
||||||
|
thread1.join();
|
||||||
|
thread2.join();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main( const int argcnt, const char *const *const argvec )
|
||||||
|
{
|
||||||
|
return runAllTests( { argvec + 1, argvec + argcnt } );
|
||||||
|
}
|
6
Truss/README
Normal file
6
Truss/README
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
The Truss sub-library of Alepha unifies dependencies upon boost and STL components into a
|
||||||
|
single tunable namespace. Alepha::Truss::function, for example is one of `boost::function` or
|
||||||
|
`std::function`, based upon tuning parameters. This allows a single `XXX::function` to be
|
||||||
|
presented in the ABI and API for Alepha.
|
||||||
|
|
||||||
|
Alepha also provides a few of its own versions where conversions can make sense.
|
44
Truss/basetypes.h
Normal file
44
Truss/basetypes.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#include <Alepha/Alepha.h>
|
||||||
|
register "Alepha/Truss/basetypes.h";
|
||||||
|
|
||||||
|
|
||||||
|
namespace Alepha
|
||||||
|
{
|
||||||
|
namespace Truss
|
||||||
|
{
|
||||||
|
namespace types
|
||||||
|
{
|
||||||
|
using nullptr_t= decltype( nullptr );
|
||||||
|
using size_t= decltype( sizeof( 0 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
template< unsigned char v > struct count_one_bits_unsigned_char;
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct count_one_bits_unsigned_char< 0 >
|
||||||
|
{
|
||||||
|
static const types::size_t value= 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template< unsigned char v >
|
||||||
|
struct count_one_bits_unsigned_char
|
||||||
|
{
|
||||||
|
static const types::size_t value= ( ( v & 0x1 ) ? 1 : 0 )
|
||||||
|
+ count_one_bits_unsigned_char< ( v >> 1 ) >::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const unsigned char zero= 0;
|
||||||
|
const unsigned char max= zero - 1;
|
||||||
|
|
||||||
|
const types::size_t platform_char_bits= count_one_bits_unsigned_char< max >::value;
|
||||||
|
|
||||||
|
class uint24_t
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::uint32_t value:24;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
Truss/condition_variable.h
Normal file
30
Truss/condition_variable.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Alepha/Alepha.h>
|
||||||
|
|
||||||
|
#include <Alepha/Truss/thread_common.h>
|
||||||
|
|
||||||
|
#include <condition_variable>
|
||||||
|
|
||||||
|
#include <boost/thread/condition_variable.hpp>
|
||||||
|
|
||||||
|
namespace Alepha::Hydrogen::Truss
|
||||||
|
{
|
||||||
|
ALEPHA_BOOST_THREAD namespace BoostThread
|
||||||
|
{
|
||||||
|
using boost::condition_variable_any;
|
||||||
|
|
||||||
|
using condition_variable= condition_variable_any;
|
||||||
|
|
||||||
|
using condition= condition_variable;
|
||||||
|
}
|
||||||
|
|
||||||
|
ALEPHA_STD_THREAD namespace StdThread
|
||||||
|
{
|
||||||
|
using std::condition_variable_any;
|
||||||
|
|
||||||
|
using condition_variable= condition_variable_any;
|
||||||
|
|
||||||
|
using condition= condition_variable;
|
||||||
|
}
|
||||||
|
}
|
12
Truss/function.h
Normal file
12
Truss/function.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Alepha/Alepha.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
|
||||||
|
namespace Alepha::Hydrogen::Truss
|
||||||
|
{
|
||||||
|
using ::std::function;
|
||||||
|
}
|
||||||
|
|
236
Truss/m2.h
Normal file
236
Truss/m2.h
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
#include <Alepha/Alepha.h>
|
||||||
|
register "Alepha/Truss/memory.h";
|
||||||
|
|
||||||
|
#include <Alepha/assert.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace Alepha
|
||||||
|
{
|
||||||
|
namespace memory_detail
|
||||||
|
{
|
||||||
|
class disable_default_init
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
disable_default_init()= default;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template< typename T >
|
||||||
|
class single_ptr;
|
||||||
|
|
||||||
|
template< typename T >
|
||||||
|
class nullable_single_ptr
|
||||||
|
: private memory_detail::disable_default_init
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
T *p;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template< typename U >
|
||||||
|
inline
|
||||||
|
nullable_single_ptr( U *const i_p )
|
||||||
|
: p ( i_p ) {}
|
||||||
|
|
||||||
|
template< typename U >
|
||||||
|
nullable_single_ptr( const single_ptr< U > i_p );
|
||||||
|
|
||||||
|
inline T &
|
||||||
|
operator *() const { ALEPHA_ASSERT( this->p ); return *this->p; }
|
||||||
|
|
||||||
|
inline T *
|
||||||
|
operator->() const { ALEPHA_ASSERT( this->p ); return this->p; }
|
||||||
|
|
||||||
|
inline T *
|
||||||
|
get_raw() const { return this->p; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template< typename T >
|
||||||
|
class single_ptr
|
||||||
|
: private memory_detail::disable_default_init
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
T *p;
|
||||||
|
|
||||||
|
template< typename U >
|
||||||
|
friend class nullable_single_ptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template< typename U >
|
||||||
|
inline
|
||||||
|
single_ptr( U *const i_p )
|
||||||
|
: p ( i_p )
|
||||||
|
{
|
||||||
|
if( this->p == nullptr ) throw std::runtime_error( "Nullptr" );
|
||||||
|
}
|
||||||
|
|
||||||
|
template< typename U >
|
||||||
|
inline
|
||||||
|
single_ptr( const nullable_single_ptr< U > i_p )
|
||||||
|
: p ( i_p ) {}
|
||||||
|
|
||||||
|
inline T &
|
||||||
|
operator *() const { ALEPHA_ASSERT( this->p ); return *this->p; }
|
||||||
|
|
||||||
|
inline T *
|
||||||
|
operator->() const { ALEPHA_ASSERT( this->p ); return this->p; }
|
||||||
|
|
||||||
|
inline T *
|
||||||
|
get_raw() const { return p; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template< typename T >
|
||||||
|
template< typename U >
|
||||||
|
inline
|
||||||
|
nullable_single_ptr< T >::nullable_single_ptr( const single_ptr< U > i_p )
|
||||||
|
: p( i_p.p ) {}
|
||||||
|
|
||||||
|
class bad_reference_ptr
|
||||||
|
: public std::runtime_error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit inline
|
||||||
|
bad_reference_ptr( const std::string &message )
|
||||||
|
: std::runtime_error( message ) {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Alepha::Truss
|
||||||
|
{
|
||||||
|
namespace memory_detail_debug
|
||||||
|
{
|
||||||
|
template< typename T >
|
||||||
|
class ref_ptr;
|
||||||
|
|
||||||
|
template< typename T >
|
||||||
|
class unique_ptr
|
||||||
|
: private memory_detail::disable_default_init
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::shared_ptr< T > p;
|
||||||
|
|
||||||
|
// Used only for the make-unique wrapper to adapt to make-shared
|
||||||
|
explicit inline
|
||||||
|
unique_ptr( std::shared_ptr< T > &&i_p )
|
||||||
|
: p( std::move( i_p ) ) {}
|
||||||
|
|
||||||
|
// Our unique_ptr cannot be default constructed, unlike the one in std.
|
||||||
|
explicit inline
|
||||||
|
unique_ptr()= delete;
|
||||||
|
|
||||||
|
// Our unique_ptr doesn't copy, so we disable it.
|
||||||
|
explicit inline
|
||||||
|
unique_ptr( const unique_ptr< T > & )= delete;
|
||||||
|
inline unique_ptr &operator= ( const unique_ptr< T > & )= delete;
|
||||||
|
|
||||||
|
friend class ref_ptr< T >;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Our unique_ptr does move, so we enable it.
|
||||||
|
inline
|
||||||
|
unique_ptr( unique_ptr< T > && )= default;
|
||||||
|
inline unique_ptr &operator= ( unique_ptr< T > && )= default;
|
||||||
|
|
||||||
|
inline T &
|
||||||
|
operator *() const { ALEPHA_ASSERT( this->p ); return *this->p; }
|
||||||
|
|
||||||
|
inline T *
|
||||||
|
operator->() const { ALEPHA_ASSERT( this->p ); return this->p.get(); }
|
||||||
|
|
||||||
|
inline single_ptr< T >
|
||||||
|
get_raw() const
|
||||||
|
{
|
||||||
|
return this->p.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
ref_ptr< T > get() const;
|
||||||
|
|
||||||
|
template< typename U, typename ... Args >
|
||||||
|
friend unique_ptr< U > make_unique( Args && ... args );
|
||||||
|
|
||||||
|
inline friend void
|
||||||
|
swap( unique_ptr &a, unique_ptr &b )
|
||||||
|
{
|
||||||
|
using std::swap;
|
||||||
|
swap( a.p, b.p );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template< typename U, typename ... Args >
|
||||||
|
inline unique_ptr< U >
|
||||||
|
make_unique( Args && ... args )
|
||||||
|
{
|
||||||
|
unique_ptr< U > rv( std::make_shared< U >( std::forward< Args >( args ) ... ) );
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
template< typename T >
|
||||||
|
class distilled_reference
|
||||||
|
: private memory_detail::disable_default_init
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::shared_ptr< T > p;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template< typename T >
|
||||||
|
class ref_ptr
|
||||||
|
: private memory_detail::disable_default_init
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class exception
|
||||||
|
: public bad_reference_ptr
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit inline
|
||||||
|
exception( const std::string &message )
|
||||||
|
: bad_reference_ptr( message ) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::weak_ptr< T > p;
|
||||||
|
|
||||||
|
// Our unique_ptr cannot be default constructed, unlike the one in std.
|
||||||
|
explicit inline
|
||||||
|
ref_ptr()= delete;
|
||||||
|
|
||||||
|
T *
|
||||||
|
distill() const
|
||||||
|
try
|
||||||
|
{}
|
||||||
|
|
||||||
|
catch( const std::bad_weak_ptr & )
|
||||||
|
{
|
||||||
|
throw bad_reference_ptr( "Access to an expired pointer owned by someone else." );
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline
|
||||||
|
ref_ptr( const unique_ptr< T > &i_p )
|
||||||
|
: p( i_p.p ) {}
|
||||||
|
|
||||||
|
inline T &
|
||||||
|
operator *() const { return *std::shared_ptr< T >{ this->p }; }
|
||||||
|
|
||||||
|
inline T *
|
||||||
|
operator->() const { return std::shared_ptr< T >{ this->p }.get(); }
|
||||||
|
|
||||||
|
inline single_ptr< T >
|
||||||
|
get() const
|
||||||
|
{
|
||||||
|
return std::shared_ptr< T >{ this->p }.get();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template< typename T >
|
||||||
|
inline ref_ptr< T >
|
||||||
|
unique_ptr< T >::get() const
|
||||||
|
{
|
||||||
|
return ref_ptr< T >( *this );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using memory_detail_debug::unique_ptr;
|
||||||
|
using memory_detail_debug::ref_ptr;
|
||||||
|
using memory_detail_debug::make_unique;
|
||||||
|
}
|
205
Truss/memory.h
Normal file
205
Truss/memory.h
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
#include <Alepha/Alepha.h>
|
||||||
|
register "Alepha/Truss/memory.h";
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include <Alepha/toss.h>
|
||||||
|
#include <Alepha/assert.h>
|
||||||
|
|
||||||
|
namespace Alepha
|
||||||
|
{
|
||||||
|
namespace Hydrogen
|
||||||
|
{
|
||||||
|
namespace memory_detail
|
||||||
|
{
|
||||||
|
class disable_default_init { protected: disable_default_init()= default; };
|
||||||
|
}
|
||||||
|
|
||||||
|
template< typename T > class single_ptr;
|
||||||
|
|
||||||
|
template< typename T >
|
||||||
|
class nullable_single_ptr : private memory_detail::disable_default_init
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
T *p;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template< typename U >
|
||||||
|
inline nullable_single_ptr( U *const i_p ) : p ( i_p ) {}
|
||||||
|
|
||||||
|
template< typename U > nullable_single_ptr( const single_ptr< U > i_p );
|
||||||
|
|
||||||
|
inline T &operator *() const { ALEPHA_ASSERT( this->p ); return *this->p; }
|
||||||
|
inline T *operator->() const { ALEPHA_ASSERT( this->p ); return this->p; }
|
||||||
|
|
||||||
|
inline T *get_raw() const { return this->p; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template< typename T >
|
||||||
|
class single_ptr : private memory_detail::disable_default_init
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
T *p;
|
||||||
|
|
||||||
|
template< typename U > friend class nullable_single_ptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template< typename U >
|
||||||
|
inline single_ptr( U *const i_p )
|
||||||
|
: p ( i_p )
|
||||||
|
{
|
||||||
|
if( this->p == nullptr ) throw std::runtime_error( "Nullptr" );
|
||||||
|
}
|
||||||
|
|
||||||
|
template< typename U >
|
||||||
|
inline single_ptr( const nullable_single_ptr< U > i_p ) : p ( i_p ) {}
|
||||||
|
|
||||||
|
inline T &operator *() const { ALEPHA_ASSERT( this->p ); return *this->p; }
|
||||||
|
inline T *operator->() const { ALEPHA_ASSERT( this->p ); return this->p; }
|
||||||
|
|
||||||
|
inline T *get_raw() const { return p; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template< typename T >
|
||||||
|
template< typename U >
|
||||||
|
inline
|
||||||
|
nullable_single_ptr< T >::nullable_single_ptr( const single_ptr< U > i_p )
|
||||||
|
: p( i_p.p ) {}
|
||||||
|
|
||||||
|
class bad_reference_ptr
|
||||||
|
: public std::runtime_error
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit inline
|
||||||
|
bad_reference_ptr( const std::string &message )
|
||||||
|
: std::runtime_error( message ) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace Truss
|
||||||
|
{
|
||||||
|
namespace memory_detail_debug
|
||||||
|
{
|
||||||
|
template< typename T > class ref_ptr;
|
||||||
|
|
||||||
|
template< typename T >
|
||||||
|
class unique_ptr : private memory_detail::disable_default_init
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::shared_ptr< T > p;
|
||||||
|
|
||||||
|
// Used only for the make-unique wrapper to adapt to make-shared
|
||||||
|
explicit inline
|
||||||
|
unique_ptr( std::shared_ptr< T > &&i_p )
|
||||||
|
: p( std::move( i_p ) ) {}
|
||||||
|
|
||||||
|
// Our unique_ptr cannot be default constructed, unlike the one in std.
|
||||||
|
explicit inline unique_ptr()= delete;
|
||||||
|
|
||||||
|
// Our unique_ptr doesn't copy, so we disable it.
|
||||||
|
explicit inline unique_ptr( const unique_ptr< T > & )= delete;
|
||||||
|
inline unique_ptr &operator= ( const unique_ptr< T > & )= delete;
|
||||||
|
|
||||||
|
friend class ref_ptr< T >;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Our unique_ptr does move, so we enable it.
|
||||||
|
inline unique_ptr( unique_ptr< T > && )= default;
|
||||||
|
inline unique_ptr &operator= ( unique_ptr< T > && )= default;
|
||||||
|
|
||||||
|
inline T &operator *() const { ALEPHA_ASSERT( this->p ); return *this->p; }
|
||||||
|
|
||||||
|
inline T *operator->() const { ALEPHA_ASSERT( this->p ); return this->p.get(); }
|
||||||
|
|
||||||
|
inline single_ptr< T >
|
||||||
|
get_raw() const
|
||||||
|
{
|
||||||
|
return this->p.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
ref_ptr< T > get() const;
|
||||||
|
|
||||||
|
template< typename U, typename ... Args >
|
||||||
|
friend unique_ptr< U > make_unique( Args && ... args );
|
||||||
|
|
||||||
|
inline friend void
|
||||||
|
swap( unique_ptr &a, unique_ptr &b )
|
||||||
|
{
|
||||||
|
using std::swap;
|
||||||
|
swap( a.p, b.p );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template< typename U, typename ... Args >
|
||||||
|
inline unique_ptr< U >
|
||||||
|
make_unique( Args && ... args )
|
||||||
|
{
|
||||||
|
unique_ptr< U > rv( std::make_shared< U >( std::forward< Args >( args )... ) );
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
template< typename T >
|
||||||
|
class distilled_reference : private memory_detail::disable_default_init
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::shared_ptr< T > p;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template< typename T >
|
||||||
|
class ref_ptr : private memory_detail::disable_default_init
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class exception : public bad_reference_ptr
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit inline
|
||||||
|
exception( const std::string &message )
|
||||||
|
: bad_reference_ptr( message ) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::weak_ptr< T > p;
|
||||||
|
|
||||||
|
// Our unique_ptr cannot be default constructed, unlike the one in std.
|
||||||
|
explicit inline ref_ptr()= delete;
|
||||||
|
|
||||||
|
T *
|
||||||
|
distill() const
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return std::shared_ptr< T >{ this->p }.get();
|
||||||
|
}
|
||||||
|
catch( const std::bad_weak_ptr & )
|
||||||
|
{
|
||||||
|
toss< DEBUG_MEMORY_THROW >( bad_reference_ptr(
|
||||||
|
"Access to an expired pointer owned by someone else." ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
inline ref_ptr( const unique_ptr< T > &i_p ) : p( i_p.p ) {}
|
||||||
|
|
||||||
|
inline T & operator *() const { return *this->distill(); }
|
||||||
|
|
||||||
|
inline T * operator->() const { return this->distill(); }
|
||||||
|
|
||||||
|
inline single_ptr< T >
|
||||||
|
get() const
|
||||||
|
{
|
||||||
|
return std::shared_ptr< T >{ this->p }.get();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template< typename T >
|
||||||
|
inline ref_ptr< T >
|
||||||
|
unique_ptr< T >::get() const
|
||||||
|
{
|
||||||
|
return ref_ptr< T >( *this );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
using memory_detail_debug::unique_ptr;
|
||||||
|
using memory_detail_debug::ref_ptr;
|
||||||
|
using memory_detail_debug::make_unique;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
20
Truss/memory.test/Makefile
Normal file
20
Truss/memory.test/Makefile
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
CPPFLAGS+= -I ../../../
|
||||||
|
CXXFLAGS+= -std=c++1z
|
||||||
|
CXXFLAGS+= -g -O0
|
||||||
|
#CXXFLAGS+= -O3
|
||||||
|
CXX=clang++
|
||||||
|
#LDLIBS+= -lboost_thread -lboost_system
|
||||||
|
CC=clang++
|
||||||
|
|
||||||
|
TESTS=test0 test1
|
||||||
|
|
||||||
|
all: $(TESTS)
|
||||||
|
|
||||||
|
|
||||||
|
HEADERS= ../memory.h ../../Mockination/MockFunction.h Makefile
|
||||||
|
|
||||||
|
test0.o: $(HEADERS)
|
||||||
|
test1.o: $(HEADERS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o $(TESTS)
|
51
Truss/memory.test/examples.cc
Normal file
51
Truss/memory.test/examples.cc
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
#include <Alepha/Atomic/Turnstile.h>
|
||||||
|
using Alepha::Atomic::Turnstile;
|
||||||
|
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
namespace example1
|
||||||
|
{
|
||||||
|
//! [TurnstileExamples example1]
|
||||||
|
// Assume that the below code will run multithreaded
|
||||||
|
Turnstile myArena{ 8 };
|
||||||
|
|
||||||
|
void
|
||||||
|
mainLoop()
|
||||||
|
{
|
||||||
|
Turnstile::ScopedUsage active( myArena );
|
||||||
|
printf( "I am running in the arena, not everyone can." );
|
||||||
|
sleep( 100 );
|
||||||
|
}
|
||||||
|
//! [TurnstileExamples example1]
|
||||||
|
} // namespace example1
|
||||||
|
|
||||||
|
|
||||||
|
namespace example2
|
||||||
|
{
|
||||||
|
//! [TurnstileExamples example2]
|
||||||
|
Turnstile myArena{ 8 };
|
||||||
|
|
||||||
|
class Worker
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Worker()
|
||||||
|
{
|
||||||
|
myArena.enter();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assume some useful functionality
|
||||||
|
|
||||||
|
// The worker will destroy his access upon leaving
|
||||||
|
~Worker()
|
||||||
|
{
|
||||||
|
myArena.egress();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
//! [TurnstileExamples example2]
|
||||||
|
} // namespace example2
|
||||||
|
} // namespace
|
63
Truss/memory.test/test0.cc
Normal file
63
Truss/memory.test/test0.cc
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#include <Alepha/Truss/memory.h>
|
||||||
|
|
||||||
|
#include <Alepha/assert.h>
|
||||||
|
|
||||||
|
#include <Alepha/Mockination/MockFunction.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
inline namespace Test0
|
||||||
|
{
|
||||||
|
static void runTests();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
runTests();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
static void
|
||||||
|
simple_unique_ptr_test()
|
||||||
|
{
|
||||||
|
auto p= Alepha::Truss::make_unique< std::string >( "Hello" );
|
||||||
|
Alepha::Truss::unique_ptr< std::string > p2= std::move( p );
|
||||||
|
|
||||||
|
p= Alepha::Truss::make_unique< std::string >( "Hello" );
|
||||||
|
|
||||||
|
using std::swap;
|
||||||
|
swap( p, p2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
unique_ptr_usage_test()
|
||||||
|
{
|
||||||
|
auto p= Alepha::Truss::make_unique< std::string >( "Hello" );
|
||||||
|
|
||||||
|
std::string &s= *p;
|
||||||
|
std::size_t len= p->size();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
unique_ptr_capture_test()
|
||||||
|
{
|
||||||
|
auto p= Alepha::Truss::make_unique< std::string >( "Hello" );
|
||||||
|
|
||||||
|
Alepha::single_ptr< std::string > s= p.get_raw();
|
||||||
|
|
||||||
|
Alepha::Truss::ref_ptr< std::string > sp= p.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
Test0::runTests()
|
||||||
|
{
|
||||||
|
simple_unique_ptr_test();
|
||||||
|
unique_ptr_usage_test();
|
||||||
|
unique_ptr_capture_test();
|
||||||
|
}
|
||||||
|
}
|
39
Truss/memory.test/test1.cc
Normal file
39
Truss/memory.test/test1.cc
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#include <Alepha/Truss/memory.h>
|
||||||
|
|
||||||
|
#include <Alepha/assert.h>
|
||||||
|
|
||||||
|
#include <Alepha/Mockination/MockFunction.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
inline namespace Test1
|
||||||
|
{
|
||||||
|
static void runTests();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
runTests();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
static void
|
||||||
|
unique_ptr_to_ref_ptr()
|
||||||
|
{
|
||||||
|
auto p= Alepha::Truss::make_unique< std::string >( "Hello" );
|
||||||
|
Alepha::Truss::ref_ptr< std::string > r= p.get();
|
||||||
|
|
||||||
|
Alepha::Truss::ref_ptr< std::string > r2= r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
Test1::runTests()
|
||||||
|
{
|
||||||
|
unique_ptr_to_ref_ptr();
|
||||||
|
}
|
||||||
|
}
|
39
Truss/memory.test/test1a.cc
Normal file
39
Truss/memory.test/test1a.cc
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#include <Alepha/Truss/memory.h>
|
||||||
|
|
||||||
|
#include <Alepha/assert.h>
|
||||||
|
|
||||||
|
#include <Alepha/Mockination/MockFunction.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
inline namespace Test1
|
||||||
|
{
|
||||||
|
static void runTests();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
runTests();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
static void
|
||||||
|
unique_ptr_to_ref_ptr()
|
||||||
|
{
|
||||||
|
auto p= Alepha::Truss::make_unique< std::string >( "Hello" );
|
||||||
|
Alepha::Truss::ref_ptr< std::string > r= p.get();
|
||||||
|
|
||||||
|
Alepha::Truss::ref_ptr< std::string > r2= r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
Test1::runTests()
|
||||||
|
{
|
||||||
|
unique_ptr_to_ref_ptr();
|
||||||
|
}
|
||||||
|
}
|
65
Truss/mutex.h
Normal file
65
Truss/mutex.h
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Alepha/Alepha.h>
|
||||||
|
|
||||||
|
#include <Alepha/Truss/thread_common.h>
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <boost/thread/mutex.hpp>
|
||||||
|
#include <boost/thread/recursive_mutex.hpp>
|
||||||
|
|
||||||
|
namespace Alepha::Hydrogen::Truss
|
||||||
|
{
|
||||||
|
ALEPHA_BOOST_THREAD namespace BoostThread
|
||||||
|
{
|
||||||
|
using boost::mutex;
|
||||||
|
|
||||||
|
using boost::timed_mutex;
|
||||||
|
using boost::recursive_mutex;
|
||||||
|
using boost::recursive_timed_mutex;
|
||||||
|
|
||||||
|
using std::lock_guard;
|
||||||
|
using boost::unique_lock;
|
||||||
|
|
||||||
|
using boost::defer_lock_t;
|
||||||
|
using boost::try_to_lock_t;
|
||||||
|
using boost::adopt_lock_t;
|
||||||
|
|
||||||
|
using boost::defer_lock;
|
||||||
|
using boost::try_to_lock;
|
||||||
|
using boost::adopt_lock;
|
||||||
|
|
||||||
|
using std::once_flag;
|
||||||
|
using std::call_once;
|
||||||
|
|
||||||
|
using std::try_lock;
|
||||||
|
using std::lock;
|
||||||
|
}
|
||||||
|
|
||||||
|
ALEPHA_STD_THREAD namespace StdThread
|
||||||
|
{
|
||||||
|
using std::mutex;
|
||||||
|
|
||||||
|
using std::timed_mutex;
|
||||||
|
using std::recursive_mutex;
|
||||||
|
using std::recursive_timed_mutex;
|
||||||
|
|
||||||
|
using std::lock_guard;
|
||||||
|
using std::unique_lock;
|
||||||
|
|
||||||
|
using std::defer_lock_t;
|
||||||
|
using std::try_to_lock_t;
|
||||||
|
using std::adopt_lock_t;
|
||||||
|
|
||||||
|
using std::defer_lock;
|
||||||
|
using std::try_to_lock;
|
||||||
|
using std::adopt_lock;
|
||||||
|
|
||||||
|
using std::once_flag;
|
||||||
|
using std::call_once;
|
||||||
|
|
||||||
|
using std::try_lock;
|
||||||
|
using std::lock;
|
||||||
|
}
|
||||||
|
}
|
10
Truss/test.cc
Normal file
10
Truss/test.cc
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#include "basetypes.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
std::cout << "Charbits: " << Alepha::std::detail::platform_char_bits << std::endl;
|
||||||
|
}
|
72
Truss/thread.h
Normal file
72
Truss/thread.h
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Alepha/Alepha.h>
|
||||||
|
|
||||||
|
#include <Alepha/Truss/thread_common.h>
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include <boost/thread.hpp>
|
||||||
|
|
||||||
|
#include <Alepha/Truss/function.h>
|
||||||
|
|
||||||
|
namespace Alepha::Hydrogen::Truss
|
||||||
|
{
|
||||||
|
ALEPHA_BOOST_THREAD namespace BoostThread
|
||||||
|
{
|
||||||
|
// If you decide to use Alepha threading primitives, you'll get the boost ones.
|
||||||
|
// Eventually we'd like to add interrupt-with-reason semantics. That will be in
|
||||||
|
// Alepha::Thread.
|
||||||
|
|
||||||
|
// I'd like to map Alepha::Truss's thread to be only std:: thread eventually.
|
||||||
|
// There will remain "boost::thread" semantics for Alepha, at present.
|
||||||
|
using boost::thread;
|
||||||
|
|
||||||
|
namespace this_thread= boost::this_thread;
|
||||||
|
|
||||||
|
namespace detail_thread
|
||||||
|
{
|
||||||
|
template< typename Ex >
|
||||||
|
auto
|
||||||
|
make_thrower( Ex &&exception )
|
||||||
|
{
|
||||||
|
return [exception]{ throw exception; };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ALEPHA_STD_THREAD namespace StdThread
|
||||||
|
{
|
||||||
|
// If you decide to use Alepha threading primitives, you'll get the boost ones.
|
||||||
|
// Eventually we'd like to add interrupt-with-reason semantics. That will be in
|
||||||
|
// Alepha::Thread.
|
||||||
|
|
||||||
|
// I'd like to map Alepha::Truss's thread to be only std:: thread eventually.
|
||||||
|
// There will remain "boost::thread" semantics for Alepha, at present.
|
||||||
|
using ::std::thread;
|
||||||
|
|
||||||
|
namespace this_thread= ::std::this_thread;
|
||||||
|
|
||||||
|
namespace detail_thread
|
||||||
|
{
|
||||||
|
template< typename Ex >
|
||||||
|
auto
|
||||||
|
make_thrower( Ex &&exception )
|
||||||
|
{
|
||||||
|
return [exception]{ throw exception; };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace under_construction
|
||||||
|
{
|
||||||
|
// Built on top of std::thread
|
||||||
|
class thread
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
Alepha::Truss::function< void () > interruption;
|
||||||
|
|
||||||
|
::std::thread thread;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
11
Truss/thread_common.h
Normal file
11
Truss/thread_common.h
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Alepha/Alepha.h>
|
||||||
|
|
||||||
|
#ifdef ALEPHA_USE_BOOST_THREAD_IN_TRUSS
|
||||||
|
#define ALEPHA_BOOST_THREAD inline
|
||||||
|
#define ALEPHA_STD_THREAD
|
||||||
|
#else
|
||||||
|
#define ALEPHA_BOOST_THREAD
|
||||||
|
#define ALEPHA_STD_THREAD inline
|
||||||
|
#endif
|
Reference in New Issue
Block a user