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-2015, 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   function Monotonic_Clock return Duration;
94   pragma Export (Ada, Monotonic_Clock, "__gnat_monotonic_clock");
95   --  Return "absolute" time, represented as an offset relative to "the Unix
96   --  Epoch", which is Jan 1, 1970 00:00:00 UTC. This clock implementation is
97   --  immune to the system's clock changes. Export this function so that it
98   --  can be imported from s-taprop-mingw.adb without changing the shared
99   --  spec (s-osprim.ads).
100
101   procedure Get_Base_Time (Data : in out Clock_Data);
102   --  Retrieve the base time and base ticks. These values will be used by
103   --  clock to compute the current time by adding to it a fraction of the
104   --  performance counter. This is for the implementation of a high-resolution
105   --  clock. Note that this routine does not change the base monotonic values
106   --  used by the monotonic clock.
107
108   -----------
109   -- Clock --
110   -----------
111
112   --  This implementation of clock provides high resolution timer values
113   --  using QueryPerformanceCounter. This call return a 64 bits values (based
114   --  on the 8253 16 bits counter). This counter is updated every 1/1_193_182
115   --  times per seconds. The call to QueryPerformanceCounter takes 6
116   --  microsecs to complete.
117
118   function Clock return Duration is
119      Max_Shift            : constant Duration        := 2.0;
120      Hundreds_Nano_In_Sec : constant Long_Long_Float := 1.0E7;
121      Data                 : Clock_Data;
122      Current_Ticks        : aliased LARGE_INTEGER;
123      Elap_Secs_Tick       : Duration;
124      Elap_Secs_Sys        : Duration;
125      Now                  : aliased Long_Long_Integer;
126      Sig1, Sig2           : Signature_Type;
127
128   begin
129      --  Try ten times to get a coherent set of base data. For this we just
130      --  check that the signature hasn't changed during the copy of the
131      --  current data.
132      --
133      --  This loop will always be done once if there is no interleaved call
134      --  to Get_Base_Time.
135
136      for K in 1 .. 10 loop
137         Sig1 := Signature;
138         Data := Current.all;
139         Sig2 := Signature;
140         exit when Sig1 = Sig2;
141      end loop;
142
143      if QueryPerformanceCounter (Current_Ticks'Access) = Win32.FALSE then
144         return 0.0;
145      end if;
146
147      GetSystemTimeAsFileTime (Now'Access);
148
149      Elap_Secs_Sys :=
150        Duration (Long_Long_Float (abs (Now - Data.Base_Time)) /
151                    Hundreds_Nano_In_Sec);
152
153      Elap_Secs_Tick :=
154        Duration (Long_Long_Float (Current_Ticks - Data.Base_Ticks) /
155                  Long_Long_Float (Tick_Frequency));
156
157      --  If we have a shift of more than Max_Shift seconds we resynchronize
158      --  the Clock. This is probably due to a manual Clock adjustment, a DST
159      --  adjustment or an NTP synchronisation. And we want to adjust the time
160      --  for this system (non-monotonic) clock.
161
162      if abs (Elap_Secs_Sys - Elap_Secs_Tick) > Max_Shift then
163         Get_Base_Time (Data);
164
165         Elap_Secs_Tick :=
166           Duration (Long_Long_Float (Current_Ticks - Data.Base_Ticks) /
167                     Long_Long_Float (Tick_Frequency));
168      end if;
169
170      return Data.Base_Clock + Elap_Secs_Tick;
171   end Clock;
172
173   -------------------
174   -- Get_Base_Time --
175   -------------------
176
177   procedure Get_Base_Time (Data : in out Clock_Data) is
178
179      --  The resolution for GetSystemTime is 1 millisecond
180
181      --  The time to get both base times should take less than 1 millisecond.
182      --  Therefore, the elapsed time reported by GetSystemTime between both
183      --  actions should be null.
184
185      epoch_1970     : constant := 16#19D_B1DE_D53E_8000#; -- win32 UTC epoch
186      system_time_ns : constant := 100;                    -- 100 ns per tick
187      Sec_Unit       : constant := 10#1#E9;
188
189      Max_Elapsed : constant LARGE_INTEGER :=
190                         LARGE_INTEGER (Tick_Frequency / 100_000);
191      --  Look for a precision of 0.01 ms
192
193      Sig            : constant Signature_Type := Signature;
194
195      Loc_Ticks, Ctrl_Ticks : aliased LARGE_INTEGER;
196      Loc_Time, Ctrl_Time   : aliased Long_Long_Integer;
197      Elapsed               : LARGE_INTEGER;
198      Current_Max           : LARGE_INTEGER := LARGE_INTEGER'Last;
199      New_Data              : Clock_Data_Access;
200
201   begin
202      --  Here we must be sure that both of these calls are done in a short
203      --  amount of time. Both are base time and should in theory be taken
204      --  at the very same time.
205
206      --  The goal of the following loop is to synchronize the system time
207      --  with the Win32 performance counter by getting a base offset for both.
208      --  Using these offsets it is then possible to compute actual time using
209      --  a performance counter which has a better precision than the Win32
210      --  time API.
211
212      --  Try at most 10 times to reach the best synchronisation (below 1
213      --  millisecond) otherwise the runtime will use the best value reached
214      --  during the runs.
215
216      Lock;
217
218      --  First check that the current value has not been updated. This
219      --  could happen if another task has called Clock at the same time
220      --  and that Max_Shift has been reached too.
221      --
222      --  But if the current value has been changed just before we entered
223      --  into the critical section, we can safely return as the current
224      --  base data (time, clock, ticks) have already been updated.
225
226      if Sig /= Signature then
227         Unlock;
228         return;
229      end if;
230
231      --  Check for the unused data buffer and set New_Data to point to it
232
233      if Current = First'Access then
234         New_Data := Second'Access;
235      else
236         New_Data := First'Access;
237      end if;
238
239      for K in 1 .. 10 loop
240         if QueryPerformanceCounter (Loc_Ticks'Access) = Win32.FALSE then
241            pragma Assert
242              (Standard.False,
243               "Could not query high performance counter in Clock");
244            null;
245         end if;
246
247         GetSystemTimeAsFileTime (Ctrl_Time'Access);
248
249         --  Scan for clock tick, will take up to 16ms/1ms depending on PC.
250         --  This cannot be an infinite loop or the system hardware is badly
251         --  damaged.
252
253         loop
254            GetSystemTimeAsFileTime (Loc_Time'Access);
255
256            if QueryPerformanceCounter (Ctrl_Ticks'Access) = Win32.FALSE then
257               pragma Assert
258                 (Standard.False,
259                  "Could not query high performance counter in Clock");
260               null;
261            end if;
262
263            exit when Loc_Time /= Ctrl_Time;
264            Loc_Ticks := Ctrl_Ticks;
265         end loop;
266
267         --  Check elapsed Performance Counter between samples
268         --  to choose the best one.
269
270         Elapsed := Ctrl_Ticks - Loc_Ticks;
271
272         if Elapsed < Current_Max then
273            New_Data.Base_Time   := Loc_Time;
274            New_Data.Base_Ticks  := Loc_Ticks;
275            Current_Max := Elapsed;
276
277            --  Exit the loop when we have reached the expected precision
278
279            exit when Elapsed <= Max_Elapsed;
280         end if;
281      end loop;
282
283      New_Data.Base_Clock :=
284        Duration
285          (Long_Long_Float
286            ((New_Data.Base_Time - epoch_1970) * system_time_ns) /
287                                               Long_Long_Float (Sec_Unit));
288
289      --  At this point all the base values have been set into the new data
290      --  record. Change the pointer (atomic operation) to these new values.
291
292      Current := New_Data;
293      Data    := New_Data.all;
294
295      --  Set new signature for this data set
296
297      Signature := Signature + 1;
298
299      Unlock;
300
301   exception
302      when others =>
303         Unlock;
304         raise;
305   end Get_Base_Time;
306
307   ---------------------
308   -- Monotonic_Clock --
309   ---------------------
310
311   function Monotonic_Clock return Duration is
312      Current_Ticks  : aliased LARGE_INTEGER;
313      Elap_Secs_Tick : Duration;
314
315   begin
316      if QueryPerformanceCounter (Current_Ticks'Access) = Win32.FALSE then
317         return 0.0;
318
319      else
320         Elap_Secs_Tick :=
321           Duration (Long_Long_Float (Current_Ticks - Base_Monotonic_Ticks) /
322                       Long_Long_Float (Tick_Frequency));
323         return Base_Monotonic_Clock + Elap_Secs_Tick;
324      end if;
325   end Monotonic_Clock;
326
327   -----------------
328   -- Timed_Delay --
329   -----------------
330
331   procedure Timed_Delay (Time : Duration; Mode : Integer) is
332
333      function Mode_Clock return Duration;
334      pragma Inline (Mode_Clock);
335      --  Return the current clock value using either the monotonic clock or
336      --  standard clock depending on the Mode value.
337
338      ----------------
339      -- Mode_Clock --
340      ----------------
341
342      function Mode_Clock return Duration is
343      begin
344         case Mode is
345            when Absolute_RT =>
346               return Monotonic_Clock;
347            when others =>
348               return Clock;
349         end case;
350      end Mode_Clock;
351
352      --  Local Variables
353
354      Base_Time : constant Duration := Mode_Clock;
355      --  Base_Time is used to detect clock set backward, in this case we
356      --  cannot ensure the delay accuracy.
357
358      Rel_Time   : Duration;
359      Abs_Time   : Duration;
360      Check_Time : Duration := Base_Time;
361
362   --  Start of processing for Timed Delay
363
364   begin
365      if Mode = Relative then
366         Rel_Time := Time;
367         Abs_Time := Time + Check_Time;
368      else
369         Rel_Time := Time - Check_Time;
370         Abs_Time := Time;
371      end if;
372
373      if Rel_Time > 0.0 then
374         loop
375            Sleep (DWORD (Rel_Time * 1000.0));
376            Check_Time := Mode_Clock;
377
378            exit when Abs_Time <= Check_Time or else Check_Time < Base_Time;
379
380            Rel_Time := Abs_Time - Check_Time;
381         end loop;
382      end if;
383   end Timed_Delay;
384
385   ----------------
386   -- Initialize --
387   ----------------
388
389   Initialized : Boolean := False;
390
391   procedure Initialize is
392   begin
393      if Initialized then
394         return;
395      end if;
396
397      Initialized := True;
398
399      --  Get starting time as base
400
401      if QueryPerformanceFrequency (Tick_Frequency'Access) = Win32.FALSE then
402         raise Program_Error with
403           "cannot get high performance counter frequency";
404      end if;
405
406      Get_Base_Time (Current.all);
407
408      --  Keep base clock and ticks for the monotonic clock. These values
409      --  should never be changed to ensure proper behavior of the monotonic
410      --  clock.
411
412      Base_Monotonic_Clock := Current.Base_Clock;
413      Base_Monotonic_Ticks := Current.Base_Ticks;
414   end Initialize;
415
416end System.OS_Primitives;
417