Developer's Guide to SMT

State Machine Threadlets

Introduction

Purpose, philosophy and principles

SMT is actually two things. One: a code generator ("codebot") that eats state machine models and produces code, and two: a set of iCL classes that implement a multithreading framework for ANSI C applications. While the code generator and kernel work together, the code generator is actually happy to produce code for any kind of multithreaded state machine implementation, using the target functionality built-in to all MOP code generators.

The need for state machine models is well known in our work. All complex software applications are usefully broken into components, and it is also useful to conceive of many of these components as event-driven state machines. The state machine model has been well tested in applications ranging from COBOL accounting packages to C protocol servers.

SMT/5 - this version - is aimed squarely at the development of protocol servers and clients written (assuming the current target) in ANSI C. While the SMT kernel is built using iCL, SMT applications do not necessarily need to look like iCL classes, they can look just like SMT models, which we call "agents".

An SMT agent implements a set of thread specifications; each thread being defined as a set of states, broken down into events and actions.

At runtime, the application creates threads, and the SMT kernel executes these, doing the normal context switching we'd expect of any multithreading system. To keep some distinction between real OS threads, and SMT threads, we call the latter "threadlets".

Each threadlet has a simple lifecycle: start, consume events, end. Events are either internal events produced by SMT for the threadlet (such as events generated by socket i/o and timers) or external events sent to the threadlet by other threadlets, and we call such external events "methods".

Threadlets run asynchronously, giving SMT applications a high degree of responsiveness. When SMT applications run on more than one OS thread (this can be selected both at compile time and at runtime), we also get good resistance to blocking activities (e.g. slow disk access) in any single threadlet.

SMT agents are reasonably difficult to write, and a good designer needs a certain non-trivial skill set:

  • The ability to design good state machines, which needs experience and training. A state machine is an abstraction of a package of logic; the abstraction must be neither too detailled nor too brief; it must properly distinguish events from actions from states; it must handle errors carefully; it must remain readable; etc.
  • Experience in writing event-driven network applications; knowledge of TCP/IP and other networking protocols; understanding of the socket metaphor; understanding of network errors and how to handle them; understanding of traffic situations, heartbeats, timeouts, etc.
  • Knowledge of the SMT grammar, which is documented in the reference section of this manual. The grammar allows wide flexibility in writing agents, but only a small part of this is typically useful, so one also needs examples and experience to work from.
  • Knowledge of the SMT kernel API, so that the agent actions (in C) can properly interface with the kernel and access its services.

While SMT was originally designed to build asynchronous traffic-processing agents - such as are needed for network servers and clients - it is also useful for asynchronous processing in a more general sense.

Using a special target (icl_smt), SMT generates a synchronous class wrapper and an asynchronous state agent for an iCL class. This is very useful in a muiltithreading environment, indeed it justifies the use of iCL for languages that even have a class model, like C++.

The combination of a synchronous wrapper and asynchronous agent provides what we call "asynchronous classes", namely objects that process events asynchronously rather than synchronously as is normal for OO code.

Asynchronous classes are essential to using SMT's multithreading support because they remove most of the difficulties of using multithreaded code. Most significantly:

  • each threadlet action is fully serialised, so threadlets do not need any locking or semaphores on their context;
  • asynchronous classes handle queues of events and allow the application designer to create decoupled designs that are more scalable, robust, and simpler than classic synchronous designs.

The use of asynchronous classes is fairly essential to large-scale applications because decoupling is a very powerful design simplification tool. We can conceive of large applications as pipelines that send each other events, but operate independently apart from this thin API.

Dependencies

SMT is part of the iMatix tool chain (the "Technology Packages"), and specifically part of the package called Base/2. It depends on Base/1 and GSL/4 - you must download and install these before starting with SMT. See http://www.imatix.net/pub.

Base/2 uses the Apache Runtime library (APR) for certain functions, and SMT applications may make calls to this library. APR is packaged with Base/2; you do not need to take any special action to get APR.

SMT is part of the Base/2 toolchain: we build it using the XNF meta-generator (a tool that turns abstracted grammars into code generators).

Copyrights and Credits

SMT is copyright (c) 2004-2007 iMatix Corporation, and is licensed as free software under the GPL, with a commercial opt-out option.

SMT was designed and written by Jonathan Schultz and Pieter Hintjens. This is the fifth major rewrite of SMT, the first version was built by Pieter Hintjens in 1995.

Features

The major features of SMT are:

  • Provides a modeling language for multithreaded finite state machines, based on the Libero "dialogue" model.
  • Provides a code generator to digest such models and a target that produces ANSI C code compatible with the iMatix iCL architecture.
  • Includes a library that supports those executable models with a multithreaded microkernel.
  • Includes support for asynchronous iCL classes.
  • Runs on Linux, Windows, and Solaris.
  • SMT kernel and applications can be compiled for single-threaded or multithreaded operation.
  • In multithreaded operation, can run on 1 or more OS threads, and maps the threadlets to OS threads.

Worked Example

For the purposes of illustration, we will build a trivial SMT-based program. This program counts from 1 to 5, then terminates.

Here is the contents of the file smt_demo_trivial.smt:

<?xml?>
<!---------------------------------------------------------------------------
 *  iMatix SMT (State Machine Threadlets)
 *
 *  smt_demo_trivial - A trivial demonstration of an SMT agent.
 *
 *  Copyright (c) 1991-2005 iMatix Corporation
 *  ------------------------------------------------------------------------->

<agent name = "smt_demo_trivial" script = "smt_gen.gsl">

    <thread name = "main">

        <state name = "starting">
            <event name = "ok" nextstate = "counting">
                <action name = "increment count"/>
                <action name = "test count value"/>
            </event>
        </state>
        <state name = "counting">
            <event name = "ok" nextstate = "counting">
                <action name = "print count value"/>
                <action name = "increment count"/>
                <action name = "test count value"/>
            </event>
            <event name = "done" nextstate = "">
                <action name = "print done"/>
            </event>
        </state>
        <context>
            int count;
        </context>
        <handler name = "thread init">
            tcb->count = 0;
            the_next_event = ok_event;
        </handler>
        <action name = "increment count">
            tcb->count++;
        </action>
        <action name = "test count value">
            if (tcb->count < 5)
                the_next_event = ok_event;
            else
                the_next_event = done_event;
        </action>
        <action name = "print count value">
            printf ("Value of count is %i\n", tcb->count);
        </action>
        <action name = "print done">
            printf ("DONE\n");
        </action>
    </thread>
</agent>

What does this mean?

As you can see, the SMT agent consists of chunks of standard C code inside an XML file. The finite state machine (FSM) is described in terms of states, events and actions in a straighforward way. The actions are identified by name and defined below the FSM description. The action definitions contain standard C code along with some special C code that interacts with the FSM. For instance, in the actions "thread init" and "test count value", a pseudo-variable "the_next_event" is assigned with the value "ok_event" or "done_event" to guide the FSM.

The event descriptions in the FSM include an attribute "nextstate" to tell the FSM which state it should enter after executing the actions inside the event. The event "done" in the state "counting" has a special value of nextstate, an empty string, to tell the FSM that the thread should terminate following the execution of the actions inside this event. If the attribute nextstate is not defined for an event, the FSM remains in the same state after executing the actions.

Thread context

Because SMT is a multi-threading FSM engine, each thread is allowed to store its own context, besides the context implied by the current state and event. Each thread receives its own identical context space, described inside the "context" object. In this example the context consists of a single integer called "count".

To access the thread context in the C code, the pseudo-structure (actually a macro) "tcb" is used. "tcb" stands for "Thread Context Block" and contains the fields exactly as they are defined in the "context" object in the SMT file. You can see the "count" field used by the actions "increment count", "test count value" and "print count value".

Implementation

For each context type, a structure is defined called <CONTEXT_NAME>_CONTEXT where the default value of <CONTEXT_NAME> is <THREAD_NAME>. The field 'context' in the thread structure is allocated when the thread is created and freed when the thread is destroyed.

Starting and terminating a thread

In addition to code that is executed as a thread runs, you may wish to define code that is run when the thread is created, started or terminated. We call this code a "handler", and our example includes a handler for thread initialisation, which initialises the count field of the thread context to zero, and provides a starting event for the thread. If no starting event is provided the thread will wait until one is provided.

Using a state machine

Here is the contents of the file smt_demo_trivial_main.c:

#include "icl.h"
#include "smt.h"
#include "smt_demo_trivial.h"

int main (int argc, char *argv [])
{
    smt_thread_t
        *thread1,
        *thread2;
    icl_system_initialise (argc, argv);
    smt_demo_trivial_init ();
    thread1 = smt_demo_trivial_main_thread_new ();
    thread2 = smt_demo_trivial_main_thread_new ();
    smt_thread_wake (thread1);
    smt_thread_wake (thread2);
    smt_thread_unlink (&thread1);
    smt_thread_unlink (&thread2);
    smt_os_thread_wait (0);
    icl_system_terminate ();
    return (0);
}

The header files for the iCL and SMT libraries are included, followed by the generated header file from our trivial SMT just described.

The main code creates two instances of the thread by calling the function smt_demo_trivial_main_thread_new. This function name is derived from the name of the SMT agent ("smt_demo_trivial"), the name of the thread ("main") and "thread_new". The code then wakes each of the two threads so that they can start. The calls to "smt_thread_unlink" are part of SMT's reference counting system, as C does not have automated garbage collection. Then the code calls smt_os_thread_wait, which waits while the SMT engine runs until it has nothing left to do. Finally icl_system_terminate is called to shut down the whole thing.

Building the trivial example

gsl smt_demo_trivial.smt

turns the FSM description into executable C. Two files are created: smt_demo_trivial.h and smt_demo_trivial.c

c smt_demo_trivial
c -r libsmt.a smt_demo_trivial

compiles the generated C and places the object file in the library libsmt.a

c -l smt_demo_trivial_main

compiles and links the main code.

./smt_demo_trivial_main

executes the example, producing the output:

14:42:15: Using fat memory allocator
14:42:15: I: SMT will use 4 OS threads.
14:42:15: Value of count is 1
14:42:15: Value of count is 1
14:42:15: Value of count is 2
14:42:15: Value of count is 2
14:42:15: Value of count is 3
14:42:15: Value of count is 3
14:42:15: Value of count is 4
14:42:15: Value of count is 4
14:42:15: DONE
14:42:15: DONE

Following the first two lines of extaneous information, we see the output from the two threads, just as expected. Note that, depending on your system and/or build, the threads may be interwoven differently, for example:

