Skip to content

Instantly share code, notes, and snippets.

@thomsmed
Created February 12, 2024 16:48
Show Gist options
  • Save thomsmed/28164a6052fba8eb389ab25507011138 to your computer and use it in GitHub Desktop.
Save thomsmed/28164a6052fba8eb389ab25507011138 to your computer and use it in GitHub Desktop.
Snippet showing how to configure a simple custom CFRunLoopSource.
//
// StopRunLoopSource.swift
//
import Foundation
final class StopRunLoopSource: NSObject {
// More on how to define custom run loop input sources: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html#//apple_ref/doc/uid/10000057i-CH16-SW3
// And tips on how to terminate threads: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/CreatingThreads.html#//apple_ref/doc/uid/10000057i-CH15-SW10
static let stopRunLoopKey = "ios.example.StopRunLoopSource.stopRunLoop"
private let runLoopSource: CFRunLoopSource
private var invalidated = false
private weak var cfRunLoop: CFRunLoop?
override init() {
var context = CFRunLoopSourceContext(
version: 0,
info: nil,
retain: nil,
release: nil,
copyDescription: nil,
equal: nil,
hash: nil,
schedule: { _, runLoop, mode in
// Called when the run loop source is attached to a run loop.
},
cancel: { _, runLoop, mode in
// Called when the run loop source is invalidated.
},
perform: { _ in
// Called when the run loop source is signalled (and the run loop is woken up).
Thread.current.threadDictionary[StopRunLoopSource.stopRunLoopKey] = true
}
)
runLoopSource = CFRunLoopSourceCreate(nil, 0, &context)
super.init()
}
deinit {
CFRunLoopSourceInvalidate(runLoopSource)
}
func schedule(in runLoop: RunLoop) {
guard !invalidated else {
return
}
let cfRunLoop = runLoop.getCFRunLoop()
CFRunLoopAddSource(cfRunLoop, runLoopSource, CFRunLoopMode.defaultMode)
self.cfRunLoop = cfRunLoop
}
func invalidate() {
CFRunLoopSourceInvalidate(runLoopSource)
invalidated = true
}
func signal() {
guard !invalidated, let cfRunLoop = cfRunLoop else {
return
}
CFRunLoopSourceSignal(runLoopSource)
CFRunLoopWakeUp(cfRunLoop)
}
}
let stopRunLoopSource = StopRunLoopSource()
let thread = Thread {
stopRunLoopSource.schedule(in: .current)
// Add additional RunLoop input sources...
var stop: Bool?
repeat {
stop = Thread.current.threadDictionary[StopRunLoopSource.stopRunLoopKey] as? Bool
} while stop != true && RunLoop.current.run(mode: .default, before: .distantFuture)
stopRunLoopSource.invalidate()
// Clean/invalidate up additional RunLoop input sources...
}
thread.start()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment