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 }