]> git.ipfire.org Git - thirdparty/gcc.git/blob - gcc/ada/s-tpobop.adb
Licensing changes to GPLv3 resp. GPLv3 with GCC Runtime Exception.
[thirdparty/gcc.git] / gcc / ada / s-tpobop.adb
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT RUN-TIME LIBRARY (GNARL) COMPONENTS --
4 -- --
5 -- SYSTEM.TASKING.PROTECTED_OBJECTS.OPERATIONS --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 1998-2009, 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 package contains all extended primitives related to Protected_Objects
33 -- with entries.
34
35 -- The handling of protected objects with no entries is done in
36 -- System.Tasking.Protected_Objects, the simple routines for protected
37 -- objects with entries in System.Tasking.Protected_Objects.Entries.
38
39 -- The split between Entries and Operations is needed to break circular
40 -- dependencies inside the run time.
41
42 -- This package contains all primitives related to Protected_Objects.
43 -- Note: the compiler generates direct calls to this interface, via Rtsfind.
44
45 with System.Task_Primitives.Operations;
46 with System.Tasking.Entry_Calls;
47 with System.Tasking.Queuing;
48 with System.Tasking.Rendezvous;
49 with System.Tasking.Utilities;
50 with System.Tasking.Debug;
51 with System.Parameters;
52 with System.Traces.Tasking;
53 with System.Restrictions;
54
55 with System.Tasking.Initialization;
56 pragma Elaborate_All (System.Tasking.Initialization);
57 -- Insures that tasking is initialized if any protected objects are created
58
59 package body System.Tasking.Protected_Objects.Operations is
60
61 package STPO renames System.Task_Primitives.Operations;
62
63 use Parameters;
64 use Task_Primitives;
65 use Ada.Exceptions;
66 use Entries;
67
68 use System.Restrictions;
69 use System.Restrictions.Rident;
70 use System.Traces;
71 use System.Traces.Tasking;
72
73 -----------------------
74 -- Local Subprograms --
75 -----------------------
76
77 procedure Update_For_Queue_To_PO
78 (Entry_Call : Entry_Call_Link;
79 With_Abort : Boolean);
80 pragma Inline (Update_For_Queue_To_PO);
81 -- Update the state of an existing entry call to reflect the fact that it
82 -- is being enqueued, based on whether the current queuing action is with
83 -- or without abort. Call this only while holding the PO's lock. It returns
84 -- with the PO's lock still held.
85
86 procedure Requeue_Call
87 (Self_Id : Task_Id;
88 Object : Protection_Entries_Access;
89 Entry_Call : Entry_Call_Link);
90 -- Handle requeue of Entry_Call.
91 -- In particular, queue the call if needed, or service it immediately
92 -- if possible.
93
94 ---------------------------------
95 -- Cancel_Protected_Entry_Call --
96 ---------------------------------
97
98 -- Compiler interface only (do not call from within the RTS)
99
100 -- This should have analogous effect to Cancel_Task_Entry_Call, setting
101 -- the value of Block.Cancelled instead of returning the parameter value
102 -- Cancelled.
103
104 -- The effect should be idempotent, since the call may already have been
105 -- dequeued.
106
107 -- Source code:
108
109 -- select r.e;
110 -- ...A...
111 -- then abort
112 -- ...B...
113 -- end select;
114
115 -- Expanded code:
116
117 -- declare
118 -- X : protected_entry_index := 1;
119 -- B80b : communication_block;
120 -- communication_blockIP (B80b);
121
122 -- begin
123 -- begin
124 -- A79b : label
125 -- A79b : declare
126 -- procedure _clean is
127 -- begin
128 -- if enqueued (B80b) then
129 -- cancel_protected_entry_call (B80b);
130 -- end if;
131 -- return;
132 -- end _clean;
133
134 -- begin
135 -- protected_entry_call (rTV!(r)._object'unchecked_access, X,
136 -- null_address, asynchronous_call, B80b, objectF => 0);
137 -- if enqueued (B80b) then
138 -- ...B...
139 -- end if;
140 -- at end
141 -- _clean;
142 -- end A79b;
143
144 -- exception
145 -- when _abort_signal =>
146 -- abort_undefer.all;
147 -- null;
148 -- end;
149
150 -- if not cancelled (B80b) then
151 -- x := ...A...
152 -- end if;
153 -- end;
154
155 -- If the entry call completes after we get into the abortable part,
156 -- Abort_Signal should be raised and ATC will take us to the at-end
157 -- handler, which will call _clean.
158
159 -- If the entry call returns with the call already completed, we can skip
160 -- this, and use the "if enqueued()" to go past the at-end handler, but we
161 -- will still call _clean.
162
163 -- If the abortable part completes before the entry call is Done, it will
164 -- call _clean.
165
166 -- If the entry call or the abortable part raises an exception,
167 -- we will still call _clean, but the value of Cancelled should not matter.
168
169 -- Whoever calls _clean first gets to decide whether the call
170 -- has been "cancelled".
171
172 -- Enqueued should be true if there is any chance that the call is still on
173 -- a queue. It seems to be safe to make it True if the call was Onqueue at
174 -- some point before return from Protected_Entry_Call.
175
176 -- Cancelled should be true iff the abortable part completed
177 -- and succeeded in cancelling the entry call before it completed.
178
179 -- ?????
180 -- The need for Enqueued is less obvious. The "if enqueued ()" tests are
181 -- not necessary, since Cancel_Protected_Entry_Call/Protected_Entry_Call
182 -- must do the same test internally, with locking. The one that makes
183 -- cancellation conditional may be a useful heuristic since at least 1/2
184 -- the time the call should be off-queue by that point. The other one seems
185 -- totally useless, since Protected_Entry_Call must do the same check and
186 -- then possibly wait for the call to be abortable, internally.
187
188 -- We can check Call.State here without locking the caller's mutex,
189 -- since the call must be over after returning from Wait_For_Completion.
190 -- No other task can access the call record at this point.
191
192 procedure Cancel_Protected_Entry_Call
193 (Block : in out Communication_Block) is
194 begin
195 Entry_Calls.Try_To_Cancel_Entry_Call (Block.Cancelled);
196 end Cancel_Protected_Entry_Call;
197
198 ---------------
199 -- Cancelled --
200 ---------------
201
202 function Cancelled (Block : Communication_Block) return Boolean is
203 begin
204 return Block.Cancelled;
205 end Cancelled;
206
207 -------------------------
208 -- Complete_Entry_Body --
209 -------------------------
210
211 procedure Complete_Entry_Body (Object : Protection_Entries_Access) is
212 begin
213 Exceptional_Complete_Entry_Body (Object, Ada.Exceptions.Null_Id);
214 end Complete_Entry_Body;
215
216 --------------
217 -- Enqueued --
218 --------------
219
220 function Enqueued (Block : Communication_Block) return Boolean is
221 begin
222 return Block.Enqueued;
223 end Enqueued;
224
225 -------------------------------------
226 -- Exceptional_Complete_Entry_Body --
227 -------------------------------------
228
229 procedure Exceptional_Complete_Entry_Body
230 (Object : Protection_Entries_Access;
231 Ex : Ada.Exceptions.Exception_Id)
232 is
233 procedure Transfer_Occurrence
234 (Target : Ada.Exceptions.Exception_Occurrence_Access;
235 Source : Ada.Exceptions.Exception_Occurrence);
236 pragma Import (C, Transfer_Occurrence, "__gnat_transfer_occurrence");
237
238 Entry_Call : constant Entry_Call_Link := Object.Call_In_Progress;
239 Self_Id : Task_Id;
240
241 begin
242 pragma Debug
243 (Debug.Trace (STPO.Self, "Exceptional_Complete_Entry_Body", 'P'));
244
245 -- We must have abort deferred, since we are inside a protected
246 -- operation.
247
248 if Entry_Call /= null then
249
250 -- The call was not requeued
251
252 Entry_Call.Exception_To_Raise := Ex;
253
254 if Ex /= Ada.Exceptions.Null_Id then
255
256 -- An exception was raised and abort was deferred, so adjust
257 -- before propagating, otherwise the task will stay with deferral
258 -- enabled for its remaining life.
259
260 Self_Id := STPO.Self;
261 Initialization.Undefer_Abort_Nestable (Self_Id);
262 Transfer_Occurrence
263 (Entry_Call.Self.Common.Compiler_Data.Current_Excep'Access,
264 Self_Id.Common.Compiler_Data.Current_Excep);
265 end if;
266
267 -- Wakeup_Entry_Caller will be called from PO_Do_Or_Queue or
268 -- PO_Service_Entries on return.
269
270 end if;
271
272 if Runtime_Traces then
273 Send_Trace_Info (PO_Done, Entry_Call.Self);
274 end if;
275 end Exceptional_Complete_Entry_Body;
276
277 --------------------
278 -- PO_Do_Or_Queue --
279 --------------------
280
281 procedure PO_Do_Or_Queue
282 (Self_ID : Task_Id;
283 Object : Protection_Entries_Access;
284 Entry_Call : Entry_Call_Link)
285 is
286 E : constant Protected_Entry_Index :=
287 Protected_Entry_Index (Entry_Call.E);
288 Barrier_Value : Boolean;
289
290 begin
291 -- When the Action procedure for an entry body returns, it is either
292 -- completed (having called [Exceptional_]Complete_Entry_Body) or it
293 -- is queued, having executed a requeue statement.
294
295 Barrier_Value :=
296 Object.Entry_Bodies (
297 Object.Find_Body_Index (Object.Compiler_Info, E)).
298 Barrier (Object.Compiler_Info, E);
299
300 if Barrier_Value then
301
302 -- Not abortable while service is in progress
303
304 if Entry_Call.State = Now_Abortable then
305 Entry_Call.State := Was_Abortable;
306 end if;
307
308 Object.Call_In_Progress := Entry_Call;
309
310 pragma Debug
311 (Debug.Trace (Self_ID, "PODOQ: start entry body", 'P'));
312 Object.Entry_Bodies (
313 Object.Find_Body_Index (Object.Compiler_Info, E)).Action (
314 Object.Compiler_Info, Entry_Call.Uninterpreted_Data, E);
315
316 if Object.Call_In_Progress /= null then
317
318 -- Body of current entry served call to completion
319
320 Object.Call_In_Progress := null;
321
322 if Single_Lock then
323 STPO.Lock_RTS;
324 end if;
325
326 STPO.Write_Lock (Entry_Call.Self);
327 Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Done);
328 STPO.Unlock (Entry_Call.Self);
329
330 if Single_Lock then
331 STPO.Unlock_RTS;
332 end if;
333
334 else
335 Requeue_Call (Self_ID, Object, Entry_Call);
336 end if;
337
338 elsif Entry_Call.Mode /= Conditional_Call
339 or else not Entry_Call.With_Abort
340 then
341
342 if Run_Time_Restrictions.Set (Max_Entry_Queue_Length)
343 and then
344 Run_Time_Restrictions.Value (Max_Entry_Queue_Length) <=
345 Queuing.Count_Waiting (Object.Entry_Queues (E))
346 then
347 -- This violates the Max_Entry_Queue_Length restriction,
348 -- raise Program_Error.
349
350 Entry_Call.Exception_To_Raise := Program_Error'Identity;
351
352 if Single_Lock then
353 STPO.Lock_RTS;
354 end if;
355
356 STPO.Write_Lock (Entry_Call.Self);
357 Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Done);
358 STPO.Unlock (Entry_Call.Self);
359
360 if Single_Lock then
361 STPO.Unlock_RTS;
362 end if;
363 else
364 Queuing.Enqueue (Object.Entry_Queues (E), Entry_Call);
365 Update_For_Queue_To_PO (Entry_Call, Entry_Call.With_Abort);
366 end if;
367 else
368 -- Conditional_Call and With_Abort
369
370 if Single_Lock then
371 STPO.Lock_RTS;
372 end if;
373
374 STPO.Write_Lock (Entry_Call.Self);
375 pragma Assert (Entry_Call.State >= Was_Abortable);
376 Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Cancelled);
377 STPO.Unlock (Entry_Call.Self);
378
379 if Single_Lock then
380 STPO.Unlock_RTS;
381 end if;
382 end if;
383
384 exception
385 when others =>
386 Queuing.Broadcast_Program_Error (Self_ID, Object, Entry_Call);
387 end PO_Do_Or_Queue;
388
389 ------------------------
390 -- PO_Service_Entries --
391 ------------------------
392
393 procedure PO_Service_Entries
394 (Self_ID : Task_Id;
395 Object : Entries.Protection_Entries_Access;
396 Unlock_Object : Boolean := True)
397 is
398 E : Protected_Entry_Index;
399 Caller : Task_Id;
400 Entry_Call : Entry_Call_Link;
401
402 begin
403 loop
404 Queuing.Select_Protected_Entry_Call (Self_ID, Object, Entry_Call);
405
406 exit when Entry_Call = null;
407
408 E := Protected_Entry_Index (Entry_Call.E);
409
410 -- Not abortable while service is in progress
411
412 if Entry_Call.State = Now_Abortable then
413 Entry_Call.State := Was_Abortable;
414 end if;
415
416 Object.Call_In_Progress := Entry_Call;
417
418 begin
419 if Runtime_Traces then
420 Send_Trace_Info (PO_Run, Self_ID,
421 Entry_Call.Self, Entry_Index (E));
422 end if;
423
424 pragma Debug
425 (Debug.Trace (Self_ID, "POSE: start entry body", 'P'));
426
427 Object.Entry_Bodies
428 (Object.Find_Body_Index (Object.Compiler_Info, E)).Action
429 (Object.Compiler_Info, Entry_Call.Uninterpreted_Data, E);
430
431 exception
432 when others =>
433 Queuing.Broadcast_Program_Error
434 (Self_ID, Object, Entry_Call);
435 end;
436
437 if Object.Call_In_Progress = null then
438 Requeue_Call (Self_ID, Object, Entry_Call);
439 exit when Entry_Call.State = Cancelled;
440
441 else
442 Object.Call_In_Progress := null;
443 Caller := Entry_Call.Self;
444
445 if Single_Lock then
446 STPO.Lock_RTS;
447 end if;
448
449 STPO.Write_Lock (Caller);
450 Initialization.Wakeup_Entry_Caller (Self_ID, Entry_Call, Done);
451 STPO.Unlock (Caller);
452
453 if Single_Lock then
454 STPO.Unlock_RTS;
455 end if;
456 end if;
457 end loop;
458
459 if Unlock_Object then
460 Unlock_Entries (Object);
461 end if;
462 end PO_Service_Entries;
463
464 ---------------------
465 -- Protected_Count --
466 ---------------------
467
468 function Protected_Count
469 (Object : Protection_Entries'Class;
470 E : Protected_Entry_Index) return Natural
471 is
472 begin
473 return Queuing.Count_Waiting (Object.Entry_Queues (E));
474 end Protected_Count;
475
476 --------------------------
477 -- Protected_Entry_Call --
478 --------------------------
479
480 -- Compiler interface only (do not call from within the RTS)
481
482 -- select r.e;
483 -- ...A...
484 -- else
485 -- ...B...
486 -- end select;
487
488 -- declare
489 -- X : protected_entry_index := 1;
490 -- B85b : communication_block;
491 -- communication_blockIP (B85b);
492
493 -- begin
494 -- protected_entry_call (rTV!(r)._object'unchecked_access, X,
495 -- null_address, conditional_call, B85b, objectF => 0);
496
497 -- if cancelled (B85b) then
498 -- ...B...
499 -- else
500 -- ...A...
501 -- end if;
502 -- end;
503
504 -- See also Cancel_Protected_Entry_Call for code expansion of asynchronous
505 -- entry call.
506
507 -- The initial part of this procedure does not need to lock the calling
508 -- task's ATCB, up to the point where the call record first may be queued
509 -- (PO_Do_Or_Queue), since before that no other task will have access to
510 -- the record.
511
512 -- If this is a call made inside of an abort deferred region, the call
513 -- should be never abortable.
514
515 -- If the call was not queued abortably, we need to wait until it is before
516 -- proceeding with the abortable part.
517
518 -- There are some heuristics here, just to save time for frequently
519 -- occurring cases. For example, we check Initially_Abortable to try to
520 -- avoid calling the procedure Wait_Until_Abortable, since the normal case
521 -- for async. entry calls is to be queued abortably.
522
523 -- Another heuristic uses the Block.Enqueued to try to avoid calling
524 -- Cancel_Protected_Entry_Call if the call can be served immediately.
525
526 procedure Protected_Entry_Call
527 (Object : Protection_Entries_Access;
528 E : Protected_Entry_Index;
529 Uninterpreted_Data : System.Address;
530 Mode : Call_Modes;
531 Block : out Communication_Block)
532 is
533 Self_ID : constant Task_Id := STPO.Self;
534 Entry_Call : Entry_Call_Link;
535 Initially_Abortable : Boolean;
536 Ceiling_Violation : Boolean;
537
538 begin
539 pragma Debug
540 (Debug.Trace (Self_ID, "Protected_Entry_Call", 'P'));
541
542 if Runtime_Traces then
543 Send_Trace_Info (PO_Call, Entry_Index (E));
544 end if;
545
546 if Self_ID.ATC_Nesting_Level = ATC_Level'Last then
547 raise Storage_Error with "not enough ATC nesting levels";
548 end if;
549
550 -- If pragma Detect_Blocking is active then Program_Error must be
551 -- raised if this potentially blocking operation is called from a
552 -- protected action.
553
554 if Detect_Blocking
555 and then Self_ID.Common.Protected_Action_Nesting > 0
556 then
557 raise Program_Error with "potentially blocking operation";
558 end if;
559
560 -- Self_ID.Deferral_Level should be 0, except when called from Finalize,
561 -- where abort is already deferred.
562
563 Initialization.Defer_Abort_Nestable (Self_ID);
564 Lock_Entries (Object, Ceiling_Violation);
565
566 if Ceiling_Violation then
567
568 -- Failed ceiling check
569
570 Initialization.Undefer_Abort_Nestable (Self_ID);
571 raise Program_Error;
572 end if;
573
574 Block.Self := Self_ID;
575 Self_ID.ATC_Nesting_Level := Self_ID.ATC_Nesting_Level + 1;
576 pragma Debug
577 (Debug.Trace (Self_ID, "PEC: entered ATC level: " &
578 ATC_Level'Image (Self_ID.ATC_Nesting_Level), 'A'));
579 Entry_Call :=
580 Self_ID.Entry_Calls (Self_ID.ATC_Nesting_Level)'Access;
581 Entry_Call.Next := null;
582 Entry_Call.Mode := Mode;
583 Entry_Call.Cancellation_Attempted := False;
584
585 if Self_ID.Deferral_Level > 1 then
586 Entry_Call.State := Never_Abortable;
587 else
588 Entry_Call.State := Now_Abortable;
589 end if;
590
591 Entry_Call.E := Entry_Index (E);
592 Entry_Call.Prio := STPO.Get_Priority (Self_ID);
593 Entry_Call.Uninterpreted_Data := Uninterpreted_Data;
594 Entry_Call.Called_PO := To_Address (Object);
595 Entry_Call.Called_Task := null;
596 Entry_Call.Exception_To_Raise := Ada.Exceptions.Null_Id;
597 Entry_Call.With_Abort := True;
598
599 PO_Do_Or_Queue (Self_ID, Object, Entry_Call);
600 Initially_Abortable := Entry_Call.State = Now_Abortable;
601 PO_Service_Entries (Self_ID, Object);
602
603 -- Try to prevent waiting later (in Try_To_Cancel_Protected_Entry_Call)
604 -- for completed or cancelled calls. (This is a heuristic, only.)
605
606 if Entry_Call.State >= Done then
607
608 -- Once State >= Done it will not change any more
609
610 if Single_Lock then
611 STPO.Lock_RTS;
612 end if;
613
614 STPO.Write_Lock (Self_ID);
615 Utilities.Exit_One_ATC_Level (Self_ID);
616 STPO.Unlock (Self_ID);
617
618 if Single_Lock then
619 STPO.Unlock_RTS;
620 end if;
621
622 Block.Enqueued := False;
623 Block.Cancelled := Entry_Call.State = Cancelled;
624 Initialization.Undefer_Abort_Nestable (Self_ID);
625 Entry_Calls.Check_Exception (Self_ID, Entry_Call);
626 return;
627
628 else
629 -- In this case we cannot conclude anything, since State can change
630 -- concurrently.
631
632 null;
633 end if;
634
635 -- Now for the general case
636
637 if Mode = Asynchronous_Call then
638
639 -- Try to avoid an expensive call
640
641 if not Initially_Abortable then
642 if Single_Lock then
643 STPO.Lock_RTS;
644 Entry_Calls.Wait_Until_Abortable (Self_ID, Entry_Call);
645 STPO.Unlock_RTS;
646 else
647 Entry_Calls.Wait_Until_Abortable (Self_ID, Entry_Call);
648 end if;
649 end if;
650
651 elsif Mode < Asynchronous_Call then
652
653 -- Simple_Call or Conditional_Call
654
655 if Single_Lock then
656 STPO.Lock_RTS;
657 Entry_Calls.Wait_For_Completion (Entry_Call);
658 STPO.Unlock_RTS;
659
660 else
661 STPO.Write_Lock (Self_ID);
662 Entry_Calls.Wait_For_Completion (Entry_Call);
663 STPO.Unlock (Self_ID);
664 end if;
665
666 Block.Cancelled := Entry_Call.State = Cancelled;
667
668 else
669 pragma Assert (False);
670 null;
671 end if;
672
673 Initialization.Undefer_Abort_Nestable (Self_ID);
674 Entry_Calls.Check_Exception (Self_ID, Entry_Call);
675 end Protected_Entry_Call;
676
677 ------------------
678 -- Requeue_Call --
679 ------------------
680
681 procedure Requeue_Call
682 (Self_Id : Task_Id;
683 Object : Protection_Entries_Access;
684 Entry_Call : Entry_Call_Link)
685 is
686 New_Object : Protection_Entries_Access;
687 Ceiling_Violation : Boolean;
688 Result : Boolean;
689 E : Protected_Entry_Index;
690
691 begin
692 New_Object := To_Protection (Entry_Call.Called_PO);
693
694 if New_Object = null then
695
696 -- Call is to be requeued to a task entry
697
698 if Single_Lock then
699 STPO.Lock_RTS;
700 end if;
701
702 Result := Rendezvous.Task_Do_Or_Queue (Self_Id, Entry_Call);
703
704 if not Result then
705 Queuing.Broadcast_Program_Error
706 (Self_Id, Object, Entry_Call, RTS_Locked => True);
707 end if;
708
709 if Single_Lock then
710 STPO.Unlock_RTS;
711 end if;
712
713 else
714 -- Call should be requeued to a PO
715
716 if Object /= New_Object then
717
718 -- Requeue is to different PO
719
720 Lock_Entries (New_Object, Ceiling_Violation);
721
722 if Ceiling_Violation then
723 Object.Call_In_Progress := null;
724 Queuing.Broadcast_Program_Error (Self_Id, Object, Entry_Call);
725
726 else
727 PO_Do_Or_Queue (Self_Id, New_Object, Entry_Call);
728 PO_Service_Entries (Self_Id, New_Object);
729 end if;
730
731 else
732 -- Requeue is to same protected object
733
734 -- ??? Try to compensate apparent failure of the scheduler on some
735 -- OS (e.g VxWorks) to give higher priority tasks a chance to run
736 -- (see CXD6002).
737
738 STPO.Yield (False);
739
740 if Entry_Call.With_Abort
741 and then Entry_Call.Cancellation_Attempted
742 then
743 -- If this is a requeue with abort and someone tried to cancel
744 -- this call, cancel it at this point.
745
746 Entry_Call.State := Cancelled;
747 return;
748 end if;
749
750 if not Entry_Call.With_Abort
751 or else Entry_Call.Mode /= Conditional_Call
752 then
753 E := Protected_Entry_Index (Entry_Call.E);
754
755 if Run_Time_Restrictions.Set (Max_Entry_Queue_Length)
756 and then
757 Run_Time_Restrictions.Value (Max_Entry_Queue_Length) <=
758 Queuing.Count_Waiting (Object.Entry_Queues (E))
759 then
760 -- This violates the Max_Entry_Queue_Length restriction,
761 -- raise Program_Error.
762
763 Entry_Call.Exception_To_Raise := Program_Error'Identity;
764
765 if Single_Lock then
766 STPO.Lock_RTS;
767 end if;
768
769 STPO.Write_Lock (Entry_Call.Self);
770 Initialization.Wakeup_Entry_Caller
771 (Self_Id, Entry_Call, Done);
772 STPO.Unlock (Entry_Call.Self);
773
774 if Single_Lock then
775 STPO.Unlock_RTS;
776 end if;
777
778 else
779 Queuing.Enqueue
780 (New_Object.Entry_Queues (E), Entry_Call);
781 Update_For_Queue_To_PO (Entry_Call, Entry_Call.With_Abort);
782 end if;
783
784 else
785 PO_Do_Or_Queue (Self_Id, New_Object, Entry_Call);
786 end if;
787 end if;
788 end if;
789 end Requeue_Call;
790
791 ----------------------------
792 -- Protected_Entry_Caller --
793 ----------------------------
794
795 function Protected_Entry_Caller
796 (Object : Protection_Entries'Class) return Task_Id is
797 begin
798 return Object.Call_In_Progress.Self;
799 end Protected_Entry_Caller;
800
801 -----------------------------
802 -- Requeue_Protected_Entry --
803 -----------------------------
804
805 -- Compiler interface only (do not call from within the RTS)
806
807 -- entry e when b is
808 -- begin
809 -- b := false;
810 -- ...A...
811 -- requeue e2;
812 -- end e;
813
814 -- procedure rPT__E10b (O : address; P : address; E :
815 -- protected_entry_index) is
816 -- type rTVP is access rTV;
817 -- freeze rTVP []
818 -- _object : rTVP := rTVP!(O);
819 -- begin
820 -- declare
821 -- rR : protection renames _object._object;
822 -- vP : integer renames _object.v;
823 -- bP : boolean renames _object.b;
824 -- begin
825 -- b := false;
826 -- ...A...
827 -- requeue_protected_entry (rR'unchecked_access, rR'
828 -- unchecked_access, 2, false, objectF => 0, new_objectF =>
829 -- 0);
830 -- return;
831 -- end;
832 -- complete_entry_body (_object._object'unchecked_access, objectF =>
833 -- 0);
834 -- return;
835 -- exception
836 -- when others =>
837 -- abort_undefer.all;
838 -- exceptional_complete_entry_body (_object._object'
839 -- unchecked_access, current_exception, objectF => 0);
840 -- return;
841 -- end rPT__E10b;
842
843 procedure Requeue_Protected_Entry
844 (Object : Protection_Entries_Access;
845 New_Object : Protection_Entries_Access;
846 E : Protected_Entry_Index;
847 With_Abort : Boolean)
848 is
849 Entry_Call : constant Entry_Call_Link := Object.Call_In_Progress;
850
851 begin
852 pragma Debug
853 (Debug.Trace (STPO.Self, "Requeue_Protected_Entry", 'P'));
854 pragma Assert (STPO.Self.Deferral_Level > 0);
855
856 Entry_Call.E := Entry_Index (E);
857 Entry_Call.Called_PO := To_Address (New_Object);
858 Entry_Call.Called_Task := null;
859 Entry_Call.With_Abort := With_Abort;
860 Object.Call_In_Progress := null;
861 end Requeue_Protected_Entry;
862
863 -------------------------------------
864 -- Requeue_Task_To_Protected_Entry --
865 -------------------------------------
866
867 -- Compiler interface only (do not call from within the RTS)
868
869 -- accept e1 do
870 -- ...A...
871 -- requeue r.e2;
872 -- end e1;
873
874 -- A79b : address;
875 -- L78b : label
876
877 -- begin
878 -- accept_call (1, A79b);
879 -- ...A...
880 -- requeue_task_to_protected_entry (rTV!(r)._object'
881 -- unchecked_access, 2, false, new_objectF => 0);
882 -- goto L78b;
883 -- <<L78b>>
884 -- complete_rendezvous;
885
886 -- exception
887 -- when all others =>
888 -- exceptional_complete_rendezvous (get_gnat_exception);
889 -- end;
890
891 procedure Requeue_Task_To_Protected_Entry
892 (New_Object : Protection_Entries_Access;
893 E : Protected_Entry_Index;
894 With_Abort : Boolean)
895 is
896 Self_ID : constant Task_Id := STPO.Self;
897 Entry_Call : constant Entry_Call_Link := Self_ID.Common.Call;
898
899 begin
900 Initialization.Defer_Abort (Self_ID);
901
902 -- We do not need to lock Self_ID here since the call is not abortable
903 -- at this point, and therefore, the caller cannot cancel the call.
904
905 Entry_Call.Needs_Requeue := True;
906 Entry_Call.With_Abort := With_Abort;
907 Entry_Call.Called_PO := To_Address (New_Object);
908 Entry_Call.Called_Task := null;
909 Entry_Call.E := Entry_Index (E);
910 Initialization.Undefer_Abort (Self_ID);
911 end Requeue_Task_To_Protected_Entry;
912
913 ---------------------
914 -- Service_Entries --
915 ---------------------
916
917 procedure Service_Entries (Object : Protection_Entries_Access) is
918 Self_ID : constant Task_Id := STPO.Self;
919 begin
920 PO_Service_Entries (Self_ID, Object);
921 end Service_Entries;
922
923 --------------------------------
924 -- Timed_Protected_Entry_Call --
925 --------------------------------
926
927 -- Compiler interface only (do not call from within the RTS)
928
929 procedure Timed_Protected_Entry_Call
930 (Object : Protection_Entries_Access;
931 E : Protected_Entry_Index;
932 Uninterpreted_Data : System.Address;
933 Timeout : Duration;
934 Mode : Delay_Modes;
935 Entry_Call_Successful : out Boolean)
936 is
937 Self_Id : constant Task_Id := STPO.Self;
938 Entry_Call : Entry_Call_Link;
939 Ceiling_Violation : Boolean;
940
941 Yielded : Boolean;
942 pragma Unreferenced (Yielded);
943
944 begin
945 if Self_Id.ATC_Nesting_Level = ATC_Level'Last then
946 raise Storage_Error with "not enough ATC nesting levels";
947 end if;
948
949 -- If pragma Detect_Blocking is active then Program_Error must be
950 -- raised if this potentially blocking operation is called from a
951 -- protected action.
952
953 if Detect_Blocking
954 and then Self_Id.Common.Protected_Action_Nesting > 0
955 then
956 raise Program_Error with "potentially blocking operation";
957 end if;
958
959 if Runtime_Traces then
960 Send_Trace_Info (POT_Call, Entry_Index (E), Timeout);
961 end if;
962
963 Initialization.Defer_Abort (Self_Id);
964 Lock_Entries (Object, Ceiling_Violation);
965
966 if Ceiling_Violation then
967 Initialization.Undefer_Abort (Self_Id);
968 raise Program_Error;
969 end if;
970
971 Self_Id.ATC_Nesting_Level := Self_Id.ATC_Nesting_Level + 1;
972 pragma Debug
973 (Debug.Trace (Self_Id, "TPEC: exited to ATC level: " &
974 ATC_Level'Image (Self_Id.ATC_Nesting_Level), 'A'));
975 Entry_Call :=
976 Self_Id.Entry_Calls (Self_Id.ATC_Nesting_Level)'Access;
977 Entry_Call.Next := null;
978 Entry_Call.Mode := Timed_Call;
979 Entry_Call.Cancellation_Attempted := False;
980
981 if Self_Id.Deferral_Level > 1 then
982 Entry_Call.State := Never_Abortable;
983 else
984 Entry_Call.State := Now_Abortable;
985 end if;
986
987 Entry_Call.E := Entry_Index (E);
988 Entry_Call.Prio := STPO.Get_Priority (Self_Id);
989 Entry_Call.Uninterpreted_Data := Uninterpreted_Data;
990 Entry_Call.Called_PO := To_Address (Object);
991 Entry_Call.Called_Task := null;
992 Entry_Call.Exception_To_Raise := Ada.Exceptions.Null_Id;
993 Entry_Call.With_Abort := True;
994
995 PO_Do_Or_Queue (Self_Id, Object, Entry_Call);
996 PO_Service_Entries (Self_Id, Object);
997
998 if Single_Lock then
999 STPO.Lock_RTS;
1000 else
1001 STPO.Write_Lock (Self_Id);
1002 end if;
1003
1004 -- Try to avoid waiting for completed or cancelled calls
1005
1006 if Entry_Call.State >= Done then
1007 Utilities.Exit_One_ATC_Level (Self_Id);
1008
1009 if Single_Lock then
1010 STPO.Unlock_RTS;
1011 else
1012 STPO.Unlock (Self_Id);
1013 end if;
1014
1015 Entry_Call_Successful := Entry_Call.State = Done;
1016 Initialization.Undefer_Abort (Self_Id);
1017 Entry_Calls.Check_Exception (Self_Id, Entry_Call);
1018 return;
1019 end if;
1020
1021 Entry_Calls.Wait_For_Completion_With_Timeout
1022 (Entry_Call, Timeout, Mode, Yielded);
1023
1024 if Single_Lock then
1025 STPO.Unlock_RTS;
1026 else
1027 STPO.Unlock (Self_Id);
1028 end if;
1029
1030 -- ??? Do we need to yield in case Yielded is False
1031
1032 Initialization.Undefer_Abort (Self_Id);
1033 Entry_Call_Successful := Entry_Call.State = Done;
1034 Entry_Calls.Check_Exception (Self_Id, Entry_Call);
1035 end Timed_Protected_Entry_Call;
1036
1037 ----------------------------
1038 -- Update_For_Queue_To_PO --
1039 ----------------------------
1040
1041 -- Update the state of an existing entry call, based on
1042 -- whether the current queuing action is with or without abort.
1043 -- Call this only while holding the server's lock.
1044 -- It returns with the server's lock released.
1045
1046 New_State : constant array (Boolean, Entry_Call_State)
1047 of Entry_Call_State :=
1048 (True =>
1049 (Never_Abortable => Never_Abortable,
1050 Not_Yet_Abortable => Now_Abortable,
1051 Was_Abortable => Now_Abortable,
1052 Now_Abortable => Now_Abortable,
1053 Done => Done,
1054 Cancelled => Cancelled),
1055 False =>
1056 (Never_Abortable => Never_Abortable,
1057 Not_Yet_Abortable => Not_Yet_Abortable,
1058 Was_Abortable => Was_Abortable,
1059 Now_Abortable => Now_Abortable,
1060 Done => Done,
1061 Cancelled => Cancelled)
1062 );
1063
1064 procedure Update_For_Queue_To_PO
1065 (Entry_Call : Entry_Call_Link;
1066 With_Abort : Boolean)
1067 is
1068 Old : constant Entry_Call_State := Entry_Call.State;
1069
1070 begin
1071 pragma Assert (Old < Done);
1072
1073 Entry_Call.State := New_State (With_Abort, Entry_Call.State);
1074
1075 if Entry_Call.Mode = Asynchronous_Call then
1076 if Old < Was_Abortable and then
1077 Entry_Call.State = Now_Abortable
1078 then
1079 if Single_Lock then
1080 STPO.Lock_RTS;
1081 end if;
1082
1083 STPO.Write_Lock (Entry_Call.Self);
1084
1085 if Entry_Call.Self.Common.State = Async_Select_Sleep then
1086 STPO.Wakeup (Entry_Call.Self, Async_Select_Sleep);
1087 end if;
1088
1089 STPO.Unlock (Entry_Call.Self);
1090
1091 if Single_Lock then
1092 STPO.Unlock_RTS;
1093 end if;
1094
1095 end if;
1096
1097 elsif Entry_Call.Mode = Conditional_Call then
1098 pragma Assert (Entry_Call.State < Was_Abortable);
1099 null;
1100 end if;
1101 end Update_For_Queue_To_PO;
1102
1103 end System.Tasking.Protected_Objects.Operations;