Should OperationQueue be Sendable?

In Xcode 14, the warning in the cancellation handler says that OperationQueue is not Sendable.

    func requestPermissionIfNeeded() async -> Bool {
        let queue = OperationQueue()
        
        return await withTaskCancellationHandler {
            queue.cancelAllOperations() // WARNING: Capture of 'queue' with non-sendable type 'OperationQueue' in a `@Sendable` closure
        } operation: {
            return await withCheckedContinuation { continuation in
                guard !Task.isCancelled else { continuation.resume(returning: false) ; return }
                let op = LocationPermissionOperation ...
                queue.addOperation(op)
            }
        }
    }

The OperationQueue generated interface has the following:

NS_HEADER_AUDIT_BEGIN(nullability, sendability)

which I assume means they have been checked and, without an explicit Sendable conformance added, defaults to non-sendable.

The documents state:

You can safely use a single OperationQueue object from multiple threads without creating additional locks to synchronize access to that object.

To silence the warning I could do this:

extension OperationQueue: @unchecked Sendable { }

or create a wrapper around OperationQueue (which seems redundant if the docs are to be trusted):

class SendableOperationQueue: @unchecked Sendable {
    
    private let operationQueue = OperationQueue()
    private let isolationQueue = DispatchQueue(label: "") // Or a lock
    
    func cancelAllOperations() {
        isolationQueue.sync {
            self.operationQueue.cancelAllOperations()
        }
    }
    
    func addOperation(_ op: Operation) {
        isolationQueue.sync {
            self.operationQueue.addOperation(op)
        }
    }
}

Any suggestions about what to do here? Thanks!

3 Likes