OSDN Git Service

* gcc-interface/trans.c (Subprogram_Body_to_gnu): Pop the stack of
[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-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 pragma Polling (Off);
33 --  Turn off polling, we do not want ATC polling to take place during
34 --  tasking operations. It causes infinite loops and other problems.
35
36 with Ada.Unchecked_Conversion;
37 with Ada.Task_Identification;
38
39 with System.Task_Primitives.Operations;
40 with System.Tasking.Utilities;
41 with System.Tasking.Initialization;
42 with System.Tasking.Debug;
43 with System.OS_Primitives;
44 with System.Interrupt_Management.Operations;
45 with System.Parameters;
46 with System.Traces.Tasking;
47
48 package body System.Tasking.Async_Delays is
49
50    package STPO renames System.Task_Primitives.Operations;
51    package ST renames System.Tasking;
52    package STU renames System.Tasking.Utilities;
53    package STI renames System.Tasking.Initialization;
54    package OSP renames System.OS_Primitives;
55
56    use Parameters;
57    use System.Traces;
58    use System.Traces.Tasking;
59
60    function To_System is new Ada.Unchecked_Conversion
61      (Ada.Task_Identification.Task_Id, Task_Id);
62
63    Timer_Server_ID : ST.Task_Id;
64
65    Timer_Attention : Boolean := False;
66    pragma Atomic (Timer_Attention);
67
68    task Timer_Server is
69       pragma Interrupt_Priority (System.Any_Priority'Last);
70    end Timer_Server;
71
72    --  The timer queue is a circular doubly linked list, ordered by absolute
73    --  wakeup time. The first item in the queue is Timer_Queue.Succ.
74    --  It is given a Resume_Time that is larger than any legitimate wakeup
75    --  time, so that the ordered insertion will always stop searching when it
76    --  gets back to the queue header block.
77
78    Timer_Queue : aliased Delay_Block;
79
80    ------------------------
81    -- Cancel_Async_Delay --
82    ------------------------
83
84    --  This should (only) be called from the compiler-generated cleanup routine
85    --  for an async. select statement with delay statement as trigger. The
86    --  effect should be to remove the delay from the timer queue, and exit one
87    --  ATC nesting level.
88    --  The usage and logic are similar to Cancel_Protected_Entry_Call, but
89    --  simplified because this is not a true entry call.
90
91    procedure Cancel_Async_Delay (D : Delay_Block_Access) is
92       Dpred : Delay_Block_Access;
93       Dsucc : Delay_Block_Access;
94
95    begin
96       --  Note that we mark the delay as being cancelled
97       --  using a level value that is reserved.
98
99       --  make this operation idempotent
100
101       if D.Level = ATC_Level_Infinity then
102          return;
103       end if;
104
105       D.Level := ATC_Level_Infinity;
106
107       --  remove self from timer queue
108
109       STI.Defer_Abort_Nestable (D.Self_Id);
110
111       if Single_Lock then
112          STPO.Lock_RTS;
113       end if;
114
115       STPO.Write_Lock (Timer_Server_ID);
116       Dpred := D.Pred;
117       Dsucc := D.Succ;
118       Dpred.Succ := Dsucc;
119       Dsucc.Pred := Dpred;
120       D.Succ := D;
121       D.Pred := D;
122       STPO.Unlock (Timer_Server_ID);
123
124       --  Note that the above deletion code is required to be
125       --  idempotent, since the block may have been dequeued
126       --  previously by the Timer_Server.
127
128       --  leave the asynchronous select
129
130       STPO.Write_Lock (D.Self_Id);
131       STU.Exit_One_ATC_Level (D.Self_Id);
132       STPO.Unlock (D.Self_Id);
133
134       if Single_Lock then
135          STPO.Unlock_RTS;
136       end if;
137
138       STI.Undefer_Abort_Nestable (D.Self_Id);
139    end Cancel_Async_Delay;
140
141    ---------------------------
142    -- Enqueue_Time_Duration --
143    ---------------------------
144
145    function Enqueue_Duration
146      (T : Duration;
147       D : Delay_Block_Access) return Boolean
148    is
149    begin
150       if T <= 0.0 then
151          D.Timed_Out := True;
152          STPO.Yield;
153          return False;
154
155       else
156          --  The corresponding call to Undefer_Abort is performed by the
157          --  expanded code (see exp_ch9).
158
159          STI.Defer_Abort (STPO.Self);
160          Time_Enqueue
161            (STPO.Monotonic_Clock
162             + Duration'Min (T, OSP.Max_Sensible_Delay), D);
163          return True;
164       end if;
165    end Enqueue_Duration;
166
167    ------------------
168    -- Time_Enqueue --
169    ------------------
170
171    --  Allocate a queue element for the wakeup time T and put it in the
172    --  queue in wakeup time order.  Assume we are on an asynchronous
173    --  select statement with delay trigger.  Put the calling task to
174    --  sleep until either the delay expires or is cancelled.
175
176    --  We use one entry call record for this delay, since we have
177    --  to increment the ATC nesting level, but since it is not a
178    --  real entry call we do not need to use any of the fields of
179    --  the call record.  The following code implements a subset of
180    --  the actions for the asynchronous case of Protected_Entry_Call,
181    --  much simplified since we know this never blocks, and does not
182    --  have the full semantics of a protected entry call.
183
184    procedure Time_Enqueue
185      (T : Duration;
186       D : Delay_Block_Access)
187    is
188       Self_Id : constant Task_Id  := STPO.Self;
189       Q       : Delay_Block_Access;
190
191       use type ST.Task_Id;
192       --  for visibility of operator "="
193
194    begin
195       pragma Debug (Debug.Trace (Self_Id, "Async_Delay", 'P'));
196       pragma Assert (Self_Id.Deferral_Level = 1,
197         "async delay from within abort-deferred region");
198
199       if Self_Id.ATC_Nesting_Level = ATC_Level'Last then
200          raise Storage_Error with "not enough ATC nesting levels";
201       end if;
202
203       Self_Id.ATC_Nesting_Level := Self_Id.ATC_Nesting_Level + 1;
204
205       pragma Debug
206         (Debug.Trace (Self_Id, "ASD: entered ATC level: " &
207          ATC_Level'Image (Self_Id.ATC_Nesting_Level), 'A'));
208
209       D.Level := Self_Id.ATC_Nesting_Level;
210       D.Self_Id := Self_Id;
211       D.Resume_Time := T;
212
213       if Single_Lock then
214          STPO.Lock_RTS;
215       end if;
216
217       STPO.Write_Lock (Timer_Server_ID);
218
219       --  Previously, there was code here to dynamically create
220       --  the Timer_Server task, if one did not already exist.
221       --  That code had a timing window that could allow multiple
222       --  timer servers to be created. Luckily, the need for
223       --  postponing creation of the timer server should now be
224       --  gone, since this package will only be linked in if
225       --  there are calls to enqueue calls on the timer server.
226
227       --  Insert D in the timer queue, at the position determined
228       --  by the wakeup time T.
229
230       Q := Timer_Queue.Succ;
231
232       while Q.Resume_Time < T loop
233          Q := Q.Succ;
234       end loop;
235
236       --  Q is the block that has Resume_Time equal to or greater than
237       --  T. After the insertion we want Q to be the successor of D.
238
239       D.Succ := Q;
240       D.Pred := Q.Pred;
241       D.Pred.Succ := D;
242       Q.Pred := D;
243
244       --  If the new element became the head of the queue,
245       --  signal the Timer_Server to wake up.
246
247       if Timer_Queue.Succ = D then
248          Timer_Attention := True;
249          STPO.Wakeup (Timer_Server_ID, ST.Timer_Server_Sleep);
250       end if;
251
252       STPO.Unlock (Timer_Server_ID);
253
254       if Single_Lock then
255          STPO.Unlock_RTS;
256       end if;
257    end Time_Enqueue;
258
259    ---------------
260    -- Timed_Out --
261    ---------------
262
263    function Timed_Out (D : Delay_Block_Access) return Boolean is
264    begin
265       return D.Timed_Out;
266    end Timed_Out;
267
268    ------------------
269    -- Timer_Server --
270    ------------------
271
272    task body Timer_Server is
273       function Get_Next_Wakeup_Time return Duration;
274       --  Used to initialize Next_Wakeup_Time, but also to ensure that
275       --  Make_Independent is called during the elaboration of this task.
276
277       --------------------------
278       -- Get_Next_Wakeup_Time --
279       --------------------------
280
281       function Get_Next_Wakeup_Time return Duration is
282       begin
283          STU.Make_Independent;
284          return Duration'Last;
285       end Get_Next_Wakeup_Time;
286
287       --  Local Declarations
288
289       Next_Wakeup_Time : Duration := Get_Next_Wakeup_Time;
290       Timedout         : Boolean;
291       Yielded          : Boolean;
292       Now              : Duration;
293       Dequeued         : Delay_Block_Access;
294       Dequeued_Task    : Task_Id;
295
296       pragma Unreferenced (Timedout, Yielded);
297
298    begin
299       Timer_Server_ID := STPO.Self;
300
301       --  Since this package may be elaborated before System.Interrupt,
302       --  we need to call Setup_Interrupt_Mask explicitly to ensure that
303       --  this task has the proper signal mask.
304
305       Interrupt_Management.Operations.Setup_Interrupt_Mask;
306
307       --  Initialize the timer queue to empty, and make the wakeup time of the
308       --  header node be larger than any real wakeup time we will ever use.
309
310       loop
311          STI.Defer_Abort (Timer_Server_ID);
312
313          if Single_Lock then
314             STPO.Lock_RTS;
315          end if;
316
317          STPO.Write_Lock (Timer_Server_ID);
318
319          --  The timer server needs to catch pending aborts after finalization
320          --  of library packages. If it doesn't poll for it, the server will
321          --  sometimes hang.
322
323          if not Timer_Attention then
324             Timer_Server_ID.Common.State := ST.Timer_Server_Sleep;
325
326             if Next_Wakeup_Time = Duration'Last then
327                Timer_Server_ID.User_State := 1;
328                Next_Wakeup_Time :=
329                  STPO.Monotonic_Clock + OSP.Max_Sensible_Delay;
330
331             else
332                Timer_Server_ID.User_State := 2;
333             end if;
334
335             STPO.Timed_Sleep
336               (Timer_Server_ID, Next_Wakeup_Time,
337                OSP.Absolute_RT, ST.Timer_Server_Sleep,
338                Timedout, Yielded);
339             Timer_Server_ID.Common.State := ST.Runnable;
340          end if;
341
342          --  Service all of the wakeup requests on the queue whose times have
343          --  been reached, and update Next_Wakeup_Time to next wakeup time
344          --  after that (the wakeup time of the head of the queue if any, else
345          --  a time far in the future).
346
347          Timer_Server_ID.User_State := 3;
348          Timer_Attention := False;
349
350          Now := STPO.Monotonic_Clock;
351          while Timer_Queue.Succ.Resume_Time <= Now loop
352
353             --  Dequeue the waiting task from the front of the queue
354
355             pragma Debug (System.Tasking.Debug.Trace
356               (Timer_Server_ID, "Timer service: waking up waiting task", 'E'));
357
358             Dequeued := Timer_Queue.Succ;
359             Timer_Queue.Succ := Dequeued.Succ;
360             Dequeued.Succ.Pred := Dequeued.Pred;
361             Dequeued.Succ := Dequeued;
362             Dequeued.Pred := Dequeued;
363
364             --  We want to abort the queued task to the level of the async.
365             --  select statement with the delay. To do that, we need to lock
366             --  the ATCB of that task, but to avoid deadlock we need to release
367             --  the lock of the Timer_Server. This leaves a window in which
368             --  another task might perform an enqueue or dequeue operation on
369             --  the timer queue, but that is OK because we always restart the
370             --  next iteration at the head of the queue.
371
372             if Parameters.Runtime_Traces then
373                Send_Trace_Info (E_Kill, Dequeued.Self_Id);
374             end if;
375
376             STPO.Unlock (Timer_Server_ID);
377             STPO.Write_Lock (Dequeued.Self_Id);
378             Dequeued_Task := Dequeued.Self_Id;
379             Dequeued.Timed_Out := True;
380             STI.Locked_Abort_To_Level
381               (Timer_Server_ID, Dequeued_Task, Dequeued.Level - 1);
382             STPO.Unlock (Dequeued_Task);
383             STPO.Write_Lock (Timer_Server_ID);
384          end loop;
385
386          Next_Wakeup_Time := Timer_Queue.Succ.Resume_Time;
387
388          --  Service returns the Next_Wakeup_Time.
389          --  The Next_Wakeup_Time is either an infinity (no delay request)
390          --  or the wakeup time of the queue head. This value is used for
391          --  an actual delay in this server.
392
393          STPO.Unlock (Timer_Server_ID);
394
395          if Single_Lock then
396             STPO.Unlock_RTS;
397          end if;
398
399          STI.Undefer_Abort (Timer_Server_ID);
400       end loop;
401    end Timer_Server;
402
403    ------------------------------
404    -- Package Body Elaboration --
405    ------------------------------
406
407 begin
408    Timer_Queue.Succ := Timer_Queue'Access;
409    Timer_Queue.Pred := Timer_Queue'Access;
410    Timer_Queue.Resume_Time := Duration'Last;
411    Timer_Server_ID := To_System (Timer_Server'Identity);
412 end System.Tasking.Async_Delays;