15 Concurrency support


Ada is one of the few mainstream languages that supports concurrency from within the language. This has the benefit of stable semantics, and therefore portability between computers. The task and rendezvous mechanism of Ada83 has been enhanced through the introduction of protected objects and greater considertion for real time systems.


An Ada program consists of at least one, and possible more tasks, which run concurrently. The tasks run independently of each other, communication/synchronisation is achieved by high level concepts such as the rendezvous or with protected objects. With an abstraction level higher than simple semaphores, the rendezvous and protected objects come supplied with a range of options for guards (as Dijkstra postulated), timeouts and (in Ada95) to selectively requeue clients and abort operations.

The death of a task does not necessarily affect the operation of other tasks, except if they attempt to communicate with it. There is no specialised task, even the main procedure of the Ada program can end, with other tasks continuing.

The rendezvous

As one mechanism for tasks to safely communicate and synchronise with each other, the rendezvous is conceptually simple. Tasks publish entry points at which they are prepared to wait for other tasks. These entry points have a similar semantics to procedures; they can have parameters of in, inout and out mode, with default parameter values. A task wishing to rendezvous with a second task simply makes a procedure-like rendezvous call with an entry; Either is held up until they can both communicate. The rendezvous is non symmetrical - one task is viewed as a server and cannot initiate a rendezvous.

When this program is run the task single_entry is started. If succeful the parent task is also activated. If 'random' returns a value less than 50, then after the initial delay the main task waits for the second task, and vice versa if it returns greater than 50. Once the two tasks have rendezvoused, they depart on their separate ways. The main task finishes, the second task continues until it too finishes. The program then terminates.

The same program can be modified to include parameters in the entry call. In this example, the two tasks swap duration times for the delay statements. However they may still not finish together as the quicker task to the rendezvous will still have to wait for the second task.

A task can have several rendezvous'. If it waits for a rendezvous and there is no possible task that can rendezvous with, then it will abort with a tasking_error exception.

Rendezvous' can be treated in a manner very similar to procedure calls. They can be in loops, if statements, subprograms etc. Typically however they are structured in a high level loop; this allows a client architecture in which after servicing a rendezvous, and performing some processing, it goes back and waits for another rendezvous.

This situation can be impoved having multiple entry points, and making use of the select statement in conjunction with the accept statement.

The select statement allows the task to wait on several different entry points. This example shows the increment entry without any associated processing. The decrement entry forces the calling task to wait while the decrement takes place. The terminate alternative causes task multiple_entry to finish if it is uncallable from any other task.

The select statement can have a selective wait, unconditional alternative, a terminate or none of these. The previous example showed the terminate. The unconditional alternative is shown below.

In this example, the task stops only briefly to inspect if another task is waiting to rendezvous. If not it goes off and does some processing.

The selective wait allows the task to wait for a given time, after which it gives up and continues on with other actions.

Here the task will wait for at least 3.0 seconds for another task. If none comes along it will call perform_some_processing.

Similarly the client task can either wait unconditionally for a rendezvous, it can not wait at all (impatient), or it can specify a time out after which it will give up on a rendezvous.

Task types

All of the above tasks are of an anonymous type. Ada allows you to create a task type (via a special and non consistent syntax from all other type declarations) and to declare objects of the type. They can be declared dynamically (through an allocater) as well as local variables. They can be composed into other objects - you could have an array of tasks, or a record, a component of which, is a task.

Problems with the rendezvous mechanism

The rendezvous mechanism is a synchronisation as well as a communication mechanism. Tasks that should normally run asynchronously and which want to pass data between them are required to stop and "have a chat", instead of simply leaving the data for the other task. The solution to this problem is usually to creat an intermediary task that manages the data flow, and, the designer hopes, is always available to respond to a rendezvous from either task. It sits between the two tasks, acting as an emissary for both.

Protected Objects

A simpler solution can be provided in Ada95 which has a concept of a protected object or type. Protected objects provide for task safe access to data via entries and guards, without the overhead of a separate task being created. The subprograms and entries inside a protected object are executed in the context of the calling task. As with tasks types you can create arrays of protected type, and compose other objects from them.

All the examples in this section come from Introducing Ada9x, John Barnes.

An example of a protected entry follows

Functions are only allowed read access to the data, procedures can manipulate them in any way they wish. Procedure calls are exclusive, that is only one task can access a procedure at any one time. Multiple reads can occur concurrently.

Another example is a semaphore, to provide exclusive access to resources.

When called the barrier on the entry is queued. If it is false, the task is queued, pending it becoming true.

to the index...