14:42:15: Using fat memory allocator
14:42:15: I: SMT will use 4 OS threads.
14:42:15: Value of count is 1
14:42:15: Value of count is 2
14:42:15: Value of count is 3
14:42:15: Value of count is 4
14:42:15: DONE
14:42:15: Value of count is 1
14:42:15: Value of count is 2
14:42:15: Value of count is 3
14:42:15: Value of count is 4
14:42:15: DONE

The FSM abstraction

History of finite state machines

The theory of finite state machines is very old. These are some of the first models ever used for software formalisation, and they are very, very useful. FSMs are a part of most CompSci courses, but what is rarely explained is that as executable models, they can also be exploited for very successful code generation.

Some literature explains how to generate FSMs from other models, e.g. from parse trees, for compilers. But the real power comes from allowing people to use FSMs as a modeling tool for their code, directly.

There is a simple and effective model for FSMs, developed by Leif Svalgaard in 1985; this model is simpler than most state machine modelling tools, and is easy to represent in text, or in XML (as we do today).

Libero

The most popular tool based on this design was Hintjens' Libero tool (1991 to present), which allowed developers to model their code in a text format, and converted this into state machines in about 20 different computer languages including such bizarre cases as PL/SQL and csh.

Libero is documented on www.imatix.com and the background documents are worth reading for understanding SMT.

The SMT FSM model

SMT's state machine model is derived from Libero's, with additional semantics to handle the needs of multithreading. The model remains based on the original State/Event/Action breakdown, in which:

  • a state represents a point of existence in the lifecycle of the threadlet or program;
  • an event represents a possible exit from the state, moving to a next state;
  • an action represents a unit of work done before moving to a next state.

States, events, and actions are named, and these names are used to produce source code that is readable (as far as needed). Actions are typically mapped to functions or subroutines - in Libero the programmer provides action code bodies in a separate document. In SMT/5 the code block for actions is provided directly within the state machine model, which makes it easier to edit.

The learning curve

It takes a developer new to state machines about 3 days to understand FSMs, and several weeks to become moderately good at designing these. The first barriers to understanding are:

  • the newness of event-driven design, in which actions are always the result of an event, not part of a simple logic flow;
  • the lack of classic conditional logic;
  • the lack of classic subroutine concepts;
  • the lack of any object-oriented concepts.

Later, when the event-driven metaphor is clear, developers make these classic mistakes:

  • confusing events and actions;
  • confusing events and states;
  • putting too much detail into the model;
  • not putting enough detail into the model.

Writing good SMT models

There are some simple rules of thumb that help the developer keep to the straight path when writing SMT models (or Libero models; the problems are similar):

  • The model must be clear enough to explain to someone and close to the level of detail that you would use when explaining to someone how your program or class works.
  • The states are places you find yourself, the events are things that happen, the actions are things you do.
  • Use long state names, longer action names, and shorter event names.
  • Use default states to compress your model where possible.
  • When you find yourself producing many internal events, consider using exceptions instead.

So state names are typically "doing something" or "expecting something" or "processing something". "ing" in a state name is generally a healthy sign.

Event names, by contrast, are methods like "open", "close", or "help" (from the outside world), or errors like "disk full", "no response", or "timeout", or decisions like "try again", "ignore", or "complete".

One helpful comparison is to think of events as push-buttons in a pop-up window. Common events are 'Ok' and 'Cancel', 'Print', 'Delete', and 'Help'. A push-button like 'Delete Next Record' would be too specific.

Lastly, actions are executive orders, like "close socket", or "send header to client", or "tell caller it is over".

If state machines were the human body, states would be moods, events would be other people, and actions would be flailing of the limbs. In fact state machines are little else than decoupled conditional "when" (not "if") statements. "When hungry and Joe vists, go to pizza joint".

Common problem symptoms

Here are some ways that people commonly abuse Libero and SMT models:

  • An action that sets a single event unconditionally, rather than producing an event that indicates the outcome of some action. This usually indicates that the author has confused the notion of state with the notion of action. By shunting to a specific state, the author says 'I want to be in this state to do some specific work'. Very rarely, this is a good way to simplify a model. 99% of times, though, it is bad practice. If you find yourself doing this, change the model so you do the work in question as early as possible, thus generating an event which you can handle neatly in the next state.
  • Models that are broken into very many small states. This usually means that the designer has not understood exceptions.
  • Models that use lots and lots of different event names. This usually means the designer is confusing the notion of event with the notion of action.

Architecture

SMT is built on top of iCL and iPR. A basic understanding of these libraries is assumed in the following description.

As is usual with iCL, structures may be defined at more than one level, and the final result is obtained by inheriting from the higher to the lower level. If the same object is defined at more than one level, inheritance rules are used to determine how the lower-level object is overlaid on the higher-level object.

SMT use of threads

SMT can be built in two distinct fashions: ST (single-threaded) and MT (multi-threaded). Although the abstraction is unchanged, there are significant internal differences between an ST and an MT build.

Single-threaded SMT

When SMT is built in ST mode, the entire program runs in a single thread. This means that there are no mutexes, read/write locks or any thread contention constructs. This has been the historical model of SMT and provides very good performance on a single-CPU machine. However it is incapable of benefiting from the improved performance that multiple CPUs can provide.

In the ST build of SMT, the FSM can only operate when the calling program requests a wait for SMT events. The crucial function is smt_os_thread_wait, which invokes the FSM engine.

