import Dispatch
import Foundation

/// A `SerialExecutor` backed by a private serial `DispatchQueue`, for actors that must NOT run on
/// the Swift cooperative pool.
///
/// The reason such actors exist is `TranscriptWriter`'s: every one of its methods does blocking
/// file I/O, and the cooperative pool is width-limited (one thread per core, non-expandable), so a
/// few sessions writing to a slow volume parked enough pool threads to starve the whole
/// concurrency runtime — decode loops, watchdogs, everything. On a private queue a blocked write
/// costs one expendable GCD thread instead.
///
/// **Why this type rather than `DispatchSerialQueue` directly.** `DispatchSerialQueue` — the class
/// with the built-in `asUnownedSerialExecutor()` — is part of the *Darwin* Dispatch overlay only.
/// swift-corelibs-libdispatch doesn't vend it, so `DispatchSerialQueue(label:qos:)` is simply not
/// in scope on Windows (and on Linux). Writing the `SerialExecutor` conformance out by hand over a
/// plain `DispatchQueue` — which corelibs does vend — is the portable spelling of exactly the same
/// thing: a serial queue, so actor isolation and per-operation atomicity hold unchanged.
///
/// Used on every platform rather than behind an `#if`, so macOS's test suite exercises the same
/// code path Windows and Linux run.
public final class SerialQueueExecutor: SerialExecutor {
    private let queue: DispatchQueue

    public init(label: String, qos: DispatchQoS = .utility) {
        queue = DispatchQueue(label: label, qos: qos)
    }

    public func enqueue(_ job: consuming ExecutorJob) {
        // `ExecutorJob` is non-copyable and can't cross into an escaping closure; `UnownedJob` is
        // the transferable handle for exactly this hand-off, and running it is what hands the
        // actor's next partial task to our queue's thread.
        let unowned = UnownedJob(job)
        let executor = asUnownedSerialExecutor()
        queue.async { unowned.runSynchronously(on: executor) }
    }

    public func asUnownedSerialExecutor() -> UnownedSerialExecutor {
        UnownedSerialExecutor(ordinary: self)
    }
}
