From Yate Documentation
Jump to: navigation, search

Messages are one of the main parts of Yate's architecture. We use them for the communication between modules instead of using functions. We do this mainly because if one module is changing we don't have to change all the other modules that are dependent, and also because we can debug what's happening much easier since we know which module passes which data to another module.


Messages can be dispatched or enqueued. Dispatching a message is done synchronously in the execution thread of the module that called dispatch. The execution of the module stops until answer is received. Enqueue is done asynchronously. The message is set in a queue and the execution thread continues without waiting for a response.

Handlers are message hooks that allow a certain module to receive the message. Each module installs handlers for all the messages it needs to receive.


How messages are processed

Message Handlers

Messages can be processed by message handlers. These receive messages whose names match the handler's name. They can freely modify the message's components (parameters, returned value, even the name) and finally they can stop processing the message or let it slip to the next handler.

The order in which handlers are called to attempt to process a message is established when the handlers are inserted in the dispatcher. The ordering of handlers is by their priority, a handler with a lower numerical priority is inserted earlier in list and receives the message before a handler with a higher numerical priority.

There is NO GUARANTEE about the order between handlers of equal priority although currently the following rules apply:

  • The order this does not change between messages.
  • To avoid uncertainity and preserve order if a handler is removed and reinserted handlers of equal priority are sorted by their address.

Message Queue

When Engine::enqueue() method is called, the message is added in YATE's main message queue. The MessageQueues class provides a mechanism for organizing YATE's messages in different queues, identified by message name and some specified filters. So when Engine::enqueue() method is called YATE will check if the message can be appended in a specific message queue, if not, it will append it in the main queue.

This behavior allows us to shrink the load on YATE's main message queue and to provide oriented message processing in YATE.

The messages from a MessageQueue are processed in its own threads, so when new instance is created the number of worker threads needs to be specified.

A message queue can be identified by the message name that it serves and a list of filters. Multiple message names can not be specified for a queue, but multiple queues can be specified for the same message name with different filters. The filters represent a name value pair. A message is added to a queue only if the message name equals the queue name and the message has identical fields in his body as the message queue filters.

The method 'received' can be re-implemented, so that a direct processing can be made for the message. Re-implementing 'received' method can sometimes be risky, because the messages will not be processed by the installed MessageHandlers; so if you are sure that the messages from the queue should only be processed by you than that's the reason the MessageQueue was implemented for. But if you want the message to be processed by installed engine's handlers, call Engine::dispatch() method after the message pre-processing is done.

A JavaScript API exists for MessageQueue and it can be found here.

Components of a message

A message is a holder for several components:

  • name - identifies the message type and allows matching it against the possible handlers
  • return value - a string that is used as returned value by the emitter of the message
  • time - the time the message was created. This is important if the messages are queued for an indefinite amount of time
  • parameters - an arbitrary number (including zero) of string values, each having a name. Each handler can take action based on specific parameters and/or modify them. Unknown parameters must be ignored.

All the messages are binary inside of Yate. However you can use Rmanager to get them out in a human readable form.

The messages are passed using memory sharing. In this way we have a pretty optimized system. Other options like pipes or sockets have been considered, but failed to give us the flexibility and speed needed. When passed to external modules the messages are converted to an escaped string encoding that allows handling any special characters that may be present in the message's components.

Example of call.route message

A very good example about how the message system works is the "call.route" message.

Generate the message

When a call comes in at some point, a messages is generated like this:

  Message *m = new Message("call.route"); 
 if (e->ies.calling_name) 
 if (e->ies.called_number) 

Send the message to engine

Then we send the message to the Engine and we find out if some module did actually accept to handle it. We must also destruct the message.

  if (Engine::dispatch(m))
    Output("Routing returned: %s",m->retValue().c_str());
    Output("Nobody routed the call!");

The Engine is getting the above message and is sending it to all the modules that have registered a "route" handler.

Fire-and-forget messages

It is also possible to fire-and-forget messages. They are stored in a queue in the engine and dispatched later in threads that belong to the engine. Once such a message is dispatched it is destroyed by the engine.

  Message *m = new Message("alert");
  m->addParam("reason","Hardware malfunction");

Handle a route request

To handle a route request (probably in another module) we first have to declare a class RouteHandler derived from MessageHandler.

class RouteHandler : public MessageHandler
    RouteHandler(int prio)
        : MessageHandler("call.route",prio) { }
    virtual bool received(Message &msg);

Then, because the received method is a pure virtual in the MessageHandler class, we must override it.

bool RouteHandler::received(Message &msg)
   const char *driver = msg.getValue("driver")
   Debug(DebugInfo,"Route for driver %s",driver);
   // don't actually route anything, let message continue
   return false;

Install the handler

And finally, in the plugin initialized method we can install the handler.

 m_route = new RouteHandler(priority);

To get more references about messages read the Message class documentation at API::Message.

Example of the messages flow in Yate

We have a Yate SIP Server with some SIP users configured in regfile.conf.

See the figure below that describes the messages flow from the point when an incomming call arrives in Yate, until the call is answered and then hangup:

Yate SIP messages flow.png

See also

Personal tools