* c-decl.c (duplicate_decls): Conditionalize DECL_SAVED_TREE copy.
[official-gcc.git] / gcc / ada / 5staprop.adb
blob3815b5fb751607ddfb231c1c6b858314ffde7c9a
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNU ADA RUN-TIME LIBRARY (GNARL) COMPONENTS --
4 -- --
5 -- S Y S T E M . T A S K _ P R I M I T I V E S . O P E R A T I O N S --
6 -- --
7 -- B o d y --
8 -- --
9 -- $Revision: 1.92 $
10 -- --
11 -- Copyright (C) 1991-2001, Florida State University --
12 -- --
13 -- GNARL is free software; you can redistribute it and/or modify it under --
14 -- terms of the GNU General Public License as published by the Free Soft- --
15 -- ware Foundation; either version 2, or (at your option) any later ver- --
16 -- sion. GNARL is distributed in the hope that it will be useful, but WITH- --
17 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
18 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
19 -- for more details. You should have received a copy of the GNU General --
20 -- Public License distributed with GNARL; see file COPYING. If not, write --
21 -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, --
22 -- MA 02111-1307, USA. --
23 -- --
24 -- As a special exception, if other files instantiate generics from this --
25 -- unit, or you link this unit with other files to produce an executable, --
26 -- this unit does not by itself cause the resulting executable to be --
27 -- covered by the GNU General Public License. This exception does not --
28 -- however invalidate any other reasons why the executable file might be --
29 -- covered by the GNU Public License. --
30 -- --
31 -- GNARL was developed by the GNARL team at Florida State University. It is --
32 -- now maintained by Ada Core Technologies Inc. in cooperation with Florida --
33 -- State University (http://www.gnat.com). --
34 -- --
35 ------------------------------------------------------------------------------
37 -- This is a Solaris (native) version of this package
39 -- This package contains all the GNULL primitives that interface directly
40 -- with the underlying OS.
42 pragma Polling (Off);
43 -- Turn off polling, we do not want ATC polling to take place during
44 -- tasking operations. It causes infinite loops and other problems.
46 with System.Tasking.Debug;
47 -- used for Known_Tasks
49 with Ada.Exceptions;
50 -- used for Raise_Exception
52 with GNAT.OS_Lib;
53 -- used for String_Access, Getenv
55 with Interfaces.C;
56 -- used for int
57 -- size_t
59 with System.Interrupt_Management;
60 -- used for Keep_Unmasked
61 -- Abort_Task_Interrupt
62 -- Interrupt_ID
64 with System.Interrupt_Management.Operations;
65 -- used for Set_Interrupt_Mask
66 -- All_Tasks_Mask
67 pragma Elaborate_All (System.Interrupt_Management.Operations);
69 with System.Parameters;
70 -- used for Size_Type
72 with System.Tasking;
73 -- used for Ada_Task_Control_Block
74 -- Task_ID
75 -- ATCB components and types
77 with System.Task_Info;
78 -- to initialize Task_Info for a C thread, in function Self
80 with System.Soft_Links;
81 -- used for Defer/Undefer_Abort
82 -- to initialize TSD for a C thread, in function Self
84 -- Note that we do not use System.Tasking.Initialization directly since
85 -- this is a higher level package that we shouldn't depend on. For example
86 -- when using the restricted run time, it is replaced by
87 -- System.Tasking.Restricted.Initialization
89 with System.OS_Primitives;
90 -- used for Delay_Modes
92 with Unchecked_Conversion;
93 with Unchecked_Deallocation;
95 package body System.Task_Primitives.Operations is
97 use System.Tasking.Debug;
98 use System.Tasking;
99 use Interfaces.C;
100 use System.OS_Interface;
101 use System.Parameters;
102 use Ada.Exceptions;
103 use System.OS_Primitives;
105 package SSL renames System.Soft_Links;
107 ------------------
108 -- Local Data --
109 ------------------
111 ATCB_Magic_Code : constant := 16#ADAADAAD#;
112 -- This is used to allow us to catch attempts to call Self
113 -- from outside an Ada task, with high probability.
114 -- For an Ada task, Task_Wrapper.Magic_Number = ATCB_Magic_Code.
116 -- The following are logically constants, but need to be initialized
117 -- at run time.
119 Environment_Task_ID : Task_ID;
120 -- A variable to hold Task_ID for the environment task.
121 -- If we use this variable to get the Task_ID, we need the following
122 -- ATCB_Key only for non-Ada threads.
124 Unblocked_Signal_Mask : aliased sigset_t;
125 -- The set of signals that should unblocked in all tasks
127 ATCB_Key : aliased thread_key_t;
128 -- Key used to find the Ada Task_ID associated with a thread,
129 -- at least for C threads unknown to the Ada run-time system.
131 All_Tasks_L : aliased System.Task_Primitives.RTS_Lock;
132 -- See comments on locking rules in System.Tasking (spec).
134 Next_Serial_Number : Task_Serial_Number := 100;
135 -- We start at 100, to reserve some special values for
136 -- using in error checking.
137 -- The following are internal configuration constants needed.
139 ------------------------
140 -- Priority Support --
141 ------------------------
143 Dynamic_Priority_Support : constant Boolean := True;
144 -- controls whether we poll for pending priority changes during sleeps
146 Priority_Ceiling_Emulation : constant Boolean := True;
147 -- controls whether we emulate priority ceiling locking
149 -- To get a scheduling close to annex D requirements, we use the real-time
150 -- class provided for LWP's and map each task/thread to a specific and
151 -- unique LWP (there is 1 thread per LWP, and 1 LWP per thread).
153 -- The real time class can only be set when the process has root
154 -- priviledges, so in the other cases, we use the normal thread scheduling
155 -- and priority handling.
157 Using_Real_Time_Class : Boolean := False;
158 -- indicates wether the real time class is being used (i.e the process
159 -- has root priviledges).
161 Prio_Param : aliased struct_pcparms;
162 -- Hold priority info (Real_Time) initialized during the package
163 -- elaboration.
165 -------------------------------------
166 -- External Configuration Values --
167 -------------------------------------
169 Time_Slice_Val : Interfaces.C.long;
170 pragma Import (C, Time_Slice_Val, "__gl_time_slice_val");
172 Locking_Policy : Character;
173 pragma Import (C, Locking_Policy, "__gl_locking_policy");
175 Dispatching_Policy : Character;
176 pragma Import (C, Dispatching_Policy, "__gl_task_dispatching_policy");
178 --------------------------------
179 -- Foreign Threads Detection --
180 --------------------------------
182 -- The following are used to allow the Self function to
183 -- automatically generate ATCB's for C threads that happen to call
184 -- Ada procedure, which in turn happen to call the Ada run-time system.
186 type Fake_ATCB;
187 type Fake_ATCB_Ptr is access Fake_ATCB;
188 type Fake_ATCB is record
189 Stack_Base : Interfaces.C.unsigned := 0;
190 -- A value of zero indicates the node is not in use.
191 Next : Fake_ATCB_Ptr;
192 Real_ATCB : aliased Ada_Task_Control_Block (0);
193 end record;
195 Fake_ATCB_List : Fake_ATCB_Ptr;
196 -- A linear linked list.
197 -- The list is protected by All_Tasks_L;
198 -- Nodes are added to this list from the front.
199 -- Once a node is added to this list, it is never removed.
201 Fake_Task_Elaborated : aliased Boolean := True;
202 -- Used to identified fake tasks (i.e., non-Ada Threads).
204 Next_Fake_ATCB : Fake_ATCB_Ptr;
205 -- Used to allocate one Fake_ATCB in advance. See comment in New_Fake_ATCB
207 ------------
208 -- Checks --
209 ------------
211 Check_Count : Integer := 0;
212 Old_Owner : Task_ID;
213 Lock_Count : Integer := 0;
214 Unlock_Count : Integer := 0;
216 function To_Lock_Ptr is
217 new Unchecked_Conversion (RTS_Lock_Ptr, Lock_Ptr);
218 function To_Task_ID is
219 new Unchecked_Conversion (Owner_ID, Task_ID);
220 function To_Owner_ID is
221 new Unchecked_Conversion (Task_ID, Owner_ID);
223 -----------------------
224 -- Local Subprograms --
225 -----------------------
227 function sysconf (name : System.OS_Interface.int)
228 return processorid_t;
229 pragma Import (C, sysconf, "sysconf");
231 SC_NPROCESSORS_CONF : constant System.OS_Interface.int := 14;
233 function Num_Procs (name : System.OS_Interface.int := SC_NPROCESSORS_CONF)
234 return processorid_t renames sysconf;
236 procedure Abort_Handler
237 (Sig : Signal;
238 Code : access siginfo_t;
239 Context : access ucontext_t);
241 function To_thread_t is new Unchecked_Conversion
242 (Integer, System.OS_Interface.thread_t);
244 function To_Task_ID is new Unchecked_Conversion (System.Address, Task_ID);
246 function To_Address is new Unchecked_Conversion (Task_ID, System.Address);
248 type Ptr is access Task_ID;
249 function To_Ptr is new Unchecked_Conversion (Interfaces.C.unsigned, Ptr);
250 function To_Ptr is new Unchecked_Conversion (System.Address, Ptr);
252 type Iptr is access Interfaces.C.unsigned;
253 function To_Iptr is new Unchecked_Conversion (Interfaces.C.unsigned, Iptr);
255 function Thread_Body_Access is
256 new Unchecked_Conversion (System.Address, Thread_Body);
258 function New_Fake_ATCB (Stack_Base : Interfaces.C.unsigned) return Task_ID;
259 -- Allocate and Initialize a new ATCB. This code can safely be called from
260 -- a foreign thread, as it doesn't access implicitely or explicitely
261 -- "self" before having initialized the new ATCB.
263 ------------
264 -- Checks --
265 ------------
267 function Check_Initialize_Lock (L : Lock_Ptr; Level : Lock_Level)
268 return Boolean;
269 pragma Inline (Check_Initialize_Lock);
271 function Check_Lock (L : Lock_Ptr) return Boolean;
272 pragma Inline (Check_Lock);
274 function Record_Lock (L : Lock_Ptr) return Boolean;
275 pragma Inline (Record_Lock);
277 function Check_Sleep (Reason : Task_States) return Boolean;
278 pragma Inline (Check_Sleep);
280 function Record_Wakeup
281 (L : Lock_Ptr;
282 Reason : Task_States) return Boolean;
283 pragma Inline (Record_Wakeup);
285 function Check_Wakeup
286 (T : Task_ID;
287 Reason : Task_States) return Boolean;
288 pragma Inline (Check_Wakeup);
290 function Check_Unlock (L : Lock_Ptr) return Boolean;
291 pragma Inline (Check_Lock);
293 function Check_Finalize_Lock (L : Lock_Ptr) return Boolean;
294 pragma Inline (Check_Finalize_Lock);
296 -------------------
297 -- New_Fake_ATCB --
298 -------------------
300 function New_Fake_ATCB (Stack_Base : Interfaces.C.unsigned)
301 return Task_ID
303 Self_ID : Task_ID;
304 P, Q : Fake_ATCB_Ptr;
305 Succeeded : Boolean;
306 Result : Interfaces.C.int;
308 begin
309 -- This section is ticklish.
310 -- We dare not call anything that might require an ATCB, until
311 -- we have the new ATCB in place.
312 -- Note: we don't use "Write_Lock (All_Tasks_L'Access);" because
313 -- we don't yet have an ATCB, and so can't pass the safety check.
315 Result := mutex_lock (All_Tasks_L.L'Access);
316 Q := null;
317 P := Fake_ATCB_List;
319 while P /= null loop
320 if P.Stack_Base = 0 then
321 Q := P;
322 elsif thr_kill (P.Real_ATCB.Common.LL.Thread, 0) /= 0 then
323 -- ????
324 -- If a C thread that has dependent Ada tasks terminates
325 -- abruptly, e.g. as a result of cancellation, any dependent
326 -- tasks are likely to hang up in termination.
327 P.Stack_Base := 0;
328 Q := P;
329 end if;
331 P := P.Next;
332 end loop;
334 if Q = null then
336 -- Create a new ATCB with zero entries.
338 Self_ID := Next_Fake_ATCB.Real_ATCB'Access;
339 Next_Fake_ATCB.Stack_Base := Stack_Base;
340 Next_Fake_ATCB.Next := Fake_ATCB_List;
341 Fake_ATCB_List := Next_Fake_ATCB;
342 Next_Fake_ATCB := null;
344 else
346 -- Reuse an existing fake ATCB.
348 Self_ID := Q.Real_ATCB'Access;
349 Q.Stack_Base := Stack_Base;
350 end if;
352 -- Do the standard initializations
354 System.Tasking.Initialize_ATCB
355 (Self_ID, null, Null_Address, Null_Task, Fake_Task_Elaborated'Access,
356 System.Priority'First, Task_Info.Unspecified_Task_Info, 0, Self_ID,
357 Succeeded);
358 pragma Assert (Succeeded);
360 -- Record this as the Task_ID for the current thread.
362 Self_ID.Common.LL.Thread := thr_self;
363 Result := thr_setspecific (ATCB_Key, To_Address (Self_ID));
364 pragma Assert (Result = 0);
366 -- Finally, it is safe to use an allocator in this thread.
368 if Next_Fake_ATCB = null then
369 Next_Fake_ATCB := new Fake_ATCB;
370 end if;
372 Self_ID.Master_of_Task := 0;
373 Self_ID.Master_Within := Self_ID.Master_of_Task + 1;
375 for L in Self_ID.Entry_Calls'Range loop
376 Self_ID.Entry_Calls (L).Self := Self_ID;
377 Self_ID.Entry_Calls (L).Level := L;
378 end loop;
380 Self_ID.Common.State := Runnable;
381 Self_ID.Awake_Count := 1;
383 -- Since this is not an ordinary Ada task, we will start out undeferred
385 Self_ID.Deferral_Level := 0;
387 -- Give the task a unique serial number.
389 Self_ID.Serial_Number := Next_Serial_Number;
390 Next_Serial_Number := Next_Serial_Number + 1;
391 pragma Assert (Next_Serial_Number /= 0);
393 System.Soft_Links.Create_TSD (Self_ID.Common.Compiler_Data);
395 -- ????
396 -- The following call is commented out to avoid dependence on
397 -- the System.Tasking.Initialization package.
399 -- It seems that if we want Ada.Task_Attributes to work correctly
400 -- for C threads we will need to raise the visibility of this soft
401 -- link to System.Soft_Links.
403 -- We are putting that off until this new functionality is otherwise
404 -- stable.
406 -- System.Tasking.Initialization.Initialize_Attributes_Link.all (T);
408 -- Must not unlock until Next_ATCB is again allocated.
410 for J in Known_Tasks'Range loop
411 if Known_Tasks (J) = null then
412 Known_Tasks (J) := Self_ID;
413 Self_ID.Known_Tasks_Index := J;
414 exit;
415 end if;
416 end loop;
418 Result := mutex_unlock (All_Tasks_L.L'Access);
420 -- We cannot use "Unlock (All_Tasks_L'Access);" because
421 -- we did not use Write_Lock, and so would not pass the checks.
423 return Self_ID;
424 end New_Fake_ATCB;
426 -------------------
427 -- Abort_Handler --
428 -------------------
430 -- Target-dependent binding of inter-thread Abort signal to
431 -- the raising of the Abort_Signal exception.
433 -- The technical issues and alternatives here are essentially
434 -- the same as for raising exceptions in response to other
435 -- signals (e.g. Storage_Error). See code and comments in
436 -- the package body System.Interrupt_Management.
438 -- Some implementations may not allow an exception to be propagated
439 -- out of a handler, and others might leave the signal or
440 -- interrupt that invoked this handler masked after the exceptional
441 -- return to the application code.
443 -- GNAT exceptions are originally implemented using setjmp()/longjmp().
444 -- On most UNIX systems, this will allow transfer out of a signal handler,
445 -- which is usually the only mechanism available for implementing
446 -- asynchronous handlers of this kind. However, some
447 -- systems do not restore the signal mask on longjmp(), leaving the
448 -- abort signal masked.
450 -- Alternative solutions include:
452 -- 1. Change the PC saved in the system-dependent Context
453 -- parameter to point to code that raises the exception.
454 -- Normal return from this handler will then raise
455 -- the exception after the mask and other system state has
456 -- been restored (see example below).
457 -- 2. Use siglongjmp()/sigsetjmp() to implement exceptions.
458 -- 3. Unmask the signal in the Abortion_Signal exception handler
459 -- (in the RTS).
461 -- The following procedure would be needed if we can't longjmp out of
462 -- a signal handler. (See below.)
464 -- procedure Raise_Abort_Signal is
465 -- begin
466 -- raise Standard'Abort_Signal;
467 -- end if;
469 -- ???
470 -- The comments above need revising. They are partly obsolete.
472 procedure Abort_Handler
473 (Sig : Signal;
474 Code : access siginfo_t;
475 Context : access ucontext_t)
477 Self_ID : Task_ID := Self;
478 Result : Interfaces.C.int;
479 Old_Set : aliased sigset_t;
481 begin
482 -- Assuming it is safe to longjmp out of a signal handler, the
483 -- following code can be used:
485 if Self_ID.Deferral_Level = 0
486 and then Self_ID.Pending_ATC_Level < Self_ID.ATC_Nesting_Level
487 and then not Self_ID.Aborting
488 then
489 -- You can comment the following out,
490 -- to make all aborts synchronous, for debugging.
492 Self_ID.Aborting := True;
494 -- Make sure signals used for RTS internal purpose are unmasked
496 Result := thr_sigsetmask (SIG_UNBLOCK,
497 Unblocked_Signal_Mask'Unchecked_Access, Old_Set'Unchecked_Access);
498 pragma Assert (Result = 0);
500 raise Standard'Abort_Signal;
502 -- ?????
503 -- Must be certain that the implementation of "raise"
504 -- does not make any OS/thread calls, or at least that
505 -- if it makes any, they are safe for interruption by
506 -- async. signals.
507 end if;
509 -- Otherwise, something like this is required:
510 -- if not Abort_Is_Deferred.all then
511 -- -- Overwrite the return PC address with the address of the
512 -- -- special raise routine, and "return" to that routine's
513 -- -- starting address.
514 -- Context.PC := Raise_Abort_Signal'Address;
515 -- return;
516 -- end if;
518 end Abort_Handler;
520 -------------------
521 -- Stack_Guard --
522 -------------------
524 -- The underlying thread system sets a guard page at the
525 -- bottom of a thread stack, so nothing is needed.
527 procedure Stack_Guard (T : ST.Task_ID; On : Boolean) is
528 begin
529 null;
530 end Stack_Guard;
532 --------------------
533 -- Get_Thread_Id --
534 --------------------
536 function Get_Thread_Id (T : ST.Task_ID) return OSI.Thread_Id is
537 begin
538 return T.Common.LL.Thread;
539 end Get_Thread_Id;
541 -----------
542 -- Self --
543 -----------
545 function Self return Task_ID is separate;
547 ---------------------
548 -- Initialize_Lock --
549 ---------------------
551 -- Note: mutexes and cond_variables needed per-task basis are
552 -- initialized in Intialize_TCB and the Storage_Error is
553 -- handled. Other mutexes (such as All_Tasks_L, Memory_Lock...)
554 -- used in RTS is initialized before any status change of RTS.
555 -- Therefore rasing Storage_Error in the following routines
556 -- should be able to be handled safely.
558 procedure Initialize_Lock
559 (Prio : System.Any_Priority;
560 L : access Lock)
562 Result : Interfaces.C.int;
564 begin
565 pragma Assert (Check_Initialize_Lock (Lock_Ptr (L), PO_Level));
567 if Priority_Ceiling_Emulation then
568 L.Ceiling := Prio;
569 end if;
571 Result := mutex_init (L.L'Access, USYNC_THREAD, System.Null_Address);
572 pragma Assert (Result = 0 or else Result = ENOMEM);
574 if Result = ENOMEM then
575 Raise_Exception (Storage_Error'Identity, "Failed to allocate a lock");
576 end if;
577 end Initialize_Lock;
579 procedure Initialize_Lock
580 (L : access RTS_Lock;
581 Level : Lock_Level)
583 Result : Interfaces.C.int;
585 begin
586 pragma Assert (Check_Initialize_Lock
587 (To_Lock_Ptr (RTS_Lock_Ptr (L)), Level));
588 Result := mutex_init (L.L'Access, USYNC_THREAD, System.Null_Address);
589 pragma Assert (Result = 0 or else Result = ENOMEM);
591 if Result = ENOMEM then
592 Raise_Exception (Storage_Error'Identity, "Failed to allocate a lock");
593 end if;
594 end Initialize_Lock;
596 -------------------
597 -- Finalize_Lock --
598 -------------------
600 procedure Finalize_Lock (L : access Lock) is
601 Result : Interfaces.C.int;
603 begin
604 pragma Assert (Check_Finalize_Lock (Lock_Ptr (L)));
605 Result := mutex_destroy (L.L'Access);
606 pragma Assert (Result = 0);
607 end Finalize_Lock;
609 procedure Finalize_Lock (L : access RTS_Lock) is
610 Result : Interfaces.C.int;
612 begin
613 pragma Assert (Check_Finalize_Lock (To_Lock_Ptr (RTS_Lock_Ptr (L))));
614 Result := mutex_destroy (L.L'Access);
615 pragma Assert (Result = 0);
616 end Finalize_Lock;
618 ----------------
619 -- Write_Lock --
620 ----------------
622 procedure Write_Lock (L : access Lock; Ceiling_Violation : out Boolean) is
623 Result : Interfaces.C.int;
625 begin
626 pragma Assert (Check_Lock (Lock_Ptr (L)));
628 if Priority_Ceiling_Emulation and then Locking_Policy = 'C' then
629 declare
630 Self_Id : constant Task_ID := Self;
631 Saved_Priority : System.Any_Priority;
633 begin
634 if Self_Id.Common.LL.Active_Priority > L.Ceiling then
635 Ceiling_Violation := True;
636 return;
637 end if;
639 Saved_Priority := Self_Id.Common.LL.Active_Priority;
641 if Self_Id.Common.LL.Active_Priority < L.Ceiling then
642 Set_Priority (Self_Id, L.Ceiling);
643 end if;
645 Result := mutex_lock (L.L'Access);
646 pragma Assert (Result = 0);
647 Ceiling_Violation := False;
649 L.Saved_Priority := Saved_Priority;
650 end;
652 else
653 Result := mutex_lock (L.L'Access);
654 pragma Assert (Result = 0);
655 Ceiling_Violation := False;
656 end if;
658 pragma Assert (Record_Lock (Lock_Ptr (L)));
659 end Write_Lock;
661 procedure Write_Lock (L : access RTS_Lock) is
662 Result : Interfaces.C.int;
664 begin
665 pragma Assert (Check_Lock (To_Lock_Ptr (RTS_Lock_Ptr (L))));
666 Result := mutex_lock (L.L'Access);
667 pragma Assert (Result = 0);
668 pragma Assert (Record_Lock (To_Lock_Ptr (RTS_Lock_Ptr (L))));
669 end Write_Lock;
671 procedure Write_Lock (T : Task_ID) is
672 Result : Interfaces.C.int;
674 begin
675 pragma Assert (Check_Lock (To_Lock_Ptr (T.Common.LL.L'Access)));
676 Result := mutex_lock (T.Common.LL.L.L'Access);
677 pragma Assert (Result = 0);
678 pragma Assert (Record_Lock (To_Lock_Ptr (T.Common.LL.L'Access)));
679 end Write_Lock;
681 ---------------
682 -- Read_Lock --
683 ---------------
685 procedure Read_Lock (L : access Lock; Ceiling_Violation : out Boolean) is
686 begin
687 Write_Lock (L, Ceiling_Violation);
688 end Read_Lock;
690 ------------
691 -- Unlock --
692 ------------
694 procedure Unlock (L : access Lock) is
695 Result : Interfaces.C.int;
697 begin
698 pragma Assert (Check_Unlock (Lock_Ptr (L)));
700 if Priority_Ceiling_Emulation and then Locking_Policy = 'C' then
701 declare
702 Self_Id : constant Task_ID := Self;
704 begin
705 Result := mutex_unlock (L.L'Access);
706 pragma Assert (Result = 0);
708 if Self_Id.Common.LL.Active_Priority > L.Saved_Priority then
709 Set_Priority (Self_Id, L.Saved_Priority);
710 end if;
711 end;
712 else
713 Result := mutex_unlock (L.L'Access);
714 pragma Assert (Result = 0);
715 end if;
716 end Unlock;
718 procedure Unlock (L : access RTS_Lock) is
719 Result : Interfaces.C.int;
721 begin
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 Unlock;
727 procedure Unlock (T : Task_ID) is
728 Result : Interfaces.C.int;
730 begin
731 pragma Assert (Check_Unlock (To_Lock_Ptr (T.Common.LL.L'Access)));
732 Result := mutex_unlock (T.Common.LL.L.L'Access);
733 pragma Assert (Result = 0);
734 end Unlock;
736 -- For the time delay implementation, we need to make sure we
737 -- achieve following criteria:
739 -- 1) We have to delay at least for the amount requested.
740 -- 2) We have to give up CPU even though the actual delay does not
741 -- result in blocking.
742 -- 3) Except for restricted run-time systems that do not support
743 -- ATC or task abort, the delay must be interrupted by the
744 -- abort_task operation.
745 -- 4) The implementation has to be efficient so that the delay overhead
746 -- is relatively cheap.
747 -- (1)-(3) are Ada requirements. Even though (2) is an Annex-D
748 -- requirement we still want to provide the effect in all cases.
749 -- The reason is that users may want to use short delays to implement
750 -- their own scheduling effect in the absence of language provided
751 -- scheduling policies.
753 ---------------------
754 -- Monotonic_Clock --
755 ---------------------
757 function Monotonic_Clock return Duration is
758 TS : aliased timespec;
759 Result : Interfaces.C.int;
761 begin
762 Result := clock_gettime (CLOCK_REALTIME, TS'Unchecked_Access);
763 pragma Assert (Result = 0);
764 return To_Duration (TS);
765 end Monotonic_Clock;
767 -------------------
768 -- RT_Resolution --
769 -------------------
771 function RT_Resolution return Duration is
772 begin
773 return 10#1.0#E-6;
774 end RT_Resolution;
776 -----------
777 -- Yield --
778 -----------
780 procedure Yield (Do_Yield : Boolean := True) is
781 begin
782 if Do_Yield then
783 System.OS_Interface.thr_yield;
784 end if;
785 end Yield;
787 ------------------
788 -- Set_Priority --
789 ------------------
791 procedure Set_Priority
792 (T : Task_ID;
793 Prio : System.Any_Priority;
794 Loss_Of_Inheritance : Boolean := False)
796 Result : Interfaces.C.int;
797 Param : aliased struct_pcparms;
799 use Task_Info;
801 begin
802 T.Common.Current_Priority := Prio;
804 if Priority_Ceiling_Emulation then
805 T.Common.LL.Active_Priority := Prio;
806 end if;
808 if Using_Real_Time_Class then
809 Param.pc_cid := Prio_Param.pc_cid;
810 Param.rt_pri := pri_t (Prio);
811 Param.rt_tqsecs := Prio_Param.rt_tqsecs;
812 Param.rt_tqnsecs := Prio_Param.rt_tqnsecs;
814 Result := Interfaces.C.int (
815 priocntl (PC_VERSION, P_LWPID, T.Common.LL.LWP, PC_SETPARMS,
816 Param'Address));
818 else
819 if T.Common.Task_Info /= null
820 and then not T.Common.Task_Info.Bound_To_LWP
821 then
822 -- The task is not bound to a LWP, so use thr_setprio
824 Result :=
825 thr_setprio (T.Common.LL.Thread, Interfaces.C.int (Prio));
827 else
829 -- The task is bound to a LWP, use priocntl
830 -- ??? TBD
832 null;
833 end if;
834 end if;
835 end Set_Priority;
837 ------------------
838 -- Get_Priority --
839 ------------------
841 function Get_Priority (T : Task_ID) return System.Any_Priority is
842 begin
843 return T.Common.Current_Priority;
844 end Get_Priority;
846 ----------------
847 -- Enter_Task --
848 ----------------
850 procedure Enter_Task (Self_ID : Task_ID) is
851 Result : Interfaces.C.int;
852 Proc : processorid_t; -- User processor #
853 Last_Proc : processorid_t; -- Last processor #
855 use System.Task_Info;
856 begin
857 Self_ID.Common.LL.Thread := thr_self;
859 Self_ID.Common.LL.LWP := lwp_self;
861 if Self_ID.Common.Task_Info /= null then
862 if Self_ID.Common.Task_Info.New_LWP
863 and then Self_ID.Common.Task_Info.CPU /= CPU_UNCHANGED
864 then
865 Last_Proc := Num_Procs - 1;
867 if Self_ID.Common.Task_Info.CPU = ANY_CPU then
868 Result := 0;
869 Proc := 0;
871 while Proc < Last_Proc loop
872 Result := p_online (Proc, PR_STATUS);
873 exit when Result = PR_ONLINE;
874 Proc := Proc + 1;
875 end loop;
877 Result := processor_bind (P_LWPID, P_MYID, Proc, null);
878 pragma Assert (Result = 0);
880 else
881 -- Use specified processor
883 if Self_ID.Common.Task_Info.CPU < 0
884 or else Self_ID.Common.Task_Info.CPU > Last_Proc
885 then
886 raise Invalid_CPU_Number;
887 end if;
889 Result := processor_bind
890 (P_LWPID, P_MYID, Self_ID.Common.Task_Info.CPU, null);
891 pragma Assert (Result = 0);
892 end if;
893 end if;
894 end if;
896 Result := thr_setspecific (ATCB_Key, To_Address (Self_ID));
897 pragma Assert (Result = 0);
899 -- We need the above code even if we do direct fetch of Task_ID in Self
900 -- for the main task on Sun, x86 Solaris and for gcc 2.7.2.
902 Lock_All_Tasks_List;
904 for I in Known_Tasks'Range loop
905 if Known_Tasks (I) = null then
906 Known_Tasks (I) := Self_ID;
907 Self_ID.Known_Tasks_Index := I;
908 exit;
909 end if;
910 end loop;
911 Unlock_All_Tasks_List;
912 end Enter_Task;
914 --------------
915 -- New_ATCB --
916 --------------
918 function New_ATCB (Entry_Num : Task_Entry_Index) return Task_ID is
919 begin
920 return new Ada_Task_Control_Block (Entry_Num);
921 end New_ATCB;
923 ----------------------
924 -- Initialize_TCB --
925 ----------------------
927 procedure Initialize_TCB (Self_ID : Task_ID; Succeeded : out Boolean) is
928 Result : Interfaces.C.int;
930 begin
931 -- Give the task a unique serial number.
933 Self_ID.Serial_Number := Next_Serial_Number;
934 Next_Serial_Number := Next_Serial_Number + 1;
935 pragma Assert (Next_Serial_Number /= 0);
937 Self_ID.Common.LL.Thread := To_thread_t (-1);
938 Result := mutex_init
939 (Self_ID.Common.LL.L.L'Access, USYNC_THREAD, System.Null_Address);
940 Self_ID.Common.LL.L.Level :=
941 Private_Task_Serial_Number (Self_ID.Serial_Number);
942 pragma Assert (Result = 0 or else Result = ENOMEM);
944 if Result = 0 then
945 Result := cond_init (Self_ID.Common.LL.CV'Access, USYNC_THREAD, 0);
946 pragma Assert (Result = 0 or else Result = ENOMEM);
948 if Result /= 0 then
949 Result := mutex_destroy (Self_ID.Common.LL.L.L'Access);
950 pragma Assert (Result = 0);
951 Succeeded := False;
952 else
953 Succeeded := True;
954 end if;
956 else
957 Succeeded := False;
958 end if;
959 end Initialize_TCB;
961 -----------------
962 -- Create_Task --
963 -----------------
965 procedure Create_Task
966 (T : Task_ID;
967 Wrapper : System.Address;
968 Stack_Size : System.Parameters.Size_Type;
969 Priority : System.Any_Priority;
970 Succeeded : out Boolean)
972 Result : Interfaces.C.int;
973 Adjusted_Stack_Size : Interfaces.C.size_t;
974 Opts : Interfaces.C.int := THR_DETACHED;
976 Page_Size : constant System.Parameters.Size_Type := 4096;
977 -- This constant is for reserving extra space at the
978 -- end of the stack, which can be used by the stack
979 -- checking as guard page. The idea is that we need
980 -- to have at least Stack_Size bytes available for
981 -- actual use.
983 use System.Task_Info;
984 begin
985 if Stack_Size = System.Parameters.Unspecified_Size then
986 Adjusted_Stack_Size :=
987 Interfaces.C.size_t (Default_Stack_Size + Page_Size);
989 elsif Stack_Size < Minimum_Stack_Size then
990 Adjusted_Stack_Size :=
991 Interfaces.C.size_t (Minimum_Stack_Size + Page_Size);
993 else
994 Adjusted_Stack_Size :=
995 Interfaces.C.size_t (Stack_Size + Page_Size);
996 end if;
998 -- Since the initial signal mask of a thread is inherited from the
999 -- creator, and the Environment task has all its signals masked, we
1000 -- do not need to manipulate caller's signal mask at this point.
1001 -- All tasks in RTS will have All_Tasks_Mask initially.
1003 if T.Common.Task_Info /= null then
1005 if T.Common.Task_Info.New_LWP then
1006 Opts := Opts + THR_NEW_LWP;
1007 end if;
1009 if T.Common.Task_Info.Bound_To_LWP then
1010 Opts := Opts + THR_BOUND;
1011 end if;
1013 else
1014 Opts := THR_DETACHED + THR_BOUND;
1015 end if;
1017 Result := 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'Access);
1025 Succeeded := Result = 0;
1026 pragma Assert
1027 (Result = 0
1028 or else Result = ENOMEM
1029 or else Result = EAGAIN);
1030 end Create_Task;
1032 ------------------
1033 -- Finalize_TCB --
1034 ------------------
1036 procedure Finalize_TCB (T : Task_ID) is
1037 Result : Interfaces.C.int;
1038 Tmp : Task_ID := T;
1040 procedure Free is new
1041 Unchecked_Deallocation (Ada_Task_Control_Block, Task_ID);
1043 begin
1044 T.Common.LL.Thread := To_thread_t (0);
1045 Result := mutex_destroy (T.Common.LL.L.L'Access);
1046 pragma Assert (Result = 0);
1047 Result := cond_destroy (T.Common.LL.CV'Access);
1048 pragma Assert (Result = 0);
1050 if T.Known_Tasks_Index /= -1 then
1051 Known_Tasks (T.Known_Tasks_Index) := null;
1052 end if;
1054 Free (Tmp);
1055 end Finalize_TCB;
1057 ---------------
1058 -- Exit_Task --
1059 ---------------
1061 -- This procedure must be called with abort deferred.
1062 -- It can no longer call Self or access
1063 -- the current task's ATCB, since the ATCB has been deallocated.
1065 procedure Exit_Task is
1066 begin
1067 thr_exit (System.Null_Address);
1068 end Exit_Task;
1070 ----------------
1071 -- Abort_Task --
1072 ----------------
1074 procedure Abort_Task (T : Task_ID) is
1075 Result : Interfaces.C.int;
1076 begin
1077 pragma Assert (T /= Self);
1079 Result := thr_kill (T.Common.LL.Thread,
1080 Signal (System.Interrupt_Management.Abort_Task_Interrupt));
1081 null;
1083 pragma Assert (Result = 0);
1084 end Abort_Task;
1086 -------------
1087 -- Sleep --
1088 -------------
1090 procedure Sleep
1091 (Self_ID : Task_ID;
1092 Reason : Task_States)
1094 Result : Interfaces.C.int;
1096 begin
1097 pragma Assert (Check_Sleep (Reason));
1099 if Dynamic_Priority_Support
1100 and then Self_ID.Pending_Priority_Change
1101 then
1102 Self_ID.Pending_Priority_Change := False;
1103 Self_ID.Common.Base_Priority := Self_ID.New_Base_Priority;
1104 Set_Priority (Self_ID, Self_ID.Common.Base_Priority);
1105 end if;
1107 Result := cond_wait
1108 (Self_ID.Common.LL.CV'Access, Self_ID.Common.LL.L.L'Access);
1109 pragma Assert (Result = 0 or else Result = EINTR);
1110 pragma Assert (Record_Wakeup
1111 (To_Lock_Ptr (Self_ID.Common.LL.L'Access), Reason));
1112 end Sleep;
1114 -- Note that we are relying heaviliy here on the GNAT feature
1115 -- that Calendar.Time, System.Real_Time.Time, Duration, and
1116 -- System.Real_Time.Time_Span are all represented in the same
1117 -- way, i.e., as a 64-bit count of nanoseconds.
1119 -- This allows us to always pass the timeout value as a Duration.
1121 -- ???
1122 -- We are taking liberties here with the semantics of the delays.
1123 -- That is, we make no distinction between delays on the Calendar clock
1124 -- and delays on the Real_Time clock. That is technically incorrect, if
1125 -- the Calendar clock happens to be reset or adjusted.
1126 -- To solve this defect will require modification to the compiler
1127 -- interface, so that it can pass through more information, to tell
1128 -- us here which clock to use!
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
1143 -- If the cond_timedwait returns 0 or EINTR, it is still
1144 -- possible that the time has actually expired, and by chance
1145 -- a signal or cond_signal occurred at around the same time.
1147 -- We have also observed that on some OS's the value ETIME
1148 -- will be returned, but the clock will show that the full delay
1149 -- has not yet expired.
1151 -- For these reasons, we need to check the clock after return
1152 -- from cond_timedwait. If the time has expired, we will set
1153 -- Timedout = True.
1155 -- This check might be omitted for systems on which the
1156 -- cond_timedwait() never returns early or wakes up spuriously.
1158 -- Annex D requires that completion of a delay cause the task
1159 -- to go to the end of its priority queue, regardless of whether
1160 -- the task actually was suspended by the delay. Since
1161 -- cond_timedwait does not do this on Solaris, we add a call
1162 -- to thr_yield at the end. We might do this at the beginning,
1163 -- instead, but then the round-robin effect would not be the
1164 -- same; the delayed task would be ahead of other tasks of the
1165 -- same priority that awoke while it was sleeping.
1167 -- For Timed_Sleep, we are expecting possible cond_signals
1168 -- to indicate other events (e.g., completion of a RV or
1169 -- completion of the abortable part of an async. select),
1170 -- we want to always return if interrupted. The caller will
1171 -- be responsible for checking the task state to see whether
1172 -- the wakeup was spurious, and to go back to sleep again
1173 -- in that case. We don't need to check for pending abort
1174 -- or priority change on the way in our out; that is the
1175 -- caller's responsibility.
1177 -- For Timed_Delay, we are not expecting any cond_signals or
1178 -- other interruptions, except for priority changes and aborts.
1179 -- Therefore, we don't want to return unless the delay has
1180 -- actually expired, or the call has been aborted. In this
1181 -- case, since we want to implement the entire delay statement
1182 -- semantics, we do need to check for pending abort and priority
1183 -- changes. We can quietly handle priority changes inside the
1184 -- procedure, since there is no entry-queue reordering involved.
1186 -----------------
1187 -- Timed_Sleep --
1188 -----------------
1190 -- This is for use within the run-time system, so abort is
1191 -- assumed to be already deferred, and the caller should be
1192 -- holding its own ATCB lock.
1194 -- Yielded should be False unles we know for certain that the
1195 -- operation resulted in the calling task going to the end of
1196 -- the dispatching queue for its priority.
1198 -- ???
1199 -- This version presumes the worst, so Yielded is always False.
1200 -- On some targets, if cond_timedwait always yields, we could
1201 -- set Yielded to True just before the cond_timedwait call.
1203 procedure Timed_Sleep
1204 (Self_ID : Task_ID;
1205 Time : Duration;
1206 Mode : ST.Delay_Modes;
1207 Reason : System.Tasking.Task_States;
1208 Timedout : out Boolean;
1209 Yielded : out Boolean)
1211 Check_Time : constant Duration := Monotonic_Clock;
1212 Abs_Time : Duration;
1213 Request : aliased timespec;
1214 Result : Interfaces.C.int;
1216 begin
1217 pragma Assert (Check_Sleep (Reason));
1218 Timedout := True;
1219 Yielded := False;
1221 if Mode = Relative then
1222 Abs_Time := Duration'Min (Time, Max_Sensible_Delay) + Check_Time;
1223 else
1224 Abs_Time := Duration'Min (Check_Time + Max_Sensible_Delay, Time);
1225 end if;
1227 if Abs_Time > Check_Time then
1228 Request := To_Timespec (Abs_Time);
1230 loop
1231 exit when Self_ID.Pending_ATC_Level < Self_ID.ATC_Nesting_Level
1232 or else (Dynamic_Priority_Support and then
1233 Self_ID.Pending_Priority_Change);
1235 Result := cond_timedwait (Self_ID.Common.LL.CV'Access,
1236 Self_ID.Common.LL.L.L'Access, Request'Access);
1238 exit when Abs_Time <= Monotonic_Clock;
1240 if Result = 0 or Result = EINTR then
1241 -- somebody may have called Wakeup for us
1242 Timedout := False;
1243 exit;
1244 end if;
1246 pragma Assert (Result = ETIME);
1247 end loop;
1248 end if;
1250 pragma Assert (Record_Wakeup
1251 (To_Lock_Ptr (Self_ID.Common.LL.L'Access), Reason));
1252 end Timed_Sleep;
1254 -----------------
1255 -- Timed_Delay --
1256 -----------------
1258 -- This is for use in implementing delay statements, so
1259 -- we assume the caller is abort-deferred but is holding
1260 -- no locks.
1262 procedure Timed_Delay
1263 (Self_ID : Task_ID;
1264 Time : Duration;
1265 Mode : ST.Delay_Modes)
1267 Check_Time : constant Duration := Monotonic_Clock;
1268 Abs_Time : Duration;
1269 Request : aliased timespec;
1270 Result : Interfaces.C.int;
1272 begin
1273 -- Only the little window between deferring abort and
1274 -- locking Self_ID is the reason we need to
1275 -- check for pending abort and priority change below!
1277 SSL.Abort_Defer.all;
1278 Write_Lock (Self_ID);
1280 if Mode = Relative then
1281 Abs_Time := Time + Check_Time;
1282 else
1283 Abs_Time := Duration'Min (Check_Time + Max_Sensible_Delay, Time);
1284 end if;
1286 if Abs_Time > Check_Time then
1287 Request := To_Timespec (Abs_Time);
1288 Self_ID.Common.State := Delay_Sleep;
1290 pragma Assert (Check_Sleep (Delay_Sleep));
1292 loop
1293 if Dynamic_Priority_Support and then
1294 Self_ID.Pending_Priority_Change then
1295 Self_ID.Pending_Priority_Change := False;
1296 Self_ID.Common.Base_Priority := Self_ID.New_Base_Priority;
1297 Set_Priority (Self_ID, Self_ID.Common.Base_Priority);
1298 end if;
1300 exit when Self_ID.Pending_ATC_Level < Self_ID.ATC_Nesting_Level;
1302 Result := cond_timedwait (Self_ID.Common.LL.CV'Access,
1303 Self_ID.Common.LL.L.L'Access, Request'Access);
1305 exit when Abs_Time <= Monotonic_Clock;
1307 pragma Assert (Result = 0 or else
1308 Result = ETIME or else
1309 Result = EINTR);
1310 end loop;
1312 pragma Assert (Record_Wakeup
1313 (To_Lock_Ptr (Self_ID.Common.LL.L'Access), Delay_Sleep));
1315 Self_ID.Common.State := Runnable;
1316 end if;
1318 Unlock (Self_ID);
1319 thr_yield;
1320 SSL.Abort_Undefer.all;
1321 end Timed_Delay;
1323 ------------
1324 -- Wakeup --
1325 ------------
1327 procedure Wakeup
1328 (T : Task_ID;
1329 Reason : Task_States)
1331 Result : Interfaces.C.int;
1333 begin
1334 pragma Assert (Check_Wakeup (T, Reason));
1335 Result := cond_signal (T.Common.LL.CV'Access);
1336 pragma Assert (Result = 0);
1337 end Wakeup;
1339 ---------------------------
1340 -- Check_Initialize_Lock --
1341 ---------------------------
1343 -- The following code is intended to check some of the invariant
1344 -- assertions related to lock usage, on which we depend.
1346 function Check_Initialize_Lock
1347 (L : Lock_Ptr;
1348 Level : Lock_Level)
1349 return Boolean
1351 Self_ID : constant Task_ID := Self;
1353 begin
1354 -- Check that caller is abort-deferred
1356 if Self_ID.Deferral_Level <= 0 then
1357 return False;
1358 end if;
1360 -- Check that the lock is not yet initialized
1362 if L.Level /= 0 then
1363 return False;
1364 end if;
1366 L.Level := Lock_Level'Pos (Level) + 1;
1367 return True;
1368 end Check_Initialize_Lock;
1370 ----------------
1371 -- Check_Lock --
1372 ----------------
1374 function Check_Lock (L : Lock_Ptr) return Boolean is
1375 Self_ID : Task_ID := Self;
1376 P : Lock_Ptr;
1378 begin
1379 -- Check that the argument is not null
1381 if L = null then
1382 return False;
1383 end if;
1385 -- Check that L is not frozen
1387 if L.Frozen then
1388 return False;
1389 end if;
1391 -- Check that caller is abort-deferred
1393 if Self_ID.Deferral_Level <= 0 then
1394 return False;
1395 end if;
1397 -- Check that caller is not holding this lock already
1399 if L.Owner = To_Owner_ID (Self_ID) then
1400 return False;
1401 end if;
1403 -- Check that TCB lock order rules are satisfied
1405 P := Self_ID.Common.LL.Locks;
1406 if P /= null then
1407 if P.Level >= L.Level
1408 and then (P.Level > 2 or else L.Level > 2)
1409 then
1410 return False;
1411 end if;
1412 end if;
1414 return True;
1415 end Check_Lock;
1417 -----------------
1418 -- Record_Lock --
1419 -----------------
1421 function Record_Lock (L : Lock_Ptr) return Boolean is
1422 Self_ID : Task_ID := Self;
1423 P : Lock_Ptr;
1425 begin
1426 Lock_Count := Lock_Count + 1;
1428 -- There should be no owner for this lock at this point
1430 if L.Owner /= null then
1431 return False;
1432 end if;
1434 -- Record new owner
1436 L.Owner := To_Owner_ID (Self_ID);
1438 -- Check that TCB lock order rules are satisfied
1440 P := Self_ID.Common.LL.Locks;
1442 if P /= null then
1443 L.Next := P;
1444 end if;
1446 Self_ID.Common.LL.Locking := null;
1447 Self_ID.Common.LL.Locks := L;
1448 return True;
1449 end Record_Lock;
1451 -----------------
1452 -- Check_Sleep --
1453 -----------------
1455 function Check_Sleep (Reason : Task_States) return Boolean is
1456 Self_ID : Task_ID := Self;
1457 P : Lock_Ptr;
1459 begin
1460 -- Check that caller is abort-deferred
1462 if Self_ID.Deferral_Level <= 0 then
1463 return False;
1464 end if;
1466 -- Check that caller is holding own lock, on top of list
1468 if Self_ID.Common.LL.Locks /=
1469 To_Lock_Ptr (Self_ID.Common.LL.L'Access)
1470 then
1471 return False;
1472 end if;
1474 -- Check that TCB lock order rules are satisfied
1476 if Self_ID.Common.LL.Locks.Next /= null then
1477 return False;
1478 end if;
1480 Self_ID.Common.LL.L.Owner := null;
1481 P := Self_ID.Common.LL.Locks;
1482 Self_ID.Common.LL.Locks := Self_ID.Common.LL.Locks.Next;
1483 P.Next := null;
1484 return True;
1485 end Check_Sleep;
1487 -------------------
1488 -- Record_Wakeup --
1489 -------------------
1491 function Record_Wakeup
1492 (L : Lock_Ptr;
1493 Reason : Task_States)
1494 return Boolean
1496 Self_ID : Task_ID := Self;
1497 P : Lock_Ptr;
1499 begin
1500 -- Record new owner
1502 L.Owner := To_Owner_ID (Self_ID);
1504 -- Check that TCB lock order rules are satisfied
1506 P := Self_ID.Common.LL.Locks;
1508 if P /= null then
1509 L.Next := P;
1510 end if;
1512 Self_ID.Common.LL.Locking := null;
1513 Self_ID.Common.LL.Locks := L;
1514 return True;
1515 end Record_Wakeup;
1517 ------------------
1518 -- Check_Wakeup --
1519 ------------------
1521 function Check_Wakeup
1522 (T : Task_ID;
1523 Reason : Task_States)
1524 return Boolean
1526 Self_ID : Task_ID := Self;
1528 begin
1529 -- Is caller holding T's lock?
1531 if T.Common.LL.L.Owner /= To_Owner_ID (Self_ID) then
1532 return False;
1533 end if;
1535 -- Are reasons for wakeup and sleep consistent?
1537 if T.Common.State /= Reason then
1538 return False;
1539 end if;
1541 return True;
1542 end Check_Wakeup;
1544 ------------------
1545 -- Check_Unlock --
1546 ------------------
1548 function Check_Unlock (L : Lock_Ptr) return Boolean is
1549 Self_ID : Task_ID := Self;
1550 P : Lock_Ptr;
1552 begin
1553 Unlock_Count := Unlock_Count + 1;
1555 if L = null then
1556 return False;
1557 end if;
1559 if L.Buddy /= null then
1560 return False;
1561 end if;
1563 if L.Level = 4 then
1564 Check_Count := Unlock_Count;
1565 end if;
1567 if Unlock_Count - Check_Count > 1000 then
1568 Check_Count := Unlock_Count;
1569 Old_Owner := To_Task_ID (All_Tasks_L.Owner);
1570 end if;
1572 -- Check that caller is abort-deferred
1574 if Self_ID.Deferral_Level <= 0 then
1575 return False;
1576 end if;
1578 -- Check that caller is holding this lock, on top of list
1580 if Self_ID.Common.LL.Locks /= L then
1581 return False;
1582 end if;
1584 -- Record there is no owner now
1586 L.Owner := null;
1587 P := Self_ID.Common.LL.Locks;
1588 Self_ID.Common.LL.Locks := Self_ID.Common.LL.Locks.Next;
1589 P.Next := null;
1590 return True;
1591 end Check_Unlock;
1593 --------------------
1594 -- Check_Finalize --
1595 --------------------
1597 function Check_Finalize_Lock (L : Lock_Ptr) return Boolean is
1598 Self_ID : Task_ID := Self;
1600 begin
1601 -- Check that caller is abort-deferred
1603 if Self_ID.Deferral_Level <= 0 then
1604 return False;
1605 end if;
1607 -- Check that no one is holding this lock
1609 if L.Owner /= null then
1610 return False;
1611 end if;
1613 L.Frozen := True;
1614 return True;
1615 end Check_Finalize_Lock;
1617 ----------------
1618 -- Check_Exit --
1619 ----------------
1621 function Check_Exit (Self_ID : Task_ID) return Boolean is
1622 begin
1623 -- Check that caller is just holding Global_Task_Lock
1624 -- and no other locks
1626 if Self_ID.Common.LL.Locks = null then
1627 return False;
1628 end if;
1630 -- 2 = Global_Task_Level
1632 if Self_ID.Common.LL.Locks.Level /= 2 then
1633 return False;
1634 end if;
1636 if Self_ID.Common.LL.Locks.Next /= null then
1637 return False;
1638 end if;
1640 -- Check that caller is abort-deferred
1642 if Self_ID.Deferral_Level <= 0 then
1643 return False;
1644 end if;
1646 return True;
1647 end Check_Exit;
1649 --------------------
1650 -- Check_No_Locks --
1651 --------------------
1653 function Check_No_Locks (Self_ID : Task_ID) return Boolean is
1654 begin
1655 return Self_ID.Common.LL.Locks = null;
1656 end Check_No_Locks;
1658 ----------------------
1659 -- Environment_Task --
1660 ----------------------
1662 function Environment_Task return Task_ID is
1663 begin
1664 return Environment_Task_ID;
1665 end Environment_Task;
1667 -------------------------
1668 -- Lock_All_Tasks_List --
1669 -------------------------
1671 procedure Lock_All_Tasks_List is
1672 begin
1673 Write_Lock (All_Tasks_L'Access);
1674 end Lock_All_Tasks_List;
1676 ---------------------------
1677 -- Unlock_All_Tasks_List --
1678 ---------------------------
1680 procedure Unlock_All_Tasks_List is
1681 begin
1682 Unlock (All_Tasks_L'Access);
1683 end Unlock_All_Tasks_List;
1685 ------------------
1686 -- Suspend_Task --
1687 ------------------
1689 function Suspend_Task
1690 (T : ST.Task_ID;
1691 Thread_Self : Thread_Id) return Boolean is
1692 begin
1693 if T.Common.LL.Thread /= Thread_Self then
1694 return thr_suspend (T.Common.LL.Thread) = 0;
1695 else
1696 return True;
1697 end if;
1698 end Suspend_Task;
1700 -----------------
1701 -- Resume_Task --
1702 -----------------
1704 function Resume_Task
1705 (T : ST.Task_ID;
1706 Thread_Self : Thread_Id) return Boolean is
1707 begin
1708 if T.Common.LL.Thread /= Thread_Self then
1709 return thr_continue (T.Common.LL.Thread) = 0;
1710 else
1711 return True;
1712 end if;
1713 end Resume_Task;
1715 ----------------
1716 -- Initialize --
1717 ----------------
1719 procedure Initialize (Environment_Task : ST.Task_ID) is
1720 act : aliased struct_sigaction;
1721 old_act : aliased struct_sigaction;
1722 Tmp_Set : aliased sigset_t;
1723 Result : Interfaces.C.int;
1725 procedure Configure_Processors;
1726 -- Processors configuration
1727 -- The user can specify a processor which the program should run
1728 -- on to emulate a single-processor system. This can be easily
1729 -- done by setting environment variable GNAT_PROCESSOR to one of
1730 -- the following :
1732 -- -2 : use the default configuration (run the program on all
1733 -- available processors) - this is the same as having
1734 -- GNAT_PROCESSOR unset
1735 -- -1 : let the RTS choose one processor and run the program on
1736 -- that processor
1737 -- 0 .. Last_Proc : run the program on the specified processor
1739 -- Last_Proc is equal to the value of the system variable
1740 -- _SC_NPROCESSORS_CONF, minus one.
1742 procedure Configure_Processors is
1744 Proc_Acc : constant GNAT.OS_Lib.String_Access :=
1745 GNAT.OS_Lib.Getenv ("GNAT_PROCESSOR");
1746 begin
1747 if Proc_Acc.all'Length /= 0 then
1749 -- Environment variable is defined
1751 declare
1752 Proc : aliased processorid_t; -- User processor #
1753 Last_Proc : processorid_t; -- Last processor #
1755 begin
1756 Last_Proc := Num_Procs - 1;
1758 if Last_Proc = -1 then
1760 -- Unable to read system variable _SC_NPROCESSORS_CONF
1761 -- Ignore environment variable GNAT_PROCESSOR
1763 null;
1765 else
1766 Proc := processorid_t'Value (Proc_Acc.all);
1768 if Proc < -2 or Proc > Last_Proc then
1769 raise Constraint_Error;
1771 elsif Proc = -2 then
1773 -- Use the default configuration
1775 null;
1777 elsif Proc = -1 then
1779 -- Choose a processor
1781 Result := 0;
1782 while Proc < Last_Proc loop
1783 Proc := Proc + 1;
1784 Result := p_online (Proc, PR_STATUS);
1785 exit when Result = PR_ONLINE;
1786 end loop;
1788 pragma Assert (Result = PR_ONLINE);
1789 Result := processor_bind (P_PID, P_MYID, Proc, null);
1790 pragma Assert (Result = 0);
1792 else
1793 -- Use user processor
1795 Result := processor_bind (P_PID, P_MYID, Proc, null);
1796 pragma Assert (Result = 0);
1797 end if;
1798 end if;
1800 exception
1801 when Constraint_Error =>
1803 -- Illegal environment variable GNAT_PROCESSOR - ignored
1805 null;
1806 end;
1807 end if;
1808 end Configure_Processors;
1810 -- Start of processing for Initialize
1812 begin
1813 Environment_Task_ID := Environment_Task;
1815 -- This is done in Enter_Task, but this is too late for the
1816 -- Environment Task, since we need to call Self in Check_Locks when
1817 -- the run time is compiled with assertions on.
1819 Result := thr_setspecific (ATCB_Key, To_Address (Environment_Task));
1820 pragma Assert (Result = 0);
1822 -- Initialize the lock used to synchronize chain of all ATCBs.
1824 Initialize_Lock (All_Tasks_L'Access, All_Tasks_Level);
1826 Enter_Task (Environment_Task);
1828 -- Install the abort-signal handler
1830 -- Set sa_flags to SA_NODEFER so that during the handler execution
1831 -- we do not change the Signal_Mask to be masked for the Abort_Signal.
1832 -- This is a temporary fix to the problem that the Signal_Mask is
1833 -- not restored after the exception (longjmp) from the handler.
1834 -- The right fix should be made in sigsetjmp so that we save
1835 -- the Signal_Set and restore it after a longjmp.
1836 -- In that case, this field should be changed back to 0. ???
1838 act.sa_flags := 16;
1840 act.sa_handler := Abort_Handler'Address;
1841 Result := sigemptyset (Tmp_Set'Access);
1842 pragma Assert (Result = 0);
1843 act.sa_mask := Tmp_Set;
1845 Result :=
1846 sigaction (
1847 Signal (System.Interrupt_Management.Abort_Task_Interrupt),
1848 act'Unchecked_Access,
1849 old_act'Unchecked_Access);
1850 pragma Assert (Result = 0);
1852 Configure_Processors;
1854 -- Create a free ATCB for use on the Fake_ATCB_List.
1856 Next_Fake_ATCB := new Fake_ATCB;
1857 end Initialize;
1859 -- Package elaboration
1861 begin
1862 declare
1863 Result : Interfaces.C.int;
1865 begin
1866 -- Mask Environment task for all signals. The original mask of the
1867 -- Environment task will be recovered by Interrupt_Server task
1868 -- during the elaboration of s-interr.adb.
1870 System.Interrupt_Management.Operations.Set_Interrupt_Mask
1871 (System.Interrupt_Management.Operations.All_Tasks_Mask'Access);
1873 -- Prepare the set of signals that should unblocked in all tasks
1875 Result := sigemptyset (Unblocked_Signal_Mask'Access);
1876 pragma Assert (Result = 0);
1878 for J in Interrupt_Management.Interrupt_ID loop
1879 if System.Interrupt_Management.Keep_Unmasked (J) then
1880 Result := sigaddset (Unblocked_Signal_Mask'Access, Signal (J));
1881 pragma Assert (Result = 0);
1882 end if;
1883 end loop;
1885 -- We need the following code to support automatic creation of fake
1886 -- ATCB's for C threads that call the Ada run-time system, even if
1887 -- we use a faster way of getting Self for real Ada tasks.
1889 Result := thr_keycreate (ATCB_Key'Access, System.Null_Address);
1890 pragma Assert (Result = 0);
1891 end;
1893 if Dispatching_Policy = 'F' then
1894 declare
1895 Result : Interfaces.C.long;
1896 Class_Info : aliased struct_pcinfo;
1897 Secs, Nsecs : Interfaces.C.long;
1899 begin
1901 -- If a pragma Time_Slice is specified, takes the value in account.
1903 if Time_Slice_Val > 0 then
1904 -- Convert Time_Slice_Val (microseconds) into seconds and
1905 -- nanoseconds
1907 Secs := Time_Slice_Val / 1_000_000;
1908 Nsecs := (Time_Slice_Val rem 1_000_000) * 1_000;
1910 -- Otherwise, default to no time slicing (i.e run until blocked)
1912 else
1913 Secs := RT_TQINF;
1914 Nsecs := RT_TQINF;
1915 end if;
1917 -- Get the real time class id.
1919 Class_Info.pc_clname (1) := 'R';
1920 Class_Info.pc_clname (2) := 'T';
1921 Class_Info.pc_clname (3) := ASCII.Nul;
1923 Result := priocntl (PC_VERSION, P_LWPID, P_MYID, PC_GETCID,
1924 Class_Info'Address);
1926 -- Request the real time class
1928 Prio_Param.pc_cid := Class_Info.pc_cid;
1929 Prio_Param.rt_pri := pri_t (Class_Info.rt_maxpri);
1930 Prio_Param.rt_tqsecs := Secs;
1931 Prio_Param.rt_tqnsecs := Nsecs;
1933 Result := priocntl (PC_VERSION, P_LWPID, P_MYID, PC_SETPARMS,
1934 Prio_Param'Address);
1936 Using_Real_Time_Class := Result /= -1;
1937 end;
1938 end if;
1939 end System.Task_Primitives.Operations;