Next: Using the Emulator Up: The Concert Tutorial Previous: Using the Concert

More Complex Examples

So far, a simple program has been used to demonstrate some features of the Concert Emulator and of Concert programs. Of course, Concert programs can do much more than say hello world, and this section will introduce a few somewhat more complex programs, and the language constructs that they use. Also, the notions of concurrency and concurrency control in Concert will be explored in more detail.

Fibonacci Numbers

The tree-recursive method of calculating Fibonacci numbers is another canonical example program, at least in languages that support recursion. The Concert implementation of this is shown in Figure 8

New constructs

The first form of this program is a method declaration. The word method is a syntactic keyword, the name integer is the class for which the method is defined; the name fib is the name of the method, and the () is the argument list, in this case empty. A method in Concert is similar to a method in CLOS or Smalltalk or a member function in C++.

In this case, the method just recursively computes a Fibonacci number by computing the previous two and adding them together. Every operation in Concert is a message send, and the message format is (selector receiver arguments); for example, the (< n 2) form sends the message < to n with the argument 2. Note that user defined messages, such as fib, work in exactly the same manner.

The if form is the same, both semantically and syntactically, as its cognate in Lisp. The first form is evaluated, and if it is true then the second form is evaluated, otherwise the optional third form is evaluated.

Like all predicates in the language, < is defined to return true if it is true and false otherwise. As in Pascal, these two constants are built into the language and do not necessarily correspond to numbers as they do in, for example, C.

Note the reply form in the fib method. As was mentioned with hello world, reply is how a return value is specified in Concert. Thus, it is like the return statement of Lisp or C, except that execution does not stop at a reply statement.

The second form in this program is special. The class osystem is a value that has just one method, the initial_message. This method is where execution starts in any Concert program, and the reply from this method is what the program returns as a result.

A Simple Buffer Object

The first program implements a simple buffer of size one. The operations supported are to insert an item, to delete an item and check if the buffer is full. The complete program is shown in Figure 9, and is explained in detail below.

New constructs

The first form is a class declaration; this is similar to such declarations in C++, CLOS, Smalltalk or any other object oriented language. The term class is a syntactic keyword; tiny_buffer is the class name; buf and full_flag are the instance variables of the class. The code inside the initial form corresponds to the constructor in C++; it is executed whenever an instance of class tiny_bufferis created. In this case, it ensures that a new buffer starts out empty.

Note the form set_full_flag; this is an automatically defined method that sets the instance variable full_flag. In general, every instance variable of every class will have a predefined method of this form to set its value.

Note the form buf in the method declaration for get; this is an automatically defined method that reads the instance variable buf. In general, every instance variable of every class will have a predefined method of this form to read its value.

The next form is another method declaration; this method gets the value in the buffer, if there is one. It first calls isfull to see if there is anything to get, and if there is it replies with the buffered value and sets the full_flagto 0 indicating the buffer is empty. If the buffer is empty, it returns the value EMPTY.

The code is straightforward, but there is one exotic construct: conc. This form means that all of the statements inside it can be executed concurrently; in this case setting the full_flagto empty and replying with the buffered value can happen simultaneously, as they will not interfere with each other. The conc block finishes only after all of the statements inside it have finished.

The method declaration for put contains nothing new, but is does provide an example of a method that takes an argument, in this case item. This is specified as in Common Lisp, C++ and numerous other languages with a parenthesized list.

The initial_message is our old friend from the the Fibonacci example, but it contains two new language constructs that need explaining. They are let and seq

The let construct is just like its cognate in Lisp. It creates new local variables and binds them to their corresponding expressions. In this case, a new variable named buf is created and bound to a new instance of the tiny_bufferclass.

The seq construct sequentializes the forms within it. These forms are done one after the other, and each one waits for the prior one to finish before it starts. Thus, the value 5 is put into the buffer before it is removed.

Load this program into Emacs, and then run the emulator and load and run this program, which called simple-buffer-example.ca and is in $CONCERT_ROOT_PATH/examples/small.

Controlling Concurrency

On thing to note about this code is that there is no explicit concurrency control anywhere. There is no obvious mechanism to prevent race conditions and errors if, say, get and put were called at the same time on the same object. In many concurrent languages, most of the code would consist of such mechanisms.

The Concert system uses object-based concurrency control; only one method can operate on any object at any given time. There are special methods that can violate this mechanism, but, in general, this rule is the way concurrency control works in Concert. It is easy to see that this will indeed prevent weird errors in this case. For a buffer object, get and put are the operations that one would like to be atomic; placing them both in individual methods ensures that this will, in fact, be the case.

Try redefining the initial_messageto use a conc block and multiple gets and puts; make sure each put takes a different argument. Also, send the result of each get to the console, using a message to the (global console) as in the hello world program. Try running the program under each of the above mentioned scheduling policies, and observe the results. An example is shown in Figure 10

The results of the gets can be different each time, but they will all be valid. You will never see the a value you did not put there nor the same value twice. Either of these conditions could easily occur without object based concurrency control.

A Distributed Counter

A novel feature of the Concert System is the ability to create multi-access abstractions called aggregates. They look just like objects, but they are actually made up of several objects, allowing them to handle multiple requests concurrently. They are the primary way of building abstractions that do not serialize their requests. A simple example of this is a counter that can handle multiple count messages simultaneously, which is illustrated in Figure 11

The main new constructs in this example all revolve around aggregates; the first one is the aggregate declaration, which resembles a class declaration. The differences are the syntactic keyword aggregate and the initial form. While the initial form is optional for class declaration, it is mandatory for aggregates because the first form is a number that specifies the size of the aggregate.

Sibling is a message used to access different members, called representatives, of an aggregate. The message (sibling aggregate n) will return the nth representative of aggregate. This is typically used within an aggregate so that different representatives can cooperate to implement some common abstraction, a counter in this case.

Handlers are methods defined on aggregates; the name difference is merely a syntactic detail, and they just the same as far as concurrency control and everything else is concerned. Handlers have three special variables defined that do not exist in methods: group, groupsize and myindex. Group is the name of the aggregate as a whole (self in a handler refers to the representative); groupsize is the size of the aggregate; myindex is the number of this representative.

More language information

The Concert System implements the Concurrent Aggregates language; the original paper describing the language and the motivation behind it is [1], and a full description of the language, complete with numerous examples, is [2].



Next: Using the Emulator Up: The Concert Tutorial


Julian Dolby
Vijay Karamcheti
John Plevyak
Xingbin Zhang
Concurrent Systems Architecture Group