I have simple variant of testing program what processes some events. If I run code with same way I'll get different behaviour
class EventBus : public std::enable_shared_from_this<EventBus>
{
using handlers = boost::signals2::signal<void(std::shared_ptr<EventBase>)>;
private:
std::atomic<double> m_GameTicks;
std::thread m_Worker;
std::mutex m_Locker;
std::condition_variable m_NewEvent;
bool m_Suspend;
std::queue<std::shared_ptr<EventBase>> m_EventStorage;
std::unordered_map<std::type_index, handlers> m_Subscribers;
std::shared_ptr<IPrinter> m_InfoPrinter;
public:
explicit EventBus(std::shared_ptr <IPrinter> printer_impl)
: m_GameTicks(0.0)
, m_Suspend(false)
, m_InfoPrinter(std::move(printer_impl))
{
logging::INFO("EventBus");
m_Worker = std::thread([this]()
{
while (true)
{
std::shared_ptr<EventBase> event;
{
std::unique_lock guard(m_Locker);
m_NewEvent.wait(guard, [this]{ return m_Suspend || !m_EventStorage.empty(); });
if (m_Suspend && m_EventStorage.empty())
{
break;
}
event = std::move(m_EventStorage.front());
m_EventStorage.pop();
}
auto raw_event = event.get();
try
{
m_Subscribers[typeid(*raw_event)](event);
}
catch (std::exception& ex)
{
std::cerr << ex.what() << std::endl;
std::exit(EXIT_FAILURE);
}
}
});
}
~EventBus()
{
m_Worker.join();
}
void AddEvent(std::shared_ptr<EventBase>&& event)
{
{
std::lock_guard guard(m_Locker);
m_EventStorage.push(std::move(event));
}
m_NewEvent.notify_one();
}
void ProcessEvent([[maybe_unused]] std::shared_ptr<FinishEvent> event)
{
{
std::lock_guard guard(m_Locker);
m_Suspend = true;
}
m_NewEvent.notify_one();
}
template <typename TEvent, typename TSubscriber>
void Subscribe(const std::shared_ptr<TSubscriber>& subscriber)
{
m_Subscribers[typeid(TEvent)].connect(
[subscriber](std::shared_ptr<EventBase> event)
{
// work flow didn't go here ???
subscriber->ProcessEvent(std::static_pointer_cast<TEvent>(event));
});
}
template <typename TEvent>
void SubscribeSelf()
{
m_Subscribers[typeid(TEvent)].connect(
[this](std::shared_ptr<EventBase> event)
{
// work flow went here
ProcessEvent(std::static_pointer_cast<TEvent>(event));
});
}
};
int main(int ac, [[maybe_unused]] char **av)
{
std::shared_ptr<EventBus> bus = std::make_shared<EventBus(std::make_shared<StdOutPrinter>());
bus->SubscribeSelf<FinishEvent>(); // OK
// bus->Subscribe<FinishEvent>(bus); // Fail
std::vector<std::shared_ptr<EventBase>> events = {
std::make_shared<FinishEvent>(),
};
for (auto& ev : events)
{
bus->AddEvent(std::move(ev));
}
}
If I run with using SubscribeSelf, it will OK. But if I run with Subscribe, it fails without calling EventBus destructor and connected slot. EventBus worker thread didn't finish too... What do I do wrong?