Multi-threaded SMT

In an MT build, SMT creates a predetermined number of OS threads to run the FSM. This allows the SMT threads to run in parallel and in the background, that is without an explicit request from the calling program. More importantly, because SMT runs in several OS threads, it can benefit from having mulitple CPUs on which to run. The cost, however, is in the thread synchronization code which must be run. SMT uses some lock-free mechanisms for improved performance, and can serialise access to application objects in order to reduce reliance on mutexes and read/write locks.

SMT methods

The way that other C code (either SMT or non-SMT) communicates with an SMT thread is with SMT methods. When a method is invoked, it is placed in a queue and eventually delivered to the destination thread. We sometimes refer to 'calling' a method as a shorthand for calling the method delivery function for the method.

An SMT agent defines the methods that it accepts. A method is defined as a number of arguments, along with 'possess' and 'release' code. The possess code defines what code must be executed at the moment the method is called so that the caller no longer has to worry about the data that it passed to the method. The typical possess code makes a copy of any string arguments, so that it doesn't matter if the calling code re-uses or frees the buffer containing the string. The release code defines must what be done when the method has been processed; in the preceeding example of string this would mean freeing the copied string.

SMT application classes

The classes with which the SMT application programmer deals are smt_thread, smt_socket, smt_socket_request and smt_timer_request.

Thread objects

The SMT thread object describes a single SMT thread. This comprises the static information such as the thread manager, exception handler and destructor functions along with dynamic information describing the thread status and current socket and requests.

Timer request objects

Timer request objects are used for thread synchronisation based on timers. This functionality is essential for programming such functionality as thread sleep and timeouts.

Socket objects

SMT provides native access to sockets. The SMT socket object describes a socket for SMT-based programs. It is essentially a container for socket requests, which are a critical part of SMT's thread synchronisation functionality.

Socket request objects

In order to allow many SMT threads to run within a single OS thread, SMT handles all socket activity and is thus able to schedule a thread for execution as activity is detect on the socket(s) that the thread is using.

SMT provides socket requests of variety of types: ACCEPT (a connection), INPUT (detect data ready for input), READ, CONNECT (to a socket), OUTPUT (detect ready for output), WRITE, CLOSE and MONITOR (described below).

Thread synchronisation using socket and timer requests

Since a socket or timer request (henceforth referred to simply as a request) implies waiting for the request to be fulfilled, an SMT thread typically wants to be put to sleep until the request has been processed. There are in fact two ways in which this can be performed. The simplest way is for the thread to sleep following the action in which the socket request is made and to only proceed to the following action when the request has been performed. We refer to this method of making a request as a 'pending request'; that is the thread is put to sleep pending the processing of the request(s).

A second way in which a thread may make a request is to specify that a particular event be delivered to the thread upon completion of the request. In this model the thread does not go to sleep immediately but continues processing successive actions. However the typical way of of making this type of request is to program the thread to enter a new state with no event provided explicitly. This causes the thread to sleep until an event is provided by the completed request.

Socket monitor requests

The socket monitor request is the most complicated form of socket request, and is used when the thread wishes to be woken on either the completion of the socket request or the delivery of another external event. In the latter case the socket request is to be cancelled. A socket monitor request is typically used in the 'sleeping' state of a server connection thread, so that the thread is woken on either the detection of activity on its connection or when another SMT thread (eg a queue) wishes to use the connection.

Agent and thread definition

An SMT file defines an SMT agent. The simplest agent consists of a single thread; however multiple threads are allowed in a single agent. Note that a single thread can have multiple instances, as the trivial example above demostrates. For instance a typical server agent has at least two threads - one to accept connections and the other to manage a connection. Note that what we really mean here is two thread types - the connection manager thread is instantiated once per connection.

Many objects in an agent can be defined at various levels, according to their level of generality and the preference of the programmer. For instance an action can be defined right inside the FSM description, at the level of the thread or at the agent level. When an object is used elsewhere from its definition, it is referred to by name; such names must be unique across the entire agent.

As in other iCL classes, SMT agents may include inheritance from other agents. At present this is little-used and there is no need for the programmer to understand its operation in detail.

Contexts

Each thread instance has a thread context block, usually called simply its context. As with threads, we also use the term 'context' to refer to a context type; the distinction should be apparent from the context.

The simplest way of defining a thread context is to do so inside the thread definition, as is the case in the trivial example above. If you wish multiple threads to use the same context, you must specify a name for the context and define the context in a single place (either in one of the threads or at the agent level). To specify that another thread is to use the same context, simply specify the name of the context but provide no context body.

States

As it is common for a thread wish to process some events (usually exeptions of some sort) in the same way across many states, SMT provides a special state called a 'defaults' state, identified simply by the name 'defaults'. The defaults state is implicitly inherited into each state in the thread. A particular state may over-ride the handling of an event defined in the defaults state by explicitly defining its handling of that event.

Events

Events are what guides the FSM; that is whenever the FSM enters a state, the actions it performs are those corresponding to the event. If no event is provided, the FSM sleeps until it is woken by an incoming method.

After performing the actions belonging to an event, the FSM progresses to the state defined by the 'nextstate' attribute of the 'event' item. If no nextstate is defined, the FSM remains in the state where it is. If the nextstate is an empty string (nextstate = ""), the thread shuts down following the when the actions have been performed.

