OSDN Git Service

2008-04-08 Ed Schonberg <schonberg@adacore.com>
[pf3gnuchains/gcc-fork.git] / gcc / ada / s-osprim-mingw.adb
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-2008, 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 2,  or (at your option) any later ver- --
14 -- sion. GNARL 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.  See the GNU General Public License --
17 -- for  more details.  You should have  received  a copy of the GNU General --
18 -- Public License  distributed with GNARL; see file COPYING.  If not, write --
19 -- to  the  Free Software Foundation,  51  Franklin  Street,  Fifth  Floor, --
20 -- Boston, MA 02110-1301, USA.                                              --
21 --                                                                          --
22 -- As a special exception,  if other files  instantiate  generics from this --
23 -- unit, or you link  this unit with other files  to produce an executable, --
24 -- this  unit  does not  by itself cause  the resulting  executable  to  be --
25 -- covered  by the  GNU  General  Public  License.  This exception does not --
26 -- however invalidate  any other reasons why  the executable file  might be --
27 -- covered by the  GNU Public License.                                      --
28 --                                                                          --
29 -- GNARL was developed by the GNARL team at Florida State University.       --
30 -- Extensive contributions were provided by Ada Core Technologies, Inc.     --
31 --                                                                          --
32 ------------------------------------------------------------------------------
33
34 --  This is the NT version of this package
35
36 with System.Win32.Ext;
37
38 package body System.OS_Primitives is
39
40    use System.Win32;
41    use System.Win32.Ext;
42
43    ----------------------------------------
44    -- Data for the high resolution clock --
45    ----------------------------------------
46
47    --  Declare some pointers to access multi-word data above. This is needed
48    --  to workaround a limitation in the GNU/Linker auto-import feature used
49    --  to build the GNAT runtime DLLs. In fact the Clock and Monotonic_Clock
50    --  routines are inlined and they are using some multi-word variables.
51    --  GNU/Linker will fail to auto-import those variables when building
52    --  libgnarl.dll. The indirection level introduced here has no measurable
53    --  penalties.
54
55    --  Note that access variables below must not be declared as constant
56    --  otherwise the compiler optimization will remove this indirect access.
57
58    type DA is access all Duration;
59    --  Use to have indirect access to multi-word variables
60
61    type LIA is access all LARGE_INTEGER;
62    --  Use to have indirect access to multi-word variables
63
64    type LLIA is access all Long_Long_Integer;
65    --  Use to have indirect access to multi-word variables
66
67    Tick_Frequency : aliased LARGE_INTEGER;
68    TFA : constant LIA := Tick_Frequency'Access;
69    --  Holds frequency of high-performance counter used by Clock
70    --  Windows NT uses a 1_193_182 Hz counter on PCs.
71
72    Base_Ticks : aliased LARGE_INTEGER;
73    BTA : constant LIA := Base_Ticks'Access;
74    --  Holds the Tick count for the base time
75
76    Base_Monotonic_Ticks : aliased LARGE_INTEGER;
77    BMTA : constant LIA := Base_Monotonic_Ticks'Access;
78    --  Holds the Tick count for the base monotonic time
79
80    Base_Clock : aliased Duration;
81    BCA : constant DA := Base_Clock'Access;
82    --  Holds the current clock for the standard clock's base time
83
84    Base_Monotonic_Clock : aliased Duration;
85    BMCA : constant DA := Base_Monotonic_Clock'Access;
86    --  Holds the current clock for monotonic clock's base time
87
88    Base_Time : aliased Long_Long_Integer;
89    BTiA : constant LLIA := Base_Time'Access;
90    --  Holds the base time used to check for system time change, used with
91    --  the standard clock.
92
93    procedure Get_Base_Time;
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       Current_Ticks        : aliased LARGE_INTEGER;
114       Elap_Secs_Tick       : Duration;
115       Elap_Secs_Sys        : Duration;
116       Now                  : aliased Long_Long_Integer;
117
118    begin
119       if QueryPerformanceCounter (Current_Ticks'Access) = Win32.FALSE then
120          return 0.0;
121       end if;
122
123       GetSystemTimeAsFileTime (Now'Access);
124
125       Elap_Secs_Sys :=
126         Duration (Long_Long_Float (abs (Now - BTiA.all)) /
127                     Hundreds_Nano_In_Sec);
128
129       Elap_Secs_Tick :=
130         Duration (Long_Long_Float (Current_Ticks - BTA.all) /
131                   Long_Long_Float (TFA.all));
132
133       --  If we have a shift of more than Max_Shift seconds we resynchonize the
134       --  Clock. This is probably due to a manual Clock adjustment, an DST
135       --  adjustment or an NTP synchronisation. And we want to adjust the time
136       --  for this system (non-monotonic) clock.
137
138       if abs (Elap_Secs_Sys - Elap_Secs_Tick) > Max_Shift then
139          Get_Base_Time;
140
141          Elap_Secs_Tick :=
142            Duration (Long_Long_Float (Current_Ticks - BTA.all) /
143                      Long_Long_Float (TFA.all));
144       end if;
145
146       return BCA.all + Elap_Secs_Tick;
147    end Clock;
148
149    -------------------
150    -- Get_Base_Time --
151    -------------------
152
153    procedure Get_Base_Time is
154
155       --  The resolution for GetSystemTime is 1 millisecond
156
157       --  The time to get both base times should take less than 1 millisecond.
158       --  Therefore, the elapsed time reported by GetSystemTime between both
159       --  actions should be null.
160
161       Max_Elapsed : constant := 0;
162
163       Test_Now : aliased Long_Long_Integer;
164
165       epoch_1970     : constant := 16#19D_B1DE_D53E_8000#; -- win32 UTC epoch
166       system_time_ns : constant := 100;                    -- 100 ns per tick
167       Sec_Unit       : constant := 10#1#E9;
168
169    begin
170       --  Here we must be sure that both of these calls are done in a short
171       --  amount of time. Both are base time and should in theory be taken
172       --  at the very same time.
173
174       loop
175          GetSystemTimeAsFileTime (Base_Time'Access);
176
177          if QueryPerformanceCounter (Base_Ticks'Access) = Win32.FALSE then
178             pragma Assert
179               (Standard.False,
180                "Could not query high performance counter in Clock");
181             null;
182          end if;
183
184          GetSystemTimeAsFileTime (Test_Now'Access);
185
186          exit when Test_Now - Base_Time = Max_Elapsed;
187       end loop;
188
189       Base_Clock := Duration
190         (Long_Long_Float ((Base_Time - epoch_1970) * system_time_ns) /
191          Long_Long_Float (Sec_Unit));
192    end Get_Base_Time;
193
194    ---------------------
195    -- Monotonic_Clock --
196    ---------------------
197
198    function Monotonic_Clock return Duration is
199       Current_Ticks  : aliased LARGE_INTEGER;
200       Elap_Secs_Tick : Duration;
201
202    begin
203       if QueryPerformanceCounter (Current_Ticks'Access) = Win32.FALSE then
204          return 0.0;
205       end if;
206
207       Elap_Secs_Tick :=
208         Duration (Long_Long_Float (Current_Ticks - BMTA.all) /
209                   Long_Long_Float (TFA.all));
210
211       return BMCA.all + Elap_Secs_Tick;
212    end Monotonic_Clock;
213
214    -----------------
215    -- Timed_Delay --
216    -----------------
217
218    procedure Timed_Delay (Time : Duration; Mode : Integer) is
219
220       function Mode_Clock return Duration;
221       pragma Inline (Mode_Clock);
222       --  Return the current clock value using either the monotonic clock or
223       --  standard clock depending on the Mode value.
224
225       ----------------
226       -- Mode_Clock --
227       ----------------
228
229       function Mode_Clock return Duration is
230       begin
231          case Mode is
232             when Absolute_RT =>
233                return Monotonic_Clock;
234             when others =>
235                return Clock;
236          end case;
237       end Mode_Clock;
238
239       --  Local Variables
240
241       Base_Time : constant Duration := Mode_Clock;
242       --  Base_Time is used to detect clock set backward, in this case we
243       --  cannot ensure the delay accuracy.
244
245       Rel_Time   : Duration;
246       Abs_Time   : Duration;
247       Check_Time : Duration := Base_Time;
248
249    --  Start of processing for Timed Delay
250
251    begin
252       if Mode = Relative then
253          Rel_Time := Time;
254          Abs_Time := Time + Check_Time;
255       else
256          Rel_Time := Time - Check_Time;
257          Abs_Time := Time;
258       end if;
259
260       if Rel_Time > 0.0 then
261          loop
262             Sleep (DWORD (Rel_Time * 1000.0));
263             Check_Time := Mode_Clock;
264
265             exit when Abs_Time <= Check_Time or else Check_Time < Base_Time;
266
267             Rel_Time := Abs_Time - Check_Time;
268          end loop;
269       end if;
270    end Timed_Delay;
271
272    ----------------
273    -- Initialize --
274    ----------------
275
276    Initialized : Boolean := False;
277
278    procedure Initialize is
279    begin
280       if Initialized then
281          return;
282       end if;
283
284       Initialized := True;
285
286       --  Get starting time as base
287
288       if QueryPerformanceFrequency (Tick_Frequency'Access) = Win32.FALSE then
289          raise Program_Error with
290            "cannot get high performance counter frequency";
291       end if;
292
293       Get_Base_Time;
294
295       --  Keep base clock and ticks for the monotonic clock. These values
296       --  should never be changed to ensure proper behavior of the monotonic
297       --  clock.
298
299       Base_Monotonic_Clock := Base_Clock;
300       Base_Monotonic_Ticks := Base_Ticks;
301    end Initialize;
302
303 end System.OS_Primitives;