Skip to content

Commit 910395b

Browse files
Add blocking-based poll_oneoff to support clock_nanosleep (bjorn3#88)
This commit adds a blocking-based `poll_oneoff` implementation to support `clock_nanosleep` in wasi-libc. This implementation is very simple and only supports a single subscription for now but is enough for `clock_nanosleep` to work.
1 parent 1d0b60c commit 910395b

File tree

2 files changed

+85
-3
lines changed

2 files changed

+85
-3
lines changed

src/wasi.ts

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -813,9 +813,57 @@ export default class WASI {
813813
return wasi.ERRNO_BADF;
814814
}
815815
},
816-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
817-
poll_oneoff(in_, out, nsubscriptions) {
818-
throw "async io not supported";
816+
poll_oneoff(
817+
in_ptr: number,
818+
out_ptr: number,
819+
nsubscriptions: number,
820+
): number {
821+
if (nsubscriptions === 0) {
822+
return wasi.ERRNO_INVAL;
823+
}
824+
// TODO: For now, we only support a single subscription just to be enough for wasi-libc's
825+
// clock_nanosleep.
826+
if (nsubscriptions > 1) {
827+
debug.log("poll_oneoff: only a single subscription is supported");
828+
return wasi.ERRNO_NOTSUP;
829+
}
830+
831+
// Read a subscription from the in buffer
832+
const buffer = new DataView(self.inst.exports.memory.buffer);
833+
const s = wasi.Subscription.read_bytes(buffer, in_ptr);
834+
const eventtype = s.eventtype;
835+
const clockid = s.clockid;
836+
const timeout = s.timeout;
837+
// TODO: For now, we only support clock subscriptions.
838+
if (eventtype !== wasi.EVENTTYPE_CLOCK) {
839+
debug.log("poll_oneoff: only clock subscriptions are supported");
840+
return wasi.ERRNO_NOTSUP;
841+
}
842+
843+
// Select timer
844+
let getNow: (() => bigint) | undefined = undefined;
845+
if (clockid === wasi.CLOCKID_MONOTONIC) {
846+
getNow = () => BigInt(Math.round(performance.now() * 1_000_000));
847+
} else if (clockid === wasi.CLOCKID_REALTIME) {
848+
getNow = () => BigInt(new Date().getTime()) * 1_000_000n;
849+
} else {
850+
return wasi.ERRNO_INVAL;
851+
}
852+
853+
// Perform the wait
854+
const endTime =
855+
(s.flags & wasi.SUBCLOCKFLAGS_SUBSCRIPTION_CLOCK_ABSTIME) !== 0
856+
? timeout
857+
: getNow() + timeout;
858+
while (endTime > getNow()) {
859+
// block until the timeout is reached
860+
}
861+
862+
// Write an event to the out buffer
863+
const event = new wasi.Event(s.userdata, wasi.ERRNO_SUCCESS, eventtype);
864+
event.write_bytes(buffer, out_ptr);
865+
866+
return wasi.ERRNO_SUCCESS;
819867
},
820868
proc_exit(exit_code: number) {
821869
throw new WASIProcExit(exit_code);

src/wasi_defs.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,40 @@ export const EVENTRWFLAGS_FD_READWRITE_HANGUP = 1 << 0;
300300

301301
export const SUBCLOCKFLAGS_SUBSCRIPTION_CLOCK_ABSTIME = 1 << 0;
302302

303+
export class Subscription {
304+
constructor(
305+
public userdata: bigint,
306+
public eventtype: number,
307+
public clockid: number,
308+
public timeout: bigint,
309+
public flags: number,
310+
) {}
311+
312+
static read_bytes(view: DataView, ptr: number): Subscription {
313+
return new Subscription(
314+
view.getBigUint64(ptr, true),
315+
view.getUint8(ptr + 8),
316+
view.getUint32(ptr + 16, true),
317+
view.getBigUint64(ptr + 24, true),
318+
view.getUint16(ptr + 36, true),
319+
);
320+
}
321+
}
322+
323+
export class Event {
324+
constructor(
325+
public userdata: bigint,
326+
public error: number,
327+
public eventtype: number,
328+
) {}
329+
330+
write_bytes(view: DataView, ptr: number) {
331+
view.setBigUint64(ptr, this.userdata, true);
332+
view.setUint16(ptr + 8, this.error, true);
333+
view.setUint8(ptr + 10, this.eventtype);
334+
}
335+
}
336+
303337
export const SIGNAL_NONE = 0;
304338
export const SIGNAL_HUP = 1;
305339
export const SIGNAL_INT = 2;

0 commit comments

Comments
 (0)