Implement file support for the WinAPI driver.
This commit is contained in:
parent
2a5252977e
commit
6b68e934f3
|
@ -31,7 +31,7 @@ DNS | yes | yes | no | no
|
||||||
Timers | yes | yes | yes | no
|
Timers | yes | yes | yes | no
|
||||||
Events | yes | yes | yes | no
|
Events | yes | yes | yes | no
|
||||||
Signals | yes² | yes² | no | no
|
Signals | yes² | yes² | no | no
|
||||||
Files | yes | yes | no | no
|
Files | yes | yes | yes | no
|
||||||
UI Integration | no | no | yes | no
|
UI Integration | no | no | yes | no
|
||||||
File watcher | yes² | yes² | yes | no
|
File watcher | yes² | yes² | yes | no
|
||||||
|
|
||||||
|
|
|
@ -20,10 +20,10 @@ import core.time : Duration;
|
||||||
import std.experimental.allocator;
|
import std.experimental.allocator;
|
||||||
import std.socket : Address;
|
import std.socket : Address;
|
||||||
|
|
||||||
|
|
||||||
static assert(HANDLE.sizeof <= FD.BaseType.sizeof);
|
static assert(HANDLE.sizeof <= FD.BaseType.sizeof);
|
||||||
static assert(FD(cast(int)INVALID_HANDLE_VALUE) == FD.init);
|
static assert(FD(cast(int)INVALID_HANDLE_VALUE) == FD.init);
|
||||||
|
|
||||||
|
|
||||||
final class WinAPIEventDriver : EventDriver {
|
final class WinAPIEventDriver : EventDriver {
|
||||||
private {
|
private {
|
||||||
WinAPIEventDriverCore m_core;
|
WinAPIEventDriverCore m_core;
|
||||||
|
@ -52,7 +52,7 @@ final class WinAPIEventDriver : EventDriver {
|
||||||
m_timers = new LoopTimeoutTimerDriver();
|
m_timers = new LoopTimeoutTimerDriver();
|
||||||
m_core = new WinAPIEventDriverCore(m_timers);
|
m_core = new WinAPIEventDriverCore(m_timers);
|
||||||
m_events = new WinAPIEventDriverEvents(m_core);
|
m_events = new WinAPIEventDriverEvents(m_core);
|
||||||
m_files = new WinAPIEventDriverFiles();
|
m_files = new WinAPIEventDriverFiles(m_core);
|
||||||
m_sockets = new WinAPIEventDriverSockets();
|
m_sockets = new WinAPIEventDriverSockets();
|
||||||
m_dns = new WinAPIEventDriverDNS();
|
m_dns = new WinAPIEventDriverDNS();
|
||||||
m_watchers = new WinAPIEventDriverWatchers(m_core);
|
m_watchers = new WinAPIEventDriverWatchers(m_core);
|
||||||
|
@ -358,58 +358,200 @@ final class WinAPIEventDriverDNS : EventDriverDNS {
|
||||||
|
|
||||||
|
|
||||||
final class WinAPIEventDriverFiles : EventDriverFiles {
|
final class WinAPIEventDriverFiles : EventDriverFiles {
|
||||||
@safe: /*@nogc:*/ nothrow:
|
@safe /*@nogc*/ nothrow:
|
||||||
|
private {
|
||||||
|
WinAPIEventDriverCore m_core;
|
||||||
|
}
|
||||||
|
|
||||||
|
this(WinAPIEventDriverCore core)
|
||||||
|
{
|
||||||
|
m_core = core;
|
||||||
|
}
|
||||||
|
|
||||||
override FileFD open(string path, FileOpenMode mode)
|
override FileFD open(string path, FileOpenMode mode)
|
||||||
{
|
{
|
||||||
assert(false, "TODO!");
|
import std.utf : toUTF16z;
|
||||||
|
|
||||||
|
auto access = mode == FileOpenMode.readWrite || mode == FileOpenMode.createTrunc ? (GENERIC_WRITE | GENERIC_READ) :
|
||||||
|
mode == FileOpenMode.append ? GENERIC_WRITE : GENERIC_READ;
|
||||||
|
auto shareMode = mode == FileOpenMode.read ? FILE_SHARE_READ : 0;
|
||||||
|
auto creation = mode == FileOpenMode.createTrunc ? CREATE_ALWAYS : mode == FileOpenMode.append? OPEN_ALWAYS : OPEN_EXISTING;
|
||||||
|
|
||||||
|
auto handle = () @trusted {
|
||||||
|
scope (failure) assert(false);
|
||||||
|
return CreateFileW(path.toUTF16z, access, shareMode, null, creation,
|
||||||
|
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, null);
|
||||||
|
} ();
|
||||||
|
auto errorcode = GetLastError();
|
||||||
|
if (handle == INVALID_HANDLE_VALUE)
|
||||||
|
return FileFD.invalid;
|
||||||
|
|
||||||
|
if (mode == FileOpenMode.createTrunc && errorcode == ERROR_ALREADY_EXISTS) {
|
||||||
|
BOOL ret = SetEndOfFile(handle);
|
||||||
|
if (!ret) {
|
||||||
|
CloseHandle(handle);
|
||||||
|
return FileFD.init;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return adopt(cast(int)handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
override FileFD adopt(int system_handle)
|
override FileFD adopt(int system_handle)
|
||||||
{
|
{
|
||||||
assert(false, "TODO!");
|
auto handle = () @trusted { return cast(HANDLE)system_handle; } ();
|
||||||
|
DWORD f;
|
||||||
|
if (!() @trusted { return GetHandleInformation(handle, &f); } ())
|
||||||
|
return FileFD.init;
|
||||||
|
|
||||||
|
auto s = &m_core.setupSlot!FileSlot(handle);
|
||||||
|
s.read.handle = s.write.handle = handle;
|
||||||
|
|
||||||
|
return FileFD(system_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
override void close(FileFD file)
|
override void close(FileFD file)
|
||||||
{
|
{
|
||||||
assert(false, "TODO!");
|
auto h = idToHandle(file);
|
||||||
|
auto slot = &m_core.m_handles[h].file();
|
||||||
|
if (slot.read.handle != INVALID_HANDLE_VALUE) {
|
||||||
|
CloseHandle(h);
|
||||||
|
slot.read.handle = slot.write.handle = INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override ulong getSize(FileFD file)
|
override ulong getSize(FileFD file)
|
||||||
{
|
{
|
||||||
assert(false, "TODO!");
|
LARGE_INTEGER size;
|
||||||
|
auto succeeded = () @trusted { return GetFileSizeEx(idToHandle(file), &size); } ();
|
||||||
|
if (!succeeded || size.QuadPart < 0)
|
||||||
|
return ulong.max;
|
||||||
|
return size.QuadPart;
|
||||||
}
|
}
|
||||||
|
|
||||||
override void write(FileFD file, ulong offset, const(ubyte)[] buffer, IOMode mode, FileIOCallback on_write_finish)
|
override void write(FileFD file, ulong offset, const(ubyte)[] buffer, IOMode mode, FileIOCallback on_write_finish)
|
||||||
{
|
{
|
||||||
assert(false, "TODO!");
|
if (!buffer.length) {
|
||||||
|
on_write_finish(file, IOStatus.ok, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto h = idToHandle(file);
|
||||||
|
auto slot = &m_core.m_handles[h].file.write;
|
||||||
|
slot.bytesTransferred = 0;
|
||||||
|
slot.offset = offset;
|
||||||
|
slot.buffer = buffer;
|
||||||
|
slot.mode = mode;
|
||||||
|
slot.callback = on_write_finish;
|
||||||
|
startIO!(WriteFileEx, true)(h, slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
override void read(FileFD file, ulong offset, ubyte[] buffer, IOMode mode, FileIOCallback on_read_finish)
|
override void read(FileFD file, ulong offset, ubyte[] buffer, IOMode mode, FileIOCallback on_read_finish)
|
||||||
{
|
{
|
||||||
assert(false, "TODO!");
|
if (!buffer.length) {
|
||||||
|
on_read_finish(file, IOStatus.ok, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto h = idToHandle(file);
|
||||||
|
auto slot = &m_core.m_handles[h].file.read;
|
||||||
|
slot.bytesTransferred = 0;
|
||||||
|
slot.offset = offset;
|
||||||
|
slot.buffer = buffer;
|
||||||
|
slot.mode = mode;
|
||||||
|
slot.callback = on_read_finish;
|
||||||
|
startIO!(ReadFileEx, false)(h, slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
override void cancelWrite(FileFD file)
|
override void cancelWrite(FileFD file)
|
||||||
{
|
{
|
||||||
assert(false, "TODO!");
|
auto h = idToHandle(file);
|
||||||
|
cancelIO!true(h, m_core.m_handles[h].file.write);
|
||||||
}
|
}
|
||||||
|
|
||||||
override void cancelRead(FileFD file)
|
override void cancelRead(FileFD file)
|
||||||
{
|
{
|
||||||
assert(false, "TODO!");
|
auto h = idToHandle(file);
|
||||||
|
cancelIO!false(h, m_core.m_handles[h].file.read);
|
||||||
}
|
}
|
||||||
|
|
||||||
override void addRef(FileFD descriptor)
|
override void addRef(FileFD descriptor)
|
||||||
{
|
{
|
||||||
assert(false, "TODO!");
|
m_core.m_handles[idToHandle(descriptor)].addRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
override bool releaseRef(FileFD descriptor)
|
override bool releaseRef(FileFD descriptor)
|
||||||
{
|
{
|
||||||
assert(false, "TODO!");
|
auto h = idToHandle(descriptor);
|
||||||
|
return m_core.m_handles[h].releaseRef({
|
||||||
|
close(descriptor);
|
||||||
|
m_core.freeSlot(h);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void startIO(alias fun, bool RO)(HANDLE h, FileSlot.Direction!RO* slot)
|
||||||
|
{
|
||||||
|
import std.algorithm.comparison : min;
|
||||||
|
|
||||||
|
with (slot.overlapped) {
|
||||||
|
Internal = 0;
|
||||||
|
InternalHigh = 0;
|
||||||
|
Offset = cast(uint)(slot.offset & 0xFFFFFFFF);
|
||||||
|
OffsetHigh = cast(uint)(slot.offset >> 32);
|
||||||
|
hEvent = () @trusted { return cast(HANDLE)slot; } ();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto nbytes = min(slot.buffer.length, DWORD.max);
|
||||||
|
if (!() @trusted { return fun(h, &slot.buffer[0], nbytes, &slot.overlapped, &onIOFinished!(fun, RO)); } ()) {
|
||||||
|
slot.invokeCallback(IOStatus.error, slot.bytesTransferred);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void cancelIO(bool RO)(HANDLE h, ref FileSlot.Direction!RO slot)
|
||||||
|
{
|
||||||
|
if (slot.callback) {
|
||||||
|
//CancelIoEx(h, &slot.overlapped); // FIXME: currently causes linker errors for DMD due to outdated kernel32.lib files
|
||||||
|
slot.callback = null;
|
||||||
|
slot.buffer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static extern(Windows)
|
||||||
|
void onIOFinished(alias fun, bool RO)(DWORD error, DWORD bytes_transferred, OVERLAPPED* overlapped)
|
||||||
|
{
|
||||||
|
|
||||||
|
auto slot = () @trusted { return cast(FileSlot.Direction!RO*)overlapped.hEvent; } ();
|
||||||
|
assert(slot !is null);
|
||||||
|
HANDLE h = slot.handle;
|
||||||
|
auto id = FileFD(cast(int)h);
|
||||||
|
|
||||||
|
if (!slot.callback) {
|
||||||
|
// request was already cancelled
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error != 0) {
|
||||||
|
slot.invokeCallback(IOStatus.error, slot.bytesTransferred + bytes_transferred);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
slot.bytesTransferred += bytes_transferred;
|
||||||
|
slot.offset += bytes_transferred;
|
||||||
|
|
||||||
|
if (slot.bytesTransferred >= slot.buffer.length || slot.mode != IOMode.all) {
|
||||||
|
slot.invokeCallback(IOStatus.ok, slot.bytesTransferred);
|
||||||
|
} else {
|
||||||
|
startIO!(fun, RO)(h, slot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HANDLE idToHandle(FileFD id)
|
||||||
|
@trusted {
|
||||||
|
return cast(HANDLE)cast(int)id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
final class WinAPIEventDriverEvents : EventDriverEvents {
|
final class WinAPIEventDriverEvents : EventDriverEvents {
|
||||||
@safe: /*@nogc:*/ nothrow:
|
@safe: /*@nogc:*/ nothrow:
|
||||||
private {
|
private {
|
||||||
|
@ -656,18 +798,15 @@ final class WinAPIEventDriverWatchers : EventDriverWatchers {
|
||||||
|
|
||||||
override bool releaseRef(WatcherID descriptor)
|
override bool releaseRef(WatcherID descriptor)
|
||||||
{
|
{
|
||||||
bool freed;
|
|
||||||
auto handle = idToHandle(descriptor);
|
auto handle = idToHandle(descriptor);
|
||||||
m_core.m_handles[handle].releaseRef(()nothrow{
|
return m_core.m_handles[handle].releaseRef(()nothrow{
|
||||||
CloseHandle(handle);
|
CloseHandle(handle);
|
||||||
() @trusted {
|
() @trusted {
|
||||||
try theAllocator.dispose(m_core.m_handles[handle].watcher.buffer);
|
try theAllocator.dispose(m_core.m_handles[handle].watcher.buffer);
|
||||||
catch (Exception e) assert(false, "Freeing directory watcher buffer failed.");
|
catch (Exception e) assert(false, "Freeing directory watcher buffer failed.");
|
||||||
} ();
|
} ();
|
||||||
m_core.freeSlot(handle);
|
m_core.freeSlot(handle);
|
||||||
freed = true;
|
|
||||||
});
|
});
|
||||||
return !freed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static nothrow extern(System)
|
private static nothrow extern(System)
|
||||||
|
@ -747,6 +886,7 @@ private long currStdTime()
|
||||||
private struct HandleSlot {
|
private struct HandleSlot {
|
||||||
static union SpecificTypes {
|
static union SpecificTypes {
|
||||||
typeof(null) none;
|
typeof(null) none;
|
||||||
|
FileSlot files;
|
||||||
WatcherSlot watcher;
|
WatcherSlot watcher;
|
||||||
}
|
}
|
||||||
int refCount;
|
int refCount;
|
||||||
|
@ -754,6 +894,7 @@ private struct HandleSlot {
|
||||||
|
|
||||||
@safe nothrow:
|
@safe nothrow:
|
||||||
|
|
||||||
|
@property ref FileSlot file() { return specific.get!FileSlot; }
|
||||||
@property ref WatcherSlot watcher() { return specific.get!WatcherSlot; }
|
@property ref WatcherSlot watcher() { return specific.get!WatcherSlot; }
|
||||||
|
|
||||||
void addRef()
|
void addRef()
|
||||||
|
@ -762,14 +903,40 @@ private struct HandleSlot {
|
||||||
refCount++;
|
refCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void releaseRef(scope void delegate() @safe nothrow on_free)
|
bool releaseRef(scope void delegate() @safe nothrow on_free)
|
||||||
{
|
{
|
||||||
assert(refCount > 0);
|
assert(refCount > 0);
|
||||||
if (--refCount == 0)
|
if (--refCount == 0) {
|
||||||
on_free();
|
on_free();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct FileSlot {
|
||||||
|
static struct Direction(bool RO) {
|
||||||
|
OVERLAPPED overlapped;
|
||||||
|
FileIOCallback callback;
|
||||||
|
ulong offset;
|
||||||
|
size_t bytesTransferred;
|
||||||
|
IOMode mode;
|
||||||
|
static if (RO) const(ubyte)[] buffer;
|
||||||
|
else ubyte[] buffer;
|
||||||
|
HANDLE handle; // set to INVALID_HANDLE_VALUE when closed
|
||||||
|
|
||||||
|
void invokeCallback(IOStatus status, size_t bytes_transferred)
|
||||||
|
@safe nothrow {
|
||||||
|
auto cb = this.callback;
|
||||||
|
this.callback = null;
|
||||||
|
assert(cb !is null);
|
||||||
|
cb(FileFD(cast(int)this.handle), status, bytes_transferred);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Direction!false read;
|
||||||
|
Direction!true write;
|
||||||
|
}
|
||||||
|
|
||||||
private struct WatcherSlot {
|
private struct WatcherSlot {
|
||||||
ubyte[] buffer;
|
ubyte[] buffer;
|
||||||
OVERLAPPED overlapped;
|
OVERLAPPED overlapped;
|
||||||
|
@ -777,3 +944,8 @@ private struct WatcherSlot {
|
||||||
bool recursive;
|
bool recursive;
|
||||||
FileChangesCallback callback;
|
FileChangesCallback callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private extern(Windows) @trusted nothrow @nogc {
|
||||||
|
BOOL SetEndOfFile(HANDLE hFile);
|
||||||
|
BOOL CancelIoEx(HANDLE hFile, OVERLAPPED* lpOverlapped);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue