Support for changing the current working directory only within the scope of a function
isolate_current_directory
This library exports a single function, withCurrentDirectory
, which can
change Directory.current
(the working directory) within the scope of a lambda, but not the global value.
That means that using this function, it’s possible to write concurrent Dart code that executes in different working directories without different computations affecting each other.
Using this library
To add a dependency on your pubspec:
dart pub add isolate_current_directory
Now, you can use withCurrentDirectory
:
withCurrentDirectory('my-dir', () async {
// this file resolves to my-dir/example.txt
final file = File('example.txt');
// use the file!
});
See isolate_current_directory_example.dart for a complete example.
Motivation
Dart’s Directory.current
is a global variable that can be changed at any time by any Dart code.
In asynchronous code, you could use a lock (see the synchronized package) to try to avoid modifying the working directory while other async code is running, but that is impossible to guarantee as any code that ignores the lock could still concurrently modify the working directory.
This problem is even more vexing in the presence of Isolate
s
because if any code in any Isolate
changes the working directory, then all other Isolate
s will see that but have
no way that I know of to synchronize access to Directory.current
, because Isolate
s are supposed to be, well,
isolated from each other so they cannot share the same lock!
By using IOOverrides
,
this library leverages Dart Zone
s to isolate
Directory.current
to the scope of a function.
No matter how many functions are running concurrently, even across many Isolate
s, each function has its own
working directory. It can change its own working directory without affecting any other code running in a different
scope.
Caveats
Process
Unfortunately, methods from Process
do not honour the scoped Directory.current
value by default.
For this reason, when using Process
, you must pass in the workingDirectory
argument explicitly:
Process.start('cmd', const ['args'], workingDirectory: Directory.current.path);
Performance
Another possible issue is performance. When a FileSystemEntity
is created within the scope of withCurrentDirectory
,
a custom implementation of the dart:io
type (File
, Directory
, Link
) is created which will check at each
operation what’s the scoped value of Directory.current
, which may have a non-negligible cost if this happens in
the hot path of an application.
Link bugs
Links mostly work, but mysteriously, exists()
doesn’t seem to.
See link_test.dart.