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

Code before:
                            if task.readyTime.readyIn(now) <= .nanoseconds(0) {
                                self._scheduledTasks.pop()
                                self.tasksCopy.append(task)
                            } else {
                                nextReadyDeadline = task.readyTime
                                break
                            }
                        }
                    } else {
                        // Reset nextreadyDeadline to nil which means we will do a blocking select.
                        nextReadyDeadline = nil
                    }

                    if self.tasksCopy.isEmpty {
                        // We will not continue to loop here. We need to be woken if a new task is enqueued.
                        self._pendingTaskPop = false
                    }
                }

                // all pending tasks are set to occur in the future, so we can stop looping.
                if self.tasksCopy.isEmpty {
                    break
                }

                // Execute all the tasks that were submitted
                tasksProcessedInTick += self.tasksCopy.count
                for task in self.tasksCopy {
                    /* for macOS: in case any calls we make to Foundation put objects into an autoreleasepool */
                    withAutoReleasePool {
                        switch task.task {
                        case .function(let function):
                            function()

                        #if compiler(>=5.9)
                        case .unownedJob(let erasedUnownedJob):
                            if #available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *) {
                                erasedUnownedJob.unownedJob.runSynchronously(on: self.asUnownedSerialExecutor())
                            } else {
                                fatalError("Tried to run an UnownedJob without runtime support")
                            }
                        #endif
                        }
                    }
                }
                // Drop everything (but keep the capacity) so we can fill it again on the next iteration.
                self.tasksCopy.removeAll(keepingCapacity: true)
            }
            let tickInfo = NIOEventLoopTickInfo(eventLoopID: ObjectIdentifier(self), numberOfTasks: tasksProcessedInTick, startTime: tickStartTime)
            self.metricsDelegate?.processedTick(info: tickInfo)
        }


Review comment (bug):
We should be a little careful here. It's not really practically possible, but conceptually this addition can trap. We probably want to replace this with an `addingReportingOverflow` that, if we overflow, saturates instead.

Code after:

    private static func _popTasksLocked(
        immediateTasks: inout Deque<UnderlyingTask>,
        scheduledTasks: inout PriorityQueue<ScheduledTask>,
        tasksCopy: inout ContiguousArray<UnderlyingTask>,
        tasksCopyBatchSize: Int) -> NIODeadline? {
        // We expect empty tasksCopy, to put a new batch of tasks into
        assert(tasksCopy.isEmpty)

        var moreImmediateTasksToConsider = !immediateTasks.isEmpty
        var moreScheduledTasksToConsider = !scheduledTasks.isEmpty

        guard moreImmediateTasksToConsider || moreScheduledTasksToConsider else {
            // Reset nextReadyDeadline to nil which means we will do a blocking select.
            return nil
        }

        // We only fetch the time one time as this may be expensive and is generally good enough as if we miss anything we will just do a non-blocking select again anyway.
        let now: NIODeadline = .now()
        var nextScheduledTaskDeadline = now

        while moreImmediateTasksToConsider || moreScheduledTasksToConsider {
            // We pick one item from immediateTasks & scheduledTask per iteration of the loop.
            // This prevents one task queue starving the other.
            if moreImmediateTasksToConsider, tasksCopy.count < tasksCopyBatchSize, let task = immediateTasks.popFirst() {
                tasksCopy.append(task)
            } else {
                moreImmediateTasksToConsider = false
            }

            if moreScheduledTasksToConsider, tasksCopy.count < tasksCopyBatchSize, let task = scheduledTasks.peek() {
                if task.readyTime.readyIn(now) <= .nanoseconds(0) {
                    scheduledTasks.pop()
                    tasksCopy.append(.function(task.task))
                } else {
                    nextScheduledTaskDeadline = task.readyTime
                    moreScheduledTasksToConsider = false
                }
            } else {
                moreScheduledTasksToConsider = false
            }
        }

        let nextDeadline = immediateTasks.count > 0 ? now : nextScheduledTaskDeadline
        debugOnly {
            // The asserts are spun off to a separate functions to aid code clarity
            // and to remove mutable access to certain structures, e.g. `immediateTasks`.
            Self._popTasksLockedAssertInvariants(
                immediateTasks: immediateTasks,
                scheduledTasks: scheduledTasks,
                tasksCopy: tasksCopy,