Coverage Report

Created: 2026-10-28 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/swift-nio/Sources/NIOEmbedded/Embedded.swift
Line
Count
Source
1
//===----------------------------------------------------------------------===//
2
//
3
// This source file is part of the SwiftNIO open source project
4
//
5
// Copyright (c) 2017-2022 Apple Inc. and the SwiftNIO project authors
6
// Licensed under Apache License v2.0
7
//
8
// See LICENSE.txt for license information
9
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
10
//
11
// SPDX-License-Identifier: Apache-2.0
12
//
13
//===----------------------------------------------------------------------===//
14
15
import Atomics
16
import DequeModule
17
import NIOConcurrencyHelpers
18
import NIOCore
19
import _NIODataStructures
20
21
#if canImport(Dispatch)
22
import Dispatch
23
#endif
24
25
#if canImport(Darwin)
26
import Darwin
27
#elseif canImport(Glibc)
28
@preconcurrency import Glibc
29
#elseif canImport(Musl)
30
@preconcurrency import Musl
31
#elseif canImport(Android)
32
@preconcurrency import Android
33
#elseif canImport(WASILibc)
34
@preconcurrency import WASILibc
35
#elseif canImport(WinSDK)
36
@preconcurrency import WinSDK
37
#else
38
#error("Unknown C library.")
39
#endif
40
41
0
private func printError(_ string: StaticString) {
42
0
    string.withUTF8Buffer { buf in
43
0
        var buf = buf
44
0
        while buf.count > 0 {
45
0
            // 2 is stderr
46
            #if canImport(WinSDK)
47
            let rc = _write(2, buf.baseAddress, UInt32(truncatingIfNeeded: buf.count))
48
            let errno = GetLastError()
49
            #else
50
0
            let rc = write(2, buf.baseAddress, buf.count)
51
            #endif
52
0
            if rc < 0 {
53
0
                let err = errno
54
0
                if err == EINTR { continue }
55
0
                fatalError("Unexpected error writing: \(err)")
56
0
            }
57
0
            buf = .init(rebasing: buf.dropFirst(Int(rc)))
58
0
        }
59
0
    }
60
0
}
61
62
internal struct EmbeddedScheduledTask {
63
    let id: UInt64
64
    let task: () -> Void
65
    let failFn: (Error) -> Void
66
    let readyTime: NIODeadline
67
    let insertOrder: UInt64
68
69
    init(
70
        id: UInt64,
71
        readyTime: NIODeadline,
72
        insertOrder: UInt64,
73
        task: @escaping () -> Void,
74
        _ failFn: @escaping (Error) -> Void
75
6.96k
    ) {
76
6.96k
        self.id = id
77
6.96k
        self.readyTime = readyTime
78
6.96k
        self.insertOrder = insertOrder
79
6.96k
        self.task = task
80
6.96k
        self.failFn = failFn
81
6.96k
    }
82
83
0
    func fail(_ error: Error) {
84
0
        self.failFn(error)
85
0
    }
86
}
87
88
extension EmbeddedScheduledTask: Comparable {
89
0
    static func < (lhs: EmbeddedScheduledTask, rhs: EmbeddedScheduledTask) -> Bool {
90
0
        if lhs.readyTime == rhs.readyTime {
91
0
            return lhs.insertOrder < rhs.insertOrder
92
0
        } else {
93
0
            return lhs.readyTime < rhs.readyTime
94
0
        }
95
0
    }
96
97
0
    static func == (lhs: EmbeddedScheduledTask, rhs: EmbeddedScheduledTask) -> Bool {
98
0
        lhs.id == rhs.id
99
0
    }
100
}
101
102
/// An `EventLoop` that is embedded in the current running context with no external
103
/// control.
104
///
105
/// Unlike more complex `EventLoop`s, such as `SelectableEventLoop`, the `EmbeddedEventLoop`
106
/// has no proper eventing mechanism. Instead, reads and writes are fully controlled by the
107
/// entity that instantiates the `EmbeddedEventLoop`. This property makes `EmbeddedEventLoop`
108
/// of limited use for many application purposes, but highly valuable for testing and other
109
/// kinds of mocking.
110
///
111
/// Time is controllable on an `EmbeddedEventLoop`. It begins at `NIODeadline.uptimeNanoseconds(0)`
112
/// and may be advanced by a fixed amount by using `advanceTime(by:)`, or advanced to a point in
113
/// time with `advanceTime(to:)`.
114
///
115
/// - warning: Unlike `SelectableEventLoop`, `EmbeddedEventLoop` **is not thread-safe**. This
116
///     is because it is intended to be run in the thread that instantiated it. Users are
117
///     responsible for ensuring they never call into the `EmbeddedEventLoop` in an
118
///     unsynchronized fashion.
119
public final class EmbeddedEventLoop: EventLoop, CustomStringConvertible {
120
71.0k
    private var _now: NIODeadline = .uptimeNanoseconds(0)
121
    /// The current "time" for this event loop. This is an amount in nanoseconds.
122
0
    public var now: NIODeadline { _now }
123
124
    private enum State { case open, closing, closed }
125
71.0k
    private var state: State = .open
126
127
71.0k
    private var scheduledTaskCounter: UInt64 = 0
128
71.0k
    private var scheduledTasks = PriorityQueue<EmbeddedScheduledTask>()
129
130
    /// Keep track of where promises are allocated to ensure we can identify their source if they leak.
131
71.0k
    private var _promiseCreationStore: [_NIOEventLoopFutureIdentifier: (file: StaticString, line: UInt)] = [:]
132
133
    // The number of the next task to be created. We track the order so that when we execute tasks
134
    // scheduled at the same time, we may do so in the order in which they were submitted for
135
    // execution.
136
71.0k
    private var taskNumber: UInt64 = 0
137
138
71.0k
    public let description = "EmbeddedEventLoop"
139
140
    #if canImport(Darwin) || canImport(Glibc) || canImport(Musl) || canImport(Android)
141
71.0k
    private let myThread: pthread_t = pthread_self()
142
143
4.87M
    func isCorrectThread() -> Bool {
144
4.87M
        pthread_equal(self.myThread, pthread_self()) != 0
145
4.87M
    }
146
    #else
147
    func isCorrectThread() -> Bool {
148
        true  // let's be conservative
149
    }
150
    #endif
151
152
14.1k
    private func nextTaskNumber() -> UInt64 {
153
14.1k
        defer {
154
14.1k
            self.taskNumber += 1
155
14.1k
        }
156
14.1k
        return self.taskNumber
157
14.1k
    }
158
159
    /// - see: `EventLoop.inEventLoop`
160
5.00M
    public var inEventLoop: Bool {
161
5.00M
        self.checkCorrectThread()
162
5.00M
        return true
163
5.00M
    }
164
165
9.85M
    public func checkCorrectThread() {
166
9.85M
        guard self.isCorrectThread() else {
167
0
            if Self.strictModeEnabled {
168
0
                preconditionFailure(
169
0
                    "EmbeddedEventLoop is not thread-safe. You can only use it from the thread you created it on."
170
0
                )
171
0
            } else {
172
0
                printError(
173
0
                    """
174
0
                    ERROR: NIO API misuse: EmbeddedEventLoop is not thread-safe. \
175
0
                    You can only use it from the thread you created it on. This problem will be upgraded to a forced \
176
0
                    crash in future versions of SwiftNIO.
177
0
178
0
                    """
179
0
                )
180
0
            }
181
0
            return
182
9.85M
        }
183
9.85M
    }
184
185
    /// Initialize a new `EmbeddedEventLoop`.
186
28.2k
    public init() {}
187
188
    /// - see: `EventLoop.scheduleTask(deadline:_:)`
189
    @discardableResult
190
14.1k
    public func scheduleTask<T>(deadline: NIODeadline, _ task: @escaping () throws -> T) -> Scheduled<T> {
191
14.1k
        self.checkCorrectThread()
192
14.1k
        let promise: EventLoopPromise<T> = makePromise()
193
14.1k
194
14.1k
        switch self.state {
195
14.1k
        case .open:
196
14.1k
            break
197
14.1k
        case .closing, .closed:
198
0
            // If the event loop is shut down, or shutting down, immediately cancel the task.
199
0
            promise.fail(EventLoopError.cancelled)
200
0
            return Scheduled(promise: promise, cancellationTask: {})
201
14.1k
        }
202
14.1k
203
14.1k
        self.scheduledTaskCounter += 1
204
14.1k
        let task = EmbeddedScheduledTask(
205
14.1k
            id: self.scheduledTaskCounter,
206
14.1k
            readyTime: deadline,
207
14.1k
            insertOrder: self.nextTaskNumber(),
208
14.1k
            task: {
209
14.1k
                do {
210
14.1k
                    promise.assumeIsolated().succeed(try task())
211
14.1k
                } catch let err {
212
0
                    promise.fail(err)
213
14.1k
                }
214
14.1k
            },
215
14.1k
            promise.fail
$s11NIOEmbedded17EmbeddedEventLoopC12scheduleTask8deadline_7NIOCore9ScheduledVyxGAF11NIODeadlineV_xyKctlFys5Error_pYbcAF0cD7PromiseVyxGYbcfu_
Line
Count
Source
215
14.1k
            promise.fail
Unexecuted instantiation: $s11NIOEmbedded17EmbeddedEventLoopC12scheduleTask8deadline_7NIOCore9ScheduledVyxGAF11NIODeadlineV_xyKctlFys5Error_pYbcAF0cD7PromiseVyxGYbcfu_ysAL_pYbcfu0_
216
14.1k
        )
217
14.1k
218
14.1k
        let taskId = task.id
219
14.1k
        let scheduled = Scheduled(
220
14.1k
            promise: promise,
221
14.1k
            cancellationTask: {
222
0
                self.scheduledTasks.removeFirst { $0.id == taskId }
223
0
            }
224
14.1k
        )
225
14.1k
        scheduledTasks.push(task)
226
14.1k
        return scheduled
227
14.1k
    }
228
229
    /// - see: `EventLoop.scheduleTask(in:_:)`
230
    @discardableResult
231
0
    public func scheduleTask<T>(in: TimeAmount, _ task: @escaping () throws -> T) -> Scheduled<T> {
232
0
        self.checkCorrectThread()
233
0
        return self.scheduleTask(deadline: self._now + `in`, task)
234
0
    }
235
236
    @preconcurrency
237
    @discardableResult
238
    public func scheduleCallback(
239
        in amount: TimeAmount,
240
        handler: some (NIOScheduledCallbackHandler & Sendable)
241
0
    ) -> NIOScheduledCallback {
242
0
        self.checkCorrectThread()
243
0
        /// Even though this type does not implement a custom `scheduleCallback(at:handler)`, it uses a manual clock so
244
0
        /// it cannot rely on the default implementation of `scheduleCallback(in:handler:)`, which computes the deadline
245
0
        /// as an offset from `NIODeadline.now`. This event loop needs the deadline to be offset from `self._now`.
246
0
        return self.scheduleCallback(at: self._now + amount, handler: handler)
247
0
    }
248
249
    /// On an `EmbeddedEventLoop`, `execute` will simply use `scheduleTask` with a deadline of _now_. This means that
250
    /// `task` will be run the next time you call `EmbeddedEventLoop.run`.
251
14.1k
    public func execute(_ task: @escaping () -> Void) {
252
14.1k
        self.checkCorrectThread()
253
14.1k
        self.scheduleTask(deadline: self._now, task)
254
14.1k
    }
255
256
    /// Run all tasks that have previously been submitted to this `EmbeddedEventLoop`, either by calling `execute` or
257
    /// events that have been enqueued using `scheduleTask`/`scheduleRepeatedTask`/`scheduleRepeatedAsyncTask` and whose
258
    /// deadlines have expired.
259
    ///
260
    /// - seealso: `EmbeddedEventLoop.advanceTime`.
261
23.7k
    public func run() {
262
23.7k
        self.checkCorrectThread()
263
23.7k
        // Execute all tasks that are currently enqueued to be executed *now*.
264
23.7k
        self.advanceTime(to: self._now)
265
23.7k
    }
266
267
    /// Runs the event loop and moves "time" forward by the given amount, running any scheduled
268
    /// tasks that need to be run.
269
0
    public func advanceTime(by increment: TimeAmount) {
270
0
        self.checkCorrectThread()
271
0
        self.advanceTime(to: self._now + increment)
272
0
    }
273
274
    /// Runs the event loop and moves "time" forward to the given point in time, running any scheduled
275
    /// tasks that need to be run.
276
    ///
277
    /// - Note: If `deadline` is before the current time, the current time will not be advanced.
278
23.7k
    public func advanceTime(to deadline: NIODeadline) {
279
23.7k
        self.checkCorrectThread()
280
23.7k
        let newTime = max(deadline, self._now)
281
23.7k
282
23.7k
        while let nextTask = self.scheduledTasks.peek() {
283
14.1k
            guard nextTask.readyTime <= newTime else {
284
0
                break
285
14.1k
            }
286
14.1k
287
14.1k
            // Now we want to grab all tasks that are ready to execute at the same
288
14.1k
            // time as the first.
289
14.1k
            var tasks = [EmbeddedScheduledTask]()
290
14.1k
            while let candidateTask = self.scheduledTasks.peek(), candidateTask.readyTime == nextTask.readyTime {
291
14.1k
                tasks.append(candidateTask)
292
14.1k
                self.scheduledTasks.pop()
293
14.1k
            }
294
14.1k
295
14.1k
            // Set the time correctly before we call into user code, then
296
14.1k
            // call in for all tasks.
297
14.1k
            self._now = nextTask.readyTime
298
14.1k
299
14.1k
            for task in tasks {
300
14.1k
                task.task()
301
14.1k
            }
302
23.7k
        }
303
23.7k
304
23.7k
        // Finally ensure we got the time right.
305
23.7k
        self._now = newTime
306
23.7k
    }
307
308
6.96k
    internal func cancelRemainingScheduledTasks() {
309
6.96k
        self.checkCorrectThread()
310
6.96k
        while let task = self.scheduledTasks.pop() {
311
0
            task.fail(EventLoopError.cancelled)
312
6.96k
        }
313
6.96k
    }
314
315
    #if canImport(Dispatch)
316
    /// - see: `EventLoop.shutdownGracefully`
317
0
    public func shutdownGracefully(queue: DispatchQueue, _ callback: @escaping (Error?) -> Void) {
318
0
        self.checkCorrectThread()
319
0
        self.state = .closing
320
0
        run()
321
0
        cancelRemainingScheduledTasks()
322
0
        self.state = .closed
323
0
        queue.sync {
324
0
            callback(nil)
325
0
        }
326
0
    }
327
    #endif
328
329
1.98M
    public func preconditionInEventLoop(file: StaticString, line: UInt) {
330
1.98M
        self.checkCorrectThread()
331
1.98M
        // Currently, inEventLoop is always true so this always passes.
332
1.98M
    }
333
334
0
    public func preconditionNotInEventLoop(file: StaticString, line: UInt) {
335
0
        // As inEventLoop always returns true, this must always precondition.
336
0
        preconditionFailure("Always in EmbeddedEventLoop", file: file, line: line)
337
0
    }
338
339
42.3k
    public func _preconditionSafeToWait(file: StaticString, line: UInt) {
340
42.3k
        self.checkCorrectThread()
341
42.3k
        // EmbeddedEventLoop always allows a wait, as waiting will essentially always block
342
42.3k
        // wait()
343
42.3k
        return
344
42.3k
    }
345
346
27.8k
    public func _promiseCreated(futureIdentifier: _NIOEventLoopFutureIdentifier, file: StaticString, line: UInt) {
347
27.8k
        self.checkCorrectThread()
348
27.8k
        precondition(_isDebugAssertConfiguration())
349
27.8k
        self._promiseCreationStore[futureIdentifier] = (file: file, line: line)
350
27.8k
    }
351
352
    public func _promiseCompleted(futureIdentifier: _NIOEventLoopFutureIdentifier) -> (file: StaticString, line: UInt)?
353
34.8k
    {
354
34.8k
        self.checkCorrectThread()
355
34.8k
        precondition(_isDebugAssertConfiguration())
356
34.8k
        return self._promiseCreationStore.removeValue(forKey: futureIdentifier)
357
34.8k
    }
358
359
0
    public func _preconditionSafeToSyncShutdown(file: StaticString, line: UInt) {
360
0
        self.checkCorrectThread()
361
0
        // EmbeddedEventLoop always allows a sync shutdown.
362
0
        return
363
0
    }
364
365
0
    public func _executeIsolatedUnsafeUnchecked(_ task: @escaping () -> Void) {
366
0
        // Because of the way EmbeddedEL works, we can just delegate this directly
367
0
        // to execute.
368
0
        self.execute(task)
369
0
    }
370
371
0
    public func _submitIsolatedUnsafeUnchecked<T>(_ task: @escaping () throws -> T) -> EventLoopFuture<T> {
372
0
        // Because of the way EmbeddedEL works, we can delegate this directly to schedule. Note that I didn't
373
0
        // say submit: we don't have an override of submit here.
374
0
        self.scheduleTask(deadline: self._now, task).futureResult
375
0
    }
376
377
    @discardableResult
378
    public func _scheduleTaskIsolatedUnsafeUnchecked<T>(
379
        deadline: NIODeadline,
380
        _ task: @escaping () throws -> T
381
0
    ) -> Scheduled<T> {
382
0
        // Because of the way EmbeddedEL works, we can delegate this directly to schedule.
383
0
        self.scheduleTask(deadline: deadline, task)
384
0
    }
385
386
    @discardableResult
387
    public func _scheduleTaskIsolatedUnsafeUnchecked<T>(
388
        in delay: TimeAmount,
389
        _ task: @escaping () throws -> T
390
0
    ) -> Scheduled<T> {
391
0
        // Because of the way EmbeddedEL works, we can delegate this directly to schedule.
392
0
        self.scheduleTask(in: delay, task)
393
0
    }
394
395
28.2k
    deinit {
396
28.2k
        self.checkCorrectThread()
397
28.2k
        precondition(scheduledTasks.isEmpty, "Embedded event loop freed with unexecuted scheduled tasks!")
398
28.2k
    }
399
400
    @available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *)
