1 /******************************************************************************
2 3 Base class for registrable client objects for the SelectDispatcher
4 5 Contains the three things that the SelectDispatcher needs:
6 1. the I/O device instance
7 2. the I/O events to register the device for
8 3. the event handler to invocate when an event occured for the device
9 10 In addition a subclass may override finalize(). When handle() returns false
11 or throws an Exception, the ISelectClient instance is unregistered from the
12 SelectDispatcher and finalize() is invoked.
13 14 Copyright:
15 Copyright (c) 2009-2016 dunnhumby Germany GmbH.
16 All rights reserved.
17 18 License:
19 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details.
20 Alternatively, this file may be distributed under the terms of the Tango
21 3-Clause BSD License (see LICENSE_BSD.txt for details).
22 23 ******************************************************************************/24 25 moduleocean.io.select.client.model.ISelectClient;
26 27 28 29 30 importocean.meta.types.Qualifiers;
31 32 importocean.core.Verify;
33 34 importocean.io.select.client.model.ISelectClientInfo;
35 36 importocean.sys.Epoll;
37 38 importocean.io.model.IConduit: ISelectable;
39 40 importocean.time.timeout.model.ITimeoutClient,
41 ocean.time.timeout.model.IExpiryRegistration: IExpiryRegistration;
42 43 importocean.core.Array: concat, append;
44 45 importocean.text.util.ClassName;
46 47 debugimportocean.io.Stdout;
48 49 importocean.text.convert.Formatter;
50 51 /******************************************************************************
52 53 ISelectClient abstract class
54 55 ******************************************************************************/56 57 publicabstractclassISelectClient : ITimeoutClient, ISelectable, ISelectClientInfo58 {
59 /**************************************************************************
60 61 Convenience alias to avoid public imports
62 63 **************************************************************************/64 65 publicalias .ISelectableISelectable;
66 67 /**************************************************************************
68 69 Enum of event types
70 71 **************************************************************************/72 73 aliasEpoll.EventEvent;
74 75 /**************************************************************************
76 77 Enum of the status when finalize() is called.
78 79 **************************************************************************/80 81 enumFinalizeStatus : uint82 {
83 Success = 0,
84 Error,
85 Timeout86 }
87 88 /**************************************************************************
89 90 I/O device instance
91 92 Note: Conforming to the name convention used in ocean.io.selector, the
93 ISelectable instance is named "conduit" although ISelectable and
94 IConduit are distinct from each other. However, in most application
95 cases the provided instance will originally implement both ISelectable
96 and IConduit (as, for example, ocean.io.device.Device and
97 ocean.net.device.Socket).
98 99 **************************************************************************/100 101 publicabstractHandlefileHandle ( );
102 103 /**************************************************************************
104 105 Events to register the conduit for.
106 107 **************************************************************************/108 109 publicabstractEventevents ( );
110 111 /**************************************************************************
112 113 Connection time out in microseconds. Effective only when used with an
114 EpollSelectDispatcher which has timeouts enabled. A value of 0 has no
115 effect.
116 117 **************************************************************************/118 119 publiculongtimeout_us = 0;
120 121 /**************************************************************************
122 123 Timeout expiry registration instance
124 125 **************************************************************************/126 127 privateIExpiryRegistrationexpiry_registration_;
128 129 /**************************************************************************
130 131 The "my conduit is registered with epoll with my events and me as
132 attachment" flag, set by registered() and cleared by unregistered().
133 134 Notes:
135 1. The system can automatically unregister the conduit when its
136 file descriptor is closed; when this happens this flag is true by
137 mistake. The EpollSelectDispatcher is aware of that. However,
138 this flag can never be false by mistake.
139 2. There are use cases where several instances of this class share
140 the same conduit. Exactly one instance is associated to the
141 conduit registration and has is_registered_ = true. For the other
142 instances is_registered_ is false although their conduit is in
143 fact registered with epoll.
144 145 **************************************************************************/146 147 privateboolis_registered_;
148 149 /**************************************************************************
150 151 Sets the timeout manager expiry registration.
152 153 Params:
154 expiry_registration_ = timeout manager expiry registration
155 156 Returns:
157 timeout manager expiry registration
158 159 **************************************************************************/160 161 publicIExpiryRegistrationexpiry_registration ( IExpiryRegistrationexpiry_registration_ )
162 {
163 returnthis.expiry_registration_ = expiry_registration_;
164 }
165 166 /***************************************************************************
167 168 Returns:
169 true if this client has timed out or false otherwise.
170 171 ***************************************************************************/172 173 publicbooltimed_out ( )
174 {
175 return (this.expiry_registration_ !isnull)?
176 this.expiry_registration_.timed_out : false;
177 }
178 179 /**************************************************************************
180 181 I/O event handler
182 183 Params:
184 event = identifier of I/O event that just occured on the device
185 186 Returns:
187 true if the handler should be called again on next event occurrence
188 or false if this instance should be unregistered from the
189 SelectDispatcher.
190 191 **************************************************************************/192 193 abstractpublicboolhandle ( Eventevent );
194 195 /**************************************************************************
196 197 Timeout method, called after a timeout occurs in the SelectDispatcher
198 eventLoop. Intended to be overridden by a subclass if required.
199 200 **************************************************************************/201 202 publicvoidtimeout ( ) { }
203 204 /**************************************************************************
205 206 Finalize method, called after this instance has been unregistered from
207 the Dispatcher. Intended to be overridden by a subclass if required.
208 209 Params:
210 status = status why this method is called
211 212 **************************************************************************/213 214 publicvoidfinalize ( FinalizeStatusstatus ) { }
215 216 /**************************************************************************
217 218 Error reporting method, called when an Exception is caught from
219 handle(). Calls the error_() method, which should be overridden by a
220 subclass if required.
221 222 Note that this method will catch all exceptions thrown by the error_()
223 method. This is to prevent unhandled exceptions flying out of the select
224 dispatcher and bringing down the event loop.
225 226 Params:
227 exception = Exception thrown by handle()
228 event = Selector event while exception was caught
229 230 **************************************************************************/231 232 finalpublicvoiderror ( Exceptionexception, Eventevent = Event.None )
233 {
234 try235 {
236 this.error_(exception, event);
237 }
238 catch ( Exceptione )
239 {
240 // Note: this should *never* happen! In case it ever does, here's241 // a helpful printout to notify the application programmer.242 debugStderr.formatln(
243 "Very bad: Exception thrown from inside ISelectClient.error() delegate! -- {} ({}:{})",
244 e.message(), e.file, e.line245 );
246 }
247 }
248 249 protectedvoiderror_ ( Exceptionexception, Eventevent ) { }
250 251 252 /**************************************************************************
253 254 Obtains the current error code of the underlying I/O device.
255 256 To be overridden by a subclass for I/O devices that support querying a
257 device specific error status (e.g. sockets with getsockopt()).
258 259 Returns:
260 the current error code of the underlying I/O device.
261 262 **************************************************************************/263 264 publicinterror_code ( )
265 {
266 return0;
267 }
268 269 /**************************************************************************
270 271 Register method, called after this client is registered with the
272 SelectDispatcher. Intended to be overridden by a subclass if required.
273 274 **************************************************************************/275 276 finalpublicvoidregistered ( )
277 {
278 verify(!this.is_registered_, classname(this) ~ ".registered(): already registered");
279 280 this.is_registered_ = true;
281 282 tryif (this.expiry_registration_ !isnull)
283 {
284 this.expiry_registration_.register(this.timeout_us);
285 }
286 finally287 {
288 this.registered_();
289 }
290 }
291 292 /**************************************************************************
293 294 Unregister method, called after this client is unregistered from the
295 SelectDispatcher. Intended to be overridden by a subclass if required.
296 297 **************************************************************************/298 299 finalpublicvoidunregistered ( )
300 {
301 verify(this.is_registered_, classname(this) ~ ".unregistered(): not registered");
302 303 this.is_registered_ = false;
304 305 tryif (this.expiry_registration_ !isnull)
306 {
307 this.expiry_registration_.unregister();
308 }
309 finally310 {
311 this.unregistered_();
312 }
313 }
314 315 /**************************************************************************
316 317 Returns true if this.conduit is currently registered for this.events
318 with this as attachment. Returns false if this.conduit is not registered
319 with epoll or, when multiple instances of this class share the same
320 conduit, if it is registered with another instance.
321 322 Note that the returned value can be true by mistake when epoll
323 unexpectedly unregistered the conduit file descriptor as it happens when
324 the file descriptor is closed (e.g. on error). However, the returned
325 value cannot be true by mistake.
326 327 Returns:
328 true if this.conduit is currently registered for this.events with
329 this as attachment or false otherwise.
330 331 **************************************************************************/332 333 publicboolis_registered ( )
334 {
335 returnthis.is_registered_;
336 }
337 338 /***************************************************************************
339 340 ISelectClientInfo method.
341 342 Returns:
343 I/O timeout value of client in microseconds. A value of 0 means that
344 no timeout is set for this client
345 346 ***************************************************************************/347 348 publiculongtimeout_value_us ( )
349 {
350 returnthis.timeout_us;
351 }
352 353 /**************************************************************************
354 355 Called by registered(); may be overridden by a subclass.
356 357 **************************************************************************/358 359 protectedvoidregistered_ ( ) { }
360 361 /**************************************************************************
362 363 Called by unregistered(); may be overridden by a subclass.
364 365 **************************************************************************/366 367 protectedvoidunregistered_ ( ) { }
368 369 /**************************************************************************
370 371 Returns an identifier string of this instance. Defaults to the name of
372 the class, but may be overridden if more detailed information is
373 required.
374 375 Note that this method is only ever called in cases where one or more
376 debug compile flags are switched on (ISelectClient, for example). Hence
377 the loop to extract the class name from the full module/class name
378 string is not considered a performance problem.
379 380 Returns:
381 identifier string of this instance
382 383 **************************************************************************/384 385 publiccstringid ( )
386 {
387 returnclassname(this);
388 }
389 390 /***************************************************************************
391 392 Returns a string describing this client, for use in debug messages.
393 394 Returns:
395 string describing client
396 397 ***************************************************************************/398 399 debugpublicoverrideistringtoString ( )
400 {
401 importocean.core.TypeConvert: assumeUnique;
402 mstringto_string_buf;
403 this.fmtInfo((cstringchunk) {to_string_buf ~= chunk;});
404 returnassumeUnique(to_string_buf);
405 }
406 407 /***************************************************************************
408 409 Produces a string containing information about this instance: Dynamic
410 type, file descriptor and events.
411 412 Params:
413 sink = `Layout.convert()`-style sink of string chunks
414 415 ***************************************************************************/416 417 publicvoidfmtInfo ( scopevoiddelegate ( cstringchunk ) sink )
418 {
419 sformat(
420 (cstringchunk) {sink(chunk); },
421 "{} fd={} events=", this.id, this.fileHandle422 );
423 foreach ( event, name; epoll_event_t.event_to_name )
424 {
425 if ( this.events & event )
426 {
427 sink(name);
428 }
429 }
430 }
431 }
432 433 /******************************************************************************
434 435 IAdvancedSelectClient abstract class
436 437 Provides a set of interfaces which can be implemented by classes which
438 desire notification of various events in the select client, and a set of
439 corresponding methods which allow the user to pass an instance of these
440 interfaces to an instance of this class:
441 * IFinalizer interface, set by the finalizer() method, called when a
442 select client is unregistered.
443 * IErrorReporter interface, set by the error_reporter() method, called
444 when an error occurs while handling a select client.
445 * ITimeoutReporter interface, set by the timeout_reporter() method,
446 called when a timeout occurs while handling a select client.
447 * IConnectionInfo interface, set by the connection_info() method, called
448 in debug(ISelectClient) mode when the selector wishes to get a string
449 containing information about the connection a select client is using.
450 451 ******************************************************************************/452 453 abstractclassIAdvancedSelectClient : ISelectClient454 {
455 /**************************************************************************/456 457 interfaceIFinalizer458 {
459 aliasIAdvancedSelectClient.FinalizeStatusFinalizeStatus;
460 461 voidfinalize ( FinalizeStatusstatus );
462 }
463 464 /**************************************************************************/465 466 interfaceIErrorReporter467 {
468 voiderror ( Exceptionexception, Eventevent = Event.None );
469 }
470 471 /**************************************************************************/472 473 interfaceITimeoutReporter474 {
475 voidtimeout ( );
476 }
477 478 /**************************************************************************
479 480 Interface instances
481 482 **************************************************************************/483 484 privateIFinalizerfinalizer_ = null;
485 privateIErrorReportererror_reporter_ = null;
486 privateITimeoutReportertimeout_reporter_ = null;
487 488 /**************************************************************************
489 490 Sets the Finalizer. May be set to null to disable finalizing.
491 492 Params:
493 finalizer_ = IFinalizer instance
494 495 **************************************************************************/496 497 publicvoidfinalizer ( IFinalizerfinalizer_ )
498 {
499 this.finalizer_ = finalizer_;
500 }
501 502 /**************************************************************************
503 504 Sets the TimeoutReporter. May be set to null to disable timeout
505 reporting.
506 507 Params:
508 timeout_reporter_ = ITimeoutReporter instance
509 510 **************************************************************************/511 512 publicvoidtimeout_reporter ( ITimeoutReportertimeout_reporter_ )
513 {
514 this.timeout_reporter_ = timeout_reporter_;
515 }
516 517 /**************************************************************************
518 519 Sets the Error Reporter. May be set to null to disable error reporting.
520 521 Params:
522 error_reporter_ = IErrorReporter instance
523 524 **************************************************************************/525 526 publicvoiderror_reporter ( IErrorReportererror_reporter_ )
527 {
528 this.error_reporter_ = error_reporter_;
529 }
530 531 /**************************************************************************
532 533 Finalize method, called after this instance has been unregistered from
534 the Dispatcher.
535 536 **************************************************************************/537 538 publicoverridevoidfinalize ( FinalizeStatusstatus )
539 {
540 if (this.finalizer_ !isnull)
541 {
542 this.finalizer_.finalize(status);
543 }
544 }
545 546 /**************************************************************************
547 548 Error reporting method, called when an Exception is caught from
549 super.handle().
550 551 Params:
552 exception = Exception thrown by handle()
553 event = Selector event while exception was caught
554 555 **************************************************************************/556 557 overrideprotectedvoiderror_ ( Exceptionexception, Eventevent )
558 {
559 if (this.error_reporter_)
560 {
561 this.error_reporter_.error(exception, event);
562 }
563 }
564 565 /**************************************************************************
566 567 Timeout method, called after this a timeout has occurred in the
568 SelectDispatcher.
569 570 **************************************************************************/571 572 overridepublicvoidtimeout ( )
573 {
574 if (this.timeout_reporter_)
575 {
576 this.timeout_reporter_.timeout();
577 }
578 }
579 }