|
| 1 | +import SystemPackage |
| 2 | +import WasmTypes |
| 3 | + |
| 4 | +#if canImport(Darwin) |
| 5 | + import Darwin |
| 6 | +#elseif canImport(Glibc) |
| 7 | + import Glibc |
| 8 | +#elseif canImport(Musl) |
| 9 | + import Musl |
| 10 | +#elseif canImport(Android) |
| 11 | + import Android |
| 12 | +#elseif os(Windows) |
| 13 | + import ucrt |
| 14 | +#elseif os(WASI) |
| 15 | + import WASILibc |
| 16 | +#else |
| 17 | + #error("Unsupported Platform") |
| 18 | +#endif |
| 19 | + |
| 20 | +extension FdTable { |
| 21 | + func fileDescriptor(fd: WASIAbi.Fd) throws -> FileDescriptor { |
| 22 | + guard case .file(let entry) = self[fd], let fd = (entry as? FdWASIEntry)?.fd else { |
| 23 | + throw WASIAbi.Errno.EBADF |
| 24 | + } |
| 25 | + |
| 26 | + return fd |
| 27 | + } |
| 28 | +} |
| 29 | + |
| 30 | +func poll( |
| 31 | + subscriptions: some Sequence<WASIAbi.Subscription>, |
| 32 | + events: UnsafeGuestBufferPointer<WASIAbi.Event>, |
| 33 | + _ fdTable: FdTable |
| 34 | +) throws -> WASIAbi.Size { |
| 35 | + #if os(Windows) |
| 36 | + throw WASIAbi.Errno.ENOTSUP |
| 37 | + #else |
| 38 | + var pollfds = [pollfd]() |
| 39 | + var fdUserData = [WASIAbi.UserData]() |
| 40 | + var timeoutMilliseconds = UInt.max |
| 41 | + var clockUserData: WASIAbi.UserData? |
| 42 | + |
| 43 | + for subscription in subscriptions { |
| 44 | + let union = subscription.union |
| 45 | + switch union { |
| 46 | + case .clock(let clock): |
| 47 | + timeoutMilliseconds = min(timeoutMilliseconds, .init(clock.timeout / 1_000_000)) |
| 48 | + clockUserData = subscription.userData |
| 49 | + case .fdRead(let fd): |
| 50 | + pollfds.append(.init(fd: try fdTable.fileDescriptor(fd: fd).rawValue, events: .init(POLLIN), revents: 0)) |
| 51 | + fdUserData.append(subscription.userData) |
| 52 | + case .fdWrite(let fd): |
| 53 | + pollfds.append(.init(fd: try fdTable.fileDescriptor(fd: fd).rawValue, events: .init(POLLOUT), revents: 0)) |
| 54 | + fdUserData.append(subscription.userData) |
| 55 | + } |
| 56 | + } |
| 57 | + |
| 58 | + let result = poll(&pollfds, .init(pollfds.count), .init(timeoutMilliseconds)) |
| 59 | + let err = errno // Preserve `errno` global immediately after `poll` |
| 60 | + var updatedEvents: WASIAbi.Size = 0 |
| 61 | + if result == 0, let clockUserData { |
| 62 | + updatedEvents += 1 |
| 63 | + events[0] = .init(userData: clockUserData, error: .SUCCESS, eventType: .clock, fdReadWrite: .init(nBytes: 0, flags: .init(rawValue: 0))) |
| 64 | + } else if result > 0 { |
| 65 | + for (i, fd) in pollfds.enumerated() { |
| 66 | + guard fd.revents != 0 else { continue } |
| 67 | + let eventIndex = updatedEvents |
| 68 | + updatedEvents += 1 |
| 69 | + let hangup: WASIAbi.Event.FdReadWrite.Flags = fd.revents & Int16(POLLHUP) != 0 ? [.hangup] : [] |
| 70 | + if fd.revents & Int16(POLLIN) != 0 || (fd.events & Int16(POLLIN) != 0 && fd.revents & Int16(POLLHUP) != 0) { |
| 71 | + events[.init(eventIndex)] = .init(userData: fdUserData[i], error: .SUCCESS, eventType: .fdRead, fdReadWrite: .init(nBytes: 0, flags: hangup)) |
| 72 | + } else if fd.revents & Int16(POLLOUT) != 0 || (fd.events & Int16(POLLOUT) != 0 && fd.revents & Int16(POLLHUP) != 0) { |
| 73 | + events[.init(eventIndex)] = .init(userData: fdUserData[i], error: .SUCCESS, eventType: .fdWrite, fdReadWrite: .init(nBytes: 0, flags: hangup)) |
| 74 | + } else if fd.revents & Int16(POLLERR | POLLNVAL) != 0 { |
| 75 | + let eventType: WASIAbi.EventType = fd.events & Int16(POLLIN) != 0 ? .fdRead : .fdWrite |
| 76 | + events[.init(eventIndex)] = .init(userData: fdUserData[i], error: .EBADF, eventType: eventType, fdReadWrite: .init(nBytes: 0, flags: [])) |
| 77 | + } |
| 78 | + } |
| 79 | + } else { |
| 80 | + switch err { |
| 81 | + case ENOMEM: throw WASIAbi.Errno.ENOMEM |
| 82 | + case EINTR: throw WASIAbi.Errno.EINTR |
| 83 | + case EINVAL: throw WASIAbi.Errno.EINVAL |
| 84 | + default: throw WASIAbi.Errno.ENOTSUP |
| 85 | + } |
| 86 | + } |
| 87 | + return updatedEvents |
| 88 | + #endif |
| 89 | +} |
0 commit comments