next up previous contents index
Next: ValuesTypes, and Constants Up: The Glish 2.6 Previous: Introduction

An Example of Using Glish

For an idea of the sorts of problems Glish is meant for and how it's used to solve them, consider a simple example   where we want to repeatedly view readings generated by an instrument attached to a remote computer called ``mon". Suppose we have a program measure that reads values from the special hardware device and converts them into two floating-point arrays, x and y. measure needs to run on the remote host ``mon" because that's where the special hardware resides. We have another program, display, for plotting the x/y data, which we want to run on our local workstation. display also has a ``Take Measurements" button that we can click on to instruct the hardware to take a new set of measurements.

  figure220
Figure 2.1: Simple Two-Program Distributed System

The first problem we're interested in is simply to connect together measure and display so that when measure produces new values they're shown by display, and when we click the display's button measure goes off and reads new values. Figure 2.1 illustrates the flow of control and data: display tells measure to take measurements, and measure informs display when new measurements are available.

To implement even this simple system under Unix requires constructing a session-layer protocol  which then has to be implemented on top of sockets or RPC. When using Glish, though, the protocol and the communication mechanism are built-in.  Every program in a Glish system communicates by generating events, messages with a name and a value.  For our simple system we might write measure so that whenever it has new readings available it generates an event called ``new_data". The value of the event will be a record with two elements, x and y, the two arrays of numbers it has computed from the raw measurements. We would write display so that when it receives a new_data event it expects the value of the event to be a record with at least x and y fields; it then plots those values. Similarly, when we push the ``Take Measurements" button display will generate a take_data event, and whenever measure receives a take_data event it will get a new set of readings and generate a new new_data event.

Here is a Glish script that when executed creates the two processes, one remotely, and conveys their messages to each other :

    m := client("measure", host="mon")
    d := client("display")

    whenever m->new_data do
        d->new_data( $value )

    whenever d->take_data do
        m->take_data( $value )
