0

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?

fenixD
  • 13
  • 2

0 Answers0