C++ Concurrency Sandbox
Loading...
Searching...
No Matches
CustomRWLock.cpp
Go to the documentation of this file.
1/**
2 * @file CustomRWLock.cpp
3 * @brief Manual implementation of a Readers-Writers Lock with Writer Preference.
4 * * Unlike std::shared_mutex, this implementation explicitly tracks waiting writers
5 * to prevent "Reader Starvation," ensuring writers aren't blocked forever by
6 * a continuous stream of concurrent readers.
7 */
8
9#include <iostream>
10#include <thread>
11#include <mutex>
12#include <condition_variable>
13#include <vector>
14#include <chrono>
15
16/** * @brief Global mutex to prevent console output interleaving.
17 * Standard output is not thread-safe; this ensures log messages remain legible.
18 */
19std::mutex log_mtx;
20
21/**
22 * @class RWLock
23 * @brief A custom synchronization primitive for the Readers-Writers problem.
24 * * This class uses a single std::mutex and std::condition_variable to manage
25 * state. It implements a "Writer Preference" policy.
26 */
27class RWLock {
28private:
29 std::mutex mtx; ///< Protects access to the internal state variables.
30 std::condition_variable cv; ///< Used to block and wake threads based on state changes.
31
32 int active_readers = 0; ///< Number of threads currently holding a read lock.
33 int waiting_writers = 0; ///< Number of threads waiting to acquire a write lock.
34 bool writer_active = false; ///< Flag indicating if a writer is in the critical section.
35
36public:
37 /**
38 * @brief Acquires a shared read lock.
39 * * Blocks if:
40 * 1. A writer is currently active.
41 * 2. There are writers waiting in the queue (Starvation Protection).
42 */
43 void lock_read() {
44 std::unique_lock<std::mutex> lock(mtx);
45
46 // Wait while a writer is active OR a writer is waiting
47 while(writer_active || waiting_writers > 0) {
48 cv.wait(lock);
49 }
50
51 active_readers++;
52 }
53
54 /**
55 * @brief Releases the shared read lock.
56 * * If this was the last active reader, it notifies all waiting threads
57 * (potentially waking a waiting writer).
58 */
59 void unlock_read() {
60 std::unique_lock<std::mutex> lock(mtx);
61 active_readers--;
62
63 if(active_readers == 0)
64 cv.notify_all();
65 }
66
67 /**
68 * @brief Acquires an exclusive write lock.
69 * * Increments the waiting_writers count to block new readers.
70 * Blocks until all active readers and any active writer have finished.
71 */
72 void lock_write() {
73 std::unique_lock<std::mutex> lock(mtx);
74 waiting_writers++;
75
76 // Wait while any readers or writers are active
77 while(active_readers > 0 || writer_active) {
78 cv.wait(lock);
79 }
80
81 waiting_writers--;
82 writer_active = true;
83 }
84
85 /**
86 * @brief Releases the exclusive write lock.
87 * * Sets writer_active to false and wakes all waiting threads.
88 */
89 void unlock_write() {
90 std::unique_lock<std::mutex> lock(mtx);
91 writer_active = false;
92 cv.notify_all();
93 }
94};
95
96/**
97 * @brief Simulates a reader's lifecycle.
98 * @param rw Pointer to the shared RWLock instance.
99 * @param id Unique identifier for the reader thread.
100 */
101void readerTask(RWLock* rw, int id) {
102 rw->lock_read();
103 {
104 std::lock_guard<std::mutex> log_lock(log_mtx);
105 std::cout << "[Reader " << id << "] Start Reading..." << std::endl;
106 }
107
108 std::this_thread::sleep_for(std::chrono::milliseconds(200));
109
110 {
111 std::lock_guard<std::mutex> log_lock(log_mtx);
112 std::cout << "[Reader " << id << "] Finished Reading." << std::endl;
113 }
114 rw->unlock_read();
115}
116
117/**
118 * @brief Simulates a writer's lifecycle.
119 * @param rw Pointer to the shared RWLock instance.
120 * @param id Unique identifier for the writer thread.
121 */
122void writerTask(RWLock* rw, int id) {
123 std::this_thread::sleep_for(std::chrono::milliseconds(50));
124
125 rw->lock_write();
126 {
127 std::lock_guard<std::mutex> log_lock(log_mtx);
128 std::cout << ">>> [Writer " << id << "] EXCLUSIVE WRITE START <<<" << std::endl;
129 }
130
131 std::this_thread::sleep_for(std::chrono::milliseconds(500));
132
133 {
134 std::lock_guard<std::mutex> log_lock(log_mtx);
135 std::cout << ">>> [Writer " << id << "] EXCLUSIVE WRITE END <<<" << std::endl;
136 }
137 rw->unlock_write();
138}
139
140/**
141 * @brief Entry point for the custom RWLock demonstration.
142 * Creates a mix of reader and writer threads to demonstrate starvation protection.
143 */
144int main(void) {
145 RWLock rw;
146 std::vector<std::thread> threads;
147
148 for(int i=1; i<=50; i++) {
149 threads.emplace_back(readerTask, &rw, i);
150 }
151
152 std::this_thread::sleep_for(std::chrono::milliseconds(2000));
153
154 for(int i=1; i<=2; i++) {
155 threads.emplace_back(writerTask, &rw, i);
156 }
157
158 std::this_thread::sleep_for(std::chrono::milliseconds(2000));
159
160 for(int i=1001; i<=1100; i++) {
161 threads.emplace_back(readerTask, &rw, i);
162 }
163
164 for(auto& t : threads) {
165 if(t.joinable())
166 t.join();
167 }
168
169 std::cout << "All metadata operations completed." << std::endl;
170 return 0;
171}
std::mutex log_mtx
Global mutex to prevent console output interleaving. Standard output is not thread-safe; this ensures...
void readerTask(RWLock *rw, int id)
Simulates a reader's lifecycle.
int main(void)
Entry point for the custom RWLock demonstration. Creates a mix of reader and writer threads to demons...
void writerTask(RWLock *rw, int id)
Simulates a writer's lifecycle.
A custom synchronization primitive for the Readers-Writers problem.
void unlock_read()
Releases the shared read lock.
void lock_write()
Acquires an exclusive write lock.
void unlock_write()
Releases the exclusive write lock.
void lock_read()
Acquires a shared read lock.