Skip to content

Commit

Permalink
Implement WASI poll_oneoff
Browse files Browse the repository at this point in the history
  • Loading branch information
electrum committed Dec 24, 2024
1 parent 11295b6 commit c51cba6
Show file tree
Hide file tree
Showing 7 changed files with 306 additions and 12 deletions.
2 changes: 1 addition & 1 deletion docs/docs/usage/wasi.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ For the most up-to-date info, and to see what specific functions we support, see
| path_rename || |
| path_symlink || |
| path_unlink_file || |
| poll_oneoff | | |
| poll_oneoff | | |
| proc_exit || |
| proc_raise | 💀 | This function is no longer part of WASI. |
| random_get || |
Expand Down
2 changes: 0 additions & 2 deletions wasi-tests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,6 @@
<exclude>tests/rust/testsuite/fd_flags_set.wasm</exclude>
<!-- Linux and Jimfs always append with APPEND -->
<exclude>tests/c/testsuite/pwrite-with-append.wasm</exclude>
<!-- poll_oneoff is not implemented -->
<exclude>tests/rust/testsuite/poll_oneoff_stdio.wasm</exclude>
<!-- path_symlink is not implemented -->
<exclude>tests/rust/testsuite/dangling_symlink.wasm</exclude>
<exclude>tests/rust/testsuite/nofollow_errors.wasm</exclude>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.file.Files.createDirectory;
import static java.nio.file.Files.createSymbolicLink;
import static java.nio.file.Files.writeString;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import com.dylibso.chicory.log.Logger;
import com.dylibso.chicory.log.SystemLogger;
Expand All @@ -28,7 +31,9 @@
import java.util.List;
import java.util.Random;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;

@Timeout(10)
public class WasiPreview1Test {
private final Logger logger = new SystemLogger();

Expand Down Expand Up @@ -301,6 +306,134 @@ public void wasiReadLink() throws IOException {
}
}

@Test
public void wasiPollOneoffNoSubscriptions() {
var wasi = WasiPreview1.builder().build();
var memory = new ByteBufferMemory(new MemoryLimits(0));
int result = wasi.pollOneoff(memory, 0, 0, 0, 0);
assertEquals(WasiErrno.EINVAL.value(), result);
}

@Test
public void wasiPollOneoffStdinNoData() {
var stdin = new ByteArrayInputStream("".getBytes(UTF_8));
var wasiOpts = WasiOptions.builder().withStdin(stdin).build();
var wasi = WasiPreview1.builder().withOptions(wasiOpts).build();
var memory = new ByteBufferMemory(new MemoryLimits(1));
int nsubscriptions = 2;
int neventsPtr = 0;
int inPtr = 4;
int outPtr = inPtr + nsubscriptions * 48;
int in = inPtr;
memory.writeLong(in, 0x8888_7777_6666_5555L); // userdata
memory.writeByte(in + 8, WasiEventType.CLOCK);
memory.writeI32(in + 16, WasiClockId.REALTIME);
memory.writeLong(in + 16 + 8, MILLISECONDS.toNanos(200));
memory.writeLong(in + 16 + 16, 0); // precision
memory.writeShort(in + 16 + 24, (short) 0);
in += 48;
memory.writeLong(in, 0xAAAA_BBBB_CCCC_DDDDL); // userdata
memory.writeByte(in + 8, WasiEventType.FD_READ);
memory.writeI32(in + 16, 0); // fd
int result = wasi.pollOneoff(memory, inPtr, outPtr, nsubscriptions, neventsPtr);
assertEquals(WasiErrno.ESUCCESS.value(), result);
assertEquals(1, memory.readInt(neventsPtr));
assertEquals(0x8888_7777_6666_5555L, memory.readLong(outPtr));
assertEquals(WasiErrno.ESUCCESS.value(), memory.readShort(outPtr + 8));
assertEquals(WasiEventType.CLOCK, memory.read(outPtr + 10));
}

@Test
public void wasPollOneoffStdinWithData() {
var stdin = new ByteArrayInputStream("Hello, World!".getBytes(UTF_8));
var wasiOpts = WasiOptions.builder().withStdin(stdin).build();
var wasi = WasiPreview1.builder().withOptions(wasiOpts).build();
var memory = new ByteBufferMemory(new MemoryLimits(1));
int nsubscriptions = 1;
int neventsPtr = 0;
int inPtr = 4;
int outPtr = inPtr + nsubscriptions * 48;
int in = inPtr;
memory.writeLong(in, 0x8888_7777_6666_5555L); // userdata
memory.writeByte(in + 8, WasiEventType.FD_READ);
memory.writeI32(in + 16, 0); // fd
int result = wasi.pollOneoff(memory, inPtr, outPtr, nsubscriptions, neventsPtr);
assertEquals(WasiErrno.ESUCCESS.value(), result);
assertEquals(1, memory.readInt(neventsPtr));
assertEquals(0x8888_7777_6666_5555L, memory.readLong(outPtr));
assertEquals(WasiErrno.ESUCCESS.value(), memory.readShort(outPtr + 8));
assertEquals(WasiEventType.FD_READ, memory.read(outPtr + 10));
}

