Object-based concurrency
\label{des/per:obj}
\parindex{object based concurrency}
Conceptually, objects are independent entities
and the paradigm of method call by message passing
allows in a natural way for concurrency.
\nop{
The notion of an object based language covers a wide range
of languages, including Ada, POOL, Emerald, Smalltalk and C++.
An object may be characterized as an entity that has a collection of
operations and a state that remembers the effect of
operations.\ftn{
In accordance with the literature, we speak of object based languages
and reserve the phrase object oriented language for
the languages offering inheritance as an additional mechanism.
See [We87] for a detailed discussion of the
dimensions of object based language design.
}
C.f. [We87].
Apart from providing a construct to group operations,
and a facility for defining an abstract interface
to a collection of data, an object
provides an additional protection mechanism since
access and modification of the data
it encapsulates is allowed only through the use of the operations
defined for the object, the so-called methods.
}
However, even when considering method calls as (synchronous) message passing,
object based languages may fit well in a sequential model
of computation,
assuming that an object is passive except when answering a method call.
Extending the sequential object model to include parallelism
may be achieved simply by allowing an object to be active
on its own account, that is when not answering a message.
As alternative ways to obtain parallelism,
we mention the possibility to employ
asynchronous communication as
encountered in the Actor languages [He77], [Ag86]; or
to add processes as an orthogonal concept to
the language.
A drawback of the last solution however is the need to
provide extra facilities for synchronization and mutual
exclusion. See also [GR88].
Active objects seem in this respect to be a much
more natural solution,
since such protection is already offered by the method interface,
assuming that only one method is answered at a time.
C.f. [Am89b].
Active objects
\parindex{active objects}
The notion of active objects, that may be created dynamically,
has been adopted by the language POOL.
For a more extensive description of POOL
see section \ref{impl/comp:lang}.
Each object may have own activity, called the body of
the object, that is started as soon as the object is created.
The own activity of the object is interrupted
to answer a method call when a so-called answer statement
is encountered.
The answer statement introduces a certain degree of
non-determinism since, although a number of method calls may be
considered acceptable, only one of these will be chosen.
The communication model of method calls in POOL has been derived
from the rendez-vous as encountered in Ada. See below.
The rendez-vous, as an interaction between processes,
has a two-way nature.
It generalizes in this respect the primitives provided by for example CSP,
that allow only one-directional point-to-point communication.
In the terminology of POOL, the rendez-vous model is based
on three concepts:
a method declaration, which is like the declaration of a
procedure having the right to access the private data
of an object;
a method call, which is like a procedure call but with an object
as an additional parameter;
and an answer statement, to interrupt the
own activity of an object and to state the willingness
to accept a particular method call.\ftn{
In the context of Ada one speaks of respectively an
entry declaration, an entry call and an
accept statement.
}
Answer statements allow to suspend the acceptance of a method call,
dependent on the state of the object.\ftn{
Even stronger acceptance conditions may be imposed in Concurrent C
that allows to inspect the actual parameters of a call
to determine acceptance.
}
The rendez-vous in Ada
\parindex{rendez-vous}
We will explain the rendez-vous concept in somewhat more detail
by looking at some simple Ada program fragments, taken from [Perrott87].
\parindex{Ada -- task}
In Ada a process is called a task.
As an example of the specification of a task, consider
the declaration of a (single-element) buffer.
\yprog{task}{
task buffer is
deposit( c : in character );
remove( c : out character );
end buffer
}
The declaration specifies that a buffer allows two operations,
namely an operation to deposit a character and an operation to remove
a character.
An implementation of the buffer is given by the following definition
\yprog{body}{
task body buffer is
ch : character;
begin
loop
accept deposit( c : in character) do ch := c end
accept remove( c : out character) do c := ch end
end loop
end buffer
}
The body of a buffer specifies the own activity of a buffer,
which is given by the succession of two accept statements,
to accept subsequently a deposit call and a remove call,
repeated indefinitely.
To illustrate the use of the buffer, we assume the existence of a
producer task in which the statement
buffer.deposit(c);
for a character c, occurs and a consumer task in which
a statement
buffer.remove(c);
occurs.
The first rendez-vous then takes place between the producer
and the buffer when the buffer accepts the call for deposit.
After that, the buffer has no other choice then to accept a remove
call, which must come from the consumer task.
It is important to note that
the rendez-vous in Ada is of a synchronous nature.
Only when the remote procedure call is completed may both tasks
resume their activity.
From the implementation of the body of the
buffer task, we can infer that the buffer actually is a one-element
buffer.
However, the implementation may be changed (without affecting the task specification)
by using for example an array of characters.
In that case we may wish to use a more sophisticated protocol
for accepting a call, a protocol that allows to take into account the number
of elements the buffer contains.
Ada offers a so-called select statement and a construct enabling
the conditional acceptance of a call by which such a protocol can be implemented.
The non-determinism allowed by these constructs is local,
since it is solely dependent on the internal state of the task.\ftn{
In constrast, CSP supports global non-determinism
by enabling a programmer to impose conditions
with respect to the environment of the process,
for instance to check whether a communication event may occur.
}
Multiple threads
\parindex{multiple threads}
As another object-based distributed language,
we wish to mention Emerald.
Just as POOL,
Emerald offers the possibility to create active objects
dynamically.
An important difference between POOL and Emerald however is
that Emerald allows multiple threads of control:
one object can be active answering a number of method calls.
Moreover, the processes created for answering a method call
run in parallel with the process executing
the own activity of the object.
A monitor construct is provided to enable synchronization
and protection when accessing local data shared by processes
active for the object.
Allocation
\disindex{allocation}
Object-based concurrency is most suitable for large-grain parallelism.
Large-grain parallelism results in processes of considerable size.
To the extent that these processes may run independently,
speed-up can be obtained by allocating these processes
to distinct processors.
The language POOL enables the programmer
to locate a newly created object
on a particular
processor by so-called pragmas, listing the set of processors
from which the system may choose.
In addition to a facility for mapping objects and
processes to processors, Emerald supports the migration of objects
and processes by allowing them to move,
or to be moved, from one processor to another.