From 47c60991f9705b1bdade58a30691eb40c46f86fa Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Mon, 30 Jan 2023 10:03:02 -0800 Subject: [PATCH 1/2] Await registered actions in Simulator --- lib/src/modules/conditional.dart | 23 ++++++++++++++++------- lib/src/simulator.dart | 14 +++++++------- test/simulator_test.dart | 28 +++++++++++++++++++++++++++- 3 files changed, 50 insertions(+), 15 deletions(-) diff --git a/lib/src/modules/conditional.dart b/lib/src/modules/conditional.dart index 36dc7d24c..5b6d1e7c4 100644 --- a/lib/src/modules/conditional.dart +++ b/lib/src/modules/conditional.dart @@ -369,13 +369,22 @@ class Sequential extends _Always { // once the clocks are stable, execute the contents of the FF _execute(); _pendingExecute = false; - }).catchError( - test: (error) => error is Exception, - // ignore: avoid_types_on_closure_parameters - (Object err, StackTrace stackTrace) { - Simulator.throwException(err as Exception, stackTrace); - }, - )); + }).catchError(test: (error) => error is Exception, + // ignore: avoid_types_on_closure_parameters + (Object err, StackTrace stackTrace) { + Simulator.throwException(err as Exception, stackTrace); + }).catchError(test: (error) => error is StateError, + // ignore: avoid_types_on_closure_parameters + (Object err, StackTrace stackTrace) { + // This could be a result of the `Simulator` being reset, causing + // the stream to `close` before `first` occurs. + if (!Simulator.simulationHasEnded) { + // If the `Simulator` is still running, rethrow immediately. + + // ignore: only_throw_errors + throw err; + } + })); } _pendingExecute = true; }); diff --git a/lib/src/simulator.dart b/lib/src/simulator.dart index 848241499..0ffc4b4f8 100644 --- a/lib/src/simulator.dart +++ b/lib/src/simulator.dart @@ -73,8 +73,8 @@ class Simulator { _pendingTimestamps.isNotEmpty || _injectedActions.isNotEmpty; /// Sorted storage for pending functions to execute at appropriate times. - static final SplayTreeMap> _pendingTimestamps = - SplayTreeMap>(); + static final SplayTreeMap> _pendingTimestamps = + SplayTreeMap>(); /// Functions to be executed as soon as possible by the [Simulator]. /// @@ -170,7 +170,7 @@ class Simulator { } /// Registers an abritrary [action] to be executed at [timestamp] time. - static void registerAction(int timestamp, void Function() action) { + static void registerAction(int timestamp, dynamic Function() action) { if (timestamp <= _currentTimestamp) { throw Exception('Cannot add timestamp "$timestamp" in the past.' ' Current time is ${Simulator.time}'); @@ -225,9 +225,9 @@ class Simulator { _currentTimestamp = nextTimeStamp; - await tickExecute(() { + await tickExecute(() async { for (final func in _pendingTimestamps[nextTimeStamp]!) { - func(); + await func(); } }); _pendingTimestamps.remove(_currentTimestamp); @@ -242,7 +242,7 @@ class Simulator { } /// Performs the actual execution of a collection of actions for a [tick()]. - static Future tickExecute(void Function() toExecute) async { + static Future tickExecute(dynamic Function() toExecute) async { _phase = SimulatorPhase.beforeTick; // useful for flop sampling @@ -252,7 +252,7 @@ class Simulator { // useful for things that need to trigger every tick without other input _startTickController.add(null); - toExecute(); + await toExecute(); _phase = SimulatorPhase.clkStable; diff --git a/test/simulator_test.dart b/test/simulator_test.dart index 156551540..3d5377dae 100644 --- a/test/simulator_test.dart +++ b/test/simulator_test.dart @@ -51,7 +51,9 @@ void main() { test('simulator reset waits for simulation to complete', () async { Simulator.registerAction(100, Simulator.endSimulation); - Simulator.registerAction(100, Simulator.reset); + Simulator.registerAction(100, () { + unawaited(Simulator.reset()); + }); Simulator.registerAction(100, () => true); await Simulator.run(); }); @@ -76,4 +78,28 @@ void main() { await Simulator.run(); expect(endOfSimActionExecuted, isTrue); }); + + test('simulator waits for async registered actions to complete', () async { + var registeredActionExecuted = false; + Simulator.registerAction(100, () => true); + Simulator.registerAction(50, () async { + await Future.delayed(const Duration(microseconds: 10)); + registeredActionExecuted = true; + }); + await Simulator.run(); + expect(registeredActionExecuted, isTrue); + }); + + test('simulator waits for async injected actions to complete', () async { + var injectedActionExecuted = false; + Simulator.registerAction(100, () => true); + Simulator.registerAction(50, () async { + Simulator.injectAction(() async { + await Future.delayed(const Duration(microseconds: 10)); + injectedActionExecuted = true; + }); + }); + await Simulator.run(); + expect(injectedActionExecuted, isTrue); + }); } From 9ff22621a228dc86498f71a2ef3f93165a6619a4 Mon Sep 17 00:00:00 2001 From: Max Korbel Date: Mon, 30 Jan 2023 10:08:33 -0800 Subject: [PATCH 2/2] update doc --- lib/src/simulator.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/src/simulator.dart b/lib/src/simulator.dart index 0ffc4b4f8..169a997f7 100644 --- a/lib/src/simulator.dart +++ b/lib/src/simulator.dart @@ -170,6 +170,8 @@ class Simulator { } /// Registers an abritrary [action] to be executed at [timestamp] time. + /// + /// The [action], if it returns a [Future], will be `await`ed. static void registerAction(int timestamp, dynamic Function() action) { if (timestamp <= _currentTimestamp) { throw Exception('Cannot add timestamp "$timestamp" in the past.'