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