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 module ocean.io.select.client.model.ISelectClient; 26 27 28 29 30 import ocean.transition; 31 32 import ocean.core.Verify; 33 34 import ocean.io.select.client.model.ISelectClientInfo; 35 36 import ocean.sys.Epoll; 37 38 import ocean.io.model.IConduit: ISelectable; 39 40 import ocean.time.timeout.model.ITimeoutClient, 41 ocean.time.timeout.model.IExpiryRegistration: IExpiryRegistration; 42 43 import ocean.core.Array: concat, append; 44 45 import ocean.text.util.ClassName; 46 47 debug import ocean.io.Stdout; 48 49 import ocean.text.convert.Formatter; 50 51 /****************************************************************************** 52 53 ISelectClient abstract class 54 55 ******************************************************************************/ 56 57 public abstract class ISelectClient : ITimeoutClient, ISelectable, ISelectClientInfo 58 { 59 /************************************************************************** 60 61 Convenience alias to avoid public imports 62 63 **************************************************************************/ 64 65 public alias .ISelectable ISelectable; 66 67 /************************************************************************** 68 69 Enum of event types 70 71 **************************************************************************/ 72 73 alias Epoll.Event Event; 74 75 /************************************************************************** 76 77 Enum of the status when finalize() is called. 78 79 **************************************************************************/ 80 81 enum FinalizeStatus : uint 82 { 83 Success = 0, 84 Error, 85 Timeout 86 } 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 public abstract Handle fileHandle ( ); 102 103 /************************************************************************** 104 105 Events to register the conduit for. 106 107 **************************************************************************/ 108 109 public abstract Event events ( ); 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 public ulong timeout_us = 0; 120 121 /************************************************************************** 122 123 Timeout expiry registration instance 124 125 **************************************************************************/ 126 127 private IExpiryRegistration expiry_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 private bool is_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 public IExpiryRegistration expiry_registration ( IExpiryRegistration expiry_registration_ ) 162 { 163 return this.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 public bool timed_out ( ) 174 { 175 return (this.expiry_registration_ !is null)? 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 abstract public bool handle ( Event event ); 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 public void timeout ( ) { } 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 public void finalize ( FinalizeStatus status ) { } 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 final public void error ( Exception exception, Event event = Event.None ) 233 { 234 try 235 { 236 this.error_(exception, event); 237 } 238 catch ( Exception e ) 239 { 240 // Note: this should *never* happen! In case it ever does, here's 241 // a helpful printout to notify the application programmer. 242 debug Stderr.formatln( 243 "Very bad: Exception thrown from inside ISelectClient.error() delegate! -- {} ({}:{})", 244 e.message(), e.file, e.line 245 ); 246 } 247 } 248 249 protected void error_ ( Exception exception, Event event ) { } 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 public int error_code ( ) 265 { 266 return 0; 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 final public void registered ( ) 277 { 278 verify(!this.is_registered_, classname(this) ~ ".registered(): already registered"); 279 280 this.is_registered_ = true; 281 282 try if (this.expiry_registration_ !is null) 283 { 284 this.expiry_registration_.register(this.timeout_us); 285 } 286 finally 287 { 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 final public void unregistered ( ) 300 { 301 verify(this.is_registered_, classname(this) ~ ".unregistered(): not registered"); 302 303 this.is_registered_ = false; 304 305 try if (this.expiry_registration_ !is null) 306 { 307 this.expiry_registration_.unregister(); 308 } 309 finally 310 { 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 public bool is_registered ( ) 334 { 335 return this.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 public ulong timeout_value_us ( ) 349 { 350 return this.timeout_us; 351 } 352 353 /************************************************************************** 354 355 Called by registered(); may be overridden by a subclass. 356 357 **************************************************************************/ 358 359 protected void registered_ ( ) { } 360 361 /************************************************************************** 362 363 Called by unregistered(); may be overridden by a subclass. 364 365 **************************************************************************/ 366 367 protected void unregistered_ ( ) { } 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 public cstring id ( ) 386 { 387 return classname(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 debug public override istring toString ( ) 400 { 401 mstring to_string_buf; 402 this.fmtInfo((cstring chunk) {to_string_buf ~= chunk;}); 403 return assumeUnique(to_string_buf); 404 } 405 406 /*************************************************************************** 407 408 Produces a string containing information about this instance: Dynamic 409 type, file descriptor and events. 410 411 Params: 412 sink = `Layout.convert()`-style sink of string chunks 413 414 ***************************************************************************/ 415 416 public void fmtInfo ( scope void delegate ( cstring chunk ) sink ) 417 { 418 sformat( 419 (cstring chunk) {sink(chunk); }, 420 "{} fd={} events=", this.id, this.fileHandle 421 ); 422 foreach ( event, name; epoll_event_t.event_to_name ) 423 { 424 if ( this.events & event ) 425 { 426 sink(name); 427 } 428 } 429 } 430 } 431 432 /****************************************************************************** 433 434 IAdvancedSelectClient abstract class 435 436 Provides a set of interfaces which can be implemented by classes which 437 desire notification of various events in the select client, and a set of 438 corresponding methods which allow the user to pass an instance of these 439 interfaces to an instance of this class: 440 * IFinalizer interface, set by the finalizer() method, called when a 441 select client is unregistered. 442 * IErrorReporter interface, set by the error_reporter() method, called 443 when an error occurs while handling a select client. 444 * ITimeoutReporter interface, set by the timeout_reporter() method, 445 called when a timeout occurs while handling a select client. 446 * IConnectionInfo interface, set by the connection_info() method, called 447 in debug(ISelectClient) mode when the selector wishes to get a string 448 containing information about the connection a select client is using. 449 450 ******************************************************************************/ 451 452 abstract class IAdvancedSelectClient : ISelectClient 453 { 454 /**************************************************************************/ 455 456 interface IFinalizer 457 { 458 alias IAdvancedSelectClient.FinalizeStatus FinalizeStatus; 459 460 void finalize ( FinalizeStatus status ); 461 } 462 463 /**************************************************************************/ 464 465 interface IErrorReporter 466 { 467 void error ( Exception exception, Event event = Event.None ); 468 } 469 470 /**************************************************************************/ 471 472 interface ITimeoutReporter 473 { 474 void timeout ( ); 475 } 476 477 /************************************************************************** 478 479 Interface instances 480 481 **************************************************************************/ 482 483 private IFinalizer finalizer_ = null; 484 private IErrorReporter error_reporter_ = null; 485 private ITimeoutReporter timeout_reporter_ = null; 486 487 /************************************************************************** 488 489 Sets the Finalizer. May be set to null to disable finalizing. 490 491 Params: 492 finalizer_ = IFinalizer instance 493 494 **************************************************************************/ 495 496 public void finalizer ( IFinalizer finalizer_ ) 497 { 498 this.finalizer_ = finalizer_; 499 } 500 501 /************************************************************************** 502 503 Sets the TimeoutReporter. May be set to null to disable timeout 504 reporting. 505 506 Params: 507 timeout_reporter_ = ITimeoutReporter instance 508 509 **************************************************************************/ 510 511 public void timeout_reporter ( ITimeoutReporter timeout_reporter_ ) 512 { 513 this.timeout_reporter_ = timeout_reporter_; 514 } 515 516 /************************************************************************** 517 518 Sets the Error Reporter. May be set to null to disable error reporting. 519 520 Params: 521 error_reporter_ = IErrorReporter instance 522 523 **************************************************************************/ 524 525 public void error_reporter ( IErrorReporter error_reporter_ ) 526 { 527 this.error_reporter_ = error_reporter_; 528 } 529 530 /************************************************************************** 531 532 Finalize method, called after this instance has been unregistered from 533 the Dispatcher. 534 535 **************************************************************************/ 536 537 public override void finalize ( FinalizeStatus status ) 538 { 539 if (this.finalizer_ !is null) 540 { 541 this.finalizer_.finalize(status); 542 } 543 } 544 545 /************************************************************************** 546 547 Error reporting method, called when an Exception is caught from 548 super.handle(). 549 550 Params: 551 exception = Exception thrown by handle() 552 event = Selector event while exception was caught 553 554 **************************************************************************/ 555 556 override protected void error_ ( Exception exception, Event event ) 557 { 558 if (this.error_reporter_) 559 { 560 this.error_reporter_.error(exception, event); 561 } 562 } 563 564 /************************************************************************** 565 566 Timeout method, called after this a timeout has occurred in the 567 SelectDispatcher. 568 569 **************************************************************************/ 570 571 override public void timeout ( ) 572 { 573 if (this.timeout_reporter_) 574 { 575 this.timeout_reporter_.timeout(); 576 } 577 } 578 }