Skip to content

v.5.8.5

Yauheni Akhotnikau edited this page Nov 16, 2025 · 1 revision

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

More efficient implementation of so_5::state_t::time_limit

v5.8.5 introduces a new and more efficient implementation of so_5::state_t::time_limit functionality. Previous implementation was a dead simple -- each time agent enters a state with defined time_limit a new hidden mbox was created under the hood. This hidden mbox was used for a delayed message that told that time limit expired. Despite the fact that creation of a new mbox is very cheap, there was an overhead related to subscription/unsubscription for a new mbox.

The new implementation creates such a hidden mbox only once and then reused it. It means there are no more intermediate subscriptions/unsubscriptions during the change of agent state. So since v5.8.5 the overhead of time_limit functionality is lower than in previous versions.

A new method so_drop_all_subscriptions_and_filters for agents

There is the so_deactivate_agent method that drops all subscriptions and delivery_filters and switches the agent to a special state. Agent in that state is deactivated: it doesn't receive messages and only waits for deregistration of its coop.

v5.8.5 introduces a companion method -- so_drop_all_subscriptions_and_filters. As its name says it just drops all agent's subscriptions and delivery filters, but leaves agent in its current state. So this method is useful if you want to drop all existing subscriptions and subscribe to new messages and set new delivery filters.

A new agent tuning option skip_demands_on_dereg

Since v5.8.5 it's possible to tune an agent this way:

class my_agent : public so_5::agent_t
{
public:
  my_agent(context_t ctx) : so_5::agent_t{ctx + so_5::skip_demands_on_dereg} {}
  ...
};

The new so_5::skip_demands_on_dereg option tells the SObjectizer that all pending demands for the agent have to be skipped when deregistration of the agent's coop begins.

Such a tuning can be useful if there are some pending demands that we won't handle on coop's deregistration or shutdown of the SObjectizer Environment. For example, we initiate shutdown of the SObjectizer and won't wait until pending demands will be handled.

This tuning option applies regardless of the cause of coop deregistration. It could be a call to so_deregister_agent_normally within the agent's event handler, or it could be the shutdown of the entire SObjectizer environment.

empty_notificator for mchains

There was the non_empty_notificator for mchains. It's very useful in cases when an agent has to read incoming messages from a mchain: non_empty_notificator sends a signal to agent and the agent reads a message from the mchain without a risk of being blocked on the receive.

But sometimes we have to solve the opposite task: an agent has to send messages to a mchain and there is a risk to block the sender. This risk could be neglected a bit by using select with send_case and no_wait_on_empty:

// Try to send a message to chW.
// Do not wait if chW is full.
select(from_all().handle_n(1).no_wait_on_empty(),
   send_case(chW, message_holder_t<Msg>::make(...), []{...}));

Unfortunately this approach doesn't solve all problems. Let's suppose that an agent produces messages to be stored to the mchain much faster than messages are read. In such a case the agent will do select with send_case and no_wait_on_empty again and again without actually sending new messages. It's better to send N messages and then wait while mchain becomes empty and resume sending new messages until the mchain becomes full again. Since v5.8.5 it's possible via empty_notificator:

class producer final : public so_5::agent_t
{
  // This signal indicates that the mchain is empty.
  struct mchain_is_empty final : public so_5::signal_t {};

public :
  producer( context_t ctx )
    : so_5::agent_t{ ctx }
  {
    // Create the target mchain.
    m_target_mchain = so_environment().create_mchain(
        so_5::make_limited_without_waiting_mchain_params(
            // Use mchain as a buffer of fixed capacity.
            5,
            so_5::mchain_props::memory_usage_t::preallocated,
            so_5::mchain_props::overflow_reaction_t::throw_exception)
        .empty_notificator(
          [m = so_direct_mbox()]() {
            // Tell the agent that the mchain is empty now.
            so_5::send< mchain_is_empty >( m );
          } )
      );
  }
  ...
  // An event for the next attempt to send another request.
  void evt_send_next(mhood_t< send_next >)
  {
    const auto result = so_5::select(
        so_5::from_all().handle_n( 1 ).no_wait_on_empty(),
        so_5::send_case(
            m_target_mchain,
            so_5::message_holder_t< request >::make(...) ),
            [this]() {
              ... // Handling of successful sent attempt.
            } ) );

    if( !result.was_sent() )
    {
      // Message wasn't sent.
      // The sending has to be paused.
      this >>= st_sending_blocked;
    }
  }

  // Reaction to notification about emptiness of the target mbox.
  void evt_mchain_is_empty( mhood_t<mchain_is_empty> )
  {
    this >>= st_sending_enabled;
  }

The mchain_receive_result_t is now structured binging friendly

An instance of mchain_receive_result_t can now be deconstructed by structured binding:

auto [extracted, handled, status] = so_5::receive(...);
if( 0 != handled ) { ... }

A new field in so_5::stats::activity_stats_t structure

A new field m_current_activity_time has been added to so_5::stats::activity_stats_t structure.

It's optional, if it's present then it contains the total time of the current activity. But if it is nullopt then it means that there is no current activity at time when activity_stats_t was filled.

A value of the m_current_activity_time can be used to control execution time of event handlers. If this field contains reasonably big value it could mean that an event handler works too long and it could indicate a problem in the agent's logic:

class execution_time_monitor final : public so_5::agent_t
{
  ...
  void evt_activity_stats(
    const so_5::stats::messages::work_thread_activity & stats)
  {
    if(stats.m_stats.m_working_stats.m_current_activity_time.has_value()
      && *stats.m_stats.m_working_stats.m_current_activity_time > 5s)
    {
      // An event handler is working for more than 5 seconds.
      ... // Logging or other reactions.
    }
  }
};

Support for MxxRu build files will be removed soon

It seems that this release is the last one that supports our old hand made build system for C++, MxxRu. We're going to remove all MxxRu-related files in the next release and CMake will be the only way to build SObjectizer.

Clone this wiki locally