OSDN Git Service

2011-12-05 Bob Duff <duff@adacore.com>
[pf3gnuchains/gcc-fork.git] / gcc / ada / s-taprop-solaris.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-2011, 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 Solaris (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
43 with System.Multiprocessors;
44 with System.Tasking.Debug;
45 with System.Interrupt_Management;
46 with System.OS_Constants;
47 with System.OS_Primitives;
48 with System.Task_Info;
49
50 pragma Warnings (Off);
51 with System.OS_Lib;
52 pragma Warnings (On);
53
54 with System.Soft_Links;
55 --  We use System.Soft_Links instead of System.Tasking.Initialization
56 --  because the later is a higher level package that we shouldn't depend on.
57 --  For example when using the restricted run time, it is replaced by
58 --  System.Tasking.Restricted.Stages.
59
60 package body System.Task_Primitives.Operations is
61
62    package OSC renames System.OS_Constants;
63    package SSL renames System.Soft_Links;
64
65    use System.Tasking.Debug;
66    use System.Tasking;
67    use Interfaces.C;
68    use System.OS_Interface;
69    use System.Parameters;
70    use System.OS_Primitives;
71
72    ----------------
73    -- Local Data --
74    ----------------
75
76    --  The following are logically constants, but need to be initialized
77    --  at run time.
78
79    Environment_Task_Id : Task_Id;
80    --  A variable to hold Task_Id for the environment task.
81    --  If we use this variable to get the Task_Id, we need the following
82    --  ATCB_Key only for non-Ada threads.
83
84    Unblocked_Signal_Mask : aliased sigset_t;
85    --  The set of signals that should unblocked in all tasks
86
87    ATCB_Key : aliased thread_key_t;
88    --  Key used to find the Ada Task_Id associated with a thread,
89    --  at least for C threads unknown to the Ada run-time system.
90
91    Single_RTS_Lock : aliased RTS_Lock;
92    --  This is a lock to allow only one thread of control in the RTS at
93    --  a time; it is used to execute in mutual exclusion from all other tasks.
94    --  Used mainly in Single_Lock mode, but also to protect All_Tasks_List
95
96    Next_Serial_Number : Task_Serial_Number := 100;
97    --  We start at 100, to reserve some special values for
98    --  using in error checking.
99    --  The following are internal configuration constants needed.
100
101    Abort_Handler_Installed : Boolean := False;
102    --  True if a handler for the abort signal is installed
103
104    Null_Thread_Id : constant Thread_Id := Thread_Id'Last;
105    --  Constant to indicate that the thread identifier has not yet been
106    --  initialized.
107
108    ----------------------
109    -- Priority Support --
110    ----------------------
111
112    Priority_Ceiling_Emulation : constant Boolean := True;
113    --  controls whether we emulate priority ceiling locking
114
115    --  To get a scheduling close to annex D requirements, we use the real-time
116    --  class provided for LWPs and map each task/thread to a specific and
117    --  unique LWP (there is 1 thread per LWP, and 1 LWP per thread).
118
119    --  The real time class can only be set when the process has root
120    --  privileges, so in the other cases, we use the normal thread scheduling
121    --  and priority handling.
122
123    Using_Real_Time_Class : Boolean := False;
124    --  indicates whether the real time class is being used (i.e. the process
125    --  has root privileges).
126
127    Prio_Param : aliased struct_pcparms;
128    --  Hold priority info (Real_Time) initialized during the package
129    --  elaboration.
130
131    -----------------------------------
132    -- External Configuration Values --
133    -----------------------------------
134
135    Time_Slice_Val : Integer;
136    pragma Import (C, Time_Slice_Val, "__gl_time_slice_val");
137
138    Locking_Policy : Character;
139    pragma Import (C, Locking_Policy, "__gl_locking_policy");
140
141    Dispatching_Policy : Character;
142    pragma Import (C, Dispatching_Policy, "__gl_task_dispatching_policy");
143
144    Foreign_Task_Elaborated : aliased Boolean := True;
145    --  Used to identified fake tasks (i.e., non-Ada Threads)
146
147    -----------------------
148    -- Local Subprograms --
149    -----------------------
150
151    function sysconf (name : System.OS_Interface.int) return processorid_t;
152    pragma Import (C, sysconf, "sysconf");
153
154    SC_NPROCESSORS_CONF : constant System.OS_Interface.int := 14;
155
156    function Num_Procs
157      (name : System.OS_Interface.int := SC_NPROCESSORS_CONF)
158       return processorid_t renames sysconf;
159
160    procedure Abort_Handler
161      (Sig     : Signal;
162       Code    : not null access siginfo_t;
163       Context : not null access ucontext_t);
164    --  Target-dependent binding of inter-thread Abort signal to
165    --  the raising of the Abort_Signal exception.
166    --  See also comments in 7staprop.adb
167
168    ------------
169    -- Checks --
170    ------------
171
172    function Check_Initialize_Lock
173      (L     : Lock_Ptr;
174       Level : Lock_Level) return Boolean;
175    pragma Inline (Check_Initialize_Lock);
176
177    function Check_Lock (L : Lock_Ptr) return Boolean;
178    pragma Inline (Check_Lock);
179
180    function Record_Lock (L : Lock_Ptr) return Boolean;
181    pragma Inline (Record_Lock);
182
183    function Check_Sleep (Reason : Task_States) return Boolean;
184    pragma Inline (Check_Sleep);
185
186    function Record_Wakeup
187      (L      : Lock_Ptr;
188       Reason : Task_States) return Boolean;
189    pragma Inline (Record_Wakeup);
190
191    function Check_Wakeup
192      (T      : Task_Id;
193       Reason : Task_States) return Boolean;
194    pragma Inline (Check_Wakeup);
195
196    function Check_Unlock (L : Lock_Ptr) return Boolean;
197    pragma Inline (Check_Unlock);
198
199    function Check_Finalize_Lock (L : Lock_Ptr) return Boolean;
200    pragma Inline (Check_Finalize_Lock);
201
202    --------------------
203    -- Local Packages --
204    --------------------
205
206    package Specific is
207
208       procedure Initialize (Environment_Task : Task_Id);
209       pragma Inline (Initialize);
210       --  Initialize various data needed by this package
211
212       function Is_Valid_Task return Boolean;
213       pragma Inline (Is_Valid_Task);
214       --  Does executing thread have a TCB?
215
216       procedure Set (Self_Id : Task_Id);
217       pragma Inline (Set);
218       --  Set the self id for the current task
219
220       function Self return Task_Id;
221       pragma Inline (Self);
222       --  Return a pointer to the Ada Task Control Block of the calling task
223
224    end Specific;
225
226    package body Specific is separate;
227    --  The body of this package is target specific
228
229    ----------------------------------
230    -- ATCB allocation/deallocation --
231    ----------------------------------
232
233    package body ATCB_Allocation is separate;
234    --  The body of this package is shared across several targets
235
236    ---------------------------------
237    -- Support for foreign threads --
238    ---------------------------------
239
240    function Register_Foreign_Thread (Thread : Thread_Id) return Task_Id;
241    --  Allocate and Initialize a new ATCB for the current Thread
242
243    function Register_Foreign_Thread
244      (Thread : Thread_Id) return Task_Id is separate;
245
246    ------------
247    -- Checks --
248    ------------
249
250    Check_Count  : Integer := 0;
251    Lock_Count   : Integer := 0;
252    Unlock_Count : Integer := 0;
253
254    -------------------
255    -- Abort_Handler --
256    -------------------
257
258    procedure Abort_Handler
259      (Sig     : Signal;
260       Code    : not null access siginfo_t;
261       Context : not null access ucontext_t)
262    is
263       pragma Unreferenced (Sig);
264       pragma Unreferenced (Code);
265       pragma Unreferenced (Context);
266
267       Self_ID : constant Task_Id := Self;
268       Old_Set : aliased sigset_t;
269
270       Result : Interfaces.C.int;
271       pragma Warnings (Off, Result);
272
273    begin
274       --  It's not safe to raise an exception when using GCC ZCX mechanism.
275       --  Note that we still need to install a signal handler, since in some
276       --  cases (e.g. shutdown of the Server_Task in System.Interrupts) we
277       --  need to send the Abort signal to a task.
278
279       if ZCX_By_Default then
280          return;
281       end if;
282
283       if Self_ID.Deferral_Level = 0
284         and then Self_ID.Pending_ATC_Level < Self_ID.ATC_Nesting_Level
285         and then not Self_ID.Aborting
286       then
287          Self_ID.Aborting := True;
288
289          --  Make sure signals used for RTS internal purpose are unmasked
290
291          Result :=
292            thr_sigsetmask
293              (SIG_UNBLOCK,
294               Unblocked_Signal_Mask'Unchecked_Access,
295               Old_Set'Unchecked_Access);
296          pragma Assert (Result = 0);
297
298          raise Standard'Abort_Signal;
299       end if;
300    end Abort_Handler;
301
302    -----------------
303    -- Stack_Guard --
304    -----------------
305
306    --  The underlying thread system sets a guard page at the
307    --  bottom of a thread stack, so nothing is needed.
308
309    procedure Stack_Guard (T : ST.Task_Id; On : Boolean) is
310       pragma Unreferenced (T);
311       pragma Unreferenced (On);
312    begin
313       null;
314    end Stack_Guard;
315
316    -------------------
317    -- Get_Thread_Id --
318    -------------------
319
320    function Get_Thread_Id (T : ST.Task_Id) return OSI.Thread_Id is
321    begin
322       return T.Common.LL.Thread;
323    end Get_Thread_Id;
324
325    ----------------
326    -- Initialize --
327    ----------------
328
329    procedure Initialize (Environment_Task : ST.Task_Id) is
330       act     : aliased struct_sigaction;
331       old_act : aliased struct_sigaction;
332       Tmp_Set : aliased sigset_t;
333       Result  : Interfaces.C.int;
334
335       procedure Configure_Processors;
336       --  Processors configuration
337       --  The user can specify a processor which the program should run
338       --  on to emulate a single-processor system. This can be easily
339       --  done by setting environment variable GNAT_PROCESSOR to one of
340       --  the following :
341       --
342       --    -2 : use the default configuration (run the program on all
343       --         available processors) - this is the same as having
344       --         GNAT_PROCESSOR unset
345       --    -1 : let the RTS choose one processor and run the program on
346       --         that processor
347       --    0 .. Last_Proc : run the program on the specified processor
348       --
349       --  Last_Proc is equal to the value of the system variable
350       --  _SC_NPROCESSORS_CONF, minus one.
351
352       procedure Configure_Processors is
353          Proc_Acc  : constant System.OS_Lib.String_Access :=
354                        System.OS_Lib.Getenv ("GNAT_PROCESSOR");
355          Proc      : aliased processorid_t;  --  User processor #
356          Last_Proc : processorid_t;          --  Last processor #
357
358       begin
359          if Proc_Acc.all'Length /= 0 then
360
361             --  Environment variable is defined
362
363             Last_Proc := Num_Procs - 1;
364
365             if Last_Proc /= -1 then
366                Proc := processorid_t'Value (Proc_Acc.all);
367
368                if Proc <= -2  or else Proc > Last_Proc then
369
370                   --  Use the default configuration
371
372                   null;
373
374                elsif Proc = -1 then
375
376                   --  Choose a processor
377
378                   Result := 0;
379                   while Proc < Last_Proc loop
380                      Proc := Proc + 1;
381                      Result := p_online (Proc, PR_STATUS);
382                      exit when Result = PR_ONLINE;
383                   end loop;
384
385                   pragma Assert (Result = PR_ONLINE);
386                   Result := processor_bind (P_PID, P_MYID, Proc, null);
387                   pragma Assert (Result = 0);
388
389                else
390                   --  Use user processor
391
392                   Result := processor_bind (P_PID, P_MYID, Proc, null);
393                   pragma Assert (Result = 0);
394                end if;
395             end if;
396          end if;
397
398       exception
399          when Constraint_Error =>
400
401             --  Illegal environment variable GNAT_PROCESSOR - ignored
402
403             null;
404       end Configure_Processors;
405
406       function State
407         (Int : System.Interrupt_Management.Interrupt_ID) return Character;
408       pragma Import (C, State, "__gnat_get_interrupt_state");
409       --  Get interrupt state.  Defined in a-init.c
410       --  The input argument is the interrupt number,
411       --  and the result is one of the following:
412
413       Default : constant Character := 's';
414       --    'n'   this interrupt not set by any Interrupt_State pragma
415       --    'u'   Interrupt_State pragma set state to User
416       --    'r'   Interrupt_State pragma set state to Runtime
417       --    's'   Interrupt_State pragma set state to System (use "default"
418       --           system handler)
419
420    --  Start of processing for Initialize
421
422    begin
423       Environment_Task_Id := Environment_Task;
424
425       Interrupt_Management.Initialize;
426
427       --  Prepare the set of signals that should unblocked in all tasks
428
429       Result := sigemptyset (Unblocked_Signal_Mask'Access);
430       pragma Assert (Result = 0);
431
432       for J in Interrupt_Management.Interrupt_ID loop
433          if System.Interrupt_Management.Keep_Unmasked (J) then
434             Result := sigaddset (Unblocked_Signal_Mask'Access, Signal (J));
435             pragma Assert (Result = 0);
436          end if;
437       end loop;
438
439       if Dispatching_Policy = 'F' then
440          declare
441             Result      : Interfaces.C.long;
442             Class_Info  : aliased struct_pcinfo;
443             Secs, Nsecs : Interfaces.C.long;
444
445          begin
446             --  If a pragma Time_Slice is specified, takes the value in account
447
448             if Time_Slice_Val > 0 then
449
450                --  Convert Time_Slice_Val (microseconds) to seconds/nanosecs
451
452                Secs := Interfaces.C.long (Time_Slice_Val / 1_000_000);
453                Nsecs :=
454                  Interfaces.C.long ((Time_Slice_Val rem 1_000_000) * 1_000);
455
456             --  Otherwise, default to no time slicing (i.e run until blocked)
457
458             else
459                Secs := RT_TQINF;
460                Nsecs := RT_TQINF;
461             end if;
462
463             --  Get the real time class id
464
465             Class_Info.pc_clname (1) := 'R';
466             Class_Info.pc_clname (2) := 'T';
467             Class_Info.pc_clname (3) := ASCII.NUL;
468
469             Result := priocntl (PC_VERSION, P_LWPID, P_MYID, PC_GETCID,
470               Class_Info'Address);
471
472             --  Request the real time class
473
474             Prio_Param.pc_cid := Class_Info.pc_cid;
475             Prio_Param.rt_pri := pri_t (Class_Info.rt_maxpri);
476             Prio_Param.rt_tqsecs := Secs;
477             Prio_Param.rt_tqnsecs := Nsecs;
478
479             Result :=
480               priocntl
481                 (PC_VERSION, P_LWPID, P_MYID, PC_SETPARMS, Prio_Param'Address);
482
483             Using_Real_Time_Class := Result /= -1;
484          end;
485       end if;
486
487       Specific.Initialize (Environment_Task);
488
489       --  The following is done in Enter_Task, but this is too late for the
490       --  Environment Task, since we need to call Self in Check_Locks when
491       --  the run time is compiled with assertions on.
492
493       Specific.Set (Environment_Task);
494
495       --  Initialize the lock used to synchronize chain of all ATCBs
496
497       Initialize_Lock (Single_RTS_Lock'Access, RTS_Lock_Level);
498
499       --  Make environment task known here because it doesn't go through
500       --  Activate_Tasks, which does it for all other tasks.
501
502       Known_Tasks (Known_Tasks'First) := Environment_Task;
503       Environment_Task.Known_Tasks_Index := Known_Tasks'First;
504
505       Enter_Task (Environment_Task);
506
507       Configure_Processors;
508
509       if State
510           (System.Interrupt_Management.Abort_Task_Interrupt) /= Default
511       then
512          --  Set sa_flags to SA_NODEFER so that during the handler execution
513          --  we do not change the Signal_Mask to be masked for the Abort_Signal
514          --  This is a temporary fix to the problem that the Signal_Mask is
515          --  not restored after the exception (longjmp) from the handler.
516          --  The right fix should be made in sigsetjmp so that we save
517          --  the Signal_Set and restore it after a longjmp.
518          --  In that case, this field should be changed back to 0. ???
519
520          act.sa_flags := 16;
521
522          act.sa_handler := Abort_Handler'Address;
523          Result := sigemptyset (Tmp_Set'Access);
524          pragma Assert (Result = 0);
525          act.sa_mask := Tmp_Set;
526
527          Result :=
528            sigaction
529              (Signal (System.Interrupt_Management.Abort_Task_Interrupt),
530               act'Unchecked_Access,
531               old_act'Unchecked_Access);
532          pragma Assert (Result = 0);
533          Abort_Handler_Installed := True;
534       end if;
535    end Initialize;
536
537    ---------------------
538    -- Initialize_Lock --
539    ---------------------
540
541    --  Note: mutexes and cond_variables needed per-task basis are initialized
542    --  in Initialize_TCB and the Storage_Error is handled. Other mutexes (such
543    --  as RTS_Lock, Memory_Lock...) used in RTS is initialized before any
544    --  status change of RTS. Therefore raising Storage_Error in the following
545    --  routines should be able to be handled safely.
546
547    procedure Initialize_Lock
548      (Prio : System.Any_Priority;
549       L    : not null access Lock)
550    is
551       Result : Interfaces.C.int;
552
553    begin
554       pragma Assert (Check_Initialize_Lock (Lock_Ptr (L), PO_Level));
555
556       if Priority_Ceiling_Emulation then
557          L.Ceiling := Prio;
558       end if;
559
560       Result := mutex_init (L.L'Access, USYNC_THREAD, System.Null_Address);
561       pragma Assert (Result = 0 or else Result = ENOMEM);
562
563       if Result = ENOMEM then
564          raise Storage_Error with "Failed to allocate a lock";
565       end if;
566    end Initialize_Lock;
567
568    procedure Initialize_Lock
569      (L     : not null access RTS_Lock;
570       Level : Lock_Level)
571    is
572       Result : Interfaces.C.int;
573
574    begin
575       pragma Assert
576         (Check_Initialize_Lock (To_Lock_Ptr (RTS_Lock_Ptr (L)), Level));
577       Result := mutex_init (L.L'Access, USYNC_THREAD, System.Null_Address);
578       pragma Assert (Result = 0 or else Result = ENOMEM);
579
580       if Result = ENOMEM then
581          raise Storage_Error with "Failed to allocate a lock";
582       end if;
583    end Initialize_Lock;
584
585    -------------------
586    -- Finalize_Lock --
587    -------------------
588
589    procedure Finalize_Lock (L : not null access Lock) is
590       Result : Interfaces.C.int;
591    begin
592       pragma Assert (Check_Finalize_Lock (Lock_Ptr (L)));
593       Result := mutex_destroy (L.L'Access);
594       pragma Assert (Result = 0);
595    end Finalize_Lock;
596
597    procedure Finalize_Lock (L : not null access RTS_Lock) is
598       Result : Interfaces.C.int;
599    begin
600       pragma Assert (Check_Finalize_Lock (To_Lock_Ptr (RTS_Lock_Ptr (L))));
601       Result := mutex_destroy (L.L'Access);
602       pragma Assert (Result = 0);
603    end Finalize_Lock;
604
605    ----------------
606    -- Write_Lock --
607    ----------------
608
609    procedure Write_Lock
610      (L                 : not null access Lock;
611       Ceiling_Violation : out Boolean)
612    is
613       Result : Interfaces.C.int;
614
615    begin
616       pragma Assert (Check_Lock (Lock_Ptr (L)));
617
618       if Priority_Ceiling_Emulation and then Locking_Policy = 'C' then
619          declare
620             Self_Id        : constant Task_Id := Self;
621             Saved_Priority : System.Any_Priority;
622
623          begin
624             if Self_Id.Common.LL.Active_Priority > L.Ceiling then
625                Ceiling_Violation := True;
626                return;
627             end if;
628
629             Saved_Priority := Self_Id.Common.LL.Active_Priority;
630
631             if Self_Id.Common.LL.Active_Priority < L.Ceiling then
632                Set_Priority (Self_Id, L.Ceiling);
633             end if;
634
635             Result := mutex_lock (L.L'Access);
636             pragma Assert (Result = 0);
637             Ceiling_Violation := False;
638
639             L.Saved_Priority := Saved_Priority;
640          end;
641
642       else
643          Result := mutex_lock (L.L'Access);
644          pragma Assert (Result = 0);
645          Ceiling_Violation := False;
646       end if;
647
648       pragma Assert (Record_Lock (Lock_Ptr (L)));
649    end Write_Lock;
650
651    procedure Write_Lock
652      (L          : not null access RTS_Lock;
653      Global_Lock : Boolean := False)
654    is
655       Result : Interfaces.C.int;
656    begin
657       if not Single_Lock or else Global_Lock then
658          pragma Assert (Check_Lock (To_Lock_Ptr (RTS_Lock_Ptr (L))));
659          Result := mutex_lock (L.L'Access);
660          pragma Assert (Result = 0);
661          pragma Assert (Record_Lock (To_Lock_Ptr (RTS_Lock_Ptr (L))));
662       end if;
663    end Write_Lock;
664
665    procedure Write_Lock (T : Task_Id) is
666       Result : Interfaces.C.int;
667    begin
668       if not Single_Lock then
669          pragma Assert (Check_Lock (To_Lock_Ptr (T.Common.LL.L'Access)));
670          Result := mutex_lock (T.Common.LL.L.L'Access);
671          pragma Assert (Result = 0);
672          pragma Assert (Record_Lock (To_Lock_Ptr (T.Common.LL.L'Access)));
673       end if;
674    end Write_Lock;
675
676    ---------------
677    -- Read_Lock --
678    ---------------
679
680    procedure Read_Lock
681      (L                 : not null access Lock;
682       Ceiling_Violation : out Boolean) is
683    begin
684       Write_Lock (L, Ceiling_Violation);
685    end Read_Lock;
686
687    ------------
688    -- Unlock --
689    ------------
690
691    procedure Unlock (L : not null access Lock) is
692       Result : Interfaces.C.int;
693
694    begin
695       pragma Assert (Check_Unlock (Lock_Ptr (L)));
696
697       if Priority_Ceiling_Emulation and then Locking_Policy = 'C' then
698          declare
699             Self_Id : constant Task_Id := Self;
700
701          begin
702             Result := mutex_unlock (L.L'Access);
703             pragma Assert (Result = 0);
704
705             if Self_Id.Common.LL.Active_Priority > L.Saved_Priority then
706                Set_Priority (Self_Id, L.Saved_Priority);
707             end if;
708          end;
709       else
710          Result := mutex_unlock (L.L'Access);
711          pragma Assert (Result = 0);
712       end if;
713    end Unlock;
714
715    procedure Unlock
716      (L           : not null access RTS_Lock;
717       Global_Lock : Boolean := False)
718    is
719       Result : Interfaces.C.int;
720    begin
721       if not Single_Lock or else Global_Lock then
722          pragma Assert (Check_Unlock (To_Lock_Ptr (RTS_Lock_Ptr (L))));
723          Result := mutex_unlock (L.L'Access);
724          pragma Assert (Result = 0);
725       end if;
726    end Unlock;
727
728    procedure Unlock (T : Task_Id) is
729       Result : Interfaces.C.int;
730    begin
731       if not Single_Lock then
732          pragma Assert (Check_Unlock (To_Lock_Ptr (T.Common.LL.L'Access)));
733          Result := mutex_unlock (T.Common.LL.L.L'Access);
734          pragma Assert (Result = 0);
735       end if;
736    end Unlock;
737
738    -----------------
739    -- Set_Ceiling --
740    -----------------
741
742    --  Dynamic priority ceilings are not supported by the underlying system
743
744    procedure Set_Ceiling
745      (L    : not null access Lock;
746       Prio : System.Any_Priority)
747    is
748       pragma Unreferenced (L, Prio);
749    begin
750       null;
751    end Set_Ceiling;
752
753    --  For the time delay implementation, we need to make sure we
754    --  achieve following criteria:
755
756    --  1) We have to delay at least for the amount requested.
757    --  2) We have to give up CPU even though the actual delay does not
758    --     result in blocking.
759    --  3) Except for restricted run-time systems that do not support
760    --     ATC or task abort, the delay must be interrupted by the
761    --     abort_task operation.
762    --  4) The implementation has to be efficient so that the delay overhead
763    --     is relatively cheap.
764    --  (1)-(3) are Ada requirements. Even though (2) is an Annex-D
765    --     requirement we still want to provide the effect in all cases.
766    --     The reason is that users may want to use short delays to implement
767    --     their own scheduling effect in the absence of language provided
768    --     scheduling policies.
769
770    ---------------------
771    -- Monotonic_Clock --
772    ---------------------
773
774    function Monotonic_Clock return Duration is
775       TS     : aliased timespec;
776       Result : Interfaces.C.int;
777    begin
778       Result := clock_gettime (OSC.CLOCK_RT_Ada, TS'Unchecked_Access);
779       pragma Assert (Result = 0);
780       return To_Duration (TS);
781    end Monotonic_Clock;
782
783    -------------------
784    -- RT_Resolution --
785    -------------------
786
787    function RT_Resolution return Duration is
788    begin
789       return 10#1.0#E-6;
790    end RT_Resolution;
791
792    -----------
793    -- Yield --
794    -----------
795
796    procedure Yield (Do_Yield : Boolean := True) is
797    begin
798       if Do_Yield then
799          System.OS_Interface.thr_yield;
800       end if;
801    end Yield;
802
803    -----------
804    -- Self ---
805    -----------
806
807    function Self return Task_Id renames Specific.Self;
808
809    ------------------
810    -- Set_Priority --
811    ------------------
812
813    procedure Set_Priority
814      (T                   : Task_Id;
815       Prio                : System.Any_Priority;
816       Loss_Of_Inheritance : Boolean := False)
817    is
818       pragma Unreferenced (Loss_Of_Inheritance);
819
820       Result : Interfaces.C.int;
821       pragma Unreferenced (Result);
822
823       Param : aliased struct_pcparms;
824
825       use Task_Info;
826
827    begin
828       T.Common.Current_Priority := Prio;
829
830       if Priority_Ceiling_Emulation then
831          T.Common.LL.Active_Priority := Prio;
832       end if;
833
834       if Using_Real_Time_Class then
835          Param.pc_cid := Prio_Param.pc_cid;
836          Param.rt_pri := pri_t (Prio);
837          Param.rt_tqsecs := Prio_Param.rt_tqsecs;
838          Param.rt_tqnsecs := Prio_Param.rt_tqnsecs;
839
840          Result := Interfaces.C.int (
841            priocntl (PC_VERSION, P_LWPID, T.Common.LL.LWP, PC_SETPARMS,
842              Param'Address));
843
844       else
845          if T.Common.Task_Info /= null
846            and then not T.Common.Task_Info.Bound_To_LWP
847          then
848             --  The task is not bound to a LWP, so use thr_setprio
849
850             Result :=
851               thr_setprio (T.Common.LL.Thread, Interfaces.C.int (Prio));
852
853          else
854             --  The task is bound to a LWP, use priocntl
855             --  ??? TBD
856
857             null;
858          end if;
859       end if;
860    end Set_Priority;
861
862    ------------------
863    -- Get_Priority --
864    ------------------
865
866    function Get_Priority (T : Task_Id) return System.Any_Priority is
867    begin
868       return T.Common.Current_Priority;
869    end Get_Priority;
870
871    ----------------
872    -- Enter_Task --
873    ----------------
874
875    procedure Enter_Task (Self_ID : Task_Id) is
876    begin
877       Self_ID.Common.LL.Thread := thr_self;
878       Self_ID.Common.LL.LWP    := lwp_self;
879
880       Set_Task_Affinity (Self_ID);
881       Specific.Set (Self_ID);
882
883       --  We need the above code even if we do direct fetch of Task_Id in Self
884       --  for the main task on Sun, x86 Solaris and for gcc 2.7.2.
885    end Enter_Task;
886
887    -------------------
888    -- Is_Valid_Task --
889    -------------------
890
891    function Is_Valid_Task return Boolean renames Specific.Is_Valid_Task;
892
893    -----------------------------
894    -- Register_Foreign_Thread --
895    -----------------------------
896
897    function Register_Foreign_Thread return Task_Id is
898    begin
899       if Is_Valid_Task then
900          return Self;
901       else
902          return Register_Foreign_Thread (thr_self);
903       end if;
904    end Register_Foreign_Thread;
905
906    --------------------
907    -- Initialize_TCB --
908    --------------------
909
910    procedure Initialize_TCB (Self_ID : Task_Id; Succeeded : out Boolean) is
911       Result : Interfaces.C.int := 0;
912
913    begin
914       --  Give the task a unique serial number
915
916       Self_ID.Serial_Number := Next_Serial_Number;
917       Next_Serial_Number := Next_Serial_Number + 1;
918       pragma Assert (Next_Serial_Number /= 0);
919
920       Self_ID.Common.LL.Thread := Null_Thread_Id;
921
922       if not Single_Lock then
923          Result :=
924            mutex_init
925              (Self_ID.Common.LL.L.L'Access, USYNC_THREAD, System.Null_Address);
926          Self_ID.Common.LL.L.Level :=
927            Private_Task_Serial_Number (Self_ID.Serial_Number);
928          pragma Assert (Result = 0 or else Result = ENOMEM);
929       end if;
930
931       if Result = 0 then
932          Result := cond_init (Self_ID.Common.LL.CV'Access, USYNC_THREAD, 0);
933          pragma Assert (Result = 0 or else Result = ENOMEM);
934       end if;
935
936       if Result = 0 then
937          Succeeded := True;
938       else
939          if not Single_Lock then
940             Result := mutex_destroy (Self_ID.Common.LL.L.L'Access);
941             pragma Assert (Result = 0);
942          end if;
943
944          Succeeded := False;
945       end if;
946    end Initialize_TCB;
947
948    -----------------
949    -- Create_Task --
950    -----------------
951
952    procedure Create_Task
953      (T          : Task_Id;
954       Wrapper    : System.Address;
955       Stack_Size : System.Parameters.Size_Type;
956       Priority   : System.Any_Priority;
957       Succeeded  : out Boolean)
958    is
959       pragma Unreferenced (Priority);
960
961       Result              : Interfaces.C.int;
962       Adjusted_Stack_Size : Interfaces.C.size_t;
963       Opts                : Interfaces.C.int := THR_DETACHED;
964
965       Page_Size           : constant System.Parameters.Size_Type := 4096;
966       --  This constant is for reserving extra space at the
967       --  end of the stack, which can be used by the stack
968       --  checking as guard page. The idea is that we need
969       --  to have at least Stack_Size bytes available for
970       --  actual use.
971
972       use System.Task_Info;
973       use type System.Multiprocessors.CPU_Range;
974
975    begin
976       --  Check whether both Dispatching_Domain and CPU are specified for the
977       --  task, and the CPU value is not contained within the range of
978       --  processors for the domain.
979
980       if T.Common.Domain /= null
981         and then T.Common.Base_CPU /= System.Multiprocessors.Not_A_Specific_CPU
982         and then
983           (T.Common.Base_CPU not in T.Common.Domain'Range
984             or else not T.Common.Domain (T.Common.Base_CPU))
985       then
986          Succeeded := False;
987          return;
988       end if;
989
990       Adjusted_Stack_Size := Interfaces.C.size_t (Stack_Size + Page_Size);
991
992       --  Since the initial signal mask of a thread is inherited from the
993       --  creator, and the Environment task has all its signals masked, we
994       --  do not need to manipulate caller's signal mask at this point.
995       --  All tasks in RTS will have All_Tasks_Mask initially.
996
997       if T.Common.Task_Info /= null then
998          if T.Common.Task_Info.New_LWP then
999             Opts := Opts + THR_NEW_LWP;
1000          end if;
1001
1002          if T.Common.Task_Info.Bound_To_LWP then
1003             Opts := Opts + THR_BOUND;
1004          end if;
1005
1006       else
1007          Opts := THR_DETACHED + THR_BOUND;
1008       end if;
1009
1010       --  Note: the use of Unrestricted_Access in the following call is needed
1011       --  because otherwise we have an error of getting a access-to-volatile
1012       --  value which points to a non-volatile object. But in this case it is
1013       --  safe to do this, since we know we have no problems with aliasing and
1014       --  Unrestricted_Access bypasses this check.
1015
1016       Result :=
1017         thr_create
1018           (System.Null_Address,
1019            Adjusted_Stack_Size,
1020            Thread_Body_Access (Wrapper),
1021            To_Address (T),
1022            Opts,
1023            T.Common.LL.Thread'Unrestricted_Access);
1024
1025       Succeeded := Result = 0;
1026       pragma Assert
1027         (Result = 0
1028           or else Result = ENOMEM
1029           or else Result = EAGAIN);
1030    end Create_Task;
1031
1032    ------------------
1033    -- Finalize_TCB --
1034    ------------------
1035
1036    procedure Finalize_TCB (T : Task_Id) is
1037       Result : Interfaces.C.int;
1038
1039    begin
1040       T.Common.LL.Thread := Null_Thread_Id;
1041
1042       if not Single_Lock then
1043          Result := mutex_destroy (T.Common.LL.L.L'Access);
1044          pragma Assert (Result = 0);
1045       end if;
1046
1047       Result := cond_destroy (T.Common.LL.CV'Access);
1048       pragma Assert (Result = 0);
1049
1050       if T.Known_Tasks_Index /= -1 then
1051          Known_Tasks (T.Known_Tasks_Index) := null;
1052       end if;
1053
1054       ATCB_Allocation.Free_ATCB (T);
1055    end Finalize_TCB;
1056
1057    ---------------
1058    -- Exit_Task --
1059    ---------------
1060
1061    --  This procedure must be called with abort deferred. It can no longer
1062    --  call Self or access the current task's ATCB, since the ATCB has been
1063    --  deallocated.
1064
1065    procedure Exit_Task is
1066    begin
1067       Specific.Set (null);
1068    end Exit_Task;
1069
1070    ----------------
1071    -- Abort_Task --
1072    ----------------
1073
1074    procedure Abort_Task (T : Task_Id) is
1075       Result : Interfaces.C.int;
1076    begin
1077       if Abort_Handler_Installed then
1078          pragma Assert (T /= Self);
1079          Result :=
1080            thr_kill
1081              (T.Common.LL.Thread,
1082               Signal (System.Interrupt_Management.Abort_Task_Interrupt));
1083          pragma Assert (Result = 0);
1084       end if;
1085    end Abort_Task;
1086
1087    -----------
1088    -- Sleep --
1089    -----------
1090
1091    procedure Sleep
1092      (Self_ID : Task_Id;
1093       Reason  : Task_States)
1094    is
1095       Result : Interfaces.C.int;
1096
1097    begin
1098       pragma Assert (Check_Sleep (Reason));
1099
1100       if Single_Lock then
1101          Result :=
1102            cond_wait
1103              (Self_ID.Common.LL.CV'Access, Single_RTS_Lock.L'Access);
1104       else
1105          Result :=
1106            cond_wait
1107              (Self_ID.Common.LL.CV'Access, Self_ID.Common.LL.L.L'Access);
1108       end if;
1109
1110       pragma Assert
1111         (Record_Wakeup (To_Lock_Ptr (Self_ID.Common.LL.L'Access), Reason));
1112       pragma Assert (Result = 0 or else Result = EINTR);
1113    end Sleep;
1114
1115    --  Note that we are relying heavily here on GNAT representing
1116    --  Calendar.Time, System.Real_Time.Time, Duration,
1117    --  System.Real_Time.Time_Span in the same way, i.e., as a 64-bit count of
1118    --  nanoseconds.
1119
1120    --  This allows us to always pass the timeout value as a Duration
1121
1122    --  ???
1123    --  We are taking liberties here with the semantics of the delays. That is,
1124    --  we make no distinction between delays on the Calendar clock and delays
1125    --  on the Real_Time clock. That is technically incorrect, if the Calendar
1126    --  clock happens to be reset or adjusted. To solve this defect will require
1127    --  modification to the compiler interface, so that it can pass through more
1128    --  information, to tell us here which clock to use!
1129
1130    --  cond_timedwait will return if any of the following happens:
1131    --  1) some other task did cond_signal on this condition variable
1132    --     In this case, the return value is 0
1133    --  2) the call just returned, for no good reason
1134    --     This is called a "spurious wakeup".
1135    --     In this case, the return value may also be 0.
1136    --  3) the time delay expires
1137    --     In this case, the return value is ETIME
1138    --  4) this task received a signal, which was handled by some
1139    --     handler procedure, and now the thread is resuming execution
1140    --     UNIX calls this an "interrupted" system call.
1141    --     In this case, the return value is EINTR
1142
1143    --  If the cond_timedwait returns 0 or EINTR, it is still possible that the
1144    --  time has actually expired, and by chance a signal or cond_signal
1145    --  occurred at around the same time.
1146
1147    --  We have also observed that on some OS's the value ETIME will be
1148    --  returned, but the clock will show that the full delay has not yet
1149    --  expired.
1150
1151    --  For these reasons, we need to check the clock after return from
1152    --  cond_timedwait. If the time has expired, we will set Timedout = True.
1153
1154    --  This check might be omitted for systems on which the cond_timedwait()
1155    --  never returns early or wakes up spuriously.
1156
1157    --  Annex D requires that completion of a delay cause the task to go to the
1158    --  end of its priority queue, regardless of whether the task actually was
1159    --  suspended by the delay. Since cond_timedwait does not do this on
1160    --  Solaris, we add a call to thr_yield at the end. We might do this at the
1161    --  beginning, instead, but then the round-robin effect would not be the
1162    --  same; the delayed task would be ahead of other tasks of the same
1163    --  priority that awoke while it was sleeping.
1164
1165    --  For Timed_Sleep, we are expecting possible cond_signals to indicate
1166    --  other events (e.g., completion of a RV or completion of the abortable
1167    --  part of an async. select), we want to always return if interrupted. The
1168    --  caller will be responsible for checking the task state to see whether
1169    --  the wakeup was spurious, and to go back to sleep again in that case. We
1170    --  don't need to check for pending abort or priority change on the way in
1171    --  our out; that is the caller's responsibility.
1172
1173    --  For Timed_Delay, we are not expecting any cond_signals or other
1174    --  interruptions, except for priority changes and aborts. Therefore, we
1175    --  don't want to return unless the delay has actually expired, or the call
1176    --  has been aborted. In this case, since we want to implement the entire
1177    --  delay statement semantics, we do need to check for pending abort and
1178    --  priority changes. We can quietly handle priority changes inside the
1179    --  procedure, since there is no entry-queue reordering involved.
1180
1181    -----------------
1182    -- Timed_Sleep --
1183    -----------------
1184
1185    procedure Timed_Sleep
1186      (Self_ID  : Task_Id;
1187       Time     : Duration;
1188       Mode     : ST.Delay_Modes;
1189       Reason   : System.Tasking.Task_States;
1190       Timedout : out Boolean;
1191       Yielded  : out Boolean)
1192    is
1193       Base_Time  : constant Duration := Monotonic_Clock;
1194       Check_Time : Duration := Base_Time;
1195       Abs_Time   : Duration;
1196       Request    : aliased timespec;
1197       Result     : Interfaces.C.int;
1198
1199    begin
1200       pragma Assert (Check_Sleep (Reason));
1201       Timedout := True;
1202       Yielded := False;
1203
1204       Abs_Time :=
1205         (if Mode = Relative
1206          then Duration'Min (Time, Max_Sensible_Delay) + Check_Time
1207          else Duration'Min (Check_Time + Max_Sensible_Delay, Time));
1208
1209       if Abs_Time > Check_Time then
1210          Request := To_Timespec (Abs_Time);
1211          loop
1212             exit when Self_ID.Pending_ATC_Level < Self_ID.ATC_Nesting_Level;
1213
1214             if Single_Lock then
1215                Result :=
1216                  cond_timedwait
1217                    (Self_ID.Common.LL.CV'Access,
1218                     Single_RTS_Lock.L'Access, Request'Access);
1219             else
1220                Result :=
1221                  cond_timedwait
1222                    (Self_ID.Common.LL.CV'Access,
1223                     Self_ID.Common.LL.L.L'Access, Request'Access);
1224             end if;
1225
1226             Yielded := True;
1227
1228             Check_Time := Monotonic_Clock;
1229             exit when Abs_Time <= Check_Time or else Check_Time < Base_Time;
1230
1231             if Result = 0 or Result = EINTR then
1232
1233                --  Somebody may have called Wakeup for us
1234
1235                Timedout := False;
1236                exit;
1237             end if;
1238
1239             pragma Assert (Result = ETIME);
1240          end loop;
1241       end if;
1242
1243       pragma Assert
1244         (Record_Wakeup (To_Lock_Ptr (Self_ID.Common.LL.L'Access), Reason));
1245    end Timed_Sleep;
1246
1247    -----------------
1248    -- Timed_Delay --
1249    -----------------
1250
1251    procedure Timed_Delay
1252      (Self_ID : Task_Id;
1253       Time    : Duration;
1254       Mode    : ST.Delay_Modes)
1255    is
1256       Base_Time  : constant Duration := Monotonic_Clock;
1257       Check_Time : Duration := Base_Time;
1258       Abs_Time   : Duration;
1259       Request    : aliased timespec;
1260       Result     : Interfaces.C.int;
1261       Yielded    : Boolean := False;
1262
1263    begin
1264       if Single_Lock then
1265          Lock_RTS;
1266       end if;
1267
1268       Write_Lock (Self_ID);
1269
1270       Abs_Time :=
1271         (if Mode = Relative
1272          then Time + Check_Time
1273          else Duration'Min (Check_Time + Max_Sensible_Delay, Time));
1274
1275       if Abs_Time > Check_Time then
1276          Request := To_Timespec (Abs_Time);
1277          Self_ID.Common.State := Delay_Sleep;
1278
1279          pragma Assert (Check_Sleep (Delay_Sleep));
1280
1281          loop
1282             exit when Self_ID.Pending_ATC_Level < Self_ID.ATC_Nesting_Level;
1283
1284             if Single_Lock then
1285                Result :=
1286                  cond_timedwait
1287                    (Self_ID.Common.LL.CV'Access,
1288                     Single_RTS_Lock.L'Access,
1289                     Request'Access);
1290             else
1291                Result :=
1292                  cond_timedwait
1293                    (Self_ID.Common.LL.CV'Access,
1294                     Self_ID.Common.LL.L.L'Access,
1295                     Request'Access);
1296             end if;
1297
1298             Yielded := True;
1299
1300             Check_Time := Monotonic_Clock;
1301             exit when Abs_Time <= Check_Time or else Check_Time < Base_Time;
1302
1303             pragma Assert
1304               (Result = 0     or else
1305                Result = ETIME or else
1306                Result = EINTR);
1307          end loop;
1308
1309          pragma Assert
1310            (Record_Wakeup
1311               (To_Lock_Ptr (Self_ID.Common.LL.L'Access), Delay_Sleep));
1312
1313          Self_ID.Common.State := Runnable;
1314       end if;
1315
1316       Unlock (Self_ID);
1317
1318       if Single_Lock then
1319          Unlock_RTS;
1320       end if;
1321
1322       if not Yielded then
1323          thr_yield;
1324       end if;
1325    end Timed_Delay;
1326
1327    ------------
1328    -- Wakeup --
1329    ------------
1330
1331    procedure Wakeup
1332      (T : Task_Id;
1333       Reason : Task_States)
1334    is
1335       Result : Interfaces.C.int;
1336    begin
1337       pragma Assert (Check_Wakeup (T, Reason));
1338       Result := cond_signal (T.Common.LL.CV'Access);
1339       pragma Assert (Result = 0);
1340    end Wakeup;
1341
1342    ---------------------------
1343    -- Check_Initialize_Lock --
1344    ---------------------------
1345
1346    --  The following code is intended to check some of the invariant assertions
1347    --  related to lock usage, on which we depend.
1348
1349    function Check_Initialize_Lock
1350      (L     : Lock_Ptr;
1351       Level : Lock_Level) return Boolean
1352    is
1353       Self_ID : constant Task_Id := Self;
1354
1355    begin
1356       --  Check that caller is abort-deferred
1357
1358       if Self_ID.Deferral_Level = 0 then
1359          return False;
1360       end if;
1361
1362       --  Check that the lock is not yet initialized
1363
1364       if L.Level /= 0 then
1365          return False;
1366       end if;
1367
1368       L.Level := Lock_Level'Pos (Level) + 1;
1369       return True;
1370    end Check_Initialize_Lock;
1371
1372    ----------------
1373    -- Check_Lock --
1374    ----------------
1375
1376    function Check_Lock (L : Lock_Ptr) return Boolean is
1377       Self_ID : constant Task_Id := Self;
1378       P       : Lock_Ptr;
1379
1380    begin
1381       --  Check that the argument is not null
1382
1383       if L = null then
1384          return False;
1385       end if;
1386
1387       --  Check that L is not frozen
1388
1389       if L.Frozen then
1390          return False;
1391       end if;
1392
1393       --  Check that caller is abort-deferred
1394
1395       if Self_ID.Deferral_Level = 0 then
1396          return False;
1397       end if;
1398
1399       --  Check that caller is not holding this lock already
1400
1401       if L.Owner = To_Owner_ID (To_Address (Self_ID)) then
1402          return False;
1403       end if;
1404
1405       if Single_Lock then
1406          return True;
1407       end if;
1408
1409       --  Check that TCB lock order rules are satisfied
1410
1411       P := Self_ID.Common.LL.Locks;
1412       if P /= null then
1413          if P.Level >= L.Level
1414            and then (P.Level > 2 or else L.Level > 2)
1415          then
1416             return False;
1417          end if;
1418       end if;
1419
1420       return True;
1421    end Check_Lock;
1422
1423    -----------------
1424    -- Record_Lock --
1425    -----------------
1426
1427    function Record_Lock (L : Lock_Ptr) return Boolean is
1428       Self_ID : constant Task_Id := Self;
1429       P       : Lock_Ptr;
1430
1431    begin
1432       Lock_Count := Lock_Count + 1;
1433
1434       --  There should be no owner for this lock at this point
1435
1436       if L.Owner /= null then
1437          return False;
1438       end if;
1439
1440       --  Record new owner
1441
1442       L.Owner := To_Owner_ID (To_Address (Self_ID));
1443
1444       if Single_Lock then
1445          return True;
1446       end if;
1447
1448       --  Check that TCB lock order rules are satisfied
1449
1450       P := Self_ID.Common.LL.Locks;
1451
1452       if P /= null then
1453          L.Next := P;
1454       end if;
1455
1456       Self_ID.Common.LL.Locking := null;
1457       Self_ID.Common.LL.Locks := L;
1458       return True;
1459    end Record_Lock;
1460
1461    -----------------
1462    -- Check_Sleep --
1463    -----------------
1464
1465    function Check_Sleep (Reason : Task_States) return Boolean is
1466       pragma Unreferenced (Reason);
1467
1468       Self_ID : constant Task_Id := Self;
1469       P       : Lock_Ptr;
1470
1471    begin
1472       --  Check that caller is abort-deferred
1473
1474       if Self_ID.Deferral_Level = 0 then
1475          return False;
1476       end if;
1477
1478       if Single_Lock then
1479          return True;
1480       end if;
1481
1482       --  Check that caller is holding own lock, on top of list
1483
1484       if Self_ID.Common.LL.Locks /=
1485         To_Lock_Ptr (Self_ID.Common.LL.L'Access)
1486       then
1487          return False;
1488       end if;
1489
1490       --  Check that TCB lock order rules are satisfied
1491
1492       if Self_ID.Common.LL.Locks.Next /= null then
1493          return False;
1494       end if;
1495
1496       Self_ID.Common.LL.L.Owner := null;
1497       P := Self_ID.Common.LL.Locks;
1498       Self_ID.Common.LL.Locks := Self_ID.Common.LL.Locks.Next;
1499       P.Next := null;
1500       return True;
1501    end Check_Sleep;
1502
1503    -------------------
1504    -- Record_Wakeup --
1505    -------------------
1506
1507    function Record_Wakeup
1508      (L      : Lock_Ptr;
1509       Reason : Task_States) return Boolean
1510    is
1511       pragma Unreferenced (Reason);
1512
1513       Self_ID : constant Task_Id := Self;
1514       P       : Lock_Ptr;
1515
1516    begin
1517       --  Record new owner
1518
1519       L.Owner := To_Owner_ID (To_Address (Self_ID));
1520
1521       if Single_Lock then
1522          return True;
1523       end if;
1524
1525       --  Check that TCB lock order rules are satisfied
1526
1527       P := Self_ID.Common.LL.Locks;
1528
1529       if P /= null then
1530          L.Next := P;
1531       end if;
1532
1533       Self_ID.Common.LL.Locking := null;
1534       Self_ID.Common.LL.Locks := L;
1535       return True;
1536    end Record_Wakeup;
1537
1538    ------------------
1539    -- Check_Wakeup --
1540    ------------------
1541
1542    function Check_Wakeup
1543      (T      : Task_Id;
1544       Reason : Task_States) return Boolean
1545    is
1546       Self_ID : constant Task_Id := Self;
1547
1548    begin
1549       --  Is caller holding T's lock?
1550
1551       if T.Common.LL.L.Owner /= To_Owner_ID (To_Address (Self_ID)) then
1552          return False;
1553       end if;
1554
1555       --  Are reasons for wakeup and sleep consistent?
1556
1557       if T.Common.State /= Reason then
1558          return False;
1559       end if;
1560
1561       return True;
1562    end Check_Wakeup;
1563
1564    ------------------
1565    -- Check_Unlock --
1566    ------------------
1567
1568    function Check_Unlock (L : Lock_Ptr) return Boolean is
1569       Self_ID : constant Task_Id := Self;
1570       P       : Lock_Ptr;
1571
1572    begin
1573       Unlock_Count := Unlock_Count + 1;
1574
1575       if L = null then
1576          return False;
1577       end if;
1578
1579       if L.Buddy /= null then
1580          return False;
1581       end if;
1582
1583       --  Magic constant 4???
1584
1585       if L.Level = 4 then
1586          Check_Count := Unlock_Count;
1587       end if;
1588
1589       --  Magic constant 1000???
1590
1591       if Unlock_Count - Check_Count > 1000 then
1592          Check_Count := Unlock_Count;
1593       end if;
1594
1595       --  Check that caller is abort-deferred
1596
1597       if Self_ID.Deferral_Level = 0 then
1598          return False;
1599       end if;
1600
1601       --  Check that caller is holding this lock, on top of list
1602
1603       if Self_ID.Common.LL.Locks /= L then
1604          return False;
1605       end if;
1606
1607       --  Record there is no owner now
1608
1609       L.Owner := null;
1610       P := Self_ID.Common.LL.Locks;
1611       Self_ID.Common.LL.Locks := Self_ID.Common.LL.Locks.Next;
1612       P.Next := null;
1613       return True;
1614    end Check_Unlock;
1615
1616    --------------------
1617    -- Check_Finalize --
1618    --------------------
1619
1620    function Check_Finalize_Lock (L : Lock_Ptr) return Boolean is
1621       Self_ID : constant Task_Id := Self;
1622
1623    begin
1624       --  Check that caller is abort-deferred
1625
1626       if Self_ID.Deferral_Level = 0 then
1627          return False;
1628       end if;
1629
1630       --  Check that no one is holding this lock
1631
1632       if L.Owner /= null then
1633          return False;
1634       end if;
1635
1636       L.Frozen := True;
1637       return True;
1638    end Check_Finalize_Lock;
1639
1640    ----------------
1641    -- Initialize --
1642    ----------------
1643
1644    procedure Initialize (S : in out Suspension_Object) is
1645       Result : Interfaces.C.int;
1646
1647    begin
1648       --  Initialize internal state (always to zero (RM D.10(6)))
1649
1650       S.State := False;
1651       S.Waiting := False;
1652
1653       --  Initialize internal mutex
1654
1655       Result := mutex_init (S.L'Access, USYNC_THREAD, System.Null_Address);
1656       pragma Assert (Result = 0 or else Result = ENOMEM);
1657
1658       if Result = ENOMEM then
1659          raise Storage_Error with "Failed to allocate a lock";
1660       end if;
1661
1662       --  Initialize internal condition variable
1663
1664       Result := cond_init (S.CV'Access, USYNC_THREAD, 0);
1665       pragma Assert (Result = 0 or else Result = ENOMEM);
1666
1667       if Result /= 0 then
1668          Result := mutex_destroy (S.L'Access);
1669          pragma Assert (Result = 0);
1670
1671          if Result = ENOMEM then
1672             raise Storage_Error;
1673          end if;
1674       end if;
1675    end Initialize;
1676
1677    --------------
1678    -- Finalize --
1679    --------------
1680
1681    procedure Finalize (S : in out Suspension_Object) is
1682       Result  : Interfaces.C.int;
1683
1684    begin
1685       --  Destroy internal mutex
1686
1687       Result := mutex_destroy (S.L'Access);
1688       pragma Assert (Result = 0);
1689
1690       --  Destroy internal condition variable
1691
1692       Result := cond_destroy (S.CV'Access);
1693       pragma Assert (Result = 0);
1694    end Finalize;
1695
1696    -------------------
1697    -- Current_State --
1698    -------------------
1699
1700    function Current_State (S : Suspension_Object) return Boolean is
1701    begin
1702       --  We do not want to use lock on this read operation. State is marked
1703       --  as Atomic so that we ensure that the value retrieved is correct.
1704
1705       return S.State;
1706    end Current_State;
1707
1708    ---------------
1709    -- Set_False --
1710    ---------------
1711
1712    procedure Set_False (S : in out Suspension_Object) is
1713       Result  : Interfaces.C.int;
1714
1715    begin
1716       SSL.Abort_Defer.all;
1717
1718       Result := mutex_lock (S.L'Access);
1719       pragma Assert (Result = 0);
1720
1721       S.State := False;
1722
1723       Result := mutex_unlock (S.L'Access);
1724       pragma Assert (Result = 0);
1725
1726       SSL.Abort_Undefer.all;
1727    end Set_False;
1728
1729    --------------
1730    -- Set_True --
1731    --------------
1732
1733    procedure Set_True (S : in out Suspension_Object) is
1734       Result : Interfaces.C.int;
1735
1736    begin
1737       SSL.Abort_Defer.all;
1738
1739       Result := mutex_lock (S.L'Access);
1740       pragma Assert (Result = 0);
1741
1742       --  If there is already a task waiting on this suspension object then
1743       --  we resume it, leaving the state of the suspension object to False,
1744       --  as it is specified in ARM D.10 par. 9. Otherwise, it just leaves
1745       --  the state to True.
1746
1747       if S.Waiting then
1748          S.Waiting := False;
1749          S.State := False;
1750
1751          Result := cond_signal (S.CV'Access);
1752          pragma Assert (Result = 0);
1753
1754       else
1755          S.State := True;
1756       end if;
1757
1758       Result := mutex_unlock (S.L'Access);
1759       pragma Assert (Result = 0);
1760
1761       SSL.Abort_Undefer.all;
1762    end Set_True;
1763
1764    ------------------------
1765    -- Suspend_Until_True --
1766    ------------------------
1767
1768    procedure Suspend_Until_True (S : in out Suspension_Object) is
1769       Result : Interfaces.C.int;
1770
1771    begin
1772       SSL.Abort_Defer.all;
1773
1774       Result := mutex_lock (S.L'Access);
1775       pragma Assert (Result = 0);
1776
1777       if S.Waiting then
1778
1779          --  Program_Error must be raised upon calling Suspend_Until_True
1780          --  if another task is already waiting on that suspension object
1781          --  (RM D.10(10)).
1782
1783          Result := mutex_unlock (S.L'Access);
1784          pragma Assert (Result = 0);
1785
1786          SSL.Abort_Undefer.all;
1787
1788          raise Program_Error;
1789
1790       else
1791          --  Suspend the task if the state is False. Otherwise, the task
1792          --  continues its execution, and the state of the suspension object
1793          --  is set to False (ARM D.10 par. 9).
1794
1795          if S.State then
1796             S.State := False;
1797          else
1798             S.Waiting := True;
1799
1800             loop
1801                --  Loop in case pthread_cond_wait returns earlier than expected
1802                --  (e.g. in case of EINTR caused by a signal).
1803
1804                Result := cond_wait (S.CV'Access, S.L'Access);
1805                pragma Assert (Result = 0 or else Result = EINTR);
1806
1807                exit when not S.Waiting;
1808             end loop;
1809          end if;
1810
1811          Result := mutex_unlock (S.L'Access);
1812          pragma Assert (Result = 0);
1813
1814          SSL.Abort_Undefer.all;
1815       end if;
1816    end Suspend_Until_True;
1817
1818    ----------------
1819    -- Check_Exit --
1820    ----------------
1821
1822    function Check_Exit (Self_ID : Task_Id) return Boolean is
1823    begin
1824       --  Check that caller is just holding Global_Task_Lock and no other locks
1825
1826       if Self_ID.Common.LL.Locks = null then
1827          return False;
1828       end if;
1829
1830       --  2 = Global_Task_Level
1831
1832       if Self_ID.Common.LL.Locks.Level /= 2 then
1833          return False;
1834       end if;
1835
1836       if Self_ID.Common.LL.Locks.Next /= null then
1837          return False;
1838       end if;
1839
1840       --  Check that caller is abort-deferred
1841
1842       if Self_ID.Deferral_Level = 0 then
1843          return False;
1844       end if;
1845
1846       return True;
1847    end Check_Exit;
1848
1849    --------------------
1850    -- Check_No_Locks --
1851    --------------------
1852
1853    function Check_No_Locks (Self_ID : Task_Id) return Boolean is
1854    begin
1855       return Self_ID.Common.LL.Locks = null;
1856    end Check_No_Locks;
1857
1858    ----------------------
1859    -- Environment_Task --
1860    ----------------------
1861
1862    function Environment_Task return Task_Id is
1863    begin
1864       return Environment_Task_Id;
1865    end Environment_Task;
1866
1867    --------------
1868    -- Lock_RTS --
1869    --------------
1870
1871    procedure Lock_RTS is
1872    begin
1873       Write_Lock (Single_RTS_Lock'Access, Global_Lock => True);
1874    end Lock_RTS;
1875
1876    ----------------
1877    -- Unlock_RTS --
1878    ----------------
1879
1880    procedure Unlock_RTS is
1881    begin
1882       Unlock (Single_RTS_Lock'Access, Global_Lock => True);
1883    end Unlock_RTS;
1884
1885    ------------------
1886    -- Suspend_Task --
1887    ------------------
1888
1889    function Suspend_Task
1890      (T           : ST.Task_Id;
1891       Thread_Self : Thread_Id) return Boolean
1892    is
1893    begin
1894       if T.Common.LL.Thread /= Thread_Self then
1895          return thr_suspend (T.Common.LL.Thread) = 0;
1896       else
1897          return True;
1898       end if;
1899    end Suspend_Task;
1900
1901    -----------------
1902    -- Resume_Task --
1903    -----------------
1904
1905    function Resume_Task
1906      (T           : ST.Task_Id;
1907       Thread_Self : Thread_Id) return Boolean
1908    is
1909    begin
1910       if T.Common.LL.Thread /= Thread_Self then
1911          return thr_continue (T.Common.LL.Thread) = 0;
1912       else
1913          return True;
1914       end if;
1915    end Resume_Task;
1916
1917    --------------------
1918    -- Stop_All_Tasks --
1919    --------------------
1920
1921    procedure Stop_All_Tasks is
1922    begin
1923       null;
1924    end Stop_All_Tasks;
1925
1926    ---------------
1927    -- Stop_Task --
1928    ---------------
1929
1930    function Stop_Task (T : ST.Task_Id) return Boolean is
1931       pragma Unreferenced (T);
1932    begin
1933       return False;
1934    end Stop_Task;
1935
1936    -------------------
1937    -- Continue_Task --
1938    -------------------
1939
1940    function Continue_Task (T : ST.Task_Id) return Boolean is
1941       pragma Unreferenced (T);
1942    begin
1943       return False;
1944    end Continue_Task;
1945
1946    -----------------------
1947    -- Set_Task_Affinity --
1948    -----------------------
1949
1950    procedure Set_Task_Affinity (T : ST.Task_Id) is
1951       Result    : Interfaces.C.int;
1952       Proc      : processorid_t;  --  User processor #
1953       Last_Proc : processorid_t;  --  Last processor #
1954
1955       use System.Task_Info;
1956       use type System.Multiprocessors.CPU_Range;
1957
1958    begin
1959       --  Do nothing if the underlying thread has not yet been created. If the
1960       --  thread has not yet been created then the proper affinity will be set
1961       --  during its creation.
1962
1963       if T.Common.LL.Thread = Null_Thread_Id then
1964          null;
1965
1966       --  pragma CPU
1967
1968       elsif T.Common.Base_CPU /=
1969            System.Multiprocessors.Not_A_Specific_CPU
1970       then
1971          --  The CPU numbering in pragma CPU starts at 1 while the subprogram
1972          --  to set the affinity starts at 0, therefore we must substract 1.
1973
1974          Result :=
1975            processor_bind
1976              (P_LWPID, id_t (T.Common.LL.LWP),
1977               processorid_t (T.Common.Base_CPU) - 1, null);
1978          pragma Assert (Result = 0);
1979
1980       --  Task_Info
1981
1982       elsif T.Common.Task_Info /= null then
1983          if T.Common.Task_Info.New_LWP
1984            and then T.Common.Task_Info.CPU /= CPU_UNCHANGED
1985          then
1986             Last_Proc := Num_Procs - 1;
1987
1988             if T.Common.Task_Info.CPU = ANY_CPU then
1989                Result := 0;
1990
1991                Proc := 0;
1992                while Proc < Last_Proc loop
1993                   Result := p_online (Proc, PR_STATUS);
1994                   exit when Result = PR_ONLINE;
1995                   Proc := Proc + 1;
1996                end loop;
1997
1998                Result :=
1999                  processor_bind
2000                    (P_LWPID, id_t (T.Common.LL.LWP), Proc, null);
2001                pragma Assert (Result = 0);
2002
2003             else
2004                --  Use specified processor
2005
2006                if T.Common.Task_Info.CPU < 0
2007                  or else T.Common.Task_Info.CPU > Last_Proc
2008                then
2009                   raise Invalid_CPU_Number;
2010                end if;
2011
2012                Result :=
2013                  processor_bind
2014                    (P_LWPID, id_t (T.Common.LL.LWP),
2015                     T.Common.Task_Info.CPU, null);
2016                pragma Assert (Result = 0);
2017             end if;
2018          end if;
2019
2020       --  Handle dispatching domains
2021
2022       elsif T.Common.Domain /= null
2023         and then (T.Common.Domain /= ST.System_Domain
2024                    or else T.Common.Domain.all /=
2025                              (Multiprocessors.CPU'First ..
2026                               Multiprocessors.Number_Of_CPUs => True))
2027       then
2028          declare
2029             CPU_Set : aliased psetid_t;
2030             Result  : int;
2031
2032          begin
2033             Result := pset_create (CPU_Set'Access);
2034             pragma Assert (Result = 0);
2035
2036             --  Set the affinity to all the processors belonging to the
2037             --  dispatching domain.
2038
2039             for Proc in T.Common.Domain'Range loop
2040
2041                --  The Ada CPU numbering starts at 1 while the subprogram to
2042                --  set the affinity starts at 0, therefore we must substract 1.
2043
2044                if T.Common.Domain (Proc) then
2045                   Result :=
2046                     pset_assign (CPU_Set, processorid_t (Proc) - 1, null);
2047                   pragma Assert (Result = 0);
2048                end if;
2049             end loop;
2050
2051             Result :=
2052               pset_bind (CPU_Set, P_LWPID, id_t (T.Common.LL.LWP), null);
2053             pragma Assert (Result = 0);
2054          end;
2055       end if;
2056    end Set_Task_Affinity;
2057
2058 end System.Task_Primitives.Operations;