Ada 95 Quality and Style Guide Chapter 6

Chapter 6: Concurrency - TOC - 6.1 CONCURRENCY OPTIONS

6.1.1 Protected Objects

guideline

  • Consider using protected objects to provide mutually exclusive access to data.
  • Consider using protected objects to control or synchronize access to data shared by multiple tasks .
  • Consider using protected objects to implement synchronization, such as a passive resource monitor.
  • Consider encapsulating protected objects in the private part or body of a package.
  • Consider using a protected procedure to implement an interrupt handler.
  • Do not attach a protected procedure handler to a hardware interrupt if that interrupt has a maximum priority greater than the ceiling priority assigned to the handler.
  • Avoid the use of global variables in entry barriers.
  • Avoid the use of barrier expressions with side effects.

  • example

    generic
       type Item is private;
       Maximum_Buffer_Size : in Positive;
    package Bounded_Buffer_Package is
    
       subtype Buffer_Index is Positive range 1..Maximum_Buffer_Size;
       subtype Buffer_Count is Natural  range 0..Maximum_Buffer_Size;
       type    Buffer_Array is array (Buffer_Index) of Item;
    
       protected type Bounded_Buffer is
          entry Get (X : out Item);
          entry Put (X : in Item);
       private
          Get_Index : Buffer_Index := 1;
          Put_Index : Buffer_Index := 1;
          Count     : Buffer_Count := 0;
          Data      : Buffer_Array;
       end Bounded_Buffer;
    
    end Bounded_Buffer_Package;
    
    ------------------------------------------------------------------
    package body Bounded_Buffer_Package is
    
       protected body Bounded_Buffer is
    
          entry Get (X : out Item) when Count > 0 is
          begin
             X := Data(Get_Index);
             Get_Index := (Get_Index mod Maximum_Buffer_Size) + 1;
             Count := Count - 1;
          end Get;
    
          entry Put (X : in Item) when Count < Maximum_Buffer_Size is
          begin
             Data(Put_Index) := X;
             Put_Index  := (Put_Index mod Maximum_Buffer_Size) + 1;
             Count := Count + 1;
          end Put;
    
       end Bounded_Buffer;
    
    end Bounded_Buffer_Package;
    
    

    rationale

    Protected objects are intended to provide a "lightweight" mechanism for mutual exclusion and data synchronization. You should use a task only when you need to introduce explicitly a new, concurrent thread of control (see Guideline 6.1.2).

    Protected objects offer a low overhead, efficient means to coordinate access to shared data. A protected type declaration is similar to a program unit and consists of both a specification and a body. The data to be protected must be declared in the specification, as well as the operations that can be used to manipulate this data. If some operations are only allowed conditionally, entries must be provided. Ada 95 rules require that entry barriers be evaluated at the end of procedure calls and entry calls on protected objects. Entry barriers should avoid referring to global variables so that the underlying assumptions of the state of the protected object are not violated. Protected procedures and entries should be used to change the state of a protected object.

    Most clients of an abstraction do not need to know how it is implemented, whether it is a regular abstraction or a shared abstraction. A protected type is inherently a limited type, and you can use protected types to implement a limited private type exported by a package. As pointed out in Guideline 5.3.3, abstractions are best implemented using private types (possibly derived from controlled types) or limited private types, providing appropriate operations that overcome the restrictiveness imposed by the use of private types.

    The Rationale (1995, §9.1) describes the interrupt handling features that make the protected procedure the recommended building block:

    A protected procedure is very well suited to act as an interrupt handler for a number of reasons; they both typically have a short bounded execution time, do not arbitrarily block, have a limited context and finally they both have to integrate with the priority model. The nonblocking critical region matches the needs of an interrupt handler, as well as the needs of non-interrupt-level code to synchronize with an interrupt handler. The entry barrier construct allows an interrupt handler to signal a normal task by changing the state of a component of the protected object and thereby making a barrier true.

    When using protected procedures for interrupt handling, you must ensure that the ceiling priority of the handler is at least as high as the maximum possible priority of the interrupt to be handled. With priority-ceiling locking, the delivery of an interrupt with a higher priority than the ceiling priority of the handler will result in erroneous execution (Ada Reference Manual 1995, §C.3.1).

    A global variable could be changed by another task or even by a call of a protected function. These changes will not be acted upon promptly. Therefore, you should not use a global variable in an entry barrier.

    Side effects in barrier expressions can cause undesirable dependencies. Therefore, you should avoid the use of barrier expressions that can cause side effects.

    See also Guideline .

    exceptions

    If the client of the abstraction containing the protected object must use a select statement with an entry call, you must expose the protected object on the package interface.


    < Previous Page Search Contents Index Next Page >
    1 2 3 4 5 6 7 8 9 10 11
    TOC TOC TOC TOC TOC TOC TOC TOC TOC TOC TOC
    Appendix References Bibliography