@volter/browser-wali 0.1.192 → 0.1.193
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/nanosleep.d.ts +9 -0
- package/dist/nanosleep.d.ts.map +1 -0
- package/dist/nanosleep.js +74 -0
- package/dist/nanosleep.js.map +1 -0
- package/dist/network-host.d.ts.map +1 -1
- package/dist/network-host.js +91 -40
- package/dist/network-host.js.map +1 -1
- package/dist/network-protocol.d.ts +1 -0
- package/dist/network-protocol.d.ts.map +1 -1
- package/dist/network-protocol.js +3 -0
- package/dist/network-protocol.js.map +1 -1
- package/dist/network.d.ts +3 -0
- package/dist/network.d.ts.map +1 -1
- package/dist/network.js +20 -6
- package/dist/network.js.map +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +5 -2
- package/dist/runtime.js.map +1 -1
- package/dist/signal-interruption.d.ts +1 -0
- package/dist/signal-interruption.d.ts.map +1 -1
- package/dist/signals.d.ts +5 -0
- package/dist/signals.d.ts.map +1 -1
- package/dist/signals.js +9 -0
- package/dist/signals.js.map +1 -1
- package/dist/system.d.ts +0 -1
- package/dist/system.d.ts.map +1 -1
- package/dist/system.js +6 -42
- package/dist/system.js.map +1 -1
- package/dist/thread-worker.js +6 -15
- package/dist/thread-worker.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
interface SleepHooks {
|
|
2
|
+
prepare?(): void;
|
|
3
|
+
checkFailure?(): void;
|
|
4
|
+
/** Existing root pthread-failure cadence, not a guest deadline. */
|
|
5
|
+
failureCheckInterval?: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function waliClockNanosleep(memory: () => WebAssembly.Memory, clock: number, flags: number, request: number, remaining: number, hooks?: SleepHooks): bigint;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=nanosleep.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nanosleep.d.ts","sourceRoot":"","sources":["../src/nanosleep.ts"],"names":[],"mappings":"AAQA,UAAU,UAAU;IAClB,OAAO,CAAC,IAAI,IAAI,CAAC;IACjB,YAAY,CAAC,IAAI,IAAI,CAAC;IACtB,mEAAmE;IACnE,oBAAoB,CAAC,EAAE,MAAM,CAAC;CAC/B;AAKD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,WAAW,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAChF,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,GAAE,UAAe,GAAG,MAAM,CAsDnF"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Clock validation and conversion extend the Emscripten clock rules in
|
|
2
|
+
// clocks.ts (MIT/NCSA, 862dc0d48acc4134a0088d27ac8f5dd18baba1cd).
|
|
3
|
+
// Linux nanosleep/clock_nanosleep interruption and remainder rules live here,
|
|
4
|
+
// behind syscalls; programs keep musl. No Linux implementation is copied.
|
|
5
|
+
import { monotonicMilliseconds, realtimeMilliseconds } from "./monotonic-clock.js";
|
|
6
|
+
import { hostWait } from "./host-idle.js";
|
|
7
|
+
import { waliSignalWaitState } from "./signal-interruption.js";
|
|
8
|
+
let sleepWord;
|
|
9
|
+
const nowNanoseconds = (clock) => BigInt(Math.round((clock === 0 ? realtimeMilliseconds() : monotonicMilliseconds()) * 1_000_000));
|
|
10
|
+
export function waliClockNanosleep(memory, clock, flags, request, remaining, hooks = {}) {
|
|
11
|
+
// RAW/coarse clocks do not provide Linux nsleep. CPU, alarm, boottime and
|
|
12
|
+
// TAI clocks require accounting/permissions or clock state we do not have.
|
|
13
|
+
if (clock !== 0 && clock !== 1)
|
|
14
|
+
return clock >= 2 && clock <= 11 && clock !== 3 ? -95n : -22n;
|
|
15
|
+
const range = (pointer) => Number.isSafeInteger(pointer) && pointer > 0
|
|
16
|
+
&& pointer + 16 <= memory().buffer.byteLength;
|
|
17
|
+
if (!range(request))
|
|
18
|
+
return -14n;
|
|
19
|
+
const input = new DataView(memory().buffer);
|
|
20
|
+
const seconds = input.getBigInt64(request, true), nanoseconds = input.getBigInt64(request + 8, true);
|
|
21
|
+
if (seconds < 0n || nanoseconds < 0n || nanoseconds >= 1000000000n)
|
|
22
|
+
return -22n;
|
|
23
|
+
const absolute = Boolean(flags & 1); // Linux nsleep interprets TIMER_ABSTIME.
|
|
24
|
+
const time = seconds * 1000000000n + nanoseconds;
|
|
25
|
+
const elapsedClock = absolute ? clock : 1;
|
|
26
|
+
const deadline = absolute ? time : nowNanoseconds(elapsedClock) + time;
|
|
27
|
+
if (deadline <= nowNanoseconds(elapsedClock))
|
|
28
|
+
return 0n;
|
|
29
|
+
// A page cannot implement a synchronous blocking syscall. Never pretend it
|
|
30
|
+
// slept when the runtime is used directly outside a worker.
|
|
31
|
+
if ("document" in globalThis)
|
|
32
|
+
return -38n;
|
|
33
|
+
if (deadline - nowNanoseconds(elapsedClock) >= 1000000n)
|
|
34
|
+
hooks.prepare?.();
|
|
35
|
+
for (;;) {
|
|
36
|
+
hooks.checkFailure?.();
|
|
37
|
+
const left = deadline - nowNanoseconds(elapsedClock);
|
|
38
|
+
if (left <= 0n)
|
|
39
|
+
return 0n;
|
|
40
|
+
let state = waliSignalWaitState();
|
|
41
|
+
if (state.pending) {
|
|
42
|
+
// Delivery distinguishes a caught signal from a default-ignored one.
|
|
43
|
+
// The interval left is sampled before the handler consumes guest time.
|
|
44
|
+
if (state.interruptSleep?.() ?? true) {
|
|
45
|
+
if (!absolute && remaining !== 0) {
|
|
46
|
+
if (!range(remaining))
|
|
47
|
+
return -14n;
|
|
48
|
+
const output = new DataView(memory().buffer);
|
|
49
|
+
output.setBigInt64(remaining, left / 1000000000n, true);
|
|
50
|
+
output.setBigInt64(remaining + 8, left % 1000000000n, true);
|
|
51
|
+
}
|
|
52
|
+
return -4n;
|
|
53
|
+
}
|
|
54
|
+
state = waliSignalWaitState();
|
|
55
|
+
}
|
|
56
|
+
const signal = state.guards[0];
|
|
57
|
+
const word = signal?.words ?? (sleepWord ??= new Int32Array(new SharedArrayBuffer(4)));
|
|
58
|
+
const index = signal?.index ?? 0, generation = signal?.value ?? Atomics.load(word, index);
|
|
59
|
+
const afterDelivery = deadline - nowNanoseconds(elapsedClock);
|
|
60
|
+
if (afterDelivery <= 0n)
|
|
61
|
+
return 0n;
|
|
62
|
+
const duration = Number(afterDelivery) / 1_000_000;
|
|
63
|
+
// Preserve the existing root fatal-thread observation cadence. Pthreads
|
|
64
|
+
// need no such poll and wait directly for signal generation or deadline.
|
|
65
|
+
const wait = Math.min(duration, hooks.failureCheckInterval ?? Infinity);
|
|
66
|
+
hostWait(word, index, generation, wait, undefined, {
|
|
67
|
+
resource: "clock", deadline: Date.now() + duration,
|
|
68
|
+
guards: state.guards, interrupt: { words: word, index },
|
|
69
|
+
});
|
|
70
|
+
// Generation wakes include ignored/masked signals and owner work; only a
|
|
71
|
+
// handler/default termination interrupts. Spurious wakes recheck deadline.
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=nanosleep.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nanosleep.js","sourceRoot":"","sources":["../src/nanosleep.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"network-host.d.ts","sourceRoot":"","sources":["../src/network-host.ts"],"names":[],"mappings":"AAOA,OAAO,
|
|
1
|
+
{"version":3,"file":"network-host.d.ts","sourceRoot":"","sources":["../src/network-host.ts"],"names":[],"mappings":"AAOA,OAAO,EAKL,KAAK,oBAAoB,EAG1B,MAAM,uBAAuB,CAAC;AAqF/B,8EAA8E;AAC9E,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,GAAG;IAC3E,OAAO,CAAC,KAAK,EAAE,YAAY,CAAC,oBAAoB,CAAC,GAAG,IAAI,CAAC;CAC1D,CAs7CA"}
|
package/dist/network-host.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// process ownership and port arbitration remain this kernel's adapters.
|
|
6
6
|
import { WaliEpollSet } from "./epoll-ready.js";
|
|
7
7
|
import { waliEpollNestingError } from "./epoll-nesting.js";
|
|
8
|
-
import { NETWORK_FD_MIN, NETWORK_PAYLOAD_BYTES, NetworkOperation, } from "./network-protocol.js";
|
|
8
|
+
import { NETWORK_FD_MIN, NETWORK_PAYLOAD_BYTES, NETWORK_RESPONSE_NO_RESTART, NetworkOperation, } from "./network-protocol.js";
|
|
9
9
|
import { WaliChildProcesses } from "./child-processes.js";
|
|
10
10
|
import { WaliSignals } from "./signals.js";
|
|
11
11
|
import { pollWaliSharedPipeState, waliSharedPipeGeneration } from "./shared-pipes.js";
|
|
@@ -157,11 +157,12 @@ export function createWaliNetworkSession(reply) {
|
|
|
157
157
|
// Reaping and completed I/O are irreversible. Claim blocking requests
|
|
158
158
|
// before it starts, so transport cancellation cannot lose status or bytes.
|
|
159
159
|
const operation = Atomics.load(control, 1);
|
|
160
|
-
const claimed = [NetworkOperation.ChildWait, NetworkOperation.PollMany, NetworkOperation.EpollWait, NetworkOperation.Receive].includes(operation)
|
|
160
|
+
const claimed = [NetworkOperation.ChildWait, NetworkOperation.PollMany, NetworkOperation.EpollWait, NetworkOperation.Receive, NetworkOperation.Accept].includes(operation)
|
|
161
161
|
|| (operation === NetworkOperation.Send && sockets.get(Atomics.load(control, 5))?.kind === "event");
|
|
162
162
|
if (claimed && Atomics.compareExchange(control, 0, requestId, -requestId) !== requestId)
|
|
163
163
|
return;
|
|
164
164
|
let result = -38;
|
|
165
|
+
let restartable = true;
|
|
165
166
|
let output = new Uint8Array();
|
|
166
167
|
let checkedActivity = activity ? Atomics.load(activity, 0) : 0;
|
|
167
168
|
const parked = (state) => {
|
|
@@ -178,7 +179,9 @@ export function createWaliNetworkSession(reply) {
|
|
|
178
179
|
const inputLength = Atomics.load(control, 3);
|
|
179
180
|
const args = Array.from({ length: 10 }, (_, index) => Atomics.load(control, 5 + index));
|
|
180
181
|
const input = payload.slice(0, inputLength);
|
|
181
|
-
|
|
182
|
+
const response = await operate(operation, args, input, process, parked);
|
|
183
|
+
({ result, output } = response);
|
|
184
|
+
restartable = response.restartable !== false;
|
|
182
185
|
if ([NetworkOperation.EpollControl, NetworkOperation.Send, NetworkOperation.Receive,
|
|
183
186
|
NetworkOperation.Close, NetworkOperation.Shutdown, NetworkOperation.TimerFdSetTime].includes(operation))
|
|
184
187
|
notifyDescriptors();
|
|
@@ -202,6 +205,7 @@ export function createWaliNetworkSession(reply) {
|
|
|
202
205
|
payload.set(output);
|
|
203
206
|
Atomics.store(control, 2, result);
|
|
204
207
|
Atomics.store(control, 4, output.byteLength);
|
|
208
|
+
Atomics.store(control, NETWORK_RESPONSE_NO_RESTART, restartable ? 0 : 1);
|
|
205
209
|
Atomics.store(control, 0, requestId + 1);
|
|
206
210
|
Atomics.notify(control, 0);
|
|
207
211
|
}
|
|
@@ -293,7 +297,7 @@ export function createWaliNetworkSession(reply) {
|
|
|
293
297
|
if (operation === NetworkOperation.Listen)
|
|
294
298
|
return scalar(await listenSocket(socket, args[1]));
|
|
295
299
|
if (operation === NetworkOperation.Accept)
|
|
296
|
-
return acceptSocket(socket, args[1]);
|
|
300
|
+
return acceptSocket(socket, args[1], signalMask(args[2] ?? 0, args[3] ?? 0), parked);
|
|
297
301
|
if (operation === NetworkOperation.Send) {
|
|
298
302
|
if ((args[1] & ~(0x40 | 0x4000)) !== 0)
|
|
299
303
|
return scalar(-95);
|
|
@@ -320,8 +324,7 @@ export function createWaliNetworkSession(reply) {
|
|
|
320
324
|
if (operation === NetworkOperation.GetSocketOption)
|
|
321
325
|
return socketOption(socket, args[1], args[2]);
|
|
322
326
|
if (operation === NetworkOperation.SetSocketOption) {
|
|
323
|
-
|
|
324
|
-
return scalar(socket.kind === "event" || socket.kind === "timer" ? -88 : -92);
|
|
327
|
+
return scalar(setSocketOption(socket, args[1], args[2], input));
|
|
325
328
|
}
|
|
326
329
|
if (operation === NetworkOperation.Duplicate)
|
|
327
330
|
return scalar(duplicateSocket(socket));
|
|
@@ -392,7 +395,9 @@ export function createWaliNetworkSession(reply) {
|
|
|
392
395
|
const done = () => { if (timer !== undefined)
|
|
393
396
|
clearTimeout(timer); descriptorWaiters.delete(done); resolve(); };
|
|
394
397
|
descriptorWaiters.add(done);
|
|
395
|
-
|
|
398
|
+
// Browser timers use signed-int32 milliseconds. A long guest deadline is
|
|
399
|
+
// rechecked after each interval; this cap is not an I/O watchdog.
|
|
400
|
+
const timer = Number.isFinite(delay) ? setTimeout(done, Math.min(0x7fff_ffff, Math.max(0, delay))) : undefined;
|
|
396
401
|
});
|
|
397
402
|
}
|
|
398
403
|
function duplicateEpoll(fd, minimum, closeOnExec = false) {
|
|
@@ -715,7 +720,6 @@ export function createWaliNetworkSession(reply) {
|
|
|
715
720
|
listening: false,
|
|
716
721
|
backlog: 0,
|
|
717
722
|
pending: [],
|
|
718
|
-
acceptWaiters: [],
|
|
719
723
|
peerClosed: false,
|
|
720
724
|
};
|
|
721
725
|
}
|
|
@@ -951,32 +955,48 @@ export function createWaliNetworkSession(reply) {
|
|
|
951
955
|
listenersByPort.set(socket.bound.port, socket);
|
|
952
956
|
return 0;
|
|
953
957
|
}
|
|
954
|
-
async function acceptSocket(socket, flags) {
|
|
958
|
+
async function acceptSocket(socket, flags, mask, parked) {
|
|
955
959
|
if (socket.kind !== "tcp" || !socket.listening)
|
|
956
960
|
return scalar(-22);
|
|
957
961
|
if ((flags & ~0x8_0800) !== 0)
|
|
958
962
|
return scalar(-22);
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
return scalar(-11);
|
|
962
|
-
if (!accepted) {
|
|
963
|
-
accepted = await new Promise((resolve) => socket.acceptWaiters.push(resolve));
|
|
964
|
-
}
|
|
965
|
-
if (!accepted)
|
|
966
|
-
return scalar(-9);
|
|
967
|
-
accepted.nonblocking = Boolean(flags & 0x800);
|
|
968
|
-
let fd;
|
|
963
|
+
const deadline = receiveDeadline(socket);
|
|
964
|
+
activeIO.set(socket, (activeIO.get(socket) ?? 0) + 1);
|
|
969
965
|
try {
|
|
970
|
-
|
|
966
|
+
for (;;) {
|
|
967
|
+
if (disposed || socket.closed)
|
|
968
|
+
return scalar(-9);
|
|
969
|
+
const accepted = socket.pending.shift();
|
|
970
|
+
if (accepted) {
|
|
971
|
+
accepted.nonblocking = Boolean(flags & 0x800);
|
|
972
|
+
let fd;
|
|
973
|
+
try {
|
|
974
|
+
fd = takeFd();
|
|
975
|
+
}
|
|
976
|
+
catch (error) {
|
|
977
|
+
socket.pending.unshift(accepted);
|
|
978
|
+
throw error;
|
|
979
|
+
}
|
|
980
|
+
sockets.set(fd, accepted);
|
|
981
|
+
socketDescriptorFlags.set(fd, flags & 0x80000 ? 1 : 0);
|
|
982
|
+
return { result: fd, output: socketAddress(accepted.domain, accepted.domain === 10 ? "::" : "0.0.0.0", 0) };
|
|
983
|
+
}
|
|
984
|
+
if (socket.nonblocking || monotonicMilliseconds() >= deadline)
|
|
985
|
+
return scalar(-11);
|
|
986
|
+
if (signals && (signals.pending() & ~mask) !== 0n)
|
|
987
|
+
return { ...scalar(-4), restartable: deadline === Infinity };
|
|
988
|
+
parked(2); // listener ingress or configured deadline can wake the call
|
|
989
|
+
try {
|
|
990
|
+
await waitForDescriptorChange(deadline - monotonicMilliseconds());
|
|
991
|
+
}
|
|
992
|
+
finally {
|
|
993
|
+
parked(0);
|
|
994
|
+
}
|
|
995
|
+
}
|
|
971
996
|
}
|
|
972
|
-
|
|
973
|
-
socket
|
|
974
|
-
throw error;
|
|
997
|
+
finally {
|
|
998
|
+
releaseActiveIO(socket);
|
|
975
999
|
}
|
|
976
|
-
sockets.set(fd, accepted);
|
|
977
|
-
socketDescriptorFlags.set(fd, flags & 0x80000 ? 1 : 0);
|
|
978
|
-
// The ingress wire does not yet carry the remote endpoint. Do not invent it.
|
|
979
|
-
return { result: fd, output: socketAddress(accepted.domain, accepted.domain === 10 ? "::" : "0.0.0.0", 0) };
|
|
980
1000
|
}
|
|
981
1001
|
function requestLoopbackListen(port) {
|
|
982
1002
|
if (!loopbackPort)
|
|
@@ -1058,17 +1078,14 @@ export function createWaliNetworkSession(reply) {
|
|
|
1058
1078
|
listening: false,
|
|
1059
1079
|
backlog: 0,
|
|
1060
1080
|
pending: [],
|
|
1061
|
-
acceptWaiters: [],
|
|
1062
1081
|
loopbackConnectionId: message.connectionId,
|
|
1063
1082
|
peerClosed: false,
|
|
1064
1083
|
...(listener.bound ? { bound: listener.bound } : {}),
|
|
1084
|
+
// Clone listener options when the connection is created, not at accept.
|
|
1085
|
+
...(listener.receiveTimeoutMicros !== undefined ? { receiveTimeoutMicros: listener.receiveTimeoutMicros } : {}),
|
|
1065
1086
|
};
|
|
1066
1087
|
loopbackConnections.set(message.connectionId, accepted);
|
|
1067
|
-
|
|
1068
|
-
if (waiter)
|
|
1069
|
-
waiter(accepted);
|
|
1070
|
-
else
|
|
1071
|
-
listener.pending.push(accepted);
|
|
1088
|
+
listener.pending.push(accepted);
|
|
1072
1089
|
notifyDescriptors();
|
|
1073
1090
|
}
|
|
1074
1091
|
function postLoopback(message) {
|
|
@@ -1218,6 +1235,7 @@ export function createWaliNetworkSession(reply) {
|
|
|
1218
1235
|
}
|
|
1219
1236
|
return { result: collected, output };
|
|
1220
1237
|
};
|
|
1238
|
+
const deadline = receiveDeadline(socket);
|
|
1221
1239
|
activeIO.set(socket, (activeIO.get(socket) ?? 0) + 1);
|
|
1222
1240
|
try {
|
|
1223
1241
|
for (;;) {
|
|
@@ -1252,13 +1270,17 @@ export function createWaliNetworkSession(reply) {
|
|
|
1252
1270
|
// WAITALL does not turn a nonblocking descriptor into a blocking one.
|
|
1253
1271
|
if (socket.nonblocking || Boolean(flags & MSG_DONTWAIT))
|
|
1254
1272
|
return partial(response);
|
|
1255
|
-
if (
|
|
1256
|
-
return partial(scalar(-
|
|
1257
|
-
|
|
1273
|
+
if (monotonicMilliseconds() >= deadline)
|
|
1274
|
+
return partial(scalar(-11));
|
|
1275
|
+
if (signals && (signals.pending() & ~mask) !== 0n) {
|
|
1276
|
+
const response = partial(scalar(-4));
|
|
1277
|
+
return response.result === -4 ? { ...response, restartable: deadline === Infinity } : response;
|
|
1278
|
+
}
|
|
1279
|
+
let delay = deadline - monotonicMilliseconds();
|
|
1258
1280
|
if (socket.kind === "timer" && socket.deadlineNanoseconds !== undefined) {
|
|
1259
|
-
delay = Number(timerRemaining(socket)) / 1_000_000;
|
|
1281
|
+
delay = Math.min(delay, Number(timerRemaining(socket)) / 1_000_000);
|
|
1260
1282
|
}
|
|
1261
|
-
parked(socket.kind === "tcp" || socket.kind === "dns"
|
|
1283
|
+
parked(deadline !== Infinity || socket.kind === "tcp" || socket.kind === "dns"
|
|
1262
1284
|
|| (socket.kind === "timer" && socket.deadlineNanoseconds !== undefined) ? 2 : 1);
|
|
1263
1285
|
// Install the wake before yielding; arrivals cannot slip between the
|
|
1264
1286
|
// scan and subscription. Signals use the existing caller-mask guard.
|
|
@@ -1437,6 +1459,30 @@ export function createWaliNetworkSession(reply) {
|
|
|
1437
1459
|
socketDescriptorFlags.set(fd, closeOnExec ? 1 : 0);
|
|
1438
1460
|
return fd;
|
|
1439
1461
|
}
|
|
1462
|
+
/** Linux socket(7) timeouts use the existing owner, not transport watchdogs. */
|
|
1463
|
+
function receiveDeadline(socket) {
|
|
1464
|
+
const timeout = socket.kind === "event" || socket.kind === "timer" ? undefined : socket.receiveTimeoutMicros;
|
|
1465
|
+
return timeout === undefined ? Infinity : monotonicMilliseconds() + Number(timeout) / 1000;
|
|
1466
|
+
}
|
|
1467
|
+
function setSocketOption(socket, level, name, input) {
|
|
1468
|
+
if (socket.kind === "event" || socket.kind === "timer")
|
|
1469
|
+
return -88;
|
|
1470
|
+
if (level !== 1 || (name !== 20 && name !== 66))
|
|
1471
|
+
return -92;
|
|
1472
|
+
if (input.byteLength < 16)
|
|
1473
|
+
return -22;
|
|
1474
|
+
const view = new DataView(input.buffer, input.byteOffset, input.byteLength);
|
|
1475
|
+
const seconds = view.getBigInt64(0, true), micros = view.getBigInt64(8, true);
|
|
1476
|
+
if (micros < 0n || micros >= 1000000n)
|
|
1477
|
+
return -33; // EDOM, sock_set_timeout
|
|
1478
|
+
if (seconds < 0n)
|
|
1479
|
+
socket.receiveTimeoutMicros = 0n;
|
|
1480
|
+
else if (seconds === 0n && micros === 0n)
|
|
1481
|
+
delete socket.receiveTimeoutMicros;
|
|
1482
|
+
else
|
|
1483
|
+
socket.receiveTimeoutMicros = seconds * 1000000n + micros;
|
|
1484
|
+
return 0;
|
|
1485
|
+
}
|
|
1440
1486
|
// libsyscall.js only supports SO_ERROR. Additional read-only Linux queries
|
|
1441
1487
|
// below expose actual owner state, never a fabricated successful setting.
|
|
1442
1488
|
function socketOption(socket, level, name) {
|
|
@@ -1444,6 +1490,13 @@ export function createWaliNetworkSession(reply) {
|
|
|
1444
1490
|
return scalar(-88);
|
|
1445
1491
|
if (level !== 1)
|
|
1446
1492
|
return scalar(-92);
|
|
1493
|
+
if (name === 20 || name === 66) {
|
|
1494
|
+
const duration = socket.receiveTimeoutMicros ?? 0n;
|
|
1495
|
+
const output = new Uint8Array(16), view = new DataView(output.buffer);
|
|
1496
|
+
view.setBigInt64(0, duration / 1000000n, true);
|
|
1497
|
+
view.setBigInt64(8, duration % 1000000n, true);
|
|
1498
|
+
return { result: 0, output };
|
|
1499
|
+
}
|
|
1447
1500
|
let value;
|
|
1448
1501
|
switch (name) {
|
|
1449
1502
|
case 3:
|
|
@@ -1510,8 +1563,6 @@ export function createWaliNetworkSession(reply) {
|
|
|
1510
1563
|
if (socket.listening && socket.bound) {
|
|
1511
1564
|
listenersByPort.delete(socket.bound.port);
|
|
1512
1565
|
postLoopback({ type: "unlisten", port: socket.bound.port });
|
|
1513
|
-
for (const resolve of socket.acceptWaiters.splice(0))
|
|
1514
|
-
resolve(undefined);
|
|
1515
1566
|
for (const accepted of socket.pending.splice(0))
|
|
1516
1567
|
closeSocket(accepted);
|
|
1517
1568
|
}
|