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.AbstractSelector; 8 9 10 public import tango.io.model.IConduit; 11 public import tango.io.selector.SelectorException; 12 13 private import tango.io.selector.model.ISelector; 14 private import tango.sys.Common; 15 private import tango.stdc.errno; 16 17 version (Windows) 18 { 19 public struct timeval 20 { 21 int tv_sec; // seconds 22 int tv_usec; // microseconds 23 } 24 } 25 26 /** 27 * Base class for all selectors. 28 * 29 * A selector is a multiplexor for I/O events associated to a Conduit. 30 * All selectors must implement this interface. 31 * 32 * A selector needs to be initialized by calling the open() method to pass 33 * it the initial amount of conduits that it will handle and the maximum 34 * amount of events that will be returned per call to select(). In both cases, 35 * these values are only hints and may not even be used by the specific 36 * ISelector implementation you choose to use, so you cannot make any 37 * assumptions regarding what results from the call to select() (i.e. you 38 * may receive more or less events per call to select() than what was passed 39 * in the 'maxEvents' argument. The amount of conduits that the selector can 40 * manage will be incremented dynamically if necessary. 41 * 42 * To add or modify conduit registrations in the selector, use the register() 43 * method. To remove conduit registrations from the selector, use the 44 * unregister() method. 45 * 46 * To wait for events from the conduits you need to call any of the select() 47 * methods. The selector cannot be modified from another thread while 48 * blocking on a call to these methods. 49 * 50 * Once the selector is no longer used you must call the close() method so 51 * that the selector can free any resources it may have allocated in the call 52 * to open(). 53 * 54 * See_Also: ISelector 55 * 56 * Examples: 57 * --- 58 * import tango.io.selector.model.ISelector; 59 * import tango.net.device.Socket; 60 * import tango.io.Stdout; 61 * 62 * AbstractSelector selector; 63 * SocketConduit conduit1; 64 * SocketConduit conduit2; 65 * MyClass object1; 66 * MyClass object2; 67 * uint eventCount; 68 * 69 * // Initialize the selector assuming that it will deal with 2 conduits and 70 * // will receive 2 events per invocation to the select() method. 71 * selector.open(2, 2); 72 * 73 * selector.register(conduit, Event.Read, object1); 74 * selector.register(conduit, Event.Write, object2); 75 * 76 * eventCount = selector.select(); 77 * 78 * if (eventCount > 0) 79 * { 80 * char[16] buffer; 81 * int count; 82 * 83 * foreach (SelectionKey key, selector.selectedSet()) 84 * { 85 * if (key.isReadable()) 86 * { 87 * count = (cast(SocketConduit) key.conduit).read(buffer); 88 * if (count != IConduit.Eof) 89 * { 90 * Stdout.format("Received '{0}' from peer\n", buffer[0..count]); 91 * selector.register(key.conduit, Event.Write, key.attachment); 92 * } 93 * else 94 * { 95 * selector.unregister(key.conduit); 96 * key.conduit.close(); 97 * } 98 * } 99 * 100 * if (key.isWritable()) 101 * { 102 * count = (cast(SocketConduit) key.conduit).write("MESSAGE"); 103 * if (count != IConduit.Eof) 104 * { 105 * Stdout("Sent 'MESSAGE' to peer\n"); 106 * selector.register(key.conduit, Event.Read, key.attachment); 107 * } 108 * else 109 * { 110 * selector.unregister(key.conduit); 111 * key.conduit.close(); 112 * } 113 * } 114 * 115 * if (key.isError() || key.isHangup() || key.isInvalidHandle()) 116 * { 117 * selector.unregister(key.conduit); 118 * key.conduit.close(); 119 * } 120 * } 121 * } 122 * 123 * selector.close(); 124 * --- 125 */ 126 abstract class AbstractSelector: ISelector 127 { 128 /** 129 * Restart interrupted system calls when blocking inside a call to select. 130 */ 131 protected bool _restartInterruptedSystemCall = true; 132 133 /** 134 * Indicates whether interrupted system calls will be restarted when 135 * blocking inside a call to select. 136 */ 137 public bool restartInterruptedSystemCall() 138 { 139 return _restartInterruptedSystemCall; 140 } 141 142 /** 143 * Sets whether interrupted system calls will be restarted when 144 * blocking inside a call to select. 145 */ 146 public void restartInterruptedSystemCall(bool value) 147 { 148 _restartInterruptedSystemCall = value; 149 } 150 151 /** 152 * Initialize the selector. 153 * 154 * Params: 155 * size = value that provides a hint for the maximum amount of 156 * conduits that will be registered 157 * maxEvents = value that provides a hint for the maximum amount of 158 * conduit events that will be returned in the selection 159 * set per call to select. 160 */ 161 public abstract void open(uint size, uint maxEvents); 162 163 /** 164 * Free any operating system resources that may have been allocated in the 165 * call to open(). 166 * 167 * Remarks: 168 * Not all of the selectors need to free resources other than allocated 169 * memory, but those that do will normally also add a call to close() in 170 * their destructors. 171 */ 172 public abstract void close(); 173 174 /** 175 * Associate a conduit to the selector and track specific I/O events. 176 * 177 * Params: 178 * conduit = conduit that will be associated to the selector 179 * events = bit mask of Event values that represent the events that 180 * will be tracked for the conduit. 181 * attachment = optional object with application-specific data that will 182 * be available when an event is triggered for the conduit 183 * 184 * Examples: 185 * --- 186 * AbstractSelector selector; 187 * SocketConduit conduit; 188 * MyClass object; 189 * 190 * selector.register(conduit, Event.Read | Event.Write, object); 191 * --- 192 */ 193 public abstract void register(ISelectable conduit, Event events, 194 Object attachment); 195 196 /** 197 * Deprecated, use register instead 198 */ 199 deprecated public final void reregister(ISelectable conduit, Event events, 200 Object attachment = null) 201 { 202 register(conduit, events, attachment); 203 } 204 205 /** 206 * Remove a conduit from the selector. 207 * 208 * Params: 209 * conduit = conduit that had been previously associated to the 210 * selector; it can be null. 211 * 212 * Remarks: 213 * Unregistering a null conduit is allowed and no exception is thrown 214 * if this happens. 215 */ 216 public abstract void unregister(ISelectable conduit); 217 218 /** 219 * Wait for I/O events from the registered conduits for a specified 220 * amount of time. 221 * 222 * Returns: 223 * The amount of conduits that have received events; 0 if no conduits 224 * have received events within the specified timeout; and -1 if the 225 * wakeup() method has been called from another thread. 226 * 227 * Remarks: 228 * This method is the same as calling select(TimeSpan.max). 229 */ 230 public int select() 231 { 232 return select(TimeSpan.max); 233 } 234 235 /** 236 * Wait for I/O events from the registered conduits for a specified 237 * amount of time. 238 * 239 * Note: This representation of timeout is not always accurate, so it is 240 * possible that the function will return with a timeout before the 241 * specified period. For more accuracy, use the TimeSpan version. 242 * 243 * Params: 244 * timeout = the maximum amount of time in seconds that the 245 * selector will wait for events from the conduits; the 246 * amount of time is relative to the current system time 247 * (i.e. just the number of milliseconds that the selector 248 * has to wait for the events). 249 * 250 * Returns: 251 * The amount of conduits that have received events; 0 if no conduits 252 * have received events within the specified timeout. 253 */ 254 public int select(double timeout) 255 { 256 return select(TimeSpan.fromInterval(timeout)); 257 } 258 259 /** 260 * Wait for I/O events from the registered conduits for a specified 261 * amount of time. 262 * 263 * Params: 264 * timeout = TimeSpan with the maximum amount of time that the 265 * selector will wait for events from the conduits; the 266 * amount of time is relative to the current system time 267 * (i.e. just the number of milliseconds that the selector 268 * has to wait for the events). 269 * 270 * Returns: 271 * The amount of conduits that have received events; 0 if no conduits 272 * have received events within the specified timeout; and -1 if the 273 * wakeup() method has been called from another thread. 274 */ 275 public abstract int select(TimeSpan timeout); 276 277 /** 278 * Causes the first call to select() that has not yet returned to return 279 * immediately. 280 * 281 * If another thread is currently blocked in an call to any of the 282 * select() methods then that call will return immediately. If no 283 * selection operation is currently in progress then the next invocation 284 * of one of these methods will return immediately. In any case the value 285 * returned by that invocation may be non-zero. Subsequent invocations of 286 * the select() methods will block as usual unless this method is invoked 287 * again in the meantime. 288 */ 289 // public abstract void wakeup(); 290 291 /** 292 * Return the selection set resulting from the call to any of the select() 293 * methods. 294 * 295 * Remarks: 296 * If the call to select() was unsuccessful or it did not return any 297 * events, the returned value will be null. 298 */ 299 public abstract ISelectionSet selectedSet(); 300 301 /** 302 * Return the selection key resulting from the registration of a conduit 303 * to the selector. 304 * 305 * Remarks: 306 * If the conduit is not registered to the selector the returned 307 * value will be null. No exception will be thrown by this method. 308 */ 309 public abstract SelectionKey key(ISelectable conduit); 310 311 /** 312 * Return the number of keys resulting from the registration of a conduit 313 * to the selector. 314 */ 315 public abstract size_t count(); 316 317 /** 318 * Cast the time duration to a C timeval struct. 319 */ 320 public timeval* toTimeval(timeval* tv, TimeSpan interval) 321 in 322 { 323 assert(tv !is null); 324 } 325 body 326 { 327 tv.tv_sec = cast(typeof(tv.tv_sec)) interval.seconds; 328 tv.tv_usec = cast(typeof(tv.tv_usec)) (interval.micros % 1_000_000); 329 return tv; 330 } 331 332 /** 333 * Check the 'errno' global variable from the C standard library and 334 * throw an exception with the description of the error. 335 * 336 * Params: 337 * file = Name of the source file where the check is being made; you 338 * would normally use __FILE__ for this parameter. 339 * line = Line number of the source file where this method was called; 340 * you would normally use __LINE__ for this parameter. 341 * 342 * $(UL 343 * $(LI $(B RegisteredConduitException) when the conduit 344 * should not be registered but it is (EEXIST).) 345 * $(LI $(B UnregisteredConduitException) when the conduit 346 * should be registered but it isn't (ENOENT).) 347 * $(LI $(B InterruptedSystemCallException) when a system call has been 348 * interrupted (EINTR).) 349 * $(LI $(B OutOfMemoryException) if a memory allocation fails (ENOMEM).) 350 * $(LI $(B SelectorException) for any of the other cases 351 * in which errno is not 0.)) 352 */ 353 protected void checkErrno(string file, size_t line) 354 { 355 int errorCode = errno; 356 switch (errorCode) 357 { 358 case EBADF: 359 throw new SelectorException("Bad file descriptor", file, line); 360 // break; 361 case EEXIST: 362 throw new RegisteredConduitException(file, line); 363 // break; 364 case EINTR: 365 throw new InterruptedSystemCallException(file, line); 366 // break; 367 case EINVAL: 368 throw new SelectorException("An invalid parameter was sent to a system call", file, line); 369 // break; 370 case ENFILE: 371 throw new SelectorException("Maximum number of open files reached", file, line); 372 // break; 373 case ENOENT: 374 throw new UnregisteredConduitException(file, line); 375 // break; 376 case ENOMEM: 377 throw new OutOfMemoryException(file, line); 378 // break; 379 case EPERM: 380 throw new SelectorException("The conduit cannot be used with this Selector", file, line); 381 // break; 382 default: 383 throw new SelectorException("Unknown Selector error: " ~ SysError.lookup(errorCode).idup, file, line); 384 // break; 385 } 386 } 387 }