OSDN Git Service

Update FSF address
[pf3gnuchains/gcc-fork.git] / gcc / ada / s-taasde.adb
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 ------------------------------------------------------------------------------
33
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.
37
38 with Ada.Exceptions;
39 --  Used for Raise_Exception
40
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
50
51 with System.Tasking.Utilities;
52 --  Used for Make_Independent
53
54 with System.Tasking.Initialization;
55 --  Used for Defer_Abort
56 --           Undefer_Abort
57
58 with System.Tasking.Debug;
59 --  Used for Trace
60
61 with System.OS_Primitives;
62 --  used for Max_Sensible_Delay
63
64 with Ada.Task_Identification;
65 --  used for Task_Id type
66
67 with System.Interrupt_Management.Operations;
68 --  used for Setup_Interrupt_Mask
69
70 with System.Parameters;
71 --  used for Single_Lock
72 --           Runtime_Traces
73
74 with System.Traces.Tasking;
75 --  used for Send_Trace_Info
76
77 with Unchecked_Conversion;
78
79 package body System.Tasking.Async_Delays is
80
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;
86
87    use Parameters;
88    use System.Traces;
89    use System.Traces.Tasking;
90
91    function To_System is new Unchecked_Conversion
92      (Ada.Task_Identification.Task_Id, Task_Id);
93
94    Timer_Server_ID : ST.Task_Id;
95
96    Timer_Attention : Boolean := False;
97    pragma Atomic (Timer_Attention);
98
99    task Timer_Server is
100       pragma Interrupt_Priority (System.Any_Priority'Last);
101    end Timer_Server;
102
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.
108
109    Timer_Queue : aliased Delay_Block;
110
111    ------------------------
112    -- Cancel_Async_Delay --
113    ------------------------
114
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.
121
122    procedure Cancel_Async_Delay (D : Delay_Block_Access) is
123       Dpred : Delay_Block_Access;
124       Dsucc : Delay_Block_Access;
125
126    begin
127       --  Note that we mark the delay as being cancelled
128       --  using a level value that is reserved.
129
130       --  make this operation idempotent
131
132       if D.Level = ATC_Level_Infinity then
133          return;
134       end if;
135
136       D.Level := ATC_Level_Infinity;
137
138       --  remove self from timer queue
139
140       STI.Defer_Abort_Nestable (D.Self_Id);
141
142       if Single_Lock then
143          STPO.Lock_RTS;
144       end if;
145
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);
154
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.
158
159       --  leave the asynchronous select
160
161       STPO.Write_Lock (D.Self_Id);
162       STU.Exit_One_ATC_Level (D.Self_Id);
163       STPO.Unlock (D.Self_Id);
164
165       if Single_Lock then
166          STPO.Unlock_RTS;
167       end if;
168
169       STI.Undefer_Abort_Nestable (D.Self_Id);
170    end Cancel_Async_Delay;
171
172    ---------------------------
173    -- Enqueue_Time_Duration --
174    ---------------------------
175
176    function Enqueue_Duration
177      (T    : in Duration;
178       D    : Delay_Block_Access)
179       return Boolean
180    is
181    begin
182       if T <= 0.0 then
183          D.Timed_Out := True;
184          STPO.Yield;
185          return False;
186
187       else
188          --  The corresponding call to Undefer_Abort is performed by the
189          --  expanded code (see exp_ch9).
190
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;
198
199    ------------------
200    -- Time_Enqueue --
201    ------------------
202
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.
207
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.
215
216    procedure Time_Enqueue
217      (T : Duration;
218       D : Delay_Block_Access)
219    is
220       Self_Id : constant Task_Id  := STPO.Self;
221       Q       : Delay_Block_Access;
222
223       use type ST.Task_Id;
224       --  for visibility of operator "="
225
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");
230
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;
235
236       Self_Id.ATC_Nesting_Level := Self_Id.ATC_Nesting_Level + 1;
237
238       pragma Debug
239         (Debug.Trace (Self_Id, "ASD: entered ATC level: " &
240          ATC_Level'Image (Self_Id.ATC_Nesting_Level), 'A'));
241
242       D.Level := Self_Id.ATC_Nesting_Level;
243       D.Self_Id := Self_Id;
244       D.Resume_Time := T;
245
246       if Single_Lock then
247          STPO.Lock_RTS;
248       end if;
249
250       STPO.Write_Lock (Timer_Server_ID);
251
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.
259
260       --  Insert D in the timer queue, at the position determined
261       --  by the wakeup time T.
262
263       Q := Timer_Queue.Succ;
264
265       while Q.Resume_Time < T loop
266          Q := Q.Succ;
267       end loop;
268
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.
271
272       D.Succ := Q;
273       D.Pred := Q.Pred;
274       D.Pred.Succ := D;
275       Q.Pred := D;
276
277       --  If the new element became the head of the queue,
278       --  signal the Timer_Server to wake up.
279
280       if Timer_Queue.Succ = D then
281          Timer_Attention := True;
282          STPO.Wakeup (Timer_Server_ID, ST.Timer_Server_Sleep);
283       end if;
284
285       STPO.Unlock (Timer_Server_ID);
286
287       if Single_Lock then
288          STPO.Unlock_RTS;
289       end if;
290    end Time_Enqueue;
291
292    ---------------
293    -- Timed_Out --
294    ---------------
295
296    function Timed_Out (D : Delay_Block_Access) return Boolean is
297    begin
298       return D.Timed_Out;
299    end Timed_Out;
300
301    ------------------
302    -- Timer_Server --
303    ------------------
304
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
309
310       --------------------------
311       -- Get_Next_Wakeup_Time --
312       --------------------------
313
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;
319
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;
326
327    begin
328       Timer_Server_ID := STPO.Self;
329
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.
333
334       Interrupt_Management.Operations.Setup_Interrupt_Mask;
335
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.
338
339       loop
340          STI.Defer_Abort (Timer_Server_ID);
341
342          if Single_Lock then
343             STPO.Lock_RTS;
344          end if;
345
346          STPO.Write_Lock (Timer_Server_ID);
347
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.
351
352          if not Timer_Attention then
353             Timer_Server_ID.Common.State := ST.Timer_Server_Sleep;
354
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;
359
360             else
361                Timer_Server_ID.User_State := 2;
362             end if;
363
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;
370
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).
375
376          Timer_Server_ID.User_State := 3;
377          Timer_Attention := False;
378
379          Now := STPO.Monotonic_Clock;
380
381          while Timer_Queue.Succ.Resume_Time <= Now loop
382
383             --  Dequeue the waiting task from the front of the queue.
384
385             pragma Debug (System.Tasking.Debug.Trace
386               (Timer_Server_ID, "Timer service: waking up waiting task", 'E'));
387
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;
393
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.
401
402             if Parameters.Runtime_Traces then
403                Send_Trace_Info (E_Kill, Dequeued.Self_Id);
404             end if;
405
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;
415
416          Next_Wakeup_Time := Timer_Queue.Succ.Resume_Time;
417
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.
422
423          STPO.Unlock (Timer_Server_ID);
424
425          if Single_Lock then
426             STPO.Unlock_RTS;
427          end if;
428
429          STI.Undefer_Abort (Timer_Server_ID);
430       end loop;
431    end Timer_Server;
432
433    ------------------------------
434    -- Package Body Elaboration --
435    ------------------------------
436
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;