File : generic_atomic_action.adb


--                              -*- Mode: Ada -*-
-- Filename        : generic_atomic_action.adb
-- Description     : automatically executes parts of an atomic action
--                    and stops them all, if at least one of them fails
--                    (by means of an exception or timing error)
-- Author          : Uwe Zimmer
-- Created On      : Wed Aug 28 17:06:35 2002
-- Last Modified By: Uwe Zimmer
-- Last Modified On: Tue Aug 21 17:21:00 2007
-- Update Count    : 0
-- Status          : Unknown, Use with caution!

with Ada.Real_Time;       use Ada.Real_Time;

with Atomic_Controller;

package body Generic_Atomic_Action is

   subtype Task_Range is Integer range Actions'Range;

   package Atomic_Action is
      new Atomic_Controller (Task_Ids => Task_Range);
   use Atomic_Action;

   procedure Perform is

      task type Action_Task;

      task body Action_Task is

         Task_Id            : Task_Range;
         Relative_Deadline,
         Real_Deadline      : Time;

      begin
         Monitor.Get_Id (Task_Id);

         Monitor.Check_In (Task_Id);

         select
            Monitor.Failed;

            Actions (Task_Id).Cleanup.all;

            Atomic_Action.Monitor.Check_Out (Failed_Check_Out) (Task_Id);

         then abort
            begin

               select
                  delay To_Duration
                    (Actions (Task_Id).Scope.Start_Delay_Max);
                  raise Late_Activation_State;
               then abort
                  delay To_Duration
                    (Actions (Task_Id).Scope.Start_Delay_Min);
               end select;

               if Actions (Task_Id).Scope.Max_Elapse = Time_Span_Last then
                  Relative_Deadline := Time_Last;
               else
                  Relative_Deadline :=
                    Clock + Actions (Task_Id).Scope.Max_Elapse;
               end if;

               if Relative_Deadline < Actions (Task_Id).Scope.Deadline then
                  Real_Deadline := Relative_Deadline;
               else
                  Real_Deadline := Actions (Task_Id).Scope.Deadline;
               end if;

               select
                  delay until Real_Deadline;
                  raise Time_Out_State;
               then abort
                  Actions (Task_Id).Action.all;
               end select;

               Atomic_Action.Monitor.Check_Out (Normal_Check_Out) (Task_Id);

           exception
               when Time_Out_State        => Monitor.Fail (Time_Out);
               when Late_Activation_State => Monitor.Fail (Late_Activation);
               when others                => Monitor.Fail (Other_Exception);
            end;
         end select;

      end Action_Task;

      Tasks: array (Task_Range) of Action_Task;

      Condition : Atomic_Condition;

   begin
      Atomic_Action.Monitor.Action_Result (Condition);
      case Condition is
         when Succeeded       => null;
         when Other_Exception => raise Failure_State;
         when Late_Activation => raise Late_Activation_State;
         when Time_Out        => raise Time_Out_State;
      end case;
   end Perform;

end Generic_Atomic_Action;