Skip to content

Commit 051787b

Browse files
authored
Merge pull request groue#1196 from layoutSubviews/development
Fix a crash when an observation is quickly cancelled
2 parents 9014b3e + feebc07 commit 051787b

File tree

2 files changed

+17
-10
lines changed

2 files changed

+17
-10
lines changed

GRDB/ValueObservation/ValueConcurrentObserver.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,8 @@ extension ValueConcurrentObserver {
192192
(self.notificationCallbacks, self.databaseAccess)
193193
}
194194
guard let notificationCallbacks = notificationCallbacksOpt, let databaseAccess = databaseAccessOpt else {
195-
// Likely a GRDB bug
195+
// Likely a GRDB bug: during a synchronous start, user is not
196+
// able to cancel observation.
196197
fatalError("can't start a cancelled or failed observation")
197198
}
198199

GRDB/ValueObservation/ValueWriteOnlyObserver.swift

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,8 @@ extension ValueWriteOnlyObserver {
155155
(self.notificationCallbacks, self.databaseAccess)
156156
}
157157
guard let notificationCallbacks = notificationCallbacksOpt, let writer = databaseAccessOpt?.writer else {
158-
// Likely a GRDB bug
158+
// Likely a GRDB bug: during a synchronous start, user is not
159+
// able to cancel observation.
159160
fatalError("can't start a cancelled or failed observation")
160161
}
161162

@@ -198,7 +199,11 @@ extension ValueWriteOnlyObserver {
198199
// from a database access.
199200
try writer.unsafeReentrantWrite { db in
200201
// Fetch & Start observing the database
201-
let fetchedValue = try fetchAndStartObservation(db)
202+
guard let fetchedValue = try fetchAndStartObservation(db) else {
203+
// Likely a GRDB bug: during a synchronous start, user is not
204+
// able to cancel observation.
205+
fatalError("can't start a cancelled or failed observation")
206+
}
202207

203208
// Reduce
204209
return reduceQueue.sync {
@@ -218,12 +223,11 @@ extension ValueWriteOnlyObserver {
218223
// Start from a write access, so that self can register as a
219224
// transaction observer.
220225
writer.asyncWriteWithoutTransaction { db in
221-
let isNotifying = self.lock.synchronized { self.notificationCallbacks != nil }
222-
guard isNotifying else { return /* Cancelled */ }
223-
224226
do {
225227
// Fetch & Start observing the database
226-
let fetchedValue = try self.fetchAndStartObservation(db)
228+
guard let fetchedValue = try self.fetchAndStartObservation(db) else {
229+
return /* Cancelled */
230+
}
227231

228232
// Reduce
229233
//
@@ -257,17 +261,19 @@ extension ValueWriteOnlyObserver {
257261

258262
/// Fetches the initial value, and start observing the database.
259263
///
264+
/// Returns nil if the observation was cancelled before database observation
265+
/// could start.
266+
///
260267
/// By grouping the initial fetch and the beginning of observation in a
261268
/// single database access, we are sure that no concurrent write can happen
262269
/// during the initial fetch, and that we won't miss any future change.
263-
private func fetchAndStartObservation(_ db: Database) throws -> Reducer.Fetched {
270+
private func fetchAndStartObservation(_ db: Database) throws -> Reducer.Fetched? {
264271
// TODO: [SR-214] remove -Opt suffix when we only support Xcode 12.5.1+
265272
let (eventsOpt, fetchOpt) = lock.synchronized {
266273
(notificationCallbacks?.events, databaseAccess?.fetch)
267274
}
268275
guard let events = eventsOpt, let fetch = fetchOpt else {
269-
// Likely a GRDB bug
270-
fatalError("can't start a cancelled or failed observation")
276+
return nil /* Cancelled */
271277
}
272278

273279
switch trackingMode {

0 commit comments

Comments
 (0)