The correct way to deliver an event is to call the method:

smt_thread_set_next_event (thread, event)

from within an action in the thread. If an event is to be delivered from a different thread then it should be done via a m ethod.

Methods

A method is closely related to an event, but with some important distinctions. An event can only provided to a thread from an action that is running in that thread; for this reason we sometimes use the term 'internal event'. An event is delivered immediately and not queued; if an action delivers one event then another before the first has been processed, the first event is lost.

A method can be provided either by the thread itself, from another thread or from code outside SMT. A method is placed in a queue and fetched when an event is required by the FSM and no internal event has been provided. Events thus take precence over methods.

Exceptions

SMT provides exceptions, which are a way to break from the standard flow of execution. An exception is actually a particular way of sending an event; it consists of an event like any other with the additional characteristic that the current processing halts after the action in progress and the FSM moves immediately to the action list corresponding the the exception event.

To raise an exception, call the function:

smt_thread_raise_exception (thread, event)

from withing an action in the thread.

Exceptions can also be provoked from error results from function calls. This typically happens on socket errors. To translate an error result into an exception event, define <catch> items in the dialog. For example the lines:

<catch error = "SMT_SOCKET_ERROR"  event = "socket error" />
<catch                             event = "other error" />

tells the FSM to translate the error SMT_SOCKET_ERROR into the exception event socket_error and any other (non-zero) result code into the exception event other_error.

Terminating a thread

A thread may terminate itself by processing an event whose 'nextstate' field is an empty string. It may also request termination inside an action by calling

smt_thread_set_next_event (thread, SMT_TERM_EVENT)

Shutdown event

In order to be robust against resource leakage on externally-imposed termination, SMT threads use a special event called 'shutdown'. If no shutdown event is defined explicitly, it is defined implicitly with no actions and an empty nextstate.

Typically a thread should respond to a shutdown event by freeing all resources it is occupying and terminating. However such a scenario is only feasible in simple cases. In more complicated examples a thread may not be aware of all the resources it is consuming as this information may be held in another thread. In such a case, the thread should move to a state (we usually call it 'shutting down') where requests to destroy objects are still accepted and processed normally, but requests to create or allocate more resources are ignored.

Code generation

SMT uses the GSL code generation engine to product ANSI C code from the SMT agent definition. The public header of this C code consists of agent initialisation and termination, thread creation and and method sending functions.

Implementation

SMT Internal Classes

OS thread objects

The smt_os_thread object contains information pertinent to a single OS thread. Conceptually this is simply a collection of thread running in that OS thread. However for implementation purposes there are other fields related to thread synchronisation and timer and socket request.

SMT methods

The internal definition of smt_method comprises the event that will be given to the destination thread when the method is delivered, a result code, a void pointer for application-dependent data and a destructor function to free any data in the data. When a given method is called (ie its delivery function is called), a method object is created containing the corresponding event (in fact the method name followed by "_m"). The data pointer points to a structure generated from the method definition. It is assigned the values provided by the caller after the 'possess' code is called. The destructor contains the code defined in the 'release' code of the method.

SMT position

smt_position objects are used to create a stack for use in 'call' and 'return' SMT constructs.

SMT internal containers

smt_method, smt_os_thread, smt_position, smt_socket_request, smt_thread and smt_timer_request have containers.

Code generation

In addition to the public functions in the generated code SMT generates internal agent manager and catcher functions for each agent. The agent manager is the most central and complicated part of the generated code; this is where the work of implementing the FSM is done.

Reference section

Summary of the SMT language

This summary shows the hierarchy of elements you can use, with the required and optional attributes for each element. The XML entity and attribute names are case-sensitive and we use only lower-case names.

<agent script name [before] [after] [copyright] [license] [role] [abstract] [target]
     [version] [animate] [trace_size]>
   <option name value/>
   <inherit name [phase] [condition]>
      <option .../>
   </inherit>
   <include filename [required]/>
   <import class [condition]/>
   <private [name]/>
   <public [name]/>
   <context name/>
   <handler [name]>
      <argument name type/>
   </handler>
   <state name [abstract] [template]>
      <option .../>
      <inherit .../>
      <handler name/>
      <event name [nextstate]>
         <action [name]/>
      </event>
      <default [nextstate]>
         <action [name]/>
      </default>
   </state>
   <thread name [abstract] [template] [context] [schedule] [share]>
      <option .../>
      <inherit .../>
      <context [name]/>
      <handler name>
         <argument .../>
      </handler>
      <state name [abstract] [template]>
         <option .../>
         <inherit .../>
         <handler .../>
         <event name [nextstate]>
            <action .../>
         </event>
         <default .../>
      </state>
      <action [name]>
         <call state [event]/>
         <return/>
      </action>
   </thread>
   <event name/>
   <action [name]>
      <call .../>
      <return .../>
   </action>
   <catch event [error]/>
   <method name [event] [priority] [message]>
      <argument .../>
      <possess/>
      <release/>
   </method>
   <message name [priority]>
      <argument .../>
      <method name [event] [priority] [message]>
         <argument .../>
         <possess .../>
         <release .../>
      </method>
      <possess .../>
      <release .../>
   </message>
</agent>

Detailed specifications

All child entities are optional and can occur zero or more times without any specific limits unless otherwise specified. The same tag may occur at different levels with different meanings, and in such cases will be detailed more than once here.

