!standard D.03 (00) 03-09-27 AI95-00354/01 !class amendment 03-09-27 !status work item 03-09-27 !status received 03-09-27 !priority Low !difficulty Medium !subject Group Execution-Time Timers !summary This AI proposes an child package of Ada.Real_Time.Execution_Time (the revised AI 00307) to allow more than one task to share an execution-time timer. !problem Currently Ada 95 has no mechanisms to allow the implementation of aperiodic servers such as sporadic servers and deferrable servers. This severely limits the language's ability to handle aperiodic activities at anything other than a background priority. The fundamental problem that prohibits the implementation of aperiodic server algorithms is that tasks cannot share CPU budgets. !proposal Add new section: Group Execution Time Timers with Ada.Task_Identification; package Ada.Real_Time.Execution_Time.Group_Timers is type Group_Timer is limited private; type Notify is access protected procedure(GT : in out Group_Timer); Min_Notify_Ceiling : constant System.Any_Priority := ; procedure Set_Notify(GT: in out Group_Timer; Signal : Notify); function Get_Notify(GT: Group_Timer) return Notify; procedure Replenish (GT: in out Group_Timer; Interval : Time_Span); procedure Add(GT: in out Group_Timer; Interval : Time_Span); procedure Add_Task(GT: in out Group_Timer; Tid : Ada.Task_Identification.Task_ID); procedure Remove_Task(GT: in out Group_Timer; Tid : Ada.Task_Identification.Task_ID); function Is_Member(GT: Group_Timer; Tid : Ada.Task_Identification.Task_ID) return Boolean; function Budget_Has_Expired(GT: Group_Timer) return Boolean; function Budget_Remaining(GT: Group_Timer) return Time_Span; Group_Timer_Error : exception; private -- not specified by the language end Ada.Real_Time.Execution_Time.Group_Timers; The type Group_Timer represents a timer (decrementer) which measures the CPU time used by a group of tasks. Tasks of any priority can be added to a group by calling Add_Task. Tasks can be subtracted from the group by calls to Remove_Task. An attempt to remove a task that has not been added to the Group_Timer will cause Group_Timer_Error to be raised. When the Group_Timer goes to zero a call is made to a protected procedure. This procedure can be set by the Set_Notify subprogram. The subprogram Get_Notify allows the current procedure to be obtained. A call to Set_Notify for a Group_Timer that is already set resets the Group_Timer. The constant Min_Notify_Ceiling indicates the minimum ceiling priority that must be assigned to any protected object associated with the Notify protected procedure. A Group_Timer is initially without budget. The Notify protected procedure is initially null. When a call to Replenish is made, the Group_Timer is loaded with the interval Time_Span value passed as a parameter. Any execution of the group of tasks result in the Group_Timer counting down. When the budget is exhausted, Signal is called but the tasks continue to execute. A Group_Timer can have its budget increased by calling Add. The precision of the the Group_Timer is the same as that of Timers from the parent package. !wording !example One common bandwidth preserving is the deferrable server. The code for a simple deferrable server is given below: with Ada.Timing_Events; use Ada.Timing_Events; with Ada.Execution_Time.Group_Timers; use Ada.Execution_Time.Group_Timers; with Ada.Task_Identification; use Ada.Task_Identification; with Ada.Real_Time; use Ada.Real_Time; with System; use System; package Deferrable_Servers is type Deferrable_Server is limited private; procedure Create(DS : in out Deferrable_Server; First : Time; Budget : Time_Span; Period : Time_Span); procedure Add(DS : in out Deferrable_Server; T : Task_Id); private protected type Controller(DS : access Deferrable_Server) is procedure Budget_Expired(T: in out Group_Timer); procedure Replenish_Due(TE : in out Timing_Event); pragma Priority(Priority'Last); end Controller; type Deferrable_Server is record Budget : Time_Span; Period : Time_Span; Next_Replenishment_Time : Time; Replenish_Control : Timing_Event; Budget_Control : Group_Timer; Server_Control : Controller(Deferrable_Server'Access); end record; end Deferrable_Servers; A deferrable server can be represented by a type which encapsulated the Budget, the replenishment period, the next replenishment time, a Timing_Event to signal the next replenishment time, a Group_Timer to monitor the execution time consumed by the controlled tasks, and a controller to perform the required suspension and resumption of the tasks. The body of the package is given below. with Ada.Asynchronous_Control; use Ada.Asynchronous_Control; package body Deferrable_Servers is procedure Create(DS : in out Deferrable_Server; First : Time; Budget : Time_Span; Period : Time_Span) is begin DS.Budget := Budget; DS.Period := Period; DS.Next_Replenishment_Time := First; Set_Notify_Procedure(DS.Budget_Control, DS.Server_Control.Budget_Expired'Access); Timing_Events.Set_Handler(DS.Replenish_Control, DS.Next_Replenishment_Time, DS.Server_Control.Replenish_Due'Access); end Create; procedure Add(DS : in out Deferrable_Server; T : Task_Id) is begin Add_Task(DS.Budget_Control,T); -- add T to a task list (T_List) end Add; protected body Controller is procedure Budget_Expired(T : in out Group_Timer) is begin -- iterate over the T_List suspending -- (Ada.Asynchronous_Control.Hold) the task end Budget_Expired; procedure Replenish_Due(TE : in out Timing_Event) is begin Replenish(DS.Budget_Control, DS.Budget); DS.Next_Replenishment_Time := DS.Next_Replenishment_Time + DS.Period; Timing_Events.Set_Handler(DS.Replenish_Control, DS.Next_Replenishment_Time, DS.Server_Control.Replenish_Due'Access); -- iterate over T_List resuming -- (Ada.Asynchronous_Control.Continue) the tasks end Replenish_Due; end Controller; end Deferrable_Servers; !discussion Various alternative models were considered including: a) Passing the Notify protected procedure as an access discriminant to the Group_Timer type. This was rejected in favour of explicit get and set methods mainly for ease of use when combining an object of the Group_Timer type and the required protected object into a single record type. b) Passing an unconstrained array of task identifiers as a parameter to the Notify protected procedure. The argument for such a facility is that the user of the package is probably going to want to know the group of tasks whose Timer has expired. It was decided by the Workshop that this complicated the interface and that it was better to have a simpler interface as possible. c) Having the Group_Timer type as a tagged type. This was rejected as the Workshop as unclear on whether the benefit was worth the added complexity and overhead. d) Having the package automatically suspend the group of tasks when the associated Group_Timer expired. This was rejected because not all Aperiodic Server approaches suspend the tasks, some set the tasks' priorities to a background priority. !ACATS test Tests should be created to check on the implementation of this feature. !appendix ****************************************************************