Skip to content

Commit 3ff76f0

Browse files
committed
Add SPI for sources and observers to RunLoop
These are for XCTest use only for now.
1 parent cfac32b commit 3ff76f0

File tree

1 file changed

+159
-6
lines changed

1 file changed

+159
-6
lines changed

Sources/Foundation/RunLoop.swift

+159-6
Original file line numberDiff line numberDiff line change
@@ -208,12 +208,165 @@ extension RunLoop {
208208
}
209209
}
210210

211-
// SPI for XCTest
212-
#if os(Windows)
211+
// These exist as SPI for XCTest for now. Do not rely on their contracts or continued existence.
212+
213213
extension RunLoop {
214-
public func _stop() {
215-
CFRunLoopStop(getCFRunLoop())
216-
}
214+
@available(*, deprecated, message: "For XCTest use only.")
215+
public func _stop() {
216+
CFRunLoopStop(getCFRunLoop())
217+
}
218+
219+
@available(*, deprecated, message: "For XCTest use only.")
220+
public func _observe(_ activities: _Activities, in mode: RunLoop.Mode = .default, repeats: Bool = true, order: Int = 0, handler: @escaping (_Activity) -> Void) -> _Observer {
221+
let observer = _Observer(activities: activities, repeats: repeats, order: order, handler: handler)
222+
CFRunLoopAddObserver(self.getCFRunLoop(), observer.cfObserver, mode._cfStringUniquingKnown)
223+
return observer
224+
}
225+
226+
@available(*, deprecated, message: "For XCTest use only.")
227+
public func _observe(_ activity: _Activity, in mode: RunLoop.Mode = .default, repeats: Bool = true, order: Int = 0, handler: @escaping (_Activity) -> Void) -> _Observer {
228+
return _observe(_Activities(activity), in: mode, repeats: repeats, order: order, handler: handler)
229+
}
230+
231+
@available(*, deprecated, message: "For XCTest use only.")
232+
func _add(_ source: _Source, forMode mode: RunLoop.Mode) {
233+
CFRunLoopAddSource(_cfRunLoop, source.cfSource, mode._cfStringUniquingKnown)
234+
}
217235
}
218-
#endif
219236