The 'smt' item

Defines an SMT agent.

<agent
    script = "smt_gen.gsl"
    name = "..."
  [ before = "..." ]
  [ after = "..." ]
  [ copyright = "..." ]
  [ license = "bsd | gpl"  ("gpl") ]
  [ role = "..." ]
  [ abstract = "0 | 1"  ("0") ]
  [ target = "stdc | stdcpp | doc"  ("$(switches.target?'stdc':)") ]
  [ version = "..." ]
  [ animate = "0 | 1"  ("0") ]
  [ trace_size = "..." ]
    >
    <option>
    <inherit>
    <include>
    <import>
    <private>
    <public>
    <context>
    <handler>
    <state>
    <thread>
    <event>
    <action>
    <catch>
    <method>
    <message>
</agent>

The smt item can have these attributes:

abstract
If set, the entity only exists in order to be inherited - no code is generated. The abstract attribute is optional. Its default value is "0". It can take one of the following values:
Value Meaning
0 normal entity
1 abstract entity

role
A file may fulfill a role. This serves two purposes: asserting that essential roles are fulfilled and helping to define the inheritence order using the 'before' and 'after' attributes. The role attribute is optional.
before
Specifies a role before which this file should should be inherited. The before attribute is optional.
after
Specifies a role before which this file should should be inherited. The after attribute is optional.
copyright
This specifies the copyright string for the model. This string is stamped into the generated sources, if specified. The copyright attribute is optional.
license
Specifies the license of the model. This license is applied to all models inherited or built from the current model. The license attribute is optional. Its default value is "gpl". It can take one of the following values:
Value Meaning
bsd generates a BSD license header
gpl generates a GPL license header

name
Agent name. The name attribute is required.
script
GSL script to process the agent definition. The script attribute is required. It can take one of the following values:
Value Meaning
smt gen.gsl The SMT processor

target
Specifies the name of the target environment; the target is implemented by a GSL script that generates code for a specific language environment. The target can be inherited from a parent class. The target attribute is optional. Its default value is "$(switches.target?'stdc':)". It can take one of the following values:
Value Meaning
stdc Standard ANSI C + iMatix runtime
stdcpp Standard ANSI C++
doc Documentation

version
Specifies the version of the class. This text can take any format but we recommend this standard format: '2.4b1' which is major version 2, minor version 4, release b, update 1. This string is stamped into the project sources. The version attribute is optional.
animate
If set, the generated code contains animation that can be switched on and off at runtime. This option can be overridden by a command-line switch (e.g. "-animate:0"). The animate option can be inherited from a parent class. The animate attribute is optional. Its default value is "0". It can take one of the following values:
Value Meaning
0 do not animate
1 generate animation code

trace_size
Allows default trace size to be over-ridden in this agent. The trace_size attribute is optional.

The 'option' item

Passes an option to an inherited class. Options can be used in the template code generation logic, or in method handlers.

<option
    name = "..."
    value = "..."
    />

The option item can have these attributes:

name
The name of the option. The name attribute is required.
value
The value for the option. The value attribute is required.

The 'inherit' item

<inherit
    name = "..."
  [ phase = "preproc | parse" ]
  [ condition = "..." ]
    >
    <option>
</inherit>

The inherit item can have these attributes:

name
Name of entity to inherit from. The name attribute is required.
phase
The processing phase during which this inheritence is performed. The phase attribute is optional. It can take one of the following values:
Value Meaning
preproc inherited in preprocessor
parse inherited in parser

condition
Specifies a condition which must be TRUE for the inheritence to occur. The condition attribute is optional.

The 'include' item

The 'include' element permits direct inclusion from another file. The included file must be a valid XML file which is included in the SMT tree as-is.

<include
    filename = "..."
  [ required = "0 | 1"  ("1") ]
    />

The include item can have these attributes:

filename
The name of the XML-format file to be included in place of the 'include' element. The filename attribute is required.
required
If set to zero, the include file is ignored if it is missing. The required attribute is optional. Its default value is "1". It can take one of the following values:
Value Meaning
0 File is not required
1 File is required

The 'import' item

Specifies other classes that this class refers to. Note if you want the generated code to be correct you must define an import item for each class that you refer to in your class context or methods. By default, the import tag is inherited unless you specify inherit = "0".

<import
    class = "..."
  [ condition = "..." ]
    />

The import item can have these attributes:

condition
Specifies a condition which must be TRUE for the entity to be created. The condition attribute is optional.
class
The name of the class imported. The class attribute is required.

The 'private' item

Private code blocks, which are copied to the generated code.

<private
  [ name = "types | data | functions" ]
    />

The private item has this single attribute:

name
What kind of code is this? Type or function definitions? The name attribute is optional. It can take one of the following values:
Value Meaning
types The code is inserted before type definitions
data The code is inserted before data definitions
functions The code is inserted before function definitions

The 'public' item

Public code blocks, which are copied to the generated header file.

<public
  [ name = "include | types | functions" ]
    />

The public item has this single attribute:

name
What kind of code is this? Type or function definitions? The name attribute is optional. It can take one of the following values:
Value Meaning
include The code is inserted in each pass - for iCL class collections.
types The code is inserted before type definitions
functions The code is inserted before function definitions

The 'agent context' item

Thread context block defined at agent level. To use a context block of this type, a thread should request a context block with this name.

