1 /*******************************************************************************
2 
3     Linux timer event file descriptor.
4 
5     Copyright:
6         Copyright (c) 2009-2016 dunnhumby Germany GmbH.
7         All rights reserved.
8 
9     License:
10         Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
11         Alternatively, this file may be distributed under the terms of the Tango
12         3-Clause BSD License (see LICENSE_BSD.txt for details).
13 
14 *******************************************************************************/
15 
16 module ocean.sys.TimerFD;
17 
18 import ocean.io.model.IConduit: ISelectable;
19 import ocean.meta.codegen.Identifier;
20 import ocean.sys.ErrnoException;
21 
22 import core.stdc.errno: EAGAIN, EWOULDBLOCK, errno;
23 import Upstream = core.sys.linux.timerfd;
24 import core.sys.posix.time: time_t, timespec, itimerspec, CLOCK_REALTIME;
25 import core.sys.posix.sys.types: ssize_t;
26 import core.sys.posix.unistd: read, close;
27 
28 /*******************************************************************************
29 
30     Timer fd class -- can be used in an allocation-free context if instantiated
31     with the ctor which accepts an exception instance.
32 
33 *******************************************************************************/
34 
35 public class TimerFD : ISelectable
36 {
37     import ocean.sys.CloseOnExec;
38 
39     /***************************************************************************
40 
41         Set to true to use an absolute or false for a relative timer. On default
42         a relative timer is used.
43 
44     ***************************************************************************/
45 
46     public bool absolute = false;
47 
48 
49     /***************************************************************************
50 
51         Exception class, thrown on errors with timer functions
52 
53     ***************************************************************************/
54 
55     static public class TimerException : ErrnoException { }
56 
57     /***************************************************************************
58 
59         Timer exception instance.
60 
61     ***************************************************************************/
62 
63     private TimerException e;
64 
65 
66     /***************************************************************************
67 
68         Integer file descriptor provided by the operating system and used to
69         manage the timer event.
70 
71     ***************************************************************************/
72 
73     private int fd;
74 
75 
76     /***************************************************************************
77 
78         Constructor.
79 
80         Params:
81             realtime = true:  use a settable system-wide clock.
82                        false: use a non-settable clock that is not affected by
83                        discontinuous changes in the system clock (e.g., manual
84                        changes to system time).
85 
86         Throws:
87             upon failure to create a timer fd
88 
89     ***************************************************************************/
90 
91     public this ( bool realtime = false )
92     {
93         this(new TimerException, realtime);
94     }
95 
96 
97     /***************************************************************************
98 
99         Constructor. Creates a timer event file descriptor.
100 
101         Params:
102             e = timer exception instance to be used internally
103             realtime = true:  use a settable system-wide clock.
104                        false: use a non-settable clock that is not affected by
105                        discontinuous changes in the system clock (e.g., manual
106                        changes to system time).
107 
108         Throws:
109             upon failure to create a timer fd
110 
111     ***************************************************************************/
112 
113     public this ( TimerException e, bool realtime = false )
114     {
115         this.e = e;
116         static bool verify (int fd) { return fd >= 0; }
117         this.fd = this.e.enforceRet!(Upstream.timerfd_create)(&verify).call(
118             realtime ? Upstream.CLOCK_REALTIME : Upstream.CLOCK_MONOTONIC,
119             setCloExec(Upstream.TFD_NONBLOCK, Upstream.TFD_CLOEXEC)
120         );
121     }
122 
123     /***************************************************************************
124 
125         Destructor. Destroys the timer event file descriptor.
126 
127     ***************************************************************************/
128 
129     ~this ( )
130     {
131         .close(this.fd);
132     }
133 
134 
135     /***************************************************************************
136 
137         Required by ISelectable interface.
138 
139         Returns:
140             file descriptor used to manage timer event
141 
142     ***************************************************************************/
143 
144     public Handle fileHandle ( )
145     {
146         return cast(Handle)this.fd;
147     }
148 
149 
150     /***************************************************************************
151 
152         Returns the next expiration time.
153 
154         Returns:
155             itimerspec instance containing the next expiration time.
156             - it_value: the amount of time until the timer will next expire. If
157                  both fields are zero, then the timer is currently disarmed.
158                  Contains always a relative value.
159             - it_interval: the interval of the timer. If both fields are zero,
160                  then the timer is set to expire just once, at the time
161                  specified by it_value.
162 
163         Throws:
164             upon failure to get the time from the timer fd
165 
166     ***************************************************************************/
167 
168     public itimerspec time ( )
169     {
170         itimerspec t;
171         this.e.enforceRetCode!(Upstream.timerfd_gettime)().call(this.fd, &t);
172         return t;
173     }
174 
175 
176     /***************************************************************************
177 
178         Sets next expiration time of interval timer.
179 
180         Params:
181             first =    Specifies the initial expiration of the timer. Setting
182                        either field to a non-zero value arms the timer. Setting
183                        both fields to zero disarms the timer.
184 
185             interval = Setting one or both fields to non-zero values specifies
186                        the period for repeated timer expirations after the
187                        initial expiration. If both fields are zero, the timer
188                        expires just once, at the time specified by it_value.
189 
190         Returns:
191             the previous expiration time as time().
192 
193         Throws:
194             upon failure to set the time
195 
196     ***************************************************************************/
197 
198     public itimerspec set ( timespec first, timespec interval = timespec.init )
199     {
200         itimerspec t_new = itimerspec(interval, first);
201         itimerspec t_old;
202 
203         this.e.enforceRetCode!(Upstream.timerfd_settime)().call(
204             this.fd,
205             this.absolute? Upstream.TFD_TIMER_ABSTIME : 0,
206             &t_new,
207             &t_old
208         );
209 
210         return t_old;
211     }
212 
213 
214     /***************************************************************************
215 
216         Sets next expiration time of interval timer.
217 
218         Setting first_s or first_ms to a non-zero value arms the timer. Setting
219         both to zero disarms the timer.
220         If both interval_s and interval_ms are zero, the timer expires just
221         once, at the time specified by first_s and first_ms.
222 
223         Params:
224             first_s     = Specifies the number of seconds of the initial
225                           expiration of the timer.
226 
227             first_ms    = Specifies an amount of milliseconds that will be added
228                           to first_s.
229 
230             interval_s = Specifies the number of seconds of the period for
231                           repeated timer expirations after the initial
232                           expiration.
233 
234             interval_ms = Specifies an amount of milliseconds that will be added
235                           to interval_ms.
236 
237         Returns:
238             the previous expiration time as time().
239 
240         Throws:
241             upon failure to set the time
242 
243     ***************************************************************************/
244 
245     public itimerspec set ( time_t first_s,        uint first_ms,
246                             time_t interval_s = 0, uint interval_ms = 0 )
247     {
248         return this.set(timespec(first_s,    first_ms    * 1_000_000),
249                         timespec(interval_s, interval_ms * 1_000_000));
250     }
251 
252 
253     /***************************************************************************
254 
255         Resets/disarms the timer.
256 
257         Returns:
258             Returns the previous expiration time as time().
259 
260         Throws:
261             upon failure to set the time
262 
263     ***************************************************************************/
264 
265     public itimerspec reset ( )
266     {
267         return this.set(timespec.init);
268     }
269 
270 
271     /***************************************************************************
272 
273         Should be called when the timer event has fired.
274 
275         Returns:
276             the number of times the event has been triggered since the last call
277             to handle().
278 
279     ***************************************************************************/
280 
281     public ulong handle ( )
282     {
283         ulong n;
284 
285         if (.read(this.fd, &n, n.sizeof) < 0)
286         {
287             scope (exit) errno = 0;
288 
289             int errnum = errno;
290 
291             switch (errnum)
292             {
293                 case EAGAIN:
294                 static if (EAGAIN != EWOULDBLOCK) case EWOULDBLOCK:
295                     return true;
296 
297                 default:
298                     throw this.e.set(errnum, identifier!(.read));
299             }
300         }
301         else
302         {
303             return n;
304         }
305     }
306 }