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 }