Mutual exclusion and deadlocks

Một phần của tài liệu Burns, wellings concurrent and real time programming in ada (Trang 137 - 140)

An_Op.Directory_Enquiry("STUART JONES",

"10 MAIN STREET, YORK", Stuarts_Number);

-- log the cost of a directory enquiry call else

-- phone up his parents and ask them, -- log the cost of a long distance call end select;

...

end loop;

end Subscriber;

Clearly, it is possible for two clients to issue simultaneous conditional entry calls, or timed entry calls with immediate expiry times. There is therefore an obli- gation on the Ada run-time support system to make the commit operation an indi- visible action so that only one client task can see the state of the server at any one time.

A conditional entry call should only be used when the task can genuinely do other productive work if the call is not accepted. Care should be taken not to use polling, or busy-wait solutions, unless they are explicitly required.

Important note:

Note that the conditional entry call uses an ‘else’, the timed entry call an ‘or’. Moreover, they cannot be mixed, nor can two entry call statements be included. A client task cannot therefore wait for more than one entry call to be serviced (without using an asynchronous select statement – see Subsection 9.4.3).

6.10 Mutual exclusion and deadlocks

A number of the inherent difficulties associated with concurrent programming were considered in Chapter 3. Of paramount importance is ensuring the integrity of re- sources that should not be accessed by more than one task at a time (non-concurrent resources). This integrity is usually assured by defining a critical section of code that must be protected by mutual exclusion. With a server task, mutual exclusion can easily be constructed:

task type Server is entry Ặ..);

entry B(...);

...

end T;

task body Server is

-- The resource is represented by the definition -- of some appropriate data structure.

begin loop

select

accept Ặ..) do ...

end A;

-- housekeeping or

accept B(...) do ...

end B;

-- housekeeping or

...

end select;

-- housekeeping end loop;

end Server;

Each task of type Serverwill define a new resource. For each of these re- sources, although entriesA,Betc. give access, the semantics of the task body (with or without a select statement) ensure that only one accept statement at a time can be executing. The accept statement is itself the critical section. As long as the resource is defined within the task body, and is not accessible to ‘subtasks’ defined within the same body, then mutual exclusion is ensured. Mutual exclusion does not, however, extend to including the evaluations of the actual parameters to an entry call. The evaluations of such parameters in two distinct entry calls may therefore interfere with each other if shared variables are used directly or are influenced by side effects.

Deadlocks are another matter. Consider the following code:

task T1 is task T2 is

entry A; entry B;

end T1; end T2;

task body T1 is task body T2 is

begin begin

T2.B; T1.A;

accept A; accept B;

end T1; end T2;

Clearly, each task will be placed on an entry queue for the other task. They will therefore not be in a position to accept the outstanding call. A task can even call its own entries and deadlock! Guidelines can reduce the possibility of deadlocks but inherent deadlocks should (must) be recognised early in the design of systems and evasive action taken.

6.10 Mutual exclusion and deadlocks 123

Important note:

The following points are pertinent:

Tasks should be constructed to be either active entities or servers.

Active tasks make entry calls but do not have entries.

Server tasks accept entry calls but make no calls themselves.

Entry calls from within a rendezvous should only be used when absolutely necessary.

Where a collection of servers is accessed by a number of tasks, then (1) Tasks should use servers one at a time if possible.

(2) If (1) is not possible, then all tasks should access the servers in the same predefined order.

(3) If (1) and (2) are not possible, then the server tasks must be designed to take appropriate remedial action. For example, resources could be preemptively removed from active tasks using a time-out structure.

With deadlocks, there is no substitute for proving that they cannot occur in the first place.

To ensure liveness, select statements can be constructed to force the system to examine each queue in turn:

loop select

accept A;

else null;

end select;

select accept B;

else null;

end select;

select accept A;

or

accept B;

end select;

end loop;

This structure would, of course, become very tedious if a large number of entries were involved. The final select statement is needed to delay the server task if no outstanding entry calls exist (otherwise the task would loop around and poll the entry queues).

Important note:

If mutual exclusion is provided by the use of protected objects and the priority ceiling protocol is in operation (see Section 13.3), then deadlock-free behaviour is guaranteed.

Một phần của tài liệu Burns, wellings concurrent and real time programming in ada (Trang 137 - 140)

Tải bản đầy đủ (PDF)

(477 trang)