401
0
    public var executor: any SerialExecutor {
402
0
        fatalError(
403
0
            "EmbeddedEventLoop is not thread safe and cannot be used as a SerialExecutor. Use NIOAsyncTestingEventLoop instead."
404
0
        )
405
0
    }
406
407
0
    static let strictModeEnabled: Bool = {
408
0
        for ciVar in ["SWIFTNIO_STRICT", "SWIFTNIO_CI", "SWIFTNIO_STRICT_EMBEDDED"] {
409
            #if os(Windows)
410
            let env = Windows.getenv("SWIFTNIO_STRICT")
411
            #else
412
0
            let env = getenv("SWIFTNIO_STRICT").flatMap { String(cString: $0) }
413
            #endif
414
0
            switch env?.lowercased() {
415
0
            case "true", "y", "yes", "on", "1":
416
0
                return true
417
0
            default:
418
0
                ()
419
0
            }
420
0
        }
421
0
        return false
422
0
    }()
423
}
424
425
// EmbeddedEventLoop is extremely _not_ Sendable. However, the EventLoop protocol
426
// requires it to be. We are doing some runtime enforcement of correct use, but
427
// ultimately we can't have the compiler validating this usage.
428
extension EmbeddedEventLoop: @unchecked Sendable {}
429
430
@usableFromInline
431
class EmbeddedChannelCore: ChannelCore {
432
    var isOpen: Bool {
433
13.9k
        get {
434
13.9k
            self._isOpen.load(ordering: .sequentiallyConsistent)
435
13.9k
        }
436
13.9k
        set {
437
13.9k
            self._isOpen.store(newValue, ordering: .sequentiallyConsistent)
438
13.9k
        }
439
    }
440
441
    var isActive: Bool {
442
6.96k
        get {
443
6.96k
            self._isActive.load(ordering: .sequentiallyConsistent)
444
6.96k
        }
445
6.96k
        set {
446
6.96k
            self._isActive.store(newValue, ordering: .sequentiallyConsistent)
447
6.96k
        }
448
    }
449
450
    var allowRemoteHalfClosure: Bool {
451
0
        get {
452
0
            self._allowRemoteHalfClosure.load(ordering: .sequentiallyConsistent)
453
0
        }
454
0
        set {
455
0
            self._allowRemoteHalfClosure.store(newValue, ordering: .sequentiallyConsistent)
456
0
        }
457
    }
458
459
    var allowOptionsWhenClosed: Bool {
460
0
        get {
461
0
            self._allowOptionsWhenClosed.load(ordering: .sequentiallyConsistent)
462
0
        }
463
0
        set {
464
0
            self._allowOptionsWhenClosed.store(newValue, ordering: .sequentiallyConsistent)
465
0
        }
466
    }
467
468
14.1k
    private let _isOpen = ManagedAtomic(true)
469
14.1k
    private let _isActive = ManagedAtomic(false)
470
14.1k
    private let _allowRemoteHalfClosure = ManagedAtomic(false)
471
14.1k
    private let _allowOptionsWhenClosed = ManagedAtomic(true)
472
473
    let eventLoop: EventLoop
474
    let closePromise: EventLoopPromise<Void>
475
    var error: Optional<Error>
476
477
    private let pipeline: ChannelPipeline
478
479
6.96k
    init(pipeline: ChannelPipeline, eventLoop: EventLoop) {
480
6.96k
        closePromise = eventLoop.makePromise()
481
6.96k
        self.pipeline = pipeline
482
6.96k
        self.eventLoop = eventLoop
483
6.96k
        self.error = nil
484
6.96k
    }
485
486
14.1k
    deinit {
487
14.1k
        assert(
488
14.1k
            !self.isOpen && !self.isActive,
489
14.1k
            "leaked an open EmbeddedChannel, maybe forgot to call channel.finish()?"
490
14.1k
        )
491
14.1k
        isOpen = false
492
14.1k
        closePromise.succeed(())
493
14.1k
    }
494
495
    /// Contains the flushed items that went into the `Channel` (and on a regular channel would have hit the network).
496
    @usableFromInline
497
14.1k
    var outboundBuffer: CircularBuffer<NIOAny> = CircularBuffer()
498
499
    /// Contains observers that want to consume the first element that would be appended to the `outboundBuffer`
500
14.1k
    private var outboundBufferConsumer: Deque<(Result<NIOAny, Error>) -> Void> = []
501
502
    /// Contains the unflushed items that went into the `Channel`
503
    @usableFromInline
504
14.1k
    var pendingOutboundBuffer: MarkedCircularBuffer<(NIOAny, EventLoopPromise<Void>?)> = MarkedCircularBuffer(
505
14.1k
        initialCapacity: 16
506
14.1k
    )
507
508
    /// Contains the items that travelled the `ChannelPipeline` all the way and hit the tail channel handler. On a
509
    /// regular `Channel` these items would be lost.
510
    @usableFromInline
511
14.1k
    var inboundBuffer: CircularBuffer<NIOAny> = CircularBuffer()
512
513
    /// Contains observers that want to consume the first element that would be appended to the `inboundBuffer`
514
14.1k
    private var inboundBufferConsumer: Deque<(Result<NIOAny, Error>) -> Void> = []
515
516
    @usableFromInline
517
    internal struct Addresses {
518
        var localAddress: SocketAddress?
519
        var remoteAddress: SocketAddress?
520
    }
521
522
    @usableFromInline
523
14.1k
    internal let _addresses: NIOLockedValueBox<Addresses> = NIOLockedValueBox(
524
14.1k
        .init(localAddress: nil, remoteAddress: nil)
525
14.1k
    )
526
527
    @usableFromInline
528
    var localAddress: SocketAddress? {
529
0
        get {
530
0
            self._addresses.withLockedValue { $0.localAddress }
531
0
        }
532
0
        set {
533
0
            self._addresses.withLockedValue { $0.localAddress = newValue }
534
0
        }
535
    }
536
537
    @usableFromInline
538
    var remoteAddress: SocketAddress? {
539
0
        get {
540
0
            self._addresses.withLockedValue { $0.remoteAddress }
541
0
        }
542
0
        set {
543
0
            self._addresses.withLockedValue { $0.remoteAddress = newValue }
544
0
        }
545
    }
546
547
    @usableFromInline
548
0
    func localAddress0() throws -> SocketAddress {
549
0
        self.eventLoop.preconditionInEventLoop()
550
0
        if let localAddress = self.localAddress {
551
0
            return localAddress
552
0
        } else {
553
0
            throw ChannelError.operationUnsupported
554
0
        }
555
0
    }
556
557
    @usableFromInline
558
0
    func remoteAddress0() throws -> SocketAddress {
559
0
        self.eventLoop.preconditionInEventLoop()
560
0
        if let remoteAddress = self.remoteAddress {
561
0
            return remoteAddress
562
0
        } else {
563
0
            throw ChannelError.operationUnsupported
564
0
        }
565
0
    }
566
567
    @usableFromInline
568
14.1k
    func close0(error: Error, mode: CloseMode, promise: EventLoopPromise<Void>?) {
569
14.1k
        self.eventLoop.preconditionInEventLoop()
570
14.1k
        guard self.isOpen else {
571
0
            promise?.fail(ChannelError.alreadyClosed)
572
0
            return
573
14.1k
        }
574
14.1k
        isOpen = false
575
14.1k
        isActive = false
576
14.1k
        promise?.succeed(())
577
14.1k
578
14.1k
        // Return a `.failure` result containing an error to all pending inbound and outbound consumers.
579
14.1k
        while let consumer = self.inboundBufferConsumer.popFirst() {
580
0
            consumer(.failure(ChannelError.ioOnClosedChannel))
581
14.1k
        }
582
14.1k
        while let consumer = self.outboundBufferConsumer.popFirst() {
583
0
            consumer(.failure(ChannelError.ioOnClosedChannel))
584
14.1k
        }
585
14.1k
586
14.1k
        // As we called register() in the constructor of EmbeddedChannel we also need to ensure we call unregistered here.
587
14.1k
        self.pipeline.syncOperations.fireChannelInactive()
588
14.1k
        self.pipeline.syncOperations.fireChannelUnregistered()
589
14.1k
590
14.1k
        let loopBoundSelf = NIOLoopBound(self, eventLoop: self.eventLoop)
591
14.1k
592
14.1k
        eventLoop.execute {
593
14.1k
            // ensure this is executed in a delayed fashion as the users code may still traverse the pipeline
594
14.1k
            let `self` = loopBoundSelf.value
595
14.1k
            self.removeHandlers(pipeline: self.pipeline)
596
14.1k
            self.closePromise.succeed(())
597
14.1k
        }
598
14.1k
    }
599
600
    @usableFromInline
601
0
    func bind0(to address: SocketAddress, promise: EventLoopPromise<Void>?) {
602
0
        self.eventLoop.preconditionInEventLoop()
603
0
        promise?.succeed(())
604
0
    }
605
606
    @usableFromInline
607
0
    func connect0(to address: SocketAddress, promise: EventLoopPromise<Void>?) {
608
0
        self.eventLoop.preconditionInEventLoop()
609
0
        isActive = true
610
0
        promise?.succeed(())
611
0
        self.pipeline.syncOperations.fireChannelActive()
612
0
    }
613
614
    @usableFromInline
615
14.1k
    func register0(promise: EventLoopPromise<Void>?) {
616
14.1k
        self.eventLoop.preconditionInEventLoop()
617
14.1k
        promise?.succeed(())
618
14.1k
        self.pipeline.syncOperations.fireChannelRegistered()
619
14.1k
    }
620
621
    @usableFromInline
622
0
    func registerAlreadyConfigured0(promise: EventLoopPromise<Void>?) {
623
0
        self.eventLoop.preconditionInEventLoop()
624
0
        isActive = true
625
0
        register0(promise: promise)
626
0
        self.pipeline.syncOperations.fireChannelActive()
627
0
    }
628
629
    @usableFromInline
630
0
    func write0(_ data: NIOAny, promise: EventLoopPromise<Void>?) {
631
0
        self.eventLoop.preconditionInEventLoop()
632
0
        self.pendingOutboundBuffer.append((data, promise))
633
0
    }
634
635
    @usableFromInline
636
0
    func flush0() {
637
0
        self.eventLoop.preconditionInEventLoop()
638
0
        self.pendingOutboundBuffer.mark()
639
0
640
0
        while self.pendingOutboundBuffer.hasMark, let dataAndPromise = self.pendingOutboundBuffer.popFirst() {
641
0
            self.addToBuffer(
642
0
                buffer: &self.outboundBuffer,
643
0
                consumer: &self.outboundBufferConsumer,
644
0
                data: dataAndPromise.0
645
0
            )
646
0
            dataAndPromise.1?.succeed(())
647
0
        }
648
0
    }
649
650
    @usableFromInline
651
0
    func read0() {
652
0
        self.eventLoop.preconditionInEventLoop()
653
0
        // NOOP
654
0
    }
655
656
0
    public final func triggerUserOutboundEvent0(_ event: Any, promise: EventLoopPromise<Void>?) {
657
0
        self.eventLoop.preconditionInEventLoop()
658
0
        promise?.fail(ChannelError.operationUnsupported)
659
0
    }
660
661
    @usableFromInline
662
2.38M
    func channelRead0(_ data: NIOAny) {
663
2.38M
        self.eventLoop.preconditionInEventLoop()
664
2.38M
        self.addToBuffer(
665
2.38M
            buffer: &self.inboundBuffer,
666
2.38M
            consumer: &self.inboundBufferConsumer,
667
2.38M
            data: data
668
2.38M
        )
669
2.38M
    }
670
671
13.3k
    public func errorCaught0(error: Error) {
672
13.3k
        self.eventLoop.preconditionInEventLoop()
673
13.3k
        if self.error == nil {
674
13.0k
            self.error = error
675
13.0k
        }
676
13.3k
    }
677
678
    private func addToBuffer(
679
        buffer: inout CircularBuffer<NIOAny>,
680
        consumer: inout Deque<(Result<NIOAny, Error>) -> Void>,
681
        data: NIOAny
682
2.38M
    ) {
683
2.38M
        self.eventLoop.preconditionInEventLoop()
684
2.38M
        if let consume = consumer.popFirst() {
685
0
            consume(.success(data))
686
2.38M
        } else {
687
2.38M
            buffer.append(data)
688
2.38M
        }
689
2.38M
    }
690
691
    /// Enqueue a consumer closure that will be invoked upon the next pending inbound write.
692
    /// - Parameter newElement: The consumer closure to enqueue. Returns a `.failure` result if the channel has already
693
    ///   closed.
694
0
    func _enqueueInboundBufferConsumer(_ newElement: @escaping (Result<NIOAny, Error>) -> Void) {
695
0
        self.eventLoop.preconditionInEventLoop()
696
0
697
0
        // The channel has already closed: there cannot be any further writes. Return a `.failure` result with an error.
698
0
        guard self.isOpen else {
699
0
            newElement(.failure(ChannelError.ioOnClosedChannel))
700
0
            return
701
0
        }
702
0
703
0
        self.inboundBufferConsumer.append(newElement)
704
0
    }
705
706
    /// Enqueue a consumer closure that will be invoked upon the next pending outbound write.
707
    /// - Parameter newElement: The consumer closure to enqueue. Returns a `.failure` result if the channel has already
708
    ///   closed.
709
0
    func _enqueueOutboundBufferConsumer(_ newElement: @escaping (Result<NIOAny, Error>) -> Void) {
710
0
        self.eventLoop.preconditionInEventLoop()
711
0
712
0
        // The channel has already closed: there cannot be any further writes. Return a `.failure` result with an error.
713
0
        guard self.isOpen else {
714
0
            newElement(.failure(ChannelError.ioOnClosedChannel))
715
0
            return
716
0
        }
717
0
718
0
        self.outboundBufferConsumer.append(newElement)
719
0
    }
720
}
721
722
// ChannelCores are basically never Sendable.
723
@available(*, unavailable)
724
extension EmbeddedChannelCore: Sendable {}
725
726
/// `EmbeddedChannel` is a `Channel` implementation that does neither any
727
/// actual IO nor has a proper eventing mechanism. The prime use-case for
728
/// `EmbeddedChannel` is in unit tests when you want to feed the inbound events
729
/// and check the outbound events manually.
730
///
731
/// Please remember to call `finish()` when you are no longer using this
732
/// `EmbeddedChannel`.
733
///
734
/// To feed events through an `EmbeddedChannel`'s `ChannelPipeline` use
735
/// `EmbeddedChannel.writeInbound` which accepts data of any type. It will then
736
/// forward that data through the `ChannelPipeline` and the subsequent
737
/// `ChannelInboundHandler` will receive it through the usual `channelRead`
738
/// event. The user is responsible for making sure the first
739
/// `ChannelInboundHandler` expects data of that type.
740
///
741
/// `EmbeddedChannel` automatically collects arriving outbound data and makes it
742
/// available one-by-one through `readOutbound`.
743
///
744
/// - Note: `EmbeddedChannel` is currently only compatible with
745
///   `EmbeddedEventLoop`s and cannot be used with `SelectableEventLoop`s from
746
///   for example `MultiThreadedEventLoopGroup`.
747
/// - warning: Unlike other `Channel`s, `EmbeddedChannel` **is not thread-safe**. This
748
///     is because it is intended to be run in the thread that instantiated it. Users are
749
///     responsible for ensuring they never call into an `EmbeddedChannel` in an
750
///     unsynchronized fashion. `EmbeddedEventLoop`s notes also apply as
751
///     `EmbeddedChannel` uses an `EmbeddedEventLoop` as its `EventLoop`.
752
public final class EmbeddedChannel: Channel {
753
    /// `LeftOverState` represents any left-over inbound, outbound, and pending outbound events that hit the
754
    /// `EmbeddedChannel` and were not consumed when `finish` was called on the `EmbeddedChannel`.
755
    ///
756
    /// `EmbeddedChannel` is most useful in testing and usually in unit tests, you want to consume all inbound and
757
    /// outbound data to verify they are what you expect. Therefore, when you `finish` an `EmbeddedChannel` it will
758
    /// return if it's either `.clean` (no left overs) or that it has `.leftOvers`.
759
    public enum LeftOverState {
760
        /// The `EmbeddedChannel` is clean, ie. no inbound, outbound, or pending outbound data left on `finish`.
761
        case clean
762
763
        /// The `EmbeddedChannel` has inbound, outbound, or pending outbound data left on `finish`.
764
        case leftOvers(inbound: [NIOAny], outbound: [NIOAny], pendingOutbound: [NIOAny])
765
766
        /// `true` if the `EmbeddedChannel` was `clean` on `finish`, ie. there is no unconsumed inbound, outbound, or
767
        /// pending outbound data left on the `Channel`.
768
0
        public var isClean: Bool {
769
0
            if case .clean = self {
770
0
                return true
771
0
            } else {
772
0
                return false
773
0
            }
774
0
        }
775
776
        /// `true` if the `EmbeddedChannel` if there was unconsumed inbound, outbound, or pending outbound data left
777
        /// on the `Channel` when it was `finish`ed.
778
0
        public var hasLeftOvers: Bool {
779
0
            !self.isClean
780
0
        }
781
    }
782
783
    /// `BufferState` represents the state of either the inbound, or the outbound `EmbeddedChannel` buffer. These
784
    /// buffers contain data that travelled the `ChannelPipeline` all the way.
785
    ///
786
    /// If the last `ChannelHandler` explicitly (by calling `fireChannelRead`) or implicitly (by not implementing
787
    /// `channelRead`) sends inbound data into the end of the `EmbeddedChannel`, it will be held in the
788
    /// `EmbeddedChannel`'s inbound buffer. Similarly for `write` on the outbound side. The state of the respective
789
    /// buffer will be returned from `writeInbound`/`writeOutbound` as a `BufferState`.
790
    public enum BufferState {
791
        /// The buffer is empty.
792
        case empty
793
794
        /// The buffer is non-empty.
795
        case full([NIOAny])
796
797
        /// Returns `true` is the buffer was empty.
798
0
        public var isEmpty: Bool {
799
0
            if case .empty = self {
800
0
                return true
801
0
            } else {
802
0
                return false
803
0
            }
804
0
        }
805
806
        /// Returns `true` if the buffer was non-empty.
807
0
        public var isFull: Bool {
808
0
            !self.isEmpty
809
0
        }
810
    }
811
812
    /// `WrongTypeError` is throws if you use `readInbound` or `readOutbound` and request a certain type but the first
813
    /// item in the respective buffer is of a different type.
814
    public struct WrongTypeError: Error, Equatable {
815
        /// The type you expected.
816
        public let expected: Any.Type
817
818
        /// The type of the actual first element.
819
        public let actual: Any.Type
820
821
0
        public init(expected: Any.Type, actual: Any.Type) {
822
0
            self.expected = expected
823
0
            self.actual = actual
824
0
        }
825
826
0
        public static func == (lhs: WrongTypeError, rhs: WrongTypeError) -> Bool {
827
0
            lhs.expected == rhs.expected && lhs.actual == rhs.actual
828
0
        }
829
    }
830
831
    /// Returns `true` if the `EmbeddedChannel` is 'active'.
832
    ///
833
    /// An active `EmbeddedChannel` can be closed by calling `close` or `finish` on the `EmbeddedChannel`.
834
    ///
835
    /// - Note: An `EmbeddedChannel` starts _inactive_ and can be activated, for example by calling `connect`.
836
0
    public var isActive: Bool { channelcore.isActive }
837
838
    @usableFromInline
839
0
    internal var isOpen: Bool { channelcore.isOpen }
840
841
    /// - see: `ChannelOptions.Types.AllowRemoteHalfClosureOption`
842
    public var allowRemoteHalfClosure: Bool {
843
0
        get {
844
0
            self.embeddedEventLoop.checkCorrectThread()
845
0
            return channelcore.allowRemoteHalfClosure
846
0
        }
847
0
        set {
848
0
            self.embeddedEventLoop.checkCorrectThread()
849
0
            channelcore.allowRemoteHalfClosure = newValue
850
0
        }
851
    }
852
853
    public var allowOptionsWhenClosed: Bool {
854
0
        get {
855
0
            self.embeddedEventLoop.checkCorrectThread()
856
0
            return channelcore.allowOptionsWhenClosed
857
0
        }
858
0
        set {
859
0
            self.embeddedEventLoop.checkCorrectThread()
860
0
            channelcore.allowOptionsWhenClosed = newValue
861
0
        }
862
    }
863
864
    /// - see: `Channel.closeFuture`
865
0
    public var closeFuture: EventLoopFuture<Void> { channelcore.closePromise.futureResult }
866
867
    @usableFromInline
868
35.5k
    lazy var channelcore: EmbeddedChannelCore = EmbeddedChannelCore(
869
35.5k
        pipeline: self._pipeline,
870
35.5k
        eventLoop: self.eventLoop
871
35.5k
    )
872
873
    /// - see: `Channel._channelCore`
874
2.43M
    public var _channelCore: ChannelCore {
875
2.43M
        self.embeddedEventLoop.checkCorrectThread()
876
2.43M
        return self.channelcore
877
2.43M
    }
878
879
    /// - see: `Channel.pipeline`
880
70.5k
    public var pipeline: ChannelPipeline {
881
70.5k
        self.embeddedEventLoop.checkCorrectThread()
882
70.5k
        return self._pipeline
883
70.5k
    }
884
885
    /// - see: `Channel.isWritable`
886
35.5k
    public var isWritable: Bool = true
887
888
    @usableFromInline
889
35.5k
    internal var _options: [(option: any ChannelOption, value: any Sendable)] = []
890
891
    /// The `ChannelOption`s set on this channel.
892
    /// - see: `Embedded.setOption`
893
0
    public var options: [(option: any ChannelOption, value: any Sendable)] { self._options }
894
895
    /// Synchronously closes the `EmbeddedChannel`.
896
    ///
897
    /// Errors in the `EmbeddedChannel` can be consumed using `throwIfErrorCaught`.
898
    ///
899
    /// - Parameters:
900
    ///   - acceptAlreadyClosed: Whether `finish` should throw if the `EmbeddedChannel` has been previously `close`d.
901
    /// - Returns: The `LeftOverState` of the `EmbeddedChannel`. If all the inbound and outbound events have been
902
    ///            consumed (using `readInbound` / `readOutbound`) and there are no pending outbound events (unflushed
903
    ///            writes) this will be `.clean`. If there are any unconsumed inbound, outbound, or pending outbound
904
    ///            events, the `EmbeddedChannel` will returns those as `.leftOvers(inbound:outbound:pendingOutbound:)`.
905
14.1k
    public func finish(acceptAlreadyClosed: Bool) throws -> LeftOverState {
906
14.1k
        self.embeddedEventLoop.checkCorrectThread()
907
14.1k
        do {
908
14.1k
            try close().wait()
909
14.1k
        } catch let error as ChannelError {
910
0
            guard error == .alreadyClosed && acceptAlreadyClosed else {
911
0
                throw error
912
0
            }
913
14.1k
        }
914
14.1k
        self.embeddedEventLoop.run()
915
14.1k
        self.embeddedEventLoop.cancelRemainingScheduledTasks()
916
14.1k
        try throwIfErrorCaught()
917
5.46k
        let c = self.channelcore
918
5.46k
        if c.outboundBuffer.isEmpty && c.inboundBuffer.isEmpty && c.pendingOutboundBuffer.isEmpty {
919
2.85k
            return .clean
920
2.85k
        } else {
921
2.61k
            return .leftOvers(
922
2.61k
                inbound: Array(c.inboundBuffer),
923
2.61k
                outbound: Array(c.outboundBuffer),
924
2.61k
                pendingOutbound: c.pendingOutboundBuffer.map { $0.0 }
925
2.61k
            )
926
2.61k
        }
927
5.46k
    }
928
929
    /// Synchronously closes the `EmbeddedChannel`.
930
    ///
931
    /// This method will throw if the `Channel` hit any unconsumed errors or if the `close` fails. Errors in the
932
    /// `EmbeddedChannel` can be consumed using `throwIfErrorCaught`.
933
    ///
934
    /// - Returns: The `LeftOverState` of the `EmbeddedChannel`. If all the inbound and outbound events have been
935
    ///            consumed (using `readInbound` / `readOutbound`) and there are no pending outbound events (unflushed
936
    ///            writes) this will be `.clean`. If there are any unconsumed inbound, outbound, or pending outbound
937
    ///            events, the `EmbeddedChannel` will returns those as `.leftOvers(inbound:outbound:pendingOutbound:)`.
938
0
    public func finish() throws -> LeftOverState {
939
0
        self.embeddedEventLoop.checkCorrectThread()
940
0
        return try self.finish(acceptAlreadyClosed: false)
941
0
    }
942
943
    private var _pipeline: ChannelPipeline!
944
945
    /// - see: `Channel.allocator`
946
35.5k
    public var allocator: ByteBufferAllocator = ByteBufferAllocator()
947
948
    /// - see: `Channel.eventLoop`
949
56.4k
    public var eventLoop: EventLoop {
950
56.4k
        self.embeddedEventLoop.checkCorrectThread()
951
56.4k
        return self.embeddedEventLoop
952
56.4k
    }
953
954
    /// Returns the `EmbeddedEventLoop` that this `EmbeddedChannel` uses. This will return the same instance as
955
    /// `EmbeddedChannel.eventLoop` but as the concrete `EmbeddedEventLoop` rather than as `EventLoop` existential.
956
35.5k
    public var embeddedEventLoop: EmbeddedEventLoop = EmbeddedEventLoop()
957
958
    /// - see: `Channel.localAddress`
959
    public var localAddress: SocketAddress? {
960
0
        get {
961
0
            self.embeddedEventLoop.checkCorrectThread()
962
0
            return self.channelcore.localAddress
963
0
        }
964
0
        set {
965
0
            self.embeddedEventLoop.checkCorrectThread()
966
0
            self.channelcore.localAddress = newValue
967
0
        }
968
    }
969
970
    /// - see: `Channel.remoteAddress`
971
    public var remoteAddress: SocketAddress? {
972
0
        get {
973
0
            self.embeddedEventLoop.checkCorrectThread()
974
0
            return self.channelcore.remoteAddress
975
0
        }
976
0
        set {
977
0
            self.embeddedEventLoop.checkCorrectThread()
978
0
            self.channelcore.remoteAddress = newValue
979
0
        }
980
    }
981
982
    /// `nil` because `EmbeddedChannel`s don't have parents.
983
35.5k
    public let parent: Channel? = nil
984
985
    /// If available, this method reads one element of type `T` out of the `EmbeddedChannel`'s outbound buffer. If the
986
    /// first element was of a different type than requested, `EmbeddedChannel.WrongTypeError` will be thrown, if there
987
    /// are no elements in the outbound buffer, `nil` will be returned.
988
    ///
989
    /// Data hits the `EmbeddedChannel`'s outbound buffer when data was written using `write`, then `flush`ed, and
990
    /// then travelled the `ChannelPipeline` all the way too the front. For data to hit the outbound buffer, the very
991
    /// first `ChannelHandler` must have written and flushed it either explicitly (by calling
992
    /// `ChannelHandlerContext.write` and `flush`) or implicitly by not implementing `write`/`flush`.
993
    ///
994
    /// - Note: Outbound events travel the `ChannelPipeline` _back to front_.
995
    /// - Note: `EmbeddedChannel.writeOutbound` will `write` data through the `ChannelPipeline`, starting with last
996
    ///         `ChannelHandler`.
997
    @inlinable
998
0
    public func readOutbound<T>(as type: T.Type = T.self) throws -> T? {
999
0
        self.embeddedEventLoop.checkCorrectThread()
1000
0
        return try _readFromBuffer(buffer: &channelcore.outboundBuffer)
1001
0
    }
1002
1003
    /// If available, this method reads one element of type `T` out of the `EmbeddedChannel`'s inbound buffer. If the
1004
    /// first element was of a different type than requested, `EmbeddedChannel.WrongTypeError` will be thrown, if there
1005
    /// are no elements in the outbound buffer, `nil` will be returned.
1006
    ///
1007
    /// Data hits the `EmbeddedChannel`'s inbound buffer when data was send through the pipeline using `fireChannelRead`
1008
    /// and then travelled the `ChannelPipeline` all the way too the back. For data to hit the inbound buffer, the
1009
    /// last `ChannelHandler` must have send the event either explicitly (by calling
1010
    /// `ChannelHandlerContext.fireChannelRead`) or implicitly by not implementing `channelRead`.
1011
    ///
1012
    /// - Note: `EmbeddedChannel.writeInbound` will fire data through the `ChannelPipeline` using `fireChannelRead`.
1013
    @inlinable
1014
0
    public func readInbound<T>(as type: T.Type = T.self) throws -> T? {
1015
0
        self.embeddedEventLoop.checkCorrectThread()
1016
0
        return try _readFromBuffer(buffer: &channelcore.inboundBuffer)
1017
0
    }
1018
1019
    /// Sends an inbound `channelRead` event followed by a `channelReadComplete` event through the `ChannelPipeline`.
1020
    ///
1021
    /// The immediate effect being that the first `ChannelInboundHandler` will get its `channelRead` method called
1022
    /// with the data you provide.
1023
    ///
1024
    /// - Parameters:
1025
    ///    - data: The data to fire through the pipeline.
1026
    /// - Returns: The state of the inbound buffer which contains all the events that travelled the `ChannelPipeline`
1027
    //             all the way.
1028
    @inlinable
1029
35.5k
    @discardableResult public func writeInbound<T>(_ data: T) throws -> BufferState {
1030
35.5k
        self.embeddedEventLoop.checkCorrectThread()
1031
35.5k
        self.pipeline.syncOperations.fireChannelRead(NIOAny(data))
1032
35.5k
        self.pipeline.syncOperations.fireChannelReadComplete()
1033
35.5k
        try self.throwIfErrorCaught()
1034
24.5k
        return self.channelcore.inboundBuffer.isEmpty ? .empty : .full(Array(self.channelcore.inboundBuffer))
1035
35.5k
    }
1036
1037
    /// Sends an outbound `writeAndFlush` event through the `ChannelPipeline`.
1038
    ///
1039
    /// The immediate effect being that the first `ChannelOutboundHandler` will get its `write` method called
1040
    /// with the data you provide. Note that the first `ChannelOutboundHandler` in the pipeline is the _last_ handler
1041
    /// because outbound events travel the pipeline from back to front.
1042
    ///
1043
    /// - Parameters:
1044
    ///    - data: The data to fire through the pipeline.
1045
    /// - Returns: The state of the outbound buffer which contains all the events that travelled the `ChannelPipeline`
1046
    //             all the way.
1047
    @inlinable
1048
0
    @discardableResult public func writeOutbound<T>(_ data: T) throws -> BufferState {
1049
0
        self.embeddedEventLoop.checkCorrectThread()
1050
0
        try self.writeAndFlush(data).wait()
1051
0
        return self.channelcore.outboundBuffer.isEmpty ? .empty : .full(Array(self.channelcore.outboundBuffer))
1052
0
    }
1053
1054
    /// This method will throw the error that is stored in the `EmbeddedChannel` if any.
1055
    ///
1056
    /// The `EmbeddedChannel` will store an error some error travels the `ChannelPipeline` all the way past its end.
1057
28.2k
    public func throwIfErrorCaught() throws {
1058
28.2k
        self.embeddedEventLoop.checkCorrectThread()
1059
28.2k
        if let error = channelcore.error {
1060
13.0k
            self.channelcore.error = nil
1061
13.0k
            throw error
1062
15.1k
        }
1063
15.1k
    }
1064
1065
    @inlinable
1066
0
    func _readFromBuffer<T>(buffer: inout CircularBuffer<NIOAny>) throws -> T? {
1067
0
        self.embeddedEventLoop.checkCorrectThread()
1068
0
        if buffer.isEmpty {
1069
0
            return nil
1070
0
        }
1071
0
        let elem = buffer.removeFirst()
1072
0
        guard let t = self._channelCore.tryUnwrapData(elem, as: T.self) else {
1073
0
            throw WrongTypeError(
1074
0
                expected: T.self,
1075
0
                actual: type(of: self._channelCore.tryUnwrapData(elem, as: Any.self)!)
1076
0
            )
1077
0
        }
1078
0
        return t
1079
0
    }
1080
1081
    /// Create a new instance.
1082
    ///
1083
    /// During creation it will automatically also register itself on the `EmbeddedEventLoop`.
1084
    ///
1085
    /// - Parameters:
1086
    ///   - handler: The `ChannelHandler` to add to the `ChannelPipeline` before register or `nil` if none should be added.
1087
    ///   - loop: The `EmbeddedEventLoop` to use.
1088
14.1k
    public convenience init(handler: ChannelHandler? = nil, loop: EmbeddedEventLoop = EmbeddedEventLoop()) {
1089
14.1k
        let handlers = handler.map { [$0] } ?? []
1090
14.1k
        self.init(handlers: handlers, loop: loop)
1091
14.1k
        self.embeddedEventLoop.checkCorrectThread()
1092
14.1k
    }
1093
1094
    /// Create a new instance.
1095
    ///
1096
    /// During creation it will automatically also register itself on the `EmbeddedEventLoop`.
1097
    ///
1098
    /// - Parameters:
1099
    ///   - handlers: The `ChannelHandler`s to add to the `ChannelPipeline` before register.
1100
    ///   - loop: The `EmbeddedEventLoop` to use.
1101
14.1k
    public init(handlers: [ChannelHandler], loop: EmbeddedEventLoop = EmbeddedEventLoop()) {
1102
14.1k
        self.embeddedEventLoop = loop
1103
14.1k
        self._pipeline = ChannelPipeline(channel: self)
1104
14.1k
1105
14.1k
        try! self._pipeline.syncOperations.addHandlers(handlers)
1106
14.1k
1107
14.1k
        // This will never throw...
1108
14.1k
        try! register().wait()
1109
14.1k
        self.embeddedEventLoop.checkCorrectThread()
1110
14.1k
    }
1111
1112
    /// - see: `Channel.setOption`
1113
    @inlinable
1114
0
    public func setOption<Option: ChannelOption>(_ option: Option, value: Option.Value) -> EventLoopFuture<Void> {
1115
0
        self.embeddedEventLoop.checkCorrectThread()
1116
0
        return self.eventLoop.makeCompletedFuture { try self.setOptionSync(option, value: value) }
1117
0
    }
1118
1119
    @inlinable
1120
0
    internal func setOptionSync<Option: ChannelOption>(_ option: Option, value: Option.Value) throws {
1121
0
        self.embeddedEventLoop.checkCorrectThread()
1122
0
1123
0
        guard self.isOpen || self.allowOptionsWhenClosed else {
1124
0
            throw ChannelError.alreadyClosed
1125
0
        }
1126
0
1127
0
        self.addOption(option, value: value)
1128
0
1129
0
        if option is ChannelOptions.Types.AllowRemoteHalfClosureOption {
1130
0
            self.allowRemoteHalfClosure = value as! Bool
1131
0
            return
1132
0
        }
1133
0
    }
1134
1135
    /// - see: `Channel.getOption`
1136
    @inlinable
1137
0
    public func getOption<Option: ChannelOption>(_ option: Option) -> EventLoopFuture<Option.Value> {
1138
0
        self.embeddedEventLoop.checkCorrectThread()
1139
0
        return self.eventLoop.makeCompletedFuture { try self.getOptionSync(option) }
1140
0
    }
1141
1142
    @inlinable
1143
0
    internal func getOptionSync<Option: ChannelOption>(_ option: Option) throws -> Option.Value {
1144
0
        self.embeddedEventLoop.checkCorrectThread()
1145
0
1146
0
        guard self.isOpen || self.allowOptionsWhenClosed else {
1147
0
            throw ChannelError.alreadyClosed
1148
0
        }
1149
0
1150
0
        if option is ChannelOptions.Types.AutoReadOption {
1151
0
            return true as! Option.Value
1152
0
        }
1153
0
        if option is ChannelOptions.Types.AllowRemoteHalfClosureOption {
1154
0
            return self.allowRemoteHalfClosure as! Option.Value
1155
0
        }
1156
0
        if option is ChannelOptions.Types.BufferedWritableBytesOption {
1157
0
            let result = self.channelcore.pendingOutboundBuffer.reduce(0) { partialResult, dataAndPromise in
1158
0
                let buffer = self.channelcore.unwrapData(dataAndPromise.0, as: ByteBuffer.self)
1159
0
                return partialResult + buffer.readableBytes
1160
0
            }
1161
0
1162
0
            return result as! Option.Value
1163
0
        }
1164
0
1165
0
        guard let value = optionValue(for: option) else {
1166
0
            fatalError("option \(option) not supported")
1167
0
        }
1168
0
1169
0
        return value
1170
0
    }
1171
1172
    @inlinable
1173
0
    internal func addOption<Option: ChannelOption>(_ option: Option, value: Option.Value) {
1174
0
        if let optionIndex = self._options.firstIndex(where: { $0.option is Option }) {
1175
0
            self._options[optionIndex] = (option: option, value: value)
1176
0
        } else {
1177
0
            self._options.append((option: option, value: value))
1178
0
        }
1179
0
    }
1180
1181
    @inlinable
1182
0
    internal func optionValue<Option: ChannelOption>(for option: Option) -> Option.Value? {
1183
0
        self.options.first(where: { $0.option is Option })?.value as? Option.Value
1184
0
    }
1185
1186
    /// Fires the (outbound) `bind` event through the `ChannelPipeline`. If the event hits the `EmbeddedChannel` which
1187
    /// happens when it travels the `ChannelPipeline` all the way to the front, this will also set the
1188
    /// `EmbeddedChannel`'s `localAddress`.
1189
    ///
1190
    /// - Parameters:
1191
    ///   - address: The address to fake-bind to.
1192
    ///   - promise: The `EventLoopPromise` which will be fulfilled when the fake-bind operation has been done.
1193
0
    public func bind(to address: SocketAddress, promise: EventLoopPromise<Void>?) {
1194
0
        self.embeddedEventLoop.checkCorrectThread()
1195
0
        let promise = promise ?? self.embeddedEventLoop.makePromise()
1196
0
        promise.futureResult.whenSuccess {
1197
0
            self.localAddress = address
1198
0
        }
1199
0
        self.pipeline.bind(to: address, promise: promise)
1200
0
    }
1201
1202
    /// Fires the (outbound) `connect` event through the `ChannelPipeline`. If the event hits the `EmbeddedChannel`
1203
    /// which happens when it travels the `ChannelPipeline` all the way to the front, this will also set the
1204
    /// `EmbeddedChannel`'s `remoteAddress`.
1205
    ///
1206
    /// - Parameters:
1207
    ///   - address: The address to fake-bind to.
1208
    ///   - promise: The `EventLoopPromise` which will be fulfilled when the fake-bind operation has been done.
1209
0
    public func connect(to address: SocketAddress, promise: EventLoopPromise<Void>?) {
1210
0
        self.embeddedEventLoop.checkCorrectThread()
1211
0
        let promise = promise ?? self.embeddedEventLoop.makePromise()
1212
0
        promise.futureResult.whenSuccess {
1213
0
            self.remoteAddress = address
1214
0
        }
1215
0
        self.pipeline.connect(to: address, promise: promise)
1216
0
    }
1217
1218
    /// An overload of `Channel.write` that does not require a Sendable type, as ``EmbeddedEventLoop``
1219
    /// is bound to a single thread.
1220
    @inlinable
1221
0
    public func write<T>(_ data: T, promise: EventLoopPromise<Void>?) {
1222
0
        self.embeddedEventLoop.checkCorrectThread()
1223
0
        self.pipeline.syncOperations.write(NIOAny(data), promise: promise)
1224
0
    }
1225
1226
    /// An overload of `Channel.write` that does not require a Sendable type, as ``EmbeddedEventLoop``
1227
    /// is bound to a single thread.
1228
    @inlinable
1229
0
    public func write<T>(_ data: T) -> EventLoopFuture<Void> {
1230
0
        self.embeddedEventLoop.checkCorrectThread()
1231
0
        let promise = self.eventLoop.makePromise(of: Void.self)
1232
0
        self.pipeline.syncOperations.write(NIOAny(data), promise: promise)
1233
0
        return promise.futureResult
1234
0
    }
1235
1236
    /// An overload of `Channel.writeAndFlush` that does not require a Sendable type, as ``EmbeddedEventLoop``
1237
    /// is bound to a single thread.
1238
    @inlinable
1239
0
    public func writeAndFlush<T>(_ data: T, promise: EventLoopPromise<Void>?) {
1240
0
        self.embeddedEventLoop.checkCorrectThread()
1241
0
        self.pipeline.syncOperations.writeAndFlush(NIOAny(data), promise: promise)
1242
0
    }
1243
1244
    /// An overload of `Channel.writeAndFlush` that does not require a Sendable type, as ``EmbeddedEventLoop``
1245
    /// is bound to a single thread.
1246
    @inlinable
1247
0
    public func writeAndFlush<T>(_ data: T) -> EventLoopFuture<Void> {
1248
0
        self.embeddedEventLoop.checkCorrectThread()
1249
0
        let promise = self.eventLoop.makePromise(of: Void.self)
1250
0
        self.pipeline.syncOperations.writeAndFlush(NIOAny(data), promise: promise)
1251
0
        return promise.futureResult
1252
0
    }
1253
}
1254
1255
extension EmbeddedChannel {
1256
    public struct SynchronousOptions: NIOSynchronousChannelOptions {
1257
        @usableFromInline
1258
        internal let channel: EmbeddedChannel
1259
1260
0
        fileprivate init(channel: EmbeddedChannel) {
1261
0
            self.channel = channel
1262
0
        }
1263
1264
        @inlinable
1265
0
        public func setOption<Option: ChannelOption>(_ option: Option, value: Option.Value) throws {
1266
0
            try self.channel.setOptionSync(option, value: value)
1267
0
        }
1268
1269
        @inlinable
1270
0
        public func getOption<Option: ChannelOption>(_ option: Option) throws -> Option.Value {
1271
0
            try self.channel.getOptionSync(option)
1272
0
        }
1273
    }
1274
1275
0
    public final var syncOptions: NIOSynchronousChannelOptions? {
1276
0
        SynchronousOptions(channel: self)
1277
0
    }
1278
}
1279
1280
// EmbeddedChannel is extremely _not_ Sendable. However, the Channel protocol
1281
// requires it to be. We are doing some runtime enforcement of correct use, but
1282
// ultimately we can't have the compiler validating this usage.
1283
extension EmbeddedChannel: @unchecked Sendable {}
1284
1285
@available(*, unavailable)
1286
extension EmbeddedChannel.LeftOverState: @unchecked Sendable {}
1287
1288
@available(*, unavailable)
1289
extension EmbeddedChannel.BufferState: @unchecked Sendable {}
1290
1291
@available(*, unavailable)
1292
extension EmbeddedChannel.SynchronousOptions: Sendable {}