1------------------------------------------------------------------------------
2--                                                                          --
3--                 GNAT RUN-TIME LIBRARY (GNARL) COMPONENTS                 --
4--                                                                          --
5--                         A D A . R E A L _ T I M E                        --
6--                                                                          --
7--                                 B o d y                                  --
8--                                                                          --
9--             Copyright (C) 1991-2017, Florida State University            --
10--                     Copyright (C) 1995-2019, AdaCore                     --
11--                                                                          --
12-- GNAT is free software;  you can  redistribute it  and/or modify it under --
13-- terms of the  GNU General Public License as published  by the Free Soft- --
14-- ware  Foundation;  either version 3,  or (at your option) any later ver- --
15-- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
16-- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
17-- or FITNESS FOR A PARTICULAR PURPOSE.                                     --
18--                                                                          --
19-- As a special exception under Section 7 of GPL version 3, you are granted --
20-- additional permissions described in the GCC Runtime Library Exception,   --
21-- version 3.1, as published by the Free Software Foundation.               --
22--                                                                          --
23-- You should have received a copy of the GNU General Public License and    --
24-- a copy of the GCC Runtime Library Exception along with this program;     --
25-- see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    --
26-- <http://www.gnu.org/licenses/>.                                          --
27--                                                                          --
28-- GNARL was developed by the GNARL team at Florida State University.       --
29-- Extensive contributions were provided by Ada Core Technologies, Inc.     --
30--                                                                          --
31------------------------------------------------------------------------------
32
33with System.Tasking;
34with Unchecked_Conversion;
35
36package body Ada.Real_Time with
37  SPARK_Mode => Off
38is
39
40   ---------
41   -- "*" --
42   ---------
43
44   --  Note that Constraint_Error may be propagated
45
46   function "*" (Left : Time_Span; Right : Integer) return Time_Span is
47      pragma Unsuppress (Overflow_Check);
48   begin
49      return Time_Span (Duration (Left) * Right);
50   end "*";
51
52   function "*" (Left : Integer; Right : Time_Span) return Time_Span is
53      pragma Unsuppress (Overflow_Check);
54   begin
55      return Time_Span (Left * Duration (Right));
56   end "*";
57
58   ---------
59   -- "+" --
60   ---------
61
62   --  Note that Constraint_Error may be propagated
63
64   function "+" (Left : Time; Right : Time_Span) return Time is
65      pragma Unsuppress (Overflow_Check);
66   begin
67      return Time (Duration (Left) + Duration (Right));
68   end "+";
69
70   function "+" (Left : Time_Span; Right : Time) return Time is
71      pragma Unsuppress (Overflow_Check);
72   begin
73      return Time (Duration (Left) + Duration (Right));
74   end "+";
75
76   function "+" (Left, Right : Time_Span) return Time_Span is
77      pragma Unsuppress (Overflow_Check);
78   begin
79      return Time_Span (Duration (Left) + Duration (Right));
80   end "+";
81
82   ---------
83   -- "-" --
84   ---------
85
86   --  Note that Constraint_Error may be propagated
87
88   function "-" (Left : Time; Right : Time_Span) return Time is
89      pragma Unsuppress (Overflow_Check);
90   begin
91      return Time (Duration (Left) - Duration (Right));
92   end "-";
93
94   function "-" (Left, Right : Time) return Time_Span is
95      pragma Unsuppress (Overflow_Check);
96   begin
97      return Time_Span (Duration (Left) - Duration (Right));
98   end "-";
99
100   function "-" (Left, Right : Time_Span) return Time_Span is
101      pragma Unsuppress (Overflow_Check);
102   begin
103      return Time_Span (Duration (Left) - Duration (Right));
104   end "-";
105
106   function "-" (Right : Time_Span) return Time_Span is
107      pragma Unsuppress (Overflow_Check);
108   begin
109      return Time_Span_Zero - Right;
110   end "-";
111
112   ---------
113   -- "/" --
114   ---------
115
116   --  Note that Constraint_Error may be propagated
117
118   function "/" (Left, Right : Time_Span) return Integer is
119      pragma Unsuppress (Overflow_Check);
120      pragma Unsuppress (Division_Check);
121
122      --  RM D.8 (27) specifies the effects of operators on Time_Span, and
123      --  rounding of the division operator in particular, to be the same as
124      --  effects on integer types. To get the correct rounding we first
125      --  convert Time_Span to its root type Duration, which is represented as
126      --  a 64-bit signed integer, and then use integer division.
127
128      type Duration_Rep is range -(2 ** 63) .. +((2 ** 63 - 1));
129
130      function To_Integer is
131        new Unchecked_Conversion (Duration, Duration_Rep);
132   begin
133      return Integer
134               (To_Integer (Duration (Left)) / To_Integer (Duration (Right)));
135   end "/";
136
137   function "/" (Left : Time_Span; Right : Integer) return Time_Span is
138      pragma Unsuppress (Overflow_Check);
139      pragma Unsuppress (Division_Check);
140   begin
141      --  Even though checks are unsuppressed, we need an explicit check for
142      --  the case of largest negative integer divided by minus one, since
143      --  some library routines we use fail to catch this case. This will be
144      --  fixed at the compiler level in the future, at which point this test
145      --  can be removed.
146
147      if Left = Time_Span_First and then Right = -1 then
148         raise Constraint_Error with "overflow";
149      end if;
150
151      return Time_Span (Duration (Left) / Right);
152   end "/";
153
154   -----------
155   -- Clock --
156   -----------
157
158   function Clock return Time is
159   begin
160      return Time (System.Task_Primitives.Operations.Monotonic_Clock);
161   end Clock;
162
163   ------------------
164   -- Microseconds --
165   ------------------
166
167   function Microseconds (US : Integer) return Time_Span is
168   begin
169      return Time_Span_Unit * US * 1_000;
170   end Microseconds;
171
172   ------------------
173   -- Milliseconds --
174   ------------------
175
176   function Milliseconds (MS : Integer) return Time_Span is
177   begin
178      return Time_Span_Unit * MS * 1_000_000;
179   end Milliseconds;
180
181   -------------
182   -- Minutes --
183   -------------
184
185   function Minutes (M : Integer) return Time_Span is
186   begin
187      return Milliseconds (M) * Integer'(60_000);
188   end Minutes;
189
190   -----------------
191   -- Nanoseconds --
192   -----------------
193
194   function Nanoseconds (NS : Integer) return Time_Span is
195   begin
196      return Time_Span_Unit * NS;
197   end Nanoseconds;
198
199   -------------
200   -- Seconds --
201   -------------
202
203   function Seconds (S : Integer) return Time_Span is
204   begin
205      return Milliseconds (S) * Integer'(1000);
206   end Seconds;
207
208   -----------
209   -- Split --
210   -----------
211
212   procedure Split (T : Time; SC : out Seconds_Count; TS : out Time_Span) is
213      T_Val : Time;
214
215   begin
216      --  Special-case for Time_First, whose absolute value is anomalous,
217      --  courtesy of two's complement.
218
219      T_Val := (if T = Time_First then abs (Time_Last) else abs (T));
220
221      --  Extract the integer part of T, truncating towards zero
222
223      SC :=
224        (if T_Val < 0.5 then 0 else Seconds_Count (Time_Span'(T_Val - 0.5)));
225
226      if T < 0.0 then
227         SC := -SC;
228      end if;
229
230      --  If original time is negative, need to truncate towards negative
231      --  infinity, to make TS non-negative, as per ARM.
232
233      if Time (SC) > T then
234         SC := SC - 1;
235      end if;
236
237      TS := Time_Span (Duration (T) - Duration (SC));
238   end Split;
239
240   -------------
241   -- Time_Of --
242   -------------
243
244   function Time_Of (SC : Seconds_Count; TS : Time_Span) return Time is
245      pragma Suppress (Overflow_Check);
246      pragma Suppress (Range_Check);
247      --  We do all our own checks for this function
248
249      --  This is not such a simple case, since TS is already 64 bits, and
250      --  so we can't just promote everything to a wider type to ensure proper
251      --  testing for overflow. The situation is that Seconds_Count is a MUCH
252      --  wider type than Time_Span and Time (both of which have the underlying
253      --  type Duration).
254
255      --         <------------------- Seconds_Count -------------------->
256      --                            <-- Duration -->
257
258      --  Now it is possible for an SC value outside the Duration range to
259      --  be "brought back into range" by an appropriate TS value, but there
260      --  are also clearly SC values that are completely out of range. Note
261      --  that the above diagram is wildly out of scale, the difference in
262      --  ranges is much greater than shown.
263
264      --  We can't just go generating out of range Duration values to test for
265      --  overflow, since Duration is a full range type, so we follow the steps
266      --  shown below.
267
268      SC_Lo : constant Seconds_Count :=
269                Seconds_Count (Duration (Time_Span_First) + Duration'(0.5));
270      SC_Hi : constant Seconds_Count :=
271                Seconds_Count (Duration (Time_Span_Last)  - Duration'(0.5));
272      --  These are the maximum values of the seconds (integer) part of the
273      --  Duration range. Used to compute and check the seconds in the result.
274
275      TS_SC : Seconds_Count;
276      --  Seconds part of input value
277
278      TS_Fraction : Duration;
279      --  Fractional part of input value, may be negative
280
281      Result_SC : Seconds_Count;
282      --  Seconds value for result
283
284      Fudge : constant Seconds_Count := 10;
285      --  Fudge value used to do end point checks far from end point
286
287      FudgeD : constant Duration := Duration (Fudge);
288      --  Fudge value as Duration
289
290      Fudged_Result : Duration;
291      --  Result fudged up or down by FudgeD
292
293      procedure Out_Of_Range;
294      pragma No_Return (Out_Of_Range);
295      --  Raise exception for result out of range
296
297      ------------------
298      -- Out_Of_Range --
299      ------------------
300
301      procedure Out_Of_Range is
302      begin
303         raise Constraint_Error with
304           "result for Ada.Real_Time.Time_Of is out of range";
305      end Out_Of_Range;
306
307   --  Start of processing for Time_Of
308
309   begin
310      --  If SC is so far out of range that there is no possibility of the
311      --  addition of TS getting it back in range, raise an exception right
312      --  away. That way we don't have to worry about SC values overflowing.
313
314      if SC < 3 * SC_Lo or else SC > 3 * SC_Hi then
315         Out_Of_Range;
316      end if;
317
318      --  Decompose input TS value
319
320      TS_SC := Seconds_Count (Duration (TS));
321      TS_Fraction := Duration (TS) - Duration (TS_SC);
322
323      --  Compute result seconds. If clearly out of range, raise error now
324
325      Result_SC := SC + TS_SC;
326
327      if Result_SC < (SC_Lo - 1) or else Result_SC > (SC_Hi + 1) then
328         Out_Of_Range;
329      end if;
330
331      --  Now the result is simply Result_SC + TS_Fraction, but we can't just
332      --  go computing that since it might be out of range. So what we do is
333      --  to compute a value fudged down or up by 10.0 (arbitrary value, but
334      --  that will do fine), and check that fudged value, and if in range
335      --  unfudge it and return the result.
336
337      --  Fudge positive result down, and check high bound
338
339      if Result_SC > 0 then
340         Fudged_Result := Duration (Result_SC - Fudge) + TS_Fraction;
341
342         if Fudged_Result <= Duration'Last - FudgeD then
343            return Time (Fudged_Result + FudgeD);
344         else
345            Out_Of_Range;
346         end if;
347
348      --  Same for negative values of seconds, fudge up and check low bound
349
350      else
351         Fudged_Result := Duration (Result_SC + Fudge) + TS_Fraction;
352
353         if Fudged_Result >= Duration'First + FudgeD then
354            return Time (Fudged_Result - FudgeD);
355         else
356            Out_Of_Range;
357         end if;
358      end if;
359   end Time_Of;
360
361   -----------------
362   -- To_Duration --
363   -----------------
364
365   function To_Duration (TS : Time_Span) return Duration is
366   begin
367      return Duration (TS);
368   end To_Duration;
369
370   ------------------
371   -- To_Time_Span --
372   ------------------
373
374   function To_Time_Span (D : Duration) return Time_Span is
375   begin
376      --  Note regarding AI-00432 requiring range checking on this conversion.
377      --  In almost all versions of GNAT (and all to which this version of the
378      --  Ada.Real_Time package apply), the range of Time_Span and Duration are
379      --  the same, so there is no issue of overflow.
380
381      return Time_Span (D);
382   end To_Time_Span;
383
384begin
385   --  Ensure that the tasking run time is initialized when using clock and/or
386   --  delay operations. The initialization routine has the required machinery
387   --  to prevent multiple calls to Initialize.
388
389   System.Tasking.Initialize;
390end Ada.Real_Time;
391