diff --git a/Sources/Blackbird/BlackbirdDatabase.swift b/Sources/Blackbird/BlackbirdDatabase.swift index 6b0ed3b..a1538a9 100644 --- a/Sources/Blackbird/BlackbirdDatabase.swift +++ b/Sources/Blackbird/BlackbirdDatabase.swift @@ -591,8 +591,13 @@ extension Blackbird { } } + private let asyncTransactionSemaphore = Blackbird.Semaphore(value: 1) + // Exactly like the function below, but accepts an async action public func cancellableTransaction(_ action: (@Sendable (_ core: isolated Blackbird.Database.Core) async throws -> R) ) async throws -> Blackbird.TransactionResult { + await asyncTransactionSemaphore.wait() + defer { asyncTransactionSemaphore.signal() } + if isClosed { throw Error.databaseIsClosed } let transactionID = nextTransactionID nextTransactionID += 1 diff --git a/Tests/BlackbirdTests/BlackbirdTests.swift b/Tests/BlackbirdTests/BlackbirdTests.swift index 8e7ef07..b1e2b51 100644 --- a/Tests/BlackbirdTests/BlackbirdTests.swift +++ b/Tests/BlackbirdTests/BlackbirdTests.swift @@ -1030,7 +1030,26 @@ final class BlackbirdTestTests: XCTestCase, @unchecked Sendable { XCTAssert(all[2].id == 3) XCTAssert(all[2].a == "a2") XCTAssert(all[2].b == 201) - + } + + // To test bug #25: https://github.com/marcoarment/Blackbird/issues/25 + func testConcurrentTransactions() async throws { + let db = try Blackbird.Database(path: sqliteFilename) + let semaphore = Blackbird.Semaphore(value: 0) + + let numTasks = 1000 + + for i in 0..