Ada 95 Quality and Style Guide                      Chapter 11

CHAPTER 11
Complete Example

CHAPTER 11 Complete Example
11.1 PORTABLE DINING PHILOSOPHERS EXAMPLE

This chapter presents an elaborate implementation of Edsger Dijkstra's famous Dining Philosophers; a classical demonstration of deadlock problems in concurrent programming. This example demonstrates the portability of Ada packages and tasking and illustrates many of the Ada 95 quality and style guidelines. Since many of the guidelines leave the program writer to decide what is best, there is no single best or correct example of how to use Ada. Instead, you will find several styles that differ from your own that may deserve consideration.

11.1 PORTABLE DINING PHILOSOPHERS EXAMPLE

This version of the Dining Philosophers example was provided by Dr. Michael B. Feldman of the George Washington University and Bjorn Kallberg of CelciusTech Systems, Sweden. This example was compiled using the GNAT Ada 95 compiler, version 2.07, on a Sun platform.

--::::::::::
--random_generic.ads
--::::::::::
generic
  type Result_Subtype is (<>);
package Random_Generic is
 
  -- Simple integer pseudo-random number generator package.
  -- Michael B. Feldman, The George Washington University, 
  -- June 1995.
 
  function Random_Value return Result_Subtype;  
 
end Random_Generic;
--::::::::::
--screen.ads
--::::::::::
package Screen is

  -- simple ANSI terminal emulator
  -- Michael Feldman, The George Washington University
  -- July, 1995

  ScreenHeight : constant Integer := 24;
  ScreenWidth  : constant Integer := 80;

  subtype Height is Integer range 1 .. ScreenHeight;
  subtype Width  is Integer range 1 .. ScreenWidth;

  type Position is record
    Row    : Height := 1;
    Column : Width  := 1;
  end record;

  procedure Beep; 
  -- Pre:  none
  -- Post: the terminal beeps once
  
  procedure ClearScreen; 
  -- Pre:  none
  -- Post: the terminal screen is cleared
  
  procedure MoveCursor (To : in Position);
  -- Pre:  To is defined
  -- Post: the terminal cursor is moved to the given position
  
end Screen;   
--::::::::::
--windows.ads
--::::::::::
with Screen;
package Windows is

  -- manager for simple, nonoverlapping screen windows
  -- Michael Feldman, The George Washington University
  -- July, 1995

  type Window is private;

  function Open (UpperLeft : Screen.Position;
                 Height    : Screen.Height;
                 Width     : Screen.Width) return Window;
  -- Pre:  W, Height, and Width are defined
  -- Post: returns a Window with the given upper-left corner,
  --   height, and width

  procedure Title (W     : in out Window;
                   Name  : in     String;
                   Under : in     Character);
  -- Pre:  W, Name, and Under are defined
  -- Post: Name is displayed at the top of the window W, underlined
  -- with the character Under. 

  procedure Borders (W      : in out Window;
                     Corner : in     Character
                     Down   : in     Character
                     Across : in     Character);
  -- Pre:  All parameters are defined
  -- Post: Draw border around current writable area in window with 
  -- characters specified.  Call this BEFORE Title.  

  procedure MoveCursor (W : in out Window;
                        P : in     Screen.Position);
  -- Pre:  W and P are defined, and P lies within the area of W
  -- Post: Cursor is moved to the specified position.
  --   Coordinates are relative to the
  --   upper left corner of W, which is (1, 1) 

  procedure Put (W  : in out Window;
                 Ch : in     Character);
  -- Pre:  W and Ch are defined.
  -- Post: Ch is displayed in the window at 
  --   the next available position.
  --   If end of column, go to the next row.
  --   If end of window, go to the top of the window. 

  procedure Put (W : in out Window;
                 S : in     String);
  -- Pre:  W and S are defined
  -- Post: S is displayed in the window, "line-wrapped" if necessary

  procedure New_Line (W : in out Window);
  -- Pre:  W is defined
  -- Post: Cursor moves to beginning of next line of W;
  --   line is not blanked until next character is written  

private
  type Window is record
    First   : Screen.Position; -- coordinates of upper left
    Last    : Screen.Position; -- coordinates of lower right
    Current : Screen.Position; -- current cursor position
  end record;

