Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

how to implement an event in c++

222 views
Skip to first unread message

mvor...@gmail.com

unread,
Feb 5, 2019, 2:57:25 PM2/5/19
to
I would like to implement an event class in C++: an object that can be signaled to wake up another thread(s), and upon signal it can stay signaled or reset itself to non signaled if it wakes up 1 thread. below is my attempt at implementing such a class using a condition variable. is this a viable approach?

#pragma once

#include <mutex>
#include <atomic>
#include <condition_variable>

class event
{
public:
void signal() noexcept
{
{
std::unique_lock<std::mutex> lock(m_mutex);
m_signaled = true;
}
m_cv.notify_all();
}

void wait(bool reset = false) noexcept
{
std::unique_lock<std::mutex> lock(m_mutex);
m_cv.wait(lock, [&](){ return m_signaled; });
if(reset) m_signaled = false;
}

void reset() noexcept
{
std::unique_lock<std::mutex> lock(m_mutex);
m_signaled = false;
}

private:
bool m_signaled = false;
std::mutex m_mutex;
std::condition_variable m_cv;
};

Chris M. Thomasson

unread,
Feb 6, 2019, 11:44:11 PM2/6/19
to
Looks okay at first glance. Need to test it. However, notice the
broadcast, aka notify_all? It should be beneficial to split apart the
two types of events. Auto-reset, and manual-reset. The former only needs
a single signal, while the latter basically needs broadcast...

Öö Tiib

unread,
Feb 7, 2019, 3:12:33 AM2/7/19
to
On Tuesday, 5 February 2019 21:57:25 UTC+2, mvor...@gmail.com wrote:
> I would like to implement an event class in C++: an object that can be signaled to wake up another thread(s), and upon signal it can stay signaled or reset itself to non signaled if it wakes up 1 thread. below is my attempt at implementing such a class using a condition variable. is this a viable approach?

What you mean by "event"? Typically it
is something to what we want to "subscribe" an
"handler" to and "unsubscribe" it when we lose
interest. Tell us about usage pattern of your
events.

By your design we have to wait. The code blocks waiting
threads infinitely if event doesn't happen.
When we don't want to miss an event then we have to make
a separate thread that waits for it.
When we lose interest in some event then we have to
detach and leak such threads, no much else to do.
Sounds quite expensive and inconvenient so you should
explain what is the point of it in actual software.

Paavo Helde

unread,
Feb 7, 2019, 4:11:37 AM2/7/19
to
I guess he is talking about Microsoft definition of "event"
(https://docs.microsoft.com/en-us/windows/desktop/sync/event-objects).

In Windows these are needed for forcibly waking up a
WaitForMultipleObjects() call which is waiting on some sockets or child
processes, etc. The most relevant Unix equivalent is pipe() + poll() if
I have understood correctly. These both also work over process
boundaries, while a simple std::mutex does not.

As a custom C++ class using its own synchronization cannot be used
neither with Windows WaitForMultipleObjects() nor Unix poll(), the
usefulness of such a class is pretty low IMO.

Chris M. Thomasson

unread,
Feb 7, 2019, 4:12:15 AM2/7/19
to
On 2/6/2019 8:44 PM, Chris M. Thomasson wrote:
> On 2/5/2019 11:57 AM, mvor...@gmail.com wrote:
>> I would like to implement an event class in C++: an object that can be
>> signaled to wake up another thread(s), and upon signal it can stay
>> signaled or reset itself to non signaled if it wakes up 1 thread.
>> below is my attempt at implementing such a class using a condition
>> variable. is this a viable approach?
>>
>> #pragma once
>>
>> #include <mutex>
>> #include <atomic>
>> #include <condition_variable>
>>
>> class event
>> {
>> public:
>>      void signal() noexcept
>>      {
>>          {
>>              std::unique_lock<std::mutex> lock(m_mutex);
>>              m_signaled = true;
>>          }
>>          m_cv.notify_all();
>>      }
>>
>>      void wait(bool reset = false) noexcept
>>      {
>>          std::unique_lock<std::mutex> lock(m_mutex);
>>          m_cv.wait(lock, [&](){ return m_signaled; });
>>          if(reset) m_signaled = false;
>>      }[...]

Beware of mixing and matching the reset value. Creating two distinct
classes: auto-reset and manual-reset should work fine. These can be
somewhat odd to work with sometimes, but they definitely have their place.

Sam

unread,
Feb 7, 2019, 6:43:51 AM2/7/19
to
mvor...@gmail.com writes:

> I would like to implement an event class in C++: an object that can be
> signaled to wake up another thread(s), and upon signal it can stay signaled
> or reset itself to non signaled if it wakes up 1 thread. below is my attempt
> at implementing such a class using a condition variable. is this a viable
> approach?

Generally viable, but there are some obvious bugs. Several operations block
on change to m_signaled, using a condition variable. Several operations that
update m_signaled fail to notify the condition variable.

Since the condition variable is used to wait for a change to m_signaled, all
code paths that modify m_signaled should notify the condition variable.

Öö Tiib

unread,
Feb 7, 2019, 11:22:06 AM2/7/19
to
Yes. Key features are capabilities to wait for any one from a list of multiple
and to set a timeout for how long to wait. His "event" lacks those
features and that makes it useful for rare corner case.

Chris M. Thomasson

unread,
Feb 7, 2019, 2:43:38 PM2/7/19
to
On 2/7/2019 3:43 AM, Sam wrote:
> mvor...@gmail.com writes:
>
>> I would like to implement an event class in C++: an object that can be
>> signaled to wake up another thread(s), and upon signal it can stay
>> signaled or reset itself to non signaled if it wakes up 1 thread.
>> below is my attempt at implementing such a class using a condition
>> variable. is this a viable approach?
>
> Generally viable, but there are some obvious bugs. Several operations
> block on change to m_signaled, using a condition variable. Several
> operations that update m_signaled fail to notify the condition variable.
>
> Since the condition variable is used to wait for a change to m_signaled,
> all code paths that modify m_signaled should notify the condition variable.

Why would one need to notify the condition variable on a reset?


>> #pragma once
>>
>> #include <mutex>
>> #include <atomic>
>> #include <condition_variable>
>>
>> class event
>> {
>> public:
>>     void signal() noexcept
>>     {
>>         {
>>             std::unique_lock<std::mutex> lock(m_mutex);
>>             m_signaled = true;
>>         }
>>         m_cv.notify_all();
>>     }
>>
>>     void wait(bool reset = false) noexcept
>>     {
>>         std::unique_lock<std::mutex> lock(m_mutex);
>>         m_cv.wait(lock, [&](){ return m_signaled; });
>>         if(reset) m_signaled = false;
>>     }

The wait wrt the reset parameter is fairly dangerous here. It allows one
to mix and match an auto-reset event with a manual-reset event. This is
not Kosher, and can lead to deadlock conditions. Creating two distinct
classes, auto-reset and manual-reset would help solve this issue.


>>     void reset() noexcept
>>     {
>>         std::unique_lock<std::mutex> lock(m_mutex);
>>         m_signaled = false;
>>     }

No need to signal anything here, even though we mutated the predicate of
the condition variable.
0 new messages