OSDN Git Service

Daily bump.
[pf3gnuchains/gcc-fork.git] / gcc / ada / 5wtaprop.adb
1 ------------------------------------------------------------------------------
2 --                                                                          --
3 --                GNU ADA 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 --                                                                          --
10 --         Copyright (C) 1992-2002, Free Software Foundation, Inc.          --
11 --                                                                          --
12 -- GNARL is free software; you can  redistribute it  and/or modify it under --
13 -- terms of the  GNU General Public License as published  by the Free Soft- --
14 -- ware  Foundation;  either version 2,  or (at your option) any later ver- --
15 -- sion. GNARL is distributed in the hope that it will be useful, but WITH- --
16 -- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
17 -- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
18 -- for  more details.  You should have  received  a copy of the GNU General --
19 -- Public License  distributed with GNARL; see file COPYING.  If not, write --
20 -- to  the Free Software Foundation,  59 Temple Place - Suite 330,  Boston, --
21 -- MA 02111-1307, USA.                                                      --
22 --                                                                          --
23 -- As a special exception,  if other files  instantiate  generics from this --
24 -- unit, or you link  this unit with other files  to produce an executable, --
25 -- this  unit  does not  by itself cause  the resulting  executable  to  be --
26 -- covered  by the  GNU  General  Public  License.  This exception does not --
27 -- however invalidate  any other reasons why  the executable file  might be --
28 -- covered by the  GNU Public License.                                      --
29 --                                                                          --
30 -- GNARL was developed by the GNARL team at Florida State University. It is --
31 -- now maintained by Ada Core Technologies, Inc. (http://www.gnat.com).     --
32 --                                                                          --
33 ------------------------------------------------------------------------------
34
35 --  This is a NT (native) version of this package.
36
37 --  This package contains all the GNULL primitives that interface directly
38 --  with the underlying OS.
39
40 pragma Polling (Off);
41 --  Turn off polling, we do not want ATC polling to take place during
42 --  tasking operations. It causes infinite loops and other problems.
43
44 with System.Tasking.Debug;
45 --  used for Known_Tasks
46
47 with Interfaces.C;
48 --  used for int
49 --           size_t
50
51 with Interfaces.C.Strings;
52 --  used for Null_Ptr
53
54 with System.OS_Interface;
55 --  used for various type, constant, and operations
56
57 with System.Parameters;
58 --  used for Size_Type
59
60 with System.Tasking;
61 --  used for Ada_Task_Control_Block
62 --           Task_ID
63
64 with System.Soft_Links;
65 --  used for Defer/Undefer_Abort
66 --       to initialize TSD for a C thread, in function Self
67
68 --  Note that we do not use System.Tasking.Initialization directly since
69 --  this is a higher level package that we shouldn't depend on. For example
70 --  when using the restricted run time, it is replaced by
71 --  System.Tasking.Restricted.Initialization
72
73 with System.OS_Primitives;
74 --  used for Delay_Modes
75
76 with System.Task_Info;
77 --  used for Unspecified_Task_Info
78
79 with Unchecked_Conversion;
80 with Unchecked_Deallocation;
81
82 package body System.Task_Primitives.Operations is
83
84    use System.Tasking.Debug;
85    use System.Tasking;
86    use Interfaces.C;
87    use Interfaces.C.Strings;
88    use System.OS_Interface;
89    use System.Parameters;
90    use System.OS_Primitives;
91
92    pragma Link_With ("-Xlinker --stack=0x800000,0x1000");
93    --  Change the stack size (8 MB) for tasking programs on Windows. This
94    --  permit to have more than 30 tasks running at the same time. Note that
95    --  we set the stack size for non tasking programs on System unit.
96
97    package SSL renames System.Soft_Links;
98
99    ------------------
100    --  Local Data  --
101    ------------------
102
103    Environment_Task_ID : Task_ID;
104    --  A variable to hold Task_ID for the environment task.
105
106    Single_RTS_Lock : aliased RTS_Lock;
107    --  This is a lock to allow only one thread of control in the RTS at
108    --  a time; it is used to execute in mutual exclusion from all other tasks.
109    --  Used mainly in Single_Lock mode, but also to protect All_Tasks_List
110
111    Time_Slice_Val : Integer;
112    pragma Import (C, Time_Slice_Val, "__gl_time_slice_val");
113
114    Dispatching_Policy : Character;
115    pragma Import (C, Dispatching_Policy, "__gl_task_dispatching_policy");
116
117    FIFO_Within_Priorities : constant Boolean := Dispatching_Policy = 'F';
118    --  Indicates whether FIFO_Within_Priorities is set.
119
120    ---------------------------------
121    --  Foreign Threads Detection  --
122    ---------------------------------
123
124    --  The following are used to allow the Self function to
125    --  automatically generate ATCB's for C threads that happen to call
126    --  Ada procedure, which in turn happen to call the Ada run-time system.
127
128    type Fake_ATCB;
129    type Fake_ATCB_Ptr is access Fake_ATCB;
130    type Fake_ATCB is record
131       Stack_Base : Interfaces.C.unsigned := 0;
132       --  A value of zero indicates the node is not in use.
133       Next       : Fake_ATCB_Ptr;
134       Real_ATCB  : aliased Ada_Task_Control_Block (0);
135    end record;
136
137    Fake_ATCB_List : Fake_ATCB_Ptr;
138    --  A linear linked list.
139    --  The list is protected by Single_RTS_Lock;
140    --  Nodes are added to this list from the front.
141    --  Once a node is added to this list, it is never removed.
142
143    Fake_Task_Elaborated : aliased Boolean := True;
144    --  Used to identified fake tasks (i.e., non-Ada Threads).
145
146    Next_Fake_ATCB : Fake_ATCB_Ptr;
147    --  Used to allocate one Fake_ATCB in advance. See comment in New_Fake_ATCB
148
149    ---------------------------------
150    --  Support for New_Fake_ATCB  --
151    ---------------------------------
152
153    function New_Fake_ATCB return Task_ID;
154    --  Allocate and Initialize a new ATCB. This code can safely be called from
155    --  a foreign thread, as it doesn't access implicitly or explicitly
156    --  "self" before having initialized the new ATCB.
157
158    ------------------------------------
159    -- The thread local storage index --
160    ------------------------------------
161
162    TlsIndex : DWORD;
163    pragma Export (Ada, TlsIndex);
164    --  To ensure that this variable won't be local to this package, since
165    --  in some cases, inlining forces this variable to be global anyway.
166
167    ----------------------------------
168    -- Utility Conversion Functions --
169    ----------------------------------
170
171    function To_Task_Id is new Unchecked_Conversion (System.Address, Task_ID);
172
173    function To_Address is new Unchecked_Conversion (Task_ID, System.Address);
174
175    -------------------
176    -- New_Fake_ATCB --
177    -------------------
178
179    function New_Fake_ATCB return Task_ID is
180       Self_ID   : Task_ID;
181       P, Q      : Fake_ATCB_Ptr;
182       Succeeded : Boolean;
183       Res       : BOOL;
184
185    begin
186       --  This section is ticklish.
187       --  We dare not call anything that might require an ATCB, until
188       --  we have the new ATCB in place.
189
190       Lock_RTS;
191       Q := null;
192       P := Fake_ATCB_List;
193
194       while P /= null loop
195          if P.Stack_Base = 0 then
196             Q := P;
197          end if;
198
199          P := P.Next;
200       end loop;
201
202       if Q = null then
203
204          --  Create a new ATCB with zero entries.
205
206          Self_ID := Next_Fake_ATCB.Real_ATCB'Access;
207          Next_Fake_ATCB.Stack_Base := 1;
208          Next_Fake_ATCB.Next := Fake_ATCB_List;
209          Fake_ATCB_List := Next_Fake_ATCB;
210          Next_Fake_ATCB := null;
211
212       else
213          --  Reuse an existing fake ATCB.
214
215          Self_ID := Q.Real_ATCB'Access;
216          Q.Stack_Base := 1;
217       end if;
218
219       --  Record this as the Task_ID for the current thread.
220
221       Self_ID.Common.LL.Thread := GetCurrentThread;
222
223       Res := TlsSetValue (TlsIndex, To_Address (Self_ID));
224       pragma Assert (Res = True);
225
226       --  Do the standard initializations
227
228       System.Tasking.Initialize_ATCB
229         (Self_ID, null, Null_Address, Null_Task, Fake_Task_Elaborated'Access,
230          System.Priority'First, Task_Info.Unspecified_Task_Info, 0, Self_ID,
231          Succeeded);
232       pragma Assert (Succeeded);
233
234       --  Finally, it is safe to use an allocator in this thread.
235
236       if Next_Fake_ATCB = null then
237          Next_Fake_ATCB := new Fake_ATCB;
238       end if;
239
240       Self_ID.Master_of_Task := 0;
241       Self_ID.Master_Within := Self_ID.Master_of_Task + 1;
242
243       for L in Self_ID.Entry_Calls'Range loop
244          Self_ID.Entry_Calls (L).Self := Self_ID;
245          Self_ID.Entry_Calls (L).Level := L;
246       end loop;
247
248       Self_ID.Common.State := Runnable;
249       Self_ID.Awake_Count := 1;
250
251       --  Since this is not an ordinary Ada task, we will start out undeferred
252
253       Self_ID.Deferral_Level := 0;
254
255       System.Soft_Links.Create_TSD (Self_ID.Common.Compiler_Data);
256
257       --  ????
258       --  The following call is commented out to avoid dependence on
259       --  the System.Tasking.Initialization package.
260       --  It seems that if we want Ada.Task_Attributes to work correctly
261       --  for C threads we will need to raise the visibility of this soft
262       --  link to System.Soft_Links.
263       --  We are putting that off until this new functionality is otherwise
264       --  stable.
265       --  System.Tasking.Initialization.Initialize_Attributes_Link.all (T);
266
267       --  Must not unlock until Next_ATCB is again allocated.
268
269       Unlock_RTS;
270       return Self_ID;
271    end New_Fake_ATCB;
272
273    ----------------------------------
274    -- Condition Variable Functions --
275    ----------------------------------
276
277    procedure Initialize_Cond (Cond : access Condition_Variable);
278    --  Initialize given condition variable Cond
279
280    procedure Finalize_Cond (Cond : access Condition_Variable);
281    --  Finalize given condition variable Cond.
282
283    procedure Cond_Signal (Cond : access Condition_Variable);
284    --  Signal condition variable Cond
285
286    procedure Cond_Wait
287      (Cond : access Condition_Variable;
288       L    : access RTS_Lock);
289    --  Wait on conditional variable Cond, using lock L
290
291    procedure Cond_Timed_Wait
292      (Cond      : access Condition_Variable;
293       L         : access RTS_Lock;
294       Rel_Time  : Duration;
295       Timed_Out : out Boolean;
296       Status    : out Integer);
297    --  Do timed wait on condition variable Cond using lock L. The duration
298    --  of the timed wait is given by Rel_Time. When the condition is
299    --  signalled, Timed_Out shows whether or not a time out occurred.
300    --  Status shows whether Cond_Timed_Wait completed successfully.
301
302    ---------------------
303    -- Initialize_Cond --
304    ---------------------
305
306    procedure Initialize_Cond (Cond : access Condition_Variable) is
307       hEvent : HANDLE;
308
309    begin
310       hEvent := CreateEvent (null, True, False, Null_Ptr);
311       pragma Assert (hEvent /= 0);
312       Cond.all := Condition_Variable (hEvent);
313    end Initialize_Cond;
314
315    -------------------
316    -- Finalize_Cond --
317    -------------------
318
319    --  No such problem here, DosCloseEventSem has been derived.
320    --  What does such refer to in above comment???
321
322    procedure Finalize_Cond (Cond : access Condition_Variable) is
323       Result : BOOL;
324
325    begin
326       Result := CloseHandle (HANDLE (Cond.all));
327       pragma Assert (Result = True);
328    end Finalize_Cond;
329
330    -----------------
331    -- Cond_Signal --
332    -----------------
333
334    procedure Cond_Signal (Cond : access Condition_Variable) is
335       Result : BOOL;
336
337    begin
338       Result := SetEvent (HANDLE (Cond.all));
339       pragma Assert (Result = True);
340    end Cond_Signal;
341
342    ---------------
343    -- Cond_Wait --
344    ---------------
345
346    --  Pre-assertion: Cond is posted
347    --                 L is locked.
348
349    --  Post-assertion: Cond is posted
350    --                  L is locked.
351
352    procedure Cond_Wait
353      (Cond : access Condition_Variable;
354       L    : access RTS_Lock)
355    is
356       Result      : DWORD;
357       Result_Bool : BOOL;
358
359    begin
360       --  Must reset Cond BEFORE L is unlocked.
361
362       Result_Bool := ResetEvent (HANDLE (Cond.all));
363       pragma Assert (Result_Bool = True);
364       Unlock (L);
365
366       --  No problem if we are interrupted here: if the condition is signaled,
367       --  WaitForSingleObject will simply not block
368
369       Result := WaitForSingleObject (HANDLE (Cond.all), Wait_Infinite);
370       pragma Assert (Result = 0);
371
372       Write_Lock (L);
373    end Cond_Wait;
374
375    ---------------------
376    -- Cond_Timed_Wait --
377    ---------------------
378
379    --  Pre-assertion: Cond is posted
380    --                 L is locked.
381
382    --  Post-assertion: Cond is posted
383    --                  L is locked.
384
385    procedure Cond_Timed_Wait
386      (Cond      : access Condition_Variable;
387       L         : access RTS_Lock;
388       Rel_Time  : Duration;
389       Timed_Out : out Boolean;
390       Status    : out Integer)
391    is
392       Time_Out : DWORD;
393       Result   : BOOL;
394
395       Int_Rel_Time : DWORD;
396       Wait_Result  : DWORD;
397
398    begin
399       --  Must reset Cond BEFORE L is unlocked.
400
401       Result := ResetEvent (HANDLE (Cond.all));
402       pragma Assert (Result = True);
403       Unlock (L);
404
405       --  No problem if we are interrupted here: if the condition is signaled,
406       --  WaitForSingleObject will simply not block
407
408       if Rel_Time <= 0.0 then
409          Timed_Out := True;
410       else
411          Int_Rel_Time := DWORD (Rel_Time);
412          Time_Out := Int_Rel_Time * 1000 +
413                      DWORD ((Rel_Time - Duration (Int_Rel_Time)) * 1000.0);
414          Wait_Result := WaitForSingleObject (HANDLE (Cond.all), Time_Out);
415
416          if Wait_Result = WAIT_TIMEOUT then
417             Timed_Out := True;
418             Wait_Result := 0;
419          else
420             Timed_Out := False;
421          end if;
422       end if;
423
424       Write_Lock (L);
425
426       --  Ensure post-condition
427
428       if Timed_Out then
429          Result := SetEvent (HANDLE (Cond.all));
430          pragma Assert (Result = True);
431       end if;
432
433       Status := Integer (Wait_Result);
434    end Cond_Timed_Wait;
435
436    ------------------
437    -- Stack_Guard  --
438    ------------------
439
440    --  The underlying thread system sets a guard page at the
441    --  bottom of a thread stack, so nothing is needed.
442    --  ??? Check the comment above
443
444    procedure Stack_Guard (T : ST.Task_ID; On : Boolean) is
445    begin
446       null;
447    end Stack_Guard;
448
449    --------------------
450    -- Get_Thread_Id  --
451    --------------------
452
453    function Get_Thread_Id (T : ST.Task_ID) return OSI.Thread_Id is
454    begin
455       return T.Common.LL.Thread;
456    end Get_Thread_Id;
457
458    ----------
459    -- Self --
460    ----------
461
462    function Self return Task_ID is
463       Self_Id : Task_ID;
464
465    begin
466       Self_Id := To_Task_Id (TlsGetValue (TlsIndex));
467
468       if Self_Id = null then
469          return New_Fake_ATCB;
470       end if;
471
472       return Self_Id;
473    end Self;
474
475    ---------------------
476    -- Initialize_Lock --
477    ---------------------
478
479    --  Note: mutexes and cond_variables needed per-task basis are
480    --  initialized in Initialize_TCB and the Storage_Error is handled.
481    --  Other mutexes (such as RTS_Lock, Memory_Lock...) used in
482    --  the RTS is initialized before any status change of RTS.
483    --  Therefore raising Storage_Error in the following routines
484    --  should be able to be handled safely.
485
486    procedure Initialize_Lock
487      (Prio : System.Any_Priority;
488       L    : access Lock) is
489    begin
490       InitializeCriticalSection (L.Mutex'Access);
491       L.Owner_Priority := 0;
492       L.Priority := Prio;
493    end Initialize_Lock;
494
495    procedure Initialize_Lock (L : access RTS_Lock; Level : Lock_Level) is
496    begin
497       InitializeCriticalSection (CRITICAL_SECTION (L.all)'Unrestricted_Access);
498    end Initialize_Lock;
499
500    -------------------
501    -- Finalize_Lock --
502    -------------------
503
504    procedure Finalize_Lock (L : access Lock) is
505    begin
506       DeleteCriticalSection (L.Mutex'Access);
507    end Finalize_Lock;
508
509    procedure Finalize_Lock (L : access RTS_Lock) is
510    begin
511       DeleteCriticalSection (CRITICAL_SECTION (L.all)'Unrestricted_Access);
512    end Finalize_Lock;
513
514    ----------------
515    -- Write_Lock --
516    ----------------
517
518    procedure Write_Lock (L : access Lock; Ceiling_Violation : out Boolean) is
519    begin
520       L.Owner_Priority := Get_Priority (Self);
521
522       if L.Priority < L.Owner_Priority then
523          Ceiling_Violation := True;
524          return;
525       end if;
526
527       EnterCriticalSection (L.Mutex'Access);
528
529       Ceiling_Violation := False;
530    end Write_Lock;
531
532    procedure Write_Lock
533      (L : access RTS_Lock; Global_Lock : Boolean := False) is
534    begin
535       if not Single_Lock or else Global_Lock then
536          EnterCriticalSection (CRITICAL_SECTION (L.all)'Unrestricted_Access);
537       end if;
538    end Write_Lock;
539
540    procedure Write_Lock (T : Task_ID) is
541    begin
542       if not Single_Lock then
543          EnterCriticalSection
544            (CRITICAL_SECTION (T.Common.LL.L)'Unrestricted_Access);
545       end if;
546    end Write_Lock;
547
548    ---------------
549    -- Read_Lock --
550    ---------------
551
552    procedure Read_Lock (L : access Lock; Ceiling_Violation : out Boolean) is
553    begin
554       Write_Lock (L, Ceiling_Violation);
555    end Read_Lock;
556
557    ------------
558    -- Unlock --
559    ------------
560
561    procedure Unlock (L : access Lock) is
562    begin
563       LeaveCriticalSection (L.Mutex'Access);
564    end Unlock;
565
566    procedure Unlock (L : access RTS_Lock; Global_Lock : Boolean := False) is
567    begin
568       if not Single_Lock or else Global_Lock then
569          LeaveCriticalSection (CRITICAL_SECTION (L.all)'Unrestricted_Access);
570       end if;
571    end Unlock;
572
573    procedure Unlock (T : Task_ID) is
574    begin
575       if not Single_Lock then
576          LeaveCriticalSection
577            (CRITICAL_SECTION (T.Common.LL.L)'Unrestricted_Access);
578       end if;
579    end Unlock;
580
581    -----------
582    -- Sleep --
583    -----------
584
585    procedure Sleep
586      (Self_ID : Task_ID;
587       Reason  : System.Tasking.Task_States) is
588    begin
589       pragma Assert (Self_ID = Self);
590
591       if Single_Lock then
592          Cond_Wait (Self_ID.Common.LL.CV'Access, Single_RTS_Lock'Access);
593       else
594          Cond_Wait (Self_ID.Common.LL.CV'Access, Self_ID.Common.LL.L'Access);
595       end if;
596
597       if Self_ID.Deferral_Level = 0
598         and then Self_ID.Pending_ATC_Level < Self_ID.ATC_Nesting_Level
599       then
600          Unlock (Self_ID);
601          raise Standard'Abort_Signal;
602       end if;
603    end Sleep;
604
605    -----------------
606    -- Timed_Sleep --
607    -----------------
608
609    --  This is for use within the run-time system, so abort is
610    --  assumed to be already deferred, and the caller should be
611    --  holding its own ATCB lock.
612
613    procedure Timed_Sleep
614      (Self_ID  : Task_ID;
615       Time     : Duration;
616       Mode     : ST.Delay_Modes;
617       Reason   : System.Tasking.Task_States;
618       Timedout : out Boolean;
619       Yielded  : out Boolean)
620    is
621       Check_Time : constant Duration := Monotonic_Clock;
622       Rel_Time   : Duration;
623       Abs_Time   : Duration;
624       Result     : Integer;
625
626       Local_Timedout : Boolean;
627
628    begin
629       Timedout := True;
630       Yielded  := False;
631
632       if Mode = Relative then
633          Rel_Time := Time;
634          Abs_Time := Duration'Min (Time, Max_Sensible_Delay) + Check_Time;
635       else
636          Rel_Time := Time - Check_Time;
637          Abs_Time := Time;
638       end if;
639
640       if Rel_Time > 0.0 then
641          loop
642             exit when Self_ID.Pending_ATC_Level < Self_ID.ATC_Nesting_Level
643               or else Self_ID.Pending_Priority_Change;
644
645             if Single_Lock then
646                Cond_Timed_Wait (Self_ID.Common.LL.CV'Access,
647                  Single_RTS_Lock'Access, Rel_Time, Local_Timedout, Result);
648             else
649                Cond_Timed_Wait (Self_ID.Common.LL.CV'Access,
650                  Self_ID.Common.LL.L'Access, Rel_Time, Local_Timedout, Result);
651             end if;
652
653             exit when Abs_Time <= Monotonic_Clock;
654
655             if not Local_Timedout then
656                --  somebody may have called Wakeup for us
657                Timedout := False;
658                exit;
659             end if;
660
661             Rel_Time := Abs_Time - Monotonic_Clock;
662          end loop;
663       end if;
664    end Timed_Sleep;
665
666    -----------------
667    -- Timed_Delay --
668    -----------------
669
670    procedure Timed_Delay
671      (Self_ID  : Task_ID;
672       Time     : Duration;
673       Mode     : ST.Delay_Modes)
674    is
675       Check_Time : constant Duration := Monotonic_Clock;
676       Rel_Time   : Duration;
677       Abs_Time   : Duration;
678       Result     : Integer;
679       Timedout   : Boolean;
680
681    begin
682       --  Only the little window between deferring abort and
683       --  locking Self_ID is the reason we need to
684       --  check for pending abort and priority change below!
685
686       SSL.Abort_Defer.all;
687
688       if Single_Lock then
689          Lock_RTS;
690       end if;
691
692       Write_Lock (Self_ID);
693
694       if Mode = Relative then
695          Rel_Time := Time;
696          Abs_Time := Time + Check_Time;
697       else
698          Rel_Time := Time - Check_Time;
699          Abs_Time := Time;
700       end if;
701
702       if Rel_Time > 0.0 then
703          Self_ID.Common.State := Delay_Sleep;
704
705          loop
706             if Self_ID.Pending_Priority_Change then
707                Self_ID.Pending_Priority_Change := False;
708                Self_ID.Common.Base_Priority := Self_ID.New_Base_Priority;
709                Set_Priority (Self_ID, Self_ID.Common.Base_Priority);
710             end if;
711
712             exit when Self_ID.Pending_ATC_Level < Self_ID.ATC_Nesting_Level;
713
714             if Single_Lock then
715                Cond_Timed_Wait (Self_ID.Common.LL.CV'Access,
716                  Single_RTS_Lock'Access, Rel_Time, Timedout, Result);
717             else
718                Cond_Timed_Wait (Self_ID.Common.LL.CV'Access,
719                  Self_ID.Common.LL.L'Access, Rel_Time, Timedout, Result);
720             end if;
721
722             exit when Abs_Time <= Monotonic_Clock;
723
724             Rel_Time := Abs_Time - Monotonic_Clock;
725          end loop;
726
727          Self_ID.Common.State := Runnable;
728       end if;
729
730       Unlock (Self_ID);
731
732       if Single_Lock then
733          Unlock_RTS;
734       end if;
735
736       Yield;
737       SSL.Abort_Undefer.all;
738    end Timed_Delay;
739
740    ------------
741    -- Wakeup --
742    ------------
743
744    procedure Wakeup (T : Task_ID; Reason : System.Tasking.Task_States) is
745    begin
746       Cond_Signal (T.Common.LL.CV'Access);
747    end Wakeup;
748
749    -----------
750    -- Yield --
751    -----------
752
753    procedure Yield (Do_Yield : Boolean := True) is
754    begin
755       if Do_Yield then
756          Sleep (0);
757       end if;
758    end Yield;
759
760    ------------------
761    -- Set_Priority --
762    ------------------
763
764    type Prio_Array_Type is array (System.Any_Priority) of Integer;
765    pragma Atomic_Components (Prio_Array_Type);
766
767    Prio_Array : Prio_Array_Type;
768    --  Global array containing the id of the currently running task for
769    --  each priority.
770    --
771    --  Note: we assume that we are on a single processor with run-til-blocked
772    --  scheduling.
773
774    procedure Set_Priority
775      (T : Task_ID;
776       Prio : System.Any_Priority;
777       Loss_Of_Inheritance : Boolean := False)
778    is
779       Res        : BOOL;
780       Array_Item : Integer;
781
782    begin
783       Res := SetThreadPriority
784         (T.Common.LL.Thread, Interfaces.C.int (Underlying_Priorities (Prio)));
785       pragma Assert (Res = True);
786
787       --  ??? Work around a bug in NT 4.0 SP3 scheduler
788       --  It looks like when a task with Thread_Priority_Idle (using RT class)
789       --  never reaches its time slice (e.g by doing multiple and simple RV,
790       --  see CXD8002), the scheduler never gives higher priority task a
791       --  chance to run.
792       --  Note that this works fine on NT 4.0 SP1
793
794       if Time_Slice_Val = 0
795         and then Underlying_Priorities (Prio) = Thread_Priority_Idle
796         and then Loss_Of_Inheritance
797       then
798          Sleep (20);
799       end if;
800
801       if FIFO_Within_Priorities then
802
803          --  Annex D requirement [RM D.2.2 par. 9]:
804          --    If the task drops its priority due to the loss of inherited
805          --    priority, it is added at the head of the ready queue for its
806          --    new active priority.
807
808          if Loss_Of_Inheritance
809            and then Prio < T.Common.Current_Priority
810          then
811             Array_Item := Prio_Array (T.Common.Base_Priority) + 1;
812             Prio_Array (T.Common.Base_Priority) := Array_Item;
813
814             loop
815                --  Let some processes a chance to arrive
816
817                Yield;
818
819                --  Then wait for our turn to proceed
820
821                exit when Array_Item = Prio_Array (T.Common.Base_Priority)
822                  or else Prio_Array (T.Common.Base_Priority) = 1;
823             end loop;
824
825             Prio_Array (T.Common.Base_Priority) :=
826               Prio_Array (T.Common.Base_Priority) - 1;
827          end if;
828       end if;
829
830       T.Common.Current_Priority := Prio;
831    end Set_Priority;
832
833    ------------------
834    -- Get_Priority --
835    ------------------
836
837    function Get_Priority (T : Task_ID) return System.Any_Priority is
838    begin
839       return T.Common.Current_Priority;
840    end Get_Priority;
841
842    ----------------
843    -- Enter_Task --
844    ----------------
845
846    --  There were two paths were we needed to call Enter_Task :
847    --  1) from System.Task_Primitives.Operations.Initialize
848    --  2) from System.Tasking.Stages.Task_Wrapper
849    --
850    --  The thread initialisation has to be done only for the first case.
851    --
852    --  This is because the GetCurrentThread NT call does not return the
853    --  real thread handler but only a "pseudo" one. It is not possible to
854    --  release the thread handle and free the system ressources from this
855    --  "pseudo" handle. So we really want to keep the real thread handle
856    --  set in System.Task_Primitives.Operations.Create_Task during the
857    --  thread creation.
858
859    procedure Enter_Task (Self_ID : Task_ID) is
860       procedure Init_Float;
861       pragma Import (C, Init_Float, "__gnat_init_float");
862       --  Properly initializes the FPU for x86 systems.
863
864       Succeeded : BOOL;
865
866    begin
867       Succeeded := TlsSetValue (TlsIndex, To_Address (Self_ID));
868       pragma Assert (Succeeded = True);
869       Init_Float;
870
871       Self_ID.Common.LL.Thread_Id := GetCurrentThreadId;
872
873       Lock_RTS;
874
875       for J in Known_Tasks'Range loop
876          if Known_Tasks (J) = null then
877             Known_Tasks (J) := Self_ID;
878             Self_ID.Known_Tasks_Index := J;
879             exit;
880          end if;
881       end loop;
882
883       Unlock_RTS;
884    end Enter_Task;
885
886    --------------
887    -- New_ATCB --
888    --------------
889
890    function New_ATCB (Entry_Num : Task_Entry_Index) return Task_ID is
891    begin
892       return new Ada_Task_Control_Block (Entry_Num);
893    end New_ATCB;
894
895    --------------------
896    -- Initialize_TCB --
897    --------------------
898
899    procedure Initialize_TCB (Self_ID : Task_ID; Succeeded : out Boolean) is
900    begin
901       Initialize_Cond (Self_ID.Common.LL.CV'Access);
902
903       if not Single_Lock then
904          Initialize_Lock (Self_ID.Common.LL.L'Access, ATCB_Level);
905       end if;
906
907       Succeeded := True;
908    end Initialize_TCB;
909
910    -----------------
911    -- Create_Task --
912    -----------------
913
914    procedure Create_Task
915      (T          : Task_ID;
916       Wrapper    : System.Address;
917       Stack_Size : System.Parameters.Size_Type;
918       Priority   : System.Any_Priority;
919       Succeeded  : out Boolean)
920    is
921       hTask          : HANDLE;
922       TaskId         : aliased DWORD;
923       pTaskParameter : System.OS_Interface.PVOID;
924       dwStackSize    : DWORD;
925       Result         : DWORD;
926       Entry_Point    : PTHREAD_START_ROUTINE;
927
928       function To_PTHREAD_START_ROUTINE is new
929         Unchecked_Conversion (System.Address, PTHREAD_START_ROUTINE);
930
931    begin
932       pTaskParameter := To_Address (T);
933
934       if Stack_Size = Unspecified_Size then
935          dwStackSize := DWORD (Default_Stack_Size);
936
937       elsif Stack_Size < Minimum_Stack_Size then
938          dwStackSize := DWORD (Minimum_Stack_Size);
939
940       else
941          dwStackSize := DWORD (Stack_Size);
942       end if;
943
944       Entry_Point := To_PTHREAD_START_ROUTINE (Wrapper);
945
946       hTask := CreateThread
947          (null,
948           dwStackSize,
949           Entry_Point,
950           pTaskParameter,
951           DWORD (Create_Suspended),
952           TaskId'Unchecked_Access);
953
954       --  Step 1: Create the thread in blocked mode
955
956       if hTask = 0 then
957          raise Storage_Error;
958       end if;
959
960       --  Step 2: set its TCB
961
962       T.Common.LL.Thread := hTask;
963
964       --  Step 3: set its priority (child has inherited priority from parent)
965
966       Set_Priority (T, Priority);
967
968       --  Step 4: Now, start it for good:
969
970       Result := ResumeThread (hTask);
971       pragma Assert (Result = 1);
972
973       Succeeded := Result = 1;
974    end Create_Task;
975
976    ------------------
977    -- Finalize_TCB --
978    ------------------
979
980    procedure Finalize_TCB (T : Task_ID) is
981       Self_ID   : Task_ID := T;
982       Result    : DWORD;
983       Succeeded : BOOL;
984
985       procedure Free is new
986         Unchecked_Deallocation (Ada_Task_Control_Block, Task_ID);
987
988    begin
989       if not Single_Lock then
990          Finalize_Lock (T.Common.LL.L'Access);
991       end if;
992
993       Finalize_Cond (T.Common.LL.CV'Access);
994
995       if T.Known_Tasks_Index /= -1 then
996          Known_Tasks (T.Known_Tasks_Index) := null;
997       end if;
998
999       --  Wait for the thread to terminate then close it. this is needed
1000       --  to release system ressources.
1001
1002       Result := WaitForSingleObject (T.Common.LL.Thread, Wait_Infinite);
1003       pragma Assert (Result /= WAIT_FAILED);
1004       Succeeded := CloseHandle (T.Common.LL.Thread);
1005       pragma Assert (Succeeded = True);
1006
1007       Free (Self_ID);
1008    end Finalize_TCB;
1009
1010    ---------------
1011    -- Exit_Task --
1012    ---------------
1013
1014    procedure Exit_Task is
1015    begin
1016       ExitThread (0);
1017    end Exit_Task;
1018
1019    ----------------
1020    -- Abort_Task --
1021    ----------------
1022
1023    procedure Abort_Task (T : Task_ID) is
1024    begin
1025       null;
1026    end Abort_Task;
1027
1028    ----------------------
1029    -- Environment_Task --
1030    ----------------------
1031
1032    function Environment_Task return Task_ID is
1033    begin
1034       return Environment_Task_ID;
1035    end Environment_Task;
1036
1037    --------------
1038    -- Lock_RTS --
1039    --------------
1040
1041    procedure Lock_RTS is
1042    begin
1043       Write_Lock (Single_RTS_Lock'Access, Global_Lock => True);
1044    end Lock_RTS;
1045
1046    ----------------
1047    -- Unlock_RTS --
1048    ----------------
1049
1050    procedure Unlock_RTS is
1051    begin
1052       Unlock (Single_RTS_Lock'Access, Global_Lock => True);
1053    end Unlock_RTS;
1054
1055    ----------------
1056    -- Initialize --
1057    ----------------
1058
1059    procedure Initialize (Environment_Task : Task_ID) is
1060       Res : BOOL;
1061    begin
1062       Environment_Task_ID := Environment_Task;
1063
1064       if Time_Slice_Val = 0 or else FIFO_Within_Priorities then
1065          Res := OS_Interface.SetPriorityClass
1066            (GetCurrentProcess, Realtime_Priority_Class);
1067       end if;
1068
1069       TlsIndex := TlsAlloc;
1070
1071       --  Initialize the lock used to synchronize chain of all ATCBs.
1072
1073       Initialize_Lock (Single_RTS_Lock'Access, RTS_Lock_Level);
1074
1075       Environment_Task.Common.LL.Thread := GetCurrentThread;
1076       Enter_Task (Environment_Task);
1077
1078       --  Create a free ATCB for use on the Fake_ATCB_List
1079
1080       Next_Fake_ATCB := new Fake_ATCB;
1081    end Initialize;
1082
1083    ---------------------
1084    -- Monotonic_Clock --
1085    ---------------------
1086
1087    function Monotonic_Clock return Duration
1088      renames System.OS_Primitives.Monotonic_Clock;
1089
1090    -------------------
1091    -- RT_Resolution --
1092    -------------------
1093
1094    function RT_Resolution return Duration is
1095    begin
1096       return 0.000_001; --  1 micro-second
1097    end RT_Resolution;
1098
1099    ----------------
1100    -- Check_Exit --
1101    ----------------
1102
1103    --  Dummy versions.  The only currently working versions is for solaris
1104    --  (native).
1105
1106    function Check_Exit (Self_ID : ST.Task_ID) return Boolean is
1107    begin
1108       return True;
1109    end Check_Exit;
1110
1111    --------------------
1112    -- Check_No_Locks --
1113    --------------------
1114
1115    function Check_No_Locks (Self_ID : ST.Task_ID) return Boolean is
1116    begin
1117       return True;
1118    end Check_No_Locks;
1119
1120    ------------------
1121    -- Suspend_Task --
1122    ------------------
1123
1124    function Suspend_Task
1125      (T           : ST.Task_ID;
1126       Thread_Self : Thread_Id) return Boolean is
1127    begin
1128       if T.Common.LL.Thread /= Thread_Self then
1129          return SuspendThread (T.Common.LL.Thread) = NO_ERROR;
1130       else
1131          return True;
1132       end if;
1133    end Suspend_Task;
1134
1135    -----------------
1136    -- Resume_Task --
1137    -----------------
1138
1139    function Resume_Task
1140      (T           : ST.Task_ID;
1141       Thread_Self : Thread_Id) return Boolean is
1142    begin
1143       if T.Common.LL.Thread /= Thread_Self then
1144          return ResumeThread (T.Common.LL.Thread) = NO_ERROR;
1145       else
1146          return True;
1147       end if;
1148    end Resume_Task;
1149
1150 end System.Task_Primitives.Operations;