1 /******************************************************************************* 2 3 Fiber-suspending timer event. Allows a fiber to be suspended for a fixed 4 time period. 5 6 Copyright: 7 Copyright (c) 2009-2016 dunnhumby Germany GmbH. 8 All rights reserved. 9 10 License: 11 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 12 Alternatively, this file may be distributed under the terms of the Tango 13 3-Clause BSD License (see LICENSE_BSD.txt for details). 14 15 *******************************************************************************/ 16 17 module ocean.io.select.client.FiberTimerEvent; 18 19 20 21 22 import ocean.core.MessageFiber; 23 24 import ocean.core.Verify; 25 26 import ocean.sys.TimerFD; 27 28 import ocean.io.select.client.model.IFiberSelectClient; 29 30 import ocean.meta.types.Qualifiers; 31 import core.stdc.math: modf; 32 33 34 35 /******************************************************************************* 36 37 Fiber-suspending timer event. Allows a fiber to be suspended for a fixed 38 time period. 39 40 *******************************************************************************/ 41 42 public class FiberTimerEvent : IFiberSelectClient 43 { 44 /*************************************************************************** 45 46 Token used when suspending / resuming fiber. 47 48 ***************************************************************************/ 49 50 static private MessageFiber.Token TimerFired; 51 52 53 /*************************************************************************** 54 55 Static ctor. Initialises fiber token. 56 57 ***************************************************************************/ 58 59 static this ( ) 60 { 61 TimerFired = MessageFiber.Token("timer_fired"); 62 } 63 64 65 /*************************************************************************** 66 67 Timer fd. 68 69 ***************************************************************************/ 70 71 private TimerFD timer; 72 73 74 /*************************************************************************** 75 76 Constructor. Initialises (but does not register) the timer fd. 77 78 Params: 79 fiber = fiber instance to be suspended/resumed by the timer 80 realtime = true: use a settable system-wide clock. 81 false: use a non-settable clock that is not affected by 82 discontinuous changes in the system clock (e.g., manual 83 changes to system time). 84 85 ***************************************************************************/ 86 87 public this ( SelectFiber fiber, bool realtime = false ) 88 { 89 super(fiber); 90 91 this.timer = new TimerFD(realtime); 92 } 93 94 95 /*************************************************************************** 96 97 Returs: 98 the epoll events to register for. 99 100 ***************************************************************************/ 101 102 public override Event events ( ) 103 { 104 return Event.EPOLLIN; 105 } 106 107 108 /*************************************************************************** 109 110 Required by ISelectable interface. 111 112 Returns: 113 file descriptor used to manage custom event 114 115 ***************************************************************************/ 116 117 public override Handle fileHandle ( ) 118 { 119 return this.timer.fileHandle; 120 } 121 122 123 /*************************************************************************** 124 125 Sets the timer to a number of seconds and milliseconds approximating the 126 floating point value specified, registers it, and suspends the fiber 127 until it fires. 128 129 Params: 130 s = number of seconds to suspend fiber for 131 132 In: 133 s must be at least 0, which implies it must not be NaN. +∞ is 134 tolerated and uses the highest possible timer value. 135 136 ***************************************************************************/ 137 138 public void wait ( double s ) 139 { 140 verify(s >= 0); // tolerate +∞ 141 double int_s; 142 auto ms = cast(uint)(modf(s, &int_s) * 1000); 143 this.wait(cast(uint)int_s, ms); 144 } 145 146 /*************************************************************************** 147 148 Sets the timer to the specified number of seconds and milliseconds, 149 registers it, and suspends the fiber until it fires. If both seconds and 150 milliseconds are 0, the fiber is not suspended and the event is not 151 registered with epoll -- no pause occurs. 152 153 Params: 154 s = number of seconds to suspend fiber for 155 ms = number of additional milliseconds to suspend fiber for 156 157 ***************************************************************************/ 158 159 public void wait ( uint s, uint ms = 0 ) 160 { 161 if ( s == 0 && ms == 0 ) return; 162 163 this.timer.set(s, ms, 0, 0); 164 this.fiber.register(this); 165 this.fiber.suspend(TimerFired, this, this.fiber.Message(true)); 166 } 167 168 169 /*************************************************************************** 170 171 Handles events which occurred for the timer event fd. Resumes the fiber. 172 173 (Implements an abstract super class method.) 174 175 Params: 176 events = events which occurred for the fd 177 178 Returns: 179 false if the fiber is finished or true if it keeps going 180 181 ***************************************************************************/ 182 183 public override bool handle ( Event events ) 184 { 185 verify(this.fiber.waiting); 186 this.timer.handle(); 187 188 SelectFiber.Message message = this.fiber.resume(TimerFired, this); 189 190 // FIXME: this should actually always return false, as we always want 191 // the timer to be one-shot. However, there is a fundamental bug with 192 // the way the messages are handled. The problem is that 193 // IFiberSelectClient.finalize() does not know whether the fiber is 194 // still in use (suspended with no client registered) or whether it 195 // should be killed. This will need to be revisited and fixed. 196 return (message.active == message.active.num)? message.num != 0 : false; 197 } 198 199 200 /*************************************************************************** 201 202 Returns: 203 identifier string for this instance, including the remaining time 204 205 ***************************************************************************/ 206 207 debug 208 { 209 private mstring time_buffer; 210 import ocean.core.Array : copy; 211 import ocean.text.convert.Formatter; 212 213 public override cstring id ( ) 214 { 215 this.time_buffer.copy(super.id()); 216 auto time = this.timer.time(); 217 218 sformat(this.time_buffer, ": {}s {}ns", 219 time.it_value.tv_sec, time.it_value.tv_nsec); 220 return this.time_buffer; 221 } 222 } 223 224 }