<context
    name = "..."
    />

The agent context item has this single attribute:

name
Context name. The name attribute is required.

The 'agent handler' item

Defines a handler for a built-in event, at the agent level.

<handler
  [ name = "agent init | agent term" ]
    >
    <argument>
</handler>

The agent handler item has this single attribute:

name
The name attribute is optional. It can take one of the following values:
Value Meaning
agent init invoked when the agent is initialised.
agent term invoked when the agent is terminated.

The 'argument' item

Defines an argument for a handler, method, or message. The arguments definitions define the interface exported to calling programs.

<argument
    name = "..."
    type = "..."
    />

The argument item can have these attributes:

name
The name attribute is required. Will be turned into a valid C variable name.
type
The type attribute is required. This is a C type for the current SMT code generation framework.

The 'agent state' item

State common to all threads.

<state
    name = "..."
  [ abstract = "0 | 1"  ("0") ]
  [ template = "..." ]
    >
    <option>
    <inherit>
    <handler>
    <event>
    <default>
</state>

The agent state item can have these attributes:

template
If specified, defines an entity that acts as template for this entity. The template attribute is optional.
abstract
If set, the entity only exists in order to be inherited - no code is generated. The abstract attribute is optional. Its default value is "0". It can take one of the following values:
Value Meaning
0 normal entity
1 abstract entity

name
The name attribute is required.

The 'state handler' item

Defines a handler for a built-in event, at the state level.

<handler
    name = "before state"
    />

The state handler item has this single attribute:

name
The name attribute is required. It can take one of the following values:
Value Meaning
before state Invoked when entering the state.

The 'agent state event' item

Defines an event in the state:

<event
    name = "..."
  [ nextstate = "..." ]
    >
    <action>
</event>

The agent state event item can have these attributes:

name
The name attribute is required.
nextstate
The nextstate attribute is optional.

The 'agent state action' item

Defines an action for an event:

<action
  [ name = "..." ]
    />

The agent state action item has this single attribute:

name
The name attribute is optional.

The 'default' item

Defines default handling for the state, i.e. for events that are not listed in the state:

<default
  [ nextstate = "..." ]
    >
    <action>
</default>

The default item has this single attribute:

nextstate
Defines a link to the 'nextstate' item with the corresponding 'name' attribute. The nextstate attribute is optional.

The 'thread state action' item

Defines an action for an event:

<action
  [ name = "..." ]
    />

The thread state action item has this single attribute:

name
The name attribute is optional.

The 'thread' item

Defines a thread:

<thread
    name = "..."
  [ abstract = "0 | 1"  ("0") ]
  [ template = "..." ]
  [ context = "..." ]
  [ schedule = "poll | cpu" ]
  [ share = "..."  ("0") ]
    >
    <option>
    <inherit>
    <context>
    <handler>
    <state>
    <action>
</thread>

The thread item can have these attributes:

template
If specified, defines an entity that acts as template for this entity. The template attribute is optional.
abstract
If set, the entity only exists in order to be inherited - no code is generated. The abstract attribute is optional. Its default value is "0". It can take one of the following values:
Value Meaning
0 normal entity
1 abstract entity

name
The name attribute is required.
context
Defines a link to the 'context' item with the corresponding 'name' attribute. The context attribute is optional.
schedule
The schedule attribute is optional. It can take one of the following values:
Value Meaning

cpu:
share
Does this thread share context with another thread? The share attribute is optional. Its default value is "0".

The 'thread context' item

Defines a context block, i.e. a structure that is created for each thread and associated with it:

<context
  [ name = "..." ]
    />

The thread context item has this single attribute:

name
The name attribute is optional.

The 'thread handler' item

Defines a handler for a built-in event, at the thread level.

<handler
    name = "thread new | thread init | thread destroy"
    >
    <argument>
</handler>

The thread handler item has this single attribute:

name
The name attribute is required. It can take one of the following values:
Value Meaning
thread new invoked when the thread is created.
thread init invoked when the thread is initialized.
thread destroy invoked when the thread is destroyed.

The 'thread state' item

State for a single thread.

<state
    name = "..."
  [ abstract = "0 | 1"  ("0") ]
  [ template = "..." ]
    >
    <option>
    <inherit>
    <handler>
    <event>
    <default>
</state>

The thread state item can have these attributes:

template
If specified, defines an entity that acts as template for this entity. The template attribute is optional.
abstract
If set, the entity only exists in order to be inherited - no code is generated. The abstract attribute is optional. Its default value is "0". It can take one of the following values:
Value Meaning
0 normal entity
1 abstract entity

name
The name attribute is required.

The 'thread state event' item

<event
    name = "..."
  [ nextstate = "..." ]
    >
    <action>
</event>

The thread state event item can have these attributes:

name
The name attribute is required.
nextstate
Defines a link to the 'nextstate' item with the corresponding 'name' attribute. The nextstate attribute is optional.

The 'thread action' item

<action
  [ name = "..." ]
    >
    <call>
    <return>
</action>

The thread action item has this single attribute:

name
The name attribute is optional.

The 'call' item

Defines a call action, which stacks the current state and jumps to a new state with the optional event specified:

<call
    state = "..."
  [ event = "..." ]
    />

The call item can have these attributes:

