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;
}
}
}
|