From d9e545bf609d1898716a80915af6f13ca2e96f1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Sun, 25 Aug 2019 12:02:08 +0200 Subject: [PATCH 1/2] Fix getFileInfo's exception handling and reduce overhead of the background work. Instead of asyncWork, now uses a worker task directly and signals the finalization of the result using message passing. This avoids the roundtrip required to return the task handle, as well as the heap allocated result buffer of Future!T. --- source/vibe/core/file.d | 50 +++++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/source/vibe/core/file.d b/source/vibe/core/file.d index 86ee6f3..410ddf3 100644 --- a/source/vibe/core/file.d +++ b/source/vibe/core/file.d @@ -7,7 +7,6 @@ */ module vibe.core.file; -import vibe.core.concurrency : asyncWork; import eventcore.core : NativeEventDriver, eventDriver; import eventcore.driver; import vibe.core.internal.release; @@ -282,7 +281,7 @@ bool existsFile(string path) nothrow static if (__VERSION__ < 2067) scope(failure) assert(0, "Error: existsFile should never throw"); - try return asyncWork((string p) => std.file.exists(p), path).getResult(); + try return performInWorker((string p) => std.file.exists(p), path); catch (Exception e) { logDebug("Failed to determine file existence for '%s': %s", path, e.msg); return false; @@ -300,10 +299,18 @@ FileInfo getFileInfo(NativePath path) /// ditto FileInfo getFileInfo(string path) { - return asyncWork((string p) { - auto ent = DirEntry(p); - return makeFileInfo(ent); - }, path).getResult(); + import std.typecons : tuple; + + auto ret = performInWorker((string p) { + try { + auto ent = DirEntry(p); + return tuple(makeFileInfo(ent), ""); + } catch (Exception e) { + return tuple(FileInfo.init, e.msg.length ? e.msg : "Failed to get file information"); + } + }, path); + if (ret[1].length) throw new Exception(ret[1]); + return ret[0]; } /** @@ -316,7 +323,7 @@ void createDirectory(NativePath path) /// ditto void createDirectory(string path, Flag!"recursive" recursive = No.recursive) { - auto fail = asyncWork((string p, bool rec) { + auto fail = performInWorker((string p, bool rec) { try { if (rec) mkdirRecurse(p); else mkdir(p); @@ -870,3 +877,32 @@ unittest { assert(readFile(name) == "create, then append"); } + + +private auto performInWorker(C, ARGS...)(C callable, auto ref ARGS args) +{ + version (none) { + import vibe.core.concurrency : asyncWork; + return asyncWork(callable, args).getResult(); + } else { + import vibe.core.core : runWorkerTask; + import core.atomic : atomicFence; + import std.concurrency : Tid, send, receiveOnly, thisTid; + + struct R {} + + alias RET = typeof(callable(args)); + shared(RET) ret; + runWorkerTask((shared(RET)* r, Tid caller, C c, ref ARGS a) nothrow { + *() @trusted { return cast(RET*)r; } () = c(a); + // Just as a precaution, because ManualEvent is not well defined in + // terms of fence semantics + atomicFence(); + try caller.send(R.init); + catch (Exception e) assert(false, e.msg); + }, () @trusted { return &ret; } (), thisTid, callable, args); + () @trusted { receiveOnly!R(); } (); + atomicFence(); + return ret; + } +} From c38d1816708a07d8f0a30b83c59d08a1cea3b690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Sun, 25 Aug 2019 14:09:59 +0200 Subject: [PATCH 2/2] Add basic tests for getFileInfo. --- tests/vibe.core.file.d | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/vibe.core.file.d b/tests/vibe.core.file.d index 254b684..1da05fa 100644 --- a/tests/vibe.core.file.d +++ b/tests/vibe.core.file.d @@ -5,6 +5,7 @@ module test; import vibe.core.file; +import std.exception; enum ubyte[] bytes(BYTES...) = [BYTES]; @@ -32,5 +33,15 @@ void main() assert(dst[] == bytes!(3, 4, 5, 6, 7)); f.close(); + auto fi = getFileInfo("test.dat"); + assert(fi.name == "test.dat"); + assert(fi.isFile); + assert(!fi.isDirectory); + assert(!fi.isSymlink); + assert(!fi.hidden); + assert(fi.size == 10); + + assertThrown(getFileInfo("*impossible:file?")); + removeFile("test.dat"); }