[Ada Information Clearinghouse]

Ada '83 Quality and Style:

Guidelines for Professional Programmers

Copyright 1989, 1991,1992 Software Productivity Consortium, Inc., Herndon, Virginia.

CHAPTER 10: Complete Examples

10.2 Line-Oriented Portable Dining Philosophers Example

Michael B. Feldman
Dept. of Electrical Engineering and Computer Science
The George Washington University
Washington, DC 20052

(202) 994-5253
mfeldman@seas.gwu.edu

Copyright 1991, Michael B. Feldman
These programs may be freely copied, distributed, and modified for educational purposes but not for profit. If you modify or enhance the program (for example, to use other display systems), please send Dr. Feldman a copy of the modified code, either on diskette or by e-mail.

This system is an elaborate implementation of Edsger Dijkstra's famous Dining Philosophers; a classical demonstration of deadlock problems in concurrent programming.

This example uses the numeric types from the Menu-Driven User Interface example. At least one compiler (Ada/Ed) does not support floating-point precision greater than 6 digits. In this case, Big_Float will need a smaller precision (digits 6 for Ada/Ed).
------------------------------------------------------------------------

with SPC_Numeric_Types; 
use  SPC_Numeric_Types; 
package Random is

   -- Simple pseudo-random number generator package. 
   -- Adapted from the Ada literature by 
   -- Michael B. Feldman, The George Washington University, 
   -- November 1990.
   
   procedure Set_Seed (N : in     Medium_Positive);
   
   function Unit_Random return Medium_Float;
   
   --returns a float >=0.0 and <1.0
   
   function Random_Int (N : in     Medium_Positive) 
         return Medium_Positive;
         
   --return a random integer in the range 1..N
   
end Random;

------------------------------------------------------------------------

package Chop is task type Stick is entry Pick_Up; entry Put_Down; end Stick; end Chop; ------------------------------------------------------------------------ with SPC_Numeric_Types; use SPC_Numeric_Types; package Phil is

task type Philosopher is

entry Come_To_Life (My_ID : in Medium_Natural; Chopstick1 : in Medium_Natural; Chopstick2 : in Medium_Natural);

end Philosopher;

type States is (Breathing, Thinking, Eating, Done_Eating, Got_One_Stick, Got_Other_Stick);

end Phil;

------------------------------------------------------------------------

with SPC_Numeric_Types; use SPC_Numeric_Types; with Chop; with Phil;

package Room is

Table_Size : constant := 5; subtype Table_Type is Medium_Natural range 1 .. Table_Size;

Sticks : array (Table_Type) of Chop.Stick;

task Head_Waiter is

entry Open_The_Room; entry Report_State (Which_Phil : in Table_Type; State : in Phil.States; How_Long : in Medium_Natural := 0);

end Head_Waiter;

end Room;

------------------------------------------------------------------------

with Room; procedure Diners is begin Room.Head_Waiter.Open_The_Room;

loop delay 20.0; end loop; end Diners;

------------------------------------------------------------------------

with Calendar; with SPC_Numeric_Types;

use Calendar; use SPC_Numeric_Types;

package body Random is

-- Body of random number generator package. -- Adapted from the Ada literature by -- Michael B. Feldman, The George Washington University, -- November 1990.

Modulus : constant := 9_317;

subtype Seed_Range is Medium_Integer range 0 .. Modulus - 1;

Seed : Seed_Range; Default_Seed : Seed_Range;

procedure Set_Seed (N : in Medium_Positive) is separate;

function Unit_Random return Medium_Float is separate;

function Random_Int (N : in Medium_Positive) return Medium_Positive is separate;

begin Default_Seed := Medium_Integer(Big_Integer(Seconds(Clock)) mod Modulus); Seed := Default_Seed; end Random;

------------------------------------------------------------------------

separate (Random) procedure Set_Seed (N : in Medium_Positive) is begin Seed := Seed_Range(N); end Set_Seed;

------------------------------------------------------------------------

separate (Random) function Unit_Random return Medium_Float is

Multiplier : constant := 421; Increment : constant := 2_073; Result : Medium_Float;

begin -- Unit_Random

Seed := (Multiplier * Seed + Increment) mod Modulus;

Result := Medium_Float(Seed) / Medium_Float(Modulus); return Result;

exception when Constraint_Error | Numeric_Error => Seed := Medium_Integer ((Multiplier * Big_Integer(Seed) + Increment) mod Modulus);

Result := Medium_Float(Seed) / Medium_Float(Modulus); return Result;

end Unit_Random;

------------------------------------------------------------------------

separate (Random) function Random_Int (N : in Medium_Positive) return Medium_Positive is

Result : Medium_Positive range 1 .. N;

begin -- Random_Int

