Skip to content
This repository was archived by the owner on Feb 22, 2018. It is now read-only.

Commit e6689e3

Browse files
committed
fix(scope): allow concurrent fire/add/remove on listeners
1 parent 9891f33 commit e6689e3

File tree

2 files changed

+26
-24
lines changed

2 files changed

+26
-24
lines changed

lib/core/scope.dart

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -743,8 +743,8 @@ class ScopeStream extends async.Stream<ScopeEvent> {
743743
final _Streams _streams;
744744
final String _name;
745745
final subscriptions = <ScopeStreamSubscription>[];
746+
final List<Function> _work = <Function>[];
746747
bool _firing = false;
747-
List<ScopeStreamSubscription> _toRemove;
748748

749749

750750
ScopeStream(this._streams, this._exceptionHandler, this._name);
@@ -753,12 +753,21 @@ class ScopeStream extends async.Stream<ScopeEvent> {
753753
{ Function onError,
754754
void onDone(),
755755
bool cancelOnError }) {
756-
if (subscriptions.isEmpty) _streams._addCount(_name, 1);
757756
var subscription = new ScopeStreamSubscription(this, onData);
758-
subscriptions.add(subscription);
757+
_concurrentSafeWork(() {
758+
if (subscriptions.isEmpty) _streams._addCount(_name, 1);
759+
subscriptions.add(subscription);
760+
});
759761
return subscription;
760762
}
761763

764+
void _concurrentSafeWork([fn]) {
765+
if (fn != null) _work.add(fn);
766+
while(!_firing && _work.isNotEmpty) {
767+
_work.removeLast()();
768+
}
769+
}
770+
762771
void _fire(ScopeEvent event) {
763772
_firing = true;
764773
try {
@@ -771,31 +780,19 @@ class ScopeStream extends async.Stream<ScopeEvent> {
771780
}
772781
} finally {
773782
_firing = false;
774-
if (_toRemove != null) {
775-
_toRemove.forEach(_actuallyRemove);
776-
_toRemove = null;
777-
}
783+
_concurrentSafeWork();
778784
}
779785
}
780786

781787
void _remove(ScopeStreamSubscription subscription) {
782-
if (_firing) {
783-
if (_toRemove == null) {
784-
_toRemove = [];
788+
_concurrentSafeWork(() {
789+
assert(subscription._scopeStream == this);
790+
if (subscriptions.remove(subscription)) {
791+
if (subscriptions.isEmpty) _streams._addCount(_name, -1);
792+
} else {
793+
throw new StateError('AlreadyCanceled');
785794
}
786-
_toRemove.add(subscription);
787-
} else {
788-
_actuallyRemove(subscription);
789-
}
790-
}
791-
792-
void _actuallyRemove(ScopeStreamSubscription subscription) {
793-
assert(subscription._scopeStream == this);
794-
if (subscriptions.remove(subscription)) {
795-
if (subscriptions.isEmpty) _streams._addCount(_name, -1);
796-
} else {
797-
throw new StateError('AlreadyCanceled');
798-
}
795+
});
799796
}
800797
}
801798

test/core/scope_spec.dart

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -681,14 +681,19 @@ void main() {
681681
expect(args).toEqual(['do', 're', 'me', 'fa']);
682682
});
683683

684-
it('should allow removing listener during an event', (RootScope rootScope) {
684+
it('should allow removing/adding listener during an event', (RootScope rootScope, Logger log) {
685685
StreamSubscription subscription;
686686
subscription = rootScope.on('foo').listen((_) {
687687
subscription.cancel();
688+
rootScope.on('foo').listen((_) => log(3));
689+
log(2);
688690
});
689691
expect(() {
692+
log(1);
690693
rootScope.broadcast('foo');
691694
}).not.toThrow();
695+
rootScope.broadcast('foo');
696+
expect(log).toEqual([1, 2, 3]);
692697
});
693698
});
694699
});

0 commit comments

Comments
 (0)