Skip to content

Commit

Permalink
Merge pull request EOSIO#9 from steemit/8-check-locking
Browse files Browse the repository at this point in the history
8 check locking
  • Loading branch information
Michael Vandeberg authored Jan 12, 2017
2 parents f1d4734 + d319d9c commit ac77ced
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 9 deletions.
100 changes: 91 additions & 9 deletions include/chainbase/chainbase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <boost/multi_index_container.hpp>

#include <boost/chrono.hpp>
#include <boost/config.hpp>
#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/thread.hpp>
Expand All @@ -22,11 +23,20 @@
#include <array>
#include <atomic>
#include <fstream>
#include <iostream>
#include <stdexcept>
#include <typeindex>

#ifndef CHAINBASE_DEFAULT_NUM_RW_LOCKS
#define CHAINBASE_DEFAULT_NUM_RW_LOCKS 10
#ifndef CHAINBASE_NUM_RW_LOCKS
#define CHAINBASE_NUM_RW_LOCKS 10
#endif

#ifdef CHAINBASE_CHECK_LOCKING
#define CHAINBASE_REQUIRE_READ_LOCK() require_read_lock()
#define CHAINBASE_REQUIRE_WRITE_LOCK() require_write_lock()
#else
#define CHAINBASE_REQUIRE_READ_LOCK()
#define CHAINBASE_REQUIRE_WRITE_LOCK()
#endif