end Windows;
--::::::::::
--Picture.ads
--::::::::::
with Windows;
with Screen;
package Picture is

  -- Manager for semigraphical presentation of the philosophers
  -- i.e. more application oriented windows, build on top of
  -- the windows package.
  -- Each picture has an orientation, which defines which borders
  -- top-bottom, bottom-top, left-right, or right-left correspond
  -- to the left and right hand of the philosopher.
  --
  -- Bjorn Kallberg, CelsiusTech Systems, Sweden
  -- July, 1995

  type Root is abstract tagged private;
  type Root_Ptr is access Root'Class;

  procedure Open (W         : in out Root;
                  UpperLeft : in     Screen.Position;
                  Height    : in     Screen.Height;
                  Width     : in     Screen.Width);
  -- Pre:  Not opened
  -- Post: An empty window exists

  procedure Title (W     : in out Root;
                   Name  : in     String);
  -- Pre:  An empty window
  -- Post: Name and a border is drawn.

  procedure Put_Line (W : in out Root; 
                      S : in     String);

  procedure Left_Fork  (W    : in out Root; 
                        Pick : in     Boolean) is abstract;
  procedure Right_Fork (W    : in out Root; 
                        Pick : in     Boolean) is abstract;
  -- left and right relates to philosopher position around table

  type North is new Root with private;
  type South is new Root with private;
  type East  is new Root with private;
  type West  is new Root with private;


private
  type Root is abstract tagged record
      W : Windows.Window;
  end record;

  type North is new Root with null record;
  type South is new Root with null record;
  type East  is new Root with null record;
  type West  is new Root with null record;

  procedure Left_Fork  (W    : in out North; 
                        Pick : in     Boolean);
  procedure Right_Fork (W    : in out North; 
                        Pick : in     Boolean);

  procedure Left_Fork  (W    : in out South; 
                        Pick : in     Boolean);
  procedure Right_Fork (W    : in out South; 
                        Pick : in     Boolean);

  procedure Left_Fork  (W    : in out East; 
                        Pick : in     Boolean);
  procedure Right_Fork (W    : in out East; 
                        Pick : in     Boolean);

  procedure Left_Fork  (W    : in out West; 
                        Pick : in     Boolean);
  procedure Right_Fork (W    : in out West; 
                        Pick : in     Boolean);

end Picture;
--::::::::::
--chop.ads
--::::::::::
package Chop is

  -- Dining Philosophers - Ada 95 edition
  -- Chopstick is an Ada 95 protected type
  -- Michael B. Feldman, The George Washington University,
  -- July, 1995.
 
  protected type Stick is
    entry Pick_Up;
    procedure Put_Down;
  private
    In_Use: Boolean := False;
  end Stick;
 
end Chop;

--::::::::::
--society.ads
--::::::::::
package Society is

  -- Dining Philosophers - Ada 95 edition
  -- Society gives unique ID's to people, and registers their names
  -- Michael B. Feldman, The George Washington University,
  -- July, 1995.

  subtype Unique_DNA_Codes is Positive range 1 .. 5;

  Name_Register : array (Unique_DNA_Codes) of String (1 .. 18) :=

     ("Edsger Dijkstra   ",
      "Bjarne Stroustrup ",
      "Chris Anderson    ",
      "Tucker Taft       ",
      "Jean Ichbiah      ");

end Society;
--::::::::::
--phil.ads
--::::::::::
with Society;
package Phil is
 
  -- Dining Philosophers - Ada 95 edition
  -- Philosopher is an Ada 95 task type with discriminant
  -- Michael B. Feldman, The George Washington University,
  -- July 1995
  --
  -- Revisions:
  -- July 1995. Bjorn Kallberg, CelsiusTech
  --            Reporting left or right instead of first stick

  task type Philosopher (My_ID : Society.Unique_DNA_Codes) is
 
    entry Start_Eating (Chopstick1 : in Positive;
                        Chopstick2 : in Positive);
 
  end Philosopher;
 
  type States is (Breathing, Thinking, Eating, Done_Eating, 
                  Got_Left_Stick, Got_Right_Stick, Got_Other_Stick, Dying);

