| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 | /******************************************************************************* copyright: Copyright (c) 2004 Kris Bell. All rights reserved license: BSD style: $(LICENSE) version: May 2005: Initial release author: Kris *******************************************************************************/ module tango.io.device.Device; private import tango.sys.Common; public import tango.io.device.Conduit; /******************************************************************************* Implements a means of reading and writing a file device. Conduits are the primary means of accessing external data, and this one is used as a superclass for the console, for files, sockets etc. *******************************************************************************/ class Device : Conduit, ISelectable { /// expose superclass definition also public alias Conduit.error error; /*********************************************************************** Throw an IOException noting the last error. ***********************************************************************/ final void error () { error (this.toString() ~ " :: " ~ SysError.lastMsg); } /*********************************************************************** Return the name of this device. ***********************************************************************/ override immutable(char)[] toString () { return "<device>"; } /*********************************************************************** Return a preferred size for buffering conduit I/O. ***********************************************************************/ override const size_t bufferSize () { return 1024 * 16; } /*********************************************************************** Windows-specific code. ***********************************************************************/ version (Win32) { struct IO { OVERLAPPED asynch; // must be the first attribute!! Handle handle; bool track; void* task; } protected IO io; /*************************************************************** Allow adjustment of standard IO handles. ***************************************************************/ protected void reopen (Handle handle) { io.handle = handle; } /*************************************************************** Return the underlying OS handle of this Conduit. ***************************************************************/ @property final Handle fileHandle () { return io.handle; } /*************************************************************** ***************************************************************/ version(TangoRuntime) { override void dispose () { if (io.handle != INVALID_HANDLE_VALUE) if (scheduler) scheduler.close (io.handle, toString); detach(); } } /*************************************************************** Release the underlying file. Note that an exception is not thrown on error, as doing so can induce some spaggetti into error handling. Instead, we need to change this to return a bool instead, so the caller can decide what to do. ***************************************************************/ override void detach () { if (io.handle != INVALID_HANDLE_VALUE) CloseHandle (io.handle); io.handle = INVALID_HANDLE_VALUE; } /*************************************************************** Read a chunk of bytes from the file into the provided array. Returns the number of bytes read, or Eof where there is no further data. Operates asynchronously where the hosting thread is configured in that manner. ***************************************************************/ override size_t read (void[] dst) { DWORD bytes; version(TangoRuntime) { if (! ReadFile (io.handle, dst.ptr, dst.length, &bytes, &io.asynch)) if ((bytes = wait (scheduler.Type.Read, bytes, timeout)) is Eof) return Eof; } else { ReadFile (io.handle, dst.ptr, dst.length, &bytes, &io.asynch); } // synchronous read of zero means Eof if (bytes is 0 && dst.length > 0) return Eof; // update stream location? if (io.track) (*cast(long*) &io.asynch.Offset) += bytes; return bytes; } /*************************************************************** Write a chunk of bytes to the file from the provided array. Returns the number of bytes written, or Eof if the output is no longer available. Operates asynchronously where the hosting thread is configured in that manner. ***************************************************************/ override size_t write (const(void)[] src) { DWORD bytes; version(TangoRuntime) { if (! WriteFile (io.handle, src.ptr, src.length, &bytes, &io.asynch)) if ((bytes = wait (scheduler.Type.Write, bytes, timeout)) is Eof) return Eof; } else { WriteFile (io.handle, src.ptr, src.length, &bytes, &io.asynch); } // update stream location? if (io.track) (*cast(long*) &io.asynch.Offset) += bytes; return bytes; } /*************************************************************** ***************************************************************/ version(TangoRuntime) { protected final size_t wait (scheduler.Type type, uint bytes, uint timeout) { while (true) { auto code = GetLastError; if (code is ERROR_HANDLE_EOF || code is ERROR_BROKEN_PIPE) return Eof; if (scheduler) { if (code is ERROR_SUCCESS || code is ERROR_IO_PENDING || code is ERROR_IO_INCOMPLETE) { if (code is ERROR_IO_INCOMPLETE) super.error ("timeout"); io.task = cast(void*) tango.core.Thread.Fiber.getThis; scheduler.await (io.handle, type, timeout); if (GetOverlappedResult (io.handle, &io.asynch, &bytes, false)) return bytes; } else error; } else if (code is ERROR_SUCCESS) return bytes; else error; } // should never get here assert(false); } } } /*********************************************************************** Unix-specific code. ***********************************************************************/ version (Posix) { protected int handle = -1; /*************************************************************** Allow adjustment of standard IO handles. ***************************************************************/ protected void reopen (Handle handle) { this.handle = handle; } /*************************************************************** Return the underlying OS handle of this Conduit. ***************************************************************/ @property final Handle fileHandle () { return cast(Handle) handle; } /*************************************************************** Release the underlying file. ***************************************************************/ override void detach () { if (handle >= 0) { //if (scheduler) // TODO Not supported on Posix // scheduler.close (handle, toString); posix.close (handle); } handle = -1; } /*************************************************************** Read a chunk of bytes from the file into the provided array. Returns the number of bytes read, or Eof where there is no further data. ***************************************************************/ override size_t read (void[] dst) { auto read = posix.read (handle, dst.ptr, dst.length); if (read is -1) error(); else if (read is 0 && dst.length > 0) return Eof; return read; } /*************************************************************** Write a chunk of bytes to the file from the provided array. Returns the number of bytes written, or Eof if the output is no longer available. ***************************************************************/ override size_t write (const(void)[] src) { size_t written = posix.write (handle, src.ptr, src.length); if (written is -1) error(); return written; } } } |