                 task.fail(error: EventLoopError.shutdown)
             }
         }
+        var nextReadyTask: ScheduledTask? = nil
         while lifecycleState != .closed {
             // Block until there are events to handle or the selector was woken up
             /* for macOS: in case any calls we make to Foundation put objects into an autoreleasepool */
             try withAutoReleasePool {
 
-                try selector.whenReady(strategy: currentSelectorStrategy()) { ev in
+                try selector.whenReady(strategy: currentSelectorStrategy(nextReadyTask: nextReadyTask)) { ev in
                     switch ev.registration {
                     case .serverSocketChannel(let chan, _):
                         self.handleEvent(ev.io, channel: chan)

                let now = DispatchTime.now()

                // Make a copy of the tasks so we can execute these while not holding the lock anymore
                while tasksCopy.count < tasksCopy.capacity, let task = scheduledTasks.peek(), task.readyIn(now) <= .nanoseconds(0) {
                    tasksCopy.append(task.task)

                    _ = scheduledTasks.pop()
                }

                tasksLock.unlock()

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


        }
    }

    private func currentSelectorStrategy(nextReadyTask: ScheduledTask?) -> SelectorStrategy {
        guard let sched = nextReadyTask else {
            // No tasks to handle so just block. If any tasks were added in the meantime wakeup(...) was called and so this
            // will directly unblock.
            return .block
        }

        let nextReady = sched.readyIn(DispatchTime.now())
        if nextReady <= .nanoseconds(0) {
            // Something is ready to be processed just do a non-blocking select of events.
            return .now
