Hi,
I am resurrecting a camera project, and need to update the OSAtomicTestAndSet method I was using in Swift 2/iOS9. Snipped code below. The instance method continuously recieves new video frames, and I want to 'do stuff' to one frame at any given time. If a frame is being processed I want to ignore any subsequently queued frames. Once a frame is processed I want to pull in the next available frame after that time point (ie not the next one in the frame queue). This is why I am setting 'self.processingFrame=0' on the video frame capture queue, so I will keep ignoring any frames queued before 'now'.
Currently I only get deprecation warnings for Swift3, that I should update to atomic_fetch_or_explicit(). Is there a more Swift-like way to implement this, or should I persist with C-like atomic thinking? Both in terms of better code, and future-proofing (Swift 4/iOS 11+).
class ViewController: UIViewController, AVCaptureVideoDataOutputSampleBufferDelegate, AVCapturePhotoCaptureDelegate {
fileprivate var processingFrame = 0
// instance method of AVCaptureVideoDataOutputSampleBufferDelegate, receives frames from video session
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) {
// Only want to process one frame at a time
if(OSAtomicTestAndSet(0, &processingFrame) == false) {
// do stuff to this frame
// execute this via the video frame queue so we don't process any frames delivered before
// this frame was finished processing
self.captureSessionFrameQueue.async(execute: {
self.processingFrame = 0
})
}
}
}cheers,
Is there a more Swift-like way to implement this, or should I persist with C-like atomic thinking?
The problem here is that Swift doesn’t have a concurrency-aware memory model. Without that, it’s very hard to offer any concrete guarantees about what will or won’t work in the long term.
This situation is unlikely to change in the next year or so. The Swift folks just kicked off the Swift 5 effort and concurrency is not on that particular radar.
Until that situation is resolved my recommendation is that you avoid doing ‘clever’ things with concurrency. And in this context ‘clever’ includes anything to do with lock-free data structures.
Note In my experience folks reach for lock-free data structures way too much. While they do have their place, it’s rare that they offer significant benefits that you can’t get otherwise.
Now, in your specific case an atomic test and set is the simplest lock-free data structure, so it’s hard to argue against it. It still makes me kinda nervous though. In your shoes I’d probably do one of two things:
Build an abstraction around
in a language that does have a memory model (that is, something C based) and then call that from Swift.processingFrameSwitch to a locked data structure.
With regards the last point, it’s easy to implement a test-and-set operation using a dispatch semaphore. Alternatively, you could add a serial queue to your view controller and use it to guard
processingFrame:
class ViewController … {
var processingFrame = false
var serialQueue = …
func captureOutput(…) {
self.serialQueue.async {
if !self.processingFrame {
self.processingFrame = true
self.captureSessionFrameQueue.async {
… do the work …
self.serialQueue.async {
self.processingFrame = false
}
}
}
}
}
}The amount of time you spend running on
serialQueue is so short that there’s a very low chance of any contention.
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"