1 ------------------------------------------------------------------------------
3 -- GNAT RUN-TIME COMPONENTS --
5 -- A D A . R E A L _ T I M E . T I M I N G _ E V E N T S --
9 -- Copyright (C) 2005-2011, Free Software Foundation, Inc. --
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. --
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. --
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/>. --
27 -- GNAT was originally developed by the GNAT team at New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc. --
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
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.).
88 pragma Priority
(System
.Priority
'Last);
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.
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
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
123 Process_Queued_Events
;
124 delay until Clock
+ Period
;
128 ---------------------------
129 -- Process_Queued_Events --
130 ---------------------------
132 procedure Process_Queued_Events
is
133 Next_Event
: Any_Timing_Event
;
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;
146 Next_Event
:= All_Events
.First_Element
;
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;
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.
181 Handler
: constant Timing_Event_Handler
:= Next_Event
.Handler
;
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));
195 -- Ignore exceptions propagated by Handler.all, as required by
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
221 function Sooner
(Left
, Right
: Any_Timing_Event
) return Boolean is
223 return Left
.Timeout
< Right
.Timeout
;
226 -- Start of processing for Insert_Into_Queue
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
258 Write_Lock
(Event_Queue_Lock
'Access);
260 Location
:= All_Events
.Find
(This
);
262 if Location
/= No_Element
then
263 All_Events
.Delete
(Location
);
266 Unlock
(Event_Queue_Lock
'Access);
268 SSL
.Abort_Undefer
.all;
269 end Remove_From_Queue
;
275 procedure Set_Handler
276 (Event
: in out Timing_Event
;
278 Handler
: Timing_Event_Handler
)
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);
306 procedure Set_Handler
307 (Event
: in out Timing_Event
;
309 Handler
: Timing_Event_Handler
)
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);
324 ---------------------
325 -- Current_Handler --
326 ---------------------
328 function Current_Handler
329 (Event
: Timing_Event
) return Timing_Event_Handler
332 return Event
.Handler
;
339 procedure Cancel_Handler
340 (Event
: in out Timing_Event
;
341 Cancelled
: out Boolean)
344 Remove_From_Queue
(Event
'Unchecked_Access);
345 Cancelled
:= Event
.Handler
/= null;
346 Event
.Handler
:= null;
353 function Time_Of_Event
(Event
: Timing_Event
) return Time
is
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
);
364 procedure Finalize
(This
: in out Timing_Event
) is
366 -- D.15 (19/2) says finalization clears the event
368 This
.Handler
:= null;
369 Remove_From_Queue
(This
'Unchecked_Access);
373 Initialize_Lock
(Event_Queue_Lock
'Access, Level
=> PO_Level
);
375 end Ada
.Real_Time
.Timing_Events
;