             throw IOError(errnoCode: EBADF, reason: "can't call whenReady for selector as it's \(self.lifecycleState).")
         }
 
-#if os(Linux)
+
+#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
+        let timespec = Selector.toKQueueTimeSpec(strategy: strategy)
+        let ready = try timespec.withUnsafeOptionalPointer { ts in
+            Int(try KQueue.kevent(kq: self.selectorFD, changelist: nil, nchanges: 0, eventlist: events, nevents: Int32(eventsCapacity), timeout: ts))
+        }
+
+        // start with no deregistrations happened
+        self.deregistrationsHappened = false
+        // temporary workaround to stop us delivering outdated events; possibly set in `deregister`
+        for i in 0..<ready where !self.deregistrationsHappened {
+            let ev = events[i]
+            let filter = Int32(ev.filter)
+            guard Int32(ev.flags) & EV_ERROR == 0 else {
+                throw IOError(errnoCode: Int32(ev.data), reason: "kevent returned with EV_ERROR set: \(String(describing: ev))")
+            }
+            guard filter != EVFILT_USER, let registration = registrations[Int(ev.ident)] else {
+                continue
+            }
+            var selectorEvent: SelectorEventSet = ._none
+            switch filter {
+            case EVFILT_READ:
+                selectorEvent.formUnion(.read)
+                fallthrough // falling through here as `EVFILT_READ` also delivers `EV_EOF` (meaning `.readEOF`)
+            case EVFILT_EXCEPT:
+                if Int32(ev.flags) & EV_EOF != 0 && registration.interested.contains(.readEOF) {
+                    // we only add `.readEOF` if it happened and the user asked for it
+                    selectorEvent.formUnion(.readEOF)
+                }
+            case EVFILT_WRITE:
+                selectorEvent.formUnion(.write)
+            default:
+                // We only use EVFILT_USER, EVFILT_READ, EVFILT_EXCEPT and EVFILT_WRITE.
+                fatalError("unexpected filter \(ev.filter)")
+            }
+            if ev.fflags != 0 {
+                selectorEvent.formUnion(.reset)
+            }
+            // we can only verify the events for i == 0 as for i > 0 the user might have changed the registrations since then.
+            assert(i != 0 || selectorEvent.isSubset(of: registration.interested), "selectorEvent: \(selectorEvent), registration: \(registration)")
+
+            // in any case we only want what the user is currently registered for & what we got
+            selectorEvent = selectorEvent.intersection(registration.interested)
+
+            guard selectorEvent != ._none else {
+                continue
+            }
+            try body((SelectorEvent(io: selectorEvent, registration: registration)))
+        }
+
+        growEventArrayIfNeeded(ready: ready)
+#else
         let ready: Int
 
         switch strategy {

            }
        }
        growEventArrayIfNeeded(ready: ready)
#else
        let timespec = Selector.toKQueueTimeSpec(strategy: strategy)
        let ready = try timespec.withUnsafeOptionalPointer { ts in
            Int(try KQueue.kevent(kq: self.selectorFD, changelist: nil, nchanges: 0, eventlist: events, nevents: Int32(eventsCapacity), timeout: ts))
        }

        // start with no deregistrations happened
        self.deregistrationsHappened = false
        // temporary workaround to stop us delivering outdated events; possibly set in `deregister`
        for i in 0..<ready where !self.deregistrationsHappened {
            let ev = events[i]
            let filter = Int32(ev.filter)
            guard Int32(ev.flags) & EV_ERROR == 0 else {
                throw IOError(errnoCode: Int32(ev.data), reason: "kevent returned with EV_ERROR set: \(String(describing: ev))")
            }
            guard filter != EVFILT_USER, let registration = registrations[Int(ev.ident)] else {
                continue
            }
            var selectorEvent: SelectorEventSet = ._none
            switch filter {
            case EVFILT_READ:
                selectorEvent.formUnion(.read)
                fallthrough // falling through here as `EVFILT_READ` also delivers `EV_EOF` (meaning `.readEOF`)
            case EVFILT_EXCEPT:
                if Int32(ev.flags) & EV_EOF != 0 && registration.interested.contains(.readEOF) {
                    // we only add `.readEOF` if it happened and the user asked for it
                    selectorEvent.formUnion(.readEOF)
                }
            case EVFILT_WRITE:
                selectorEvent.formUnion(.write)
            default:
                // We only use EVFILT_USER, EVFILT_READ, EVFILT_EXCEPT and EVFILT_WRITE.
                fatalError("unexpected filter \(ev.filter)")
            }
            if ev.fflags != 0 {
                selectorEvent.formUnion(.reset)
            }
            // we can only verify the events for i == 0 as for i > 0 the user might have changed the registrations since then.
            assert(i != 0 || selectorEvent.isSubset(of: registration.interested), "selectorEvent: \(selectorEvent), registration: \(registration)")

            // in any case we only want what the user is currently registered for & what we got
            selectorEvent = selectorEvent.intersection(registration.interested)

            guard selectorEvent != ._none else {
                continue
            }
            try body((SelectorEvent(io: selectorEvent, registration: registration)))
        }

        growEventArrayIfNeeded(ready: ready)
#endif
    }


internal class Selector<R: Registration> {
    private var lifecycleState: SelectorLifecycleState

    #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
    private typealias EventType = kevent
    #else
    private typealias EventType = Epoll.epoll_event
    private var earliestTimer: NIODeadline = .distantFuture
    #endif

    private var eventsCapacity = 64
