1 /******************************************************************************* 2 copyright: Copyright (c) 2006 Juan Jose Comellas. All rights reserved 3 license: BSD style: $(LICENSE) 4 author: Juan Jose Comellas $(EMAIL juanjo@comellas.com.ar) 5 *******************************************************************************/ 6 7 module tango.io.selector.model.ISelector; 8 9 10 public import tango.time.Time; 11 12 public import tango.io.model.IConduit; 13 14 /** 15 * Events that are used to register a Conduit to a selector and are returned 16 * in a SelectionKey after calling ISelector.select(). 17 */ 18 enum Event: uint 19 { 20 None = 0, // No event 21 // IMPORTANT: Do not change the values of the following symbols. They were 22 // set in this way to map the values returned by the POSIX poll() 23 // system call. 24 Read = (1 << 0), // POLLIN 25 UrgentRead = (1 << 1), // POLLPRI 26 Write = (1 << 2), // POLLOUT 27 // The following events should not be used when registering a conduit to a 28 // selector. They are only used when returning events to the user. 29 Error = (1 << 3), // POLLERR 30 Hangup = (1 << 4), // POLLHUP 31 InvalidHandle = (1 << 5) // POLLNVAL 32 } 33 34 35 /** 36 * The SelectionKey struct holds the information concerning the conduits and 37 * their association to a selector. Each key keeps a reference to a registered 38 * conduit and the events that are to be tracked for it. The 'events' member 39 * of the key can take two meanings, depending on where it's used. If used 40 * with the register() method of the selector it represents the events we want 41 * to track; if used within a foreach cycle on an ISelectionSet it represents 42 * the events that have been detected for a conduit. 43 * 44 * The SelectionKey can also hold an optional object via the 'attachment' 45 * member. This member is very convenient to keep application-specific data 46 * that will be needed when the tracked events are triggered. 47 * 48 * See_also: $(SYMLINK ISelector, ISelector), 49 * $(SYMLINK ISelectionSet, ISelectionSet) 50 */ 51 struct SelectionKey 52 { 53 /** 54 * The conduit referred to by the SelectionKey. 55 */ 56 ISelectable conduit; 57 58 /** 59 * The registered (or selected) events as a bit mask of different Event 60 * values. 61 */ 62 Event events; 63 64 /** 65 * The attached Object referred to by the SelectionKey. 66 */ 67 Object attachment; 68 69 /** 70 * Check if a Read event has been associated to this SelectionKey. 71 */ 72 public bool isReadable() 73 { 74 return ((events & Event.Read) != 0); 75 } 76 77 /** 78 * Check if an UrgentRead event has been associated to this SelectionKey. 79 */ 80 public bool isUrgentRead() 81 { 82 return ((events & Event.UrgentRead) != 0); 83 } 84 85 /** 86 * Check if a Write event has been associated to this SelectionKey. 87 */ 88 public bool isWritable() 89 { 90 return ((events & Event.Write) != 0); 91 } 92 93 /** 94 * Check if an Error event has been associated to this SelectionKey. 95 */ 96 public bool isError() 97 { 98 return ((events & Event.Error) != 0); 99 } 100 101 /** 102 * Check if a Hangup event has been associated to this SelectionKey. 103 */ 104 public bool isHangup() 105 { 106 return ((events & Event.Hangup) != 0); 107 } 108 109 /** 110 * Check if an InvalidHandle event has been associated to this SelectionKey. 111 */ 112 public bool isInvalidHandle() 113 { 114 return ((events & Event.InvalidHandle) != 0); 115 } 116 } 117 118 119 /** 120 * Container that holds the SelectionKey's for all the conduits that have 121 * triggered events during a previous invocation to ISelector.select(). 122 * Instances of this container are normally returned from calls to 123 * ISelector.selectedSet(). 124 */ 125 interface ISelectionSet 126 { 127 /** 128 * Returns the number of SelectionKey's in the set. 129 */ 130 public abstract size_t length(); 131 132 /** 133 * Operator to iterate over a set via a foreach block. Note that any 134 * modifications to the SelectionKey will be ignored. 135 */ 136 public abstract int opApply(scope int delegate(ref SelectionKey) dg); 137 } 138 139 140 /** 141 * A selector is a multiplexor for I/O events associated to a Conduit. 142 * All selectors must implement this interface. 143 * 144 * A selector needs to be initialized by calling the open() method to pass 145 * it the initial amount of conduits that it will handle and the maximum 146 * amount of events that will be returned per call to select(). In both cases, 147 * these values are only hints and may not even be used by the specific 148 * ISelector implementation you choose to use, so you cannot make any 149 * assumptions regarding what results from the call to select() (i.e. you 150 * may receive more or less events per call to select() than what was passed 151 * in the 'maxEvents' argument. The amount of conduits that the selector can 152 * manage will be incremented dynamically if necessary. 153 * 154 * To add or modify conduit registrations in the selector, use the register() 155 * method. To remove conduit registrations from the selector, use the 156 * unregister() method. 157 * 158 * To wait for events from the conduits you need to call any of the select() 159 * methods. The selector cannot be modified from another thread while 160 * blocking on a call to these methods. 161 * 162 * Once the selector is no longer used you must call the close() method so 163 * that the selector can free any resources it may have allocated in the call 164 * to open(). 165 * 166 * Examples: 167 * --- 168 * import tango.io.selector.model.ISelector; 169 * import tango.io.SocketConduit; 170 * import tango.io.Stdout; 171 * 172 * ISelector selector; 173 * SocketConduit conduit1; 174 * SocketConduit conduit2; 175 * MyClass object1; 176 * MyClass object2; 177 * int eventCount; 178 * 179 * // Initialize the selector assuming that it will deal with 2 conduits and 180 * // will receive 2 events per invocation to the select() method. 181 * selector.open(2, 2); 182 * 183 * selector.register(conduit, Event.Read, object1); 184 * selector.register(conduit, Event.Write, object2); 185 * 186 * eventCount = selector.select(); 187 * 188 * if (eventCount > 0) 189 * { 190 * char[16] buffer; 191 * int count; 192 * 193 * foreach (SelectionKey key, selector.selectedSet()) 194 * { 195 * if (key.isReadable()) 196 * { 197 * count = (cast(SocketConduit) key.conduit).read(buffer); 198 * if (count != IConduit.Eof) 199 * { 200 * Stdout.format("Received '{0}' from peer\n", buffer[0..count]); 201 * selector.register(key.conduit, Event.Write, key.attachment); 202 * } 203 * else 204 * { 205 * selector.unregister(key.conduit); 206 * key.conduit.close(); 207 * } 208 * } 209 * 210 * if (key.isWritable()) 211 * { 212 * count = (cast(SocketConduit) key.conduit).write("MESSAGE"); 213 * if (count != IConduit.Eof) 214 * { 215 * Stdout.print("Sent 'MESSAGE' to peer\n"); 216 * selector.register(key.conduit, Event.Read, key.attachment); 217 * } 218 * else 219 * { 220 * selector.unregister(key.conduit); 221 * key.conduit.close(); 222 * } 223 * } 224 * 225 * if (key.isError() || key.isHangup() || key.isInvalidHandle()) 226 * { 227 * selector.unregister(key.conduit); 228 * key.conduit.close(); 229 * } 230 * } 231 * } 232 * 233 * selector.close(); 234 * --- 235 */ 236 interface ISelector 237 { 238 /** 239 * Initialize the selector. 240 * 241 * Params: 242 * size = value that provides a hint for the maximum amount of 243 * conduits that will be registered 244 * maxEvents = value that provides a hint for the maximum amount of 245 * conduit events that will be returned in the selection 246 * set per call to select. 247 */ 248 public abstract void open(uint size, uint maxEvents); 249 250 /** 251 * Free any operating system resources that may have been allocated in the 252 * call to open(). 253 * 254 * Remarks: 255 * Not all of the selectors need to free resources other than allocated 256 * memory, but those that do will normally also add a call to close() in 257 * their destructors. 258 */ 259 public abstract void close(); 260 261 /** 262 * Associate a conduit to the selector and track specific I/O events. 263 * If the conduit is already part of the selector, modify the events or 264 * atachment. 265 * 266 * Params: 267 * conduit = conduit that will be associated to the selector; 268 * must be a valid conduit (i.e. not null and open). 269 * events = bit mask of Event values that represent the events that 270 * will be tracked for the conduit. 271 * attachment = optional object with application-specific data that will 272 * be available when an event is triggered for the conduit 273 * 274 * Examples: 275 * --- 276 * ISelector selector; 277 * SocketConduit conduit; 278 * MyClass object; 279 * 280 * selector.register(conduit, Event.Read | Event.Write, object); 281 * --- 282 */ 283 public abstract void register(ISelectable conduit, Event events, 284 Object attachment = null); 285 286 287 /** 288 * Deprecated, use register instead 289 */ 290 deprecated public abstract void reregister(ISelectable conduit, Event 291 events, Object attachment = null); 292 293 /** 294 * Remove a conduit from the selector. 295 * 296 * Params: 297 * conduit = conduit that had been previously associated to the 298 * selector; it can be null. 299 * 300 * Remarks: 301 * Unregistering a null conduit is allowed and no exception is thrown 302 * if this happens. 303 */ 304 public abstract void unregister(ISelectable conduit); 305 306 307 /** 308 * Wait indefinitely for I/O events from the registered conduits. 309 * 310 * Returns: 311 * The amount of conduits that have received events; 0 if no conduits 312 * have received events within the specified timeout and -1 if there 313 * was an error. 314 */ 315 public abstract int select(); 316 317 /** 318 * Wait for I/O events from the registered conduits for a specified 319 * amount of time. 320 * 321 * Params: 322 * timeout = TimeSpan with the maximum amount of time that the 323 * selector will wait for events from the conduits; the 324 * amount of time is relative to the current system time 325 * (i.e. just the number of milliseconds that the selector 326 * has to wait for the events). 327 * 328 * Returns: 329 * The amount of conduits that have received events; 0 if no conduits 330 * have received events within the specified timeout. 331 */ 332 public abstract int select(TimeSpan timeout); 333 334 /** 335 * Wait for I/O events from the registered conduits for a specified 336 * amount of time. 337 * 338 * Note: This representation of timeout is not always accurate, so it is 339 * possible that the function will return with a timeout before the 340 * specified period. For more accuracy, use the TimeSpan version. 341 * 342 * Note: Implementers should define this method as: 343 * ------- 344 * select(TimeSpan.interval(timeout)); 345 * ------- 346 * 347 * Params: 348 * timeout = the maximum amount of time in seconds that the 349 * selector will wait for events from the conduits; the 350 * amount of time is relative to the current system time 351 * (i.e. just the number of milliseconds that the selector 352 * has to wait for the events). 353 * 354 * Returns: 355 * The amount of conduits that have received events; 0 if no conduits 356 * have received events within the specified timeout. 357 */ 358 public abstract int select(double timeout); 359 360 /** 361 * Return the selection set resulting from the call to any of the select() 362 * methods. 363 * 364 * Remarks: 365 * If the call to select() was unsuccessful or it did not return any 366 * events, the returned value will be null. 367 */ 368 public abstract ISelectionSet selectedSet(); 369 370 /** 371 * Return the selection key resulting from the registration of a conduit 372 * to the selector. 373 * 374 * Remarks: 375 * If the conduit is not registered to the selector the returned 376 * value will SelectionKey.init. No exception will be thrown by this 377 * method. 378 */ 379 public abstract SelectionKey key(ISelectable conduit); 380 381 /** 382 * Iterate through the currently registered selection keys. Note that you 383 * should not erase or add any items from the selector while iterating, 384 * although you can register existing conduits again. 385 */ 386 public abstract int opApply(scope int delegate(ref SelectionKey sk) dg); 387 }