v.5.7.3 - Stiffstream/sobjectizer GitHub Wiki

This page describes changes and new features of v.5.7.3.

Procedures for closing a mchain have been changed

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(...) {}
  ...
}

A possibility to provide own threads to SObjectizer's dispatchers is introduced

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_deactivate_agent added to agent_t class

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).

More strict control for thread-safe event-handlers

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.

⚠️ **GitHub.com Fallback** ⚠️