Skip to content

Commit bbd1eab

Browse files
authored
Merge pull request groue#1732 from groue/dev/sending-closures
Async database accesses no longer require Sendable closures.
2 parents 9b09c53 + 53c9d12 commit bbd1eab

File tree

9 files changed

+106
-62
lines changed

9 files changed

+106
-62
lines changed

GRDB/Core/DatabasePool.swift

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -352,13 +352,17 @@ extension DatabasePool: DatabaseReader {
352352
}
353353

354354
public func read<T: Sendable>(
355-
_ value: @escaping @Sendable (Database) throws -> T
355+
_ value: sending @escaping (Database) throws -> T
356356
) async throws -> T {
357357
GRDBPrecondition(currentReader == nil, "Database methods are not reentrant.")
358358
guard let readerPool else {
359359
throw DatabaseError.connectionIsClosed()
360360
}
361361

362+
// Avoid compiler warning. There is no data race because `value` is invoked once.
363+
typealias SendableClosure = @Sendable (Database) throws -> T
364+
let value = unsafeBitCast(value, to: SendableClosure.self)
365+
362366
let dbAccess = CancellableDatabaseAccess()
363367
return try await dbAccess.withCancellableContinuation { continuation in
364368
readerPool.asyncGet { result in
@@ -390,13 +394,17 @@ extension DatabasePool: DatabaseReader {
390394
}
391395

392396
public func asyncRead(
393-
_ value: @escaping @Sendable (Result<Database, Error>) -> Void
397+
_ value: sending @escaping (Result<Database, Error>) -> Void
394398
) {
395399
guard let readerPool else {
396400
value(.failure(DatabaseError.connectionIsClosed()))
397401
return
398402
}
399403

404+
// Avoid compiler warning. There is no data race because `value` is invoked once.
405+
typealias SendableClosure = @Sendable (Result<Database, Error>) -> Void
406+
let value = unsafeBitCast(value, to: SendableClosure.self)
407+
400408
readerPool.asyncGet { result in
401409
do {
402410
let (reader, releaseReader) = try result.get()
@@ -436,12 +444,16 @@ extension DatabasePool: DatabaseReader {
436444
}
437445

438446
public func unsafeRead<T: Sendable>(
439-
_ value: @escaping @Sendable (Database) throws -> T
447+
_ value: sending @escaping (Database) throws -> T
440448
) async throws -> T {
441449
guard let readerPool else {
442450
throw DatabaseError.connectionIsClosed()
443451
}
444452

453+
// Avoid compiler warning. There is no data race because `value` is invoked once.
454+
typealias SendableClosure = @Sendable (Database) throws -> T
455+
let value = unsafeBitCast(value, to: SendableClosure.self)
456+
445457
let dbAccess = CancellableDatabaseAccess()
446458
return try await dbAccess.withCancellableContinuation { continuation in
447459
readerPool.asyncGet { result in
@@ -470,13 +482,17 @@ extension DatabasePool: DatabaseReader {
470482
}
471483

472484
public func asyncUnsafeRead(
473-
_ value: @escaping @Sendable (Result<Database, Error>) -> Void
485+
_ value: sending @escaping (Result<Database, Error>) -> Void
474486
) {
475487
guard let readerPool else {
476488
value(.failure(DatabaseError.connectionIsClosed()))
477489
return
478490
}
479491

492+
// Avoid compiler warning. There is no data race because `value` is invoked once.
493+
typealias SendableClosure = @Sendable (Result<Database, Error>) -> Void
494+
let value = unsafeBitCast(value, to: SendableClosure.self)
495+
480496
readerPool.asyncGet { result in
481497
do {
482498
let (reader, releaseReader) = try result.get()
@@ -517,7 +533,7 @@ extension DatabasePool: DatabaseReader {
517533
}
518534

519535
public func spawnConcurrentRead(
520-
_ value: @escaping @Sendable (Result<Database, Error>) -> Void
536+
_ value: sending @escaping (Result<Database, Error>) -> Void
521537
) {
522538
asyncConcurrentRead(value)
523539
}
@@ -560,7 +576,7 @@ extension DatabasePool: DatabaseReader {
560576
///
561577
/// - parameter value: A function that accesses the database.
562578
public func asyncConcurrentRead(
563-
_ value: @escaping @Sendable (Result<Database, Error>) -> Void
579+
_ value: sending @escaping (Result<Database, Error>) -> Void
564580
) {
565581
// Check that we're on the writer queue...
566582
writer.execute { db in
@@ -802,7 +818,7 @@ extension DatabasePool: DatabaseWriter {
802818
}
803819

804820
public func writeWithoutTransaction<T: Sendable>(
805-
_ updates: @escaping @Sendable (Database) throws -> T
821+
_ updates: sending @escaping (Database) throws -> T
806822
) async throws -> T {
807823
try await writer.execute(updates)
808824
}
@@ -818,8 +834,12 @@ extension DatabasePool: DatabaseWriter {
818834
}
819835

820836
public func barrierWriteWithoutTransaction<T: Sendable>(
821-
_ updates: @escaping @Sendable (Database) throws -> T
837+
_ updates: sending @escaping (Database) throws -> T
822838
) async throws -> T {
839+
// Avoid compiler warning. There is no data race because `updates` is invoked once.
840+
typealias SendableClosure = @Sendable (Database) throws -> T
841+
let updates = unsafeBitCast(updates, to: SendableClosure.self)
842+
823843
let dbAccess = CancellableDatabaseAccess()
824844
return try await dbAccess.withCancellableContinuation { continuation in
825845
asyncBarrierWriteWithoutTransaction { dbResult in
@@ -838,7 +858,7 @@ extension DatabasePool: DatabaseWriter {
838858
}
839859

840860
public func asyncBarrierWriteWithoutTransaction(
841-
_ updates: @escaping @Sendable (Result<Database, Error>) -> Void
861+
_ updates: sending @escaping (Result<Database, Error>) -> Void
842862
) {
843863
guard let readerPool else {
844864
updates(.failure(DatabaseError.connectionIsClosed()))
@@ -894,7 +914,7 @@ extension DatabasePool: DatabaseWriter {
894914
}
895915

896916
public func asyncWriteWithoutTransaction(
897-
_ updates: @escaping @Sendable (Database) -> Void
917+
_ updates: sending @escaping (Database) -> Void
898918
) {
899919
writer.async(updates)
900920
}

GRDB/Core/DatabaseQueue.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ extension DatabaseQueue: DatabaseReader {
234234
}
235235

236236
public func read<T: Sendable>(
237-
_ value: @escaping @Sendable (Database) throws -> T
237+
_ value: sending @escaping (Database) throws -> T
238238
) async throws -> T {
239239
try await writer.execute { db in
240240
try db.isolated(readOnly: true) {
@@ -244,7 +244,7 @@ extension DatabaseQueue: DatabaseReader {
244244
}
245245

246246
public func asyncRead(
247-
_ value: @escaping @Sendable (Result<Database, Error>) -> Void
247+
_ value: sending @escaping (Result<Database, Error>) -> Void
248248
) {
249249
writer.async { db in
250250
defer {
@@ -272,13 +272,13 @@ extension DatabaseQueue: DatabaseReader {
272272
}
273273

274274
public func unsafeRead<T: Sendable>(
275-
_ value: @escaping @Sendable (Database) throws -> T
275+
_ value: sending @escaping (Database) throws -> T
276276
) async throws -> T {
277277
try await writer.execute(value)
278278
}
279279

280280
public func asyncUnsafeRead(
281-
_ value: @escaping @Sendable (Result<Database, Error>) -> Void
281+
_ value: sending @escaping (Result<Database, Error>) -> Void
282282
) {
283283
writer.async { value(.success($0)) }
284284
}
@@ -288,7 +288,7 @@ extension DatabaseQueue: DatabaseReader {
288288
}
289289

290290
public func spawnConcurrentRead(
291-
_ value: @escaping @Sendable (Result<Database, Error>) -> Void
291+
_ value: sending @escaping (Result<Database, Error>) -> Void
292292
) {
293293
// Check that we're on the writer queue...
294294
writer.execute { db in
@@ -385,7 +385,7 @@ extension DatabaseQueue: DatabaseWriter {
385385
}
386386

387387
public func writeWithoutTransaction<T: Sendable>(
388-
_ updates: @escaping @Sendable (Database) throws -> T
388+
_ updates: sending @escaping (Database) throws -> T
389389
) async throws -> T {
390390
try await writer.execute(updates)
391391
}
@@ -396,13 +396,13 @@ extension DatabaseQueue: DatabaseWriter {
396396
}
397397

398398
public func barrierWriteWithoutTransaction<T: Sendable>(
399-
_ updates: @escaping @Sendable (Database) throws -> T
399+
_ updates: sending @escaping (Database) throws -> T
400400
) async throws -> T {
401401
try await writer.execute(updates)
402402
}
403403

404404
public func asyncBarrierWriteWithoutTransaction(
405-
_ updates: @escaping @Sendable (Result<Database, Error>) -> Void
405+
_ updates: sending @escaping (Result<Database, Error>) -> Void
406406
) {
407407
writer.async { updates(.success($0)) }
408408
}
@@ -450,7 +450,7 @@ extension DatabaseQueue: DatabaseWriter {
450450
}
451451

452452
public func asyncWriteWithoutTransaction(
453-
_ updates: @escaping @Sendable (Database) -> Void
453+
_ updates: sending @escaping (Database) -> Void
454454
) {
455455
writer.async(updates)
456456
}

GRDB/Core/DatabaseReader.swift

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@ import Dispatch
2222
/// ### Reading from the Database
2323
///
2424
/// - ``read(_:)-3806d``
25-
/// - ``read(_:)-4d1da``
25+
/// - ``read(_:)-4zff3``
2626
/// - ``readPublisher(receiveOn:value:)``
2727
/// - ``asyncRead(_:)``
2828
///
2929
/// ### Unsafe Methods
3030
///
3131
/// - ``unsafeRead(_:)-5i7tf``
32-
/// - ``unsafeRead(_:)-5gsav``
32+
/// - ``unsafeRead(_:)-5b76n``
3333
/// - ``unsafeReentrantRead(_:)``
3434
/// - ``asyncUnsafeRead(_:)``
3535
///
@@ -215,7 +215,7 @@ public protocol DatabaseReader: AnyObject, Sendable {
215215
/// database access, or the error thrown by `value`, or
216216
/// `CancellationError` if the task is cancelled.
217217
func read<T: Sendable>(
218-
_ value: @escaping @Sendable (Database) throws -> T
218+
_ value: sending @escaping (Database) throws -> T
219219
) async throws -> T
220220

221221
/// Schedules read-only database operations for execution, and
@@ -245,7 +245,7 @@ public protocol DatabaseReader: AnyObject, Sendable {
245245
/// is a `Result` that provides the database connection, or the failure
246246
/// that would prevent establishing the read access to the database.
247247
func asyncRead(
248-
_ value: @escaping @Sendable (Result<Database, Error>) -> Void
248+
_ value: sending @escaping (Result<Database, Error>) -> Void
249249
)
250250

251251
/// Executes database operations, and returns their result after they have
@@ -319,7 +319,7 @@ public protocol DatabaseReader: AnyObject, Sendable {
319319
/// database access, or the error thrown by `value`, or
320320
/// `CancellationError` if the task is cancelled.
321321
func unsafeRead<T: Sendable>(
322-
_ value: @escaping @Sendable (Database) throws -> T
322+
_ value: sending @escaping (Database) throws -> T
323323
) async throws -> T
324324

325325
/// Schedules database operations for execution, and returns immediately.
@@ -354,7 +354,7 @@ public protocol DatabaseReader: AnyObject, Sendable {
354354
/// is a `Result` that provides the database connection, or the failure
355355
/// that would prevent establishing the read access to the database.
356356
func asyncUnsafeRead(
357-
_ value: @escaping @Sendable (Result<Database, Error>) -> Void
357+
_ value: sending @escaping (Result<Database, Error>) -> Void
358358
)
359359

360360
/// Executes database operations, and returns their result after they have
@@ -652,13 +652,13 @@ extension AnyDatabaseReader: DatabaseReader {
652652
}
653653

654654
public func read<T: Sendable>(
655-
_ value: @escaping @Sendable (Database) throws -> T
655+
_ value: sending @escaping (Database) throws -> T
656656
) async throws -> T {
657657
try await base.read(value)
658658
}
659659

660660
public func asyncRead(
661-
_ value: @escaping @Sendable (Result<Database, Error>) -> Void
661+
_ value: sending @escaping (Result<Database, Error>) -> Void
662662
) {
663663
base.asyncRead(value)
664664
}
@@ -669,13 +669,13 @@ extension AnyDatabaseReader: DatabaseReader {
669669
}
670670

671671
public func unsafeRead<T: Sendable>(
672-
_ value: @escaping @Sendable (Database) throws -> T
672+
_ value: sending @escaping (Database) throws -> T
673673
) async throws -> T {
674674
try await base.unsafeRead(value)
675675
}
676676

677677
public func asyncUnsafeRead(
678-
_ value: @escaping @Sendable (Result<Database, Error>) -> Void
678+
_ value: sending @escaping (Result<Database, Error>) -> Void
679679
) {
680680
base.asyncUnsafeRead(value)
681681
}
@@ -748,7 +748,7 @@ extension DatabaseSnapshotReader {
748748

749749
// There is no such thing as an unsafe access to a snapshot.
750750
public func asyncUnsafeRead(
751-
_ value: @escaping @Sendable (Result<Database, Error>) -> Void
751+
_ value: sending @escaping (Result<Database, Error>) -> Void
752752
) {
753753
asyncRead(value)
754754
}

GRDB/Core/DatabaseSnapshot.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -152,13 +152,13 @@ extension DatabaseSnapshot: DatabaseSnapshotReader {
152152
}
153153

154154
public func read<T: Sendable>(
155-
_ value: @escaping @Sendable (Database) throws -> T
155+
_ value: sending @escaping (Database) throws -> T
156156
) async throws -> T {
157157
try await reader.execute(value)
158158
}
159159

160160
public func asyncRead(
161-
_ value: @escaping @Sendable (Result<Database, Error>) -> Void
161+
_ value: sending @escaping (Result<Database, Error>) -> Void
162162
) {
163163
reader.async { value(.success($0)) }
164164
}
@@ -173,13 +173,13 @@ extension DatabaseSnapshot: DatabaseSnapshotReader {
173173
// `DatabaseSnapshotReader`, because of
174174
// <https://github.com/apple/swift/issues/74469>.
175175
public func unsafeRead<T: Sendable>(
176-
_ value: @escaping @Sendable (Database) throws -> T
176+
_ value: sending @escaping (Database) throws -> T
177177
) async throws -> T {
178178
try await read(value)
179179
}
180180

181181
public func asyncUnsafeRead(
182-
_ value: @escaping @Sendable (Result<Database, Error>) -> Void
182+
_ value: sending @escaping (Result<Database, Error>) -> Void
183183
) {
184184
reader.async { value(.success($0)) }
185185
}

GRDB/Core/DatabaseSnapshotPool.swift

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -294,12 +294,16 @@ extension DatabaseSnapshotPool: DatabaseSnapshotReader {
294294
}
295295

296296
public func read<T: Sendable>(
297-
_ value: @escaping @Sendable (Database) throws -> T
297+
_ value: sending @escaping (Database) throws -> T
298298
) async throws -> T {
299299
guard let readerPool else {
300300
throw DatabaseError.connectionIsClosed()
301301
}
302302

303+
// Avoid compiler warning. There is no data race because `value` is invoked once.
304+
typealias SendableClosure = @Sendable (Database) throws -> T
305+
let value = unsafeBitCast(value, to: SendableClosure.self)
306+
303307
let dbAccess = CancellableDatabaseAccess()
304308
return try await dbAccess.withCancellableContinuation { continuation in
305309
readerPool.asyncGet { result in
@@ -327,13 +331,17 @@ extension DatabaseSnapshotPool: DatabaseSnapshotReader {
327331
}
328332

329333
public func asyncRead(
330-
_ value: @escaping @Sendable (Result<Database, Error>) -> Void
334+
_ value: sending @escaping (Result<Database, Error>) -> Void
331335
) {
332336
guard let readerPool else {
333337
value(.failure(DatabaseError.connectionIsClosed()))
334338
return
335339
}
336340

341+
// Avoid compiler warning. There is no data race because `value` is invoked once.
342+
typealias SendableClosure = @Sendable (Result<Database, Error>) -> Void
343+
let value = unsafeBitCast(value, to: SendableClosure.self)
344+
337345
readerPool.asyncGet { result in
338346
do {
339347
let (reader, releaseReader) = try result.get()
@@ -353,7 +361,7 @@ extension DatabaseSnapshotPool: DatabaseSnapshotReader {
353361
// `DatabaseSnapshotReader`, because of
354362
// <https://github.com/apple/swift/issues/74469>.
355363
public func unsafeRead<T: Sendable>(
356-
_ value: @escaping @Sendable (Database) throws -> T
364+
_ value: sending @escaping (Database) throws -> T
357365
) async throws -> T {
358366
try await read(value)
359367
}

0 commit comments

Comments
 (0)