0

Code below is a test program, emulating some events processing. Full code snippets is here. There are 3 threads for event producer, for event distributor like event bus, what handles system event too, and for a certain game, what handles significant events.

And I have stdout printer in event bus, what method pass into every event as a callback. The event bus thread:

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:
    EventBus()
    {
        m_Worker = std::thread([this]() /// Firing event
        {
            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();
    }

    AddEvent(std::shared_ptr<EventBase>&& event)
    {
        event->SetPrintingCallback(
            [/*ptr = shared_from_this()*/ this](auto&& message)
            {
                return m_InfoPrinter->SafetyPrint(
                    m_GameTicks,
                    std::forward<decltype(message)>(message));
            });
        {
            std::lock_guard guard(m_Locker);
            m_EventStorage.push(std::move(event));
        }
        m_NewEvent.notify_one();
    }

    template <typename TEvent, typename TSubscriber> // TODO add enable_if
    void Subscribe(const std::shared_ptr<TSubscriber>& subscriber)
    {
        m_Subscribers[typeid(TEvent)].connect(
            [subscriber](std::shared_ptr<EventBase> event)
            {
                subscriber->ProcessEvent(std::static_pointer_cast<TEvent>(event));
            });
    }

    template <typename TEvent, typename TFunc>
    void Subscribe(const TFunc& subscriber_callback)
    {
        m_Subscribers[typeid(TEvent)].connect(
            [subscriber_callback](std::shared_ptr<EventBase> event)
            {
                subscriber_callback(std::static_pointer_cast<TEvent>(event));
            });
    }

    template <typename TEvent>
    void SubscribeSelf()
    {
        m_Subscribers[typeid(TEvent)].connect(
            [this](std::shared_ptr<EventBase> event)
            {
                ProcessEvent(std::static_pointer_cast<TEvent>(event));
            });
    }
};

There is SIGSEGV at this variant sometimes of AddEvent, but if I pass shared_form_this, it will be called either never, or sometimes, processed one or two events. But I don't why does it happen, and the first, and the second variant?

I try to debug and I see that boost.signal2 disconnects handlers at the second variant with shared_from_this passing.

The game thread:

class GameMap : public std::enable_shared_from_this<GameMap>
{
public:
    GameMap(const MapPoint& map_size, const Key<GameMapFactory>&)
        : m_MapSize(map_size)
        , m_Work(std::make_unique<dummy_game_work_type>(m_GameContext.get_executor()))
    {
        m_GameThread = std::thread([this] ()
        {
            m_GameContext.run();
        });
    }

    ~GameMap()
    {
        m_Work.reset(); // need transfer this to processing finish event?
        m_GameThread.join();
    }
};

GameMap handles events into ProcessEvent method, does some validation and post it handlers via boost::asio::post for further processing.

The main thread:

int main(int ac, char **av)
{
    std::shared_ptr<GameMap> map;
    std::shared_ptr<EventBus> bus = std::make_shared<EventBus (std::make_shared<StdOutPrinter>());

    bus->Subscribe<MapCreationEvent>(
        [&map, &bus](auto&& event) // todo check about references in capture list
        {
            map = GameMapFactory{}.CreateMap(std::forward<decltype(event)>(event));
            bus->Subscribe<MarchEvent>(map); 
            // todo think about replace this to Game Map Factory
        });
    bus->Subscribe<SpawnCreatureEvent>(
        [](auto&& event)
        {
            CreatureFactory{}.CreateCreature(std::forward<decltype(event)>(event));
        });
    //    bus->Subscribe<WaitEvent>(bus); // todo it doesn't work too
    //    bus->Subscribe<WaitEvent>( // todo why it doesn't work subscription to self slot??
    //        [bus](auto&& event)
    //        {
    //            bus->ProcessEvent(std::forward<decltype(event)>(event));
    //        });
    bus->SubscribeSelf<WaitEvent>();
    bus->SubscribeSelf<FinishEvent>();

    // todo test producer
    std::vector<std::shared_ptr<EventBase>> events = {
        std::make_shared<MapCreationEvent>(MapPoint(40, 40)),
        std::make_shared<SpawnCreatureEvent>(1, MapPoint(40, 40), 50),
        std::make_shared<SpawnCreatureEvent>(2, MapPoint(30, 40), 100),
        std::make_shared<SpawnCreatureEvent>(3, MapPoint(10, 20), 70),
        std::make_shared<SpawnCreatureEvent>(4, MapPoint(40, 30), 90),
        std::make_shared<MarchEvent>(1, MapPoint(40, 30)),
        std::make_shared<MarchEvent>(2, MapPoint(20, 20)),
        std::make_shared<WaitEvent>(1),
        std::make_shared<WaitEvent>(1),
        std::make_shared<WaitEvent>(1),
        std::make_shared<WaitEvent>(1),
        std::make_shared<WaitEvent>(1),
        std::make_shared<WaitEvent>(1),
        std::make_shared<WaitEvent>(1),
        std::make_shared<WaitEvent>(1),
        std::make_shared<WaitEvent>(1),
        std::make_shared<WaitEvent>(1),
        std::make_shared<MarchEvent>(4, MapPoint(20, 20)),
        std::make_shared<FinishEvent>(),
    };

    for (auto& ev : events)
    {
        bus->AddEvent(std::move(ev));
    }
}

I caught similar problem here. If I subscribe bus to handling Wait and Finish events via Subscribe, I will get the similar behaviour I wrote above: signals2 disconnect all my slots and stop the program. Why?

Could somebody help me with disconnecting and sigsegv?

I tried to change passing this to shared_from_this but I got only other error

fenixD
  • 13
  • 2

0 Answers0