// // Copyright 2023 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only // public import GRDB public import LibSignalClient @objc public class DBReadTransaction: NSObject { public let database: Database public let startDate: MonotonicDate init(database: Database) { self.database = database self.startDate = MonotonicDate() } } @objc public class DBWriteTransaction: DBReadTransaction, LibSignalClient.StoreContext { private enum TransactionState { case open case finalizing case finalized } typealias FinalizationBlock = (DBWriteTransaction) -> Void typealias CompletionBlock = () -> Void private var transactionState: TransactionState private var finalizationBlocks: [String: FinalizationBlock] private(set) var completionBlocks: [CompletionBlock] override init(database: Database) { self.transactionState = .open self.finalizationBlocks = [:] self.completionBlocks = [] super.init(database: database) } deinit { owsAssertDebug( transactionState == .finalized, "Write transaction deallocated without finalization!", ) } // MARK: - func finalizeTransaction() { guard transactionState == .open else { owsFailDebug("Write transaction finalized multiple times!") return } transactionState = .finalizing for (_, finalizationBlock) in finalizationBlocks { finalizationBlock(self) } finalizationBlocks.removeAll() transactionState = .finalized } // MARK: - /// Schedule the given block to run when this transaction is finalized. /// /// - Important /// `block` must not capture any database models, as they may no longer be /// valid by time the transaction finalizes. func addFinalizationBlock(key: String, block: @escaping FinalizationBlock) { finalizationBlocks[key] = block } /// Run the given block synchronously after the transaction is finalized. public func addSyncCompletion(block: @escaping () -> Void) { completionBlocks.append(block) } } // MARK: - public extension LibSignalClient.StoreContext { var asTransaction: DBWriteTransaction { return self as! DBWriteTransaction } }