* c-cppbuiltin.c (c_cpp_builtins): Define __pic__ and __PIC__ when
[official-gcc.git] / gcc / ada / s-taasde.adb
blob9253862c89e74647955aa1cfe4794a1782a8b077
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT RUN-TIME LIBRARY (GNARL) COMPONENTS --
4 -- --
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 --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 1998-2005, 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 2, or (at your option) any later ver- --
14 -- sion. GNARL 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. See the GNU General Public License --
17 -- for more details. You should have received a copy of the GNU General --
18 -- Public License distributed with GNARL; see file COPYING. If not, write --
19 -- to the Free Software Foundation, 51 Franklin Street, Fifth Floor, --
20 -- Boston, MA 02110-1301, USA. --
21 -- --
22 -- As a special exception, if other files instantiate generics from this --
23 -- unit, or you link this unit with other files to produce an executable, --
24 -- this unit does not by itself cause the resulting executable to be --
25 -- covered by the GNU General Public License. This exception does not --
26 -- however invalidate any other reasons why the executable file might be --
27 -- covered by the GNU Public License. --
28 -- --
29 -- GNARL was developed by the GNARL team at Florida State University. --
30 -- Extensive contributions were provided by Ada Core Technologies, Inc. --
31 -- --
32 ------------------------------------------------------------------------------
34 pragma Polling (Off);
35 -- Turn off polling, we do not want ATC polling to take place during
36 -- tasking operations. It causes infinite loops and other problems.
38 with Ada.Exceptions;
39 -- Used for Raise_Exception
41 with System.Task_Primitives.Operations;
42 -- Used for Write_Lock,
43 -- Unlock,
44 -- Self,
45 -- Monotonic_Clock,
46 -- Self,
47 -- Timed_Sleep,
48 -- Wakeup,
49 -- Yield
51 with System.Tasking.Utilities;
52 -- Used for Make_Independent
54 with System.Tasking.Initialization;
55 -- Used for Defer_Abort
56 -- Undefer_Abort
58 with System.Tasking.Debug;
59 -- Used for Trace
61 with System.OS_Primitives;
62 -- used for Max_Sensible_Delay
64 with Ada.Task_Identification;
65 -- used for Task_Id type
67 with System.Interrupt_Management.Operations;
68 -- used for Setup_Interrupt_Mask
70 with System.Parameters;
71 -- used for Single_Lock
72 -- Runtime_Traces
74 with System.Traces.Tasking;
75 -- used for Send_Trace_Info
77 with Unchecked_Conversion;
79 package body System.Tasking.Async_Delays is
81 package STPO renames System.Task_Primitives.Operations;
82 package ST renames System.Tasking;
83 package STU renames System.Tasking.Utilities;
84 package STI renames System.Tasking.Initialization;
85 package OSP renames System.OS_Primitives;
87 use Parameters;
88 use System.Traces;
89 use System.Traces.Tasking;
91 function To_System is new Unchecked_Conversion
92 (Ada.Task_Identification.Task_Id, Task_Id);
94 Timer_Server_ID : ST.Task_Id;
96 Timer_Attention : Boolean := False;
97 pragma Atomic (Timer_Attention);
99 task Timer_Server is
100 pragma Interrupt_Priority (System.Any_Priority'Last);
101 end Timer_Server;
103 -- The timer queue is a circular doubly linked list, ordered by absolute
104 -- wakeup time. The first item in the queue is Timer_Queue.Succ.
105 -- It is given a Resume_Time that is larger than any legitimate wakeup
106 -- time, so that the ordered insertion will always stop searching when it
107 -- gets back to the queue header block.
109 Timer_Queue : aliased Delay_Block;
111 ------------------------
112 -- Cancel_Async_Delay --
113 ------------------------
115 -- This should (only) be called from the compiler-generated cleanup routine
116 -- for an async. select statement with delay statement as trigger. The
117 -- effect should be to remove the delay from the timer queue, and exit one
118 -- ATC nesting level.
119 -- The usage and logic are similar to Cancel_Protected_Entry_Call, but
120 -- simplified because this is not a true entry call.
122 procedure Cancel_Async_Delay (D : Delay_Block_Access) is
123 Dpred : Delay_Block_Access;
124 Dsucc : Delay_Block_Access;
126 begin
127 -- Note that we mark the delay as being cancelled
128 -- using a level value that is reserved.
130 -- make this operation idempotent
132 if D.Level = ATC_Level_Infinity then
133 return;
134 end if;
136 D.Level := ATC_Level_Infinity;
138 -- remove self from timer queue
140 STI.Defer_Abort_Nestable (D.Self_Id);
142 if Single_Lock then
143 STPO.Lock_RTS;
144 end if;
146 STPO.Write_Lock (Timer_Server_ID);
147 Dpred := D.Pred;
148 Dsucc := D.Succ;
149 Dpred.Succ := Dsucc;
150 Dsucc.Pred := Dpred;
151 D.Succ := D;
152 D.Pred := D;
153 STPO.Unlock (Timer_Server_ID);
155 -- Note that the above deletion code is required to be
156 -- idempotent, since the block may have been dequeued
157 -- previously by the Timer_Server.
159 -- leave the asynchronous select
161 STPO.Write_Lock (D.Self_Id);
162 STU.Exit_One_ATC_Level (D.Self_Id);
163 STPO.Unlock (D.Self_Id);
165 if Single_Lock then
166 STPO.Unlock_RTS;
167 end if;
169 STI.Undefer_Abort_Nestable (D.Self_Id);
170 end Cancel_Async_Delay;
172 ---------------------------
173 -- Enqueue_Time_Duration --
174 ---------------------------
176 function Enqueue_Duration
177 (T : in Duration;
178 D : Delay_Block_Access)
179 return Boolean
181 begin
182 if T <= 0.0 then
183 D.Timed_Out := True;
184 STPO.Yield;
185 return False;
187 else
188 -- The corresponding call to Undefer_Abort is performed by the
189 -- expanded code (see exp_ch9).
191 STI.Defer_Abort (STPO.Self);
192 Time_Enqueue
193 (STPO.Monotonic_Clock
194 + Duration'Min (T, OSP.Max_Sensible_Delay), D);
195 return True;
196 end if;
197 end Enqueue_Duration;
199 ------------------
200 -- Time_Enqueue --
201 ------------------
203 -- Allocate a queue element for the wakeup time T and put it in the
204 -- queue in wakeup time order. Assume we are on an asynchronous
205 -- select statement with delay trigger. Put the calling task to
206 -- sleep until either the delay expires or is cancelled.
208 -- We use one entry call record for this delay, since we have
209 -- to increment the ATC nesting level, but since it is not a
210 -- real entry call we do not need to use any of the fields of
211 -- the call record. The following code implements a subset of
212 -- the actions for the asynchronous case of Protected_Entry_Call,
213 -- much simplified since we know this never blocks, and does not
214 -- have the full semantics of a protected entry call.
216 procedure Time_Enqueue
217 (T : Duration;
218 D : Delay_Block_Access)
220 Self_Id : constant Task_Id := STPO.Self;
221 Q : Delay_Block_Access;
223 use type ST.Task_Id;
224 -- for visibility of operator "="
226 begin
227 pragma Debug (Debug.Trace (Self_Id, "Async_Delay", 'P'));
228 pragma Assert (Self_Id.Deferral_Level = 1,
229 "async delay from within abort-deferred region");
231 if Self_Id.ATC_Nesting_Level = ATC_Level'Last then
232 Ada.Exceptions.Raise_Exception (Storage_Error'Identity,
233 "not enough ATC nesting levels");
234 end if;
236 Self_Id.ATC_Nesting_Level := Self_Id.ATC_Nesting_Level + 1;
238 pragma Debug
239 (Debug.Trace (Self_Id, "ASD: entered ATC level: " &
240 ATC_Level'Image (Self_Id.ATC_Nesting_Level), 'A'));
242 D.Level := Self_Id.ATC_Nesting_Level;
243 D.Self_Id := Self_Id;
244 D.Resume_Time := T;
246 if Single_Lock then
247 STPO.Lock_RTS;
248 end if;
250 STPO.Write_Lock (Timer_Server_ID);
252 -- Previously, there was code here to dynamically create
253 -- the Timer_Server task, if one did not already exist.
254 -- That code had a timing window that could allow multiple
255 -- timer servers to be created. Luckily, the need for
256 -- postponing creation of the timer server should now be
257 -- gone, since this package will only be linked in if
258 -- there are calls to enqueue calls on the timer server.
260 -- Insert D in the timer queue, at the position determined
261 -- by the wakeup time T.
263 Q := Timer_Queue.Succ;
265 while Q.Resume_Time < T loop
266 Q := Q.Succ;
267 end loop;
269 -- Q is the block that has Resume_Time equal to or greater than
270 -- T. After the insertion we want Q to be the successor of D.
272 D.Succ := Q;
273 D.Pred := Q.Pred;
274 D.Pred.Succ := D;
275 Q.Pred := D;
277 -- If the new element became the head of the queue,
278 -- signal the Timer_Server to wake up.
280 if Timer_Queue.Succ = D then
281 Timer_Attention := True;
282 STPO.Wakeup (Timer_Server_ID, ST.Timer_Server_Sleep);
283 end if;
285 STPO.Unlock (Timer_Server_ID);
287 if Single_Lock then
288 STPO.Unlock_RTS;
289 end if;
290 end Time_Enqueue;
292 ---------------
293 -- Timed_Out --
294 ---------------
296 function Timed_Out (D : Delay_Block_Access) return Boolean is
297 begin
298 return D.Timed_Out;
299 end Timed_Out;
301 ------------------
302 -- Timer_Server --
303 ------------------
305 task body Timer_Server is
306 function Get_Next_Wakeup_Time return Duration;
307 -- Used to initialize Next_Wakeup_Time, but also to ensure that
308 -- Make_Independent is called during the elaboration of this task
310 --------------------------
311 -- Get_Next_Wakeup_Time --
312 --------------------------
314 function Get_Next_Wakeup_Time return Duration is
315 begin
316 STU.Make_Independent;
317 return Duration'Last;
318 end Get_Next_Wakeup_Time;
320 Next_Wakeup_Time : Duration := Get_Next_Wakeup_Time;
321 Timedout : Boolean;
322 Yielded : Boolean;
323 Now : Duration;
324 Dequeued : Delay_Block_Access;
325 Dequeued_Task : Task_Id;
327 begin
328 Timer_Server_ID := STPO.Self;
330 -- Since this package may be elaborated before System.Interrupt,
331 -- we need to call Setup_Interrupt_Mask explicitly to ensure that
332 -- this task has the proper signal mask.
334 Interrupt_Management.Operations.Setup_Interrupt_Mask;
336 -- Initialize the timer queue to empty, and make the wakeup time of the
337 -- header node be larger than any real wakeup time we will ever use.
339 loop
340 STI.Defer_Abort (Timer_Server_ID);
342 if Single_Lock then
343 STPO.Lock_RTS;
344 end if;
346 STPO.Write_Lock (Timer_Server_ID);
348 -- The timer server needs to catch pending aborts after finalization
349 -- of library packages. If it doesn't poll for it, the server will
350 -- sometimes hang.
352 if not Timer_Attention then
353 Timer_Server_ID.Common.State := ST.Timer_Server_Sleep;
355 if Next_Wakeup_Time = Duration'Last then
356 Timer_Server_ID.User_State := 1;
357 Next_Wakeup_Time :=
358 STPO.Monotonic_Clock + OSP.Max_Sensible_Delay;
360 else
361 Timer_Server_ID.User_State := 2;
362 end if;
364 STPO.Timed_Sleep
365 (Timer_Server_ID, Next_Wakeup_Time,
366 OSP.Absolute_RT, ST.Timer_Server_Sleep,
367 Timedout, Yielded);
368 Timer_Server_ID.Common.State := ST.Runnable;
369 end if;
371 -- Service all of the wakeup requests on the queue whose times have
372 -- been reached, and update Next_Wakeup_Time to next wakeup time
373 -- after that (the wakeup time of the head of the queue if any, else
374 -- a time far in the future).
376 Timer_Server_ID.User_State := 3;
377 Timer_Attention := False;
379 Now := STPO.Monotonic_Clock;
381 while Timer_Queue.Succ.Resume_Time <= Now loop
383 -- Dequeue the waiting task from the front of the queue.
385 pragma Debug (System.Tasking.Debug.Trace
386 (Timer_Server_ID, "Timer service: waking up waiting task", 'E'));
388 Dequeued := Timer_Queue.Succ;
389 Timer_Queue.Succ := Dequeued.Succ;
390 Dequeued.Succ.Pred := Dequeued.Pred;
391 Dequeued.Succ := Dequeued;
392 Dequeued.Pred := Dequeued;
394 -- We want to abort the queued task to the level of the async.
395 -- select statement with the delay. To do that, we need to lock
396 -- the ATCB of that task, but to avoid deadlock we need to release
397 -- the lock of the Timer_Server. This leaves a window in which
398 -- another task might perform an enqueue or dequeue operation on
399 -- the timer queue, but that is OK because we always restart the
400 -- next iteration at the head of the queue.
402 if Parameters.Runtime_Traces then
403 Send_Trace_Info (E_Kill, Dequeued.Self_Id);
404 end if;
406 STPO.Unlock (Timer_Server_ID);
407 STPO.Write_Lock (Dequeued.Self_Id);
408 Dequeued_Task := Dequeued.Self_Id;
409 Dequeued.Timed_Out := True;
410 STI.Locked_Abort_To_Level
411 (Timer_Server_ID, Dequeued_Task, Dequeued.Level - 1);
412 STPO.Unlock (Dequeued_Task);
413 STPO.Write_Lock (Timer_Server_ID);
414 end loop;
416 Next_Wakeup_Time := Timer_Queue.Succ.Resume_Time;
418 -- Service returns the Next_Wakeup_Time.
419 -- The Next_Wakeup_Time is either an infinity (no delay request)
420 -- or the wakeup time of the queue head. This value is used for
421 -- an actual delay in this server.
423 STPO.Unlock (Timer_Server_ID);
425 if Single_Lock then
426 STPO.Unlock_RTS;
427 end if;
429 STI.Undefer_Abort (Timer_Server_ID);
430 end loop;
431 end Timer_Server;
433 ------------------------------
434 -- Package Body Elaboration --
435 ------------------------------
437 begin
438 Timer_Queue.Succ := Timer_Queue'Unchecked_Access;
439 Timer_Queue.Pred := Timer_Queue'Unchecked_Access;
440 Timer_Queue.Resume_Time := Duration'Last;
441 Timer_Server_ID := To_System (Timer_Server'Identity);
442 end System.Tasking.Async_Delays;