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.meta.types.Qualifiers; 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 import ocean.core.TypeConvert: assumeUnique; 402 mstring to_string_buf; 403 this.fmtInfo((cstring chunk) {to_string_buf ~= chunk;}); 404 return assumeUnique(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 public void fmtInfo ( scope void delegate ( cstring chunk ) sink ) 418 { 419 sformat( 420 (cstring chunk) {sink(chunk); }, 421 "{} fd={} events=", this.id, this.fileHandle 422 ); 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 abstract class IAdvancedSelectClient : ISelectClient 454 { 455 /**************************************************************************/ 456 457 interface IFinalizer 458 { 459 alias IAdvancedSelectClient.FinalizeStatus FinalizeStatus; 460 461 void finalize ( FinalizeStatus status ); 462 } 463 464 /**************************************************************************/ 465 466 interface IErrorReporter 467 { 468 void error ( Exception exception, Event event = Event.None ); 469 } 470 471 /**************************************************************************/ 472 473 interface ITimeoutReporter 474 { 475 void timeout ( ); 476 } 477 478 /************************************************************************** 479 480 Interface instances 481 482 **************************************************************************/ 483 484 private IFinalizer finalizer_ = null; 485 private IErrorReporter error_reporter_ = null; 486 private ITimeoutReporter timeout_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 public void finalizer ( IFinalizer finalizer_ ) 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 public void timeout_reporter ( ITimeoutReporter timeout_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 public void error_reporter ( IErrorReporter error_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 public override void finalize ( FinalizeStatus status ) 539 { 540 if (this.finalizer_ !is null) 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 override protected void error_ ( Exception exception, Event event ) 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 override public void timeout ( ) 573 { 574 if (this.timeout_reporter_) 575 { 576 this.timeout_reporter_.timeout(); 577 } 578 } 579 }