237+
extension RunLoop {
238+
@available(*, deprecated, message: "For XCTest use only.")
239+
public enum _Activity: UInt {
240+
// These must match CFRunLoopActivity.
241+
case entry = 0
242+
case beforeTimers = 1
243+
case beforeSources = 2
244+
case beforeWaiting = 32
245+
case afterWaiting = 64
246+
case exit = 128
247+
}
248+
249+
@available(*, deprecated, message: "For XCTest use only.")
250+
public struct _Activities: OptionSet {
251+
public var rawValue: UInt
252+
public init(rawValue: UInt) {
253+
self.rawValue = rawValue
254+
}
255+
256+
public init(_ activity: _Activity) {
257+
self.rawValue = activity.rawValue
258+
}
259+
260+
public static let entry = _Activities(rawValue: _Activity.entry.rawValue)
261+
public static let beforeTimers = _Activities(rawValue: _Activity.beforeTimers.rawValue)
262+
public static let beforeSources = _Activities(rawValue: _Activity.beforeSources.rawValue)
263+
public static let beforeWaiting = _Activities(rawValue: _Activity.beforeWaiting.rawValue)
264+
public static let afterWaiting = _Activities(rawValue: _Activity.afterWaiting.rawValue)
265+
public static let exit = _Activities(rawValue: _Activity.exit.rawValue)
266+
public static let allActivities = _Activities(rawValue: 0x0FFFFFFF)
267+
}
268+
269+
@available(*, deprecated, message: "For XCTest use only.")
270+
public class _Observer {
271+
fileprivate let cfObserver: CFRunLoopObserver
272+
273+
fileprivate init(activities: _Activities, repeats: Bool, order: Int, handler: @escaping (_Activity) -> Void) {
274+
self.cfObserver = CFRunLoopObserverCreateWithHandler(kCFAllocatorSystemDefault, CFOptionFlags(activities.rawValue), repeats, CFIndex(order), { (cfObserver, cfActivity) in
275+
guard let activity = _Activity(rawValue: UInt(cfActivity.rawValue)) else { return }
276+
handler(activity)
277+
})
278+
}
279+
280+
public func invalidate() {
281+
CFRunLoopObserverInvalidate(cfObserver)
282+
}
283+
284+
var order: Int {
285+
Int(CFRunLoopObserverGetOrder(cfObserver))
286+
}
287+
288+
var isValid: Bool {
289+
CFRunLoopObserverIsValid(cfObserver)
290+
}
291+
}
292+
293+
@available(*, deprecated, message: "For XCTest use only.")
294+
open class _Source: NSObject {
295+
fileprivate var cfSource: CFRunLoopSource!
296+
297+
public init(order: Int = 0) {
298+
super.init()
299+
300+
var context = CFRunLoopSourceContext(
301+
version: 0,
302+
info: Unmanaged.passUnretained(self).toOpaque(),
303+
retain: nil,
304+
release: nil,
305+
copyDescription: { (info) -> Unmanaged<CFString>? in
306+
let me = Unmanaged<_Source>.fromOpaque(info!).takeUnretainedValue()
307+
return .passRetained(String(describing: me)._cfObject)
308+
},
309+
equal: { (infoA, infoB) -> DarwinBoolean in
310+
let a = Unmanaged<_Source>.fromOpaque(infoA!).takeUnretainedValue()
311+
let b = Unmanaged<_Source>.fromOpaque(infoB!).takeUnretainedValue()
312+
return a == b ? true : false
313+
},
314+
hash: { (info) -> CFHashCode in
315+
let me = Unmanaged<_Source>.fromOpaque(info!).takeUnretainedValue()
316+
return CFHashCode(me.hashValue)
317+
},
318+
schedule: { (info, cfRunLoop, cfRunLoopMode) in
319+
let me = Unmanaged<_Source>.fromOpaque(info!).takeUnretainedValue()
320+
var mode: RunLoop.Mode = .default
321+
if let cfRunLoopMode = cfRunLoopMode {
322+
mode = RunLoop.Mode(rawValue: cfRunLoopMode._swiftObject)
323+
}
324+
325+
me.didSchedule(in: mode)
326+
},
327+
cancel: { (info, cfRunLoop, cfRunLoopMode) in
328+
let me = Unmanaged<_Source>.fromOpaque(info!).takeUnretainedValue()
329+
var mode: RunLoop.Mode = .default
330+
if let cfRunLoopMode = cfRunLoopMode {
331+
mode = RunLoop.Mode(rawValue: cfRunLoopMode._swiftObject)
332+
}
333+
334+
me.didCancel(in: mode)
335+
},
336+
perform: { (info) in
337+
let me = Unmanaged<_Source>.fromOpaque(info!).takeUnretainedValue()
338+
me.perform()
339+
})
340+
341+
self.cfSource = CFRunLoopSourceCreate(kCFAllocatorSystemDefault, CFIndex(order), &context)
342+
}
343+
344+
open func didSchedule(in mode: RunLoop.Mode) {
345+
// Override me.
346+
}
347+
348+
open func didCancel(in mode: RunLoop.Mode) {
349+
// Override me.
350+
}
351+
352+
open func perform() {
353+
// Override me.
354+
}
355+
356+
open func invalidate() {
357+
CFRunLoopSourceInvalidate(cfSource)
358+
}
359+
360+
var order: Int {
361+
Int(CFRunLoopSourceGetOrder(cfSource))
362+
}
363+
364+
var isValid: Bool {
365+
CFRunLoopSourceIsValid(cfSource)
366+
}
367+
368+
open func signal() {
369+
CFRunLoopSourceSignal(cfSource)
370+
}
371+
}
372+
}

0 commit comments

Comments
 (0)