Skip to content

Commit 712068c

Browse files
authored
Fix poll_oneoff and more I/O syscalls in WASI 0.1 (#187)
Removed following XFAILs: `pwrite-with-append`, `renumber`, `stdio`, `remove_directory_trailing_slashes`, `sched_yield`, `sock_shutdown-invalid_fd`, `sock_shutdown-not_sock` Testing Swift Concurrency for WebAssembly requires presence of `poll_oneoff`, which is absent in WasmKit. This change provides an implementation for POSIX hosts according to the ABI specified in WebAssembly System Interface standard https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md#poll_oneoff rdar://149935761
1 parent ad5a8a1 commit 712068c

7 files changed

Lines changed: 436 additions & 79 deletions

File tree

Sources/WASI/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ add_wasmkit_library(WASI
1515
FileSystem.swift
1616
GuestMemorySupport.swift
1717
Clock.swift
18+
Poll.swift
1819
RandomBufferGenerator.swift
1920
WASI.swift
2021
WASIBridgeToHost.swift

Sources/WASI/Platform/Directory.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ extension DirEntry: WASIDir, FdWASIEntry {
111111
#if os(Windows)
112112
throw WASIAbi.Errno.ENOSYS
113113
#else
114+
let path = SandboxPrimitives.stripDirSuffix(path)
114115
let result = try SandboxPrimitives.openParent(start: fd, path: path)
115116
let dir = result.parentFd
116117
let basename = result.basename

Sources/WASI/Poll.swift

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
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

Comments
 (0)