1 /******************************************************************************* 2 3 Hosts a ITimeoutClient with a timeout value to be managed by the 4 TimeoutManager. 5 6 Build flags: 7 -debug=TimeoutManager = verbose output 8 9 Copyright: 10 Copyright (c) 2009-2016 dunnhumby Germany GmbH. 11 All rights reserved. 12 13 License: 14 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 15 Alternatively, this file may be distributed under the terms of the Tango 16 3-Clause BSD License (see LICENSE_BSD.txt for details). 17 18 *******************************************************************************/ 19 20 module ocean.time.timeout.model.ExpiryRegistrationBase; 21 22 23 import ocean.meta.types.Qualifiers; 24 25 import ocean.time.timeout.model.IExpiryRegistration, 26 ocean.time.timeout.model.ITimeoutClient; 27 28 /******************************************************************************* 29 30 The EBTree import and aliases should be in the TimeoutManager module and are 31 here only to work around DMD's flaw of supporting mutual module imports. 32 33 TODO: Move to the TimeoutManager module when DMD is fixed. 34 35 *******************************************************************************/ 36 37 import ocean.util.container.ebtree.EBTree64; 38 39 alias EBTree64!() ExpiryTree; 40 41 alias ExpiryTree.Node Expiry; 42 43 /******************************************************************************* 44 45 Struct storing a reference to an expiry time registry and an item in the 46 registry. An instance of this struct should be owned by each client which is 47 to be registered with the expiry time registry. 48 49 *******************************************************************************/ 50 51 abstract class ExpiryRegistrationBase : IExpiryRegistration 52 { 53 /*************************************************************************** 54 55 Enables access of TimeoutManager internals. 56 57 ***************************************************************************/ 58 59 interface ITimeoutManagerInternal 60 { 61 /*********************************************************************** 62 63 Registers registration and sets the timeout. 64 65 Params: 66 registration = IExpiryRegistration instance to register 67 timeout_us = timeout in microseconds from now 68 69 Returns: 70 expiry token: required for unregister(); "key" member reflects 71 the expiration wall clock time. 72 73 ***********************************************************************/ 74 75 Expiry* register ( IExpiryRegistration registration, ulong timeout_us ); 76 77 /*********************************************************************** 78 79 Unregisters IExpiryRegistration instance corresponding to expiry. 80 81 Params: 82 expiry = expiry token returned by register() when registering 83 the IExpiryRegistration instance to unregister 84 85 In: 86 Must not be called from within timeout(). Doing so would still 87 leave already fired timer events in the TimeoutManager internal 88 list and their respective `timeout` will be called despite 89 unregistration. 90 91 ***********************************************************************/ 92 93 void unregister ( ref Expiry expiry ); 94 95 /*********************************************************************** 96 97 Unregisters the specified expiry. If the expiry is present in the 98 list of expired registrations being currently iterated over by 99 checkTimeouts, then it will be removed (its timeout method will not 100 be called). (This means that drop can be called from timeout 101 callbacks, unlike unregister.) 102 103 Params: 104 registration = expiry registration reference 105 106 ***********************************************************************/ 107 108 void drop ( IExpiryRegistration registration ); 109 110 /*********************************************************************** 111 112 Returns: 113 the current wall clock time as UNIX time in microseconds. 114 115 ***********************************************************************/ 116 117 ulong now ( ); 118 } 119 120 /*************************************************************************** 121 122 Timeout client: Object that times out after register() has been called 123 when the time interval passed to register() has expired. 124 125 The client instance is set by a subclass. The subclass must make sure 126 that a client instance is set before it calls register(). It may reset 127 the client instance to null after it has called unregister() (even if 128 unregister() throws an exception). 129 130 ***************************************************************************/ 131 132 protected ITimeoutClient client = null; 133 134 /*************************************************************************** 135 136 Reference to an expiry time item in the registry; this is the key 137 returned from register() and passed to unregister(). 138 The expiry item is null if and only if the client is registered with the 139 timeout manager. 140 141 ***************************************************************************/ 142 143 /* package(ocean) */ 144 public Expiry* expiry = null; 145 146 /*************************************************************************** 147 148 Object providing access to a timeout manager instance to 149 register/unregister a client with that timeout manager. 150 151 ***************************************************************************/ 152 153 private ITimeoutManagerInternal mgr; 154 155 /*************************************************************************** 156 157 "Timed out" flag: set by timeout() and cleared by register(). 158 159 ***************************************************************************/ 160 161 private bool timed_out_ = false; 162 163 /*************************************************************************** 164 165 Makes sure we have a client while registered. 166 167 ***************************************************************************/ 168 169 invariant ( ) 170 { 171 assert (this.client !is null || this.expiry is null, "client required when registered"); 172 } 173 174 /*************************************************************************** 175 176 Constructor 177 178 Params: 179 mgr = object providing access to a timeout manager instance to 180 register/unregister a client with that timeout manager. 181 182 ***************************************************************************/ 183 184 protected this ( ITimeoutManagerInternal mgr ) 185 { 186 this.mgr = mgr; 187 } 188 189 /*************************************************************************** 190 191 Unregisters the current client. 192 If a client is currently not registered, nothing is done. 193 194 The subclass may reset the client instance to null after it has called 195 this method (even if it throws an exception). 196 197 Returns: 198 true on success or false if no client was registered. 199 200 In: 201 Must not be called from within timeout(). 202 203 ***************************************************************************/ 204 205 public bool unregister ( ) 206 { 207 if (this.expiry) 208 { 209 try 210 { 211 debug (TimeoutManager) 212 { 213 Stderr("*** unregister ")(this.id)('\n').flush(); 214 } 215 216 this.mgr.unregister(*this.expiry); 217 218 return true; 219 } 220 finally 221 { 222 this.expiry = null; 223 } 224 } 225 else 226 { 227 return false; 228 } 229 230 } 231 232 /*************************************************************************** 233 234 Same as `unregister` but also removes expirty from list of currently 235 expired registrations. That means it can be called from `timeout` 236 callbacks, contrary to `unregister`. 237 238 Returns: 239 true on success or false if no client was registered. 240 241 ***************************************************************************/ 242 243 public bool drop ( ) 244 { 245 this.mgr.drop(this); 246 return this.unregister(); 247 } 248 249 /*************************************************************************** 250 251 Returns: 252 the client timeout wall clock time as UNIX time in microseconds, if 253 a client is currently registered, or ulong.max otherwise. 254 255 ***************************************************************************/ 256 257 public ulong expires ( ) 258 { 259 return this.expiry? this.expiry.key : ulong.max; 260 } 261 262 /*************************************************************************** 263 264 Returns: 265 the number of microseconds left until timeout from now, if a client 266 is currently registered, or long.max otherwise. A negative value 267 indicates that the client has timed out but was not yet 268 unregistered. 269 270 ***************************************************************************/ 271 272 public long us_left ( ) 273 in 274 { 275 assert (this.expiry, "not registered"); 276 } 277 do 278 { 279 return this.expiry? this.expiry.key - this.mgr.now : long.max; 280 } 281 282 /*************************************************************************** 283 284 Invokes the timeout() method of the client. 285 286 Should only be called from inside the timeout manager. 287 288 Returns: 289 current client which has been notified that it has timed out. 290 291 In: 292 A client must be registered. 293 294 ***************************************************************************/ 295 296 public ITimeoutClient timeout ( ) 297 in 298 { 299 assert (this.expiry !is null, "timeout - no client"); // The invariant makes sure that 300 } // this.client !is null if this.expiry !is null. 301 do 302 { 303 debug ( TimeoutManager ) Stderr("*** timeout for ")(this.id)('\n').flush(); 304 305 this.timed_out_ = true; 306 307 this.client.timeout(); 308 309 return this.client; 310 } 311 312 /*************************************************************************** 313 314 Returns: 315 true if the client has timed out or false otherwise. 316 317 ***************************************************************************/ 318 319 public bool timed_out ( ) 320 { 321 return this.timed_out_; 322 } 323 324 /*************************************************************************** 325 326 Returns: 327 true if the client is registered or false otherwise 328 329 ***************************************************************************/ 330 331 public bool registered ( ) 332 { 333 return this.expiry !is null; 334 } 335 336 /*************************************************************************** 337 338 Sets the timeout for the client and registers it with the timeout 339 manager. On timeout the client will automatically be unregistered. 340 The client must not already be registered. 341 342 The subclass must make sure that a client instance is set before it 343 calls this method. It may reset the client instance to null after it has 344 called unregister() (even if unregister() throws an exception). 345 346 Params: 347 timeout_us = timeout in microseconds from now. 0 is ignored. 348 349 Returns: 350 true if registered or false if timeout_us is 0. 351 352 In: 353 - this.client must not be null. 354 - The client must not already be registered. 355 356 ***************************************************************************/ 357 358 public bool register ( ulong timeout_us ) 359 in 360 { 361 assert (this.expiry is null, "already registered"); 362 assert (this.client !is null, "client required to register"); 363 } 364 do 365 { 366 debug ( TimeoutManager ) Stderr("*** register ")(this.id)(": "); 367 368 this.timed_out_ = false; 369 370 if (timeout_us) 371 { 372 debug ( TimeoutManager ) Stderr(timeout_us)(" µs\n").flush(); 373 374 this.expiry = this.mgr.register(this, timeout_us); 375 376 return true; 377 } 378 else 379 { 380 debug ( TimeoutManager ) Stderr("no timeout\n").flush(); 381 382 return false; 383 } 384 } 385 386 /*************************************************************************** 387 388 Identifier string for debugging. 389 390 ***************************************************************************/ 391 392 debug protected override cstring id ( ) 393 { 394 return (this.client !is null)? this.client.id : typeof (this).stringof; 395 } 396 }