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.
This commit is contained in:
Sönke Ludwig 2019-08-25 12:02:08 +02:00
parent d7dfb6cd23
commit d9e545bf60

View file

@ -7,7 +7,6 @@
*/ */
module vibe.core.file; module vibe.core.file;
import vibe.core.concurrency : asyncWork;
import eventcore.core : NativeEventDriver, eventDriver; import eventcore.core : NativeEventDriver, eventDriver;
import eventcore.driver; import eventcore.driver;
import vibe.core.internal.release; import vibe.core.internal.release;
@ -282,7 +281,7 @@ bool existsFile(string path) nothrow
static if (__VERSION__ < 2067) static if (__VERSION__ < 2067)
scope(failure) assert(0, "Error: existsFile should never throw"); 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) { catch (Exception e) {
logDebug("Failed to determine file existence for '%s': %s", path, e.msg); logDebug("Failed to determine file existence for '%s': %s", path, e.msg);
return false; return false;
@ -300,10 +299,18 @@ FileInfo getFileInfo(NativePath path)
/// ditto /// ditto
FileInfo getFileInfo(string path) FileInfo getFileInfo(string path)
{ {
return asyncWork((string p) { import std.typecons : tuple;
auto ret = performInWorker((string p) {
try {
auto ent = DirEntry(p); auto ent = DirEntry(p);
return makeFileInfo(ent); return tuple(makeFileInfo(ent), "");
}, path).getResult(); } 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 /// ditto
void createDirectory(string path, Flag!"recursive" recursive = No.recursive) void createDirectory(string path, Flag!"recursive" recursive = No.recursive)
{ {
auto fail = asyncWork((string p, bool rec) { auto fail = performInWorker((string p, bool rec) {
try { try {
if (rec) mkdirRecurse(p); if (rec) mkdirRecurse(p);
else mkdir(p); else mkdir(p);
@ -870,3 +877,32 @@ unittest {
assert(readFile(name) == "create, then append"); 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;
}
}