SObjectizer  5.7
so-5.4.0: thread_safety flag for event handlers

By default in SObjectizer all event handlers are assumed to be not thread safe. Standard dispatchers guarantee that not_thread_safe event handlers will work alone and only on the context of one working thread.

A new feature in v.5.4.0 allows to specify that event handler is a thread safe handler. It means that event handler doesn't change state of agent (or does it in thread safe manner). If user specifies a event handler as thread_safe handler it gives to SObjectizer's dispatcher a right of running thread-safe handlers of the same agent in parallel on the different working threads. But with the following conditions:

a not_thread_safe event handler can't be started until there is any other running event handler of the agent. A start of not_thread_safe event handler will be delayed until all already running event handlers finish their work; no one event handler can't be started until there is a working not_thread_safe event handler; no one thread_safe event handler can't be started until there is a working not_thread_safe event handler.

To specify thread_safe event handler it is necessary to pass additional argument to event() method in subscription chain:

class my_agent : public so_5::rt::agent_t
{
public :
void so_define_agent() override
{
so_subscribe( so_direct_mbox() ).in( st_disconnected )
// thread_safety flag is not specified.
// This is not_thread_safe event handler and it
// can't work in parallel with other event handlers
// because it changes agent's state.
.event( so_5::signal< msg_reconnect >, &my_agent::evt_reconnect )
// This is thread_safe agent and it doesn't change agent's state.
.event(
&my_agent::evt_send_when_disconnected,
// This is thread_safe agent and it doesn't change agent's state.
.event( so_5::signal< msg_get_status >,
[]() -> std::string { return "disconnected"; },
...
}
...
}

As a example of thread_safe and not_thread_safe event handler lets see an agent for performing cryptography operations: encrypting and decrypting. This operations are stateless and can be done in parallel. But the agent must be reconfigured from time to time. The reconfiguration operation is stateful and event handler for reconfigure message is not_thread_safe.

User must only specify the thread_safety flag for those event handlers. All other work will be done by SObjectizer. It means that evt_encrypt and evt_decrypt will be running in parallel until an occurrence of msg_reconfigure. SObjectizer will wait until all previously started evt_encrypt/evt_decrypt finish their work and only then will start evt_reconfigure. While evt_reconfigure is working all other event handlers will be delayed and will be started only after return from evt_reconfigure:

class a_cryptographer_t : public so_5::rt::agent_t
{
public :
virtual void
so_define_agent() override
{
// not_thread_safe event handler.
so_subscribe( so_direct_mbox() )
.event( &a_cryptographer_t::evt_reconfigure );
// thread_safe event handlers.
so_subscribe( so_direct_mbox() )
.event( &a_cryptographer_t::evt_encrypt, so_5::thread_safe )
.event( &a_cryptographer_t::evt_decrypt, so_5::thread_safe );
}
void
evt_reconfigure( const msg_reconfigure & evt ) { ... }
encrypt_result_t
evt_encrypt( const msg_encrypt & evt ) { ... }
decrypt_result_t
evt_decrypt( const msg_decrypt & evt ) { ... }
};

Because event handlers can be either ordinary event handlers or synchronous service requests handlers (it depends not on handler but on message sender or service caller) it means that thread_safe event handler can be used for parallel processing of several parallel service requests.