Ada 95 Quality and Style Guide Chapter 11
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.
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;