end Phil;
--::::::::::
--room.ads
--::::::::::
with Chop;
with Phil;
with Society;
package Room is
 
  -- Dining Philosophers - Ada 95 edition

  -- Room.Maitre_D is responsible for assigning seats at the
  --   table, "left" and "right" chopsticks, and for reporting
  --   interesting events to the outside world.

  -- Michael B. Feldman, The George Washington University,
  -- July, 1995.

  Table_Size : constant := 5;
  subtype Table_Type is Positive range 1 .. Table_Size;
 
  Sticks : array (Table_Type) of Chop.Stick;
 
  task Maitre_D is
    entry Start_Serving;
    entry Report_State (Which_Phil : in Society.Unique_DNA_Codes;
                        State      : in Phil.States;
                        How_Long   : in Natural := 0;
                        Which_Meal : in Natural := 0);
  end Maitre_D;
 
end Room;
--::::::::::
--random_generic.adb
--::::::::::
with Ada.Numerics.Discrete_Random;
package body Random_Generic is
 
  -- Body of random number generator package.
  -- Uses Ada 95 random number generator; hides generator parameters
  -- Michael B. Feldman, The George Washington University, 
  -- June 1995.
 
  package Ada95_Random is new Ada.Numerics.Discrete_Random
    (Result_Subtype => Result_Subtype);

  G : Ada95_Random.Generator;

  function Random_Value return Result_Subtype is 
  begin
    return Ada95_Random.Random (Gen => G);
  end Random_Value;

begin -- Random_Generic

  Ada95_Random.Reset (Gen => G);  -- time-dependent initialization

end Random_Generic;
--::::::::::
--screen.adb
--::::::::::
with Text_IO;
package body Screen is

  -- simple ANSI terminal emulator
  -- Michael Feldman, The George Washington University
  -- July, 1995

  -- These procedures will work correctly only if the actual
  -- terminal is ANSI compatible. ANSI.SYS on a DOS machine
  -- will suffice.

  package Int_IO is new Text_IO.Integer_IO (Num => Integer);

  procedure Beep is
  begin
    Text_IO.Put (Item => ASCII.BEL);
  end Beep;

  procedure ClearScreen is
  begin
    Text_IO.Put (Item => ASCII.ESC);
    Text_IO.Put (Item => "[2J");
  end ClearScreen;

  procedure MoveCursor (To : in Position) is
  begin                                                
    Text_IO.New_Line;
    Text_IO.Put (Item => ASCII.ESC);
    Text_IO.Put ("[");
    Int_IO.Put (Item => To.Row, Width => 1);
    Text_IO.Put (Item => ';');
    Int_IO.Put (Item => To.Column, Width => 1);
    Text_IO.Put (Item => 'f');
  end MoveCursor;  

