1 ------------------------------------------------------------------------------
3 -- GNU ADA RUN-TIME LIBRARY (GNARL) COMPONENTS --
5 -- S Y S T E M . T A S K I N G . A S Y N C _ D E L A Y S --
11 -- Copyright (C) 1998-2001 Ada Core Technologies, Inc. --
13 -- GNARL is free software; you can redistribute it and/or modify it under --
14 -- terms of the GNU General Public License as published by the Free Soft- --
15 -- ware Foundation; either version 2, or (at your option) any later ver- --
16 -- sion. GNARL is distributed in the hope that it will be useful, but WITH- --
17 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
18 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
19 -- for more details. You should have received a copy of the GNU General --
20 -- Public License distributed with GNARL; see file COPYING. If not, write --
21 -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, --
22 -- MA 02111-1307, USA. --
24 -- As a special exception, if other files instantiate generics from this --
25 -- unit, or you link this unit with other files to produce an executable, --
26 -- this unit does not by itself cause the resulting executable to be --
27 -- covered by the GNU General Public License. This exception does not --
28 -- however invalidate any other reasons why the executable file might be --
29 -- covered by the GNU Public License. --
31 -- GNARL was developed by the GNARL team at Florida State University. It is --
32 -- now maintained by Ada Core Technologies Inc. in cooperation with Florida --
33 -- State University (http://www.gnat.com). --
35 ------------------------------------------------------------------------------
38 -- Turn off polling, we do not want ATC polling to take place during
39 -- tasking operations. It causes infinite loops and other problems.
42 -- Used for Raise_Exception
44 with System.Task_Primitives.Operations;
45 -- Used for Write_Lock,
54 with System.Tasking.Utilities;
55 -- Used for Make_Independent
57 with System.Tasking.Initialization;
58 -- Used for Defer_Abort
61 with System.Tasking.Debug;
64 with System.OS_Primitives;
65 -- used for Max_Sensible_Delay
67 with Ada.Task_Identification;
68 -- used for Task_ID type
70 with Unchecked_Conversion;
72 package body System.Tasking.Async_Delays is
74 package STPO renames System.Task_Primitives.Operations;
75 package ST renames System.Tasking;
76 package STU renames System.Tasking.Utilities;
77 package STI renames System.Tasking.Initialization;
78 package OSP renames System.OS_Primitives;
80 function To_System is new Unchecked_Conversion
81 (Ada.Task_Identification.Task_Id, Task_ID);
83 Timer_Server_ID : ST.Task_ID;
85 Timer_Attention : Boolean := False;
86 pragma Atomic (Timer_Attention);
89 pragma Interrupt_Priority (System.Any_Priority'Last);
92 -- The timer queue is a circular doubly linked list, ordered by absolute
93 -- wakeup time. The first item in the queue is Timer_Queue.Succ.
94 -- It is given a Resume_Time that is larger than any legitimate wakeup
95 -- time, so that the ordered insertion will always stop searching when it
96 -- gets back to the queue header block.
98 Timer_Queue : aliased Delay_Block;
100 ------------------------
101 -- Cancel_Async_Delay --
102 ------------------------
104 -- This should (only) be called from the compiler-generated cleanup routine
105 -- for an async. select statement with delay statement as trigger. The
106 -- effect should be to remove the delay from the timer queue, and exit one
107 -- ATC nesting level.
108 -- The usage and logic are similar to Cancel_Protected_Entry_Call, but
109 -- simplified because this is not a true entry call.
111 procedure Cancel_Async_Delay (D : Delay_Block_Access) is
112 Dpred : Delay_Block_Access;
113 Dsucc : Delay_Block_Access;
116 -- Note that we mark the delay as being cancelled
117 -- using a level value that is reserved.
119 -- make this operation idempotent
121 if D.Level = ATC_Level_Infinity then
125 D.Level := ATC_Level_Infinity;
127 -- remove self from timer queue
129 STI.Defer_Abort_Nestable (D.Self_Id);
130 STPO.Write_Lock (Timer_Server_ID);
137 STPO.Unlock (Timer_Server_ID);
139 -- Note that the above deletion code is required to be
140 -- idempotent, since the block may have been dequeued
141 -- previously by the Timer_Server.
143 -- leave the asynchronous select
145 STPO.Write_Lock (D.Self_Id);
146 STU.Exit_One_ATC_Level (D.Self_Id);
147 STPO.Unlock (D.Self_Id);
148 STI.Undefer_Abort_Nestable (D.Self_Id);
149 end Cancel_Async_Delay;
151 ---------------------------
152 -- Enqueue_Time_Duration --
153 ---------------------------
155 function Enqueue_Duration
157 D : Delay_Block_Access)
167 STI.Defer_Abort (STPO.Self);
169 (STPO.Monotonic_Clock
170 + Duration'Min (T, OSP.Max_Sensible_Delay), D);
173 end Enqueue_Duration;
179 -- Allocate a queue element for the wakeup time T and put it in the
180 -- queue in wakeup time order. Assume we are on an asynchronous
181 -- select statement with delay trigger. Put the calling task to
182 -- sleep until either the delay expires or is cancelled.
184 -- We use one entry call record for this delay, since we have
185 -- to increment the ATC nesting level, but since it is not a
186 -- real entry call we do not need to use any of the fields of
187 -- the call record. The following code implements a subset of
188 -- the actions for the asynchronous case of Protected_Entry_Call,
189 -- much simplified since we know this never blocks, and does not
190 -- have the full semantics of a protected entry call.
192 procedure Time_Enqueue
194 D : Delay_Block_Access)
196 Self_Id : constant Task_ID := STPO.Self;
197 Q : Delay_Block_Access;
200 -- for visibility of operator "="
203 pragma Debug (Debug.Trace (Self_Id, "Async_Delay", 'P'));
204 pragma Assert (Self_Id.Deferral_Level = 1,
205 "async delay from within abort-deferred region");
207 if Self_Id.ATC_Nesting_Level = ATC_Level'Last then
208 Ada.Exceptions.Raise_Exception (Storage_Error'Identity,
209 "not enough ATC nesting levels");
212 Self_Id.ATC_Nesting_Level := Self_Id.ATC_Nesting_Level + 1;
215 (Debug.Trace (Self_Id, "ASD: entered ATC level: " &
216 ATC_Level'Image (Self_Id.ATC_Nesting_Level), 'A'));
218 D.Level := Self_Id.ATC_Nesting_Level;
219 D.Self_Id := Self_Id;
222 STI.Defer_Abort (Self_Id);
223 STPO.Write_Lock (Timer_Server_ID);
225 -- Previously, there was code here to dynamically create
226 -- the Timer_Server task, if one did not already exist.
227 -- That code had a timing window that could allow multiple
228 -- timer servers to be created. Luckily, the need for
229 -- postponing creation of the timer server should now be
230 -- gone, since this package will only be linked in if
231 -- there are calls to enqueue calls on the timer server.
233 -- Insert D in the timer queue, at the position determined
234 -- by the wakeup time T.
236 Q := Timer_Queue.Succ;
238 while Q.Resume_Time < T loop
242 -- Q is the block that has Resume_Time equal to or greater than
243 -- T. After the insertion we want Q to be the successor of D.
250 -- If the new element became the head of the queue,
251 -- signal the Timer_Server to wake up.
253 if Timer_Queue.Succ = D then
254 Timer_Attention := True;
255 STPO.Wakeup (Timer_Server_ID, ST.Timer_Server_Sleep);
258 STPO.Unlock (Timer_Server_ID);
259 STI.Undefer_Abort (Self_Id);
266 function Timed_Out (D : Delay_Block_Access) return Boolean is
275 task body Timer_Server is
276 Next_Wakeup_Time : Duration := Duration'Last;
282 Tsucc : Delay_Block_Access;
283 Dequeued_Task : Task_ID;
285 -- Initialize_Timer_Queue returns null, but has critical side-effects
286 -- of initializing the timer queue.
289 Timer_Server_ID := STPO.Self;
290 STU.Make_Independent;
292 -- Initialize the timer queue to empty, and make the wakeup time of the
293 -- header node be larger than any real wakeup time we will ever use.
296 STI.Defer_Abort (Timer_Server_ID);
297 STPO.Write_Lock (Timer_Server_ID);
299 -- The timer server needs to catch pending aborts after finalization
300 -- of library packages. If it doesn't poll for it, the server will
303 if not Timer_Attention then
304 Timer_Server_ID.Common.State := ST.Timer_Server_Sleep;
306 if Next_Wakeup_Time = Duration'Last then
307 Timer_Server_ID.User_State := 1;
309 STPO.Monotonic_Clock + OSP.Max_Sensible_Delay;
312 Timer_Server_ID.User_State := 2;
316 (Timer_Server_ID, Next_Wakeup_Time,
317 OSP.Absolute_RT, ST.Timer_Server_Sleep,
319 Timer_Server_ID.Common.State := ST.Runnable;
322 -- Service all of the wakeup requests on the queue whose times have
323 -- been reached, and update Next_Wakeup_Time to next wakeup time
324 -- after that (the wakeup time of the head of the queue if any, else
325 -- a time far in the future).
327 Timer_Server_ID.User_State := 3;
328 Timer_Attention := False;
330 Now := STPO.Monotonic_Clock;
332 while Timer_Queue.Succ.Resume_Time <= Now loop
334 -- Dequeue the waiting task from the front of the queue.
336 pragma Debug (System.Tasking.Debug.Trace
337 ("Timer service: waking up waiting task", 'E'));
339 Dequeued := Timer_Queue.Succ;
340 Timer_Queue.Succ := Dequeued.Succ;
341 Dequeued.Succ.Pred := Dequeued.Pred;
342 Dequeued.Succ := Dequeued;
343 Dequeued.Pred := Dequeued;
345 -- We want to abort the queued task to the level of the async.
346 -- select statement with the delay. To do that, we need to lock
347 -- the ATCB of that task, but to avoid deadlock we need to release
348 -- the lock of the Timer_Server. This leaves a window in which
349 -- another task might perform an enqueue or dequeue operation on
350 -- the timer queue, but that is OK because we always restart the
351 -- next iteration at the head of the queue.
353 STPO.Unlock (Timer_Server_ID);
354 STPO.Write_Lock (Dequeued.Self_Id);
355 Dequeued_Task := Dequeued.Self_Id;
356 Dequeued.Timed_Out := True;
357 STI.Locked_Abort_To_Level
358 (Timer_Server_ID, Dequeued_Task, Dequeued.Level - 1);
359 STPO.Unlock (Dequeued_Task);
360 STPO.Write_Lock (Timer_Server_ID);
363 Next_Wakeup_Time := Timer_Queue.Succ.Resume_Time;
365 -- Service returns the Next_Wakeup_Time.
366 -- The Next_Wakeup_Time is either an infinity (no delay request)
367 -- or the wakeup time of the queue head. This value is used for
368 -- an actual delay in this server.
370 STPO.Unlock (Timer_Server_ID);
371 STI.Undefer_Abort (Timer_Server_ID);
375 ------------------------------
376 -- Package Body Elaboration --
377 ------------------------------
380 Timer_Queue.Succ := Timer_Queue'Unchecked_Access;
381 Timer_Queue.Pred := Timer_Queue'Unchecked_Access;
382 Timer_Queue.Resume_Time := Duration'Last;
383 Timer_Server_ID := To_System (Timer_Server'Identity);
384 end System.Tasking.Async_Delays;