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-2018, 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 -- We need to Initialize_Lock before Timer is activated. The purpose of the
68 -- Dummy package is to get around Ada's syntax rules.
70 package Dummy
is end Dummy
;
73 Initialize_Lock
(Event_Queue_Lock
'Access, Level
=> PO_Level
);
76 procedure Process_Queued_Events
;
77 -- Examine the queue of pending events for any that have timed out. For
78 -- those that have timed out, remove them from the queue and invoke their
79 -- handler (unless the user has cancelled the event by setting the handler
80 -- pointer to null). Mutually exclusive access is held via Event_Queue_Lock
81 -- during part of the processing.
83 procedure Insert_Into_Queue
(This
: Any_Timing_Event
);
84 -- Insert the specified event pointer into the queue of pending events
85 -- with mutually exclusive access via Event_Queue_Lock.
87 procedure Remove_From_Queue
(This
: Any_Timing_Event
);
88 -- Remove the specified event pointer from the queue of pending events with
89 -- mutually exclusive access via Event_Queue_Lock. This procedure is used
90 -- by the client-side routines (Set_Handler, etc.).
97 pragma Priority
(System
.Priority
'Last);
101 Period
: constant Time_Span
:= Milliseconds
(100);
102 -- This is a "chiming" clock timer that fires periodically. The period
103 -- selected is arbitrary and could be changed to suit the application
104 -- requirements. Obviously a shorter period would give better resolution
105 -- at the cost of more overhead.
107 Ignore
: constant Boolean := System
.Tasking
.Utilities
.Make_Independent
;
108 pragma Unreferenced
(Ignore
);
111 -- Since this package may be elaborated before System.Interrupt,
112 -- we need to call Setup_Interrupt_Mask explicitly to ensure that
113 -- this task has the proper signal mask.
115 System
.Interrupt_Management
.Operations
.Setup_Interrupt_Mask
;
118 Process_Queued_Events
;
119 delay until Clock
+ Period
;
123 ---------------------------
124 -- Process_Queued_Events --
125 ---------------------------
127 procedure Process_Queued_Events
is
128 Next_Event
: Any_Timing_Event
;
134 Write_Lock
(Event_Queue_Lock
'Access);
136 if All_Events
.Is_Empty
then
137 Unlock
(Event_Queue_Lock
'Access);
138 SSL
.Abort_Undefer
.all;
141 Next_Event
:= All_Events
.First_Element
;
144 if Next_Event
.Timeout
> Clock
then
146 -- We found one that has not yet timed out. The queue is in
147 -- ascending order by Timeout so there is no need to continue
148 -- processing (and indeed we must not continue since we always
149 -- delete the first element).
151 Unlock
(Event_Queue_Lock
'Access);
152 SSL
.Abort_Undefer
.all;
156 -- We have an event that has timed out so we will process it. It must
157 -- be the first in the queue so no search is needed.
159 All_Events
.Delete_First
;
161 -- A fundamental issue is that the invocation of the event's handler
162 -- might call Set_Handler on itself to re-insert itself back into the
163 -- queue of future events. Thus we cannot hold the lock on the queue
164 -- while invoking the event's handler.
166 Unlock
(Event_Queue_Lock
'Access);
168 SSL
.Abort_Undefer
.all;
170 -- There is no race condition with the user changing the handler
171 -- pointer while we are processing because we are executing at the
172 -- highest possible application task priority and are not doing
173 -- anything to block prior to invoking their handler.
176 Handler
: constant Timing_Event_Handler
:= Next_Event
.Handler
;
179 -- The first act is to clear the event, per D.15(13/2). Besides,
180 -- we cannot clear the handler pointer *after* invoking the
181 -- handler because the handler may have re-inserted the event via
182 -- Set_Event. Thus we take a copy and then clear the component.
184 Next_Event
.Handler
:= null;
186 if Handler
/= null then
187 Handler
.all (Timing_Event
(Next_Event
.all));
190 -- Ignore exceptions propagated by Handler.all, as required by
198 end Process_Queued_Events
;
200 -----------------------
201 -- Insert_Into_Queue --
202 -----------------------
204 procedure Insert_Into_Queue
(This
: Any_Timing_Event
) is
206 function Sooner
(Left
, Right
: Any_Timing_Event
) return Boolean;
207 -- Compares events in terms of timeout values
209 package By_Timeout
is new Events
.Generic_Sorting
(Sooner
);
210 -- Used to keep the events in ascending order by timeout value
216 function Sooner
(Left
, Right
: Any_Timing_Event
) return Boolean is
218 return Left
.Timeout
< Right
.Timeout
;
221 -- Start of processing for Insert_Into_Queue
226 Write_Lock
(Event_Queue_Lock
'Access);
228 All_Events
.Append
(This
);
230 -- A critical property of the implementation of this package is that
231 -- all occurrences are in ascending order by Timeout. Thus the first
232 -- event in the queue always has the "next" value for the Timer task
233 -- to use in its delay statement.
235 By_Timeout
.Sort
(All_Events
);
237 Unlock
(Event_Queue_Lock
'Access);
239 SSL
.Abort_Undefer
.all;
240 end Insert_Into_Queue
;
242 -----------------------
243 -- Remove_From_Queue --
244 -----------------------
246 procedure Remove_From_Queue
(This
: Any_Timing_Event
) is
253 Write_Lock
(Event_Queue_Lock
'Access);
255 Location
:= All_Events
.Find
(This
);
257 if Location
/= No_Element
then
258 All_Events
.Delete
(Location
);
261 Unlock
(Event_Queue_Lock
'Access);
263 SSL
.Abort_Undefer
.all;
264 end Remove_From_Queue
;
270 procedure Set_Handler
271 (Event
: in out Timing_Event
;
273 Handler
: Timing_Event_Handler
)
276 Remove_From_Queue
(Event
'Unchecked_Access);
277 Event
.Handler
:= null;
279 -- RM D.15(15/2) required that at this point, we check whether the time
280 -- has already passed, and if so, call Handler.all directly from here
281 -- instead of doing the enqueuing below. However, this caused a nasty
282 -- race condition and potential deadlock. If the current task has
283 -- already locked the protected object of Handler.all, and the time has
284 -- passed, deadlock would occur. It has been fixed by AI05-0094-1, which
285 -- says that the handler should be executed as soon as possible, meaning
286 -- that the timing event will be executed after the protected action
287 -- finishes (Handler.all should not be called directly from here).
288 -- The same comment applies to the other Set_Handler below.
290 if Handler
/= null then
291 Event
.Timeout
:= At_Time
;
292 Event
.Handler
:= Handler
;
293 Insert_Into_Queue
(Event
'Unchecked_Access);
301 procedure Set_Handler
302 (Event
: in out Timing_Event
;
304 Handler
: Timing_Event_Handler
)
307 Remove_From_Queue
(Event
'Unchecked_Access);
308 Event
.Handler
:= null;
310 -- See comment in the other Set_Handler above
312 if Handler
/= null then
313 Event
.Timeout
:= Clock
+ In_Time
;
314 Event
.Handler
:= Handler
;
315 Insert_Into_Queue
(Event
'Unchecked_Access);
319 ---------------------
320 -- Current_Handler --
321 ---------------------
323 function Current_Handler
324 (Event
: Timing_Event
) return Timing_Event_Handler
327 return Event
.Handler
;
334 procedure Cancel_Handler
335 (Event
: in out Timing_Event
;
336 Cancelled
: out Boolean)
339 Remove_From_Queue
(Event
'Unchecked_Access);
340 Cancelled
:= Event
.Handler
/= null;
341 Event
.Handler
:= null;
348 function Time_Of_Event
(Event
: Timing_Event
) return Time
is
350 -- RM D.15(18/2): Time_First must be returned in the event is not set
352 return (if Event
.Handler
= null then Time_First
else Event
.Timeout
);
359 procedure Finalize
(This
: in out Timing_Event
) is
361 -- D.15 (19/2) says finalization clears the event
363 This
.Handler
:= null;
364 Remove_From_Queue
(This
'Unchecked_Access);
367 end Ada
.Real_Time
.Timing_Events
;