1------------------------------------------------------------------------------
2--                                                                          --
3--                 GNAT RUN-TIME LIBRARY (GNARL) COMPONENTS                 --
4--                                                                          --
5--                  S Y S T E M . O S _ P R I M I T I V E S                 --
6--                                                                          --
7--                                 B o d y                                  --
8--                                                                          --
9--          Copyright (C) 1998-2013, Free Software Foundation, Inc.         --
10--                                                                          --
11-- GNARL is free software; you can  redistribute it  and/or modify it under --
12-- terms of the  GNU General Public License as published  by the Free Soft- --
13-- ware  Foundation;  either version 3,  or (at your option) any later ver- --
14-- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
15-- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
16-- or FITNESS FOR A PARTICULAR PURPOSE.                                     --
17--                                                                          --
18-- As a special exception under Section 7 of GPL version 3, you are granted --
19-- additional permissions described in the GCC Runtime Library Exception,   --
20-- version 3.1, as published by the Free Software Foundation.               --
21--                                                                          --
22-- You should have received a copy of the GNU General Public License and    --
23-- a copy of the GCC Runtime Library Exception along with this program;     --
24-- see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    --
25-- <http://www.gnu.org/licenses/>.                                          --
26--                                                                          --
27-- GNARL was developed by the GNARL team at Florida State University.       --
28-- Extensive contributions were provided by Ada Core Technologies, Inc.     --
29--                                                                          --
30------------------------------------------------------------------------------
31
32--  This is the NT version of this package
33
34with System.Task_Lock;
35with System.Win32.Ext;
36
37package body System.OS_Primitives is
38
39   use System.Task_Lock;
40   use System.Win32;
41   use System.Win32.Ext;
42
43   ----------------------------------------
44   -- Data for the high resolution clock --
45   ----------------------------------------
46
47   Tick_Frequency : aliased LARGE_INTEGER;
48   --  Holds frequency of high-performance counter used by Clock
49   --  Windows NT uses a 1_193_182 Hz counter on PCs.
50
51   Base_Monotonic_Ticks : LARGE_INTEGER;
52   --  Holds the Tick count for the base monotonic time
53
54   Base_Monotonic_Clock : Duration;
55   --  Holds the current clock for monotonic clock's base time
56
57   type Clock_Data is record
58      Base_Ticks : LARGE_INTEGER;
59      --  Holds the Tick count for the base time
60
61      Base_Time : Long_Long_Integer;
62      --  Holds the base time used to check for system time change, used with
63      --  the standard clock.
64
65      Base_Clock : Duration;
66      --  Holds the current clock for the standard clock's base time
67   end record;
68
69   type Clock_Data_Access is access all Clock_Data;
70
71   --  Two base clock buffers. This is used to be able to update a buffer
72   --  while the other buffer is read. The point is that we do not want to
73   --  use a lock inside the Clock routine for performance reasons. We still
74   --  use a lock in the Get_Base_Time which is called very rarely. Current
75   --  is a pointer, the pragma Atomic is there to ensure that the value can
76   --  be set or read atomically. That's it, when Get_Base_Time has updated
77   --  a buffer the switch to the new value is done by changing Current
78   --  pointer.
79
80   First, Second : aliased Clock_Data;
81   Current       : Clock_Data_Access := First'Access;
82   pragma Atomic (Current);
83
84   --  The following signature is to detect change on the base clock data
85   --  above. The signature is a modular type, it will wrap around without
86   --  raising an exception. We would need to have exactly 2**32 updates of
87   --  the base data for the changes to get undetected.
88
89   type Signature_Type is mod 2**32;
90   Signature     : Signature_Type := 0;
91   pragma Atomic (Signature);
92
93   procedure Get_Base_Time (Data : out Clock_Data);
94   --  Retrieve the base time and base ticks. These values will be used by
95   --  clock to compute the current time by adding to it a fraction of the
96   --  performance counter. This is for the implementation of a
97   --  high-resolution clock. Note that this routine does not change the base
98   --  monotonic values used by the monotonic clock.
99
100   -----------
101   -- Clock --
102   -----------
103
104   --  This implementation of clock provides high resolution timer values
105   --  using QueryPerformanceCounter. This call return a 64 bits values (based
106   --  on the 8253 16 bits counter). This counter is updated every 1/1_193_182
107   --  times per seconds. The call to QueryPerformanceCounter takes 6
108   --  microsecs to complete.
109
110   function Clock return Duration is
111      Max_Shift            : constant Duration        := 2.0;
112      Hundreds_Nano_In_Sec : constant Long_Long_Float := 1.0E7;
113      Data                 : Clock_Data;
114      Current_Ticks        : aliased LARGE_INTEGER;
115      Elap_Secs_Tick       : Duration;
116      Elap_Secs_Sys        : Duration;
117      Now                  : aliased Long_Long_Integer;
118      Sig1, Sig2           : Signature_Type;
119
120   begin
121      --  Try ten times to get a coherent set of base data. For this we just
122      --  check that the signature hasn't changed during the copy of the
123      --  current data.
124      --
125      --  This loop will always be done once if there is no interleaved call
126      --  to Get_Base_Time.
127
128      for K in 1 .. 10 loop
129         Sig1 := Signature;
130         Data := Current.all;
131         Sig2 := Signature;
132         exit when Sig1 = Sig2;
133      end loop;
134
135      if QueryPerformanceCounter (Current_Ticks'Access) = Win32.FALSE then
136         return 0.0;
137      end if;
138
139      GetSystemTimeAsFileTime (Now'Access);
140
141      Elap_Secs_Sys :=
142        Duration (Long_Long_Float (abs (Now - Data.Base_Time)) /
143                    Hundreds_Nano_In_Sec);
144
145      Elap_Secs_Tick :=
146        Duration (Long_Long_Float (Current_Ticks - Data.Base_Ticks) /
147                  Long_Long_Float (Tick_Frequency));
148
149      --  If we have a shift of more than Max_Shift seconds we resynchronize
150      --  the Clock. This is probably due to a manual Clock adjustment, a DST
151      --  adjustment or an NTP synchronisation. And we want to adjust the time
152      --  for this system (non-monotonic) clock.
153
154      if abs (Elap_Secs_Sys - Elap_Secs_Tick) > Max_Shift then
155         Get_Base_Time (Data);
156
157         Elap_Secs_Tick :=
158           Duration (Long_Long_Float (Current_Ticks - Data.Base_Ticks) /
159                     Long_Long_Float (Tick_Frequency));
160      end if;
161
162      return Data.Base_Clock + Elap_Secs_Tick;
163   end Clock;
164
165   -------------------
166   -- Get_Base_Time --
167   -------------------
168
169   procedure Get_Base_Time (Data : out Clock_Data) is
170
171      --  The resolution for GetSystemTime is 1 millisecond
172
173      --  The time to get both base times should take less than 1 millisecond.
174      --  Therefore, the elapsed time reported by GetSystemTime between both
175      --  actions should be null.
176
177      epoch_1970     : constant := 16#19D_B1DE_D53E_8000#; -- win32 UTC epoch
178      system_time_ns : constant := 100;                    -- 100 ns per tick
179      Sec_Unit       : constant := 10#1#E9;
180      Max_Elapsed    : constant LARGE_INTEGER :=
181                         LARGE_INTEGER (Tick_Frequency / 100_000);
182      --  Look for a precision of 0.01 ms
183      Sig            : constant Signature_Type := Signature;
184
185      Loc_Ticks, Ctrl_Ticks : aliased LARGE_INTEGER;
186      Loc_Time, Ctrl_Time   : aliased Long_Long_Integer;
187      Elapsed               : LARGE_INTEGER;
188      Current_Max           : LARGE_INTEGER := LARGE_INTEGER'Last;
189      New_Data              : Clock_Data_Access;
190
191   begin
192      --  Here we must be sure that both of these calls are done in a short
193      --  amount of time. Both are base time and should in theory be taken
194      --  at the very same time.
195
196      --  The goal of the following loop is to synchronize the system time
197      --  with the Win32 performance counter by getting a base offset for both.
198      --  Using these offsets it is then possible to compute actual time using
199      --  a performance counter which has a better precision than the Win32
200      --  time API.
201
202      --  Try at most 10 times to reach the best synchronisation (below 1
203      --  millisecond) otherwise the runtime will use the best value reached
204      --  during the runs.
205
206      Lock;
207
208      --  First check that the current value has not been updated. This
209      --  could happen if another task has called Clock at the same time
210      --  and that Max_Shift has been reached too.
211      --
212      --  But if the current value has been changed just before we entered
213      --  into the critical section, we can safely return as the current
214      --  base data (time, clock, ticks) have already been updated.
215
216      if Sig /= Signature then
217         return;
218      end if;
219
220      --  Check for the unused data buffer and set New_Data to point to it
221
222      if Current = First'Access then
223         New_Data := Second'Access;
224      else
225         New_Data := First'Access;
226      end if;
227
228      for K in 1 .. 10 loop
229         if QueryPerformanceCounter (Loc_Ticks'Access) = Win32.FALSE then
230            pragma Assert
231              (Standard.False,
232               "Could not query high performance counter in Clock");
233            null;
234         end if;
235
236         GetSystemTimeAsFileTime (Ctrl_Time'Access);
237
238         --  Scan for clock tick, will take up to 16ms/1ms depending on PC.
239         --  This cannot be an infinite loop or the system hardware is badly
240         --  damaged.
241
242         loop
243            GetSystemTimeAsFileTime (Loc_Time'Access);
244
245            if QueryPerformanceCounter (Ctrl_Ticks'Access) = Win32.FALSE then
246               pragma Assert
247                 (Standard.False,
248                  "Could not query high performance counter in Clock");
249               null;
250            end if;
251
252            exit when Loc_Time /= Ctrl_Time;
253            Loc_Ticks := Ctrl_Ticks;
254         end loop;
255
256         --  Check elapsed Performance Counter between samples
257         --  to choose the best one.
258
259         Elapsed := Ctrl_Ticks - Loc_Ticks;
260
261         if Elapsed < Current_Max then
262            New_Data.Base_Time   := Loc_Time;
263            New_Data.Base_Ticks  := Loc_Ticks;
264            Current_Max := Elapsed;
265
266            --  Exit the loop when we have reached the expected precision
267
268            exit when Elapsed <= Max_Elapsed;
269         end if;
270      end loop;
271
272      New_Data.Base_Clock := Duration
273        (Long_Long_Float ((New_Data.Base_Time - epoch_1970) * system_time_ns) /
274           Long_Long_Float (Sec_Unit));
275
276      --  At this point all the base values have been set into the new data
277      --  record. We just change the pointer (atomic operation) to this new
278      --  values.
279
280      Current := New_Data;
281      Data    := New_Data.all;
282
283      --  Set new signature for this data set
284
285      Signature := Signature + 1;
286
287      Unlock;
288
289   exception
290      when others =>
291         Unlock;
292         raise;
293   end Get_Base_Time;
294
295   ---------------------
296   -- Monotonic_Clock --
297   ---------------------
298
299   function Monotonic_Clock return Duration is
300      Current_Ticks  : aliased LARGE_INTEGER;
301      Elap_Secs_Tick : Duration;
302
303   begin
304      if QueryPerformanceCounter (Current_Ticks'Access) = Win32.FALSE then
305         return 0.0;
306
307      else
308         Elap_Secs_Tick :=
309           Duration (Long_Long_Float (Current_Ticks - Base_Monotonic_Ticks) /
310                       Long_Long_Float (Tick_Frequency));
311         return Base_Monotonic_Clock + Elap_Secs_Tick;
312      end if;
313   end Monotonic_Clock;
314
315   -----------------
316   -- Timed_Delay --
317   -----------------
318
319   procedure Timed_Delay (Time : Duration; Mode : Integer) is
320
321      function Mode_Clock return Duration;
322      pragma Inline (Mode_Clock);
323      --  Return the current clock value using either the monotonic clock or
324      --  standard clock depending on the Mode value.
325
326      ----------------
327      -- Mode_Clock --
328      ----------------
329
330      function Mode_Clock return Duration is
331      begin
332         case Mode is
333            when Absolute_RT =>
334               return Monotonic_Clock;
335            when others =>
336               return Clock;
337         end case;
338      end Mode_Clock;
339
340      --  Local Variables
341
342      Base_Time : constant Duration := Mode_Clock;
343      --  Base_Time is used to detect clock set backward, in this case we
344      --  cannot ensure the delay accuracy.
345
346      Rel_Time   : Duration;
347      Abs_Time   : Duration;
348      Check_Time : Duration := Base_Time;
349
350   --  Start of processing for Timed Delay
351
352   begin
353      if Mode = Relative then
354         Rel_Time := Time;
355         Abs_Time := Time + Check_Time;
356      else
357         Rel_Time := Time - Check_Time;
358         Abs_Time := Time;
359      end if;
360
361      if Rel_Time > 0.0 then
362         loop
363            Sleep (DWORD (Rel_Time * 1000.0));
364            Check_Time := Mode_Clock;
365
366            exit when Abs_Time <= Check_Time or else Check_Time < Base_Time;
367
368            Rel_Time := Abs_Time - Check_Time;
369         end loop;
370      end if;
371   end Timed_Delay;
372
373   ----------------
374   -- Initialize --
375   ----------------
376
377   Initialized : Boolean := False;
378
379   procedure Initialize is
380   begin
381      if Initialized then
382         return;
383      end if;
384
385      Initialized := True;
386
387      --  Get starting time as base
388
389      if QueryPerformanceFrequency (Tick_Frequency'Access) = Win32.FALSE then
390         raise Program_Error with
391           "cannot get high performance counter frequency";
392      end if;
393
394      Get_Base_Time (Current.all);
395
396      --  Keep base clock and ticks for the monotonic clock. These values
397      --  should never be changed to ensure proper behavior of the monotonic
398      --  clock.
399
400      Base_Monotonic_Clock := Current.Base_Clock;
401      Base_Monotonic_Ticks := Current.Base_Ticks;
402   end Initialize;
403
404end System.OS_Primitives;
405