Skip to content

Commit

Permalink
Merge pull request #42 from schveiguy/addstdhandles
Browse files Browse the repository at this point in the history
Add standard handle accessors, as well as the capability to use std.io handles without closing the descriptors.
  • Loading branch information
schveiguy authored Jul 26, 2020
2 parents c3002df + ed34eab commit 6c93762
Show file tree
Hide file tree
Showing 9 changed files with 118 additions and 27 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ io-test-*
*.o
*.obj
*.lst
.*.swp
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ addons:
- libssl-dev
- pkg-config
- zlib1g-dev
- ninja-build
# - ninja-build
- python3
- python3-pip
- python3-setuptools
Expand Down
2 changes: 1 addition & 1 deletion meson.build
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
project('io', 'd', default_options: ['optimization=3'], version: '0.2.5', license: 'BSL-1.0' )
project('io', 'd', default_options: ['optimization=3'], version: '0.3.0', license: 'BSL-1.0' )
common_project_arguments = ['-release'] #, '--d-debug'
add_project_arguments(common_project_arguments, language: 'd')

Expand Down
75 changes: 69 additions & 6 deletions src/std/io/driver/package.d
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,7 @@ interface Driver
It is already null-terminated for usage with C APIs.
*/
FILE createFile( /*in*/ const scope tchar[] path, Mode mode);
/**
Covert platform-specific file handle to `FILE`.
*/
/// Convert platform-specific file handle to `FILE`.
version (Posix)
FILE fileFromHandle(int handle);
else version (Windows)
Expand Down Expand Up @@ -103,9 +101,7 @@ interface Driver
shared {
/// create socket
SOCKET createSocket(AddrFamily family, SocketType type, Protocol protocol);
/**
Covert platform-specific socket handle to `SOCKET`.
*/
/// Covert platform-specific socket handle to `SOCKET`.
version (Posix)
SOCKET socketFromHandle(int handle);
else version (Windows)
Expand Down Expand Up @@ -263,6 +259,73 @@ interface Driver
atomicStore!(MemoryOrder.raw)(_globalDriver, d);
}

// fetch standard handle, 0 = stdin, 1 = stdout, 2 = stderr.
version(Windows)
{
private HANDLE _getStandardHandle(int fd) @safe @nogc
{
pragma(inline, true);
import core.sys.windows.winbase : GetStdHandle, STD_INPUT_HANDLE,
STD_OUTPUT_HANDLE, STD_ERROR_HANDLE, INVALID_HANDLE_VALUE;
if (fd < 0 || fd > 2)
return INVALID_HANDLE_VALUE;
static immutable handles = [
STD_INPUT_HANDLE,
STD_OUTPUT_HANDLE,
STD_ERROR_HANDLE,
];
return () @trusted { return GetStdHandle(handles[fd]); }();
}
}
else version(Posix)
{
private int _getStandardHandle(int fd) @safe @nogc
{
pragma(inline, true);
// simple sanity check
if (fd < 0 || fd > 2)
return -1;
return fd;
}
}

/**
Get standard handles for the process. The items returned by these functions
are OS handles, and the intention is that you convert them into an
appropriate IO (e.g. File or Socket) for use.
*/
auto stdin() @safe @nogc
{
pragma(inline, true);
return _getStandardHandle(0);
}
/// ditto
auto stdout() @safe @nogc
{
pragma(inline, true);
return _getStandardHandle(1);
}
/// ditto
auto stderr() @safe @nogc
{
pragma(inline, true);
return _getStandardHandle(2);
}

///
unittest {
import std.io;
import std.string : representation;
// use standard handle
{
auto processOut = File(stdout);
processOut.write("hello, world!\n".representation);
}
// note, opening files using OS handles does not close them on destruction.
auto newProcessOut = File(stdout);
newProcessOut.write("still there!\n".representation);
}

private:
// cannot store a shared type in TLS :/
Driver _driver;
Expand Down
29 changes: 21 additions & 8 deletions src/std/io/file.d
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ struct File
*/
this(S)(S path, Mode mode = mode!"r") @trusted if (isStringLike!S)
{
closeOnDestroy = true;
version (Posix)
{
import std.internal.cstring : tempCString;
Expand All @@ -159,22 +160,26 @@ struct File
}
}

/// take ownership of an existing open file `handle`
/// Wrap an existing open file `handle`. If `takeOwnership` is set to true,
/// then the descriptor will be closed when the destructor runs.
version (Posix)
this(int handle)
this(int handle, bool takeOwnership = false)
{
closeOnDestroy = false;
f = driver.fileFromHandle(handle);
}
else version (Windows)
this(HANDLE handle)
this(HANDLE handle, bool takeOwnership = false)
{
closeOnDestroy = false;
f = driver.fileFromHandle(handle);
}

///
~this() scope
{
close();
if(closeOnDestroy)
close();
}

// workaround Issue 18000
Expand All @@ -193,6 +198,7 @@ struct File
return;
driver.closeFile(f);
f = Driver.FILE.INVALID;
closeOnDestroy = false;
}

/// return whether file is open
Expand Down Expand Up @@ -322,7 +328,7 @@ struct File
Returns:
resulting offset in file
*/
ulong seek(long offset, Seek whence)
ulong seek(long offset, Seek whence) scope
{
return driver.seek(f, offset, whence);
}
Expand Down Expand Up @@ -353,21 +359,27 @@ struct File
File move() return scope nothrow /*pure Issue 18590*/
{
auto f = this.f;
auto cod = closeOnDestroy;
this.f = Driver.FILE.INVALID;
return File(f);
this.closeOnDestroy = false;
return File(f, cod);
}

