@@ -15,7 +15,11 @@ namespace csv {
1515 namespace internals {
1616 /* * A std::deque wrapper which allows multiple read and write threads to concurrently
1717 * access it along with providing read threads the ability to wait for the deque
18- * to become populated
18+ * to become populated.
19+ *
20+ * Concurrency strategy: writer-side mutations (push_back/pop_front) are locked;
21+ * hot-path flags (empty/is_waitable) are atomic; operator[] and iterators are
22+ * not synchronized and must not run concurrently with writers.
1923 */
2024 template <typename T>
2125 class ThreadSafeDeque {
@@ -32,11 +36,6 @@ namespace csv {
3236 this ->_is_empty .store (source.empty (), std::memory_order_release);
3337 }
3438
35- void clear () noexcept {
36- this ->data .clear ();
37- this ->_is_empty .store (true , std::memory_order_release);
38- }
39-
4039 bool empty () const noexcept {
4140 return this ->_is_empty .load (std::memory_order_acquire);
4241 }
@@ -46,6 +45,11 @@ namespace csv {
4645 return this ->data .front ();
4746 }
4847
48+ /* * NOTE: operator[] is not synchronized.
49+ * Only call when no concurrent push_back/pop_front can occur.
50+ * std::deque can reallocate its internal map on push_back, which
51+ * makes concurrent operator[] access undefined behavior.
52+ */
4953 T& operator [](size_t n) {
5054 return this ->data [n];
5155 }
@@ -102,15 +106,13 @@ namespace csv {
102106
103107 /* * Tell listeners that this deque is actively being pushed to */
104108 void notify_all () {
105- std::unique_lock<std::mutex> lock{ this ->_lock };
106- this ->_is_waitable = true ;
109+ this ->_is_waitable .store (true , std::memory_order_release);
107110 this ->_cond .notify_all ();
108111 }
109112
110113 /* * Tell all listeners to stop */
111114 void kill_all () {
112- std::unique_lock<std::mutex> lock{ this ->_lock };
113- this ->_is_waitable = false ;
115+ this ->_is_waitable .store (false , std::memory_order_release);
114116 this ->_cond .notify_all ();
115117 }
116118
0 commit comments