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