Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DRAFT] Add ReadWrite locking mechanism on top of Blockchain. #9173

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
125 changes: 125 additions & 0 deletions contrib/epee/include/syncobj.h
Expand Up @@ -32,10 +32,14 @@

#include <boost/chrono/duration.hpp>
#include <boost/thread/condition_variable.hpp>
#include <boost/thread/detail/thread.hpp>
#include <boost/thread/locks.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/recursive_mutex.hpp>
#include <boost/thread/thread.hpp>
#include <cstdint>
#include "misc_log_ex.h"
#include "misc_language.h"

namespace epee
{
Expand Down Expand Up @@ -150,6 +154,127 @@ namespace epee
};


struct reader_writer_lock {

reader_writer_lock() noexcept :
write_mode(false),
read_mode(0) {};

bool start_read() noexcept {
if (have_write()) {
return false;
}
lock_reader();
return true;
}

void end_read() noexcept {
unlock_reader();
condition.notify_all();
}

bool start_write() noexcept {
if (have_write()) {
return false;
}
lock_and_change(boost::this_thread::get_id());
return true;
}

void end_write() noexcept {
unlock_writer();
condition.notify_all();
}

bool have_write() noexcept {
boost::mutex::scoped_lock lock(internal_mutex);
if (write_mode
&& writer_id == boost::this_thread::get_id()) {
return true;
}
return false;
}

~reader_writer_lock() = default;
reader_writer_lock(reader_writer_lock&) = delete;
reader_writer_lock operator=(reader_writer_lock&) = delete;

private:

void unlock_writer() noexcept {
CHECK_AND_ASSERT_MES2(write_mode && !read_mode, "Ending write transaction by " << boost::this_thread::get_id() << " while the lock is not in write mode");
boost::mutex::scoped_lock lock(internal_mutex);
rw_mutex.unlock();
write_mode = false;
writer_id = boost::thread::id();
}

void unlock_reader() noexcept {
CHECK_AND_ASSERT_MES2(read_mode && !write_mode, "Ending read transaction by " << boost::this_thread::get_id() << " while the lock is not in read mode");
boost::mutex::scoped_lock lock(internal_mutex);
rw_mutex.unlock_shared();
read_mode--;
if (rw_mutex.try_lock()) {
CHECK_AND_ASSERT_MES2(!read_mode, "Reader is not zero but goes to read_mode = 0 by " << boost::this_thread::get_id());
write_mode = false;
rw_mutex.unlock();
}
return;
}

void lock_reader() noexcept {
CHECK_AND_ASSERT_MES2(read_mode < UINT32_MAX, "Maximum number of readers reached.");
do {
boost::mutex::scoped_lock lock(internal_mutex);
if(!write_mode && rw_mutex.try_lock_shared()) {
write_mode = false;
read_mode++;
return;
}
condition.wait(lock);
} while(true);
}

void lock_and_change(boost::thread::id new_owner) noexcept {
do {
boost::mutex::scoped_lock lock(internal_mutex);
if (!read_mode && rw_mutex.try_lock()) {
writer_id = new_owner;
write_mode = true;
read_mode = 0;
return;
}
condition.wait(lock);
} while(true);
}

boost::mutex internal_mutex; // keep the data in RWLock consistent.
boost::shared_mutex rw_mutex;
bool write_mode = false;
uint32_t read_mode = 0;
boost::thread::id writer_id;
boost::condition_variable condition;

};

#define RWLOCK(lock) \
bool rw_release_required##lock = lock.start_write(); \
epee::misc_utils::auto_scope_leave_caller scope_exit_handler##lock = \
epee::misc_utils::create_scope_leave_handler([&]() { \
if (rw_release_required##lock) \
lock.end_write(); \
});


#define RLOCK(lock) \
bool r_release_required##lock = lock.start_read(); \
epee::misc_utils::auto_scope_leave_caller scope_exit_handler##lock = \
epee::misc_utils::create_scope_leave_handler([&]() { \
if (r_release_required##lock) \
lock.end_read(); \
});


#define CRITICAL_REGION_LOCAL(x) {} epee::critical_region_t<decltype(x)> critical_region_var(x)
#define CRITICAL_REGION_BEGIN(x) { boost::this_thread::sleep_for(boost::chrono::milliseconds(epee::debug::g_test_dbg_lock_sleep())); epee::critical_region_t<decltype(x)> critical_region_var(x)
#define CRITICAL_REGION_LOCAL1(x) {boost::this_thread::sleep_for(boost::chrono::milliseconds(epee::debug::g_test_dbg_lock_sleep()));} epee::critical_region_t<decltype(x)> critical_region_var1(x)
Expand Down