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 while
72   --  the other buffer is read. The point is that we do not want to use a lock
73   --  inside the Clock routine for performance reasons. We still use a lock
74   --  in the Get_Base_Time which is called very rarely. Current is a pointer,
75   --  the pragma Atomic is there to ensure that the value can be set or read
76   --  atomically. That's it, when Get_Base_Time has updated a buffer the
77   --  switch to the new value is done by changing Current pointer.
78
79   First, Second : aliased Clock_Data;
80
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
181      Max_Elapsed : constant LARGE_INTEGER :=
182                         LARGE_INTEGER (Tick_Frequency / 100_000);
183      --  Look for a precision of 0.01 ms
184
185      Sig            : constant Signature_Type := Signature;
186
187      Loc_Ticks, Ctrl_Ticks : aliased LARGE_INTEGER;
188      Loc_Time, Ctrl_Time   : aliased Long_Long_Integer;
189      Elapsed               : LARGE_INTEGER;
190      Current_Max           : LARGE_INTEGER := LARGE_INTEGER'Last;
191      New_Data              : Clock_Data_Access;
192
193   begin
194      --  Here we must be sure that both of these calls are done in a short
195      --  amount of time. Both are base time and should in theory be taken
196      --  at the very same time.
197
198      --  The goal of the following loop is to synchronize the system time
199      --  with the Win32 performance counter by getting a base offset for both.
200      --  Using these offsets it is then possible to compute actual time using
201      --  a performance counter which has a better precision than the Win32
202      --  time API.
203
204      --  Try at most 10 times to reach the best synchronisation (below 1
205      --  millisecond) otherwise the runtime will use the best value reached
206      --  during the runs.
207
208      Lock;
209
210      --  First check that the current value has not been updated. This
211      --  could happen if another task has called Clock at the same time
212      --  and that Max_Shift has been reached too.
213      --
214      --  But if the current value has been changed just before we entered
215      --  into the critical section, we can safely return as the current
216      --  base data (time, clock, ticks) have already been updated.
217
218      if Sig /= Signature then
219         return;
220      end if;
221
222      --  Check for the unused data buffer and set New_Data to point to it
223
224      if Current = First'Access then
225         New_Data := Second'Access;
226      else
227         New_Data := First'Access;
228      end if;
229
230      for K in 1 .. 10 loop
231         if QueryPerformanceCounter (Loc_Ticks'Access) = Win32.FALSE then
232            pragma Assert
233              (Standard.False,
234               "Could not query high performance counter in Clock");
235            null;
236         end if;
237
238         GetSystemTimeAsFileTime (Ctrl_Time'Access);
239
240         --  Scan for clock tick, will take up to 16ms/1ms depending on PC.
241         --  This cannot be an infinite loop or the system hardware is badly
242         --  damaged.
243
244         loop
245            GetSystemTimeAsFileTime (Loc_Time'Access);
246
247            if QueryPerformanceCounter (Ctrl_Ticks'Access) = Win32.FALSE then
248               pragma Assert
249                 (Standard.False,
250                  "Could not query high performance counter in Clock");
251               null;
252            end if;
253
254            exit when Loc_Time /= Ctrl_Time;
255            Loc_Ticks := Ctrl_Ticks;
256         end loop;
257
258         --  Check elapsed Performance Counter between samples
259         --  to choose the best one.
260
261         Elapsed := Ctrl_Ticks - Loc_Ticks;
262
263         if Elapsed < Current_Max then
264            New_Data.Base_Time   := Loc_Time;
265            New_Data.Base_Ticks  := Loc_Ticks;
266            Current_Max := Elapsed;
267
268            --  Exit the loop when we have reached the expected precision
269
270            exit when Elapsed <= Max_Elapsed;
271         end if;
272      end loop;
273
274      New_Data.Base_Clock :=
275        Duration
276          (Long_Long_Float
277            ((New_Data.Base_Time - epoch_1970) * system_time_ns) /
278                                               Long_Long_Float (Sec_Unit));
279
280      --  At this point all the base values have been set into the new data
281      --  record. Change the pointer (atomic operation) to these new values.
282
283      Current := New_Data;
284      Data    := New_Data.all;
285
286      --  Set new signature for this data set
287
288      Signature := Signature + 1;
289
290      Unlock;
291
292   exception
293      when others =>
294         Unlock;
295         raise;
296   end Get_Base_Time;
297
298   ---------------------
299   -- Monotonic_Clock --
300   ---------------------
301
302   function Monotonic_Clock return Duration is
303      Current_Ticks  : aliased LARGE_INTEGER;
304      Elap_Secs_Tick : Duration;
305
306   begin
307      if QueryPerformanceCounter (Current_Ticks'Access) = Win32.FALSE then
308         return 0.0;
309
310      else
311         Elap_Secs_Tick :=
312           Duration (Long_Long_Float (Current_Ticks - Base_Monotonic_Ticks) /
313                       Long_Long_Float (Tick_Frequency));
314         return Base_Monotonic_Clock + Elap_Secs_Tick;
315      end if;
316   end Monotonic_Clock;
317
318   -----------------
319   -- Timed_Delay --
320   -----------------
321
322   procedure Timed_Delay (Time : Duration; Mode : Integer) is
323
324      function Mode_Clock return Duration;
325      pragma Inline (Mode_Clock);
326      --  Return the current clock value using either the monotonic clock or
327      --  standard clock depending on the Mode value.
328
329      ----------------
330      -- Mode_Clock --
331      ----------------
332
333      function Mode_Clock return Duration is
334      begin
335         case Mode is
336            when Absolute_RT =>
337               return Monotonic_Clock;
338            when others =>
339               return Clock;
340         end case;
341      end Mode_Clock;
342
343      --  Local Variables
344
345      Base_Time : constant Duration := Mode_Clock;
346      --  Base_Time is used to detect clock set backward, in this case we
347      --  cannot ensure the delay accuracy.
348
349      Rel_Time   : Duration;
350      Abs_Time   : Duration;
351      Check_Time : Duration := Base_Time;
352
353   --  Start of processing for Timed Delay
354
355   begin
356      if Mode = Relative then
357         Rel_Time := Time;
358         Abs_Time := Time + Check_Time;
359      else
360         Rel_Time := Time - Check_Time;
361         Abs_Time := Time;
362      end if;
363
364      if Rel_Time > 0.0 then
365         loop
366            Sleep (DWORD (Rel_Time * 1000.0));
367            Check_Time := Mode_Clock;
368
369            exit when Abs_Time <= Check_Time or else Check_Time < Base_Time;
370
371            Rel_Time := Abs_Time - Check_Time;
372         end loop;
373      end if;
374   end Timed_Delay;
375
376   ----------------
377   -- Initialize --
378   ----------------
379
380   Initialized : Boolean := False;
381
382   procedure Initialize is
383   begin
384      if Initialized then
385         return;
386      end if;
387
388      Initialized := True;
389
390      --  Get starting time as base
391
392      if QueryPerformanceFrequency (Tick_Frequency'Access) = Win32.FALSE then
393         raise Program_Error with
394           "cannot get high performance counter frequency";
395      end if;
396
397      Get_Base_Time (Current.all);
398
399      --  Keep base clock and ticks for the monotonic clock. These values
400      --  should never be changed to ensure proper behavior of the monotonic
401      --  clock.
402
403      Base_Monotonic_Clock := Current.Base_Clock;
404      Base_Monotonic_Ticks := Current.Base_Ticks;
405   end Initialize;
406
407end System.OS_Primitives;
408