Figure 8-1 Notice Routing
The sending process creates a message, fills in attribute values, and sends it. The ToolTalk service matches message and pattern attribute values, then gives a copy of the message to one handler and to all matching observers. File-scoped messages are automatically transferred across session boundaries to processes that have declared interest in the file.
Figure 8-2 Request Routing
The ToolTalk service delivers a request to only one handler. The handler adds results to the message and sends it back. Other processes can observe a request before or after it is handled, or at both times; observers absorb a request without sending it back.
ToolTalk messages are simple structures that contain attributes for address, subject (such as operation and arguments), and delivery information (such as class and scope.) Each message contains attributes fromTable 8-1.
Table 8-1 ToolTalk Message Attributes
Table 8-2 Scoping a Message with Patterns to a File
To scope to the union of TT_FILE_IN_SESSION and TT_SESSION, add both scopes to the same pattern, as shown in Code Example 8-1.
Code Example 8-1 Scoping to Union of TT_FILE_IN_SESSION and TT_SESSION
tt_open();
Tt_pattern pat = tt_create_pattern();
tt_pattern_scope_add(pat, TT_FILE_IN_SESSION);
tt_pattern_scope_add(pat, TT_SESSION);
tt_pattern_file_add(pat, file);
tt_pattern_session_add(pat, tt_default_session());
tt_pattern_register(pat);
Table 8-3 Scoping Mechanisms for Messages
When a message is scoped to TT_FILE or TT_BOTH, the ToolTalk client library checks the database server for all sessions that have clients that are interested in the file and sends the message to all of the interested ToolTalk sessions. The ToolTalk sessions then match the messages to the appropriate clients. The message sender is not required to explicitly call to tt_file_join.
If a message that is scoped to TT_FILE_IN_SESSION or TT_SESSION contains a file, the database server is not contacted and the message is sent only to clients that are scoped to the message's session.
Code Example 8-2 Setting a Session
Applications can also scope a message to every client that has registered interest in the message's session by specifying TT_SESSION with tt_message_file_set for the message scope. When the message is received by a client whose pattern matches, the receiving client can get the file name by calling tt_message_file.
Code Example 8-3 Setting a File
To send any other data type in a ToolTalk message, the client must serialize the data into a string or byte string and then deserialize it on receipt. The new XDR argument API calls provided with the ToolTalk service now handles these serialization and deserialization functions. The client only needs to provide an XDR routine and a pointer to the data. After serializing the data into the internal buffer, the ToolTalk service treats the data in the same manner as it treats a byte stream.
The sender obtains a message handle and fills in the address, scope, and class attributes.
The sender fills in the operation and arguments attributes.
If the sender has declared only one ptype, the ToolTalk service fills in sender_ptype by default; otherwise, the sender must fill it in.
If the scope is TT_FILE, the file name must be filled in or defaulted. If the scope is TT_SESSION, the session name must be filled in or defaulted. If the scope is TT_BOTH or TT_FILE_IN_SESSION, both the file name and session name must be filled in or defaulted.
The ToolTalk service compares the address, scope, message class, operation, and argument modes and types to all signatures in the Handle section of each ptype.
Only one ptype will usually contain a message pattern that matches the operation and arguments and specifies a handle. If a handler ptype is found, then the ToolTalk service fills in opnum, handler_ptype, and disposition from the ptype message pattern.
If the address is TT_HANDLER, the ToolTalk service looks for the specified procid and adds the message to the handler's message queue. TT_HANDLER messages cannot be observed because no pattern matching is done.
The ToolTalk service compares the scope, class, operation, and argument types to all message patterns in the Observe section of each ptype.
For all observe signatures that match the message and specify TT_QUEUE or TT_START, the ToolTalk service attaches a record (called an "observe promise") to the message that specifies the ptype and the queue or start options. The ToolTalk service then adds the ptype to its internal ObserverPtypeList.
If a running process has a registered handler message pattern that matches the message, the ToolTalk service delivers the message to the process; otherwise, the ToolTalk service honors the disposition (start or queue) options.
If more than one process has registered a dynamic pattern that matches the handler information, the more specific pattern (determined by counting the number of non-wildcard matches) is given preference. If two patterns are equally specific, the choice of handler is arbitrary.
The ToolTalk service delivers the message to all running processes that have registered Observer patterns that match the message. As each delivery is made, the ToolTalk service checks off any observe promise for the ptype of the observer. After this process is completed and there are observe promises left unfulfilled, the ToolTalk service honors the start and queue options in the promises.
The editor has the following Handle pattern in its ptype:
(HandlerPtype: TextEditor;
Op: ShowLine;
Scope: TT_SESSION;
Session: my_session_id;
File: /home/butterfly/astrid/src/ebe.c)
The sender fills in the class, operation, arguments, and the target objid attributes.
The sender attribute is automatically filled in by the ToolTalk service. The sender can either fill in the sender_ptype and session attributes or allow the ToolTalk service to fill in the default values.
If the scope is TT_FILE, the file name must be filled in or defaulted. If the scope is TT_SESSION, the session name must be filled in or defaulted. If the scope is TT_BOTH or TT_FILE_IN_SESSION, both the file name and session name must be filled in or defaulted.
The ToolTalk service looks up the objid in the ToolTalk database and fills in the otype and file attributes.
The ToolTalk service searches through the otype definitions for Handler message patterns that match the message's operation and arguments attributes. When a match is found, the ToolTalk service fills in scope, opnum, handler_ptype, and disposition from the otype message pattern.
The ToolTalk service compares the message's class, operation, and argument attributes against all Observe message patterns of the otype. When a match is found, if the message pattern specifies TT_QUEUE or TT_START, the ToolTalk service attaches a record (called an "observe promise") to the message that specifies the ptype and the queue or start options.
The ToolTalk service continues to match the message's class, operation, and argument attributes against all Observe message patterns of all ptypes. When a match is found, if the signature specifies TT_QUEUE or TT_START, the ToolTalk service attaches an observe promise record to the message, specifying the ptype and the queue or start options.
If a running process has a registered Handler pattern that matches the message, the ToolTalk service delivers the message to the process; otherwise, the ToolTalk service honors the disposition (queue or start) options.
If more than one process has registered a dynamic pattern that matches the handler information, the more specific pattern (determined by counting the number of non-wildcard matches) is given preference. If two patterns are equally specific, the choice of handler is arbitrary.
The ToolTalk service delivers the message to all running processes that have registered Observer patterns that match the message. As each delivery is made, the ToolTalk service checks off any observe promise for the ptype of the observer. After this process is completed and there are observe promises left unfulfilled, the ToolTalk service honors the disposition (queue or start) options in the promises.
a. Examines the spec associated with the objid and finds that the type of the objid is FinnogaCalc_cell and that the corresponding object is in the file wardrobe.wks.
b. Consults the otype definition for FinnogaCalc_cell. From the otype, the ToolTalk service determines that this message is observed by processes of ptype FinnogaCalc and that the scope of the message should be TT_FILE.
c. Matches the message against registered patterns and locates all processes of this ptype that are observing the proper file. FinnogaCalc2 matches, but FinnogaCalc does not.
d. Delivers the message to FinnogaCalc2.
Table 8-4 Functions Used to Create Messages
Table 8-5 Functions Used to Complete Messages
Usually, one process makes a general request, picks the handler attribute from the reply, and directs further messages to that handler. If you specify the exact procid of the handler, the ToolTalk service will deliver the message directly -- no pattern matching is done and no other applications can observe the message. This point-to-point (PTP) message passing feature enables two processes to rendezvous through broadcast message passing and then communicate explicitly with one another.
The vtype name helps the message receiver interpret data. For example, if a word processor rendered a paragraph into a PostScript representation in memory, it could call tt_message_arg_add with the following arguments:
tt_message_arg_add (m, "PostScript", buf);
In this case, the ToolTalk service would assume buf pointed to a zero-terminated string and send it.Similarly, an application could send an enum value in a ToolTalk message; for example, an element of Tt_status:
tt_message_iarg_add(m, "Tt_status", (int) TT_OK);
The ToolTalk service sends the value as an integer but the "Tt_status" vtype tells the recipient what the value means.
When you create a message with tt_pnotice_create or tt_prequest_create, you must supply the following two attributes as arguments:
When you create a message with tt_onotice_create or tt_orequest_create, you must supply the following two attributes as arguments:
You use tt_message_callback_add to add the callback routine to your request. When the reply comes back and the reply message has been processed through the callback routine, the reply message must be destroyed before the callback function returns TT_CALLBACK_PROCESSED. To destroy the reply message, use tt_message_destroy, as illustrated in Figure 8-3.
Figure 8-3 Destroying a Message
Tt_callback_action
sample_msg_callback(Tt_message m, Tt_pattern p)
{
... process the reply msg ...
tt_message_destroy(m);
return TT_CALLBACK_PROCESSED;
}
The following code sample is a callback routine, cntl_msg_callback, that examines the state field of the reply and takes action if the state is started, handled, or failed.
/*
* Default callback for all the ToolTalk messages we send.
*/
Tt_callback_action
cntl_msg_callback(m, p)
Tt_message m;
Tt_pattern p;
{
int mark;
char msg[255];
char *errstr;
mark = tt_mark();
switch (tt_message_state(m)) {
case TT_STARTED:
xv_set(cntl_ui_base_window, FRAME_LEFT_FOOTER,
"Starting editor...", NULL);
break;
case TT_HANDLED:
xv_set(cntl_ui_base_window, FRAME_LEFT_FOOTER, "", NULL);
break;
case TT_FAILED:
errstr = tt_message_status_string(m);
if (tt_pointer_error(errstr) == TT_OK && errstr) {
sprintf(msg,"%s failed: %s", tt_message_op(m), errstr);
} else if (tt_message_status(m) == TT_ERR_NO_MATCH) {
sprintf(msg,"%s failed: Couldn't contact editor",
tt_message_op(m),
tt_status_message(tt_message_status(m)));
} else {
sprintf(msg,"%s failed: %s",
tt_message_op(m),
tt_status_message(tt_message_status(m)));
}
xv_set(cntl_ui_base_window, FRAME_LEFT_FOOTER, msg, NULL);
break;
default:
break;
}
/*
* no further action required for this message. Destroy it
* and return TT_CALLBACK_PROCESSED so no other callbacks will
* be run for the message.
*/
tt_message_destroy(m);
tt_release(mark);
return TT_CALLBACK_PROCESSED;
}
You can also add callbacks to static patterns by attaching a callback to the opnum of a signature in a ptype. When a message is delivered because it matched a static pattern with an opnum, the ToolTalk service checks for any callbacks attached to the opnum and runs them.
If the ToolTalk service returns TT_WRN_STALE_OBJID, it has found a forwarding pointer in the ToolTalk database that indicates the object mentioned in the message has been moved. However, the ToolTalk service will send the message with the new objid. You can then use tt_message_object to retrieve the new objid from the message and put it into your internal data structure.
If you will not need the message in the future (for example, if the message was a notice), you can use tt_message_destroy to delete the message and free storage space.
Figure 8-4 Creating and Sending a Pnotice
/*
* Create and send a ToolTalk notice message
* ttsample1_value(in int <new value)
*/
msg_out = tt_pnotice_create(TT_SESSION, "ttsample1_value");
tt_message_arg_add(msg_out, TT_IN, "integer", NULL);
tt_message_arg_ival_set(msg_out, 0, (int)xv_get(slider, PANEL_VALUE));
tt_message_send(msg_out);
/*
* Since this message is a notice, we don't expect a reply, so
* there's no reason to keep a handle for the message.
*/
tt_message_destroy(msg_out);
Figure 8-5 illustrates how an orequest is created and sent when the callback routine for cntl_ui_hilite_button is called.Figure 8-5 Creating and Sending an Orequest
/*
* Notify callback function for `cntl_ui_hilite_button'.
*/
void
cntl_ui_hilite_button_handler(item, event)
Panel_item item;
Event *event;
{
Tt_message msg;
if (cntl_objid == (char *)0) {
xv_set(cntl_ui_base_window, FRAME_LEFT_FOOTER,
"No object id selected", NULL);
return;
}
msg = tt_orequest_create(cntl_objid, "hilite_obj");
tt_message_arg_add(msg, TT_IN, "string", cntl_objid);
tt_message_callback_add(msg, cntl_msg_callback);
tt_message_send(msg);
}