end Screen;
--::::::::::
--windows.adb
--::::::::::
with Text_IO, with Screen;
package body Windows is

  -- manager for simple, nonoverlapping screen windows
  -- Michael Feldman, The George Washington University
  -- July, 1995

  function Open (UpperLeft : Screen.Position;
                 Height    : Screen.Height;
                 Width     : Screen.Width) return Window is
    Result : Window;
  begin
    Result.Current := UpperLeft;
    Result.First   := UpperLeft;
    Result.Last    := (Row    => UpperLeft.Row + Height - 1, 
                       Column => UpperLeft.Column + Width - 1);
    return Result; 
  end Open;

  procedure EraseToEndOfLine (W : in out Window) is
  begin
    Screen.MoveCursor (W.Current);
    for Count in W.Current.Column .. W.Last.Column loop
      Text_IO.Put (' ');
    end loop;
    Screen.MoveCursor (W.Current);
  end EraseToEndOfLine;

  procedure Put (W  : in out Window;
                 Ch : in     Character) is
  begin

    -- If at end of current line, move to next line 
    if W.Current.Column > W.Last.Column then
      if W.Current.Row = W.Last.Row then
        W.Current.Row := W.First.Row;
      else
        W.Current.Row := W.Current.Row + 1;
      end if;
      W.Current.Column := W.First.Column;
    end if;

    -- If at First char, erase line
    if W.Current.Column = W.First.Column then
      EraseToEndOfLine (W);
    end if;

    Screen.MoveCursor (To => W.Current);

     -- here is where we actually write the character!
     Text_IO.Put (Ch);
     W.Current.Column := W.Current.Column + 1;
 
  end Put;

  procedure Put (W : in out Window;
                 S : in     String) is
  begin
    for Count in S'Range loop
      Put (W, S (Count));
    end loop;
  end Put;

  procedure New_Line (W : in out Window) is
  begin
    if W.Current.Column = 1 then
      EraseToEndOfLine (W);
    end if;
    if W.Current.Row = W.Last.Row then
      W.Current.Row := W.First.Row;
    else
      W.Current.Row := W.Current.Row + 1;
    end if;
    W.Current.Column := W.First.Column;
  end New_Line;
  procedure Title (W     : in out Window;
                   Name  : in     String;
                   Under : in     Character) is
  begin
    -- Put name on top line
    W.Current := W.First;
    Put (W, Name);
    New_Line (W);
    -- Underline name if desired, and reduce the writable area
    -- of the window by one line
    if Under = ' ' then   -- no underlining
      W.First.Row := W.First.Row + 1;      
    else                  -- go across the row, underlining
      for Count in W.First.Column .. W.Last.Column loop 
        Put (W, Under);
      end loop;
      New_Line (W);
      W.First.Row := W.First.Row + 2; -- reduce writable area
    end if;
  end Title;
 
  procedure Borders (W       : in out Window;
                     Corner  : in     Character
                     Down    : in     Character
                     Across  : in     Character is
, 
  begin
    -- Put top line of border
    Screen.MoveCursor (W.First);
    Text_IO.Put (Corner);
    for Count in W.First.Column + 1 .. W.Last.Column - 1 loop
      Text_IO.Put (Across);
    end loop;
    Text_IO.Put (Corner);

    -- Put the two side lines
    for Count in W.First.Row + 1 .. W.Last.Row - 1 loop
      Screen.MoveCursor ((Row => Count, Column => W.First.Column));
      Text_IO.Put (Down);
      Screen.MoveCursor ((Row => Count, Column => W.Last.Column));
      Text_IO.Put (Down);
    end loop;

    -- Put the bottom line of the border
    Screen.MoveCursor ((Row => W.Last.Row, Column => W.First.Column));
    Text_IO.Put (Corner);
    for Count in W.First.Column + 1 .. W.Last.Column - 1 loop
      Text_IO.Put (Across);
    end loop;
    Text_IO.Put (Corner);

    -- Make the Window smaller by one character on each side
    W.First   := (Row => W.First.Row + 1, Column => W.First.Column + 1);
    W.Last    := (Row => W.Last.Row - 1,  Column => W.Last.Column - 1);
    W.Current := W.First;
  end Borders;

  procedure MoveCursor (W : in out Window;
                        P : in     Screen.Position) is
    -- Relative to writable Window boundaries, of course
  begin 
    W.Current.Row    := W.First.Row + P.Row;
    W.Current.Column := W.First.Column + P.Column;
  end MoveCursor;

begin -- Windows

  Text_IO.New_Line;
  Screen.ClearScreen;
  Text_IO.New_Line;

end Windows;
--------------------
package Windows.Util is
  --
  -- Child package to change the borders of an existing window
  -- Bjorn Kallberg, CelsiusTech Systems, Sweden
  -- July, 1995.
  
  -- call these procedures after border and title
  procedure Draw_Left   (W  : in out Window; 
                         C  : in     Character);
  procedure Draw_Right  (W  : in out Window; 
                         C  : in     Character);
  procedure Draw_Top    (W  : in out Window; 
                         C  : in     Character);
  procedure Draw_Bottom (W  : in out Window; 
                         C  : in     Character);

end Windows.Util;
--------------------
with Text_IO;
package body Windows.Util is

  -- Bjorn Kallberg, CelsiusTech Systems, Sweden
  -- July, 1995.
 
  -- When making borders and titles, the size has shrunk, so
  -- we must now draw outside the First and Last points


   procedure Draw_Left (W  : in out Window; 
                        C  : in     Character) is
   begin
     for R in W.First.Row - 3  .. W.Last.Row + 1 loop
       Screen.MoveCursor ((Row => R, Column => W.First.Column-1));
       Text_IO.Put (C);
      end loop;
   end;
 
   procedure Draw_Right (W  : in out Window; 
                         C  : in     Character) is
   begin
     for R in W.First.Row - 3  .. W.Last.Row + 1 loop
       Screen.MoveCursor ((Row => R, Column => W.Last.Column + 1));
       Text_IO.Put (C);
     end loop;
   end;

   procedure Draw_Top (W  : in out Window; 
                       C  : in     Character) is
   begin
     for I in W.First.Column - 1 .. W.Last.Column + 1 loop
       Screen.MoveCursor ((Row => W.First.Row - 3, Column => I));
       Text_IO.Put (C);
     end loop;
   end;

   procedure Draw_Bottom (W  : in out Window; 
                          C  : in     Character) is
   begin
     for I in W.First.Column - 1 .. W.Last.Column + 1 loop
       Screen.MoveCursor ((Row => W.Last.Row + 1, Column => I));
       Text_IO.Put (C);
     end loop;
   end;

end Windows.Util;

--::::::::::
--Picture.adb
--::::::::::
with Windows.Util;
package body Picture is
  -- 
  -- Bjorn Kallberg, CelsiusTech Systems, Sweden
  -- July, 1995


  function Vertical_Char (Stick : Boolean) return Character is
  begin
     if Stick then 
        return '#'; 
     else 
       return ':'; 
     end if;
  end;

  function Horizontal_Char (Stick : Boolean) return Character is
  begin
    if Stick then 
       return '#'; 
    else 
       return '-'; 
    end if;
  end;


  procedure Open (W         : in out Root;
                  UpperLeft : in     Screen.Position;
                  Height    : in     Screen.Height;
                  Width     : in     Screen.Width) is
  begin 
     W.W := Windows.Open (UpperLeft, Height, Width);
  end;


  procedure Title (W     : in out Root;
                   Name  : in     String) is
  -- Pre:  An empty window
  -- Post: Name and a boarder is drawn.

  begin
      Windows.Borders (W.W, '+', ':', '-');
      Windows.Title (W.W, Name,'-');
  end;
 
  procedure Put_Line (W : in out Root; 
                      S : in     String) is
  begin
     Windows.Put (W.W, S);
     Windows.New_Line (W.W);
  end;


  -- North
  procedure Left_Fork  (W    : in out North; 
                        Pick : in     Boolean) is
  begin
     Windows.Util.Draw_Right (W.W, Vertical_Char (Pick));
  end; 

  procedure Right_Fork  (W    : in out North; 
                         Pick : in     Boolean) is
  begin
     Windows.Util.Draw_Left (W.W, Vertical_Char (Pick));
  end;


  -- South
  procedure Left_Fork  (W    : in out South; 
                        Pick : in     Boolean) is
  begin
     Windows.Util.Draw_Left (W.W, Vertical_Char (Pick));
  end;

  procedure Right_Fork  (W    : in out South; 
                         Pick : in     Boolean) is
  begin
     Windows.Util.Draw_Right (W.W, Vertical_Char (Pick));
  end;


  -- East
  procedure Left_Fork  (W    : in out East; 
                        Pick : in     Boolean) is
  begin
     Windows.Util.Draw_Bottom (W.W, Horizontal_Char (Pick));
  end;
  procedure Right_Fork  (W    : in out East; 
                         Pick : in     Boolean) is
  begin
     Windows.Util.Draw_Top (W.W, Horizontal_Char (Pick));
  end;


  -- West
  procedure Left_Fork  (W    : in out West; 
                        Pick : in     Boolean) is
  begin
     Windows.Util.Draw_Top (W.W, Horizontal_Char (Pick));
  end;

  procedure Right_Fork  (W    : in out West; 
                         Pick : in     Boolean) is
  begin
     Windows.Util.Draw_Bottom (W.W, Horizontal_Char (Pick));
  end;


end Picture;

--::::::::::
--chop.adb
--::::::::::
package body Chop is

  -- Dining Philosophers - Ada 95 edition
  -- Chopstick is an Ada 95 protected type
  -- Michael B. Feldman, The George Washington University,
  -- July, 1995.
 
  protected body Stick is

    entry Pick_Up when not In_Use is
    begin
      In_Use := True;
    end Pick_Up;

    procedure Put_Down is
    begin
      In_Use := False;
    end Put_Down;

  end Stick;
 
end Chop;
--::::::::::
--phil.adb
--::::::::::
with Society;
with Room;
with Random_Generic;
package body Phil is
 
  -- Dining Philosophers - Ada 95 edition
  -- Philosopher is an Ada 95 task type with discriminant.

  -- Chopsticks are assigned by a higher authority, which
  --   can vary the assignments to show different algorithms.
  -- Philosopher always grabs First_Grab, then Second_Grab.
  -- Philosopher is oblivious to outside world, but needs to
  --   communicate is life-cycle events the Maitre_D.
  -- Chopsticks assigned to one philosopher must be
  -- consecutive numbers, or the first and last chopstick.

  -- Michael B. Feldman, The George Washington University,
  -- July, 1995.
  -- Revisions:
  -- July, 1995. Bjorn Kallberg, CelsiusTech

  subtype Think_Times is Positive range 1 .. 8;
  package Think_Length is 
    new Random_Generic (Result_Subtype => Think_Times);

  subtype Meal_Times is Positive range 1 .. 10;
  package Meal_Length is
    new Random_Generic (Result_Subtype => Meal_Times);

  task body Philosopher is  -- My_ID is discriminant
 
    subtype Life_Time is Positive range 1 .. 5;
 
    Who_Am_I    : Society.Unique_DNA_Codes := My_ID; -- discriminant
    First_Grab  : Positive;
    Second_Grab : Positive;
    Meal_Time   : Meal_Times; 
    Think_Time  : Think_Times;
    First_Stick : States;
     
  begin
      -- get assigned the first and second chopsticks here
    accept Start_Eating (Chopstick1 : in Positive;
                         Chopstick2 : in Positive) do
      First_Grab  := Chopstick1;
      Second_Grab := Chopstick2;
      if (First_Grab mod Room.Table_Type'Last) + 1 = Second_Grab then
         First_Stick := Got_Right_Stick;
      else
         First_Stick := Got_Left_Stick;
      end if;
    end Start_Eating;
    Room.Maitre_D.Report_State (Who_Am_I, Breathing);
 
    for Meal in Life_Time loop
      Room.Sticks (First_Grab).Pick_Up;
      Room.Maitre_D.Report_State (Who_Am_I, First_Stick, First_Grab);
      Room.Sticks (Second_Grab).Pick_Up;
      Room.Maitre_D.Report_State (Who_Am_I, Got_Other_Stick, Second_Grab);
      Meal_Time := Meal_Length.Random_Value;
      Room.Maitre_D.Report_State (Who_Am_I, Eating, Meal_Time, Meal);
      delay Duration (Meal_Time);
      Room.Maitre_D.Report_State (Who_Am_I, Done_Eating);
      Room.Sticks (First_Grab).Put_Down;
      Room.Sticks (Second_Grab).Put_Down;
      Think_Time := Think_Length.Random_Value; 
      Room.Maitre_D.Report_State (Who_Am_I, Thinking, Think_Time);
      delay Duration (Think_Time);
    end loop;
    Room.Maitre_D.Report_State (Who_Am_I, Dying);
  end Philosopher;
end Phil;
--::::::::::
--room.adb
--::::::::::
with Picture;
with Chop;
with Phil;
with Society;
with Calendar;
pragma Elaborate (Phil);
package body Room is
 
  -- Dining Philosophers, Ada 95 edition
  -- A line-oriented version of the Room package
  -- Michael B. Feldman, The George Washington University, 
  -- July, 1995.
  -- Revisions
  -- July, 1995. Bjorn Kallberg, CelsiusTech Systems, Sweden.
  --             Pictorial display of stick in use 

  -- philosophers sign into dining room, giving Maitre_D their DNA code
 
  Dijkstra   : aliased Phil.Philosopher (My_ID => 1);
  Stroustrup : aliased Phil.Philosopher (My_ID => 2);
  Anderson   : aliased Phil.Philosopher (My_ID => 3);
  Taft       : aliased Phil.Philosopher (My_ID => 4);
  Ichbiah    : aliased Phil.Philosopher (My_ID => 5);
 
  type Philosopher_Ptr is access all Phil.Philosopher;

  Phils      : array (Table_Type) of Philosopher_Ptr;
  Phil_Pics  : array (Table_Type) of Picture.Root_Ptr;
  Phil_Seats : array (Society.Unique_DNA_Codes) of Table_Type;

  task body Maitre_D is
 
    T          : Natural;
    Start_Time : Calendar.Time;
    Blanks     : constant String := "     ";


  begin
 
    accept Start_Serving;

    Start_Time := Calendar.Clock;
 
    -- now Maitre_D assigns phils to seats at the table

    Phils :=
      (Dijkstra'Access,
       Anderson'Access,
       Ichbiah'Access,
       Taft'Access,
       Stroustrup'Access);
  
    -- Which seat each phil occupies.
    for I in Table_Type loop
       Phil_Seats (Phils(I).My_Id) := I;
    end loop;

    Phil_Pics :=
       (new Picture.North, 
        new Picture.East, 
        new Picture.South,
        new Picture.South,
        new Picture.West);
  
    Picture.Open (Phil_Pics(1).all,( 1, 24), 7, 30);
    Picture.Open (Phil_Pics(2).all,( 9, 46), 7, 30);
    Picture.Open (Phil_Pics(3).all,(17, 41), 7, 30);
    Picture.Open (Phil_Pics(4).all,(17,  7), 7, 30);
    Picture.Open (Phil_Pics(5).all,( 9,  2), 7, 30);


    -- and assigns them their chopsticks.

    Phils (1).Start_Eating (1, 2);
    Phils (3).Start_Eating (3, 4);
    Phils (2).Start_Eating (2, 3);
    Phils (5).Start_Eating (1, 5);
    Phils (4).Start_Eating (4, 5);
 
    loop
      select
        accept Report_State (Which_Phil : in Society.Unique_DNA_Codes;
                             State      : in Phil.States;
                             How_Long   : in Natural := 0;
                             Which_Meal : in Natural := 0) do

          T := Natural (Calendar."-" (Calendar.Clock, Start_Time));
 
          case State is
 
            when Phil.Breathing =>
              Picture.Title (Phil_Pics (Phil_Seats (Which_Phil)).all,
                     Society.Name_Register (Which_Phil));
              Picture.Put_line (Phil_Pics (Phil_Seats (Which_Phil)).all,
                     "T =" & Integer'Image (T) & " " 
                      & "Breathing...");

            when Phil.Thinking =>
              Picture.Put_line (Phil_Pics (Phil_Seats (Which_Phil)).all,
                     "T =" & Integer'Image (T) & " " 
                      & "Thinking" 
                      & Integer'Image (How_Long) & " seconds.");

            when Phil.Eating =>
              Picture.Put_line (Phil_Pics (Phil_Seats (Which_Phil)).all,
                     "T =" & Integer'Image (T) & " " 
                      & "Meal"  
                      & Integer'Image (Which_Meal)
                      & ","  
                      & Integer'Image (How_Long) & " seconds.");

            when Phil.Done_Eating =>
              Picture.Put_line (Phil_Pics (Phil_Seats (Which_Phil)).all,
                     "T =" & Integer'Image (T) & " " 
                      & "Yum-yum (burp)");
              Picture.Left_Fork (Phil_Pics (Phil_Seats (Which_Phil)).all, False);
              Picture.Right_Fork (Phil_Pics (Phil_Seats (Which_Phil)).all, False);

            when Phil.Got_Left_Stick =>
              Picture.Put_line (Phil_Pics (Phil_Seats (Which_Phil)).all,
                     "T =" & Integer'Image (T) & " " 
                      & "First chopstick" 
                      & Integer'Image (How_Long));
              Picture.Left_Fork (Phil_Pics (Phil_Seats (Which_Phil)).all, True);

            when Phil.Got_Right_Stick =>
              Picture.Put_line (Phil_Pics (Phil_Seats (Which_Phil)).all,
                     "T =" & Integer'Image (T) & " " 
                      & "First chopstick" 
                      & Integer'Image (How_Long));
              Picture.Right_Fork (Phil_Pics (Phil_Seats (Which_Phil)).all, True);

            when Phil.Got_Other_Stick =>
              Picture.Put_line (Phil_Pics (Phil_Seats (Which_Phil)).all,
                     "T =" & Integer'Image (T) & " " 
                      & "Second chopstick" 
                      & Integer'Image (How_Long));
              Picture.Left_Fork (Phil_Pics (Phil_Seats (Which_Phil)).all, True);
              Picture.Right_Fork (Phil_Pics (Phil_Seats (Which_Phil)).all, True);

            when Phil.Dying =>
              Picture.Put_line (Phil_Pics (Phil_Seats (Which_Phil)).all,
                     "T =" & Integer'Image (T) & " " 
                      & "Croak");

          end case; -- State
          
        end Report_State;
 
      or
        terminate;
      end select;
 
    end loop;
 
  end Maitre_D;
 
end Room;

--::::::::::
--diners.adb
--::::::::::
with Text_IO;
with Room;
procedure Diners is

  -- Dining Philosophers - Ada 95 edition

  -- This is the main program, responsible only for telling the
  --   Maitre_D to get busy.

  -- Michael B. Feldman, The George Washington University,
  -- July, 1995.
 
begin
  --Text_IO.New_Line;     -- artifice to flush output buffer
  Room.Maitre_D.Start_Serving;
end Diners;