Skip to content

Commit f6336c7

Browse files
committed
added unsafe member functions to directly manipulate the queue and mutex
1 parent e6882c4 commit f6336c7

File tree

2 files changed

+78
-0
lines changed

2 files changed

+78
-0
lines changed

include/mt_queue/include/ut/mt_queue/mt_queue.hpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,51 @@ class MtQueue {
197197
std::lock_guard<std::mutex> g {m_mu};
198198
return fn(m_q);
199199
}
200+
201+
/**
202+
* Returns mutable reference to the internal mutex
203+
*
204+
* This function should almost never be used! Using the mutex directly can lead to deadlocks.
205+
*
206+
* !!!!!!!!WARNING!!!!!!!!
207+
* Locking the mutex, then calling any non-`unsafe` member function in the same thread *WILL* lead to a deadlock!
208+
*
209+
* To perform multiple operations atomically on the underlying queue, prefer `underLock`
210+
*/
211+
[[nodiscard]]
212+
std::mutex &unsafeGetMutex() const {
213+
return m_mu;
214+
}
215+
216+
/**
217+
* Returns a reference to the internal queue.
218+
*
219+
* This function should almost never be used! Accessing the queue directly can lead to data-races.
220+
*
221+
* In order to safely access the underlying queue in a multithreaded environment, first lock the mutex,
222+
* returned from `unsafeGetMutex`, or use one of the non-`unsafe` functions instead of directly
223+
* manipulating the queue.
224+
*/
225+
[[nodiscard]]
226+
Q &unsafeGetQueue() {
227+
return m_q;
228+
}
229+
230+
/**
231+
* Returns a reference to the internal queue.
232+
*
233+
* This function should almost never be used! Accessing the queue directly can lead to data-races.
234+
*
235+
* In order to safely access the underlying queue in a multithreaded environment, first lock the mutex,
236+
* returned from `unsafeGetMutex`, or use one of the non-`unsafe` functions instead of directly
237+
* manipulating the queue.
238+
*/
239+
[[nodiscard]]
240+
Q const &unsafeGetQueue() const {
241+
return m_q;
242+
}
243+
244+
200245
private:
201246
mutable std::mutex m_mu;
202247
mutable std::condition_variable m_cv;

tests/mt_queue.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
// NOLINTBEGIN(readability-magic-numbers,google-build-using-namespace)
2+
#include <catch2/catch_test_macros.hpp>
23
#include <ut/mt_queue/mt_queue.hpp>
34
#include <ut/overload/overload.hpp>
45

56
#include <barrier>
67
#include <chrono>
8+
#include <iostream>
79
#include <numeric>
810
#include <thread>
911
#include <utility>
@@ -235,4 +237,35 @@ TEMPLATE_TEST_CASE("Single trheaded using shared_ptr",
235237
REQUIRE(*front == 30);
236238
}
237239

240+
TEMPLATE_TEST_CASE("2 threaded get mutex", "[mt_queue]", std::queue<int>, std::deque<int>, std::priority_queue<int>) {
241+
242+
MtQueue<int, TestType> q;
243+
244+
std::barrier b {2};
245+
auto t1 = std::jthread([&q, &b]() {
246+
b.arrive_and_wait();
247+
auto &mu = q.unsafeGetMutex();
248+
mu.lock();
249+
std::this_thread::sleep_for(100ms);
250+
ut::Overload {
251+
[](std::queue<int> &inner_q) { inner_q.push(10); },
252+
[](std::deque<int> &inner_q) { inner_q.push_back(10); },
253+
[](std::priority_queue<int> &inner_q) { inner_q.push(10); },
254+
}(q.unsafeGetQueue());
255+
mu.unlock();
256+
});
257+
auto t2 = std::jthread([&q, &b]() {
258+
b.arrive_and_wait();
259+
std::this_thread::sleep_for(50ms);
260+
q.push(9);
261+
});
262+
263+
t1.join();
264+
t2.join();
265+
REQUIRE(q.front() == 10);
266+
REQUIRE(q.pop() == 10);
267+
REQUIRE(q.front() == 9);
268+
REQUIRE(q.pop() == 9);
269+
}
270+
238271
// NOLINTEND(readability-magic-numbers,google-build-using-namespace)

0 commit comments

Comments
 (0)