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 moduleocean.time.timeout.model.ExpiryRegistrationBase;
21 22 23 importocean.meta.types.Qualifiers;
24 25 importocean.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 importocean.util.container.ebtree.EBTree64;
38 39 aliasEBTree64!() ExpiryTree;
40 41 aliasExpiryTree.NodeExpiry;
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 abstractclassExpiryRegistrationBase : IExpiryRegistration52 {
53 /***************************************************************************
54 55 Enables access of TimeoutManager internals.
56 57 ***************************************************************************/58 59 interfaceITimeoutManagerInternal60 {
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 ( IExpiryRegistrationregistration, ulongtimeout_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 voidunregister ( refExpiryexpiry );
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 voiddrop ( IExpiryRegistrationregistration );
109 110 /***********************************************************************
111 112 Returns:
113 the current wall clock time as UNIX time in microseconds.
114 115 ***********************************************************************/116 117 ulongnow ( );
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 protectedITimeoutClientclient = 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 publicExpiry* 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 privateITimeoutManagerInternalmgr;
154 155 /***************************************************************************
156 157 "Timed out" flag: set by timeout() and cleared by register().
158 159 ***************************************************************************/160 161 privatebooltimed_out_ = false;
162 163 /***************************************************************************
164 165 Makes sure we have a client while registered.
166 167 ***************************************************************************/168 169 invariant ( )
170 {
171 assert (this.client !isnull || this.expiryisnull, "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 protectedthis ( ITimeoutManagerInternalmgr )
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 publicboolunregister ( )
206 {
207 if (this.expiry)
208 {
209 try210 {
211 debug (TimeoutManager)
212 {
213 Stderr("*** unregister ")(this.id)('\n').flush();
214 }
215 216 this.mgr.unregister(*this.expiry);
217 218 returntrue;
219 }
220 finally221 {
222 this.expiry = null;
223 }
224 }
225 else226 {
227 returnfalse;
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 publicbooldrop ( )
244 {
245 this.mgr.drop(this);
246 returnthis.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 publiculongexpires ( )
258 {
259 returnthis.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 publiclongus_left ( )
273 in274 {
275 assert (this.expiry, "not registered");
276 }
277 do278 {
279 returnthis.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 publicITimeoutClienttimeout ( )
297 in298 {
299 assert (this.expiry !isnull, "timeout - no client"); // The invariant makes sure that300 } // this.client !is null if this.expiry !is null.301 do302 {
303 debug ( TimeoutManager ) Stderr("*** timeout for ")(this.id)('\n').flush();
304 305 this.timed_out_ = true;
306 307 this.client.timeout();
308 309 returnthis.client;
310 }
311 312 /***************************************************************************
313 314 Returns:
315 true if the client has timed out or false otherwise.
316 317 ***************************************************************************/318 319 publicbooltimed_out ( )
320 {
321 returnthis.timed_out_;
322 }
323 324 /***************************************************************************
325 326 Returns:
327 true if the client is registered or false otherwise
328 329 ***************************************************************************/330 331 publicboolregistered ( )
332 {
333 returnthis.expiry !isnull;
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 publicboolregister ( ulongtimeout_us )
359 in360 {
361 assert (this.expiryisnull, "already registered");
362 assert (this.client !isnull, "client required to register");
363 }
364 do365 {
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 returntrue;
377 }
378 else379 {
380 debug ( TimeoutManager ) Stderr("no timeout\n").flush();
381 382 returnfalse;
383 }
384 }
385 386 /***************************************************************************
387 388 Identifier string for debugging.
389 390 ***************************************************************************/391 392 debugprotectedoverridecstringid ( )
393 {
394 return (this.client !isnull)? this.client.id : typeof (this).stringof;
395 }
396 }