Section 13.4 - Protected Types Part 2

Now that you know the different types of protected operations, declaring a protected type will make more sense.

Here's an example - this is a semaphore implemented using protected types. You can request to "Seize" the semaphore; once it is Seized no other task can Seize it until it is Released.

  protected type Resource is
    entry Seize;        -- Acquire this resource exclusively.
    procedure Release;  -- Release the resource.
  private
    Busy : Boolean := False;
  end Resource;
 
  protected body Resource is
    entry Seize when not Busy is
    begin
      Busy := True;
    end Seize;
 
    procedure Release is
    begin
      Busy := False;
    end Release;
  end Resource;

Here's an example of creating a protected variable that is an instance of the protected type Resource:

  Control : Resource;

And here's an example of using it:

  Control.Seize;
  Diddle;
  Control.Release;

Here's the BNF for a protected (type) declaration and its corresponding protected body:

  protected_declaration ::= "protected" [ "type" ] identifier "is"
                            { protected_operation_declaration }
                            [ "private" { protected_element_declaration } ]
                            "end" [ identifier ]
  protected_operation_declaration ::= subprogram_declaration |
                                      entry_declaration
  protected_element_declaration ::= protected_operation_declaration |
                                    component_declaration
  
  protected_body ::= "protected" "body" identifier "is"
                     { protected_operation_item }
                     "end" [ identifier ] ";"

I've shown how to implement a semaphore using protected types because semaphores are a well-known construct for concurrent programs. However, it's better to use protected types directly instead of trying to build task interaction constructs using semaphores as the building block. One reason is that semaphores are notoriously hard to use correctly for complex task interactions - once multiple semaphores are involved it can be difficult to get the interactions correct for all cases (truly getting such interactions right may require developing a formal proof of a concurrent protocol, a really difficult thing to do). Also, when exceptions occur Ada can handle protected types automatically, which is easy to get wrong when doing it "by hand". Besides, the protected type may be more efficient.

One particularly useful use of protected types is to implement a buffered queue of messages between tasks. See the Ada Rationale (Part 2, section 9.1.2) for an example of this (the Mailbox_Pkg protected type).


There is no quiz question for this section.

You may go to the next section.


You may also:

PREVIOUS Go back to the previous section

OUTLINE  Go up to the outline of lesson 13

David A. Wheeler (dwheeler@dwheeler.com)

The master copy of this file is at "http://www.adahome.com/Tutorials/Lovelace/s13s4.htm".