/// not copyable
@disable this(this);

private:

this(return scope Driver.FILE f) @trusted pure nothrow
this(return scope Driver.FILE f, bool cod) @trusted pure nothrow
{
this.f = f;
this.closeOnDestroy = cod;
}

Driver.FILE f = Driver.FILE.INVALID;
// close when the destructor is run. True normally unless one wraps an
// existing handle (e.g. stdout).
bool closeOnDestroy = false;
}

///
Expand Down Expand Up @@ -396,6 +408,7 @@ private:
}

auto f = File("temp.txt", mode!"w");
scope(exit) remove("temp.txt");
f = use(f.move);
f = File("temp.txt", Mode.read);
ubyte[4] buf;
Expand All @@ -405,7 +418,7 @@ private:
f.read(a[], b[]);
assert(a[] == [4, 5]);
assert(b[] == [6, 7]);
remove("temp.txt");
//remove("temp.txt");
}

version (unittest) private void remove(in char[] path) @trusted @nogc
Expand Down
2 changes: 2 additions & 0 deletions src/std/io/internal/string.d
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ nothrow pure @safe @nogc unittest
assert(s[] == "Hello String", s[]);
auto s2 = s.clone;
assert(s == s2);
// TODO: we need to enable this, the cloned string snould not be identical.
//assert(s.ptr != s2.ptr);
}

nothrow @safe @nogc unittest
Expand Down
25 changes: 18 additions & 7 deletions src/std/io/net/socket.d
Original file line number Diff line number Diff line change
Expand Up @@ -104,24 +104,29 @@ struct Socket
this(ProtocolFamily family, SocketType type, Protocol protocol = Protocol.default_) @trusted
{
s = driver.createSocket(family, type, protocol);
closeOnDestroy = true;
}

/// take ownership of an existing socket `handle`
/// Wrap an existing open socket `handle`. If `takeOwnership` is set to
/// true, then the handle will be closed when the destructor runs.
version (Posix)
this(int handle)
this(int handle, bool takeOwnership = false)
{
s = driver.socketFromHandle(handle);
closeOnDestroy = takeOwnership;
}
else version (Windows)
this(SOCKET handle)
this(SOCKET handle, bool takeOwnership = false)
{
s = driver.socketFromHandle(handle);
closeOnDestroy = takeOwnership;
}

///
~this()
{
close();
if(closeOnDestroy)
close();
}

/// close the socket
Expand All @@ -131,6 +136,7 @@ struct Socket
return;
driver.closeSocket(s);
s = Driver.SOCKET.INVALID;
closeOnDestroy = false;
}

/// return whether the socket is open
Expand Down Expand Up @@ -269,7 +275,7 @@ struct Socket
Socket accept(ref SocketAddr remoteAddr) @trusted
{
socklen_t addrlen = remoteAddr.sizeof;
return Socket(driver.accept(s, cast(sockaddr*)&remoteAddr, addrlen));
return Socket(driver.accept(s, cast(sockaddr*)&remoteAddr, addrlen), true);
}

///
Expand Down Expand Up @@ -552,8 +558,9 @@ struct Socket
Socket move() return scope nothrow /*pure Issue 18590*/
{
auto s = this.s;
auto cod = this.closeOnDestroy;
this.s = Driver.SOCKET.INVALID;
return Socket(s);
return Socket(s, cod);
}

/// not copyable
Expand Down Expand Up @@ -606,10 +613,14 @@ package(std.io.net):
private:
import std.typecons : Tuple;

this(return scope Driver.SOCKET s) @trusted pure nothrow
this(return scope Driver.SOCKET s, bool cod) @trusted pure nothrow
{
this.s = s;
this.closeOnDestroy = cod;
}

Driver.SOCKET s = Driver.SOCKET.INVALID;
// close when the destructor is run. True normally unless one wraps an
// existing handle (e.g. stdout).
bool closeOnDestroy = false;
}
7 changes: 4 additions & 3 deletions src/std/io/package.d
Original file line number Diff line number Diff line change
Expand Up @@ -205,16 +205,17 @@ IOObject!IO ioObject(IO)(IO io)
/// takes and interface
static ubyte[] consume(scope Input input, return ubyte[] buf) @safe
{

return buf[0 .. input.read(buf)];
}

ubyte[4] ping = ['p', 'i', 'n', 'g'];

File("temp.txt", mode!"w").write(ping[]);
File("UT_1.txt", mode!"w").write(ping[]);
scope (exit)
remove("temp.txt");
remove("UT_1.txt");

auto file = ioObject(File("temp.txt"));
auto file = ioObject(File("UT_1.txt"));
ubyte[4] buf;
assert(consume(file, buf[]) == ping[]);

Expand Down
2 changes: 1 addition & 1 deletion travis.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ fi
case "${BUILD_TOOL}" in
meson)
pip3 install --user --upgrade pip
pip install --user --upgrade meson
pip install --user --upgrade meson ninja
meson builddir -Drun_test=true
ninja -C builddir test
;;
Expand Down

0 comments on commit 6c93762

Please sign in to comment.