I have a sender()
function, which sends out UDP packet. At the end of the sender()
it wakes up a receiver thread to wait for the UDP response with a timeout.
Here the sender()
could be called by main thread or by the receiver thread. Receiver thread after receiving a response message or timeout, it may decide to send a new packet. That's why, the sender()
should be able to called from receiver's context too. The pesudo code is as follows:
std::mutex m;
std::conditional_variable reciever_cv;
void sender()
{
request = create_new_packet();
socket.sendBytes( request );
receiver_cv.notify_one();
}
// receiver() gets started as a thread when system is up
void receiver()
{
while(true)
{
std::uniq_lock lock(m);
receiver_cv.wait( lock, [](){ return predicate; }); // predicate could be anything
socket.receiveBytes();
// ... some processing
if( new packet needs to be sent )
{
sender();
}
}
}
My question here is that can a thread notify itself to wakeup in the next loop?
My task is simple. The only complication is there is a function shared by different threads. I hope the number of wakeup is memorized somehow, and the receiver just wakes up to match that number.
So far, all online materials I have browsed give suggestion on issuing notification from a separate thread.
Would counting semaphore a better approach in my case?
My question here is that can a thread notify itself to wakeup in the next loop?
No, but you can prevent unnecessarily waiting.
One thing crucially missing from your example is the typical (atomic) bool
that signals whether you should wait:
std::mutex m;
std::conditional_variable reciever_cv;
// with a std::atomic<bool>, you could also avoid locking the mutex within sender()
bool waiting_for_response = false;
void sender()
{
request = create_new_packet();
socket.sendBytes( request );
{
std::scoped_lock l( m );
waiting_for_response = true;
}
receiver_cv.notify_one();
}
void receiver()
{
while(true)
{
{
std::unique_lock lock(m);
receiver_cv.wait( lock, [](){ return waiting_for_response; });
socket.receiveBytes();
waiting_for_response = false;
}
// ...
sender(); // safe
Calling sender()
would now be safe and not lock wait at all because the predicate is checked before calling wait()
(without a predicate).
You typically need such a bool
anyway to deal with spurious wakeup,
and that's probably all you need to cover your use case.
This is not really a thread notifying itself though.
However, this design is still quite flawed. The biggest issue is that it doesn't properly handle when sender()
is called while the receiver is already waiting, so you can only be waiting for one packet at a time.
You actually need something like a std::counting_semaphore
for a proper single-producer, single-consumer design with more than one element.
See also The usage case of counting semaphore
You can also get rid of both the condition variable and the mutex if you simply use std::atomic<bool>::wait
/std:atomic<bool>::notify
, added in C++20.
See also std::atomic<bool>::wait vs. std::condition_variable::wait.
With the use of std::atomic<int>
, you could even extend that approach to handle multiple packets simultaneously.