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
37pragma 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
41with Interfaces.C;
42
43with System.Multiprocessors;
44with System.Tasking.Debug;
45with System.Interrupt_Management;
46with System.OS_Constants;
47with System.OS_Primitives;
48with System.Task_Info;
49
50pragma Warnings (Off);
51with System.OS_Lib;
52pragma Warnings (On);
53
54with 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
60package 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
2058end System.Task_Primitives.Operations;
2059