В конце февраля, после C++ Russia 2017, возникла тема добавления в SObjectizer поддержки не константных экземпляров сообщений. Суть в том, что в SObjectizer из-за первоначальной направленности на взаимодействие в режиме 1:N, все сообщения доставлялись получателям в виде константных объектов. Получатель имел константную ссылку на пришедшее к нему сообщение и не должен был менять этот объект, т.к. в это же время то же самое сообщение мог обрабатывать еще кто-то.
Для некоторых сценариев это оказалось не очень хорошо. Например, если цепочка агентов выстраивается в конвейер, по которому движется большой объект, а каждый из агентов должен что-то в этом объекте поменять и передать измененный объект дальше. Или когда один агент должен передать "тяжелый" объект другому агенту, но использовать shared_ptr для этих целей не выгодно.
В итоге мы начали думать, как дать возможность пересылать не константные объекты-сообщения при взаимодействии агентов в режиме 1:1. Сначала идеи крутились вокруг movable-сообщений, т.е. сообщений, содержимое которых можно перемещать между экземплярами (например, когда кто-то делает send, то содержимое сообщение сразу же конструируется где-то в заранее созданном буфере у агента-получателя). Но затем была выбрана, как мне представляется, более простая идея: мы применили к сообщениям тот же подход, который в C++ используется для динамически-созданных объектов и unique_ptr. Класс unique_ptr говорит разработчику, что есть только один "владеющий" указатель на объект. Нельзя расплодить unique_ptr, можно лишь передавать право владения так, что только один unique_ptr будет владеть динамически созданным объектом.
Мы добавили в SObjectizer такое понятие, как unique_mbox. Т.е. специальный тип mbox-а, отсылка сообщения в который приводит к передаче не константного объекта-сообщения. Для того, чтобы получить не константное сообщение из unique_mbox-а, обработчик сообщения должен использовать специальный новый тип unique_mhood_t<Msg>. Это тонкая шаблонная обертка, похожая на уже существующий mhood_t, но она позволяет модифицировать полученный объект-сообщение. Так же unique_mhood_t позволяет переслать не константное сообщение дальше.
Вот небольшой кусок реального unit-теста из SObjectizer, который проверяет работу этого механизма. Там агент отправляет самому себе сообщение типа std::string. Получив сообщение, агент меняет текст сообщения и перепосылает измененный объект. При этом проверяется то, что повторно приходит именно тот же самый std::string, но с другим содержимым.
Все самые важные фрагменты выделены жирным. Поскольку взаимодействие посредством unique-сообщениями строится исключительно в режиме 1:1, то unique_mbox создается под конкретного агента посредством метода agent_t::so_create_unique_mbox(). Только тот агент, для которого был вызван so_create_unique_mbox() сможет получать unique-сообщения, отправленные в unique_mbox.
class user_message_tester final : public so_5::agent_t { public : user_message_tester(context_t ctx) : so_5::agent_t(std::move(ctx)) , m_umbox(so_create_unique_mbox()) { so_subscribe(m_umbox).event(&user_message_tester::on_user_type_message); } virtual void so_evt_start() override { so_5::send<std::string>(m_umbox, "hello!"); } private : const so_5::unique_mbox_t m_umbox; std::string * m_received_ptr{nullptr}; void on_user_type_message(unique_mhood_t<std::string> cmd) { std::cout << "user_type_message: " << *cmd << std::endl; if("bye!" == *cmd) { ensure(cmd.get() == m_received_ptr, "cmd must have the same pointer"); so_deregister_agent_coop_normally(); } else { m_received_ptr = cmd.get(); *cmd = "bye!"; so_5::send(m_umbox, std::move(cmd)); } } }; |
Комментариев нет:
Отправить комментарий