Repository: apple/swift-nio
File: Sources/NIOPosix/SelectableEventLoop.swift (Swift)

Code before:
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import Dispatch
import NIOCore
import NIOConcurrencyHelpers
import _NIODataStructures
import Atomics

/// Execute the given closure and ensure we release all auto pools if needed.
@inlinable
internal func withAutoReleasePool<T>(_ execute: () throws -> T) rethrows -> T {
#if canImport(Darwin)
    return try autoreleasepool {
        try execute()
    }
#else
    return try execute()
#endif
}

/// Information about an EventLoop tick
public struct EventLoopTickInfo {
    /// The number of tasks which were executed in this tick
    public var numberOfTasks: Int
    /// The time at which the tick began
    public var startTime: NIODeadline

    internal init(numberOfTasks: Int, startTime: NIODeadline) {
        self.numberOfTasks = numberOfTasks
        self.startTime = startTime
    }
}

/// Implement this delegate to receive information about the EventLoop, such as each tick
public protocol EventLoopDelegate {
    /// Called after a tick has run
    /// This function is called after every tick - avoid long-running tasks here
    /// - Parameter info: Information about the tick, such as how many tasks were executed
    func processedTick(info: EventLoopTickInfo)
}

/// `EventLoop` implementation that uses a `Selector` to get notified once there is more I/O or tasks to process.
/// The whole processing of I/O and tasks is done by a `NIOThread` that is tied to the `SelectableEventLoop`. This `NIOThread`
/// is guaranteed to never change!
@usableFromInline
internal final class SelectableEventLoop: EventLoop {


Review comment (bug):
- needs `NIO` prefix
- lacks identifier for EventLoop
- as said above, I think we should probably periodically give the user one value which contains information from _all_ loops in a group together. Something like


```
public struct NIOEventLoopMetrics {
    public struct LoopMetric {
        var loopID: ...
        var numberOfTasks: Int
        var startTime: NIODeadline
    }
    
    public var loopMetrics: [LoopMetric]
}
```

I know this raises a lot of questions but I think we should think through how a user would use this and then settle on what NIO should provide. Just calling the delegate _on each loop_ for every tick is easy but requires a lot of heavy-lifting on the user's part which seems bound to go wrong, no?


Code after:
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import DequeModule
import Dispatch
import NIOCore
import NIOConcurrencyHelpers
import _NIODataStructures
import Atomics

/// Execute the given closure and ensure we release all auto pools if needed.
@inlinable
internal func withAutoReleasePool<T>(_ execute: () throws -> T) rethrows -> T {
#if canImport(Darwin)
    return try autoreleasepool {
        try execute()
    }
#else
    return try execute()
#endif
}

/// Information about an EventLoop tick
public struct NIOEventLoopTickInfo: Sendable, Hashable {
    /// The eventloop which ticked
    public var eventLoopID: ObjectIdentifier
    /// The number of tasks which were executed in this tick
    public var numberOfTasks: Int
    /// The time at which the tick began
    public var startTime: NIODeadline

    internal init(eventLoopID: ObjectIdentifier, numberOfTasks: Int, startTime: NIODeadline) {
        self.eventLoopID = eventLoopID
        self.numberOfTasks = numberOfTasks
        self.startTime = startTime
    }
}

/// Implement this delegate to receive information about the EventLoop, such as each tick
public protocol NIOEventLoopMetricsDelegate: Sendable {
    /// Called after a tick has run
    /// This function is called after every tick - avoid long-running tasks here
    /// - Warning: This function is called after every event loop tick and on the event loop thread. Any non-trivial work in this function will block the event loop and cause latency increases and performance degradation.
    /// - Parameter info: Information about the tick, such as how many tasks were executed
    func processedTick(info: NIOEventLoopTickInfo)
}

/// `EventLoop` implementation that uses a `Selector` to get notified once there is more I/O or tasks to process.