When Glish executes the first two lines of this script it creates instances of measure (running on the host ``mon") and display (running locally) and assigns to the variables m and d values corresponding to these Glish clients. Executing the next line:
    whenever m->new_data do
specifies that whenever the client associated with m generates a new_data event, execute the following statement:
        d->new_data( $value )
This statement says to send a new event to the client associated with d. The event's name will be new_data and the event's value is specified by whatever comes inside the parentheses; in this case, the special expression $value, indicating the value of the most recently received event (measure's new_data event).

The last two lines of the script are analogous; they say that whenever display generates a take_data event an event with the same name and value should be sent to measure.

Our system could easily be a bit more complicated. Suppose that prior to viewing the measurements with display, we first want to perform some transformation on them. The transformation might for example calibrate the values and scale them into different units, filter out part of the values, or FFT the values to convert them into frequency spectra. Rather than building the transformation into measure, we would like our system to be modular , so we use a separate program called transform.

  figure271
Figure 2.2: Three-Program Distributed System

Figure 2.2 shows the flow of control and data in this new system. measure sends its values to transform; transform derives some transformed values and sends them to display; and display tells measure when to take more measurements. With Glish it's easy to accommodate this change :

    m := client("measure", host="mon")
    d := client("display")
    t := client("transform")

    whenever m->new_data do
        t->new_data( $value )

    whenever t->transformed_data do
        d->new_data( $value )

    whenever d->take_data do
        m->take_data( $value )
The third line runs transform on the local host and assigns a corresponding value to the variable t. The first whenever forwards new_data events from measure to transform; the second whenever effectively forwards transform's transformed_data events to display, but changes the event name to new_data, since that's what display expects. The third whenever is the same as before.

  figure298
Figure 2.3: Conceptual Event Flows vs. Actual Flows

An important point in this example is that while conceptually control and data flow directly from one program to another, in reality all events pass through the Glish interpreter.  Figure 2.3 illustrates the difference. Here solid lines show the paths by which events actually travel, while dashed lines indicate the conceptual flow . While this centralized architecture doubles the cost of simple ``point-to-point" communication, it buys enormous flexibility. For example, suppose sometimes we want to use transform before viewing the data and other times we don't. We add to display another button that lets us choose between the two. It generates a set_transform event with a boolean value. If the value is true then we first pass the measurements through transform, otherwise we don't.

To accommodate this change in our Glish program we could add a global variable do_transform to control whether or not we use transform:

    m := client("measure", host="mon")
    t := client("transform")
    d := client("display")
    do_transform := T

    whenever m->new_data do
        {
        if ( do_transform )
            t->new_data( $value )
        else
            d->new_data( $value )
        }

    whenever t->transformed_data do
        d->new_data( $value )

    whenever d->take_data do
        m->take_data( $value )

    whenever d->set_transform do
        do_transform := $value
We initialize do_transform to T, the boolean ``true" constant. We change it whenever display generates a set_transform event (see the last two lines). When measure generates a new_data event we test the variable to determine whether to pass the event's value along to transform or directly to display.

Furthermore, if the data transformation done by transform is fairly simple, we could skip writing a program to do the work and instead just use Glish . For example, suppose the transformation is to find all of the x measurements that are larger than some threshold, and then to set those x measurements to the threshold value and the corresponding y measurements to 0. We could do the transformation in Glish using:

    m := client("measure", host="mon")
    d := client("display")
    do_transform := T

    if ( len(argv) > 0 )
        thresh := as_double(argv[1])
    else
        thresh := 1e6

    whenever m->new_data do
        {
        if ( do_transform )
            {
            too_big := $value.x > thresh
            $value.x[too_big] := thresh
            $value.y[too_big] := 0
            }

        d->new_data( $value )
        }

    whenever d->take_data do
        m->take_data( $value )

    whenever d->set_transform do
        do_transform := $value
Here we first check to see whether any arguments were passed to the Glish script and if so we initialize thresh to be the first argument interpreted as a double precision value. If no arguments were given then we use a default value of one million.

Now whenever measure generates a new_data event and we want to do the transformation, we set too_big to a boolean mask  selecting those x elements that were larger than thresh. We then set those x elements to the threshold, zero the corresponding y elements, and pass the result to display as a new_data event. We have eliminated the need for transform.

Finally, for situations in which performance is vital Glish provides point-to-point links between programs.  The link statement     connects events generated by one program directly to another program. The unlink statement     suspends such a link (further events are sent to the central Glish interpreter) until another link. Here is the last example written to use point-to-point links:

    m := client("measure", host="mon")
    d := client("display")

    link m->new_data to d->new_data

    if ( len(argv) > 0 )
        thresh := as_double(argv[1])
    else
        thresh := 1e6

    whenever m->new_data do
        {
        too_big := $value.x > thresh
        $value.x[too_big] := thresh
        $value.y[too_big] := 0
        d->new_data( $value )
        }

    whenever d->take_data do
        m->take_data( $value )

    whenever d->set_transform do
      {
      if ( $value )
        unlink m->new_data to d->new_data
      else
        link m->new_data to d->new_data
      }
We now no longer need the do_transform variable. Instead we initially create a link for measure's new_data events directly to display. Whenever display sends a set_transform event requesting that the transformation be activated, we break the direct link between measure and display. Now when measure generates new_data events they will be sent to Glish, which will then transform the data and pass it along to display.

These examples illustrate the main goals of Glish : making it easy to dynamically connect together processes in a distributed system, and providing powerful ways to manipulate the data sent between the processes. One other important point is that because measure, transform, and display are all written in an event-driven style, each of them can be easily replaced by a different program that has the same ``event interface".  For our own work (scientific programming) we often want to replace measure with simulate (a program that simulates the quantity being measured), display with a non-interactive program once we have ironed out the measurement cycle, and transform with a variety of different transformations. We also might want to run measure and simulate together, so we can compare simulate's model with the actual phenomenon measured by measure. The ability to quickly ``plug in" different programs in this fashion is one of Glish's main benefits.


next up previous contents index
Next: ValuesTypes, and Constants Up: The Glish 2.6 Previous: Introduction

Thu Nov 13 16:44:05 EST 1997