apple-cf 0.9.0

Safe Rust bindings for Apple's shared Core* frameworks (CoreFoundation, CoreMedia, CoreVideo, CoreGraphics, IOSurface, Dispatch).
Documentation
import Foundation

@_cdecl("acf_dispatch_async_f")
public func acf_dispatch_async_f(
    _ queue: UnsafeMutableRawPointer,
    _ context: UnsafeMutableRawPointer?,
    _ work: (@convention(c) (UnsafeMutableRawPointer?) -> Void)?
) {
    guard let work else { return }
    let queue = Unmanaged<DispatchQueue>.fromOpaque(queue).takeUnretainedValue()
    queue.async {
        work(context)
    }
}

@_cdecl("acf_dispatch_async_and_wait_f")
public func acf_dispatch_async_and_wait_f(
    _ queue: UnsafeMutableRawPointer,
    _ context: UnsafeMutableRawPointer?,
    _ work: (@convention(c) (UnsafeMutableRawPointer?) -> Void)?
) {
    guard let work else { return }
    let queue = Unmanaged<DispatchQueue>.fromOpaque(queue).takeUnretainedValue()
    queue.asyncAndWait {
        work(context)
    }
}

@_cdecl("acf_dispatch_apply_f")
public func acf_dispatch_apply_f(
    _ iterations: Int,
    _ queue: UnsafeMutableRawPointer,
    _ context: UnsafeMutableRawPointer?,
    _ work: (@convention(c) (Int, UnsafeMutableRawPointer?) -> Void)?
) {
    guard let work, iterations > 0 else { return }
    let queue = Unmanaged<DispatchQueue>.fromOpaque(queue).takeUnretainedValue()
    let group = DispatchGroup()
    for iteration in 0..<iterations {
        group.enter()
        queue.async {
            work(iteration, context)
            group.leave()
        }
    }
    group.wait()
}

private func dispatchDeadline(timeoutMs: Int64) -> DispatchTime {
    if timeoutMs < 0 {
        return .distantFuture
    }
    return .now() + .milliseconds(Int(timeoutMs))
}

final class DispatchSourceTimerHolder {
    let source: DispatchSourceTimer
    private(set) var fireCount: UInt64 = 0

    init(intervalMs: UInt64, leewayMs: UInt64) {
        let queue = DispatchQueue(label: "com.doomfish.apple-cf.dispatch-source")
        source = DispatchSource.makeTimerSource(queue: queue)
        source.setEventHandler { [weak self] in
            self?.fireCount += 1
        }
        source.schedule(
            deadline: .now() + .milliseconds(Int(intervalMs)),
            repeating: .milliseconds(Int(intervalMs)),
            leeway: .milliseconds(Int(leewayMs))
        )
    }
}

@_cdecl("acf_dispatch_group_create")
public func acf_dispatch_group_create() -> UnsafeMutableRawPointer {
    return Unmanaged.passRetained(DispatchGroup()).toOpaque()
}

@_cdecl("acf_dispatch_group_enter")
public func acf_dispatch_group_enter(_ group: UnsafeMutableRawPointer) {
    let group = Unmanaged<DispatchGroup>.fromOpaque(group).takeUnretainedValue()
    group.enter()
}

@_cdecl("acf_dispatch_group_leave")
public func acf_dispatch_group_leave(_ group: UnsafeMutableRawPointer) {
    let group = Unmanaged<DispatchGroup>.fromOpaque(group).takeUnretainedValue()
    group.leave()
}

@_cdecl("acf_dispatch_group_wait")
public func acf_dispatch_group_wait(_ group: UnsafeMutableRawPointer, _ timeoutMs: Int64) -> Bool {
    let group = Unmanaged<DispatchGroup>.fromOpaque(group).takeUnretainedValue()
    return group.wait(timeout: dispatchDeadline(timeoutMs: timeoutMs)) == .success
}

@_cdecl("acf_dispatch_semaphore_create")
public func acf_dispatch_semaphore_create(_ value: Int64) -> UnsafeMutableRawPointer {
    return Unmanaged.passRetained(DispatchSemaphore(value: Int(value))).toOpaque()
}

@_cdecl("acf_dispatch_semaphore_signal")
public func acf_dispatch_semaphore_signal(_ semaphore: UnsafeMutableRawPointer) -> Int64 {
    let semaphore = Unmanaged<DispatchSemaphore>.fromOpaque(semaphore).takeUnretainedValue()
    return Int64(semaphore.signal())
}

@_cdecl("acf_dispatch_semaphore_wait")
public func acf_dispatch_semaphore_wait(_ semaphore: UnsafeMutableRawPointer, _ timeoutMs: Int64) -> Bool {
    let semaphore = Unmanaged<DispatchSemaphore>.fromOpaque(semaphore).takeUnretainedValue()
    return semaphore.wait(timeout: dispatchDeadline(timeoutMs: timeoutMs)) == .success
}

@_cdecl("acf_dispatch_source_timer_create")
public func acf_dispatch_source_timer_create(_ intervalMs: UInt64, _ leewayMs: UInt64) -> UnsafeMutableRawPointer {
    return Unmanaged.passRetained(DispatchSourceTimerHolder(intervalMs: intervalMs, leewayMs: leewayMs)).toOpaque()
}

@_cdecl("acf_dispatch_source_timer_resume")
public func acf_dispatch_source_timer_resume(_ source: UnsafeMutableRawPointer) {
    let source = Unmanaged<DispatchSourceTimerHolder>.fromOpaque(source).takeUnretainedValue()
    source.source.resume()
}

@_cdecl("acf_dispatch_source_timer_cancel")
public func acf_dispatch_source_timer_cancel(_ source: UnsafeMutableRawPointer) {
    let source = Unmanaged<DispatchSourceTimerHolder>.fromOpaque(source).takeUnretainedValue()
    source.source.cancel()
}

@_cdecl("acf_dispatch_source_timer_fire_count")
public func acf_dispatch_source_timer_fire_count(_ source: UnsafeMutableRawPointer) -> UInt64 {
    let source = Unmanaged<DispatchSourceTimerHolder>.fromOpaque(source).takeUnretainedValue()
    return source.fireCount
}