Comments (3)
This is intended (and documented!) behavior. Among the Script
class's guarantees is:
Once done completes, stdout and stderr will emit done events immediately afterwards (modulo a few microtasks).
That "modulo" is important! If you remove await new Future(() {})
, your test has a race condition between the final microtasks of the Script
completing and the microtasks necessary for the test case to reach expect(fakeStdout.lines, ['hello\n']);
. Notably, there is not a guarantee that stdout
emits a done
event before exitCode
completes.
from dart_cli_script.
This is intended (and documented!) behavior.
You're right!
But is it useful? Would dart_cli_script
be easier to use if exitCode
and done
would wait for the completion of stdout
and stderr
before completing? Is there any benefit of the current behaviour?
I would like to have a Script
API that notifies me when everything is finished and closed. This would allow me to overcome my race condition. .done
feels like it is the correct method to accomplish this.
await Future((){});
does not feel like an official way.
Delay .done
by one macrotask
Adding the delay, would make done
more predictable. After completion, pipes could be guaranteed to be closed.
It would also make the API more future-proof, in case it requires two macrotasks at some point in the future.
exitCode.then((code) async {
if (_doneCompleter.isCompleted) return;
+ await Future(() {});
if (code == 0) {
debug("[$name] exited with exit code 0");
_doneCompleter.complete(null);
} else {
debug("[$name] exited with exit code $code");
_doneCompleter.completeError(
ScriptException(name, code), StackTrace.current);
}
_closeOutputStreams();
_extraStderrController.close();
_stdinCloser.close();
}, onError: _handleError);
Adding the macrotask would also make Script.kill()
more predictable, always returning false
after done
completed.
See interrupts pipe
test (the only one failing), which requires one implicit macrotask (await lines.toList()
) after script.done
before kill()
returns false.
test('interrupts pipe', () async {
var controller = StreamController<List<int>>();
var completer = Completer<void>();
var script = controller.stream | _watchSignalsAndStdin(completer);
var lines = script.stdout.lines;
controller.sink.add(utf8.encode('before'));
await pumpEventQueue();
expect(script.kill(), true);
await pumpEventQueue();
controller.sink.add(utf8.encode('after'));
await pumpEventQueue();
expect(script.kill(), true);
await pumpEventQueue();
completer.complete();
expect(script.done, throwsScriptException(143));
expect(await lines.toList(), [
'from a: before',
'b: SIGTERM',
'b: bye!',
]);
+ await script.done.catchError((_) {});
expect(script.kill(), false);
});
With the additional delay, the test can use await script.done.catchError((_) {});
before the final assertion expect(script.kill(), false);
to explicitly reach the correct state.
from dart_cli_script.
Your proposed fix introduces a new race condition: if _doneCompleter
is completed during the added padding macrotask, it would end up double-completing. That's certainly fixable, but it does illustrate that messing around with task ordering without having explicit data dependency paths is very error-prone. Even if we did provide a completion-order guarantee, it would be a very bad idea to rely on it.
I'm skeptical just adding a macrotask wait would actually provide as strong a guarantee as you want it to. Asynchronous programming is complex, full of timing-dependent edge cases, and difficult to reason about, so I'm generally more comfortable providing a looser guarantee that pushes users to explicitly set up future chains rather than relying on implicit timings.
from dart_cli_script.
Related Issues (13)
- check function needs to handle stdin HOT 1
- Can't access Script stderr and exitCode together HOT 5
- capture outputs to the console when an error occurs. HOT 1
- Can't access Script.stderr for captured scripts HOT 1
- (stack overflow) what's the best way to pipe the output of Script into console? HOT 1
- Doesn't work with environment variables HOT 1
- While testing, Script.capture hangs when piping a closed ByteStream
- How to kill a running script? HOT 2
- Deadlock when using `operator>` on a stream that emits more than 16KiB HOT 1
- Different results with real terminal for double quotes searchs HOT 1
- Dart 3 compatability HOT 1
- Script.kill() makes script.done error HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from dart_cli_script.