diff --git a/Sources/Verge/Library/BackgroundDeallocationQueue.swift b/Sources/Verge/Library/BackgroundDeallocationQueue.swift index 703858a9ed..8fb17c8a8a 100644 --- a/Sources/Verge/Library/BackgroundDeallocationQueue.swift +++ b/Sources/Verge/Library/BackgroundDeallocationQueue.swift @@ -20,43 +20,40 @@ // THE SOFTWARE. import Foundation +import Collections -final class BackgroundDeallocationQueue { +actor BackgroundDeallocationQueue { - private let queue = DispatchQueue.init(label: "org.VergeGroup.deallocQueue", qos: .background) - - private var buffer: ContiguousArray> = .init() - - private let lock = NSLock() + private var buffer: Deque> = .init() func releaseObjectInBackground(object: AnyObject) { let innerCurrentRef = Unmanaged.passRetained(object) - lock.lock() - let isFirstEntry = buffer.isEmpty buffer.append(innerCurrentRef) - lock.unlock() - if isFirstEntry { - queue.asyncAfter(deadline: .now() + 0.1) { - self.drain() + Task { + // accumulate objects to dealloc for batching + try? await Task.sleep(nanoseconds: 1_000_000) + await self.drain() } } } - func drain() { + func drain() async { - lock.lock() - let block = buffer - buffer = .init() - lock.unlock() + guard buffer.isEmpty == false else { + return + } - for pointer in block { + while let pointer = buffer.popFirst() { pointer.release() + await Task.yield() } + await drain() + } } diff --git a/Sources/Verge/Store/Changes.swift b/Sources/Verge/Store/Changes.swift index d141a6ce91..7203b1abc3 100644 --- a/Sources/Verge/Store/Changes.swift +++ b/Sources/Verge/Store/Changes.swift @@ -24,7 +24,7 @@ import Foundation #if !COCOAPODS #endif -private let changesDeallocationQueue = BackgroundDeallocationQueue() +private let _shared_changesDeallocationQueue = BackgroundDeallocationQueue() public protocol AnyChangesType: AnyObject, Sendable { @@ -169,7 +169,9 @@ public final class Changes: @unchecked Sendable, ChangesType, deinit { vergeSignpostEvent("Changes.deinit", label: "\(type(of: self))") - changesDeallocationQueue.releaseObjectInBackground(object: innerBox) + Task { [innerBox] in + await _shared_changesDeallocationQueue.releaseObjectInBackground(object: innerBox) + } } @inline(__always)