Skip to content

Commit 160897b

Browse files
Updated Signal class to allow auto-reset and custom signal value
1 parent 362aaac commit 160897b

File tree

1 file changed

+38
-10
lines changed

1 file changed

+38
-10
lines changed

Common/interface/LockHelper.h

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <thread>
2727
#include <mutex>
2828
#include <condition_variable>
29+
#include <atomic>
2930

3031
#include "Atomics.h"
3132

@@ -137,7 +138,7 @@ class Signal
137138
Signal() {}
138139

139140
// http://en.cppreference.com/w/cpp/thread/condition_variable
140-
void Trigger(bool bNotifyAll = false)
141+
void Trigger(bool NotifyAll = false, int SignalValue = 1)
141142
{
142143
// The thread that intends to modify the variable has to
143144
// * acquire a std::mutex (typically via std::lock_guard)
@@ -146,17 +147,25 @@ class Signal
146147
{
147148
// std::condition_variable works only with std::unique_lock<std::mutex>
148149
std::lock_guard<std::mutex> Lock(m_Mutex);
149-
m_bIsTriggered = true;
150+
VERIFY(SignalValue != 0, "Signal value must not be 0");
151+
VERIFY(m_SignalledValue == 0 && m_NumThreadsAwaken == 0, "Not all threads have been awaken since the signal was triggered last time, or the signal has not been reset");
152+
m_SignalledValue = SignalValue;
150153
}
151154
// Unlocking is done before notifying, to avoid waking up the waiting
152155
// thread only to block again (see notify_one for details)
153-
if(bNotifyAll)
156+
if(NotifyAll)
154157
m_CondVar.notify_all();
155158
else
156159
m_CondVar.notify_one();
157160
}
158161

159-
void Wait()
162+
// WARNING!
163+
// If multiple threads are waiting for a signal in an infinite loop,
164+
// autoresetting the signal does not guarantee that one thread cannot
165+
// go through the loop twice. In this case, every thread must wait for its
166+
// own auto-reset signal or the threads must be blocked by another signal
167+
168+
int Wait(bool AutoReset = false, int NumThreadsWaiting = 0)
160169
{
161170
// Any thread that intends to wait on std::condition_variable has to
162171
// * acquire a std::unique_lock<std::mutex>, on the SAME MUTEX as used to protect the shared variable
@@ -166,26 +175,45 @@ class Signal
166175
// the thread is awakened, and the mutex is atomically reacquired:
167176
// - The thread should then check the condition and resume waiting if the wake up was spurious.
168177
std::unique_lock<std::mutex> Lock(m_Mutex);
169-
if (!m_bIsTriggered)
178+
// It is safe to check m_SignalledValue since we are holding
179+
// the mutex
180+
if(m_SignalledValue == 0)
181+
{
182+
m_CondVar.wait(Lock, [&] {return m_SignalledValue != 0;} );
183+
}
184+
int SignalledValue = m_SignalledValue;
185+
// Count the number of threads awaken while holding the mutex
186+
++m_NumThreadsAwaken;
187+
if (AutoReset)
170188
{
171-
m_CondVar.wait(Lock, [&] {return m_bIsTriggered; });
189+
VERIFY(NumThreadsWaiting > 0, "Number of waiting threads must not be 0 when auto resetting the signal");
190+
// Reset the signal while holding the mutex. If Trigger() is executed by another
191+
// thread, it will wait until we release the mutex
192+
if(m_NumThreadsAwaken == NumThreadsWaiting)
193+
{
194+
m_SignalledValue = 0;
195+
m_NumThreadsAwaken = 0;
196+
}
172197
}
198+
return SignalledValue;
173199
}
174200

175201
void Reset()
176202
{
177203
std::lock_guard<std::mutex> Lock(m_Mutex);
178-
m_bIsTriggered = false;
204+
m_SignalledValue = 0;
205+
m_NumThreadsAwaken = 0;
179206
}
180207

181-
volatile bool IsTriggered()const { return m_bIsTriggered; }
208+
bool IsTriggered()const { return m_SignalledValue != 0; }
182209

183210
private:
184211

185212
std::mutex m_Mutex;
186213
std::condition_variable m_CondVar;
187-
volatile bool m_bIsTriggered = false;
188-
214+
std::atomic_int m_SignalledValue = 0;
215+
std::atomic_int m_NumThreadsAwaken = 0;
216+
189217
Signal(const Signal&) = delete;
190218
Signal& operator = (const Signal&) = delete;
191219
};

0 commit comments

Comments
 (0)