Version 1.18 of ais/ai-00307.txt

Unformatted version of ais/ai-00307.txt version 1.18
Other versions for file ais/ai-00307.txt

!standard D.14 (00)          05-10-01 AI95-00307/13
!standard D.14.1 (00)
!class amendment 02-08-28
!status Amendment 200Y 04-06-24
!status ARG Approved 8-0-0 04-06-13
!status work item 03-09-21
!status ARG Approved 12-0-1 03-06-20
!status work item 02-08-28
!status received 02-08-28
!priority High
!difficulty Hard
!subject Execution-time clocks
!summary
Modern real-time scheduling policies require that applications have the ability to measure the execution time of tasks, and to detect execution time overruns. This functionality is provided by a new package that defines execution time clocks and a child of this package which provides timers.
!problem
Performance analysis techniques are usually based on the assumption that the application developer can accurately measure/estimate the execution time of each task. Measurement is always very difficult, because, with effects like cache misses, pipelined and superscalar processor architectures, etc., the execution time is highly unpredictable. There are models that allow the calculation of worst-case execution time (WCET) for some architectures, but they are generally very complex and not widely available for all architectures. A language defined means of measuring execution time is desirable.
In hard real-time systems it is essential to monitor the execution times of all tasks and detect situations in which the estimated WCET is exceeded. This detection is usually available in systems scheduled with cyclic executives, because the periodic nature of a cycle allows checking that all initiated work has been completed at each cycle. In event-driven concurrent systems the same capability should be available, and this can be accomplished with execution time clocks and timers.
Moreover, many flexible real-time scheduling algorithms require the capability to measure execution time and be able to perform scheduling actions when a certain amount of execution time has been consumed (for example, sporadic servers in fixed priority systems, or the constant bandwidth server in EDF-scheduled systems). Support for execution time clocks and timers would ease implementation of such flexible scheduling algorithms.
The Real-Time extensions to POSIX have recently incorporated support for execution time monitoring. Real-Time POSIX supports execution time clocks and timers that allow an application to monitor the consumption of execution time by its tasks, and to set limits for this consumption.
It could be argued that given that the POSIX standard already defines execution time clocks and timers it would not be necessary to have the same functionality in the Ada language standard, because the POSIX services can be accessed through the appropriate bindings. This is true for Ada platforms built on top of POSIX OS implementations, but not for bare-machine implementations, like the ones used in embedded systems, avionics, etc. For portability purposes it is necessary to have this functionality supported in a homogeneous way for all the implementations that choose to support it.
!proposal
(See wording.)
!wording
Add new section D.14.
D.14 Execution Time
This clause describes a language-defined package to measure execution time.
Static Semantics
The following language-defined library package exists:
with Ada.Task_Identification; with Ada.Real_Time; use Ada.Real_Time; package Ada.Execution_Time is
type CPU_Time is private; CPU_Time_First : constant CPU_Time; CPU_Time_Last : constant CPU_Time; CPU_Time_Unit : constant := implementation-defined-real-number; CPU_Tick : constant Time_Span;
function Clock (T : Ada.Task_Identification.Task_Id := Ada.Task_Identification.Current_Task) return CPU_Time;
function "+" (Left : CPU_Time; Right : Time_Span) return CPU_Time; function "+" (Left : Time_Span; Right : CPU_Time) return CPU_Time; function "-" (Left : CPU_Time; Right : Time_Span) return CPU_Time; function "-" (Left : CPU_Time; Right : CPU_Time) return Time_Span;
function "<" (Left, Right : CPU_Time) return Boolean; function "<=" (Left, Right : CPU_Time) return Boolean; function ">" (Left, Right : CPU_Time) return Boolean; function ">=" (Left, Right : CPU_Time) return Boolean;
procedure Split (T : in CPU_Time; SC : out Seconds_Count; TS : out Time_Span);
function Time_Of (SC : Seconds_Count; TS : Time_Span := Time_Span_Zero) return CPU_Time;
private ... -- not specified by the language end Ada.Execution_Time;
The execution time or CPU time of a given task is defined as the time spent by the system executing that task, including the time spent executing run-time or system services on its behalf. The mechanism used to measure execution time is implementation defined. It is implementation defined which task, if any, is charged the execution time that is consumed by interrupt handlers and run-time services on behalf of the system.
The type CPU_Time represents the execution time of a task. The set of values of this type corresponds one-to-one with an implementation-defined range of mathematical integers.
The CPU_Time value I represents the half-open execution-time interval that starts with I*CPU_Time_Unit and is limited by (I+1)*CPU_Time_Unit, where CPU_Time_Unit is an implementation-defined real number. For each task, the execution time value is set to zero at the creation of the task.
CPU_Time_First and CPU_Time_Last are the smallest and largest values of the CPU_Time type, respectively.
Dynamic Semantics
CPU_Time_Unit is the smallest amount of execution time representable by the CPU_Time type; it is expressed in seconds. A CPU clock tick is an execution time interval during which the clock value (as observed by calling the Clock function) remains constant. CPU_Tick is the average length of such intervals.
The effects of the operators on CPU_Time and Time_Span are as for the operators defined for integer types.
The function Clock returns the current execution time of the task identified by T; Tasking_Error is raised if that task has terminated; Program_Error is raised if the value of T is Task_Identification.Null_Task_Id.
The effects of the Split and Time_Of operations are defined as follows, treating values of type CPU_Time, Time_Span, and Seconds_Count as mathematical integers. The effect of Split (T, SC, TS) is to set SC and TS to values such that TCPU_Time_Unit = SC1.0 + TSCPU_Time_Unit, and 0.0 <= TSCPU_Time_Unit < 1.0. The value returned by Time_Of(SC,TS) is the execution-time value T such that T*CPU_Time_Unit=SC*1.0 + TS*CPU_Time_Unit.
Erroneous Execution
For a call of Clock, if the task identified by T no longer exists, the execution of the program is erroneous.
Implementation Requirements
The range of CPU_Time values shall be sufficient to uniquely represent the range of execution times from the task creation to 50 years of execution time later. CPU_Tick shall be no greater than 1 millisecond.
Documentation Requirements
The implementation shall document the values of CPU_Time_First, CPU_Time_Last, CPU_Time_Unit, and CPU_Tick.
The implementation shall document the properties of the underlying mechanism used to measure execution times, such as the range of values supported and any relevant aspects of the underlying hardware or operating system facilities used.
Metrics
The implementation shall document the following metrics:
- An upper bound on the execution-time duration of a clock
tick. This is a value D such that if t1 and t2 are any execution times of a given task such that t1 < t2 and Clock[t1] = Clock[t2] then t2-t1 <= D.
- An upper bound on the size of a clock jump. A clock jump is the
difference between two successive distinct values of an execution -time clock (as observed by calling the Clock function with the same Task_Id).
- An upper bound on the execution time of a call to the Clock
function, in processor clock cycles.
- Upper bounds on the execution times of the operators of the type
CPU_Time, in processor clock cycles.
Implementation Permissions
Implementations targeted to machines with word size smaller than 32 bits need not support the full range and granularity of the CPU_Time type.
Implementation Advice
When appropriate, implementations should provide configuration mechanisms to change the value of CPU_Tick.
Add new section D.14.1:
D.14.1 Execution Time Timers
This clause describes a language-defined package that provides a facility for calling a handler when a task has used a defined amount of CPU time.
Static Semantics
The following language-defined library package exists:
with System; package Ada.Execution_Time.Timers is
type Timer (T : not null access constant Ada.Task_Identification.Task_Id) is tagged limited private;
type Timer_Handler is access protected procedure (TM : in out Timer);
Min_Handler_Ceiling : constant System.Any_Priority := implementation-defined;
procedure Set_Handler (TM : in out Timer; In_Time : in Time_Span; Handler : in Timer_Handler); procedure Set_Handler (TM : in out Timer; At_Time : in CPU_Time; Handler : in Timer_Handler); function Current_Handler (TM: Timer) return Timer_Handler; procedure Cancel_Handler (TM : in out Timer; Cancelled : out Boolean);
function Time_Remaining (TM : Timer) return Time_Span;
Timer_Resource_Error : exception;
private ... -- not specified by the language end Ada.Execution_Time.Timers;
The type Timer represents an execution-time event for a single task and is capable of detecting execution-time overruns. The access discriminant T identifies the task concerned. The type Timer needs finalization (see 7.6).
An object of type Timer is said to be set if it is associated with a non-null value of type Timer_Handler and cleared otherwise. All Timer objects are initially cleared.
The type Timer_Handler identifies a protected procedure to be executed by the implementation when the timer expires. Such a protected procedure is called a handler.
AARM Note: Type Timer is tagged. This makes it possible to share a handler between several events. In simple cases, 'Access can be used to compare the parameter with a specific timer object (this works because a tagged type is a by-reference type). In more complex cases, a type extension of type Timer can be declared; a double type conversion can be used to access the extension data. End AARM Note.
Dynamic Semantics
When a Timer object is created, or upon the first call of a Set_Handler procedure with the timer as parameter, the resources required to operate an execution-time timer based on the associated execution-time clock are allocated and initialized. If this operation would exceed the available resources, Timer_Resource_Error is raised.
The procedures Set_Handler associate the handler Handler with the timer TM; if Handler is null, the timer is cleared, otherwise it is set. The first procedure Set_Handler loads the timer TM with an interval specified by the Time_Span parameter. In this mode, the timer TM expires when the execution time of the task identified by TM.T.all has increased by In_Time; if In_Time is less than or equal to zero, the timer expires immediately. The second procedure Set_Handler loads the timer TM with the absolute value specified by At_Time. In this mode, the timer TM expires when the execution time of the task identified by TM.T.all reaches At_Time; if the value of At_Time has already been reached when Set_Handler is called, the timer expires immediately.
A call of a procedure Set_Handler for a timer that is already set replaces the handler and the (absolute or relative) execution time; if Handler is not null, the timer remains set.
When a timer expires, the associated handler is executed, passing the timer as parameter. The initial action of the execution of the handler is to clear the event.
The function Current_Handler returns the handler associated with the timer TM if that timer is set; otherwise it returns null.
The procedure Cancel_Handler clears the timer if it is set. Cancelled is assigned True if the timer was set prior to it being cleared; otherwise it is assigned False.
The function Time_Remaining returns the execution time interval that remains until the timer TM would expire, if that timer is set; otherwise it returns Time_Span_Zero.
The constant Min_Handler_Ceiling is the minimum ceiling priority required for a protected object with a handler to ensure that no ceiling violation will occur when that handler is invoked.
As part of the finalization of an object of type Timer, the timer is cleared.
For all the subprograms defined in this package, Tasking_Error is raised if the task identified by TM.T.all has terminated, and Program_Error is raised if the value of TM.T.all is Task_Identification.Null_Task_Id.
An exception propagated from a handler invoked as part of the expiration of a timer has no effect.
Erroneous Execution
For a call of any of the subprograms defined in this package, if the task identified by TM.T.all no longer exists, the execution of the program is erroneous.
Implementation Requirements
For a given Timer object, the implementation shall perform the operations declared in this package atomically with respect to any of these operations on the same Timer object. The replacement of a handler by a call of Set_Handler shall be performed atomically with respect to the execution of the handler.
AARM note: This prevents various race conditions. In particular it ensures that if an event occurs when Set_Handler is changing the handler then either the new or old handler is executed in response to the appropriate event. It is never possible for a new handler to be executed in response to an old event. end AARM note
When an object of type Timer is finalized, the system resources used by the timer shall be deallocated.
Implementation Permissions
Implementations may limit the number of timers that can be defined for each task. If this limit is exceeded then Timer_Resource_Error is raised.
Notes
A Timer_Handler can be associated with several Timer objects.
!discussion
Modern real-time scheduling policies require that applications have the ability to measure execution time of tasks, and to detect execution-time overruns. Two mechanisms were proposed to achieve these requirements. One was based on a library-level package, and the other one was integrated into the Ada 95 language by creating a new Time type that could be used in the languages time constructs such as the delay until statement or various forms of the select statement. Prototype implementations exist for both.
After discussion by the ARG it was decided that the package mechanism was preferable.
A revision of the AI was produced by IRTAW 12 in response to the requirement to be able to wait for one or more Timers to expire. This has required a restructuring of the earlier solution. The consequence is that the Timer is no longer a protected object. Timer expirations are notified by the system by calling a protected procedure that is passed to it by a parameter in the Timer operations.
A prototype implementations was developed for the initial proposal, using the MaRTE operating system that provides a POSIX.13 interface and includes execution-time clocks and timers.
The implementation of the proposal is very simple, because it does not require modifications to the compiler nor to the runtime system.
Implementations on bare machines or systems without POSIX execution-time clocks and timers would be a bit more complex because the underlying execution-time monitoring functionality would have to be implemented in the scheduler. Document [2] describes one such implementation and it can be seen that it is relatively simple, and that it does not introduce any significant overhead into the scheduler.
The overhead of the implementation is small.
The original proposal included Time_Of taking a Task_Id parameter. This was included so that an implementation could choose to have the Task_Id as part of an Execution_Time value, in case we wished to disallow operations between CPU_Time values of different tasks. Since we allow operations between CPU_Time values from different tasks, the Task_Id is not needed.
The initial proposal left the initial value of the execution time clock of each task unspecified. However, it is valuable to be able to determine a task's absolute execution time, and thus the value should be forced to start at zero. In some implementations this requirement may require saving an initial value of the CPU-Time clock, but this isn't an expensive requirement.
Because the execution time value may be set to zero during the task's activation, the requirement is that the value is set to zero at some unspecified point between the task creation and the end of the task's activation.
The initial proposal had two protected operations inside the Timer: Initialize and Finalize. Initialize took a parameter that identified the execution time clock to be used. These operations appeared unnecessary to reviewers; the parameter could be moved to an access discriminant of the protected type or a parameter of the Set_Handler operation. The reviewers preferred the later solution.
Without these operations, a library implementation of the package on top of the POSIX execution-time services will have to include a component derived from Finalization.Controlled in the protected type to ensure that appropriate initialization and finalization are made. This should not cause a hardship; restricted runtime implementations may have to resort to some "magic", but most implementations will not need extra support. In either case, the overhead is small and bounded.
An earlier version of the timer had procedures Arm and Disarm and three states, armed, armed but expired, and disarmed. It was changed to bring it into line with the style of AI-297 on real-time timers.
!example
Example 1: Stopped task -----------------------
The following is an example of a periodic task that limits its own execution time to some predefined amount called WCET (worst-case execution time). If an execution-time overrun is detected, the task aborts the remainder of its execution, until the next period.
with Ada.Real_Time, Ada.Execution_Time.Timers, Ada.Task_Identification; use Ada; use type Ada.Real_Time.Time;
...
task Periodic_Stopped;
protected Control is entry Wait_Budget_Expiry; procedure Budget_Expired(T : in out Ada.Execution_Time.Timers.Timer); pragma Priority(Ada.Execution_Time.Timers.Min_Handler_Ceiling); private Expired : Boolean := False; end Control;
protected body Control is entry Wait_Budget_Expiry when Expired is begin Expired := False; end Wait_Budget_Expiry;
procedure Budget_Expired(T : in out Ada.Execution_Time.Timers.Timer) is begin Expired := True; end Budget_Expired; end Control;
task body Periodic_Stopped is My_Id : aliased Task_Identification.Task_Id:= Periodic_Stopped'Identity; The_Timer : Ada.Execution_Time.Timers.Timer(My_Id'access);
Next_Start : Real_Time.Time:=Real_Time.Clock; WCET : constant Duration:=1.0E-3; Period : constant Duration:=1.0E-2; begin loop Ada.Execution_Time.Timers.Set_Handler (The_Timer, Real_Time.To_Time_Span(WCET), Control.Budget_Expired'access); select Control.Wait_Budget_Expiry; -- Execution-time overrun detection -- Handle_the_error; then abort null; -- Do_useful_work; end select; Ada.Execution_Time.Timers.Cancel_Handler(The_Timer); Next_Start := Next_Start + Real_Time.To_Time_Span(Period); delay until Next_Start; end loop; end Periodic_Stopped;
...
Example 2: Lowered task -----------------------
In this example, when an execution-time overrun is detected the priority of the task is lowered to allow other lower priority tasks to continue meeting their timing requirements.
A simple implementation of this scheme uses two nested tasks: the Worker task and the Supervisor task. The Supervisor task sleeps until the execution time of the Worker task reaches the instant of the priority change. When this happens the supervisor lowers (or increases) the priority of the worker task. If the Worker task completes the work before the instant of the priority change then it aborts the Supervisor task.
with Ada.Real_Time,Ada.Execution_Time.Timers, Ada.Dynamic_Priorities, System, Ada.Task_Identification; use Ada;
...
protected Control is entry Wait_Budget_Expiry; procedure Budget_Expired(T : in out Ada.Execution_Time.Timers.Timer); pragma Priority(Ada.Execution_Time.Timers.Min_Handler_Ceiling); private Expired : Boolean := False; end Control;
protected body Control is entry Wait_Budget_Expiry when Expired is begin Expired := False; end Wait_Budget_Expiry;
procedure Budget_Expired(T : in out Ada.Execution_Time.Timers.Timer) is begin Expired := True; end Budget_Expired; end Control;
task Worker;
task body Worker is task Supervisor is pragma Priority(System.Priority'Last); end Supervisor;
task body Supervisor is WCET : constant Duration:=1.0E-3; My_Id : aliased Task_Identification.Task_Id:=Supervisor'Identity; The_Timer : Ada.Execution_Time.Timers.Timer(My_Id'access); Low_Prio : System.Priority:=System.Priority'First; begin Ada.Execution_Time.Timers.Set_Handler (The_Timer, Real_Time.To_Time_Span(WCET), Control.Budget_Expired'access); Control.Wait_Budget_Expiry; -- wait for timer overrun Dynamic_Priorities.Set_Priority(Low_Prio,Worker'Identity); end Supervisor; begin -- Do_useful_work; abort Supervisor; end Worker;
...
!corrigendum D.14(01)
Insert new clause:
This clause describes a language-defined package to measure execution time.
Static Semantics
The following language-defined library package exists:
with Ada.Task_Identification; with Ada.Real_Time; use Ada.Real_Time; package Ada.Execution_Time is
type CPU_Time is private; CPU_Time_First : constant CPU_Time; CPU_Time_Last : constant CPU_Time; CPU_Time_Unit : constant := implementation-defined-real-number; CPU_Tick : constant Time_Span;
function Clock (T : Ada.Task_Identification.Task_Id := Ada.Task_Identification.Current_Task) return CPU_Time;
function "+" (Left : CPU_Time; Right : Time_Span) return CPU_Time; function "+" (Left : Time_Span; Right : CPU_Time) return CPU_Time; function "-" (Left : CPU_Time; Right : Time_Span) return CPU_Time; function "-" (Left : CPU_Time; Right : CPU_Time) return Time_Span;
function "<" (Left, Right : CPU_Time) return Boolean; function "<=" (Left, Right : CPU_Time) return Boolean; function ">" (Left, Right : CPU_Time) return Boolean; function ">=" (Left, Right : CPU_Time) return Boolean;
procedure Split (T : in CPU_Time; SC : out Seconds_Count; TS : out Time_Span);
function Time_Of (SC : Seconds_Count; TS : Time_Span := Time_Span_Zero) return CPU_Time;
private ... -- not specified by the language end Ada.Execution_Time;
The execution time or CPU time of a given task is defined as the time spent by the system executing that task, including the time spent executing run-time or system services on its behalf. The mechanism used to measure execution time is implementation defined. It is implementation defined which task, if any, is charged the execution time that is consumed by interrupt handlers and run-time services on behalf of the system.
The type CPU_Time represents the execution time of a task. The set of values of this type corresponds one-to-one with an implementation-defined range of mathematical integers.
The CPU_Time value I represents the half-open execution-time interval that starts with I*CPU_Time_Unit and is limited by (I+1)*CPU_Time_Unit, where CPU_Time_Unit is an implementation-defined real number. For each task, the execution time value is set to zero at the creation of the task.
CPU_Time_First and CPU_Time_Last are the smallest and largest values of the CPU_Time type, respectively.
Dynamic Semantics
CPU_Time_Unit is the smallest amount of execution time representable by the CPU_Time type; it is expressed in seconds. A CPU clock tick is an execution time interval during which the clock value (as observed by calling the Clock function) remains constant. CPU_Tick is the average length of such intervals.
The effects of the operators on CPU_Time and Time_Span are as for the operators defined for integer types.
The function Clock returns the current execution time of the task identified by T; Tasking_Error is raised if that task has terminated; Program_Error is raised if the value of T is Task_Identification.Null_Task_Id.
The effects of the Split and Time_Of operations are defined as follows, treating values of type CPU_Time, Time_Span, and Seconds_Count as mathematical integers. The effect of Split (T, SC, TS) is to set SC and TS to values such that T*CPU_Time_Unit = SC*1.0 + TS*CPU_Time_Unit, and 0.0 <= TS*CPU_Time_Unit < 1.0. The value returned by Time_Of(SC,TS) is the execution-time value T such that T*CPU_Time_Unit=SC*1.0 + TS*CPU_Time_Unit.
Erroneous Execution
For a call of Clock, if the task identified by T no longer exists, the execution of the program is erroneous.
Implementation Requirements
The range of CPU_Time values shall be sufficient to uniquely represent the range of execution times from the task start-up to 50 years of execution time later. CPU_Tick shall be no greater than 1 millisecond.
Documentation Requirements
The implementation shall document the values of CPU_Time_First, CPU_Time_Last, CPU_Time_Unit, and CPU_Tick.
The implementation shall document the properties of the underlying mechanism used to measure execution times, such as the range of values supported and any relevant aspects of the underlying hardware or operating system facilities used.
Metrics
The implementation shall document the following metrics:
Implementation Permissions
Implementations targeted to machines with word size smaller than 32 bits need not support the full range and granularity of the CPU_Time type.
Implementation Advice
When appropriate, implementations should provide configuration mechanisms to change the value of CPU_Tick.
!corrigendum D.14.1(01)
Insert new clause:
This clause describes a language-defined package that provides a facility for calling a handler when a task has used a defined amount of CPU time.
Static Semantics
The following language-defined library package exists:
with System; package Ada.Execution_Time.Timers is
type Timer (T : not null access constant Ada.Task_Identification.Task_Id) is tagged limited private;
type Timer_Handler is access protected procedure (TM : in out Timer);
Min_Handler_Ceiling : constant System.Any_Priority := implementation-defined;
procedure Set_Handler (TM : in out Timer; In_Time : in Time_Span; Handler : in Timer_Handler); procedure Set_Handler (TM : in out Timer; At_Time : in CPU_Time; Handler : in Timer_Handler); function Current_Handler (TM : Timer) return Timer_Handler; procedure Cancel_Handler (TM : in out Timer; Cancelled : out Boolean);
function Time_Remaining (TM : Timer) return Time_Span;
Timer_Resource_Error : exception;
private ... -- not specified by the language end Ada.Execution_Time.Timers;
The type Timer represents an execution-time event for a single task and is capable of detecting execution-time overruns. The access discriminant T identifies the task concerned. The type Timer needs finalization (see 7.6).
An object of type Timer is said to be set if it is associated with a non-null value of type Timer_Handler and cleared otherwise. All Timer objects are initially cleared.
The type Timer_Handler identifies a protected procedure to be executed by the implementation when the timer expires. Such a protected procedure is called a handler.
Dynamic Semantics
When a Timer object is created, or upon the first call of a Set_Handler procedure with the timer as parameter, the resources required to operate an execution-time timer based on the associated execution-time clock are allocated and initialized. If this operation would exceed the available resources, Timer_Resource_Error is raised.
The procedures Set_Handler associate the handler Handler with the timer TM; if Handler is null, the timer is cleared, otherwise it is set. The first procedure Set_Handler loads the timer TM with an interval specified by the Time_Span parameter. In this mode, the timer TM expires when the execution time of the task identified by TM.T.all has increased by In_Time; if In_Time is less than or equal to zero, the timer expires immediately. The second procedure Set_Handler loads the timer TM with the absolute value specified by At_Time. In this mode, the timer TM expires when the execution time of the task identified by TM.T.all reaches At_Time; if the value of At_Time has already been reached when Set_Handler is called, the timer expires immediately.
A call of a procedure Set_Handler for a timer that is already set replaces the handler and the (absolute or relative) execution time; if Handler is not null, the timer remains set.
When a timer expires, the associated handler is executed, passing the timer as parameter. The initial action of the execution of the handler is to clear the event.
The function Current_Handler returns the handler associated with the timer TM if that timer is set; otherwise it returns null.
The procedure Cancel_Handler clears the timer if it is set. Cancelled is assigned True if the timer was set prior to it being cleared; otherwise it is assigned False.
The function Time_Remaining returns the execution time interval that remains until the timer TM would expire, if that timer is set; otherwise it returns Time_Span_Zero.
The constant Min_Handler_Ceiling is the minimum ceiling priority required for a protected object with a handler to ensure that no ceiling violation will occur when that handler is invoked.
As part of the finalization of an object of type Timer, the timer is cleared.
For all the subprograms defined in this package, Tasking_Error is raised if the task identified by TM.T.all has terminated, and Program_Error is raised if the value of TM.T.all is Task_Identification.Null_Task_Id.
An exception propagated from a handler invoked as part of the expiration of a timer has no effect.
Erroneous Execution
For a call of any of the subprograms defined in this package, if the task identified by TM.T.all no longer exists, the execution of the program is erroneous.
Implementation Requirements
For a given Timer object, the implementation shall perform the operations declared in this package atomically with respect to any of these operations on the same Timer object. The replacement of a handler by a call of Set_Handler shall be performed atomically with respect to the execution of the handler.
When an object of type Timer is finalized, the system resources used by the timer shall be deallocated.
Implementation Permissions
Implementations may limit the number of timers that can be defined for each task. If this limit is exceeded then Timer_Resource_Error is raised.
NOTES
46 A Timer_Handler can be associated with several Timer objects.
!ACATS test
ACATS test(s) should be created for this package.
!appendix

From: Alan Burns
Sent: Monday, August 5, 2002  8:28 AM

I attach the AI from Michael Harbour on CPU clocks.
I'm not sure if this issue actually had an AI number.

For the real-time folks this is the second most important
issue (after Ravenscar).

You can see that the AI contains two possible approches, a
new timer type or a new library package. It would be useful
at the next ARG to have a general discusion about the approach
to be take.

[Editor's note: The attachment is draft 1 of this AI. The draft credited
authors of Michael Gonzalez Harbour, Javier Miranda, and Mario Aldea
on behalf of IRTAW11].

****************************************************************

Additional references for the original proposal:

 [1] A Proposal to Integrate the POSIX Execution-Time Clocks into Ada
     95. Technical Report, June, 2002, By J. Miranda Gonzalez and
     M. Gonzalez Harbour.

 [2] Implementing and using Execution Time Clocks in Ada Hard
     Real-Time Applications. By Gonzalez Harbour M., Aldea Rivas M.,
     Gutierrez Garcia J.J., Palencia Gutierrez J.C. International
     Conference on Reliable Software Technologies, Ada-Europe'98,
     Uppsala, Sweden, in Lecture Notes on Computer Science No. 1411,
     Junio, 1998, ISBN:3-540-64563-5, pp. 91,101.

 [3] Extending Ada's Real-Time Systems Annex with the POSIX Scheduling
     Services. By: Mario Aldea Rivas and Michael Gonzalez
     Harbour. IRTAW-2000, Las Navas, Avila, Spain.

****************************************************************

From: Alan Burns
Sent: Tuesday, August 10, 2004  10:50 PM

I attach new version of 307 - execution time clocks
[Editor's note: This is version /07.]

Main change is the spliting of the package into 2.
Also handler is now 'not null'. Text for meaning of
operators was there al the time; other changes to
words from minutes made.

****************************************************************

From: John Barnes
Sent: Tuesday, March 29, 2005  3:08 AM

I have been writing the part of the Rationale on real time and when talking
about the package Ada.Execution_Time (AI-307) I said

The constants CPU_Time_First and CPU_Time_Last give the range of values of
CPU_Time. CPU_Tick gives the average interval during which successive calls
of Clock give the same value and thus is a measure of the accuracy whereas
CPU_Time_Unit gives the unit of time measured in seconds. We are guaranteed
that CPU_Tick is no greater than one millisecond and that the range of
values of CPU_Time is at least 50 years.

Randy is concerned about this. He said

 John notes: "We are guaranteed that CPU_Tick is no greater than one
millisecond and that the range of values of CPU_Time is at least 50 years."
Which is interesting because it guarentees that this package cannot be
implemented on Windows. Well, you could say the tick is 100 nanoseconds,
because the functions in question return 100 nanoseconds. But the actual
results have a granularity of 10 milliseconds. (I tried a number of
different approaches, and concluded that this was all the accuracy that was
available for "User_Time" and "Kernel_Time".) I wonder if we need to address
this somehow.

So I put a placeholder in the rat saying "Randy is worried about this"

And  then he said

"And I still am. Sorry, I'm still concerned about the requirements for
CPU_Time because CPU_Tick is 10 millisecond on Windows (in practice, not in
definition -- but the definition of CPU_Tick is definitely about practice,
not the accuracy of API calls). Note that I personally don't care (I don't
believe Windows is real-time enough to make the claim that it supports
real-time and thus this Annex), but I know that others have been concerned
about it in the past. Someone should write a note to the ARG list (I'd like
to hear Robert's opinion)."

So, can we have any comments please.

****************************************************************

From: Bob Duff
Sent: Wednesday, March 30, 2005  7:06 AM

> "And I still am. Sorry, I'm still concerned about the requirements for
> CPU_Time because CPU_Tick is 10 millisecond on Windows (in practice, not in
> definition -- but the definition of CPU_Tick is definitely about practice,
> not the accuracy of API calls). Note that I personally don't care (I don't
> believe Windows is real-time enough to make the claim that it supports
> real-time and thus this Annex), but I know that others have been concerned
> about it in the past. Someone should write a note to the ARG list (I'd like
> to hear Robert's opinion)."

I agree with the part where Randy says, "I don't believe Windows is
real-time enough to make the claim that it supports real-time and thus
this Annex".  Windows compilers can support this package as best they
can, but not claim conformance.  Seems OK to me.

On the other hand, these kinds of accuracy requirements seem fairly
silly, because in practise, compiler writers will provide whatever the
underlying hardware and OS provide.  In that sense, saying things like
"no greater than one millisecond" doesn't place any requirement on any
Ada implementation.  We're not (perhaps unfortunately) in a position to
dictate what hardware and operating systems do, so why the pretense?
Furthermore, people who *can* support better than one millisecond will
do so whether the RM says so or not.

Therefore, I wouldn't mind dropping the requirement.

****************************************************************

From: Randy Brukardt
Sent: Wednesday, March 30, 2005  7:01 PM

...
> I agree with the part where Randy says, "I don't believe Windows is
> real-time enough to make the claim that it supports real-time and thus
> this Annex".  Windows compilers can support this package as best they
> can, but not claim conformance.  Seems OK to me.

This subject has come up in other contexts (esp. on the FRT), and generally
the conclusion was that it would be Ada, not Windows that would take the
blame for such a rule. People would have a hard time seeing that Windows
isn't real-time if the capabilities are in fact good enough for their
application. And then if Ada won't let them program their application, I'm
sure that they can find another language to use. That's not the message we
want to send for Ada!

> On the other hand, these kinds of accuracy requirements seem fairly
> silly, because in practise, compiler writers will provide whatever the
> underlying hardware and OS provide.  In that sense, saying things like
> "no greater than one millisecond" doesn't place any requirement on any
> Ada implementation.  We're not (perhaps unfortunately) in a position to
> dictate what hardware and operating systems do, so why the pretense?
> Furthermore, people who *can* support better than one millisecond will
> do so whether the RM says so or not.
>
> Therefore, I wouldn't mind dropping the requirement.

That seems like a better solution. It's fair to have some requirements on
the range of CPU_Time (that's not totally forced by the underlying system),
but requirements on CPU_Tick are silly. That's probably true for
Real_Time.Tick as well (here, the native Windows tick is also 10
milliseconds, but there is a high-performance timer that can be used with
some difficulty to provide better. I don't see any way to safely use the
high-performance timer for this case, because it is global, not per-thread).

Both Tick and CPU_Tick are required to be documented (in fact, it says so in
two different places, and it's implied in a third place), so there isn't any
likely problem of confusion - users can check what they get.

****************************************************************

From: Robert A Duff
Sent: Wednesday, March 30, 2005  7:43 PM

> This subject has come up in other contexts (esp. on the FRT), and generally
> the conclusion was that it would be Ada, not Windows that would take the
> blame for such a rule.

If I recall correctly, some years ago, there was an issue where the Ada
RM said roughly 32 real-time priority levels are required, and the FRT
decided that it was OK that Windows has only 7 or so.  I don't remember
what my opinion was at that time, but right now, I'm inclined to say:
let's not try to make the Ada standard impose requirements on
OS/hardware.  And let somebody else (not the Ada RM) decide which
OS/hardware environments are appropriate for real-time/embedded/whatever
systems.

****************************************************************

From: Alan Burns
Sent: Wednesday, March 30, 2005  7:59 PM

Just picked up this trail of emails on CPU_Tick

The definition of the new package follows closely the original
real-time package - which had these minimum requirements for
accuracy. In Ada95 I believe it was felt that some minimum
was needed to 'encourage' implementers to really do real-time
kernels and not just run Ada on top of ordinary OSs.

Windows can support real-time if the timing requirements are at
the seconds levels. Other slimmer versions of Windows can do better,
but I do not know what the size of CPU_Tick is on these (WindowCE
for example).

For the 'usual' rnage of real-time requirements for embedded systems
ordinary Windows is not a real-time OS.

I have no real problem with the miniumum requirements been expressed
in other ways (implementation advice?) - but the message should be
there somewhere that some level of useful accuracy would be a good idea!

****************************************************************

From: Robert Dewar
Sent: Thursday, March 31, 2005  6:09 AM

...
> If I recall correctly, some years ago, there was an issue where the Ada
> RM said roughly 32 real-time priority levels are required, and the FRT
> decided that it was OK that Windows has only 7 or so.  I don't remember
> what my opinion was at that time, but right now, I'm inclined to say:
> let's not try to make the Ada standard impose requirements on
> OS/hardware.  And let somebody else (not the Ada RM) decide which
> OS/hardware environments are appropriate for real-time/embedded/whatever
> systems.

The Ada standard never imposes requirements that cannot be met. That's clear
in the RM

     6  Contain no variations except those explicitly permitted by this
        International Standard, or those that are impossible or
        impractical to avoid given the implementation's execution
        environment;

That clause (people with good historical knowledge can associate a well
known Ada 83 AI with this) is there precisely so we do not water down
requirements just because some OS cannot meet them.

****************************************************************

From: Alan Burns
Sent: Thursday, March 31, 2005  6:25 AM

A very good point, I had forgotten this global statement

****************************************************************

From: Robert I. Eachus
Sent: Thursday, March 31, 2005  2:43 PM

> That clause (people with good historical knowledge can associate a well
> known Ada 83 AI with this) is there precisely so we do not water down
> requirements just because some OS cannot meet them.

I totally agree.  A Windows implementation of the Real Time Annex should
document the issues and move on.

However, it does bring up an interesting issue.  At MITRE I worked with
real-time software as diverse as radars and aircraft landing systems.
Very few applications--radar of various flavors being the usual
exceptions--required deadlines shorter than 150 milliseconds.  It may
seem strange that a fighter jet's fight guidance software uses a 40
cycle or so cyclic executive--but you can't, and don't want to, move the
control surfaces even that fast.

Would it be worth having two support levels for the real-time Annex?  I
don't think so. In practice the division is between "bare metal"
implementations and those that run on top of an OS, even a real-time
executive.  There are a lot of features where, in hard-real time systems
you *have* to know the worst case execution times for all higher
priority interrupts, and where in (should I call it soft real-time?)
other systems rough guidance from the OS or compiler implementor is fine.

In practice I think everyone who cares is aware of this divide.  It
might be nice to put a note in the RT Annex that references 1.1.3(6),
something like:

"*Note:* Implementations of this Annex may deviate from the
specifications given here, for example when run under an operating
system. (See 1.1.3)   In such a situation, deviations from expected
behavior can be caused by other executing programs and will be difficult
to determine in advance.  Implementors should document where such
deviations can be expected.  However, it will not be possible to
guarantee any real-time performance without an examination of the
expected environment at execution time."

In other words, "Silly Rabbit! Of course the RM can't tell you anything
about performance if other high-priority programs are running."  But
also reminding implementors that documenting such deviations where they
are understood is very helpful.

****************************************************************

From: Stephen Michell
Sent: Tuesday, April 5, 2005  7:44 AM

I'm going to agree and disagree with Robert at the same time. The term
"Real time" has changed in context in the last few years. It is true
that human-sized devices such as airplanes or tanks or radards need to
measure real world events in the 1ms-100ms range, but there are now
whole classes of systems running in the 1microsecond range and even
sub-microsecond range. The most obvious that comes to mind are
communications systems.

We are now seeing processors that execute in the range of a few
megahertz  up to a few gigahertz.. CPU_Tick of less than 1ms makes
absolulutely no sense, and words about there being 50 years worth of
CPU_Ticks in 32 bits doesn't do anything. If CPU_Tick even closely
resembles the rate at which a processor functions then we would need to
make CPU_Tick at least 1 microsecond  and if an implementation has
CPU_Tick of 1ns and uses a 32-bit counter then the counter would roll
over every 2 seconds or 4 seconds, depending upon whether or not you
used signed integers.

We clearly need to be open to implementations that use 16, 32 or 64 bits
and have CPU_Tick of a few (say 100) microseconds but we also need be
able to address the needs of much faster/larger RT systems.

I would suggest that we leave the wording alone. The note should say
that a CPU_Tick of 1ms gives a 50 yr range before being zeroed, a 1
microsecond CPU_Tick uses its whole range in 1/2 hour and faster rates
for CPU_Tick require 64-bit integers to maintain sufficient time for
real-world applications.

****************************************************************

From: Robert Dewar
Sent: Tuesday, April 5, 2005  5:17 PM

> I'm going to agree and disagree with Robert at the same time.

I read your msg several times and was unable to detect any respect
in which you disagreed with me.

****************************************************************

From: Robert I. Eachus
Sent: Tuesday, April 5, 2005  7:32 PM

I think I was the Robert in question here.  As far as what Steve said, I
thought about mentioning communications software, especially military
radios with frequency hopping as another example of hard real time.
However, I didn't think it added to the discussion.  There are real-time
environments where time budgets are short and critical, and where  tasks
not a part of the main application just won't be run.  There are also
evironments where real-time behavior is at best an afterthought.  The
implementor should understand his target market, and not let anything in
the ARM prevent the implementor from serving that market.

So all I was saying was that some sort of indication in the RM that we
don't expect hard real-time implementations on top of non-real-time
operating systems might help.  I know what the RM says, and what it
intends to imply, but we still get implementors who don't grok
1.1.3(6).  This just seems to be a good place for a note in the RM, or
perhaps a "to be honest" in the AARM to say, "Look guys we know that
there are things you can't do on top of most operating systems, and we
are not asking you to fake it, just to describe the expected execution
environment, and where or why it causes problems."

****************************************************************

From: Stephen Michell
Sent: Tuesday, April 5, 2005  8:34 PM

> I read your msg several times and was unable to detect any respect
> in which you disagreed with me.

ah, the other Robert. I quoted part of his message. Sorry about the mix-up.

****************************************************************

From: Bob Duff
Sent: Sunday, April 3, 2005 12:08 PM

I've been reading a draft of John Barnes' paper "Rationale for Ada 2005:
4 Tasking and Real-Time", and I think I've discovered several minor bugs
in AI's 307, 354, and 357.  I'm looking at version 10 of AI-307 (version
1.13 in CVS), version 6 of AI-354 (version 1.8 in CVS), and
AI95-00357/08 (version 1.12 in CVS).

Very nice paper, by the way.

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

For Timers, we have:

For each task, the execution time value is set to zero at
some unspecified point between the creation of the task and the start
of the activation of the task.

This seems like a bad idea, because you can ask for the time before it's been
initialized.  I see no benefit to allowing this implementation freedom.  It
should be zeroed at task creation.  It makes no sense to me that a task's
priority is set on creation, but it's CPU time is left uninitialized for a
while.

If you don't agree, then you have to specify what happens if the program *does*
query an uninitialized time.  Erroneous?  Some implementation-defined value?
Bounded error (like other uninit vars?).  Better to eliminate the
tripping hazard.

----

Package Ada.Dispatching.EDF has the same problem: it makes no sense to
set the priority on task creation, but leave the deadline undefined for
some unspecified period of time.

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

We have:

package Ada.Execution_Time.Timers is

   type Timer (T : access Ada.Task_Identification.Task_ID) is
      limited private;

The discriminant should be "access constant".

Well, it shouldn't be "access" at all, but that's needed to get around
the fact that discriminants can't be of a private type -- an annoying
restriction indeed.  Are we sure we want to expose this language-design
flaw?  You can't say "X: Timer(T'Identity'Access)".  You have to declare
an aliased object.  Yuck.  Maybe we should get rid of the discriminant,
and just have a Set_Task operation.

The discriminant raises other issues.  For example:

    My_Id: aliased Task_Id := ...;
    My_Timer: Timer(My_Id'Access);
    ...
    My_Id := Something_Else;

Does that change the task associated with the timer?  Or is the implementation
of Timer supposed to make a copy of the Task_Id during initialization?
Seems to me the AI needs to say, one way or the other.

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

It's not clear what happens when a Timer is finalized.  The AI says resources
are deallocated (which seems like it goes without saying).  But if the Timer is
Finalized while the task is still running, it seems like we need to say that we
quit timing it -- the handler won't get called.

I suppose a similar issue arises for Group_Budgets.

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

Packages Ada.Execution_Time, Ada.Execution_Time.Timers, and
Ada.Execution_Time.Group_Budgets all have this sentence:

For all the operations and types defined in this package, Tasking_Error
is raised if the task identified by T has terminated. Program_Error
is raised if the value of T is Null_Task_ID.

It doesn't make sense in all cases.

First, I don't understand the "and types" part.  Types don't raise exceptions.

Ada.Execution_Time has only one operation (Clock) that has anything to do with
tasks.  Split has a parameter called T, but it's not a Task_Id.  The arithmetic
operations aren't going to raise T_E.  So it should be reworded to talk about
Clock.

For Timers, there are no parameters called T.  Probably what we want is to
replace "T" with "TM.T.all"; all the operations have a TM parameter.

I suppose creating a Timer object should also raise T_E if T.all is terminated.
Maybe that's what the "and types" part was getting at -- but it's not clear.

For Group_Budgets, we need to talk about just those operations that take a
Task_Id parameter.

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

Group_Budgets has:

  function Members(GB: Group_Budget) return Task_Array;

It seems like a conversion the other way would be useful.

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

****************************************************************

From: Randy Brukardt
Sent: Sunday, April 3, 2005  7:09 PM

> I've been reading a draft of John Barnes' paper "Rationale for Ada 2005:
> 4 Tasking and Real-Time", and I think I've discovered several minor bugs
> in AI's 307, 354, and 357.  I'm looking at version 10 of AI-307 (version
> 1.13 in CVS), version 6 of AI-354 (version 1.8 in CVS), and
> AI95-00357/08 (version 1.12 in CVS).

The AIs have been rewritten a lot since they were last posted (by pretty
much everyone!), so I wouldn't draw too many conclusions from that text...

> Very nice paper, by the way.
>
> ----------------
>
> For Timers, we have:

You mean execution time. Timers are something else. Execution-time timers
are something else still. Prepare to be confused. :-)

> For each task, the execution time value is set to zero at
> some unspecified point between the creation of the task and the start
> of the activation of the task.
>
> This seems like a bad idea, because you can ask for the time before it's been
> initialized.  I see no benefit to allowing this implementation freedom. It
> should be zeroed at task creation.  It makes no sense to me that a task's
> priority is set on creation, but it's CPU time is left uninitialized for a
> while.

I recall that this question was asked during the deliberations on the AI,
and I recall being told it was important. I think the issue is that these
usually come in some way from the kernel (like POSIX or WIndows), and
setting the zero point in some precise way is impractical. In large part
because determining the zero value (these things don't start at zero
naturally) will take some execution time (a kernel call), and we don't want
to require that overhead at creation. Best let Alan or another real-time
person explain that further.

> If you don't agree, then you have to specify what happens if the program *does*
> query an uninitialized time.  Erroneous?  Some implementation-defined value?
> Bounded error (like other uninit vars?).  Better to eliminate the
> tripping hazard.

Since these are queried by Task_Id, we already have erroneous cases for
them. So it doesn't seem to be a major problem. But if you think this is
worth worrying about, I think Program_Error would be appropriate (surely the
task knows whether the timer has been initialized). I don't see any reason
for a bounded error here (either you get the right answer or no answer,
presuming the Task_Id is good).

> ----
>
> Package Ada.Dispatching.EDF has the same problem: it makes no sense to
> set the priority on task creation, but leave the deadline undefined for
> some unspecified period of time.

I don't think the deadline is needed until the task is running.

> ----------------
>
> We have:
>
> package Ada.Execution_Time.Timers is
>
>    type Timer (T : access Ada.Task_Identification.Task_ID) is
>       limited private;
>
> The discriminant should be "access constant".
>
> Well, it shouldn't be "access" at all, but that's needed to get around
> the fact that discriminants can't be of a private type -- an annoying
> restriction indeed.  Are we sure we want to expose this language-design
> flaw?  You can't say "X: Timer(T'Identity'Access)".  You have to declare
> an aliased object.  Yuck.  Maybe we should get rid of the discriminant,
> and just have a Set_Task operation.
>
> The discriminant raises other issues.  For example:
>
>     My_Id: aliased Task_Id := ...;
>     My_Timer: Timer(My_Id'Access);
>     ...
>     My_Id := Something_Else;
>
> Does that change the task associated with the timer?  Or is the
> implementation
> of Timer supposed to make a copy of the Task_Id during initialization?
> Seems to me the AI needs to say, one way or the other.

Ugh.

> ----------------
>
> It's not clear what happens when a Timer is finalized.  The AI says resources
> are deallocated (which seems like it goes without saying).  But if the Timer is
> Finalized while the task is still running, it seems like we need to say that we
> quit timing it -- the handler won't get called.
>
> I suppose a similar issue arises for Group_Budgets.

This would seem to apply to all three kinds of timers; it would seem to
apply to them all.

> ----------------
>
> Packages Ada.Execution_Time, Ada.Execution_Time.Timers, and
> Ada.Execution_Time.Group_Budgets all have this sentence:
>
> For all the operations and types defined in this package, Tasking_Error
> is raised if the task identified by T has terminated. Program_Error
> is raised if the value of T is Null_Task_ID.

This was completely replaced in all of the AIs.

> It doesn't make sense in all cases.
>
> First, I don't understand the "and types" part.  Types don't
> raise exceptions.
>
> Ada.Execution_Time has only one operation (Clock) that has anything to do with
> tasks.  Split has a parameter called T, but it's not a Task_Id. The arithmetic
> operations aren't going to raise T_E.  So it should be reworded to talk about
> Clock.
>
> For Timers, there are no parameters called T.  Probably what we want is to
> replace "T" with "TM.T.all"; all the operations have a TM parameter.
>
> I suppose creating a Timer object should also raise T_E if T.all is terminated.
> Maybe that's what the "and types" part was getting at -- but it's not clear.
>
> For Group_Budgets, we need to talk about just those operations that take a
> Task_Id parameter.

I think all of the above issues were already handled. But feel free to
review the actual AARM text when it is available.

> ----------------
>
> Group_Budgets has:
>
>   function Members(GB: Group_Budget) return Task_Array;
>
> It seems like a conversion the other way would be useful.

What do you mean?

    function What_the_Heck_is_this (T : Task_Array) return Group_Budget;

makes no sense at all, given that budgets are independent objects. And you
really don't want to be returning budget objects (these are limited, and
creating new ones makes no sense).

So what *are* you thinking??

> ----------------

General comment: A number of us have been fighting through various issues in
the remaining parts of the AARM. We've just about completed that. Reviewing
old versions isn't particularly helpful (you're more likely to catch bugs
already known than ones not known), and it exhausts those of us who work on
this every day.

Given that there will be new versions of many of the AIs, of the Amendment,
and of the AARM no later than next Tuesday, and a meeting will follow that
quickly, it would be best to save up everyone's energy for those events.

****************************************************************

From: Bob Duff
Sent: Monday, April 4, 2005  10:51 AM

> I recall that this question was asked during the deliberations on the AI,
> and I recall being told it was important. I think the issue is that these
> usually come in some way from the kernel (like POSIX or WIndows), and
> setting the zero point in some precise way is impractical. In large part
> because determining the zero value (these things don't start at zero
> naturally) will take some execution time (a kernel call), and we don't want
> to require that overhead at creation. Best let Alan or another real-time
> person explain that further.

I believe that some implementations might want to interact with the
OS/kernel at the activation point rather than at the creation point.
(In between would surprise me!)  But I don't see why this
implementation detail needs to leak into the semantics as a tripping
hazard.

> > Group_Budgets has:
> >
> >   function Members(GB: Group_Budget) return Task_Array;
> >
> > It seems like a conversion the other way would be useful.
>
> What do you mean?
>
>     function What_the_Heck_is_this (T : Task_Array) return Group_Budget;

Something like:

  procedure Add_Tasks
    (GB: in out Group_Budget; Tasks : Task_Array);

> Given that there will be new versions of many of the AIs, of the Amendment,
> and of the AARM no later than next Tuesday, and a meeting will follow that
> quickly, it would be best to save up everyone's energy for those events.

OK.  Pascal, do you want to put these issues on the agenda for the next
meeting?  If you say my request to change the semantics to "at creation
time" is out of line (already decided), I still think there are some
bugs -- holes where the semantics is unclear.

****************************************************************

From: Robert I. Eachus
Sent: Tuesday, April 5, 2005  2:37 AM

>We have:
>
>package Ada.Execution_Time.Timers is
>
>   type Timer (T : access Ada.Task_Identification.Task_ID) is
>      limited private;
>
>The discriminant should be "access constant".
>
>Well, it shouldn't be "access" at all, but that's needed to get around
>the fact that discriminants can't be of a private type -- an annoying
>restriction indeed.  Are we sure we want to expose this language-design
>flaw?  You can't say "X: Timer(T'Identity'Access)".  You have to declare
>an aliased object.  Yuck.  Maybe we should get rid of the discriminant,
>and just have a Set_Task operation.
>
>The discriminant raises other issues.  For example:
>
>    My_Id: aliased Task_Id := ...;
>    My_Timer: Timer(My_Id'Access);
>    ...
>    My_Id := Something_Else;
>
>Does that change the task associated with the timer?  Or is the implementation
>of Timer supposed to make a copy of the Task_Id during initialization?
>Seems to me the AI needs to say, one way or the other.

Maybe it is time to fix all this the right way.  Operations on Task_IDs
originally went into an Annex for good and sufficient reasons.  But we
were too dumb to realize that making Task_ID a discrete type and putting
it in Standard made lots of sense.  Does any implementation use
something other than a discrete type for Task_ID?  Yeah, I know that it
is probably an address, but we don't want to make that potential
conversion visible.  But just having an implementation defined discrete
type in Standard would recognize the reality that every task does have
an ID.  (It isn't a distributed cost, since such an ID is needed by the
task scheduler.) The rest of Task_Identification can be left as a package.

If that is too radical, just make Task_ID a discrete type.  The real
reason for doing this is that putting tasks in containers is going to be
a common thing to want to do--at least for some styles of programming.
Technically having Task_ID as a private type is not going to make that
impossible, but if it is known to be discrete, AKA word-sized, container
implementations can be more efficient.

****************************************************************

From: Bob Duff
Sent: Tuesday, April 5, 2005  6:58 AM

> Maybe it is time to fix all this the right way.  Operations on Task_IDs
> originally went into an Annex for good and sufficient reasons.  But we
> were too dumb to realize that making Task_ID a discrete type and putting
> it in Standard made lots of sense.  Does any implementation use
> something other than a discrete type for Task_ID?  Yeah, I know that it
> is probably an address, but we don't want to make that potential
> conversion visible.

In AdaMagic, Task_Id is a record containing an access type pointing to a
TCB, plus a generation count used to detect dangling Ids.  I believe
that in GNAT it's access-to-TCB.  Not discrete, in either case.

Note that we considered making it a discrete type during Ada 9X.
In fact, I think we considered requiring/recommending that it
be dense, so you could index arrays by it.  I guess implementation
freedom was considered more important; hence a private type.

The "right" fix would be to allow private types as discrims.
That ain't gonna happen!  It would probably cause all manner
of language anomalies.

****************************************************************

From: Tucker Taft
Sent: Tuesday, April 5, 2005  6:54 AM

For what it is worth, our Task-ID is a pair consisting
of a pointer and a "generation number."  The pointer
points directly at a "task control block," the generation
number comes from a field in a TCB that is bumped each time
the TCB is reused for a different task.  Once storage is
used as a TCB, it is never reclaimed, but can be reused
for a different task once the earlier one terminates.
If the generation number in the task-id doesn't match the one in
the TCB, then we presume the task has terminated.

This avoids our having to set any kind of pre-set limit
on the number of tasks, which might be necessary if
we only had an integral task number, which would presumably
be an index into an array of TCB pointers.  We could
change from our current approach if there were compelling
advantages to some other, but I guess I'm not convinced
that being a non-private integer type is even a good
thing, much less a "better" thing.  It is true it
would allow us to use them as discriminants, but
that doesn't seem that important overall.

****************************************************************

From: Pascal Leroy
Sent: Wednesday, April 6, 2005  8:14 PM

> OK.  Pascal, do you want to put these issues on the agenda
> for the next meeting?  If you say my request to change the
> semantics to "at creation time" is out of line (already
> decided), I still think there are some bugs -- holes where
> the semantics is unclear.

Fine with me, but you'll have to write an AI, and do it real quick.
Please ask Randy for the latest version of the relevant AIs, so that you
start from the most current wording.

****************************************************************


Questions? Ask the ACAA Technical Agent