and sequential write policy actor AccessScheduler { private enum OperationType { case read, write } private struct Operation { let id: UUID let continuation: CheckedContinuation<Void, Never> let type: OperationType } let container: ModelContainer private var executingOperations: [Operation] = [] private var pendingOperations: [Operation] = [] // TODO: use queue init(container: ModelContainer) { self.container = container } /// Executes the given block sequentially as a writing transaction. /// - Parameter block: the writing transaction /// - Returns: the value which the given transaction block returns @discardableResult func executeWrite<T>(block: @Sendable (ModelContext) throws -> T) async rethrows -> T where T: Sendable { try await _execute(type: .write) { try block(ModelContext(self.container)) } } /// Executes the given block concurrently as a reading transaction. /// > Warning: Do not perform any operations which modify the persisted data. /// - Parameter block: the reading transaction /// - Returns: the value which the given transaction block returns func executeRead<T>(block: @Sendable @escaping (ModelContext) -> T) async -> T where T: Sendable { await _execute(type: .read) { await Task.detached { block(ModelContext(self.container)) }.value } } /// Executes the given block concurrently as a reading transaction. /// > Warning: Do not perform any operations which modify the persisted data. /// - Parameter block: the reading transaction /// - Returns: the value which the given transaction block returns func executeRead<T>(block: @Sendable @escaping (ModelContext) throws-> T) async throws -> T where T: Sendable { try await _execute(type: .read) { try await Task.detached { try block(ModelContext(self.container)) }.value } } private func _execute<T>( type: OperationType, block: () async throws-> T ) async rethrows -> T where T: Sendable { let id = UUID() await withCheckedContinuation { pendingOperations.append(Operation(id: id, continuation: $0, type: type)) popPendingOperations() } let result = try await block() executingOperations.removeAll { $0.id == id } popPendingOperations() return result } private func popPendingOperations() { guard executingOperations.isEmpty else { return } while !pendingOperations.isEmpty { let pendingOperation = pendingOperations.removeFirst() executingOperations.append(pendingOperation) pendingOperation.continuation.resume() guard pendingOperation.type == .read else { // Allow only one execution break } } } } IUUQTHJTUHJUIVCDPNUFBNIJNFI EFGGCDCFCD