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