PR target/60039
[official-gcc.git] / gcc / ada / a-rttiev.adb
blob67b81c72ba84c10530ca97a165709df781e62ec8
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT RUN-TIME COMPONENTS --
4 -- --
5 -- A D A . R E A L _ T I M E . T I M I N G _ E V E N T S --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 2005-2011, Free Software Foundation, Inc. --
10 -- --
11 -- GNAT 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 -- GNAT was originally developed by the GNAT team at New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc. --
29 -- --
30 ------------------------------------------------------------------------------
32 with System.Task_Primitives.Operations;
33 with System.Tasking.Utilities;
34 with System.Soft_Links;
35 with System.Interrupt_Management.Operations;
37 with Ada.Containers.Doubly_Linked_Lists;
38 pragma Elaborate_All (Ada.Containers.Doubly_Linked_Lists);
40 ---------------------------------
41 -- Ada.Real_Time.Timing_Events --
42 ---------------------------------
44 package body Ada.Real_Time.Timing_Events is
46 use System.Task_Primitives.Operations;
48 package SSL renames System.Soft_Links;
50 type Any_Timing_Event is access all Timing_Event'Class;
51 -- We must also handle user-defined types derived from Timing_Event
53 ------------
54 -- Events --
55 ------------
57 package Events is new Ada.Containers.Doubly_Linked_Lists (Any_Timing_Event);
58 -- Provides the type for the container holding pointers to events
60 All_Events : Events.List;
61 -- The queue of pending events, ordered by increasing timeout value, that
62 -- have been "set" by the user via Set_Handler.
64 Event_Queue_Lock : aliased System.Task_Primitives.RTS_Lock;
65 -- Used for mutually exclusive access to All_Events
67 procedure Process_Queued_Events;
68 -- Examine the queue of pending events for any that have timed out. For
69 -- those that have timed out, remove them from the queue and invoke their
70 -- handler (unless the user has cancelled the event by setting the handler
71 -- pointer to null). Mutually exclusive access is held via Event_Queue_Lock
72 -- during part of the processing.
74 procedure Insert_Into_Queue (This : Any_Timing_Event);
75 -- Insert the specified event pointer into the queue of pending events
76 -- with mutually exclusive access via Event_Queue_Lock.
78 procedure Remove_From_Queue (This : Any_Timing_Event);
79 -- Remove the specified event pointer from the queue of pending events with
80 -- mutually exclusive access via Event_Queue_Lock. This procedure is used
81 -- by the client-side routines (Set_Handler, etc.).
83 -----------
84 -- Timer --
85 -----------
87 task Timer is
88 pragma Priority (System.Priority'Last);
89 entry Start;
90 end Timer;
92 task body Timer is
93 Period : constant Time_Span := Milliseconds (100);
94 -- This is a "chiming" clock timer that fires periodically. The period
95 -- selected is arbitrary and could be changed to suit the application
96 -- requirements. Obviously a shorter period would give better resolution
97 -- at the cost of more overhead.
99 begin
100 System.Tasking.Utilities.Make_Independent;
102 -- Since this package may be elaborated before System.Interrupt,
103 -- we need to call Setup_Interrupt_Mask explicitly to ensure that
104 -- this task has the proper signal mask.
106 System.Interrupt_Management.Operations.Setup_Interrupt_Mask;
108 -- We await the call to Start to ensure that Event_Queue_Lock has been
109 -- initialized by the package executable part prior to accessing it in
110 -- the loop. The task is activated before the first statement of the
111 -- executable part so it would otherwise be possible for the task to
112 -- call EnterCriticalSection in Process_Queued_Events before the
113 -- initialization.
115 -- We don't simply put the initialization here, prior to the loop,
116 -- because other application tasks could call the visible routines that
117 -- also call Enter/LeaveCriticalSection prior to this task doing the
118 -- initialization.
120 accept Start;
122 loop
123 Process_Queued_Events;
124 delay until Clock + Period;
125 end loop;
126 end Timer;
128 ---------------------------
129 -- Process_Queued_Events --
130 ---------------------------
132 procedure Process_Queued_Events is
133 Next_Event : Any_Timing_Event;
135 begin
136 loop
137 SSL.Abort_Defer.all;
139 Write_Lock (Event_Queue_Lock'Access);
141 if All_Events.Is_Empty then
142 Unlock (Event_Queue_Lock'Access);
143 SSL.Abort_Undefer.all;
144 return;
145 else
146 Next_Event := All_Events.First_Element;
147 end if;
149 if Next_Event.Timeout > Clock then
151 -- We found one that has not yet timed out. The queue is in
152 -- ascending order by Timeout so there is no need to continue
153 -- processing (and indeed we must not continue since we always
154 -- delete the first element).
156 Unlock (Event_Queue_Lock'Access);
157 SSL.Abort_Undefer.all;
158 return;
159 end if;
161 -- We have an event that has timed out so we will process it. It must
162 -- be the first in the queue so no search is needed.
164 All_Events.Delete_First;
166 -- A fundamental issue is that the invocation of the event's handler
167 -- might call Set_Handler on itself to re-insert itself back into the
168 -- queue of future events. Thus we cannot hold the lock on the queue
169 -- while invoking the event's handler.
171 Unlock (Event_Queue_Lock'Access);
173 SSL.Abort_Undefer.all;
175 -- There is no race condition with the user changing the handler
176 -- pointer while we are processing because we are executing at the
177 -- highest possible application task priority and are not doing
178 -- anything to block prior to invoking their handler.
180 declare
181 Handler : constant Timing_Event_Handler := Next_Event.Handler;
183 begin
184 -- The first act is to clear the event, per D.15(13/2). Besides,
185 -- we cannot clear the handler pointer *after* invoking the
186 -- handler because the handler may have re-inserted the event via
187 -- Set_Event. Thus we take a copy and then clear the component.
189 Next_Event.Handler := null;
191 if Handler /= null then
192 Handler.all (Timing_Event (Next_Event.all));
193 end if;
195 -- Ignore exceptions propagated by Handler.all, as required by
196 -- RM D.15(21/2).
198 exception
199 when others =>
200 null;
201 end;
202 end loop;
203 end Process_Queued_Events;
205 -----------------------
206 -- Insert_Into_Queue --
207 -----------------------
209 procedure Insert_Into_Queue (This : Any_Timing_Event) is
211 function Sooner (Left, Right : Any_Timing_Event) return Boolean;
212 -- Compares events in terms of timeout values
214 package By_Timeout is new Events.Generic_Sorting (Sooner);
215 -- Used to keep the events in ascending order by timeout value
217 ------------
218 -- Sooner --
219 ------------
221 function Sooner (Left, Right : Any_Timing_Event) return Boolean is
222 begin
223 return Left.Timeout < Right.Timeout;
224 end Sooner;
226 -- Start of processing for Insert_Into_Queue
228 begin
229 SSL.Abort_Defer.all;
231 Write_Lock (Event_Queue_Lock'Access);
233 All_Events.Append (This);
235 -- A critical property of the implementation of this package is that
236 -- all occurrences are in ascending order by Timeout. Thus the first
237 -- event in the queue always has the "next" value for the Timer task
238 -- to use in its delay statement.
240 By_Timeout.Sort (All_Events);
242 Unlock (Event_Queue_Lock'Access);
244 SSL.Abort_Undefer.all;
245 end Insert_Into_Queue;
247 -----------------------
248 -- Remove_From_Queue --
249 -----------------------
251 procedure Remove_From_Queue (This : Any_Timing_Event) is
252 use Events;
253 Location : Cursor;
255 begin
256 SSL.Abort_Defer.all;
258 Write_Lock (Event_Queue_Lock'Access);
260 Location := All_Events.Find (This);
262 if Location /= No_Element then
263 All_Events.Delete (Location);
264 end if;
266 Unlock (Event_Queue_Lock'Access);
268 SSL.Abort_Undefer.all;
269 end Remove_From_Queue;
271 -----------------
272 -- Set_Handler --
273 -----------------
275 procedure Set_Handler
276 (Event : in out Timing_Event;
277 At_Time : Time;
278 Handler : Timing_Event_Handler)
280 begin
281 Remove_From_Queue (Event'Unchecked_Access);
282 Event.Handler := null;
284 -- RM D.15(15/2) required that at this point, we check whether the time
285 -- has already passed, and if so, call Handler.all directly from here
286 -- instead of doing the enqueuing below. However, this caused a nasty
287 -- race condition and potential deadlock. If the current task has
288 -- already locked the protected object of Handler.all, and the time has
289 -- passed, deadlock would occur. It has been fixed by AI05-0094-1, which
290 -- says that the handler should be executed as soon as possible, meaning
291 -- that the timing event will be executed after the protected action
292 -- finishes (Handler.all should not be called directly from here).
293 -- The same comment applies to the other Set_Handler below.
295 if Handler /= null then
296 Event.Timeout := At_Time;
297 Event.Handler := Handler;
298 Insert_Into_Queue (Event'Unchecked_Access);
299 end if;
300 end Set_Handler;
302 -----------------
303 -- Set_Handler --
304 -----------------
306 procedure Set_Handler
307 (Event : in out Timing_Event;
308 In_Time : Time_Span;
309 Handler : Timing_Event_Handler)
311 begin
312 Remove_From_Queue (Event'Unchecked_Access);
313 Event.Handler := null;
315 -- See comment in the other Set_Handler above
317 if Handler /= null then
318 Event.Timeout := Clock + In_Time;
319 Event.Handler := Handler;
320 Insert_Into_Queue (Event'Unchecked_Access);
321 end if;
322 end Set_Handler;
324 ---------------------
325 -- Current_Handler --
326 ---------------------
328 function Current_Handler
329 (Event : Timing_Event) return Timing_Event_Handler
331 begin
332 return Event.Handler;
333 end Current_Handler;
335 --------------------
336 -- Cancel_Handler --
337 --------------------
339 procedure Cancel_Handler
340 (Event : in out Timing_Event;
341 Cancelled : out Boolean)
343 begin
344 Remove_From_Queue (Event'Unchecked_Access);
345 Cancelled := Event.Handler /= null;
346 Event.Handler := null;
347 end Cancel_Handler;
349 -------------------
350 -- Time_Of_Event --
351 -------------------
353 function Time_Of_Event (Event : Timing_Event) return Time is
354 begin
355 -- RM D.15(18/2): Time_First must be returned in the event is not set
357 return (if Event.Handler = null then Time_First else Event.Timeout);
358 end Time_Of_Event;
360 --------------
361 -- Finalize --
362 --------------
364 procedure Finalize (This : in out Timing_Event) is
365 begin
366 -- D.15 (19/2) says finalization clears the event
368 This.Handler := null;
369 Remove_From_Queue (This'Unchecked_Access);
370 end Finalize;
372 begin
373 Initialize_Lock (Event_Queue_Lock'Access, Level => PO_Level);
374 Timer.Start;
375 end Ada.Real_Time.Timing_Events;