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.