state
Defines a link to the 'state' item with the corresponding 'name' attribute. The state attribute is required.
event
Defines a link to the 'event' item with the corresponding 'name' attribute. The event attribute is optional.

The 'return' item

Returns to the parent state, with the event "return":

<return>

The 'agent event' item

<event
    name = "..."
    />

The agent event item has this single attribute:

name
The name attribute is required.

The 'agent action' item

<action
  [ name = "..." ]
    >
    <call>
    <return>
</action>

The agent action item has this single attribute:

name
The name attribute is optional.

The 'catch' item

Catches an exception:

<catch
    event = "..."
  [ error = "..." ]
    />

The catch item can have these attributes:

error
The error attribute is optional.
event
Defines a link to the 'event' item with the corresponding 'name' attribute. The event attribute is required.

The 'method' item

<method
    name = "..."
  [ event = "..."  ("$(name?)_m") ]
  [ priority = "normal | high"  ("normal") ]
  [ message = "..." ]
    >
    <argument>
    <possess>
    <release>
</method>

The method item can have these attributes:

name
The name attribute is required.
event
Defines a link to the 'event' item with the corresponding 'name' attribute. The event attribute is optional. Its default value is "$(name?)_m".
priority
The priority attribute is optional. Its default value is "normal". It can take one of the following values:
Value Meaning
normal Normal priority
high High priority

message
The message attribute is optional.

The 'possess' item

Code to allow the agent to take possession of the data in the method.

<possess>

The 'release' item

Code to allow the agent to release possession of the data in the method. This code is turned into a callback function for the method queue and is called either on failure to deliver the method, or when the method is destroyed.

<release>

The 'message' item

<message
    name = "..."
  [ priority = "normal | high"  ("normal") ]
    >
    <argument>
    <method>
    <possess>
    <release>
</message>

The message item can have these attributes:

name
The name attribute is required.
priority
The priority attribute is optional. Its default value is "normal". It can take one of the following values:
Value Meaning
normal Normal priority
high High priority

The 'message method' item

<method
    name = "..."
  [ event = "..."  ("$(name?)_m") ]
  [ priority = "..."  ("normal") ]
  [ message = "..." ]
    >
    <argument>
    <possess>
    <release>
</method>

The message method item can have these attributes:

name
The name attribute is required.
event
Defines a link to the 'event' item with the corresponding 'name' attribute. The event attribute is optional. Its default value is "$(name?)_m".
priority
The priority attribute is optional. Its default value is "normal".
message
The message attribute is optional.

Comments

Add a New Comment

Edit | Files | Tags | Source | Print

rating: +1+x

Author

iMatix Corporation <moc.xitami|sloot#moc.xitami|sloot>

Table of Contents

Installing and using OpenAMQ

Introduction to OpenAMQ: This document is an introduction to the concept of business messaging in general, and to OpenAMQ in particular. It is intended for new OpenAMQ users who wish to understand the problems that OpenAMQ solves, and how OpenAMQ can be useful in software applications.

Basic use of OpenAMQ: This document explains how to get OpenAMQ running on your system. It explains how to download the software, how to unpack and build it (if you are using a source package), and how to run basic tests on the resulting software.

Advanced use of OpenAMQ: This guide is for people who need to configure and manage OpenAMQ servers. We explain how to configure and tune an OpenAMQ server, covering these topics: logging, monitoring, high-availability failover, and joining OpenAMQ servers into wide-area federations.

Writing applications

Programming WireAPI: This is the main guide for developers who wish to use OpenAMQ in their applications. We describe WireAPI, the C/C++ API that OpenAMQ provides for accessing AMQP. Expert WireAPI users may wish to read the iMatix iCL guide, but this document is otherwise self-complete.

Programming PAL: This guide is for OpenAMQ developers who need a quick way to write test cases and simple scenarios. We explain the PAL language, an XML scripting tool that gives you a fast way to construct AMQP applications to test routing models, performance and stability tests, and other test cases.

Programming the Console: This document explains how to write applications that automate management of OpenAMQ servers via console automation. The OpenAMQ console automation architecture offers developers different ways of accessing the functionality of the console API and integrating it with their own preferred tools and management facilities.

Technical library

Developer's Guide to ASL: This is a technical guide for protocol developers who wish to use the iMatix ASL framework for the development of connected client-server protocols. ASL is a generic framework that uses a protocol modeling language to construct the whole infrastructure for a given protocol. ASL was built primarily to support AMQP.

Developer's Guide to iCL: This is a technical guide for developers who wish to understand how the iMatix iCL framework works. iCL is a class-oriented modelling language for C applications and is one of the basic frameworks used in iMatix applications such as OpenAMQ.

Developer's Guide to MOP: This is a technical guide for developers who wish to understand how the iMatix code generation frameworks are constructed. We explain the principles of model oriented programming, and the basics of code generation using the iMatix GSL language. This provides essential basic knowledge for anyone intending to modify the OpenAMQ software.

Developer's Guide to SMT: This is a technical guide for developers who wish to understand how the iMatix SMT framework works. To use this guide the reader should be familiar with the iMatix iCL framework, and the iMatix Model Oriented Programming principles.

RFCs

The CML Request for Comments: We describe a generic technique for managing AMQP servers from a remote client application. This technique consists of a standard transport mechanism built over AMQP, and a standard XML language used to exchange information between a management component built-in to the server, and a management application. This is a request for comments, it is not a standard.