Menu

SObjectizer v.5.3.0 TODOs

Yauheni Akhotnikau Nicolai Grodzitski

Usage simplification

A simple way to create service invocation proxy

DON'T KNOW HOW TO DO THAT!

It is necessary something like this:

so_5::rt::service_proxy_t< int(const msg_convert &) > proxy =
  mbox->get_one< int >().wait_forever();
proxy.make_sync_get( "1234" );

Instead of:

so_5::rt::infinite_wait_service_invoke_proxy_t< int > proxy =
  mbox->get_one< int >().wait_forever();
proxy.make_sync_get< msg_convert >( "1234" );

And that service_proxy_t should accept both wait_forever()-created and wait_for()-created proxies.


List of implemented features

Ad-hoc agents

  • documentation in Doxygen format;
  • documentation in SourceForge wiki;
  • review of samples for rewriting appropriate samples via ad-hoc agents;

Support for mutable lambda functions

This kind of lambda does not tested:

[]() mutable { ... }

Synchronous services

  • doxygen docs in source code comments;
  • support for svc_handler with void as return type;
  • useful overloads for service_invoke_proxy_t::request/sync_request (for support raw pointers, unique_ptr, message_ref and so on);
  • compile-time checking for signal_t/message_t (as for message subscription);
  • unit tests for svc_handlers;
  • useful overloads for service_invoke_proxy_t::request/sync_request in style of std::make_shared or std::vector::emplace_back (based on variadic templates and std::forward) NOTE: requires compiler with variadic templates support (e.g. GCC 4.8, MSVC++12.0 (but ACE 6.2.6 has no project files for MSVC++12.0));
  • overloads for service_invoke_proxy_t::sync_request with timeout parameter;
  • may be it is a good idea to allow event-handlers with non-void return type? E.g. std::string evt_handler(...) instead of void evt_handler(...). It could allow to use a method as event-handler and service-handler at the same time;
  • check for the case where requested service prototype doesn't match actual service prototype (for example actual is std::string(my_data_t) but expected is void(my_data_t)). There should be a run-time exception with bad_cast or something like this because of dynamic_cast failure;
  • syntax for service invocation should be changed (see bellow);
  • a stress test for perfomance and resource leaks checking necessary -- this is sample/so_5/svc/parallel_sum;
  • sample with exception handling demonstration;
  • may be it is better to store mbox_ref_t in service_invoke_proxy_t instead of const mbox_t&? The safest way is to store mbox_ref_t. But there some benchmarking should be done. Or there may be another idea: a template class svc_mbox_t<RESULT>, which stores mbox_ref_t inside;
  • run code, tests and samples under MSVC 12.0;
  • docs in Wiki;

A new syntax for service invocation:

// Ansyc service call. A std::future is returned.
mbox.get_one<ret_type>().async(msg);
mbox.get_one<ret_type>().make_async<msg_type>(msg_args);

// Sync service call with infinite wail.
// A value is returned.
mbox.get_one<ret_type>().wait_forever().sync_get(msg);
mbox.get_one<ret_type>().wait_forever().make_sync_get<msg_type>(msg_args);

// Sync service call with time-limited wail.
// A value is returned or exception is thrown.
mbox.get_one<ret_type>().wait_for(time).sync_get(msg);
mbox.get_one<ret_type>().wait_for(time).make_sync_get<msg_type>(msg_args);

// This could be used instead of get_one<void>:
mbox.run_one()./* some_action_here... */

Ability to subscribe event handler for several states

Like this:

so_subscribe( mbox ).in( st_1 ).in( st_2 ).in( st_3 )
  .event( &my_agent_t::evt_handler );

instead of the current form:

so_subscribe( mbox ).in( st_1 ).event( &my_agent_t::evt_handler );
so_subscribe( mbox ).in( st_2 ).event( &my_agent_t::evt_handler )
so_subscribe( mbox ).in( st_3 ).event( &my_agent_t::evt_handler );

Declaration of event handlers without use of so_5::rt::event_data_t

May be it is better to write:

virtual void
so_define_agent()
  {
    so_subscribe( mbox ).event( &my_agent_t::evt_do_something );
    so_subscribe( mbox ).event(
      so_5::signal< msg_query_status >,
      &my_agent_t::evt_query_status );
  }
void
evt_do_something( const msg_parameters & ) { ... }

std::string
evt_query_status() { ... }

Instead of the current form with so_5::rt::event_data_t.

Usage of lambda-function as event handlers

For example:

virtual void
so_define_agent()
  {
    so_subscribe(mbox).event(
      so_5::signal< msg_query_status >,
      []() -> std::string
        {
          return m_current_status;
        } );

    so_subscribe(mbox).event(
      [](const msg_convert & msg) -> int { return std::atoi(msg.m_value); } );
  }

Default exception_reaction for cooperation and whole so_environment

SEE [so-5.3.0 Exception reaction inheritance] FOR THE DESCRIPTION

For details about unhandled exception reaction please see [so-5.2.3 Reaction to unhandled exceptions].

There are cases where unhandled exception reaction should be specified not on agent's level, but on cooperation level or event on whole so_environment level. For example in a simple program any exception should lead to program termination. In such case is hard or even impossible to redefine so_exception_reaction for every agent in the program.

The solution could be as follows:

  • new return value for so_exception_reaction() -> so_5::rt::inherit_exception_reaction. It means that exception reaction for that agent should be inherited from its cooperation;
  • so_exception_reaction() should return so_5::rt::inherit_exception_reaction by default;
  • new methods agent_coop_t::exception_reaction() and agent_coop_t::set_exception_reaction() should be introduced;
  • method agent_coop_t::exception_reaction() should return so_5::rt::inherit_exception_reaction by default;
  • new methods so_environment_t::exception_reaction() and so_environment_t::set_exception_reaction() should be introduced;
  • so_environment_t::exception_reaction() should return so_5::rt::abort_on_exception by default;
  • if agent throws an exception and its so_exception_reaction() returns inherit_exception_reaction then so_exception_reaction() from agent's cooperation should be called. If it returns inherit_exception_reaction too then so_exception_reaction() for so_environment should be called.

NOTE. May be it is necessary to call so_exception_reaction() for parent cooperation and so on.

run_so_environment with lambda as argument

SEE [so-5.3.0 New run_so_environment variants] FOR THE DETAILS

There should be a variant of run_so_environment which receives lambda-function or std::function object:

so_5::api::run_so_environment(
  [](so_5::rt::so_environment_t & env) {
    env->register_agent_as_coop( "test", new a_test_t( env ) );
  } );

Support for std::chrono::duration as time units for schedule_timer/single_timer

There are two variants of schedule_timer/single_timer methods: one for message objects and second for the signals. It is possible to create schedule_timer/single_timer overloads for std::chrono::duration<Rep,Period>. It would lead to templates like that:

template< class M, class R1, class P1, class R2, class P2 >
so_5::timer_thread::timer_id_ref_t
schedule_timer(
  std::unique_ptr< M > message,
  const so_5::rt::mbox_ref_t & mbox,
  const std::chrono::duration< R1, P1 > & delay,
  const std::chrono::duration< R2, P2 > & period ) { ... }

But for the case where M -- is a type of signal that overload:

template< class M, class R1, class P1, class R2, class P2 >
so_5::timer_thread::timer_id_ref_t
schedule_timer(
  const so_5::rt::mbox_ref_t & mbox,
  const std::chrono::duration< R1, P1 > & delay,
  const std::chrono::duration< R2, P2 > & period ) { ... }

Would lead to practical impossibility to use such schedule_timer method because of necessity to write something like this:

so_environment().schedule_timer< my_signal, int, std::milli, int, std::milli >( mbox,
  std::chrono::millisecond(500),
  std::chrono::millisecond(250) );

So instead of creating various schedule_timer/single_timer overloads a simple std::chrono::duration to milliseconds helper function is added: so_5::chrono_helpers::to_ms. It allows to write:

so_environment().schedule_timer< my_signal >( mbox,
  so_5::chrono_helpers::to_ms( std::chrono::minutes(3) ),
  so_5::chrono_helpers::to_ms( std::chrono::second(45) ) );

List of declined features

Execution speed up

THERE IS NO SPEED UP WHEN RAW POINTERS ARE USED

Some time are spent in event-handler invocation routine on the following fragment:

const event_data_t< MESSAGE > event_data(
  dynamic_cast< MESSAGE * >( message_ref.get() ) );

But if store raw-pointer to message in execution demand together with message_ref it would allow to call event handler without this casting. It is safe because delivery_message() methods receive pointers which are already of type MESSAGE*. Dynamic casting is used because message_ref stores pointer to so_5::rt::message_t and there is no original MESSAGE raw-pointer value.


Related

Wiki: Internals
Wiki: so-5.2.3 Reaction to unhandled exceptions
Wiki: so-5.3.0 Exception reaction inheritance
Wiki: so-5.3.0 New run_so_environment variants