Result := Medium_Positive(Medium_Float(N) * Unit_Random + 0.5); return Result;

exception when Constraint_Error | Numeric_Error => return 1;

end Random_Int;

------------------------------------------------------------------------

package body Chop is

task body Stick is begin

loop select accept Pick_Up; accept Put_Down; or terminate; end select; end loop;

-- No exception handler is needed here. end Stick;

end Chop;

------------------------------------------------------------------------

with SPC_Numeric_Types; with Room; with Random;

use SPC_Numeric_Types;

package body Phil is

task body Philosopher is

type Life_Time is range 1 .. 100_000;

Who_Am_I : Medium_Natural; First_Grab : Medium_Natural; Second_Grab : Medium_Natural; Meal_Time : Medium_Natural; Think_Time : Medium_Natural;

begin -- Philosopher accept Come_To_Life (My_ID : in Medium_Natural; Chopstick1 : in Medium_Natural; Chopstick2 : in Medium_Natural) do Who_Am_I := My_ID; First_Grab := Chopstick1; Second_Grab := Chopstick2;

end Come_To_Life;

Room.Head_Waiter.Report_State(Who_Am_I, Breathing);

for Meal in Life_Time loop

Room.Sticks(First_Grab).Pick_Up; Room.Head_Waiter.Report_State (Who_Am_I, Got_One_Stick, First_Grab);

Room.Sticks(Second_Grab).Pick_Up; Room.Head_Waiter.Report_State (Who_Am_I, Got_Other_Stick, Second_Grab);

Meal_Time := Random.Random_Int(10); Room.Head_Waiter.Report_State(Who_Am_I, Eating, Meal_Time);

delay Duration(Meal_Time); Room.Head_Waiter.Report_State(Who_Am_I, Done_Eating);

Room.Sticks(First_Grab).Put_Down; Room.Sticks(Second_Grab).Put_Down;

Think_Time := Random.Random_Int(10); Room.Head_Waiter.Report_State (Who_Am_I, Thinking, Think_Time); delay Duration(Think_Time);

end loop;

-- No exception handler is needed here. end Philosopher;

end Phil;

------------------------------------------------------------------------

with SPC_Numeric_Types; use SPC_Numeric_Types; with Text_IO; with Chop; with Phil; with Calendar;

pragma Elaborate (Phil); package body Room is

-- A line-oriented version of the Room package, for line-oriented -- terminals like IBM 3270's where the user cannot do ASCII -- screen control. -- This is the only file in the dining philosophers system that -- needs changing to use in a line-oriented environment. -- Michael B. Feldman, The George Washington University, -- November 1990.

Phils : array (Table_Type) of Phil.Philosopher;

type Phil_Name is (Dijkstra, Texel, Booch, Ichbiah, Stroustrup);

task body Head_Waiter is

T : Medium_Natural; Start_Time : Calendar.Time;

Phil_Names : constant array (Table_Type) of String (1 .. 18) := ("Eddy Dijkstra ", "Putnam Texel ", "Grady Booch ", "Jean Ichbiah ", "Bjarne Stroustrup ");

Blanks : constant String := " ";

begin -- Head_Waiter

accept Open_The_Room; Start_Time := Calendar.Clock;

Phils(1).Come_To_Life(1, 1, 2); Phils(3).Come_To_Life(3, 3, 4); Phils(2).Come_To_Life(2, 2, 3); Phils(5).Come_To_Life(5, 1, 5); Phils(4).Come_To_Life(4, 4, 5);

loop select accept Report_State (Which_Phil : in Table_Type; State : in Phil.States; How_Long : in Medium_Natural := 0) do T := Medium_Natural (Calendar."-"(Calendar.Clock, Start_Time)); Text_IO.Put ("T=" & Medium_Natural'Image(T) & " " & Blanks(1 .. Positive(Which_Phil)) & Phil_Names(Which_Phil));

case State is

when Phil.Breathing => Text_IO.Put("Breathing");

when Phil.Thinking => Text_IO.Put ("Thinking" & Medium_Natural'Image(How_Long) & " seconds.");

when Phil.Eating => Text_IO.Put ("Eating" & Medium_Natural'Image(How_Long) & " seconds.");

when Phil.Done_Eating => Text_IO.Put("Yum-yum (burp)");

when Phil.Got_One_Stick => Text_IO.Put ("First chopstick" & Medium_Natural'Image(How_Long));

when Phil.Got_Other_Stick => Text_IO.Put ("Second chopstick" & Medium_Natural'Image(How_Long));

end case; -- State

Text_IO.New_Line; end Report_State; or terminate; end select; end loop; -- An exception handler is not needed here. end Head_Waiter; end Room; ------------------------------------------------------------------------


Back to document index