|
SObjectizer
5.7
|
There is a new major change introduced in v.5.3.0 – a possibility to synchronous wait for result of message processing. An agent, called service client, could ask another agent (called service_handler) to do something by asynchronously sending message to service_handler and then synchronously waiting for result of that request.
All parameters for service request must be defined as ordinary message or signal type:
Service_handler is an ordinary agent which defines its service_request handling methods by traditional way. The only change with previous version is that event handlers could return values since v.5.3.0:
Event handler methods are subscribed as usual:
All those mean that there is almost no differences for service_handler: it always works as ordinary agent. And all event handlers for that agent are invoked on agent’s working thread.
All requests are delivered to service_handler’s agent as ordinary messages via agent’s event queue. The mechanism for event handler invocation for service requests is the same as for traditional asynchronous messages.
To send service request it is necessary to invoke mbox’s get_one template method. It means that exactly one request of the specified type will be requested:
Then it is necessary to declare how the waiting of service request will be performed. There are the three variants.
The first one, the most simple and the most dangerous, is wait service request result infinitely long:
The second one, simple and one of the most safe, is wait service request result for the specified amount of time:
The third one, the most complex and the most flexible, is receive std::future object associated with service request and then perform any operation user wish:
There is no any help to user from SObjectizer to avoid or to diagnose deadlocks when synchronous interactions are used. It is the user task to guarantee that service_handler and service client are working on different threads and there is no any complex internal interactions like: agent A1 calls A2 which calls A3 which calls A1.
It is the reason why the wait_forever() in a dangerous form. If there is a deadlock the application will hang forever.
The safest way – is to use wait_for() with the reasonable timeout. Even if a deadlock is here it will be automatically destroyed after timeout expiration.
There is a strong possibility of exceptions appearance during synchronous interactions.
Some exceptions could be raised by SObjectizer. Those are:
In such cases SObjectizer raises object of so_5::exception_t class with the appropriate error_code inside.
The exceptions could be raised by event handlers itself. As in the sample of evt_convert event above. Those exceptions are intercepted by SObjectizer and then transferred to the std::future connected with the service request. It means that those exceptions will be reraised during std::future::get() invocation (as in the case of direct call by used and in the case of calling get() inside of wait_forever() and wait_for()). So the application exception which are went out from event handler will be propagated to service client.
The dialing with non-handled application exception is the main difference between ordinary event-handling and service_request-handling. When agent lets the exception go out during ordinary event handler invocation then SObjectizer calls agent’s so_exception_reaction() method and does appropriate actions. But when agent lets the exception go out during service_request handling then the exception is propagated to service client and so_exception_reaction() is not called.
If the compiler supports variadic templates then make_async and make_sync_get methods could be used instead of async and sync_get. It allows to write:
instead of
There could be cases where service_handler returns void. For example:
That service could be called by ordinary get_one method chain:
But instead of get_one<void> it is possible to use run_one method that is a synonym for get_one<void>:
Or:
Method chains get_one(), get_one().wait_forever() and get_one().wait_for() produces temporary proxy objects which could be captured as local variable by auto keyword. It would lead to shorter form of service calling:
1.8.14