namespace chainbase {
Expand Down Expand Up @@ -133,6 +143,29 @@ namespace chainbase {
int64_t revision = 0;
};

/**
* The code we want to implement is this:
*
* ++target; try { ... } finally { --target }
*
* In C++ the only way to implement finally is to create a class
* with a destructor, so that's what we do here.
*/
class int_incrementer
{
public:
int_incrementer( int32_t& target ) : _target(target)
{ ++_target; }
~int_incrementer()
{ --_target; }

int32_t get()const
{ return _target; }

private:
int32_t& _target;
};

/**
* The value_type stored in the multiindex container must have a integer field with the name 'id'. This will
* be the primary key and it will be assigned and managed by generic_index.
Expand Down Expand Up @@ -586,17 +619,22 @@ namespace chainbase {
void next_lock()
{
_current_lock++;
new( &_locks[ _current_lock % 10 ] ) read_write_mutex();
new( &_locks[ _current_lock % CHAINBASE_NUM_RW_LOCKS ] ) read_write_mutex();
}

read_write_mutex& current_lock()
{
return _locks[ _current_lock % 10 ];
return _locks[ _current_lock % CHAINBASE_NUM_RW_LOCKS ];
}

uint32_t current_lock_num()
{
return _current_lock;
}

private:
std::array< read_write_mutex, 10 > _locks;
std::atomic< uint32_t > _current_lock;
std::array< read_write_mutex, CHAINBASE_NUM_RW_LOCKS > _locks;
std::atomic< uint32_t > _current_lock;
};


Expand All @@ -615,6 +653,23 @@ namespace chainbase {
void close();
void flush();
void wipe( const bfs::path& dir );
void set_require_locking( bool enable_require_locking );

#ifdef CHAINBASE_CHECK_LOCKING
void require_lock_fail( const char* lock_type )const;

void require_read_lock()const
{
if( BOOST_UNLIKELY( _enable_require_locking & _read_only & (_read_lock_count <= 0) ) )
require_lock_fail("read");
}

void require_write_lock()
{
if( BOOST_UNLIKELY( _enable_require_locking & (_write_lock_count <= 0) ) )
require_lock_fail("write");
}
#endif

struct session {
public:
Expand Down Expand Up @@ -672,6 +727,7 @@ namespace chainbase {

void set_revision( uint64_t revision )
{
CHAINBASE_REQUIRE_WRITE_LOCK();
for( auto i : _index_list ) i->set_revision( revision );
}

Expand Down Expand Up @@ -716,7 +772,9 @@ namespace chainbase {
}

template<typename MultiIndexType>
const generic_index<MultiIndexType>& get_index()const {
const generic_index<MultiIndexType>& get_index()const
{
CHAINBASE_REQUIRE_READ_LOCK();
typedef generic_index<MultiIndexType> index_type;
typedef index_type* index_type_ptr;
assert( _index_map.size() > index_type::value_type::type_id );
Expand All @@ -725,7 +783,9 @@ namespace chainbase {
}

template<typename MultiIndexType, typename ByIndex>
auto get_index()const -> decltype( ((generic_index<MultiIndexType>*)( nullptr ))->indicies().template get<ByIndex>() ) {
auto get_index()const -> decltype( ((generic_index<MultiIndexType>*)( nullptr ))->indicies().template get<ByIndex>() )
{
CHAINBASE_REQUIRE_READ_LOCK();
typedef generic_index<MultiIndexType> index_type;
typedef index_type* index_type_ptr;
assert( _index_map.size() > index_type::value_type::type_id );
Expand All @@ -734,7 +794,9 @@ namespace chainbase {
}

template<typename MultiIndexType>
generic_index<MultiIndexType>& get_mutable_index() {
generic_index<MultiIndexType>& get_mutable_index()
{
CHAINBASE_REQUIRE_WRITE_LOCK();
typedef generic_index<MultiIndexType> index_type;
typedef index_type* index_type_ptr;
assert( _index_map.size() > index_type::value_type::type_id );
Expand All @@ -745,6 +807,7 @@ namespace chainbase {
template< typename ObjectType, typename IndexedByType, typename CompatibleKey >
const ObjectType* find( CompatibleKey&& key )const
{
CHAINBASE_REQUIRE_READ_LOCK();
typedef typename get_index_type< ObjectType >::type index_type;
const auto& idx = get_index< index_type >().indicies().template get< IndexedByType >();
auto itr = idx.find( std::forward< CompatibleKey >( key ) );
Expand All @@ -755,6 +818,7 @@ namespace chainbase {
template< typename ObjectType >
const ObjectType* find( oid< ObjectType > key = oid< ObjectType >() ) const
{
CHAINBASE_REQUIRE_READ_LOCK();
typedef typename get_index_type< ObjectType >::type index_type;
const auto& idx = get_index< index_type >().indices();
auto itr = idx.find( key );
Expand All @@ -765,6 +829,7 @@ namespace chainbase {
template< typename ObjectType, typename IndexedByType, typename CompatibleKey >
const ObjectType& get( CompatibleKey&& key )const
{
CHAINBASE_REQUIRE_READ_LOCK();
auto obj = find< ObjectType, IndexedByType >( std::forward< CompatibleKey >( key ) );
if( !obj ) BOOST_THROW_EXCEPTION( std::out_of_range( "unknown key" ) );
return *obj;
Expand All @@ -773,6 +838,7 @@ namespace chainbase {
template< typename ObjectType >
const ObjectType& get( const oid< ObjectType >& key = oid< ObjectType >() )const
{
CHAINBASE_REQUIRE_READ_LOCK();
auto obj = find< ObjectType >( key );
if( !obj ) BOOST_THROW_EXCEPTION( std::out_of_range( "unknown key") );
return *obj;
Expand All @@ -781,20 +847,23 @@ namespace chainbase {
template<typename ObjectType, typename Modifier>
void modify( const ObjectType& obj, Modifier&& m )
{
CHAINBASE_REQUIRE_WRITE_LOCK();
typedef typename get_index_type<ObjectType>::type index_type;
get_mutable_index<index_type>().modify( obj, m );
}

template<typename ObjectType>
void remove( const ObjectType& obj )
{
CHAINBASE_REQUIRE_WRITE_LOCK();
typedef typename get_index_type<ObjectType>::type index_type;
return get_mutable_index<index_type>().remove( obj );
}

template<typename ObjectType, typename Constructor>
const ObjectType& create( Constructor&& con )
{
CHAINBASE_REQUIRE_WRITE_LOCK();
typedef typename get_index_type<ObjectType>::type index_type;
return get_mutable_index<index_type>().emplace( std::forward<Constructor>(con) );
}
Expand All @@ -803,6 +872,10 @@ namespace chainbase {
auto with_read_lock( Lambda&& callback, uint64_t wait_micro = 1000000 ) -> decltype( (*(Lambda*)nullptr)() )
{
read_lock lock( _rw_manager->current_lock(), bip::defer_lock_type() );
#ifdef CHAINBASE_CHECK_LOCKING
BOOST_ATTRIBUTE_UNUSED
int_incrementer ii( _read_lock_count );
#endif

if( !wait_micro )
{
Expand All @@ -825,6 +898,10 @@ namespace chainbase {
BOOST_THROW_EXCEPTION( std::logic_error( "cannot acquire write lock on read-only process" ) );

write_lock lock( _rw_manager->current_lock(), boost::defer_lock_t() );
#ifdef CHAINBASE_CHECK_LOCKING
BOOST_ATTRIBUTE_UNUSED
int_incrementer ii( _write_lock_count );
#endif

if( !wait_micro )
{
Expand All @@ -835,6 +912,7 @@ namespace chainbase {
while( !lock.timed_lock( boost::posix_time::microsec_clock::local_time() + boost::posix_time::microseconds( wait_micro ) ) )
{
_rw_manager->next_lock();
std::cerr << "Lock timeout, moving to lock " << _rw_manager->current_lock_num() << std::endl;
lock = write_lock( _rw_manager->current_lock(), boost::defer_lock_t() );
}
}
Expand All @@ -860,6 +938,10 @@ namespace chainbase {
vector<unique_ptr<abstract_index>> _index_map;

bfs::path _data_dir;

int32_t _read_lock_count = 0;
int32_t _write_lock_count = 0;
bool _enable_require_locking = false;
};

template<typename Object, typename... Args>
Expand Down
16 changes: 16 additions & 0 deletions src/chainbase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,22 @@ namespace chainbase {
_index_map.clear();
}

void database::set_require_locking( bool enable_require_locking )
{
#ifdef CHAINBASE_CHECK_LOCKING
_enable_require_locking = enable_require_locking;
#endif
}

#ifdef CHAINBASE_CHECK_LOCKING
void database::require_lock_fail( const char* lock_type )const
{
std::string err_msg = "require_" + std::string( lock_type ) + "_lock() failed";
std::cerr << err_msg << std::endl;
BOOST_THROW_EXCEPTION( std::runtime_error( err_msg ) );
}
#endif

void database::undo()
{
for( auto& item : _index_list )
Expand Down

0 comments on commit ac77ced

Please sign in to comment.