v.5.7.3 - Stiffstream/sobjectizer GitHub Wiki
This page describes changes and new features of v.5.7.3.
Method abstract_message_chain_t::close(close_mode)
is deprecated in v.5.7.3. It will be removed in some next major branch (5.8 probably). A new method that accepts two parameters is introduced. Now, a mchain has to be closed one of the following ways:
auto ch1 = create_mchain(env);
auto ch2 = create_mchain(env);
...
// Close mchain and enable exceptions from 'close'.
ch1->close(so_5::exceptions_enabled, so_5::mchain_props::close_mode_t::retain_content);
// Close mchain with termination if 'close' throws.
ch2->close(so_5::terminate_if_throws, so_5::mchain_props::close_mode_t::retain_content);
The same changes were performed with helper functions like close_drop_content
and close_retain_content
: old versions are deprecated and new ones have been introduced:
auto ch1 = create_mchain(env);
auto ch2 = create_mchain(env);
...
// Close mchain and enable exceptions from 'close'.
so_5::close_retain_content(so_5::exceptions_enabled, ch1);
// Close mchain with termination if 'close' throws.
so_5::close_drop_content(so_5::terminate_if_throws, ch2);
The motivation behind this change is simple. The experience shows that close
is often called in contexts like destructors and catch-blocks. Usually, we don't expect exceptions here, but mchain's close can throw, it's a throwing method. So it's good to specify our expectations explicitly. It means that:
some_class::~some_class()
{
comm_ch_.close(so_5::terminate_if_throws, so_5::mchain_props::close_mode_t::drop_content);
}
tells about exception safety much more than:
some_class::~some_class()
{
comm_ch_.close(so_5::mchain_props::close_mode_t::drop_content);
}
The old deprecated calls will stay here in 5.7 forever, they will be removed only in 5.8 somewhere in the future (we don't have any plans for 5.8 at the moment). So it isn't necessary to change old calls to new ones, but you'll receive warnings from the compiler. To avoid those warnings you have to replace old calls of close()
/close_drop_content()
/close_retain_content()
to new ones. For example, instead of:
close_retain_content(chain);
you have to write:
close_retain_content(so_5::exceptions_enabled, chain);
for noexcept-contexts. For noexcept-contexts (like destructors) you have to write:
some_class::~some_class()
{
...
close_retain_content(so_5::terminate_if_throws, chain);
...
}
or
some_class::~some_class()
{
...
try{ close_retain_content(so_5::exceptions_enabled, chain); } catch(...) {}
...
}
Version 5.7.3 introduces two new interfaces so_5::disp::abstract_work_thread_t
and so_5::disp::abstract_work_thread_factory_t
that allows to introduce own threads to SObjectizer.
Before v.5.7.3 SObjectizer used std::thread
for making worker threads for dispatchers. There wasn't a way to use custom worker thread with standard SObjectizer dispatchers. Since v.5.7.3 it can be done that way:
- a user writes own implementation of worker thread via inheriting from
so_5::disp::abstract_work_thread_t
and implementing its pure virtual methods; - a user writes own thread factory via inheriting from
so_5::disp::abstract_work_thread_factory_t
and implementing its pure virtual methods; - a user creates an instance of his/her own thread factory and specifies it to the params of a particular dispatcher or to the params of the whole SObjectizer Environment;
- SObjectizer will use the specified factory for making new instances of worker threads.
See Custom Worker Threads for more details.
New method so_5::agent_t::so_deactivate_agent
deactivates the agent:
- drops all agent's subscriptions (including deadletter handlers) and delivery filters;
- switches the agent to a special state in that the agent does nothing and just waits for the deregistration.
Being in a deactivated state an agent can't make new subscriptions and change its state.
Deactivation is necessary sometimes if an agent falls in some failed state, can't continue its normal work, but can't be deregistered immediately because it's a part of bigger cooperation. In that case, the agent can be switched to a special state in that it will wait for the deregistration:
class some_agent final : public so_5::agent_t
{
state_t st_working{ this, "working" };
...
void switch_to_failed_state()
{
// Notify some supervisor about the failure.
// It will deregister the whole cooperation with failed agent.
so_5::send<msg_failure>( supervisor_mbox(), ... );
// Deactivate the agent.
so_deactivate_agent();
}
...
void so_define_agent() override
{
this >>= st_working;
...
}
void evt_some_event(mhood_t<some_msg> cmd)
{
try
{
do_some_processing_of(*cmd);
}
catch(...)
{
// Processing failed, agent can't continue work normally.
// Have to switch it to the failed state and wait for
// the deregistration.
switch_to_failed_state();
}
}
...
};
Please note that the deactivated agent stays here until its coop is deregistered. For example, if the deactivated agent was bound to active_obj
dispatcher then the dispatcher will hold a dedicated thread for that agent until the coop is gone. Only then the worker thread for that agent will be released.
The deactivation means that the agent is still here, but does nothing. Because it is still here it will hold some resources allocated for it (like worker thread in active_obj
dispatcher).
Moreover, so_evt_finish
will be called for deactivated agent the usual way. It can be helpful if so_evt_start
and so_evt_finish
are used for acquiring/releasing some external resources (like files, shared memory regions, database connections, and so on). The so_evt_finish
for deactivated agent will be called during coop deregistration procedure (not at the moment of agent deactivation).
There is a possibility to subscribe a thread-safe event-handler. Such event-handler will be treated by adv_thread_pool dispatcher in a special way: several thread-safe handlers can be run at the same time on different threads. But thread-safe event-handlers have limitations: they can't change agent's state (can't switch agent from one state to another, can't create or drop subscriptions, and so on).
Those limitations were controlled only when an agent was bound to adv_thread_pool dispatcher only. But if a thread-safe handler was invoked by a dispatcher of some other type, then there weren't such checks.
Since v.5.7.3 actions performed in thread-safe handler are checked regardless of the dispatcher type.