Skip to content

Commit 3c30cd9

Browse files
authored
[RTDB] Fix concurrency crash in FView (#15548)
1 parent b0ac315 commit 3c30cd9

File tree

3 files changed

+38
-26
lines changed

3 files changed

+38
-26
lines changed

FirebaseDatabase/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# Unreleased
2+
- [fixed] Concurrency crash in FView. (#15514)
3+
14
# 11.9.0
25
- [fixed] Fix connection failure issue introduced in 10.27.0 by restoring the
36
Socket Rocket implementation instead of `NSURLSessionWebSocket`. Note that

FirebaseDatabase/Sources/Core/View/FView.m

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ @interface FView ()
7070
@property(nonatomic, strong, readwrite) FQuerySpec *query;
7171
@property(nonatomic, strong) FViewProcessor *processor;
7272
@property(nonatomic, strong) FViewCache *viewCache;
73+
74+
// All accesses must be guarded by @synchronized(self.eventRegistrations)
7375
@property(nonatomic, strong) NSMutableArray *eventRegistrations;
7476
@property(nonatomic, strong) FEventGenerator *eventGenerator;
7577

@@ -165,11 +167,15 @@ - (id)initWithQuery:(FQuerySpec *)query
165167
}
166168

167169
- (BOOL)isEmpty {
168-
return self.eventRegistrations.count == 0;
170+
@synchronized(self.eventRegistrations) {
171+
return self.eventRegistrations.count == 0;
172+
}
169173
}
170174

171175
- (void)addEventRegistration:(id<FEventRegistration>)eventRegistration {
172-
[self.eventRegistrations addObject:eventRegistration];
176+
@synchronized(self.eventRegistrations) {
177+
[self.eventRegistrations addObject:eventRegistration];
178+
}
173179
}
174180

175181
/**
@@ -181,31 +187,33 @@ - (void)addEventRegistration:(id<FEventRegistration>)eventRegistration {
181187
- (NSArray *)removeEventRegistration:(id<FEventRegistration>)eventRegistration
182188
cancelError:(NSError *)cancelError {
183189
NSMutableArray *cancelEvents = [[NSMutableArray alloc] init];
184-
if (cancelError != nil) {
185-
NSAssert(eventRegistration == nil,
186-
@"A cancel should cancel all event registrations.");
187-
FPath *path = self.query.path;
188-
for (id<FEventRegistration> registration in self.eventRegistrations) {
189-
FCancelEvent *maybeEvent =
190-
[registration createCancelEventFromError:cancelError path:path];
191-
if (maybeEvent) {
192-
[cancelEvents addObject:maybeEvent];
190+
@synchronized(self.eventRegistrations) {
191+
if (cancelError != nil) {
192+
NSAssert(eventRegistration == nil,
193+
@"A cancel should cancel all event registrations.");
194+
FPath *path = self.query.path;
195+
for (id<FEventRegistration> registration in self
196+
.eventRegistrations) {
197+
FCancelEvent *maybeEvent =
198+
[registration createCancelEventFromError:cancelError
199+
path:path];
200+
if (maybeEvent) {
201+
[cancelEvents addObject:maybeEvent];
202+
}
193203
}
194204
}
195-
}
196205

197-
if (eventRegistration) {
198-
NSUInteger i = 0;
199-
while (i < self.eventRegistrations.count) {
200-
id<FEventRegistration> existing = self.eventRegistrations[i];
201-
if ([existing matches:eventRegistration]) {
202-
[self.eventRegistrations removeObjectAtIndex:i];
203-
} else {
204-
i++;
205-
}
206+
if (eventRegistration) {
207+
NSIndexSet *indexesToRemove =
208+
[self.eventRegistrations indexesOfObjectsPassingTest:^BOOL(
209+
id<FEventRegistration> existing,
210+
NSUInteger idx, BOOL *stop) {
211+
return [existing matches:eventRegistration];
212+
}];
213+
[self.eventRegistrations removeObjectsAtIndexes:indexesToRemove];
214+
} else {
215+
[self.eventRegistrations removeAllObjects];
206216
}
207-
} else {
208-
[self.eventRegistrations removeAllObjects];
209217
}
210218
return cancelEvents;
211219
}
@@ -270,7 +278,10 @@ - (NSArray *)generateEventsForChanges:(NSArray *)changes
270278
registration:(id<FEventRegistration>)registration {
271279
NSArray *registrations;
272280
if (registration == nil) {
273-
registrations = [[NSArray alloc] initWithArray:self.eventRegistrations];
281+
@synchronized(self.eventRegistrations) {
282+
registrations =
283+
[[NSArray alloc] initWithArray:self.eventRegistrations];
284+
}
274285
} else {
275286
registrations = [[NSArray alloc] initWithObjects:registration, nil];
276287
}

FirebaseDatabase/Tests/Integration/FIRDatabaseQueryTests.m

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3563,7 +3563,6 @@ - (void)testListenForChildAddedWithLimitEnsureEventsFireProperly {
35633563
WAIT_FOR(count == 3);
35643564
}
35653565

3566-
#ifdef FLAKY_TEST
35673566
- (void)testListenForChildChangedWithLimitEnsureEventsFireProperly {
35683567
FTupleFirebase* refs = [FTestHelpers getRandomNodePair];
35693568
FIRDatabaseReference* writer = refs.one;
@@ -3603,7 +3602,6 @@ - (void)testListenForChildChangedWithLimitEnsureEventsFireProperly {
36033602

36043603
WAIT_FOR(count == 3);
36053604
}
3606-
#endif
36073605

36083606
- (void)testListenForChildRemovedWithLimitEnsureEventsFireProperly {
36093607
FTupleFirebase* refs = [FTestHelpers getRandomNodePair];

0 commit comments

Comments
 (0)