v.5.7.0 - Stiffstream/sobjectizer GitHub Wiki
This page describes changes and new features of v.5.7.0.
There is a set of significant changes for the select()
function in v.5.7.0 and those changes break compatibility with the previous versions of SObjectizer-5. Switching to v.5.7.0 will require rewriting code where select()
is used.
Helper function case_()
that describes receive-case for select()
was removed. The similar function but with new name receive_case()
is introduced in v.5.7.0. Because of that fragments like that:
using namespace so_5;
select(from_all().handle_n(1),
case_(ch1, ...),
case_(ch2, ...),
case_(ch3, ...));
should be rewritten that way:
using namespace so_5;
select(from_all().handle_n(1),
receive_case(ch1, ...),
receive_case(ch2, ...),
receive_case(ch3, ...));
Since v.5.7.0 SObjectizer's select()
supports sending of messages. For example:
using namespace so_5;
select(from_all().handle_n(1),
send_case(ch1, message_holder_t<outgoing_msg>::make(...), []{...}),
receive_case(ch2, [](mhood_t<incoming_msg> cmd) {...}));
This call to select()
performs sending a message of type outgoing_msg
into ch1
or receiving a message of type incoming_msg
from ch2
.
SObjectizer's select()
can handle sending to several chains:
using namespace so_5;
select(from_all().handle_n(3),
send_case(ch1, message_holder_t<first_message>::make(...), []{...}),
send_case(ch2, message_holder_t<second_message>::make(...), []{...}),
send_case(ch3, message_holder_t<third_message>::make(...), []{...}));
This call to select()
returns only when all three messages are sent to the corresponding chains (or if some chain is closed).
Note that modificators like empty_timeout()
, no_wait_on_empty()
and total_time()
are supported for select()
with send_case()
:
using namespace so_5;
// Return immediately if all chains are full.
select(from_all().handle_n(3).no_wait_on_empty(),
send_case(ch1, message_holder_t<first_message>::make(...), []{...}),
send_case(ch2, message_holder_t<second_message>::make(...), []{...}),
send_case(ch3, message_holder_t<third_message>::make(...), []{...}));
// Wait no more than 3s.
select(from_all().handle_n(3).total_time(3s),
send_case(ch1, message_holder_t<first_message>::make(...), []{...}),
send_case(ch2, message_holder_t<second_message>::make(...), []{...}),
send_case(ch3, message_holder_t<third_message>::make(...), []{...}));
Usage of no_wait_on_empty()
or empty_timeout()
for send-cases may look contradictory but those names are kept for compatibility reasons.
In the previous versions of SObjectizer select()
returned a value of type mchain_receive_result_t
. Since v.5.7.0 select()
returns an instance of a new type mchain_select_result_t
. This new type has the following public interface:
class mchain_select_result_t {
public :
mchain_select_result_t() noexcept;
mchain_select_result_t(
std::size_t extracted,
std::size_t handled,
std::size_t sent,
std::size_t closed ) noexcept;
[[nodiscard]] std::size_t extracted() const noexcept;
[[nodiscard]] std::size_t handled() const noexcept;
[[nodiscard]] std::size_t sent() const noexcept;
[[nodiscard]] std::size_t closed() const noexcept;
[[nodiscard]] bool was_extracted() const noexcept;
[[nodiscard]] bool was_handled() const noexcept;
[[nodiscard]] bool was_sent() const noexcept;
[[nodiscard]] bool was_closed() const noexcept;
[[nodiscard]] bool was_sent_or_received() const noexcept;
};
It means that fragments like that:
using namespace so_5;
mchain_receive_result_t select_result;
do {
select_result = select(...);
...
}
while(0 != select_result.handled());
should be rewritten that way:
using namespace so_5;
mchain_select_result_t select_result;
do {
select_result = select(...);
...
}
while(0 != select_result.handled()); // or while(select_result.was_handled());
A flaw was found in previous versions of SObjectizer-5 when enveloped messages are sent to an agent that uses transfer_to_state()
and suppress()
helpers. A message can be ignored due to the absence of an event handler in the current agent state (or because the handling of the message in the current state is disabled by suppress()
), but the message envelope is informed that the message is delivered to the subscriber.
For example:
class demo_agent final : public so_5::agent_t {
state_t st_first{ this, "first" };
state_t st_second{ this, "second" };
...
public:
...
void so_define_agent() override {
// NOTE: there is no event handler for 'Status' message in st_first.
st_first.event([](mhood_t<Hello>) {...})
.event([](mhood_t<Bye>) {...});
st_second.event([](mhood_t<Hello>) {...})
// Delegate processing of Status and Bye to st_first.
.transfer_to_state<Status>(st_first)
.transfer_to_state<Bye>(st_first);
}
...
};
If an agent is in st_second state and receives Bye message then it changes its state to st_first and handles Bye in st_first state. But if an agent in st_second state and receives Status message then it changes its state to st_first, but ignores Status message because there is no event-handler for Status in st_first.
In the previous versions of SObjectizer if Status message is sent as an enveloped message then the payload of the envelope was extracted during the processing of transfer_to_state()
action. And the envelope was informed that the message was delivered to the subscriber even if the message was actually ignored.
Since v.5.7.0 that flaw is fixed. Now the payload of an enveloped message is not extracted during the processing of transfer_to_state()
and suppress()
actions. So if a message is ignored as in the example above then the message envelope won't be informed about the delivery of the message.
To fix the behavior of enveloped messages in the case of transfer_to_state() and suppress() (see above) a new enumeration so_5::event_handler_kind_t was introduced. This enum tells SObjectizer Run-Time about the event-handler: is an event-handler real handler that processes incoming messages, or it is just a transition point that delegates the actual processing to some other handler (or handlers).
The format of so_5::agent_t::so_create_event_subscription()
method was also changed: now that method receives an additional parameter of type event_handler_kind_t
. This should be taken into the account if so_create_event_subscription()
is directly used in your code.