|
SObjectizer
5.5
|
The main purpose of message chain (or just mchain) mechanism is providing a way for interacting between SObjectizer- and non-SObjectizer-part of an application. The interaction in opposite direction is very simple: usual message passing via mboxes is used. For example:
But how to receive some messages from an agent back to non-SObjectizer-part of the application?
Message chain is the answer.
Message chain looks almost like mbox for agents. An agent can send messages to mchain exactly the same way as for mbox. So mchain can be passed to agent and agent will use it as destination for reply messages. On the other side of a mchain will be non-SObjectizer message handler. This handler will receive messages from the mchain by using special API functions and handle them appropriately.
There are two types of mchains:
Type of mchain is specified at the creation time. Once created the type of mchain cannot be changed.
Type of mchain is specified by content of so_5::mchain_params_t instance passed to so_5::environment_t::create_mchain method. There are three helper functions which return properly initialized mchain_params_t instances:
The type so_5::mchain_t is like the type so_5::mbox_t – it is an alias for smart intrusive pointer to so_5::abstract_message_chain_t. It means that mchain created by create_mchain will be destroyed automatically after destruction of the last mchain_t object pointed to it.
Size-limited mchains have a serious difference from size-unlimited mchains: a size-limited mchain can't contain more message than the max capacity of the mchain. So there should be some reaction on attempt to add another message to full mchain.
There could be size-limited mchains which will perform waiting for some time on attempt of pushing new message to full mchain. If there is a free place in the mchain after that waiting then new message will be stored into the mchain. An appropriate overload reaction will be performed otherwise.
There also could be size-limited mchains without any waiting on full mchain. If there is no free room in the mchain then an appropriate overload reaction will be performed immediately.
There are four overload reactions which can be selected for an mchain at the moment of mchain creation:
so_5::exception_t with error code so_5::rc_msg_chain_overflow will be raised as result of attempt of pushing new message to full mchain.std::abort().All those variants are described as items of enumeration so_5::mchain_props::overflow_reaction_t.
There is yet another important property which must be specified for size-limited mchain at the creation time: the type of memory usage.
Memory for storing messages inside mchain can be used dynamically: it would be allocated when mchain grows and deallocated when mchain shrinks.
Or memory for mchain could be preallocated and there will be fixed-size buffer for messages. Size of that buffer will not change during growth and shrinking of the mchain.
Types of memory usage are described as items of enumeration so_5::mchain_props::memory_usage_t.
All those traits of size-limited mchains are controlled by mchain_params_t object passed to create_mchain method. The simplest and recommended way of preparing the corresponding mchain_params_t is usage of make_limited_without_waiting_mchain_params and make_limited_with_waiting_mchain_params helper functions:
There are two variants of so_5::receive function which allows to receive and handle messages from a mchain.
The first variant of receive takes a mchain_t, a timeout and a list of message handlers:
It checks the mchain and wait for no more than 500ms if the mchain is empty. If the mchain is not empty it extracts just one message from the mchain and tries to find an appropriate handler for it. If handler is found it is called. If a handler is not found then the message extracted will be thrown out without any processing.
If the mchain is empty even after the specified timeout then receive will do nothing.
There are two special values which can be used as timeout:
so_5::no_wait specifies zero timeout. It means that receive will not wait if the mchain is empty;so_5::infinite_wait specifies unlimited waiting. The return from receive will be on arrival of any message. Or if the mchain is closed explicitly.There is also more advanced version of so_5::receive which can receive and handle more than one message from mchain. It receives a so_5::mchain_receive_params_t objects with list of conditions and returns control if any of those conditions becomes true. For example:
There is a difference between a number of extracted messages and a number of handled messages. A message is extracted from a mchain and count of extracted messages increases. But the count of handled messages is increased only if a handler for that message type if found and called. It means that the number of extracted messages can be greater than number of handled messages.
Both receive return an object of so_5::mchain_receive_result_t type. Methods of that object allow to get number of extracted and handled messages, and also the status of receive operation.
The usage of advanced version of receive could look like:
Message handlers which are passed to receive functions family must be lambda-functions or functional objects with format:
For example:
A handler for signal of type signal_type can be created by the help of so_5::handler<signal_type>() function:
NOTE! All message handlers must handle different message types. It is an error if some handlers are defined for the same message type.
All traditional send-functions like so_5::send, so_5::send_delayed and so_5::send_periodic work with mchains the same way they work with mboxes. It allows to write code the traditional way:
The functions for performing service request, so_5::request_value and so_5::request_future, work with mchain too. It means that an agent can make a service request to non-SObjectizer part of an application and receive the result of that request as usual:
There is a method so_5::abstract_message_chain_t::as_mbox() which can represent mchain as almost ordinary mbox. This method returns so_5::mbox_t and this mbox can be used for sending messages and performing service request to the mchain.
Method as_mbox() can be useful if there is a necessity to hide the fact of mchain existence. For example, an agent inside SObjectizer part of an application can receive mbox and think that there is another agent on the opposite side:
The only difference between ordinary mbox created by so_5::environment_t::create_mbox() and mbox created by as_mbox() is impossibility of subscriptions creation. It means that agent worker from the example above can't subscribe its event handlers to messages from m_request_mbox.
The only case when mchain can be passed to SObjectizer part directly as mchain (without casting it to mbox by as_mbox()) is a necessity of explicit closing of mchain in the SObjectizer part of an application:
There is a possibility to specify a function which will be called automatically when a message in stored into the empty mchain:
This feature can be used in GUI applications for example. Some widget can create mchain and needs to know when there are messages in that mchain. Then widget can add notificator to the mchain and send some GUI-message/signal to itself from that notificator. Some of receive functions will be called in processing of that GUI-message/signal.
The mchain mechanism introduced in v.5.5.13 uses MPSC queue inside. It means that receive on a mchain must be called from one thread.
There will be no any damage if several receive will be called for one mchain from different threads at the same time (no messages will be lost or processed several times). But there could be not efficient work of that threads (like threads starvation).
1.8.14