@Test
public void wasiPollOneoffRegularFile() {
try (var fs = newJimfs()) {
Path root = fs.getPath("test");
createDirectory(root);
var file = root.resolve("hello.txt");
writeString(file, "Hello, World!");
try (var wasi = wasiWithDirectory(root.toString(), root)) {
var memory = new ByteBufferMemory(new MemoryLimits(1));
int fdPtr = 1024;
int result = wasi.pathOpen(memory, 3, 0, "hello.txt", 0, 0, 0, 0, fdPtr);
assertEquals(WasiErrno.ESUCCESS.value(), result);
int fd = memory.readInt(fdPtr);
assertEquals(4, fd);

int nsubscriptions = 2;
int neventsPtr = 0;
int inPtr = 4;
int outPtr = inPtr + nsubscriptions * 48;
int in = inPtr;
memory.writeLong(in, 0x8888_7777_6666_5555L); // userdata
memory.writeByte(in + 8, WasiEventType.FD_READ);
memory.writeI32(in + 16, fd);
in += 48;
memory.writeLong(in, 0xAAAA_BBBB_CCCC_DDDDL); // userdata
memory.writeByte(in + 8, WasiEventType.FD_WRITE);
memory.writeI32(in + 16, fd);
result = wasi.pollOneoff(memory, inPtr, outPtr, nsubscriptions, neventsPtr);
assertEquals(WasiErrno.ESUCCESS.value(), result);
assertEquals(2, memory.readInt(neventsPtr));
int out = outPtr;
assertEquals(0x8888_7777_6666_5555L, memory.readLong(out));
assertEquals(WasiErrno.ESUCCESS.value(), memory.readShort(out + 8));
assertEquals(WasiEventType.FD_READ, memory.read(out + 10));
out += 32;
assertEquals(0xAAAA_BBBB_CCCC_DDDDL, memory.readLong(out));
assertEquals(WasiErrno.ESUCCESS.value(), memory.readShort(out + 8));
assertEquals(WasiEventType.FD_WRITE, memory.read(out + 10));
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}

@Test
public void wasiPollOneoffClockAbstime() {
long deadline = System.nanoTime() + MILLISECONDS.toNanos(25);
var wasi = WasiPreview1.builder().build();
var memory = new ByteBufferMemory(new MemoryLimits(1));
int nsubscriptions = 1;
int neventsPtr = 0;
int inPtr = 4;
int outPtr = inPtr + nsubscriptions * 48;
int in = inPtr;
memory.writeLong(in, 0x8888_7777_6666_5555L); // userdata
memory.writeByte(in + 8, WasiEventType.CLOCK);
memory.writeI32(in + 16, WasiClockId.MONOTONIC);
memory.writeLong(in + 16 + 8, deadline);
memory.writeLong(in + 16 + 16, 0); // precision
memory.writeShort(in + 16 + 24, (short) WasiSubClockFlags.SUBSCRIPTION_CLOCK_ABSTIME);
int result = wasi.pollOneoff(memory, inPtr, outPtr, nsubscriptions, neventsPtr);
assertEquals(WasiErrno.ESUCCESS.value(), result);
assertEquals(1, memory.readInt(neventsPtr));
assertEquals(0x8888_7777_6666_5555L, memory.readLong(outPtr));
assertEquals(WasiErrno.ESUCCESS.value(), memory.readShort(outPtr + 8));
assertEquals(WasiEventType.CLOCK, memory.read(outPtr + 10));
assertTrue(System.nanoTime() >= deadline);
}

private static FileSystem newJimfs() {
return Jimfs.newFileSystem(
Configuration.unix().toBuilder().setAttributeViews("unix").build());
Expand Down
6 changes: 4 additions & 2 deletions wasi/src/main/java/com/dylibso/chicory/wasi/Descriptors.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ final class Descriptors {
private final List<Descriptor> descriptors = new ArrayList<>();
private final NavigableSet<Integer> freeFds = new TreeSet<>();

Descriptors() {}

public Descriptor get(int fd) {
if (fd < 0 || fd >= descriptors.size()) {
return null;
Expand Down Expand Up @@ -90,6 +88,10 @@ public InStream(InputStream in) {
public int read(byte[] data) throws IOException {
return in.read(data);
}

public int available() throws IOException {
return in.available();
}
}

static final class OutStream implements Descriptor, DataWriter {
Expand Down
12 changes: 12 additions & 0 deletions wasi/src/main/java/com/dylibso/chicory/wasi/WasiEventType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.dylibso.chicory.wasi;

/**
* WASI <a href="https://github.com/WebAssembly/WASI/blob/v0.2.1/legacy/preview1/docs.md#eventtype">eventtype</a>
*/
final class WasiEventType {
private WasiEventType() {}

public static final byte CLOCK = 0;
public static final byte FD_READ = 1;
public static final byte FD_WRITE = 2;
}
Loading

0 comments on commit c51cba6

Please sign in to comment.