Skip to content

Instantly share code, notes, and snippets.

@jrepp
Created September 9, 2022 18:05
Show Gist options
  • Save jrepp/baac41719a0357a213beb5e72d7e61e2 to your computer and use it in GitHub Desktop.
Save jrepp/baac41719a0357a213beb5e72d7e61e2 to your computer and use it in GitHub Desktop.
Example of using a flyweight pattern to lock and protect access to a hardware bus
#include <iostream>
#include <unordered_map>
#include <mutex>
#include <thread>
#include <vector>
#include <atomic>
#include <cstdint>
#include <cassert>
#include <chrono>
#include <random>
using namespace std;
struct bus_address_t {
uint32_t bus_id;
};
struct device_address_t {
uint32_t device_id;
};
// forward declare the flyweight
class bus_access_t;
// the bus manager doesn't provide bus access directdly, instead
// users must acquire a bus_access_t flyweight, when it goes
// out of scope it automatically releases the bus.
class bus_manager_t {
public:
bus_manager_t(const bus_address_t &address) : m_address(address) {}
~bus_manager_t() { assert(m_holders == 0); }
bool acquire(bus_access_t &access);
protected:
friend class bus_access_t;
void enter() {
m_holders++;
}
void leave() {
m_holders--;
}
uint32_t read() {
m_value = m_value + (m_value & 0xffff) + 1;
return m_value;
}
void write(uint32_t v) {
m_value = v << 16;
}
private:
bus_address_t m_address = { 0 };
uint32_t m_value = 0;
mutex m_mut;
atomic<uint32_t> m_holders;
inline bool ensure_locked() {
return m_holders == 0;
}
};
class bus_access_t {
public:
bus_access_t(bus_manager_t &bus, uint32_t client_id)
: m_bus(bus), m_lock(bus.m_mut), m_client_id(client_id)
{
bus.enter();
}
~bus_access_t() {
m_bus.leave();
}
// bus access API:
uint32_t read() {
uint32_t v = m_bus.read();
cout << "client[" << m_client_id << "] READ " << v << endl;
return v;
}
void write(uint32_t v) {
cout << "client[" << m_client_id << "] WRITE " << v << endl;
m_bus.write(m_client_id);
}
private:
bus_manager_t &m_bus;
unique_lock<mutex> m_lock;
uint32_t m_client_id;
};
void serialize_protocol(bus_access_t &access) {
}
// To execute C++, please define "int main()"
int main() {
constexpr uint32_t c_num_threads = 32;
random_device r;
// Generate a normal distribution around that mean
seed_seq seed2{r(), r(), r(), r(), r(), r(), r(), r()};
mt19937 e2(seed2);
std::normal_distribution<> normal_dist(5, 2);
bus_manager_t mgr(bus_address_t{1});
vector<thread> threads;
for(uint32_t i = 0; i < c_num_threads; ++i) {
threads.emplace_back([i, &mgr, &e2, &normal_dist]{
cout << "hello from thread " << i << endl;
auto sleep_ms = normal_dist(e2);
bus_access_t access(mgr, i);
for (uint32_t i = 0; i < 100; ++i) {
access.read();
access.write(i);
access.write(i);
access.write(i);
}
this_thread::sleep_for(chrono::milliseconds((uint64_t)sleep_ms));
cout << "thread finished " << i << endl;
});
}
for (auto &t : threads) {
if (t.joinable()) {
t.join();
}
}
cout << "finished." << endl;
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment