-
Notifications
You must be signed in to change notification settings - Fork 46
v.5.7.1
This page describes changes and new features of v.5.7.1.
Since v.5.7.1 SObjectizer allows to specify default (generic) message limit that will be automatically applied for all subscribed messages for that a particular message limit wasn't set:
class demo final : public so_5::agent_t
{
public:
demo(context_t ctx)
: so_5::agent_t{ctx
+ limit_then_drop<msg_A>(100u)
+ limit_then_abort<msg_B>(10u)
// That limit will be used for all other messages.
+ limit_then_drop<any_unspecified_message>(50u)
}
{}
void so_define_agent() override
{
// Explicitly defined limit will be used.
so_subscribe_self().event([](mhood_t<msg_A> cmd) {...});
// A new limit object will be created for msg_C here.
so_subscribe_self().event([](mhood_t<msg_C> cmd) {...});
}
};
Please note that any_unspecified_message
can be used only with limit_then_drop
, limit_then_abort
and limit_then_redirect
functions. It can't be used with limit_then_transform
.
The procedure of message delivery to MPSC mboxes changed and became more similar to that for MPMC mboxes
Until v.5.7.1 there wasn't a step of checking the presence of subscription for message type during a message send to MPSC mbox. The message sent could be stored in the receiver's queue even if there wasn't a subscription for that message type. Because of that until v.5.7.1 that code works fine:
class mpsc_trick_demo final : public so_5::agent_t
{
struct msg_A final : public so_5::signal_t {};
struct msg_B final : public so_5::signal_t {};
public:
using so_5::agent_t::agent_t;
void so_define_agent() override
{
// NOTE: at least one subscription should be made.
so_subscrible_self().event(&mpsc_trick_demo::on_A);
}
void so_evt_start() override
{
// Send messages of two types.
// NOTE: there is no subscription for msg_B yet.
so_5::send<msg_A>(*this);
so_5::send<msg_B>(*this);
}
private:
void on_A(mhood_t<msg_A>)
{
std::cout << "msg_A handled" << std::endl;
// Create a new subscription now.
so_subscribe_self().event(&mpsc_trick_demo::on_B);
}
void on_B(mhood_t<msg_B>)
{
std::cout << "msg_B handled" << std::endl;
}
};
It is because in old versions of SObjectizer signal of type msg_B
was stored in the agent's queue and the presence of the subscription was checked after the extraction of msg_B
from the queue. In the example above the subscription is created after storing msg_B
in the queue but before the extraction of msg_B
from the queue.
That trick didn't work for MPMC mboxes where the presence of the subscription is checked during sending.
Since v.5.7.1 the sending procedure for MPSC mboxes changed and became more similar to sending procedure for MPMC mboxes. Now the presence of a subscription is checked during sending and a message isn't stored to receiver's queue if the receiver is not subscribed to the message at the sending time. So in v.5.7.1 the trick shown above doesn't work.
A new version of limit_then_redirect
that accepts a mbox is added:
class demo final : public so_5::agent_t
{
public:
demo(context_t ctx, const so_5::mbox_t & redirection_target)
: so_5::agent_t{ctx
+ limit_then_redirect<some_message>(10u, redirection_target)
+ ... }
{}
...
};
v.5.7.1 fixes a flaw in the implementation of on_enter/on_exit handlers for agent states: now noexcept
methods and lambdas can be used as those handlers. For example:
class demo final : public so_5::agent_t
{
state_t st_normal{this, "normal"};
...
void on_enter_st_normal() noexcept { ... }
void so_define_agent() override
{
st_normal
.on_enter(&demo::on_enter_st_normal)
.on_exit([]() noexcept {...})
;
}
...
};
In the previous versions of SO-5.6/5.7 there was a compilation error here, because noexcept
is a part of function signature in C++17, but this wasn't taken into the account during the development of v.5.6/5.7.0 after switching from C++11 to C++17.
There is a new template function make_agent_ref
that returns an instance of a reference-counted smart-pointer for an agent. That smart-pointer can be used for prolonging the lifetime of the agent. It can be necessary, for example, if a pointer to the agent has to be stored somewhere or passed to some callback.
For example, let assume that an agent initiates some asynchronous operation via Asio library and wants to pass a pointer to itself to a callback called by Asio:
class io_performer final : public so_5::agent_t
{
asio::io::tcp::socket connection_;
std::array<std::byte, 4096> input_buffer_;
void handle_incoming_data(std::size_t bytes_transferred) {...}
void on_some_event(mhood_t<some_msg> cmd)
{
// It's time to read some data.
connection_.async_read_some(asio::buffer(input_buffer_),
[self=so_5::make_agent_ref(this)]
(const asio::error_code & ec, std::size_t bytes_transferred)
{
if(!ec)
self->handle_incoming_data(bytes_transferred);
});
}
...
};
Capturing the raw value of this
in the lambda can be dangerous because if agent io_performer
will be deregistered before the completion of I/O operation, then captured this
value will become invalid.
But if a smart pointer returned by make_agent_ref
is captured, then its value will remain valid even after the deregistration of the agent.