diff --git a/spot/bricks/brick-assert b/spot/bricks/brick-assert index c02b35f4e..649e1be12 100644 --- a/spot/bricks/brick-assert +++ b/spot/bricks/brick-assert @@ -58,21 +58,21 @@ #define ASSERT_EQ_IDX(i, x, y) static_cast< decltype(i, x, y, void(0)) >(0) #endif -/* you must #include to use ASSERT_UNREACHABLE_F */ +/* you must #include to use ASSERT_UNREACHABLE_F */ #define UNREACHABLE_F(...) ::brick::_assert::assert_die_fn( \ BRICK_LOCWRAP( BRICK_LOCATION( brick::string::fmtf(__VA_ARGS__) ) ) ) -#define UNREACHABLE(x) ::brick::_assert::assert_die_fn( \ - BRICK_LOCWRAP( BRICK_LOCATION( x ) ) ) +#define UNREACHABLE(...) ::brick::_assert::assert_die_fn( \ + BRICK_LOCWRAP( BRICK_LOCATION( ::brick::_assert::fmt( __VA_ARGS__ ) ) ) ) #define UNREACHABLE_() ::brick::_assert::assert_die_fn( \ BRICK_LOCWRAP( BRICK_LOCATION( "an unreachable location" ) ) ) #define NOT_IMPLEMENTED() ::brick::_assert::assert_die_fn( \ BRICK_LOCWRAP( BRICK_LOCATION( "a missing implementation" ) ) ) #ifdef _MSC_VER -#define DIVINE_UNUSED +#define UNUSED #define noexcept #else -#define DIVINE_UNUSED __attribute__((unused)) +#define UNUSED __attribute__((unused)) #endif #ifndef BRICK_ASSERT_H @@ -139,7 +139,7 @@ struct AssertFailed : std::exception (*this) << ":\n " << expected << " " << l.stmt; } - const char *what() const noexcept override { return str.c_str(); } + const char *what() const noexcept { return str.c_str(); } }; static inline void format( AssertFailed & ) {} @@ -196,9 +196,9 @@ inline void assert_die_fn( Location l ) } \ } -ASSERT_FN(eq, ==, !=) -ASSERT_FN(leq, <=, >) -ASSERT_FN(lt, <, >=) +ASSERT_FN(eq, ==, !=); +ASSERT_FN(leq, <=, >); +ASSERT_FN(lt, <, >=); template< typename Location, typename X > void assert_pred_fn( Location l, X x, bool p ) @@ -221,6 +221,10 @@ void assert_neq_fn( Location l, X x, Y y ) throw f; } +inline std::string fmt( const char *str ) { return str; } +inline std::string fmt( std::string str ) { return str; } +// another overload of fmt is exported by brick-string and it allows more complex formating + } } diff --git a/spot/bricks/brick-bitlevel b/spot/bricks/brick-bitlevel index 0c33b8ee9..682e9441d 100644 --- a/spot/bricks/brick-bitlevel +++ b/spot/bricks/brick-bitlevel @@ -28,7 +28,7 @@ #include #ifdef __linux -#include +#include #include #elif !defined LITTLE_ENDIAN // if defined _WIN32 #define BYTE_ORDER 1234 @@ -55,6 +55,28 @@ constexpr inline T1 downalign( T1 v, T2 a ) { return v - (v % T1(a)); } +template< typename F, typename T > +void maybe_bitcast( F f, T &t ) +{ + ASSERT( sizeof( T ) == sizeof( F ) ); + memcpy( &t, &f, sizeof( T ) ); +} + +template< typename F, typename T > +void bitcast( F f, T &t ) +{ + //static_assert( sizeof( F ) == sizeof( T ) ); + maybe_bitcast( f, t ); +} + +template< typename T, typename F > +T bitcast( F f ) +{ + T t; + bitcast( f, t ); + return t; +} + namespace compiletime { template< typename T > @@ -89,10 +111,7 @@ struct bvpair constexpr bvpair( L l, H h = 0 ) : low( l ), high( h ) {} constexpr bvpair() = default; explicit constexpr operator bool() const { return low || high; } -#if ((__GNUC__ >= 4 && __GNUC_MINOR__ > 9) || (__clang_major__ == 3 && __clang_minor__ >= 6)) - constexpr -#endif - bvpair operator<<( int s ) const + constexpr bvpair operator<<( int s ) const { int rem = 8 * sizeof( low ) - s; int unshift = std::max( rem, 0 ); @@ -100,10 +119,7 @@ struct bvpair H carry = ( low & ~ones< L >( unshift ) ) >> unshift; return bvpair( low << s, ( high << s ) | ( carry << shift ) ); } -#if ((__GNUC__ >= 4 && __GNUC_MINOR__ > 9) || (__clang_major__ == 3 && __clang_minor__ >= 6)) - constexpr -#endif - bvpair operator>>( int s ) const + constexpr bvpair operator>>( int s ) const { int rem = 8 * sizeof( low ) - s; int unshift = std::max( rem, 0 ); @@ -133,22 +149,45 @@ template< int i > using bitvec = typename _bitvec< i >::T; namespace { -uint32_t mixdown( uint64_t i ) /* due to Thomas Wang */ +template< typename T > +union Cast { - i = (~i) + (i << 18); - i = i ^ (i >> 31); - i = i * 21; - i = i ^ (i >> 11); - i = i + (i << 6); - i = i ^ (i >> 22); - return i; + bitvec< sizeof( T ) * 8 > r; + T c; + Cast( T c ) : c( c ) {} + Cast( bitvec< sizeof( T ) * 8 > r ) : r( r ) {} +}; + +template< typename T > +bitvec< sizeof( T ) * 8 > raw( T t ) +{ + Cast< T > c( t ); + return c.r; } -__attribute__((unused)) uint32_t mixdown( uint32_t a, uint32_t b ) +template< typename T > +T unraw( bitvec< sizeof( T ) * 8 > r ) { - return mixdown( ( uint64_t( a ) << 32 ) | uint64_t( b ) ); + Cast< T > c( r ); + return c.c; } +// uint32_t mixdown( uint64_t i ) /* due to Thomas Wang */ +// { +// i = (~i) + (i << 18); +// i = i ^ (i >> 31); +// i = i * 21; +// i = i ^ (i >> 11); +// i = i + (i << 6); +// i = i ^ (i >> 22); +// return i; +// } + +// uint32_t mixdown( uint32_t a, uint32_t b ) +// { +// return mixdown( ( uint64_t( a ) << 32 ) | uint64_t( b ) ); +// } + } /* @@ -367,13 +406,13 @@ struct BitField return *this; \ } - OP(+=) - OP(-=) - OP(*=) - OP(/=) - OP(%=) - OP(|=) - OP(&=) + OP(+=); + OP(-=); + OP(*=); + OP(/=); + OP(%=); + OP(|=); + OP(&=); #undef OP }; }; @@ -654,7 +693,8 @@ struct BitTupleTest { ASSERT( !get< 0 >( bt ).word() ); } - TEST(assign) { + TEST(assign) /* TODO fails in DIVINE */ + { bitlevel::BitTuple< BitField< bool, 1 >, BitField< int, 6 >, diff --git a/spot/bricks/brick-hashset b/spot/bricks/brick-hashset index 22c0a0006..d4b9ccbe8 100644 --- a/spot/bricks/brick-hashset +++ b/spot/bricks/brick-hashset @@ -43,7 +43,7 @@ using hash::hash128_t; struct DefaultHasher { - auto hash( int64_t v ) const -> std::pair + auto hash( int64_t v ) const { return std::make_pair( v, ~v ); } @@ -153,7 +153,8 @@ struct FastAtomicCell : CellBase< T, Hasher > return true; } - bool tryStore( T v, hash32_t hash ) { + bool tryStore( T v, hash32_t hash ) + { hash |= 0x1; hash32_t chl = 0; if ( _hashlock.compare_exchange_strong( chl, (hash << 2) | 1 ) ) { @@ -164,6 +165,8 @@ struct FastAtomicCell : CellBase< T, Hasher > return false; } + void store( T v, hash32_t hash ) { tryStore( v, hash ); } + template< typename Value > bool is( const Value &v, hash64_t hash, Hasher &h ) { hash |= 0x1; @@ -209,9 +212,9 @@ struct AtomicCell : CellBase< T, Hasher > { std::atomic< Tagged< T > > value; - static_assert( sizeof( std::atomic< Tagged< T > > ) == sizeof( Tagged< T > ), - "std::atomic< Tagged< T > > must be lock-free" ); - static_assert( Tagged< T >::tag_bits > 0, "T has at least a one-bit tagspace" ); + // static_assert( sizeof( std::atomic< Tagged< T > > ) == sizeof( Tagged< T > ), + // "std::atomic< Tagged< T > > must be lock-free" ); + // static_assert( Tagged< T >::tag_bits > 0, "T has at least a one-bit tagspace" ); bool empty() { return !value.load().t; } bool invalid() { @@ -226,20 +229,19 @@ struct AtomicCell : CellBase< T, Hasher > return AtomicCell( value.exchange( v ) ); } - Tagged< T > &deatomize() { - value.load(); // fence - return *reinterpret_cast< Tagged< T > * >( &value ); - } - - T &fetch() { return deatomize().t; } + T fetch() { return value.load().t; } T copy() { Tagged< T > v = value; v.tag( 0 ); return v.t; } bool wait() { return !invalid(); } - void store( T bn, hash64_t hash ) { - return tryStore( bn, hash ); + void store( T bn, hash64_t hash ) + { + Tagged< T > next( bn ); + next.tag( highbits( hash, Tagged< T >::tag_bits ) | 1 ); + value.store( next ); } - bool tryStore( T b, hash64_t hash ) { + bool tryStore( T b, hash64_t hash ) + { Tagged< T > zero; Tagged< T > next( b ); next.tag( highbits( hash, Tagged< T >::tag_bits ) | 1 ); @@ -299,13 +301,25 @@ struct HashSetBase Hasher hasher; - struct iterator { + struct proxy + { + value_type v; + value_type *operator->() { return &v; } + }; + + struct iterator + { Cell *_cell; + value_type _value; bool _new; - iterator( Cell *c = nullptr, bool n = false ) : _cell( c ), _new( n ) {} - value_type *operator->() { return &(_cell->fetch()); } - value_type &operator*() { return _cell->fetch(); } + + iterator( Cell *c = nullptr, bool n = false ) + : _cell( c ), _value( c ? c->fetch() : value_type() ), _new( n ) + {} + proxy operator->() { return proxy( _value ); } + value_type operator*() { return _value; } value_type copy() { return _cell->copy(); } + void update( value_type v ) { _cell->store( v ); } bool valid() { return _cell; } bool isnew() { return _new; } }; @@ -355,13 +369,16 @@ struct _HashSet : HashSetBase< Cell > size_t _maxsize; bool _growing; - size_t size() const { return _table.size(); } + size_t capacity() const { return _table.size(); } bool empty() const { return !_used; } int count( const value_type &i ) { return find( i ).valid(); } hash64_t hash( const value_type &i ) { return hash128( i ).first; } hash128_t hash128( const value_type &i ) { return this->hasher.hash( i ); } - iterator insert( value_type i ) { return insertHinted( i, hash( i ) ); } + iterator insert( value_type i, bool replace = false ) + { + return insertHinted( i, hash( i ), replace ); + } template< typename T > iterator find( const T &i ) { @@ -387,27 +404,35 @@ struct _HashSet : HashSetBase< Cell > return this->end(); } - iterator insertHinted( const value_type &i, hash64_t h ) { - return insertHinted( i, h, _table, _used ); + iterator insertHinted( const value_type &i, hash64_t h, bool replace = false ) + { + return insertHinted( i, h, _table, _used, replace ); } - iterator insertHinted( const value_type &item, hash64_t h, Table &table, int &used ) + iterator insertHinted( const value_type &item, hash64_t h, Table &table, int &used, + bool replace = false ) { - if ( !_growing && size_t( _used ) > (size() / 100) * 75 ) + if ( !_growing && size_t( _used ) > ( capacity() / 100 ) * 75 ) grow(); size_t idx; - for ( size_t i = 0; i < this->maxcollisions; ++i ) { + for ( size_t i = 0; i < this->maxcollisions; ++i ) + { idx = this->index( h, i, _bits ); - if ( table[ idx ].empty() ) { + if ( table[ idx ].empty() ) + { ++ used; table[ idx ].store( item, h ); return iterator( &table[ idx ], true ); } if ( table[ idx ].is( item, h, this->hasher ) ) + { + if ( replace ) + table[ idx ].store( item, h ); return iterator( &table[ idx ], false ); + } } grow(); @@ -416,7 +441,7 @@ struct _HashSet : HashSetBase< Cell > } void grow() { - if ( 2 * size() >= _maxsize ) + if ( 2 * capacity() >= _maxsize ) UNREACHABLE( "ran out of space in the hash table" ); if( _growing ) @@ -428,7 +453,7 @@ struct _HashSet : HashSetBase< Cell > Table table; - table.resize( 2 * size(), Cell() ); + table.resize( 2 * capacity(), Cell() ); _bits |= (_bits << 1); // unmask more for ( auto cell : _table ) { @@ -444,7 +469,7 @@ struct _HashSet : HashSetBase< Cell > _growing = false; } - void setSize( size_t s ) + void reserve( size_t s ) { _bits = 0; while ((s = s >> 1)) @@ -454,7 +479,7 @@ struct _HashSet : HashSetBase< Cell > void clear() { _used = 0; - std::fill( _table.begin(), _table.end(), value_type() ); + std::fill( _table.begin(), _table.end(), Cell() ); } Cell &cellAt( size_t idx ) { return _table[ idx ]; } @@ -467,7 +492,7 @@ struct _HashSet : HashSetBase< Cell > _HashSet( Hasher h, int initial ) : Base( h ), _used( 0 ), _maxsize( -1 ), _growing( false ) { - setSize( initial ); + reserve( initial ); } }; @@ -595,28 +620,29 @@ struct _ConcurrentHashSet : HashSetBase< Cell > return s * 2; } - size_t size() { return current().size(); } + size_t capacity() { return current().size(); } Row ¤t() { return _s->table[ _s->currentRow ]; } Row ¤t( unsigned index ) { return _s->table[ index ]; } bool changed( unsigned row ) { return row < _s->currentRow || _s->growing; } - iterator insert( value_type x ) + iterator insert( value_type x, bool replace = false ) { - return insertHinted( x, hasher.hash( x ).first ); + return insertHinted( x, hasher.hash( x ).first, replace ); } template< typename T > - iterator find( const T &x ) { + iterator find( const T &x ) + { return findHinted( x, hasher.hash( x ).first ); } int count( value_type x ) { return find( x ).valid() ? 1 : 0; } - iterator insertHinted( value_type x, hash64_t h ) + iterator insertHinted( value_type x, hash64_t h, bool replace = false ) { while ( true ) { Row &row = current( _l.currentRow ); - Insert ir = insertCell< false >( row, x, h ); + Insert ir = insertCell< false >( row, x, h, replace ); switch ( ir.r ) { case Resolution::Success: increaseUsage(); @@ -689,7 +715,7 @@ struct _ConcurrentHashSet : HashSetBase< Cell > } template< bool force > - Insert insertCell( Row &row, value_type x, hash64_t h ) + Insert insertCell( Row &row, value_type x, hash64_t h, bool replace = false ) { if ( !force ) { // read usage first to guarantee usage <= size @@ -709,14 +735,20 @@ struct _ConcurrentHashSet : HashSetBase< Cell > { Cell &cell = row[ Base::index( h, i, mask ) ]; - if ( cell.empty() ) { + if ( cell.empty() ) + { if ( cell.tryStore( x, h ) ) return Insert( Resolution::Success, &cell ); if ( !force && changed( _l.currentRow ) ) return Insert( Resolution::Growing ); } + if ( cell.is( x, h, hasher ) ) + { + if ( replace ) + cell.store( x, h ); return Insert( Resolution::Found, &cell ); + } if ( !force && changed( _l.currentRow ) ) return Insert( Resolution::Growing ); @@ -932,7 +964,7 @@ struct Sequential ASSERT( set.count( 1 ) ); unsigned count = 0; - for ( unsigned i = 0; i != set.size(); ++i ) + for ( unsigned i = 0; i != set.capacity(); ++i ) if ( set.valueAt( i ) ) ++count; @@ -1024,7 +1056,7 @@ struct Parallel static HS< int > _multi( std::size_t count, int from, int to ) { shmem::ThreadSet< Insert > arr; - arr.resize( count, Insert() ); + arr.resize( count ); arr[ 0 ].set.initialSize( isize ); for ( std::size_t i = 0; i < count; ++i ) @@ -1049,7 +1081,7 @@ struct Parallel int count = 0; std::set< int > s; - for ( size_t i = 0; i != set.size(); ++i ) { + for ( size_t i = 0; i != set.capacity(); ++i ) { if ( set.valueAt( i ) ) { if ( s.find( set.valueAt( i ) ) == s.end() ) s.insert( set.valueAt( i ) ); @@ -1395,7 +1427,7 @@ struct RandomInsert { : insert( true ), max( max ) { if ( bg->reserve() > 0 ) - t.setSize( bg->items() * bg->reserve() ); + t.reserve( bg->items() * bg->reserve() ); } template< typename BG > @@ -1478,7 +1510,7 @@ struct wrap_set { C< T > *t; struct ThreadData {}; HashTable< T > withTD( ThreadData & ) { return *this; } - void setSize( int s ) { t->rehash( s ); } + void reserve( int s ) { t->rehash( s ); } void insert( T i ) { t->insert( i ); } int count( T i ) { return t->count( i ); } HashTable() : t( new C< T > ) {} diff --git a/spot/bricks/brick-shmem b/spot/bricks/brick-shmem index d5fc92989..a9dd25472 100644 --- a/spot/bricks/brick-shmem +++ b/spot/bricks/brick-shmem @@ -42,10 +42,10 @@ #include #include -#include #include #include #include +#include #include // alarm #include @@ -54,18 +54,21 @@ #define BRICK_SHMEM_H #ifndef BRICKS_CACHELINE -#if (__GNUC__ >= 7) // Please new Versions of GCC -#define BRICKS_CACHELINE 16 -#else #define BRICKS_CACHELINE 64 #endif -#endif namespace brick { namespace shmem { +struct ThreadBase +{ + virtual void start() = 0; + virtual void stop() = 0; + virtual ~ThreadBase() {} +}; + template< typename T > -struct Thread : T +struct Thread : T, ThreadBase { std::unique_ptr< std::thread > _thread; bool _start_on_move; // :-( @@ -89,9 +92,9 @@ struct Thread : T start(); } - virtual void start() noexcept + virtual void start() { - _thread.reset( new std::thread( [this]() noexcept { this->main(); } ) ); + _thread.reset( new std::thread( [this]() { this->main(); } ) ); } virtual void stop() @@ -117,13 +120,6 @@ struct Thread : T _thread.reset(); } } - - const Thread& operator=(const Thread& other) - { - std::cerr << "FIXME Added by us (Spot) to avoid compilation warnings\n"; - std::cerr << " Should not pass here.\n"; - return other; - } }; template< typename T > @@ -178,13 +174,13 @@ struct AsyncLoop : Thread< LoopWrapper< T > > stop(); /* call the correct stop(), with interrupt() */ } - void start() noexcept override + void start() { this->_interrupted.store( false, std::memory_order_relaxed ); Super::start(); } - void stop() override + void stop() { this->interrupt(); Super::stop(); @@ -192,19 +188,22 @@ struct AsyncLoop : Thread< LoopWrapper< T > > }; template< typename L > -auto async_loop( L &&l ) -> AsyncLoop< LambdaWrapper< L > >&& +using AsyncLambdaLoop = AsyncLoop< LambdaWrapper< L > >; + +template< typename L > +auto async_loop( L &&l ) { - AsyncLoop< LambdaWrapper< L > > al( std::forward< L >( l ) ); + AsyncLambdaLoop< L > al( std::forward< L >( l ) ); al._start_on_move = true; return std::move( al ); } template< typename L > -auto thread( L &&l ) -> Thread< LambdaWrapper< L > > +auto thread( L &&l ) { Thread< LambdaWrapper< L > > thr( std::forward< L >( l ) ); thr._start_on_move = true; - return thr; + return std::move( thr ); } template< typename T > @@ -213,7 +212,7 @@ struct ThreadSet : std::vector< Thread< T > > template< typename... Args > ThreadSet( Args&&... args ) : std::vector< Thread< T > >( std::forward< Args >( args )... ) {} - void start() noexcept { for ( auto &t : *this ) t.start(); } + void start() { for ( auto &t : *this ) t.start(); } void join() { for ( auto &t : *this ) t.join(); } }; @@ -638,6 +637,41 @@ struct Chunked template< typename T > using SharedQueue = Chunked< LockedQueue, T >; +namespace +{ + +using steady_time = std::chrono::time_point< std::chrono::steady_clock >; + +// steady_time later( int ms ) +// { +// return std::chrono::steady_clock::now() + std::chrono::milliseconds( ms ); +// } + +template< typename I, typename F > +std::future_status wait( I b, I e, F cleanup, + steady_time until /*= 0 later( 500 )*/ ) try +{ + int total = 0, ready = 0; + for ( auto i = b; i != e; ++i ) + { + if ( !i->valid() ) continue; + ++ total; + if ( i->wait_until( until ) == std::future_status::ready ) + { + ++ ready; + i->get(); /* throw any exceptions */ + } + } + if ( total == ready ) + { + cleanup(); + return std::future_status::ready; + } + return std::future_status::timeout; +} catch ( ... ) { cleanup(); throw; }; + +} + } namespace t_shmem {