What You Should Know About Concurrency — Part III — Implementation
By: Ismayil Shahaliyev
Tags: Programming, Computer Science, Concurrency, Programming Languages, Software Development

Ada Concurrency

We will focus on synchronous message passing. Ada tasks consist of two syntactic parts: specification and body. The interface of tasks is points of entry or where the messages are accepted. Just like message parameters, entry points are described in the task’s specification part.

task T begin

entry E (parameters)

end task T

Note: In order to make the explanation easier for me and clearer for you, I will use pseudo-codes that are very similar to Ada syntax. The actual codes written in Ada you can find in the book.

A task’s body entry points can be specified by the accept clause, with the help of reserved words accept & end.

accept entry E (parameters) do

[rendezvous operations]

end entry E

Just like during the rendezvous, when an accept clause doesn’t want to accept a message, the sender's task is suspended. Similar to some other synchronization tools which we have discussed in the previous part (e.g. Sysyphus), an accept clause keeps the suspended tasks in its queue.

If you still have difficulties while clearly understanding what rendezvous is, then I advise you to read this and this. It’s an important term that needs a perfect understanding. However, simply, synchronous rendezvous is when both the sender and receiver agree to proceed synchronously.

Tasks that have graduated from a drama school are called actor tasks. They don’t need entry points, and hence, do not wait for a rendezvous to proceed. By contrast, a server task consists of a code within an accept clause and nothing outside. It can only react to other tasks.

If an Ada task sends a message, it needs to know the entry name of that task. However, in contrast to CSP, a task entry doesn’t have to know the name of the task from which it will accept messages.

Tasks can be statically or dynamically created. Statically created tasks start their execution at the same time as the statements where the task is declared. If the task is declared in the main program, the execution of the task begins at the same time as the first statement in the main program.

A task with more than one entry point, requiring them to receive messages in any order, uses a select statement which encloses the entries.

task body task T begin

loop

select

accept entry E1 (parameters) do

[rendezvous operations]

end entry E1

or

accept entry E2 (parameters) do

[rendezvous operations]

end entry E2

end select

end loop

end task T

The possible code between an accept clause and or (or between an accept clause and end select) is called extended accept clause, which is executed only after the preceding accept clause and is not a part of rendezvous.

As a way of cooperation synchronization, a guard in the form of a when (when not) clause may delay rendezvous. If the accept clause is closed, that is — if its boolean guard returns false, then no rendezvous can take place.

when Full(Buffer) =>

accept entry E (parameters) do

[rendezvous operations]

end entry E

A special statement, terminate, can be used at the select clause, which, if selected would mean that the task has finished its execution but not terminated.

But now we need to understand how competition synchronization — mutually exclusive access to a resource — can be implemented at Ada. If a task controls access to a resource, then competition synchronization can be achieved by declaring the data structure within the task, as only one accept clause can be active at a time.

However, if tasks are nested, then the nested task may also access the shared data structure. Therefore, in order to keep the integrity of the data, synchronized tasks should not define other tasks.

Section 13.6.3 introduces two great examples of competition synchronization, which I advise you to have a look at. There you will also find an example of an extended accept clause if it was not very clear for you from the definition, as it wasn’t for me.

Protected objects is an important concept of Ada 95. Although enclosing the data structure within a task implies competition synchronization, it undermines the efficiency of the rendezvous mechanism. Therefore, the usage of protected objects is an alternative (and better) way of implementing mutually exclusive access to a resource. It makes the code simpler and more efficient.

A protected object is more like a monitor, which is described in the previous part. It can be accessed either by subprograms or by entries that are similar to accept clauses.

Protected subprograms can arrive in two forms: protected procedures & protected functions. Protected procedures provide mutually exclusive read-write access, whereas protected functions provide concurrent read-only access to the data of the protected object.

Within the body of a protected procedure, the current instance of the protected unit is a variable, whereas, in protected function, it is defined as a constant which allows concurrent read-only access. Again, for the example, I highly encourage you to refer to the book section 13.6.4.

Entries differ from subprograms by their capacity for guard implementation. They use the reserved word entry instead of accept.

Finally, let’s briefly note that, in distributed systems, message passing is a better model of concurrency, when processes can execute parallelly in separate processors.