1 /******************************************************************************* 2 3 copyright: Copyright (c) 2004 Kris Bell. All rights reserved 4 5 license: BSD style: $(LICENSE) 6 7 version: May 2005: Initial release 8 9 author: Kris 10 11 *******************************************************************************/ 12 13 module tango.io.device.Device; 14 15 private import tango.sys.Common; 16 17 public import tango.io.device.Conduit; 18 19 /******************************************************************************* 20 21 Implements a means of reading and writing a file device. Conduits 22 are the primary means of accessing external data, and this one is 23 used as a superclass for the console, for files, sockets etc. 24 25 *******************************************************************************/ 26 27 class Device : Conduit, ISelectable 28 { 29 /// expose superclass definition also 30 public alias Conduit.error error; 31 32 /*********************************************************************** 33 34 Throw an IOException noting the last error. 35 36 ***********************************************************************/ 37 38 final void error () 39 { 40 error (this.toString() ~ " :: " ~ SysError.lastMsg); 41 } 42 43 /*********************************************************************** 44 45 Return the name of this device. 46 47 ***********************************************************************/ 48 49 override immutable(char)[] toString () 50 { 51 return "<device>"; 52 } 53 54 /*********************************************************************** 55 56 Return a preferred size for buffering conduit I/O. 57 58 ***********************************************************************/ 59 60 override const size_t bufferSize () 61 { 62 return 1024 * 16; 63 } 64 65 /*********************************************************************** 66 67 Windows-specific code. 68 69 ***********************************************************************/ 70 71 version (Win32) 72 { 73 struct IO 74 { 75 OVERLAPPED asynch; // must be the first attribute!! 76 Handle handle; 77 bool track; 78 void* task; 79 } 80 81 protected IO io; 82 83 /*************************************************************** 84 85 Allow adjustment of standard IO handles. 86 87 ***************************************************************/ 88 89 protected void reopen (Handle handle) 90 { 91 io.handle = handle; 92 } 93 94 /*************************************************************** 95 96 Return the underlying OS handle of this Conduit. 97 98 ***************************************************************/ 99 100 @property final Handle fileHandle () 101 { 102 return io.handle; 103 } 104 105 /*************************************************************** 106 107 ***************************************************************/ 108 109 version(TangoRuntime) 110 { 111 override void dispose () 112 { 113 if (io.handle != INVALID_HANDLE_VALUE) 114 if (scheduler) 115 scheduler.close (io.handle, toString); 116 detach(); 117 } 118 } 119 120 /*************************************************************** 121 122 Release the underlying file. Note that an exception 123 is not thrown on error, as doing so can induce some 124 spaggetti into error handling. Instead, we need to 125 change this to return a bool instead, so the caller 126 can decide what to do. 127 128 ***************************************************************/ 129 130 override void detach () 131 { 132 if (io.handle != INVALID_HANDLE_VALUE) 133 CloseHandle (io.handle); 134 135 io.handle = INVALID_HANDLE_VALUE; 136 } 137 138 /*************************************************************** 139 140 Read a chunk of bytes from the file into the provided 141 array. Returns the number of bytes read, or Eof where 142 there is no further data. 143 144 Operates asynchronously where the hosting thread is 145 configured in that manner. 146 147 ***************************************************************/ 148 149 override size_t read (void[] dst) 150 { 151 DWORD bytes; 152 153 version(TangoRuntime) 154 { 155 if (! ReadFile (io.handle, dst.ptr, dst.length, &bytes, &io.asynch)) 156 if ((bytes = wait (scheduler.Type.Read, bytes, timeout)) is Eof) 157 return Eof; 158 } 159 else 160 { 161 ReadFile (io.handle, dst.ptr, dst.length, &bytes, &io.asynch); 162 } 163 164 // synchronous read of zero means Eof 165 if (bytes is 0 && dst.length > 0) 166 return Eof; 167 168 // update stream location? 169 if (io.track) 170 (*cast(long*) &io.asynch.Offset) += bytes; 171 return bytes; 172 } 173 174 /*************************************************************** 175 176 Write a chunk of bytes to the file from the provided 177 array. Returns the number of bytes written, or Eof if 178 the output is no longer available. 179 180 Operates asynchronously where the hosting thread is 181 configured in that manner. 182 183 ***************************************************************/ 184 185 override size_t write (const(void)[] src) 186 { 187 DWORD bytes; 188 189 version(TangoRuntime) 190 { 191 if (! WriteFile (io.handle, src.ptr, src.length, &bytes, &io.asynch)) 192 if ((bytes = wait (scheduler.Type.Write, bytes, timeout)) is Eof) 193 return Eof; 194 } 195 else 196 { 197 WriteFile (io.handle, src.ptr, src.length, &bytes, &io.asynch); 198 } 199 200 // update stream location? 201 if (io.track) 202 (*cast(long*) &io.asynch.Offset) += bytes; 203 return bytes; 204 } 205 206 /*************************************************************** 207 208 ***************************************************************/ 209 210 version(TangoRuntime) 211 { 212 protected final size_t wait (scheduler.Type type, uint bytes, uint timeout) 213 { 214 while (true) 215 { 216 auto code = GetLastError; 217 if (code is ERROR_HANDLE_EOF || 218 code is ERROR_BROKEN_PIPE) 219 return Eof; 220 221 if (scheduler) 222 { 223 if (code is ERROR_SUCCESS || 224 code is ERROR_IO_PENDING || 225 code is ERROR_IO_INCOMPLETE) 226 { 227 if (code is ERROR_IO_INCOMPLETE) 228 super.error ("timeout"); 229 230 io.task = cast(void*) tango.core.Thread.Fiber.getThis; 231 scheduler.await (io.handle, type, timeout); 232 if (GetOverlappedResult (io.handle, &io.asynch, &bytes, false)) 233 return bytes; 234 } 235 else 236 error; 237 } 238 else 239 if (code is ERROR_SUCCESS) 240 return bytes; 241 else 242 error; 243 } 244 245 // should never get here 246 assert(false); 247 } 248 } 249 } 250 251 252 /*********************************************************************** 253 254 Unix-specific code. 255 256 ***********************************************************************/ 257 258 version (Posix) 259 { 260 protected int handle = -1; 261 262 /*************************************************************** 263 264 Allow adjustment of standard IO handles. 265 266 ***************************************************************/ 267 268 protected void reopen (Handle handle) 269 { 270 this.handle = handle; 271 } 272 273 /*************************************************************** 274 275 Return the underlying OS handle of this Conduit. 276 277 ***************************************************************/ 278 279 @property final Handle fileHandle () 280 { 281 return cast(Handle) handle; 282 } 283 284 /*************************************************************** 285 286 Release the underlying file. 287 288 ***************************************************************/ 289 290 override void detach () 291 { 292 if (handle >= 0) 293 { 294 //if (scheduler) 295 // TODO Not supported on Posix 296 // scheduler.close (handle, toString); 297 posix.close (handle); 298 } 299 handle = -1; 300 } 301 302 /*************************************************************** 303 304 Read a chunk of bytes from the file into the provided 305 array. Returns the number of bytes read, or Eof where 306 there is no further data. 307 308 ***************************************************************/ 309 310 override size_t read (void[] dst) 311 { 312 auto read = posix.read (handle, dst.ptr, dst.length); 313 314 if (read is -1) 315 error(); 316 else 317 if (read is 0 && dst.length > 0) 318 return Eof; 319 return read; 320 } 321 322 /*************************************************************** 323 324 Write a chunk of bytes to the file from the provided 325 array. Returns the number of bytes written, or Eof if 326 the output is no longer available. 327 328 ***************************************************************/ 329 330 override size_t write (const(void)[] src) 331 { 332 size_t written = posix.write (handle, src.ptr, src.length); 333 if (written is -1) 334 error(); 335 return written; 336 } 337 } 338 }