6.4 Protected Units and Operations

[ Table of Contents ] Prev ] Chapter Overview ] Next ] [ Glossary/Index ]

A protected unit is a program unit that defines either a protected object or a protected type. A protected object provides coordinated access to shared data through calls on its visible protected operations. There are three kinds of protected operations: protected procedures, protected functions, and protected entries. The general idea is to protect against simultaneous manipulation of the protected data elements. Thus, if a protected procedure or entry has been called, no other operations are permitted until that operation is concluded. However, multiple functions may be in process at the same time because functions do not change the values of the encapsulated data components.

The two forms of a protected unit are shown below. When the first form is used, a one-of-a-kind protected object of an anonymous type is defined. When the second form is used, one can subsequently declare protected objects of the named type. There is always a private part of the declaration, where components of the shared data structure are defined.

Expanded Symbol - Object

General Form of a Protected Object

Im6-5a.gif (2978 bytes)

protected My_Object is
  procedure My_Procedure(params);
  function My_Function(params);
  entry My_Entry(params);
private
  -- component declarations
  -- internal entries
end My_Object;
-----------------------------
protected body My_Object is
  -- implementations
end My_Object;

Expanded Symbol - Type

General Form of a Protected Type

Im6-5b.gif (2926 bytes)

protected type My_Type is
  procedure My_Procedure(params);
  function My_Function(params);
  entry My_Entry(params);
private
  -- component declarations
  -- internal entries
end My_Type;
-----------------------------
protected body My_Type is
  -- implementations
end My_Type;  

There may be zero or more procedures, zero or more functions, or zero or more entries, but there must be at least one protected operation. For each operation declared, there must be an operation body implemented in the body of the protected unit. Thus, there are entry bodies, and every entry body has a barrier condition (similar to the guard associated with an accept alternative in a task body), as shown in the example program below. The conditions on the entry bodies are re-evaluated each time the "state" of the protected object changes -- as might happen whenever another procedure or entry of the object is called. There may also be "internal" entries declared in the private part -- see next page.

Example Program with a Protected Object

The following example is based on the producer-consumer example of Section 13.2 of [Ben-Ari98]. We have made certain changes and additions (discussed below) in order to allow all the tasks to terminate in an orderly fashion.

This program includes a protected object named Buffer, which exports two protected entries named Insert and Remove. A Producer task produces a series of characters that are consumed by two tasks named C1 and C2, both instances of task type Consumer.

Im6-5c.gif (4730 bytes)

The executable part of the main procedure consists of a null statement. The three tasks begin executing immediately after the main procedure is elaborated.

In order to allow orderly termination of the tasks, the following elements were added to Ben-Ari's version: the Time_To_Quit Boolean variable, the "exit when Time_To_Quit" statement and timed entry call in the Consumer body, and the "delay 1.0" and "Time_To_Quit := True" statements in the Producer body.

Source Code Listing

--------------------------------------------------------------
--  This program has one task produce a sequence of 20 upper-
--  case characters, and another pair of tasks that consume the 
--  characters. A protected object named Buffer synchronizes 
--  the interaction while guarding against over-filling and 
--  against consumption from an empty buffer. 
--------------------------------------------------------------
with Ada.Text_IO; use Ada.Text_IO;
procedure Produce_Consume is
  type Index is mod 8;
  type Buffer_Array is array (Index) of Character;
  
  Time_To_Quit : Boolean := False;
  
  protected Buffer is
    entry Insert(C : in Character);
    entry Remove(C : out Character);
  private
    BA : Buffer_Array;
    In_Ptr, Out_Ptr, Count : Index := 0;    
  end Buffer;
  
  protected body  Buffer is
    entry Insert(C : in Character) when Count < Index'Last is
    begin
      BA(In_Ptr) := C;
      Count  := Count + 1;
      In_Ptr := In_Ptr + 1;
    end Insert;
           
    entry Remove(C : out Character) when Count > 0 is
    begin
      C := BA(Out_Ptr);
      Count  := Count - 1;
      Out_Ptr := Out_Ptr + 1;
    end Remove;
  end Buffer;
  
  task Producer;
  task body Producer is
    Ch : Character;
  begin
    for I in 65..84 loop           -- produce A-T
      Ch := Character'Val(I);
      Buffer.Insert(Ch);
      Put("  Producing " & Ch);  
    end loop;
                   
    delay 1.0;                     -- provide time to consume
    Time_To_Quit := True;
  end Producer;
  
  task type Consumer(ID : Integer);
  task body Consumer is
    Ch : Character;
  begin
    loop
      exit when Time_To_Quit;
      select
        Buffer.Remove(Ch);
        Put_Line("");
        Put("  C" & Integer'Image(ID) & " consuming " & Ch);
      or
        delay 0.2;                 -- prevent block at entry call
      end select;  
    end loop;  
  end Consumer;

  C1 : Consumer(1);
  C2 : Consumer(2);
  
begin                   
  null;    
end Produce_Consume; 
----------------------------------------------------------

Here is a sample output from the above program:

  Producing A  Producing B  Producing C  Producing D
  C 1 consuming A
  C 1 consuming C
  C 1 consuming D  Producing E  Producing F  Producing G
  C 2 consuming B
  C 2 consuming F
  C 2 consuming G
  C 1 consuming E  Producing H  Producing I
  C 2 consuming H
  C 2 consuming J
  C 1 consuming I  Producing J  Producing K  Producing L
  C 2 consuming K
  C 2 consuming M
  C 1 consuming L  Producing M  Producing N  Producing O  Producing P
  C 2 consuming N
  C 2 consuming P
  C 2 consuming Q
  C 1 consuming O  Producing Q  Producing R
  C 2 consuming R  Producing S  Producing T
  C 1 consuming S
  C 1 consuming T

Interrupt Handling

Facilities defined in Annex C, Systems Programming, of [ARM95] can be used to create a protected procedure that handles external interrupts.

Related Topics

2.3  Program Units A.4  Special Needs Annexes

[ Back to top of pagePrev ] Next ]