Skip to content

Instantly share code, notes, and snippets.

@dymk
Created February 3, 2017 02:39
Show Gist options
  • Select an option

  • Save dymk/7a4cb61405207ce857a70241ec97b572 to your computer and use it in GitHub Desktop.

Select an option

Save dymk/7a4cb61405207ce857a70241ec97b572 to your computer and use it in GitHub Desktop.
clang++-3.8 --std=c++14 rw_tx_lock.cc
#include <chrono>
#include <thread>
#include <mutex>
#include <atomic>
#include <utility>
#include <thread>
#include <memory>
#include <string>
#include <iostream>
template <typename T>
class rw_tx_lock {
std::shared_ptr<T> local_t;
std::mutex obj_lock; // synchronize the entire object
std::mutex writer_lock; // only allow one writer at a time
public:
template <typename... Args>
rw_tx_lock(Args&&... args) {
local_t = std::make_shared<T>(std::forward<Args>(args)...);
}
std::shared_ptr<const T> get_readable() {
std::lock_guard<std::mutex> l(this->obj_lock);
return local_t;
}
template <typename Cb>
void get_writeable(Cb const& cb) {
std::lock_guard<std::mutex> l(writer_lock); // only one writer at a time
auto readable = this->get_readable();
std::shared_ptr<T> new_local_t = cb(readable);
std::lock_guard<std::mutex> rl(obj_lock); // serialize access to local_t
this->local_t = new_local_t;
}
};
struct Foo {
int local;
Foo(int local) : local(local) {}
};
std::mutex cout_mtx;
void rand_sleep() {
auto const min = 100;
auto const max = 300;
int rand_ms = rand()%(max-min + 1) + min;
std::this_thread::sleep_for(std::chrono::milliseconds(rand_ms));
}
int main() {
rw_tx_lock<Foo> foo_transactor(0);
auto reader_routine = [&foo_transactor](int tid) {
// release lock on readable twice
for(int j = 0; j < 2; j++) {
auto readable = foo_transactor.get_readable();
for(int i = 0; i < 3; i++) {
{
std::lock_guard<std::mutex> l(cout_mtx);
std::cout << "thread " << tid << " read value: " << readable->local << std::endl;
}
// does not compile :) readable is a `const Foo`
// readable->local += 1
rand_sleep();
}
}
};
auto writer_routine = [&foo_transactor](int tid) {
for(int i = 0; i < 3; i++) {
foo_transactor.get_writeable([&](auto const& old) {
// also does not compile
// old->local += 1;
{
std::lock_guard<std::mutex> l(cout_mtx);
std::cout << "thread " << tid << " write value: " << old->local << std::endl;
}
return std::make_shared<Foo>(old->local + 1);
});
rand_sleep();
}
};
std::thread t0(reader_routine, 0);
std::thread t1(reader_routine, 1);
std::thread t2(reader_routine, 2);
std::thread t3(writer_routine, 3);
std::thread t4(writer_routine, 4);
t0.join();
t1.join();
t2.join();
t3.join();
t4.join();
std::cout << "final value: " << foo_transactor.get_readable()->local << std::endl;
return 0;
}
// thread 0 read value: 0
// thread 2 read value: 0
// thread 1 read value: 0
// thread 3 write value: 0
// thread 4 write value: 1
// thread 3 write value: 2
// thread 4 write value: 3
// thread 2 read value: 0
// thread 0 read value: 0
// thread 1 read value: 0
// thread 3 write value: 4
// thread 4 write value: 5
// thread 2 read value: 0
// thread 1 read value: 0
// thread 0 read value: 0
// thread 2 read value: 6
// thread 0 read value: 6
// thread 1 read value: 6
// thread 1 read value: 6
// thread 0 read value: 6
// thread 2 read value: 6
// thread 1 read value: 6
// thread 0 read value: 6
// thread 2 read value: 6
// final value: 6
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment