1------------------------------------------------------------------------------ 2-- -- 3-- GNAT RUN-TIME LIBRARY (GNARL) COMPONENTS -- 4-- -- 5-- SYSTEM.TASKING.PROTECTED_OBJECTS.SINGLE_ENTRY -- 6-- -- 7-- B o d y -- 8-- -- 9-- Copyright (C) 1998-2018, 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 32pragma Style_Checks (All_Checks); 33-- Turn off subprogram ordering check, since restricted GNARLI subprograms are 34-- gathered together at end. 35 36-- This package provides an optimized version of Protected_Objects.Operations 37-- and Protected_Objects.Entries making the following assumptions: 38 39-- PO has only one entry 40-- There is only one caller at a time (No_Entry_Queue) 41-- There is no dynamic priority support (No_Dynamic_Priorities) 42-- No Abort Statements 43-- (No_Abort_Statements, Max_Asynchronous_Select_Nesting => 0) 44-- PO are at library level 45-- No Requeue 46-- None of the tasks will terminate (no need for finalization) 47 48-- This interface is intended to be used in the ravenscar and restricted 49-- profiles, the compiler is responsible for ensuring that the conditions 50-- mentioned above are respected, except for the No_Entry_Queue restriction 51-- that is checked dynamically in this package, since the check cannot be 52-- performed at compile time, and is relatively cheap (see PO_Do_Or_Queue, 53-- Service_Entry). 54 55pragma Polling (Off); 56-- Turn off polling, we do not want polling to take place during tasking 57-- operations. It can cause infinite loops and other problems. 58 59pragma Suppress (All_Checks); 60-- Why is this required ??? 61 62with Ada.Exceptions; 63 64with System.Task_Primitives.Operations; 65with System.Parameters; 66 67package body System.Tasking.Protected_Objects.Single_Entry is 68 69 package STPO renames System.Task_Primitives.Operations; 70 71 use Parameters; 72 73 ----------------------- 74 -- Local Subprograms -- 75 ----------------------- 76 77 procedure Send_Program_Error (Entry_Call : Entry_Call_Link); 78 pragma Inline (Send_Program_Error); 79 -- Raise Program_Error in the caller of the specified entry call 80 81 -------------------------- 82 -- Entry Calls Handling -- 83 -------------------------- 84 85 procedure Wakeup_Entry_Caller (Entry_Call : Entry_Call_Link); 86 pragma Inline (Wakeup_Entry_Caller); 87 -- This is called at the end of service of an entry call, to abort the 88 -- caller if he is in an abortable part, and to wake up the caller if he 89 -- is on Entry_Caller_Sleep. Call it holding the lock of Entry_Call.Self. 90 91 procedure Wait_For_Completion (Entry_Call : Entry_Call_Link); 92 pragma Inline (Wait_For_Completion); 93 -- This procedure suspends the calling task until the specified entry call 94 -- has either been completed or cancelled. On exit, the call will not be 95 -- queued. This waits for calls on protected entries. 96 -- Call this only when holding Self_ID locked. 97 98 procedure Check_Exception 99 (Self_ID : Task_Id; 100 Entry_Call : Entry_Call_Link); 101 pragma Inline (Check_Exception); 102 -- Raise any pending exception from the Entry_Call. This should be called 103 -- at the end of every compiler interface procedure that implements an 104 -- entry call. The caller should not be holding any locks, or there will 105 -- be deadlock. 106 107 procedure PO_Do_Or_Queue 108 (Object : Protection_Entry_Access; 109 Entry_Call : Entry_Call_Link); 110 -- This procedure executes or queues an entry call, depending on the status 111 -- of the corresponding barrier. The specified object is assumed locked. 112 113 --------------------- 114 -- Check_Exception -- 115 --------------------- 116 117 procedure Check_Exception 118 (Self_ID : Task_Id; 119 Entry_Call : Entry_Call_Link) 120 is 121 pragma Warnings (Off, Self_ID); 122 123 procedure Internal_Raise (X : Ada.Exceptions.Exception_Id); 124 pragma Import (C, Internal_Raise, "__gnat_raise_with_msg"); 125 126 use type Ada.Exceptions.Exception_Id; 127 128 E : constant Ada.Exceptions.Exception_Id := 129 Entry_Call.Exception_To_Raise; 130 131 begin 132 if E /= Ada.Exceptions.Null_Id then 133 Internal_Raise (E); 134 end if; 135 end Check_Exception; 136 137 ------------------------ 138 -- Send_Program_Error -- 139 ------------------------ 140 141 procedure Send_Program_Error (Entry_Call : Entry_Call_Link) is 142 Caller : constant Task_Id := Entry_Call.Self; 143 144 begin 145 Entry_Call.Exception_To_Raise := Program_Error'Identity; 146 147 if Single_Lock then 148 STPO.Lock_RTS; 149 end if; 150 151 STPO.Write_Lock (Caller); 152 Wakeup_Entry_Caller (Entry_Call); 153 STPO.Unlock (Caller); 154 155 if Single_Lock then 156 STPO.Unlock_RTS; 157 end if; 158 end Send_Program_Error; 159 160 ------------------------- 161 -- Wait_For_Completion -- 162 ------------------------- 163 164 procedure Wait_For_Completion (Entry_Call : Entry_Call_Link) is 165 Self_Id : constant Task_Id := Entry_Call.Self; 166 begin 167 Self_Id.Common.State := Entry_Caller_Sleep; 168 STPO.Sleep (Self_Id, Entry_Caller_Sleep); 169 Self_Id.Common.State := Runnable; 170 end Wait_For_Completion; 171 172 ------------------------- 173 -- Wakeup_Entry_Caller -- 174 ------------------------- 175 176 -- This is called at the end of service of an entry call, to abort the 177 -- caller if he is in an abortable part, and to wake up the caller if it 178 -- is on Entry_Caller_Sleep. It assumes that the call is already off-queue. 179 180 -- (This enforces the rule that a task must be off-queue if its state is 181 -- Done or Cancelled.) Call it holding the lock of Entry_Call.Self. 182 183 -- The caller is waiting on Entry_Caller_Sleep, in Wait_For_Completion. 184 185 procedure Wakeup_Entry_Caller 186 (Entry_Call : Entry_Call_Link) 187 is 188 Caller : constant Task_Id := Entry_Call.Self; 189 begin 190 pragma Assert 191 (Caller.Common.State /= Terminated and then 192 Caller.Common.State /= Unactivated); 193 Entry_Call.State := Done; 194 STPO.Wakeup (Caller, Entry_Caller_Sleep); 195 end Wakeup_Entry_Caller; 196 197 ----------------------- 198 -- Restricted GNARLI -- 199 ----------------------- 200 201 -------------------------------------------- 202 -- Exceptional_Complete_Single_Entry_Body -- 203 -------------------------------------------- 204 205 procedure Exceptional_Complete_Single_Entry_Body 206 (Object : Protection_Entry_Access; 207 Ex : Ada.Exceptions.Exception_Id) 208 is 209 begin 210 Object.Call_In_Progress.Exception_To_Raise := Ex; 211 end Exceptional_Complete_Single_Entry_Body; 212 213 --------------------------------- 214 -- Initialize_Protection_Entry -- 215 --------------------------------- 216 217 procedure Initialize_Protection_Entry 218 (Object : Protection_Entry_Access; 219 Ceiling_Priority : Integer; 220 Compiler_Info : System.Address; 221 Entry_Body : Entry_Body_Access) 222 is 223 begin 224 Initialize_Protection (Object.Common'Access, Ceiling_Priority); 225 226 Object.Compiler_Info := Compiler_Info; 227 Object.Call_In_Progress := null; 228 Object.Entry_Body := Entry_Body; 229 Object.Entry_Queue := null; 230 end Initialize_Protection_Entry; 231 232 ---------------- 233 -- Lock_Entry -- 234 ---------------- 235 236 -- Compiler interface only 237 238 -- Do not call this procedure from within the run-time system. 239 240 procedure Lock_Entry (Object : Protection_Entry_Access) is 241 begin 242 Lock (Object.Common'Access); 243 end Lock_Entry; 244 245 -------------------------- 246 -- Lock_Read_Only_Entry -- 247 -------------------------- 248 249 -- Compiler interface only 250 251 -- Do not call this procedure from within the runtime system 252 253 procedure Lock_Read_Only_Entry (Object : Protection_Entry_Access) is 254 begin 255 Lock_Read_Only (Object.Common'Access); 256 end Lock_Read_Only_Entry; 257 258 -------------------- 259 -- PO_Do_Or_Queue -- 260 -------------------- 261 262 procedure PO_Do_Or_Queue 263 (Object : Protection_Entry_Access; 264 Entry_Call : Entry_Call_Link) 265 is 266 Barrier_Value : Boolean; 267 268 begin 269 -- When the Action procedure for an entry body returns, it must be 270 -- completed (having called [Exceptional_]Complete_Entry_Body). 271 272 Barrier_Value := Object.Entry_Body.Barrier (Object.Compiler_Info, 1); 273 274 if Barrier_Value then 275 if Object.Call_In_Progress /= null then 276 277 -- This violates the No_Entry_Queue restriction, send 278 -- Program_Error to the caller. 279 280 Send_Program_Error (Entry_Call); 281 return; 282 end if; 283 284 Object.Call_In_Progress := Entry_Call; 285 Object.Entry_Body.Action 286 (Object.Compiler_Info, Entry_Call.Uninterpreted_Data, 1); 287 Object.Call_In_Progress := null; 288 289 if Single_Lock then 290 STPO.Lock_RTS; 291 end if; 292 293 STPO.Write_Lock (Entry_Call.Self); 294 Wakeup_Entry_Caller (Entry_Call); 295 STPO.Unlock (Entry_Call.Self); 296 297 if Single_Lock then 298 STPO.Unlock_RTS; 299 end if; 300 301 else 302 pragma Assert (Entry_Call.Mode = Simple_Call); 303 304 if Object.Entry_Queue /= null then 305 306 -- This violates the No_Entry_Queue restriction, send 307 -- Program_Error to the caller. 308 309 Send_Program_Error (Entry_Call); 310 return; 311 else 312 Object.Entry_Queue := Entry_Call; 313 end if; 314 315 end if; 316 317 exception 318 when others => 319 Send_Program_Error (Entry_Call); 320 end PO_Do_Or_Queue; 321 322 ---------------------------- 323 -- Protected_Single_Count -- 324 ---------------------------- 325 326 function Protected_Count_Entry (Object : Protection_Entry) return Natural is 327 begin 328 if Object.Entry_Queue /= null then 329 return 1; 330 else 331 return 0; 332 end if; 333 end Protected_Count_Entry; 334 335 --------------------------------- 336 -- Protected_Single_Entry_Call -- 337 --------------------------------- 338 339 procedure Protected_Single_Entry_Call 340 (Object : Protection_Entry_Access; 341 Uninterpreted_Data : System.Address) 342 is 343 Self_Id : constant Task_Id := STPO.Self; 344 Entry_Call : Entry_Call_Record renames Self_Id.Entry_Calls (1); 345 begin 346 -- If pragma Detect_Blocking is active then Program_Error must be 347 -- raised if this potentially blocking operation is called from a 348 -- protected action. 349 350 if Detect_Blocking 351 and then Self_Id.Common.Protected_Action_Nesting > 0 352 then 353 raise Program_Error with "potentially blocking operation"; 354 end if; 355 356 Lock_Entry (Object); 357 358 Entry_Call.Mode := Simple_Call; 359 Entry_Call.State := Now_Abortable; 360 Entry_Call.Uninterpreted_Data := Uninterpreted_Data; 361 Entry_Call.Exception_To_Raise := Ada.Exceptions.Null_Id; 362 363 PO_Do_Or_Queue (Object, Entry_Call'Access); 364 Unlock_Entry (Object); 365 366 -- The call is either `Done' or not. It cannot be cancelled since there 367 -- is no ATC construct. 368 369 pragma Assert (Entry_Call.State /= Cancelled); 370 371 if Entry_Call.State /= Done then 372 if Single_Lock then 373 STPO.Lock_RTS; 374 end if; 375 376 STPO.Write_Lock (Self_Id); 377 Wait_For_Completion (Entry_Call'Access); 378 STPO.Unlock (Self_Id); 379 380 if Single_Lock then 381 STPO.Unlock_RTS; 382 end if; 383 end if; 384 385 Check_Exception (Self_Id, Entry_Call'Access); 386 end Protected_Single_Entry_Call; 387 388 ----------------------------------- 389 -- Protected_Single_Entry_Caller -- 390 ----------------------------------- 391 392 function Protected_Single_Entry_Caller 393 (Object : Protection_Entry) return Task_Id 394 is 395 begin 396 return Object.Call_In_Progress.Self; 397 end Protected_Single_Entry_Caller; 398 399 ------------------- 400 -- Service_Entry -- 401 ------------------- 402 403 procedure Service_Entry (Object : Protection_Entry_Access) is 404 Entry_Call : constant Entry_Call_Link := Object.Entry_Queue; 405 Caller : Task_Id; 406 407 begin 408 if Entry_Call /= null 409 and then Object.Entry_Body.Barrier (Object.Compiler_Info, 1) 410 then 411 Object.Entry_Queue := null; 412 413 if Object.Call_In_Progress /= null then 414 415 -- Violation of No_Entry_Queue restriction, raise exception 416 417 Send_Program_Error (Entry_Call); 418 Unlock_Entry (Object); 419 return; 420 end if; 421 422 Object.Call_In_Progress := Entry_Call; 423 Object.Entry_Body.Action 424 (Object.Compiler_Info, Entry_Call.Uninterpreted_Data, 1); 425 Object.Call_In_Progress := null; 426 Caller := Entry_Call.Self; 427 Unlock_Entry (Object); 428 429 if Single_Lock then 430 STPO.Lock_RTS; 431 end if; 432 433 STPO.Write_Lock (Caller); 434 Wakeup_Entry_Caller (Entry_Call); 435 STPO.Unlock (Caller); 436 437 if Single_Lock then 438 STPO.Unlock_RTS; 439 end if; 440 441 else 442 -- Just unlock the entry 443 444 Unlock_Entry (Object); 445 end if; 446 447 exception 448 when others => 449 Send_Program_Error (Entry_Call); 450 Unlock_Entry (Object); 451 end Service_Entry; 452 453 ------------------ 454 -- Unlock_Entry -- 455 ------------------ 456 457 procedure Unlock_Entry (Object : Protection_Entry_Access) is 458 begin 459 Unlock (Object.Common'Access); 460 end Unlock_Entry; 461 462end System.Tasking.Protected_Objects.Single_Entry; 463