1 /****************************************************************************** 2 3 Fiber that can register select clients in a dispatcher, optimizing 4 re-registrations and event changes. 5 6 MessageFiber that includes a select dispatcher and memorizes the last client 7 it has registered to optimize registrations by skipping unnecessary 8 register() or unregister() calls. 9 10 Copyright: 11 Copyright (c) 2009-2016 dunnhumby Germany GmbH. 12 All rights reserved. 13 14 License: 15 Boost Software License Version 1.0. See LICENSE_BOOST.txt for details. 16 Alternatively, this file may be distributed under the terms of the Tango 17 3-Clause BSD License (see LICENSE_BSD.txt for details). 18 19 ******************************************************************************/ 20 21 module ocean.io.select.fiber.SelectFiber; 22 23 24 import ocean.core.MessageFiber; 25 26 import ocean.core.Verify; 27 28 import core.thread; 29 30 import ocean.io.select.client.model.ISelectClient; 31 import ocean.io.select.client.model.ISelectClientInfo; 32 33 import ocean.io.select.EpollSelectDispatcher; 34 35 debug ( SelectFiber) import ocean.io.Stdout : Stderr; 36 37 38 /******************************************************************************/ 39 40 public class SelectFiber : MessageFiber 41 { 42 /************************************************************************** 43 44 Epoll instance to use 45 46 **************************************************************************/ 47 48 public EpollSelectDispatcher epoll; 49 50 /************************************************************************** 51 52 Currently registered select client 53 54 **************************************************************************/ 55 56 private ISelectClient current = null; 57 58 /************************************************************************** 59 60 Constructor 61 62 Params: 63 epoll = EpollSelectDispatcher instance 64 fiber = already created core.thread.Fiber 65 66 **************************************************************************/ 67 68 this ( EpollSelectDispatcher epoll, Fiber fiber ) 69 { 70 this.epoll = epoll; 71 72 super(fiber); 73 } 74 75 /************************************************************************** 76 77 Constructor 78 79 Params: 80 epoll = EpollSelectDispatcher instance 81 coroutine = fiber coroutine 82 83 **************************************************************************/ 84 85 this ( EpollSelectDispatcher epoll, scope void delegate ( ) coroutine ) 86 { 87 this.epoll = epoll; 88 89 super(coroutine); 90 } 91 92 /************************************************************************** 93 94 Constructor 95 96 Params: 97 epoll = EpollSelectDispatcher instance 98 coroutine = fiber coroutine 99 sz = fiber stack size 100 101 **************************************************************************/ 102 103 this ( EpollSelectDispatcher epoll, scope void delegate ( ) coroutine, size_t sz ) 104 { 105 this.epoll = epoll; 106 107 super(coroutine, sz); 108 } 109 110 /************************************************************************** 111 112 Allows to change underlying core.thread.Fiber instance. Unregisters 113 the ISelectClient if necessary. 114 115 Params: 116 fiber = new fiber instance to use 117 118 **************************************************************************/ 119 120 override public void reset ( Fiber fiber ) 121 { 122 this.unregister(); 123 super.reset(fiber); 124 } 125 126 /************************************************************************** 127 128 Registers client in epoll and sets client to the current client. 129 130 Params: 131 client = select client to register 132 133 Returns: 134 true if an epoll registration was actually added or modified or 135 false if the client was the currently registered client. 136 137 **************************************************************************/ 138 139 public bool register ( ISelectClient client ) 140 { 141 verify(client !is null); 142 143 debug ( SelectFiber) Stderr.formatln("{}.register fd {}:", 144 typeof(this).stringof, client.fileHandle); 145 146 if ( this.current is null ) 147 { 148 // No client is currently registered: Add an epoll registration for 149 // the a new client. 150 151 debug ( SelectFiber) Stderr.formatln(" Register new {}", client); 152 153 this.epoll.register(this.current = client); 154 155 return true; 156 } 157 else if ( this.current is client ) 158 { 159 // The currently registered client is used for another I/O 160 // operation: Only refresh the timeout registration of the client, 161 // no need to change the epoll registration. 162 163 debug ( SelectFiber) 164 { 165 Stderr.formatln(" Leaving registered {}", this.current); 166 } 167 168 // As there is not way to modify a registration with the 169 // timeout manager, it is necessary to call unregistered(), then 170 // registered() even if this.current and client are identical. This 171 // ensures that, even if the epoll registration doesn't need to be 172 // updated, that the timeout timeout registration is updated 173 // correctly. 174 175 this.current.unregistered(); 176 client.registered(); 177 178 return false; 179 } 180 else if ( this.current.fileHandle == client.fileHandle ) 181 { 182 // The currently registered client and the new client share the same 183 // I/O device: Update the epoll registration of the I/O device to 184 // the new client. 185 186 debug ( SelectFiber) 187 { 188 Stderr.formatln(" Changing event registration {}", 189 this.current); 190 Stderr.formatln(" Register {}", client); 191 } 192 193 this.epoll.changeClient(this.current, client); 194 195 this.current = client; 196 197 return true; 198 } 199 else 200 { 201 // The currently registered client and the new client have different 202 // I/O devices: Unregister the epoll registration of the current 203 // client and register the new client instead. 204 205 debug ( SelectFiber) Stderr.formatln(" Unregister {}", 206 this.current); 207 208 this.epoll.unregister(this.current); 209 210 debug ( SelectFiber) Stderr.formatln(" Register {}", client); 211 212 this.epoll.register(this.current = client); 213 214 return true; 215 } 216 } 217 218 /************************************************************************** 219 220 Unegisters the current client from epoll and clears it, if any. 221 222 Returns: 223 true if the current client was unregistered or false if there was 224 no current client. 225 226 **************************************************************************/ 227 228 public bool unregister ( ) 229 { 230 if ( this.current !is null ) 231 { 232 debug ( SelectFiber) Stderr.formatln("{}.unregister fd {}", 233 typeof(this).stringof, this.current.fileHandle); 234 235 this.epoll.unregister(this.current); 236 this.current = null; 237 238 return true; 239 } 240 else 241 { 242 return false; 243 } 244 } 245 246 /************************************************************************** 247 248 Checks if client is identical to the current client. 249 Note that the client instance is compared, not the client conduit, 250 file descriptor or events. 251 252 Params: 253 client = client to compare for identity with the current client, 254 pass null to check if there is no current client. 255 256 Returns: 257 true if client is the current client or false otherwise. 258 259 **************************************************************************/ 260 261 public bool isRegistered ( ISelectClient client ) 262 { 263 return this.current is client; 264 } 265 266 /************************************************************************** 267 268 Clears the current client; usually called from the client finalizer. 269 270 Note that the client does not need to be unregistered here, as the epoll 271 selector always unregisters the client after calling its finalizer. 272 273 Returns: 274 true if there actually was a current client or false otherwise. 275 276 **************************************************************************/ 277 278 public bool clear ( ) 279 { 280 scope (success) this.current = null; 281 282 return this.current !is null; 283 } 284 285 /************************************************************************** 286 287 Returns: 288 informational interface to currently registered client (null if no 289 client is registered) 290 291 **************************************************************************/ 292 293 public ISelectClientInfo registered_client ( ) 294 { 295 return this.current; 296 } 297 } 298 299