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 
19 
20 
21 import ocean.sys.ErrnoException;
22 
23 import ocean.io.model.IConduit: ISelectable;
24 
25 import ocean.meta.codegen.Identifier;
26 
27 import core.sys.posix.time: time_t, timespec, itimerspec, CLOCK_REALTIME;
28 
29 import ocean.stdc.posix.sys.types: ssize_t;
30 
31 import core.sys.posix.unistd: read, close;
32 
33 import core.stdc.errno: EAGAIN, EWOULDBLOCK, errno;
34 
35 
36 
37 /*******************************************************************************
38 
39     Definitions of external constants and functions required to manage timer
40     events.
41 
42 *******************************************************************************/
43 
44 /// <sys/timerfd.h>
45 
46 static immutable TFD_TIMER_ABSTIME = 1,
47       TFD_CLOEXEC       = 0x80000, // octal 02000000
48       TFD_NONBLOCK      = 0x800;   // octal 04000
49 
50 /// <linux/time.h>
51 
52 static immutable CLOCK_MONOTONIC = 1;
53 
54 // TODO: move into C bindings
55 
56 extern (C)
57 {
58     /**************************************************************************
59 
60         Creates a new timer object.
61 
62         The file descriptor supports the following operations:
63 
64         read(2)
65                If  the timer has already expired one or more times since its
66                settings were last modified using timerfd_settime(), or since the
67                last successful read(2), then the buffer given to read(2) returns
68                an unsigned 8-byte integer (uint64_t) containing  the  number of
69                expirations that have occurred.  (The returned value is in host
70                byte order, i.e., the native byte order for integers on the host
71                machine.)
72 
73                If no timer expirations have occurred at the time of the read(2),
74                then the call either blocks until the next timer  expiration, or
75                fails with the error EAGAIN if the file descriptor has been made
76                non-blocking (via the use of the fcntl(2) F_SETFL operation to
77                set the O_NONBLOCK flag).
78 
79                A read(2) will fail with the error EINVAL if the size of the
80                supplied buffer is less than 8 bytes.
81 
82         poll(2), select(2) (and similar)
83                The file descriptor is readable (the select(2) readfds argument;
84                the poll(2) POLLIN flag) if one or more timer expirations have
85                occurred.
86 
87                The file descriptor also supports the other file-descriptor
88                multiplexing APIs: pselect(2), ppoll(2), and epoll(7).
89 
90         close(2)
91                When  the  file descriptor is no longer required it should be
92                closed.  When all file descriptors associated with the same timer
93                object have been closed, the timer is disarmed and its resources
94                are freed by the kernel.
95 
96         fork(2) semantics
97             After a fork(2), the child inherits a copy of the file descriptor
98             created by timerfd_create().  The file descriptor refers to the same
99             underlying  timer  object  as the corresponding file descriptor in
100             the parent, and read(2)s in the child will return information about
101             expirations of the timer.
102 
103         execve(2) semantics
104             A file descriptor created by timerfd_create() is preserved across
105             execve(2), and continues to generate timer expirations if the  timer
106             was armed.
107 
108         Params:
109             clockid = Specifies the clock  that is used to mark the progress of
110                       the timer, and must be either CLOCK_REALTIME or
111                       CLOCK_MONOTONIC.
112                       - CLOCK_REALTIME is a settable system-wide clock.
113                       - CLOCK_MONOTONIC is a non-settable clock that is not
114                           affected by discontinuous changes in the system clock
115                           (e.g., manual changes to system time). The current
116                           value of each of these clocks can be retrieved using
117                           clock_gettime(2).
118 
119             flags   = Starting with Linux 2.6.27: 0 or a bitwise OR combination
120                       of
121                       - TFD_NONBLOCK: Set the O_NONBLOCK file status flag on the
122                             new open file description.
123                       - TFD_CLOEXEC: Set the close-on-exec (FD_CLOEXEC) flag on
124                             the new file descriptor. (See the description of the
125                             O_CLOEXEC  flag  in open(2) for reasons why this may
126                             be useful.)
127 
128                       Up to Linux version 2.6.26: Must be 0.
129 
130         Returns:
131             a file descriptor that refers to that timer
132 
133      **************************************************************************/
134 
135     int timerfd_create(int clockid, int flags = 0);
136 
137 
138     /**************************************************************************
139 
140         Sets next expiration time of interval timer source fd to new_value.
141 
142         Params:
143             fd        = file descriptor referring to the timer
144 
145             flags     = 0 starts a relative timer using new_value.it_interval;
146                         TFD_TIMER_ABSTIME starts an absolute timer using
147                         new_value.it_value.
148 
149             new_value = - it_value: Specifies the initial expiration of the
150                             timer. Setting either field to a non-zero value arms
151                             the timer. Setting both fields to zero disarms the
152                             timer.
153                         - it_interval: Setting one or both fields to non-zero
154                             values specifies the period for repeated timer
155                             expirations after the initial expiration. If both
156                             fields are zero, the timer expires just once, at the
157                             time specified by it_value.
158 
159             old_value = Returns the old expiration time as timerfd_gettime().
160 
161         Returns:
162             0 on success or -1 on error. Sets errno in case of error.
163 
164      **************************************************************************/
165 
166     int timerfd_settime(int fd, int flags,
167                         itimerspec* new_value,
168                         itimerspec* old_value);
169 
170 
171     /**************************************************************************
172 
173         Returns the next expiration time of fd.
174 
175         Params:
176             fd         = file descriptor referring to the timer
177             curr_value = - it_value:
178                              Returns the amount of time until the timer will
179                              next expire. If both fields are zero, then the
180                              timer is currently disarmed. Contains always a
181                              relative value, regardless of whether the
182                              TFD_TIMER_ABSTIME flag was specified when setting
183                              the timer.
184                         - it_interval: Returns the interval of the timer. If
185                              both fields are zero, then the timer is set to
186                              expire just once, at the time specified by
187                              it_value.
188 
189         Returns:
190             0 on success or -1 on error. Sets errno in case of error.
191 
192      **************************************************************************/
193 
194     int timerfd_gettime(int fd, itimerspec* curr_value);
195 }
196 
197 
198 
199 /*******************************************************************************
200 
201     Timer fd class -- can be used in an allocation-free context if instantiated
202     with the ctor which accepts an exception instance.
203 
204 *******************************************************************************/
205 
206 public class TimerFD : ISelectable
207 {
208     import ocean.sys.CloseOnExec;
209 
210     /***************************************************************************
211 
212         Set to true to use an absolute or false for a relative timer. On default
213         a relative timer is used.
214 
215     ***************************************************************************/
216 
217     public bool absolute = false;
218 
219 
220     /***************************************************************************
221 
222         Exception class, thrown on errors with timer functions
223 
224     ***************************************************************************/
225 
226     static public class TimerException : ErrnoException { }
227 
228     /***************************************************************************
229 
230         Timer exception instance.
231 
232     ***************************************************************************/
233 
234     private TimerException e;
235 
236 
237     /***************************************************************************
238 
239         Integer file descriptor provided by the operating system and used to
240         manage the timer event.
241 
242     ***************************************************************************/
243 
244     private int fd;
245 
246 
247     /***************************************************************************
248 
249         Constructor.
250 
251         Params:
252             realtime = true:  use a settable system-wide clock.
253                        false: use a non-settable clock that is not affected by
254                        discontinuous changes in the system clock (e.g., manual
255                        changes to system time).
256 
257         Throws:
258             upon failure to create a timer fd
259 
260     ***************************************************************************/
261 
262     public this ( bool realtime = false )
263     {
264         this(new TimerException, realtime);
265     }
266 
267 
268     /***************************************************************************
269 
270         Constructor. Creates a timer event file descriptor.
271 
272         Params:
273             e = timer exception instance to be used internally
274             realtime = true:  use a settable system-wide clock.
275                        false: use a non-settable clock that is not affected by
276                        discontinuous changes in the system clock (e.g., manual
277                        changes to system time).
278 
279         Throws:
280             upon failure to create a timer fd
281 
282     ***************************************************************************/
283 
284     public this ( TimerException e, bool realtime = false )
285     {
286         this.e = e;
287         static bool verify (int fd) { return fd >= 0; }
288         this.fd = this.e.enforceRet!(.timerfd_create)(&verify).call(
289             realtime ? CLOCK_REALTIME : CLOCK_MONOTONIC,
290             setCloExec(TFD_NONBLOCK, TFD_CLOEXEC)
291         );
292     }
293 
294     /***************************************************************************
295 
296         Destructor. Destroys the timer event file descriptor.
297 
298     ***************************************************************************/
299 
300     ~this ( )
301     {
302         .close(this.fd);
303     }
304 
305 
306     /***************************************************************************
307 
308         Required by ISelectable interface.
309 
310         Returns:
311             file descriptor used to manage timer event
312 
313     ***************************************************************************/
314 
315     public Handle fileHandle ( )
316     {
317         return cast(Handle)this.fd;
318     }
319 
320 
321     /***************************************************************************
322 
323         Returns the next expiration time.
324 
325         Returns:
326             itimerspec instance containing the next expiration time.
327             - it_value: the amount of time until the timer will next expire. If
328                  both fields are zero, then the timer is currently disarmed.
329                  Contains always a relative value.
330             - it_interval: the interval of the timer. If both fields are zero,
331                  then the timer is set to expire just once, at the time
332                  specified by it_value.
333 
334         Throws:
335             upon failure to get the time from the timer fd
336 
337     ***************************************************************************/
338 
339     public itimerspec time ( )
340     {
341         itimerspec t;
342         this.e.enforceRetCode!(timerfd_gettime)().call(this.fd, &t);
343         return t;
344     }
345 
346 
347     /***************************************************************************
348 
349         Sets next expiration time of interval timer.
350 
351         Params:
352             first =    Specifies the initial expiration of the timer. Setting
353                        either field to a non-zero value arms the timer. Setting
354                        both fields to zero disarms the timer.
355 
356             interval = Setting one or both fields to non-zero values specifies
357                        the period for repeated timer expirations after the
358                        initial expiration. If both fields are zero, the timer
359                        expires just once, at the time specified by it_value.
360 
361         Returns:
362             the previous expiration time as time().
363 
364         Throws:
365             upon failure to set the time
366 
367     ***************************************************************************/
368 
369     public itimerspec set ( timespec first, timespec interval = timespec.init )
370     {
371         itimerspec t_new = itimerspec(interval, first);
372         itimerspec t_old;
373 
374         this.e.enforceRetCode!(timerfd_settime)().call(
375             this.fd,
376             this.absolute? TFD_TIMER_ABSTIME : 0,
377             &t_new,
378             &t_old
379         );
380 
381         return t_old;
382     }
383 
384 
385     /***************************************************************************
386 
387         Sets next expiration time of interval timer.
388 
389         Setting first_s or first_ms to a non-zero value arms the timer. Setting
390         both to zero disarms the timer.
391         If both interval_s and interval_ms are zero, the timer expires just
392         once, at the time specified by first_s and first_ms.
393 
394         Params:
395             first_s     = Specifies the number of seconds of the initial
396                           expiration of the timer.
397 
398             first_ms    = Specifies an amount of milliseconds that will be added
399                           to first_s.
400 
401             interval_s = Specifies the number of seconds of the period for
402                           repeated timer expirations after the initial
403                           expiration.
404 
405             interval_ms = Specifies an amount of milliseconds that will be added
406                           to interval_ms.
407 
408         Returns:
409             the previous expiration time as time().
410 
411         Throws:
412             upon failure to set the time
413 
414     ***************************************************************************/
415 
416     public itimerspec set ( time_t first_s,        uint first_ms,
417                             time_t interval_s = 0, uint interval_ms = 0 )
418     {
419         return this.set(timespec(first_s,    first_ms    * 1_000_000),
420                         timespec(interval_s, interval_ms * 1_000_000));
421     }
422 
423 
424     /***************************************************************************
425 
426         Resets/disarms the timer.
427 
428         Returns:
429             Returns the previous expiration time as time().
430 
431         Throws:
432             upon failure to set the time
433 
434     ***************************************************************************/
435 
436     public itimerspec reset ( )
437     {
438         return this.set(timespec.init);
439     }
440 
441 
442     /***************************************************************************
443 
444         Should be called when the timer event has fired.
445 
446         Returns:
447             the number of times the event has been triggered since the last call
448             to handle().
449 
450     ***************************************************************************/
451 
452     public ulong handle ( )
453     {
454         ulong n;
455 
456         if (.read(this.fd, &n, n.sizeof) < 0)
457         {
458             scope (exit) errno = 0;
459 
460             int errnum = errno;
461 
462             switch (errnum)
463             {
464                 case EAGAIN:
465                 static if (EAGAIN != EWOULDBLOCK) case EWOULDBLOCK:
466                     return true;
467 
468                 default:
469                     throw this.e.set(errnum, identifier!(.read));
470             }
471         }
472         else
473         {
474             return n;
475         }
476     }
477 }