----------------------------------------------------------------------
-- resource management for varying requests
--
-- structure:  requesters = task, resourcepool(+mangement) = protected object
-- version2:   requeue unserviced requests
----------------------------------------------------------------------

with Stringpack, Ada.Numerics.Float_Random; use Stringpack, Ada.Numerics.Float_Random;
procedure Resources2 is

   G : Generator;
   Number_Of_Clients : constant Natural := 3;
   Number_Of_Resources : constant Natural := 5;
   How_Often : constant Natural := 5;


   protected Resource_Manager  is ------------------------------------
      entry Request(How_Many : Positive);
      procedure Release(How_Many : Positive);
   private
      entry Wait (How_Many : Positive);            -- private entry for requeue
      Free : Natural := Number_Of_Resources;
      Release_Event : Boolean := False;
      To_Try : Natural := 0;  -- # requests to be tried on release; <= wait'count
      Wanted : Natural := 0;  -- for trace only
   end Resource_Manager;

   protected body Resource_Manager is

      procedure Trace is
	 Not_Free : Natural := Number_Of_Resources - Free;
      begin
         Outbuffer := Varnull;
         for I in 1..Number_Of_Resources loop
	    if I <= Not_Free then Outbuffer := Outbuffer & " X";
	    else Outbuffer := Outbuffer & " -";
	    end if;
	 end loop;
	 Outbuffer := (Outbuffer & "  wanted = ") & Wanted;
	 Print;
      end Trace;

      entry Request(How_Many : Positive) when True is  -- could be "when free > 0"
      begin
	 if How_Many <= Free
	 then Free := Free - How_Many;   --allocate
	 Trace;
	 else  Wanted := Wanted + How_Many;   -- for trace
	 Trace;
	 requeue Wait;
	 end if;
      end Request;

      entry Wait (How_Many : Positive) when Release_Event  is
      begin
	 To_Try := To_Try - 1; --  loop over all waiting requesters !!!
	 if To_Try = 0 then Release_Event := False; end if;
	 if How_Many <=  Free
	 then Free := Free - How_Many;   --allocate
	 Wanted := Wanted - How_Many;   -- for trace
	 Trace;
	 else requeue Wait;
	 end if;
      end Wait;

      procedure Release(How_Many : Positive) is
      begin
	 Free := How_Many + Free;  -- release
	 if Wait'Count > 0 then To_Try := Wait'Count; Release_Event := True; end if;
	 Trace;
      end Release;


   end Resource_Manager;  --------------------------------------------


   task type Clients is   --------------------------------------------
      entry Start(How_Many : Natural);
   end Clients;

   task body Clients is
      My_Need : Natural;
   begin
      accept Start(How_Many : Natural)
      do My_Need := How_Many; end Start;
      for I in 1..How_Often loop
	 delay Duration(0.001* Random(G));
	 Resource_Manager.Request(My_Need);
	 delay Duration(0.001 * Random(G));
	 Resource_Manager.Release(My_Need);
      end loop;
   end Clients;

   Client : array(1..Number_Of_Clients ) of Clients;  ----------------

begin  -----------------------------------------------------------main

   Client(1).Start(1); Client(2).Start(2); Client(3).Start(5);

end Resources2;  -----------------------------------------------------
