OSDN Git Service

2012-02-22 Pascal Obry <obry@adacore.com>
[pf3gnuchains/gcc-fork.git] / gcc / ada / s-taprop-mingw.adb
1 ------------------------------------------------------------------------------
2 --                                                                          --
3 --                 GNAT RUN-TIME LIBRARY (GNARL) COMPONENTS                 --
4 --                                                                          --
5 --     S Y S T E M . T A S K _ P R I M I T I V E S . O P E R A T I O N S    --
6 --                                                                          --
7 --                                  B o d y                                 --
8 --                                                                          --
9 --          Copyright (C) 1992-2012, 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 a NT (native) version of this package
33
34 --  This package contains all the GNULL primitives that interface directly with
35 --  the underlying OS.
36
37 pragma Polling (Off);
38 --  Turn off polling, we do not want ATC polling to take place during tasking
39 --  operations. It causes infinite loops and other problems.
40
41 with Interfaces.C;
42 with Interfaces.C.Strings;
43
44 with System.Float_Control;
45 with System.Interrupt_Management;
46 with System.Multiprocessors;
47 with System.OS_Primitives;
48 with System.Task_Info;
49 with System.Tasking.Debug;
50 with System.Win32.Ext;
51
52 with System.Soft_Links;
53 --  We use System.Soft_Links instead of System.Tasking.Initialization because
54 --  the later is a higher level package that we shouldn't depend on. For
55 --  example when using the restricted run time, it is replaced by
56 --  System.Tasking.Restricted.Stages.
57
58 package body System.Task_Primitives.Operations is
59
60    package SSL renames System.Soft_Links;
61
62    use Interfaces.C;
63    use Interfaces.C.Strings;
64    use System.OS_Interface;
65    use System.OS_Primitives;
66    use System.Parameters;
67    use System.Task_Info;
68    use System.Tasking;
69    use System.Tasking.Debug;
70    use System.Win32;
71    use System.Win32.Ext;
72
73    pragma Link_With ("-Xlinker --stack=0x200000,0x1000");
74    --  Change the default stack size (2 MB) for tasking programs on Windows.
75    --  This allows about 1000 tasks running at the same time. Note that
76    --  we set the stack size for non tasking programs on System unit.
77    --  Also note that under Windows XP, we use a Windows XP extension to
78    --  specify the stack size on a per task basis, as done under other OSes.
79
80    ---------------------
81    -- Local Functions --
82    ---------------------
83
84    procedure InitializeCriticalSection (pCriticalSection : access RTS_Lock);
85    procedure InitializeCriticalSection
86      (pCriticalSection : access CRITICAL_SECTION);
87    pragma Import
88      (Stdcall, InitializeCriticalSection, "InitializeCriticalSection");
89
90    procedure EnterCriticalSection (pCriticalSection : access RTS_Lock);
91    procedure EnterCriticalSection
92      (pCriticalSection : access CRITICAL_SECTION);
93    pragma Import (Stdcall, EnterCriticalSection, "EnterCriticalSection");
94
95    procedure LeaveCriticalSection (pCriticalSection : access RTS_Lock);
96    procedure LeaveCriticalSection (pCriticalSection : access CRITICAL_SECTION);
97    pragma Import (Stdcall, LeaveCriticalSection, "LeaveCriticalSection");
98
99    procedure DeleteCriticalSection (pCriticalSection : access RTS_Lock);
100    procedure DeleteCriticalSection
101      (pCriticalSection : access CRITICAL_SECTION);
102    pragma Import (Stdcall, DeleteCriticalSection, "DeleteCriticalSection");
103
104    ----------------
105    -- Local Data --
106    ----------------
107
108    Environment_Task_Id : Task_Id;
109    --  A variable to hold Task_Id for the environment task
110
111    Single_RTS_Lock : aliased RTS_Lock;
112    --  This is a lock to allow only one thread of control in the RTS at
113    --  a time; it is used to execute in mutual exclusion from all other tasks.
114    --  Used mainly in Single_Lock mode, but also to protect All_Tasks_List
115
116    Time_Slice_Val : Integer;
117    pragma Import (C, Time_Slice_Val, "__gl_time_slice_val");
118
119    Dispatching_Policy : Character;
120    pragma Import (C, Dispatching_Policy, "__gl_task_dispatching_policy");
121
122    function Get_Policy (Prio : System.Any_Priority) return Character;
123    pragma Import (C, Get_Policy, "__gnat_get_specific_dispatching");
124    --  Get priority specific dispatching policy
125
126    Foreign_Task_Elaborated : aliased Boolean := True;
127    --  Used to identified fake tasks (i.e., non-Ada Threads)
128
129    Null_Thread_Id : constant Thread_Id := 0;
130    --  Constant to indicate that the thread identifier has not yet been
131    --  initialized.
132
133    ------------------------------------
134    -- The thread local storage index --
135    ------------------------------------
136
137    TlsIndex : DWORD;
138    pragma Export (Ada, TlsIndex);
139    --  To ensure that this variable won't be local to this package, since
140    --  in some cases, inlining forces this variable to be global anyway.
141
142    --------------------
143    -- Local Packages --
144    --------------------
145
146    package Specific is
147
148       function Is_Valid_Task return Boolean;
149       pragma Inline (Is_Valid_Task);
150       --  Does executing thread have a TCB?
151
152       procedure Set (Self_Id : Task_Id);
153       pragma Inline (Set);
154       --  Set the self id for the current task
155
156    end Specific;
157
158    package body Specific is
159
160       function Is_Valid_Task return Boolean is
161       begin
162          return TlsGetValue (TlsIndex) /= System.Null_Address;
163       end Is_Valid_Task;
164
165       procedure Set (Self_Id : Task_Id) is
166          Succeeded : BOOL;
167       begin
168          Succeeded := TlsSetValue (TlsIndex, To_Address (Self_Id));
169          pragma Assert (Succeeded = Win32.TRUE);
170       end Set;
171
172    end Specific;
173
174    ----------------------------------
175    -- ATCB allocation/deallocation --
176    ----------------------------------
177
178    package body ATCB_Allocation is separate;
179    --  The body of this package is shared across several targets
180
181    ---------------------------------
182    -- Support for foreign threads --
183    ---------------------------------
184
185    function Register_Foreign_Thread (Thread : Thread_Id) return Task_Id;
186    --  Allocate and Initialize a new ATCB for the current Thread
187
188    function Register_Foreign_Thread
189      (Thread : Thread_Id) return Task_Id is separate;
190
191    ----------------------------------
192    -- Condition Variable Functions --
193    ----------------------------------
194
195    procedure Initialize_Cond (Cond : not null access Condition_Variable);
196    --  Initialize given condition variable Cond
197
198    procedure Finalize_Cond (Cond : not null access Condition_Variable);
199    --  Finalize given condition variable Cond
200
201    procedure Cond_Signal (Cond : not null access Condition_Variable);
202    --  Signal condition variable Cond
203
204    procedure Cond_Wait
205      (Cond : not null access Condition_Variable;
206       L    : not null access RTS_Lock);
207    --  Wait on conditional variable Cond, using lock L
208
209    procedure Cond_Timed_Wait
210      (Cond      : not null access Condition_Variable;
211       L         : not null access RTS_Lock;
212       Rel_Time  : Duration;
213       Timed_Out : out Boolean;
214       Status    : out Integer);
215    --  Do timed wait on condition variable Cond using lock L. The duration
216    --  of the timed wait is given by Rel_Time. When the condition is
217    --  signalled, Timed_Out shows whether or not a time out occurred.
218    --  Status is only valid if Timed_Out is False, in which case it
219    --  shows whether Cond_Timed_Wait completed successfully.
220
221    ---------------------
222    -- Initialize_Cond --
223    ---------------------
224
225    procedure Initialize_Cond (Cond : not null access Condition_Variable) is
226       hEvent : HANDLE;
227    begin
228       hEvent := CreateEvent (null, Win32.TRUE, Win32.FALSE, Null_Ptr);
229       pragma Assert (hEvent /= 0);
230       Cond.all := Condition_Variable (hEvent);
231    end Initialize_Cond;
232
233    -------------------
234    -- Finalize_Cond --
235    -------------------
236
237    --  No such problem here, DosCloseEventSem has been derived.
238    --  What does such refer to in above comment???
239
240    procedure Finalize_Cond (Cond : not null access Condition_Variable) is
241       Result : BOOL;
242    begin
243       Result := CloseHandle (HANDLE (Cond.all));
244       pragma Assert (Result = Win32.TRUE);
245    end Finalize_Cond;
246
247    -----------------
248    -- Cond_Signal --
249    -----------------
250
251    procedure Cond_Signal (Cond : not null access Condition_Variable) is
252       Result : BOOL;
253    begin
254       Result := SetEvent (HANDLE (Cond.all));
255       pragma Assert (Result = Win32.TRUE);
256    end Cond_Signal;
257
258    ---------------
259    -- Cond_Wait --
260    ---------------
261
262    --  Pre-condition: Cond is posted
263    --                 L is locked.
264
265    --  Post-condition: Cond is posted
266    --                  L is locked.
267
268    procedure Cond_Wait
269      (Cond : not null access Condition_Variable;
270       L    : not null access RTS_Lock)
271    is
272       Result      : DWORD;
273       Result_Bool : BOOL;
274
275    begin
276       --  Must reset Cond BEFORE L is unlocked
277
278       Result_Bool := ResetEvent (HANDLE (Cond.all));
279       pragma Assert (Result_Bool = Win32.TRUE);
280       Unlock (L, Global_Lock => True);
281
282       --  No problem if we are interrupted here: if the condition is signaled,
283       --  WaitForSingleObject will simply not block
284
285       Result := WaitForSingleObject (HANDLE (Cond.all), Wait_Infinite);
286       pragma Assert (Result = 0);
287
288       Write_Lock (L, Global_Lock => True);
289    end Cond_Wait;
290
291    ---------------------
292    -- Cond_Timed_Wait --
293    ---------------------
294
295    --  Pre-condition: Cond is posted
296    --                 L is locked.
297
298    --  Post-condition: Cond is posted
299    --                  L is locked.
300
301    procedure Cond_Timed_Wait
302      (Cond      : not null access Condition_Variable;
303       L         : not null access RTS_Lock;
304       Rel_Time  : Duration;
305       Timed_Out : out Boolean;
306       Status    : out Integer)
307    is
308       Time_Out_Max : constant DWORD := 16#FFFF0000#;
309       --  NT 4 can't handle excessive timeout values (e.g. DWORD'Last - 1)
310
311       Time_Out    : DWORD;
312       Result      : BOOL;
313       Wait_Result : DWORD;
314
315    begin
316       --  Must reset Cond BEFORE L is unlocked
317
318       Result := ResetEvent (HANDLE (Cond.all));
319       pragma Assert (Result = Win32.TRUE);
320       Unlock (L, Global_Lock => True);
321
322       --  No problem if we are interrupted here: if the condition is signaled,
323       --  WaitForSingleObject will simply not block.
324
325       if Rel_Time <= 0.0 then
326          Timed_Out := True;
327          Wait_Result := 0;
328
329       else
330          Time_Out :=
331            (if Rel_Time >= Duration (Time_Out_Max) / 1000
332             then Time_Out_Max
333             else DWORD (Rel_Time * 1000));
334
335          Wait_Result := WaitForSingleObject (HANDLE (Cond.all), Time_Out);
336
337          if Wait_Result = WAIT_TIMEOUT then
338             Timed_Out := True;
339             Wait_Result := 0;
340          else
341             Timed_Out := False;
342          end if;
343       end if;
344
345       Write_Lock (L, Global_Lock => True);
346
347       --  Ensure post-condition
348
349       if Timed_Out then
350          Result := SetEvent (HANDLE (Cond.all));
351          pragma Assert (Result = Win32.TRUE);
352       end if;
353
354       Status := Integer (Wait_Result);
355    end Cond_Timed_Wait;
356
357    ------------------
358    -- Stack_Guard  --
359    ------------------
360
361    --  The underlying thread system sets a guard page at the bottom of a thread
362    --  stack, so nothing is needed.
363    --  ??? Check the comment above
364
365    procedure Stack_Guard (T : ST.Task_Id; On : Boolean) is
366       pragma Unreferenced (T, On);
367    begin
368       null;
369    end Stack_Guard;
370
371    --------------------
372    -- Get_Thread_Id  --
373    --------------------
374
375    function Get_Thread_Id (T : ST.Task_Id) return OSI.Thread_Id is
376    begin
377       return T.Common.LL.Thread;
378    end Get_Thread_Id;
379
380    ----------
381    -- Self --
382    ----------
383
384    function Self return Task_Id is
385       Self_Id : constant Task_Id := To_Task_Id (TlsGetValue (TlsIndex));
386    begin
387       if Self_Id = null then
388          return Register_Foreign_Thread (GetCurrentThread);
389       else
390          return Self_Id;
391       end if;
392    end Self;
393
394    ---------------------
395    -- Initialize_Lock --
396    ---------------------
397
398    --  Note: mutexes and cond_variables needed per-task basis are initialized
399    --  in Initialize_TCB and the Storage_Error is handled. Other mutexes (such
400    --  as RTS_Lock, Memory_Lock...) used in the RTS is initialized before any
401    --  status change of RTS. Therefore raising Storage_Error in the following
402    --  routines should be able to be handled safely.
403
404    procedure Initialize_Lock
405      (Prio : System.Any_Priority;
406       L    : not null access Lock)
407    is
408    begin
409       InitializeCriticalSection (L.Mutex'Access);
410       L.Owner_Priority := 0;
411       L.Priority := Prio;
412    end Initialize_Lock;
413
414    procedure Initialize_Lock
415      (L : not null access RTS_Lock; Level : Lock_Level)
416    is
417       pragma Unreferenced (Level);
418    begin
419       InitializeCriticalSection (L);
420    end Initialize_Lock;
421
422    -------------------
423    -- Finalize_Lock --
424    -------------------
425
426    procedure Finalize_Lock (L : not null access Lock) is
427    begin
428       DeleteCriticalSection (L.Mutex'Access);
429    end Finalize_Lock;
430
431    procedure Finalize_Lock (L : not null access RTS_Lock) is
432    begin
433       DeleteCriticalSection (L);
434    end Finalize_Lock;
435
436    ----------------
437    -- Write_Lock --
438    ----------------
439
440    procedure Write_Lock
441      (L : not null access Lock; Ceiling_Violation : out Boolean) is
442    begin
443       L.Owner_Priority := Get_Priority (Self);
444
445       if L.Priority < L.Owner_Priority then
446          Ceiling_Violation := True;
447          return;
448       end if;
449
450       EnterCriticalSection (L.Mutex'Access);
451
452       Ceiling_Violation := False;
453    end Write_Lock;
454
455    procedure Write_Lock
456      (L           : not null access RTS_Lock;
457       Global_Lock : Boolean := False)
458    is
459    begin
460       if not Single_Lock or else Global_Lock then
461          EnterCriticalSection (L);
462       end if;
463    end Write_Lock;
464
465    procedure Write_Lock (T : Task_Id) is
466    begin
467       if not Single_Lock then
468          EnterCriticalSection (T.Common.LL.L'Access);
469       end if;
470    end Write_Lock;
471
472    ---------------
473    -- Read_Lock --
474    ---------------
475
476    procedure Read_Lock
477      (L : not null access Lock; Ceiling_Violation : out Boolean) is
478    begin
479       Write_Lock (L, Ceiling_Violation);
480    end Read_Lock;
481
482    ------------
483    -- Unlock --
484    ------------
485
486    procedure Unlock (L : not null access Lock) is
487    begin
488       LeaveCriticalSection (L.Mutex'Access);
489    end Unlock;
490
491    procedure Unlock
492      (L : not null access RTS_Lock; Global_Lock : Boolean := False) is
493    begin
494       if not Single_Lock or else Global_Lock then
495          LeaveCriticalSection (L);
496       end if;
497    end Unlock;
498
499    procedure Unlock (T : Task_Id) is
500    begin
501       if not Single_Lock then
502          LeaveCriticalSection (T.Common.LL.L'Access);
503       end if;
504    end Unlock;
505
506    -----------------
507    -- Set_Ceiling --
508    -----------------
509
510    --  Dynamic priority ceilings are not supported by the underlying system
511
512    procedure Set_Ceiling
513      (L    : not null access Lock;
514       Prio : System.Any_Priority)
515    is
516       pragma Unreferenced (L, Prio);
517    begin
518       null;
519    end Set_Ceiling;
520
521    -----------
522    -- Sleep --
523    -----------
524
525    procedure Sleep
526      (Self_ID : Task_Id;
527       Reason  : System.Tasking.Task_States)
528    is
529       pragma Unreferenced (Reason);
530
531    begin
532       pragma Assert (Self_ID = Self);
533
534       if Single_Lock then
535          Cond_Wait (Self_ID.Common.LL.CV'Access, Single_RTS_Lock'Access);
536       else
537          Cond_Wait (Self_ID.Common.LL.CV'Access, Self_ID.Common.LL.L'Access);
538       end if;
539
540       if Self_ID.Deferral_Level = 0
541         and then Self_ID.Pending_ATC_Level < Self_ID.ATC_Nesting_Level
542       then
543          Unlock (Self_ID);
544          raise Standard'Abort_Signal;
545       end if;
546    end Sleep;
547
548    -----------------
549    -- Timed_Sleep --
550    -----------------
551
552    --  This is for use within the run-time system, so abort is assumed to be
553    --  already deferred, and the caller should be holding its own ATCB lock.
554
555    procedure Timed_Sleep
556      (Self_ID  : Task_Id;
557       Time     : Duration;
558       Mode     : ST.Delay_Modes;
559       Reason   : System.Tasking.Task_States;
560       Timedout : out Boolean;
561       Yielded  : out Boolean)
562    is
563       pragma Unreferenced (Reason);
564       Check_Time : Duration := Monotonic_Clock;
565       Rel_Time   : Duration;
566       Abs_Time   : Duration;
567
568       Result : Integer;
569       pragma Unreferenced (Result);
570
571       Local_Timedout : Boolean;
572
573    begin
574       Timedout := True;
575       Yielded  := False;
576
577       if Mode = Relative then
578          Rel_Time := Time;
579          Abs_Time := Duration'Min (Time, Max_Sensible_Delay) + Check_Time;
580       else
581          Rel_Time := Time - Check_Time;
582          Abs_Time := Time;
583       end if;
584
585       if Rel_Time > 0.0 then
586          loop
587             exit when Self_ID.Pending_ATC_Level < Self_ID.ATC_Nesting_Level;
588
589             if Single_Lock then
590                Cond_Timed_Wait
591                  (Self_ID.Common.LL.CV'Access,
592                   Single_RTS_Lock'Access,
593                   Rel_Time, Local_Timedout, Result);
594             else
595                Cond_Timed_Wait
596                  (Self_ID.Common.LL.CV'Access,
597                   Self_ID.Common.LL.L'Access,
598                   Rel_Time, Local_Timedout, Result);
599             end if;
600
601             Check_Time := Monotonic_Clock;
602             exit when Abs_Time <= Check_Time;
603
604             if not Local_Timedout then
605
606                --  Somebody may have called Wakeup for us
607
608                Timedout := False;
609                exit;
610             end if;
611
612             Rel_Time := Abs_Time - Check_Time;
613          end loop;
614       end if;
615    end Timed_Sleep;
616
617    -----------------
618    -- Timed_Delay --
619    -----------------
620
621    procedure Timed_Delay
622      (Self_ID : Task_Id;
623       Time    : Duration;
624       Mode    : ST.Delay_Modes)
625    is
626       Check_Time : Duration := Monotonic_Clock;
627       Rel_Time   : Duration;
628       Abs_Time   : Duration;
629
630       Timedout : Boolean;
631       Result   : Integer;
632       pragma Unreferenced (Timedout, Result);
633
634    begin
635       if Single_Lock then
636          Lock_RTS;
637       end if;
638
639       Write_Lock (Self_ID);
640
641       if Mode = Relative then
642          Rel_Time := Time;
643          Abs_Time := Time + Check_Time;
644       else
645          Rel_Time := Time - Check_Time;
646          Abs_Time := Time;
647       end if;
648
649       if Rel_Time > 0.0 then
650          Self_ID.Common.State := Delay_Sleep;
651
652          loop
653             exit when Self_ID.Pending_ATC_Level < Self_ID.ATC_Nesting_Level;
654
655             if Single_Lock then
656                Cond_Timed_Wait
657                  (Self_ID.Common.LL.CV'Access,
658                   Single_RTS_Lock'Access,
659                   Rel_Time, Timedout, Result);
660             else
661                Cond_Timed_Wait
662                  (Self_ID.Common.LL.CV'Access,
663                   Self_ID.Common.LL.L'Access,
664                   Rel_Time, Timedout, Result);
665             end if;
666
667             Check_Time := Monotonic_Clock;
668             exit when Abs_Time <= Check_Time;
669
670             Rel_Time := Abs_Time - Check_Time;
671          end loop;
672
673          Self_ID.Common.State := Runnable;
674       end if;
675
676       Unlock (Self_ID);
677
678       if Single_Lock then
679          Unlock_RTS;
680       end if;
681
682       Yield;
683    end Timed_Delay;
684
685    ------------
686    -- Wakeup --
687    ------------
688
689    procedure Wakeup (T : Task_Id; Reason : System.Tasking.Task_States) is
690       pragma Unreferenced (Reason);
691    begin
692       Cond_Signal (T.Common.LL.CV'Access);
693    end Wakeup;
694
695    -----------
696    -- Yield --
697    -----------
698
699    procedure Yield (Do_Yield : Boolean := True) is
700    begin
701       --  Note: in a previous implementation if Do_Yield was False, then we
702       --  introduced a delay of 1 millisecond in an attempt to get closer to
703       --  annex D semantics, and in particular to make ACATS CXD8002 pass. But
704       --  this change introduced a huge performance regression evaluating the
705       --  Count attribute. So we decided to remove this processing.
706
707       --  Moreover, CXD8002 appears to pass on Windows (although we do not
708       --  guarantee full Annex D compliance on Windows in any case).
709
710       if Do_Yield then
711          SwitchToThread;
712       end if;
713    end Yield;
714
715    ------------------
716    -- Set_Priority --
717    ------------------
718
719    type Prio_Array_Type is array (System.Any_Priority) of Integer;
720    pragma Atomic_Components (Prio_Array_Type);
721
722    Prio_Array : Prio_Array_Type;
723    --  Global array containing the id of the currently running task for
724    --  each priority.
725    --
726    --  Note: we assume that we are on a single processor with run-til-blocked
727    --  scheduling.
728
729    procedure Set_Priority
730      (T                   : Task_Id;
731       Prio                : System.Any_Priority;
732       Loss_Of_Inheritance : Boolean := False)
733    is
734       Res        : BOOL;
735       Array_Item : Integer;
736
737    begin
738       Res := SetThreadPriority
739         (T.Common.LL.Thread, Interfaces.C.int (Underlying_Priorities (Prio)));
740       pragma Assert (Res = Win32.TRUE);
741
742       if Dispatching_Policy = 'F' or else Get_Policy (Prio) = 'F' then
743
744          --  Annex D requirement [RM D.2.2 par. 9]:
745          --    If the task drops its priority due to the loss of inherited
746          --    priority, it is added at the head of the ready queue for its
747          --    new active priority.
748
749          if Loss_Of_Inheritance
750            and then Prio < T.Common.Current_Priority
751          then
752             Array_Item := Prio_Array (T.Common.Base_Priority) + 1;
753             Prio_Array (T.Common.Base_Priority) := Array_Item;
754
755             loop
756                --  Let some processes a chance to arrive
757
758                Yield;
759
760                --  Then wait for our turn to proceed
761
762                exit when Array_Item = Prio_Array (T.Common.Base_Priority)
763                  or else Prio_Array (T.Common.Base_Priority) = 1;
764             end loop;
765
766             Prio_Array (T.Common.Base_Priority) :=
767               Prio_Array (T.Common.Base_Priority) - 1;
768          end if;
769       end if;
770
771       T.Common.Current_Priority := Prio;
772    end Set_Priority;
773
774    ------------------
775    -- Get_Priority --
776    ------------------
777
778    function Get_Priority (T : Task_Id) return System.Any_Priority is
779    begin
780       return T.Common.Current_Priority;
781    end Get_Priority;
782
783    ----------------
784    -- Enter_Task --
785    ----------------
786
787    --  There were two paths were we needed to call Enter_Task :
788    --  1) from System.Task_Primitives.Operations.Initialize
789    --  2) from System.Tasking.Stages.Task_Wrapper
790
791    --  The thread initialisation has to be done only for the first case
792
793    --  This is because the GetCurrentThread NT call does not return the real
794    --  thread handler but only a "pseudo" one. It is not possible to release
795    --  the thread handle and free the system resources from this "pseudo"
796    --  handle. So we really want to keep the real thread handle set in
797    --  System.Task_Primitives.Operations.Create_Task during thread creation.
798
799    procedure Enter_Task (Self_ID : Task_Id) is
800       procedure Get_Stack_Bounds (Base : Address; Limit : Address);
801       pragma Import (C, Get_Stack_Bounds, "__gnat_get_stack_bounds");
802       --  Get stack boundaries
803    begin
804       Specific.Set (Self_ID);
805
806       --  Properly initializes the FPU for x86 systems
807
808       System.Float_Control.Reset;
809
810       if Self_ID.Common.Task_Info /= null
811         and then
812           Self_ID.Common.Task_Info.CPU >= CPU_Number (Number_Of_Processors)
813       then
814          raise Invalid_CPU_Number;
815       end if;
816
817       Self_ID.Common.LL.Thread_Id := GetCurrentThreadId;
818
819       Get_Stack_Bounds
820         (Self_ID.Common.Compiler_Data.Pri_Stack_Info.Base'Address,
821          Self_ID.Common.Compiler_Data.Pri_Stack_Info.Limit'Address);
822    end Enter_Task;
823
824    -------------------
825    -- Is_Valid_Task --
826    -------------------
827
828    function Is_Valid_Task return Boolean renames Specific.Is_Valid_Task;
829
830    -----------------------------
831    -- Register_Foreign_Thread --
832    -----------------------------
833
834    function Register_Foreign_Thread return Task_Id is
835    begin
836       if Is_Valid_Task then
837          return Self;
838       else
839          return Register_Foreign_Thread (GetCurrentThread);
840       end if;
841    end Register_Foreign_Thread;
842
843    --------------------
844    -- Initialize_TCB --
845    --------------------
846
847    procedure Initialize_TCB (Self_ID : Task_Id; Succeeded : out Boolean) is
848    begin
849       --  Initialize thread ID to 0, this is needed to detect threads that
850       --  are not yet activated.
851
852       Self_ID.Common.LL.Thread := Null_Thread_Id;
853
854       Initialize_Cond (Self_ID.Common.LL.CV'Access);
855
856       if not Single_Lock then
857          Initialize_Lock (Self_ID.Common.LL.L'Access, ATCB_Level);
858       end if;
859
860       Succeeded := True;
861    end Initialize_TCB;
862
863    -----------------
864    -- Create_Task --
865    -----------------
866
867    procedure Create_Task
868      (T          : Task_Id;
869       Wrapper    : System.Address;
870       Stack_Size : System.Parameters.Size_Type;
871       Priority   : System.Any_Priority;
872       Succeeded  : out Boolean)
873    is
874       Initial_Stack_Size : constant := 1024;
875       --  We set the initial stack size to 1024. On Windows version prior to XP
876       --  there is no way to fix a task stack size. Only the initial stack size
877       --  can be set, the operating system will raise the task stack size if
878       --  needed.
879
880       function Is_Windows_XP return Integer;
881       pragma Import (C, Is_Windows_XP, "__gnat_is_windows_xp");
882       --  Returns 1 if running on Windows XP
883
884       hTask          : HANDLE;
885       TaskId         : aliased DWORD;
886       pTaskParameter : Win32.PVOID;
887       Result         : DWORD;
888       Entry_Point    : PTHREAD_START_ROUTINE;
889
890       use type System.Multiprocessors.CPU_Range;
891
892    begin
893       --  Check whether both Dispatching_Domain and CPU are specified for the
894       --  task, and the CPU value is not contained within the range of
895       --  processors for the domain.
896
897       if T.Common.Domain /= null
898         and then T.Common.Base_CPU /= System.Multiprocessors.Not_A_Specific_CPU
899         and then
900           (T.Common.Base_CPU not in T.Common.Domain'Range
901             or else not T.Common.Domain (T.Common.Base_CPU))
902       then
903          Succeeded := False;
904          return;
905       end if;
906
907       pTaskParameter := To_Address (T);
908
909       Entry_Point := To_PTHREAD_START_ROUTINE (Wrapper);
910
911       if Is_Windows_XP = 1 then
912          hTask := CreateThread
913            (null,
914             DWORD (Stack_Size),
915             Entry_Point,
916             pTaskParameter,
917             DWORD (Create_Suspended) or
918               DWORD (Stack_Size_Param_Is_A_Reservation),
919             TaskId'Unchecked_Access);
920       else
921          hTask := CreateThread
922            (null,
923             Initial_Stack_Size,
924             Entry_Point,
925             pTaskParameter,
926             DWORD (Create_Suspended),
927             TaskId'Unchecked_Access);
928       end if;
929
930       --  Step 1: Create the thread in blocked mode
931
932       if hTask = 0 then
933          Succeeded := False;
934          return;
935       end if;
936
937       --  Step 2: set its TCB
938
939       T.Common.LL.Thread := hTask;
940
941       --  Note: it would be useful to initialize Thread_Id right away to avoid
942       --  a race condition in gdb where Thread_ID may not have the right value
943       --  yet, but GetThreadId is a Vista specific API, not available under XP:
944       --  T.Common.LL.Thread_Id := GetThreadId (hTask); so instead we set the
945       --  field to 0 to avoid having a random value. Thread_Id is initialized
946       --  in Enter_Task anyway.
947
948       T.Common.LL.Thread_Id := 0;
949
950       --  Step 3: set its priority (child has inherited priority from parent)
951
952       Set_Priority (T, Priority);
953
954       if Time_Slice_Val = 0
955         or else Dispatching_Policy = 'F'
956         or else Get_Policy (Priority) = 'F'
957       then
958          --  Here we need Annex D semantics so we disable the NT priority
959          --  boost. A priority boost is temporarily given by the system to
960          --  a thread when it is taken out of a wait state.
961
962          SetThreadPriorityBoost (hTask, DisablePriorityBoost => Win32.TRUE);
963       end if;
964
965       --  Step 4: Handle pragma CPU and Task_Info
966
967       Set_Task_Affinity (T);
968
969       --  Step 5: Now, start it for good
970
971       Result := ResumeThread (hTask);
972       pragma Assert (Result = 1);
973
974       Succeeded := Result = 1;
975    end Create_Task;
976
977    ------------------
978    -- Finalize_TCB --
979    ------------------
980
981    procedure Finalize_TCB (T : Task_Id) is
982       Succeeded : BOOL;
983
984    begin
985       if not Single_Lock then
986          Finalize_Lock (T.Common.LL.L'Access);
987       end if;
988
989       Finalize_Cond (T.Common.LL.CV'Access);
990
991       if T.Known_Tasks_Index /= -1 then
992          Known_Tasks (T.Known_Tasks_Index) := null;
993       end if;
994
995       if T.Common.LL.Thread /= 0 then
996
997          --  This task has been activated. Close the thread handle. This
998          --  is needed to release system resources.
999
1000          Succeeded := CloseHandle (T.Common.LL.Thread);
1001          pragma Assert (Succeeded = Win32.TRUE);
1002       end if;
1003
1004       ATCB_Allocation.Free_ATCB (T);
1005    end Finalize_TCB;
1006
1007    ---------------
1008    -- Exit_Task --
1009    ---------------
1010
1011    procedure Exit_Task is
1012    begin
1013       Specific.Set (null);
1014    end Exit_Task;
1015
1016    ----------------
1017    -- Abort_Task --
1018    ----------------
1019
1020    procedure Abort_Task (T : Task_Id) is
1021       pragma Unreferenced (T);
1022    begin
1023       null;
1024    end Abort_Task;
1025
1026    ----------------------
1027    -- Environment_Task --
1028    ----------------------
1029
1030    function Environment_Task return Task_Id is
1031    begin
1032       return Environment_Task_Id;
1033    end Environment_Task;
1034
1035    --------------
1036    -- Lock_RTS --
1037    --------------
1038
1039    procedure Lock_RTS is
1040    begin
1041       Write_Lock (Single_RTS_Lock'Access, Global_Lock => True);
1042    end Lock_RTS;
1043
1044    ----------------
1045    -- Unlock_RTS --
1046    ----------------
1047
1048    procedure Unlock_RTS is
1049    begin
1050       Unlock (Single_RTS_Lock'Access, Global_Lock => True);
1051    end Unlock_RTS;
1052
1053    ----------------
1054    -- Initialize --
1055    ----------------
1056
1057    procedure Initialize (Environment_Task : Task_Id) is
1058       Discard : BOOL;
1059       pragma Unreferenced (Discard);
1060
1061    begin
1062       Environment_Task_Id := Environment_Task;
1063       OS_Primitives.Initialize;
1064       Interrupt_Management.Initialize;
1065
1066       if Time_Slice_Val = 0 or else Dispatching_Policy = 'F' then
1067          --  Here we need Annex D semantics, switch the current process to the
1068          --  Realtime_Priority_Class.
1069
1070          Discard := OS_Interface.SetPriorityClass
1071                       (GetCurrentProcess, Realtime_Priority_Class);
1072       end if;
1073
1074       TlsIndex := TlsAlloc;
1075
1076       --  Initialize the lock used to synchronize chain of all ATCBs
1077
1078       Initialize_Lock (Single_RTS_Lock'Access, RTS_Lock_Level);
1079
1080       Environment_Task.Common.LL.Thread := GetCurrentThread;
1081
1082       --  Make environment task known here because it doesn't go through
1083       --  Activate_Tasks, which does it for all other tasks.
1084
1085       Known_Tasks (Known_Tasks'First) := Environment_Task;
1086       Environment_Task.Known_Tasks_Index := Known_Tasks'First;
1087
1088       Enter_Task (Environment_Task);
1089
1090       --  pragma CPU and dispatching domains for the environment task
1091
1092       Set_Task_Affinity (Environment_Task);
1093    end Initialize;
1094
1095    ---------------------
1096    -- Monotonic_Clock --
1097    ---------------------
1098
1099    function Monotonic_Clock return Duration
1100      renames System.OS_Primitives.Monotonic_Clock;
1101
1102    -------------------
1103    -- RT_Resolution --
1104    -------------------
1105
1106    function RT_Resolution return Duration is
1107    begin
1108       return 0.000_001; --  1 micro-second
1109    end RT_Resolution;
1110
1111    ----------------
1112    -- Initialize --
1113    ----------------
1114
1115    procedure Initialize (S : in out Suspension_Object) is
1116    begin
1117       --  Initialize internal state. It is always initialized to False (ARM
1118       --  D.10 par. 6).
1119
1120       S.State := False;
1121       S.Waiting := False;
1122
1123       --  Initialize internal mutex
1124
1125       InitializeCriticalSection (S.L'Access);
1126
1127       --  Initialize internal condition variable
1128
1129       S.CV := CreateEvent (null, Win32.TRUE, Win32.FALSE, Null_Ptr);
1130       pragma Assert (S.CV /= 0);
1131    end Initialize;
1132
1133    --------------
1134    -- Finalize --
1135    --------------
1136
1137    procedure Finalize (S : in out Suspension_Object) is
1138       Result : BOOL;
1139
1140    begin
1141       --  Destroy internal mutex
1142
1143       DeleteCriticalSection (S.L'Access);
1144
1145       --  Destroy internal condition variable
1146
1147       Result := CloseHandle (S.CV);
1148       pragma Assert (Result = Win32.TRUE);
1149    end Finalize;
1150
1151    -------------------
1152    -- Current_State --
1153    -------------------
1154
1155    function Current_State (S : Suspension_Object) return Boolean is
1156    begin
1157       --  We do not want to use lock on this read operation. State is marked
1158       --  as Atomic so that we ensure that the value retrieved is correct.
1159
1160       return S.State;
1161    end Current_State;
1162
1163    ---------------
1164    -- Set_False --
1165    ---------------
1166
1167    procedure Set_False (S : in out Suspension_Object) is
1168    begin
1169       SSL.Abort_Defer.all;
1170
1171       EnterCriticalSection (S.L'Access);
1172
1173       S.State := False;
1174
1175       LeaveCriticalSection (S.L'Access);
1176
1177       SSL.Abort_Undefer.all;
1178    end Set_False;
1179
1180    --------------
1181    -- Set_True --
1182    --------------
1183
1184    procedure Set_True (S : in out Suspension_Object) is
1185       Result : BOOL;
1186
1187    begin
1188       SSL.Abort_Defer.all;
1189
1190       EnterCriticalSection (S.L'Access);
1191
1192       --  If there is already a task waiting on this suspension object then
1193       --  we resume it, leaving the state of the suspension object to False,
1194       --  as it is specified in ARM D.10 par. 9. Otherwise, it just leaves
1195       --  the state to True.
1196
1197       if S.Waiting then
1198          S.Waiting := False;
1199          S.State := False;
1200
1201          Result := SetEvent (S.CV);
1202          pragma Assert (Result = Win32.TRUE);
1203
1204       else
1205          S.State := True;
1206       end if;
1207
1208       LeaveCriticalSection (S.L'Access);
1209
1210       SSL.Abort_Undefer.all;
1211    end Set_True;
1212
1213    ------------------------
1214    -- Suspend_Until_True --
1215    ------------------------
1216
1217    procedure Suspend_Until_True (S : in out Suspension_Object) is
1218       Result      : DWORD;
1219       Result_Bool : BOOL;
1220
1221    begin
1222       SSL.Abort_Defer.all;
1223
1224       EnterCriticalSection (S.L'Access);
1225
1226       if S.Waiting then
1227
1228          --  Program_Error must be raised upon calling Suspend_Until_True
1229          --  if another task is already waiting on that suspension object
1230          --  (ARM D.10 par. 10).
1231
1232          LeaveCriticalSection (S.L'Access);
1233
1234          SSL.Abort_Undefer.all;
1235
1236          raise Program_Error;
1237
1238       else
1239          --  Suspend the task if the state is False. Otherwise, the task
1240          --  continues its execution, and the state of the suspension object
1241          --  is set to False (ARM D.10 par. 9).
1242
1243          if S.State then
1244             S.State := False;
1245
1246             LeaveCriticalSection (S.L'Access);
1247
1248             SSL.Abort_Undefer.all;
1249
1250          else
1251             S.Waiting := True;
1252
1253             --  Must reset CV BEFORE L is unlocked
1254
1255             Result_Bool := ResetEvent (S.CV);
1256             pragma Assert (Result_Bool = Win32.TRUE);
1257
1258             LeaveCriticalSection (S.L'Access);
1259
1260             SSL.Abort_Undefer.all;
1261
1262             Result := WaitForSingleObject (S.CV, Wait_Infinite);
1263             pragma Assert (Result = 0);
1264          end if;
1265       end if;
1266    end Suspend_Until_True;
1267
1268    ----------------
1269    -- Check_Exit --
1270    ----------------
1271
1272    --  Dummy versions, currently this only works for solaris (native)
1273
1274    function Check_Exit (Self_ID : ST.Task_Id) return Boolean is
1275       pragma Unreferenced (Self_ID);
1276    begin
1277       return True;
1278    end Check_Exit;
1279
1280    --------------------
1281    -- Check_No_Locks --
1282    --------------------
1283
1284    function Check_No_Locks (Self_ID : ST.Task_Id) return Boolean is
1285       pragma Unreferenced (Self_ID);
1286    begin
1287       return True;
1288    end Check_No_Locks;
1289
1290    ------------------
1291    -- Suspend_Task --
1292    ------------------
1293
1294    function Suspend_Task
1295      (T           : ST.Task_Id;
1296       Thread_Self : Thread_Id) return Boolean
1297    is
1298    begin
1299       if T.Common.LL.Thread /= Thread_Self then
1300          return SuspendThread (T.Common.LL.Thread) = NO_ERROR;
1301       else
1302          return True;
1303       end if;
1304    end Suspend_Task;
1305
1306    -----------------
1307    -- Resume_Task --
1308    -----------------
1309
1310    function Resume_Task
1311      (T           : ST.Task_Id;
1312       Thread_Self : Thread_Id) return Boolean
1313    is
1314    begin
1315       if T.Common.LL.Thread /= Thread_Self then
1316          return ResumeThread (T.Common.LL.Thread) = NO_ERROR;
1317       else
1318          return True;
1319       end if;
1320    end Resume_Task;
1321
1322    --------------------
1323    -- Stop_All_Tasks --
1324    --------------------
1325
1326    procedure Stop_All_Tasks is
1327    begin
1328       null;
1329    end Stop_All_Tasks;
1330
1331    ---------------
1332    -- Stop_Task --
1333    ---------------
1334
1335    function Stop_Task (T : ST.Task_Id) return Boolean is
1336       pragma Unreferenced (T);
1337    begin
1338       return False;
1339    end Stop_Task;
1340
1341    -------------------
1342    -- Continue_Task --
1343    -------------------
1344
1345    function Continue_Task (T : ST.Task_Id) return Boolean is
1346       pragma Unreferenced (T);
1347    begin
1348       return False;
1349    end Continue_Task;
1350
1351    -----------------------
1352    -- Set_Task_Affinity --
1353    -----------------------
1354
1355    procedure Set_Task_Affinity (T : ST.Task_Id) is
1356       Result : DWORD;
1357
1358       use type System.Multiprocessors.CPU_Range;
1359
1360    begin
1361       --  Do nothing if the underlying thread has not yet been created. If the
1362       --  thread has not yet been created then the proper affinity will be set
1363       --  during its creation.
1364
1365       if T.Common.LL.Thread = Null_Thread_Id then
1366          null;
1367
1368       --  pragma CPU
1369
1370       elsif T.Common.Base_CPU /= Multiprocessors.Not_A_Specific_CPU then
1371
1372          --  The CPU numbering in pragma CPU starts at 1 while the subprogram
1373          --  to set the affinity starts at 0, therefore we must substract 1.
1374
1375          Result :=
1376            SetThreadIdealProcessor
1377              (T.Common.LL.Thread, ProcessorId (T.Common.Base_CPU) - 1);
1378          pragma Assert (Result = 1);
1379
1380       --  Task_Info
1381
1382       elsif T.Common.Task_Info /= null then
1383          if T.Common.Task_Info.CPU /= Task_Info.Any_CPU then
1384             Result :=
1385               SetThreadIdealProcessor
1386                 (T.Common.LL.Thread, T.Common.Task_Info.CPU);
1387             pragma Assert (Result = 1);
1388          end if;
1389
1390       --  Dispatching domains
1391
1392       elsif T.Common.Domain /= null
1393         and then (T.Common.Domain /= ST.System_Domain
1394                    or else
1395                      T.Common.Domain.all /=
1396                        (Multiprocessors.CPU'First ..
1397                         Multiprocessors.Number_Of_CPUs => True))
1398       then
1399          declare
1400             CPU_Set : DWORD := 0;
1401
1402          begin
1403             for Proc in T.Common.Domain'Range loop
1404                if T.Common.Domain (Proc) then
1405
1406                   --  The thread affinity mask is a bit vector in which each
1407                   --  bit represents a logical processor.
1408
1409                   CPU_Set := CPU_Set + 2 ** (Integer (Proc) - 1);
1410                end if;
1411             end loop;
1412
1413             Result := SetThreadAffinityMask (T.Common.LL.Thread, CPU_Set);
1414             pragma Assert (Result = 1);
1415          end;
1416       end if;
1417    end Set_Task_Affinity;
1418
1419 end System.Task_Primitives.Operations;