Here now is the precise description of the concurrency facilities presented in earlier sections. There is no new material in this section, which serves only as reference and may be skipped on first reading. The description consists of four parts: syntax; validity rules; semantics library mechanisms. It extends the sequential O-O mechanisms developed in the preceding chapters. On first reading you may move to "DISCUSSION", 28.12, page 998.
The syntactic extension involves just one new keyword, separate.
A declaration of an entity or function, which normally appears as
may now also be of the form
x: separate TYPE
In addition, a class declaration, which normally begins with one of class C, deferred class C and expanded class C, may now also be of the form: separate class C. In this case C will be called a separate class. It follows from the syntax convention that a class may be at most one of: separated, expanded, deferred. As with expanded and deferred, the property of being separate is not inherited: a class is separate or not according to its own declaration, regardless of its parents' separateness status.
A type is said to be separate if it is either based on a separate class or of the form separate T for some T (in which case it is not an error, although redundant, for T to be separate --- again the same convention as for expanded). An entity or function is separate if its type is separate. An expression is separate if it is either a separate entity or a call to a separate function. A call or creation instruction is separate if its target (an expression) is separate. A precondition clause is separate if it involves a separate call (whose target, because of rules that follow, can only be a formal argument).
A Separateness Consistency rule in three parts governs the validity of separate calls:
There is also a simple consistency rule on types (not given earlier): In a type of the form separate TYPE, the base class of TYPE must be neither deferred nor expanded.
For a separate call to be valid, the target of the call must be a formal argument of the enclosing routine.
Finally, if an assertion contains a function call, any actual argument of that call must, if separate, be a formal argument of the enclosing routine, if any (separate argument rule).
Each object is handled by a processor, its handler. If the target t of a creation instruction is non-separate, the newly created object will be handled by the same processor as the creating object. If t is separate, the new object will be allocated to a new processor.
Once it has been created, an object will at any time be in either of two states: free or reserved. It is free if no routine is being executed on it, and no separate client is currently executing a routine that uses as actual argument a separate reference attached to it.
A processor will be in either of three states: idle, busy and suspended. It is busy if it is executing a routine whose target is an object that it handles. It becomes suspended if it attempts an unsuccessful call (defined below) whose target is an object that it handles.
The semantics of calls is affected only if one of more of the elements involved --- target and actual arguments --- are separate. The discussion assumes a call of the general form t.f (..., s, ...) where f is a routine. (If f is an attribute, we will assume for simplicity that it is called through an implicit function returning its value.)
Assume first that one or more of the actual arguments are separate, but the target t is not. The call is executed as part of the execution of a routine on a certain object C_OBJ, which may only be in a busy state at that stage. The basic notion is the following:
-------------------------------------------------------------------------------- Definition: Satisfiable call In the absence of CONCURRENCY features (described next), a call to a routine f, executed on behalf of an object C_OBJ, is satisfiable if and only if its target's handler is idle or suspended, and every separate actual argument having a non-void value, and hence attached to a separate object A_OBJ, satisfies the following two conditions: S1 · A_OBJ is free or reserved by C_OBJ. S2 · Every separate clause of the precondition of f has value true when evaluated for A_OBJ and the actual arguments given. --------------------------------------------------------------------------------
If a processor executes a satisfiable call, the call is said to be successful and proceeds immediately; C_OBJ remains reserved, its processor remains busy state, every A_OBJ becomes reserved, the target remains reserved, the target's handler becomes busy, and it starts executing the routine of the call. When the call terminates, the target's handler returns to its previous state (idle or suspended) and each A_OBJ object returns to its previous state (free or reserved by C_OBJ).
If the call is not satisfiable, it is said to be unsuccessful; C_OBJ enters the suspended state. The call attempt has no immediate effect on its target and actual arguments. If one or more earlier unsuccessful calls are now satisfiable, the processor selects one of them to become successful as just described. The semantics does not specify which satisfiable call to select if there is more than one, but does require the processor to select one.
The final semantic change is the definition of wait by necessity: if a client has started one of more calls on a certain separate object, and it executes on that object a call to a query, that call will only proceed after all the earlier ones have been completed, and any further client operations will wait for the query call to terminate. (We have seen that an optimizing implementation might apply this rule only to queries returning an expanded result.) When waiting for these calls to terminate, the client remains in the "reserved" state.
Features of class
CONCURRENCY enable us in some cases to consider that condition
S1 of the satisfiable call definition holds even if A_OBJ has been
reserved by another object (the "holder"), assuming C_OBJ (the
"challenger') has called demand or insist; if as a result
the call is considered satisfiable, the holder will get an exception.
This will only occur if the holder is in a "yielding" state, which it
can achieve by calling yield. To go back to the default
non-yielding state, the holder can execute retain; the boolean
query yielding indicates the current state. The challenger's
state is given by the integer query Challenging which may have
the value Normal, Demanding or Insisting. To
return to the default Normal state the challenger can execute
wait_turn. The difference between demand and insist
affects what happens if the holder is not yielding: with
demand the challenger will get an except