Skip to content

Commit 1a724e1

Browse files
committed
Fixed coroutine not removed after finishing
1 parent 7673bd3 commit 1a724e1

File tree

2 files changed

+32
-12
lines changed

2 files changed

+32
-12
lines changed

lib/sync.dart

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@ library coroutines;
22

33
typedef CoroutineValue<T> = Iterable<T>;
44
typedef Coroutine<T> = CoroutineValue<T> Function();
5+
typedef CoroutineInstance<T> = Iterator<T>;
56

67
mixin class CoroutineExecutor {
78
/// Map of running coroutines
89
/// Key: coroutine hash
910
/// Value: coroutine instance
10-
final Map<int, Iterator> _runningCoroutines = {};
11+
final Map<int, CoroutineInstance> _runningCoroutines = {};
12+
13+
/// The count of running coroutines
14+
int get countCoroutines => _runningCoroutines.length;
1115

1216
/// Adds a coroutine to the executor without starting it
1317
/// Does nothing if the coroutine is already running
@@ -16,39 +20,38 @@ mixin class CoroutineExecutor {
1620
}
1721

1822
@pragma('vm:always-consider-inlining')
19-
Iterator<T> _getOrAddCoroutine<T>(Coroutine<T> coroutine) {
23+
CoroutineInstance<T> _getOrAddCoroutine<T>(Coroutine<T> coroutine) {
2024
final int id = coroutine.hashCode;
2125
if (_runningCoroutines[id] == null) {
2226
_runningCoroutines[id] = coroutine().iterator;
2327
}
2428

25-
return _runningCoroutines[id]! as Iterator<T>;
29+
return _runningCoroutines[id]! as CoroutineInstance<T>;
2630
}
2731

2832
/// Starts or continues a coroutine
2933
/// It starts or continues its execution depending on its current state
3034
/// If the coroutine is paused, it resumes execution from the last yield point
3135
T? runCoroutine<T>(Coroutine<T> coroutine) {
32-
final Iterator<T> instance = _getOrAddCoroutine(coroutine);
33-
return _stepCoroutine(instance);
36+
return _stepCoroutine(coroutine.hashCode, _getOrAddCoroutine(coroutine));
3437
}
3538

3639
/// Continues all coroutines in this executor
3740
void runAllCoroutines() {
38-
for (final instance in _runningCoroutines.values) {
39-
_stepCoroutine(instance);
41+
for (final id in _runningCoroutines.keys) {
42+
_stepCoroutine(id, _runningCoroutines[id]!);
4043
}
4144
}
4245

4346
@pragma('vm:always-consider-inlining')
44-
T? _stepCoroutine<T>(Iterator<T> instance) {
47+
T? _stepCoroutine<T>(int id, CoroutineInstance<T> instance) {
4548
final bool hasNext = instance.moveNext();
4649

4750
if (hasNext) {
4851
return instance.current;
4952
} else {
5053
// coroutine has finished execution
51-
_runningCoroutines.remove(instance.hashCode);
54+
_runningCoroutines.remove(id);
5255
return null;
5356
}
5457
}
@@ -63,7 +66,4 @@ mixin class CoroutineExecutor {
6366
void stopAllCoroutines() {
6467
_runningCoroutines.clear();
6568
}
66-
67-
/// The count of running coroutines
68-
int get countCoroutines => _runningCoroutines.length;
6969
}

test/sync.dart

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,24 @@ void main() {
115115
expect(counterA, equals(2));
116116
expect(counterB, equals(4));
117117
});
118+
119+
test('finished coroutine is removed from running coroutines', () {
120+
final executor = CoroutineExecutor();
121+
122+
CoroutineValue<int> myCoroutine() sync* {
123+
int counter = 1;
124+
yield counter;
125+
counter = 2;
126+
yield counter;
127+
}
128+
129+
expect(executor.countCoroutines, equals(0));
130+
131+
executor.runCoroutine(myCoroutine); // value 1
132+
expect(executor.countCoroutines, equals(1));
133+
134+
executor.runCoroutine(myCoroutine); // value 2
135+
executor.runCoroutine(myCoroutine); // value null, coroutine finished
136+
expect(executor.countCoroutines, equals(0));
137+
});
118138
}

0 commit comments

Comments
 (0)