@volter/browser-wali 0.1.192 → 0.1.194
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 +99 -42
- package/dist/network-host.js.map +1 -1
- package/dist/network-protocol.d.ts +2 -0
- package/dist/network-protocol.d.ts.map +1 -1
- package/dist/network-protocol.js +6 -0
- package/dist/network-protocol.js.map +1 -1
- package/dist/network.d.ts +4 -0
- package/dist/network.d.ts.map +1 -1
- package/dist/network.js +28 -13
- 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,EAML,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,CA47CA"}
|
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_FLAGS, 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,13 @@ 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 messageFlags = 0;
|
|
166
|
+
let restartable = true;
|
|
165
167
|
let output = new Uint8Array();
|
|
166
168
|
let checkedActivity = activity ? Atomics.load(activity, 0) : 0;
|
|
167
169
|
const parked = (state) => {
|
|
@@ -178,7 +180,10 @@ export function createWaliNetworkSession(reply) {
|
|
|
178
180
|
const inputLength = Atomics.load(control, 3);
|
|
179
181
|
const args = Array.from({ length: 10 }, (_, index) => Atomics.load(control, 5 + index));
|
|
180
182
|
const input = payload.slice(0, inputLength);
|
|
181
|
-
|
|
183
|
+
const response = await operate(operation, args, input, process, parked);
|
|
184
|
+
({ result, output } = response);
|
|
185
|
+
messageFlags = response.messageFlags ?? 0;
|
|
186
|
+
restartable = response.restartable !== false;
|
|
182
187
|
if ([NetworkOperation.EpollControl, NetworkOperation.Send, NetworkOperation.Receive,
|
|
183
188
|
NetworkOperation.Close, NetworkOperation.Shutdown, NetworkOperation.TimerFdSetTime].includes(operation))
|
|
184
189
|
notifyDescriptors();
|
|
@@ -202,6 +207,8 @@ export function createWaliNetworkSession(reply) {
|
|
|
202
207
|
payload.set(output);
|
|
203
208
|
Atomics.store(control, 2, result);
|
|
204
209
|
Atomics.store(control, 4, output.byteLength);
|
|
210
|
+
Atomics.store(control, NETWORK_RESPONSE_FLAGS, messageFlags);
|
|
211
|
+
Atomics.store(control, NETWORK_RESPONSE_NO_RESTART, restartable ? 0 : 1);
|
|
205
212
|
Atomics.store(control, 0, requestId + 1);
|
|
206
213
|
Atomics.notify(control, 0);
|
|
207
214
|
}
|
|
@@ -293,7 +300,7 @@ export function createWaliNetworkSession(reply) {
|
|
|
293
300
|
if (operation === NetworkOperation.Listen)
|
|
294
301
|
return scalar(await listenSocket(socket, args[1]));
|
|
295
302
|
if (operation === NetworkOperation.Accept)
|
|
296
|
-
return acceptSocket(socket, args[1]);
|
|
303
|
+
return acceptSocket(socket, args[1], signalMask(args[2] ?? 0, args[3] ?? 0), parked);
|
|
297
304
|
if (operation === NetworkOperation.Send) {
|
|
298
305
|
if ((args[1] & ~(0x40 | 0x4000)) !== 0)
|
|
299
306
|
return scalar(-95);
|
|
@@ -320,8 +327,7 @@ export function createWaliNetworkSession(reply) {
|
|
|
320
327
|
if (operation === NetworkOperation.GetSocketOption)
|
|
321
328
|
return socketOption(socket, args[1], args[2]);
|
|
322
329
|
if (operation === NetworkOperation.SetSocketOption) {
|
|
323
|
-
|
|
324
|
-
return scalar(socket.kind === "event" || socket.kind === "timer" ? -88 : -92);
|
|
330
|
+
return scalar(setSocketOption(socket, args[1], args[2], input));
|
|
325
331
|
}
|
|
326
332
|
if (operation === NetworkOperation.Duplicate)
|
|
327
333
|
return scalar(duplicateSocket(socket));
|
|
@@ -392,7 +398,9 @@ export function createWaliNetworkSession(reply) {
|
|
|
392
398
|
const done = () => { if (timer !== undefined)
|
|
393
399
|
clearTimeout(timer); descriptorWaiters.delete(done); resolve(); };
|
|
394
400
|
descriptorWaiters.add(done);
|
|
395
|
-
|
|
401
|
+
// Browser timers use signed-int32 milliseconds. A long guest deadline is
|
|
402
|
+
// rechecked after each interval; this cap is not an I/O watchdog.
|
|
403
|
+
const timer = Number.isFinite(delay) ? setTimeout(done, Math.min(0x7fff_ffff, Math.max(0, delay))) : undefined;
|
|
396
404
|
});
|
|
397
405
|
}
|
|
398
406
|
function duplicateEpoll(fd, minimum, closeOnExec = false) {
|
|
@@ -715,7 +723,6 @@ export function createWaliNetworkSession(reply) {
|
|
|
715
723
|
listening: false,
|
|
716
724
|
backlog: 0,
|
|
717
725
|
pending: [],
|
|
718
|
-
acceptWaiters: [],
|
|
719
726
|
peerClosed: false,
|
|
720
727
|
};
|
|
721
728
|
}
|
|
@@ -951,32 +958,48 @@ export function createWaliNetworkSession(reply) {
|
|
|
951
958
|
listenersByPort.set(socket.bound.port, socket);
|
|
952
959
|
return 0;
|
|
953
960
|
}
|
|
954
|
-
async function acceptSocket(socket, flags) {
|
|
961
|
+
async function acceptSocket(socket, flags, mask, parked) {
|
|
955
962
|
if (socket.kind !== "tcp" || !socket.listening)
|
|
956
963
|
return scalar(-22);
|
|
957
964
|
if ((flags & ~0x8_0800) !== 0)
|
|
958
965
|
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;
|
|
966
|
+
const deadline = receiveDeadline(socket);
|
|
967
|
+
activeIO.set(socket, (activeIO.get(socket) ?? 0) + 1);
|
|
969
968
|
try {
|
|
970
|
-
|
|
969
|
+
for (;;) {
|
|
970
|
+
if (disposed || socket.closed)
|
|
971
|
+
return scalar(-9);
|
|
972
|
+
const accepted = socket.pending.shift();
|
|
973
|
+
if (accepted) {
|
|
974
|
+
accepted.nonblocking = Boolean(flags & 0x800);
|
|
975
|
+
let fd;
|
|
976
|
+
try {
|
|
977
|
+
fd = takeFd();
|
|
978
|
+
}
|
|
979
|
+
catch (error) {
|
|
980
|
+
socket.pending.unshift(accepted);
|
|
981
|
+
throw error;
|
|
982
|
+
}
|
|
983
|
+
sockets.set(fd, accepted);
|
|
984
|
+
socketDescriptorFlags.set(fd, flags & 0x80000 ? 1 : 0);
|
|
985
|
+
return { result: fd, output: socketAddress(accepted.domain, accepted.domain === 10 ? "::" : "0.0.0.0", 0) };
|
|
986
|
+
}
|
|
987
|
+
if (socket.nonblocking || monotonicMilliseconds() >= deadline)
|
|
988
|
+
return scalar(-11);
|
|
989
|
+
if (signals && (signals.pending() & ~mask) !== 0n)
|
|
990
|
+
return { ...scalar(-4), restartable: deadline === Infinity };
|
|
991
|
+
parked(2); // listener ingress or configured deadline can wake the call
|
|
992
|
+
try {
|
|
993
|
+
await waitForDescriptorChange(deadline - monotonicMilliseconds());
|
|
994
|
+
}
|
|
995
|
+
finally {
|
|
996
|
+
parked(0);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
971
999
|
}
|
|
972
|
-
|
|
973
|
-
socket
|
|
974
|
-
throw error;
|
|
1000
|
+
finally {
|
|
1001
|
+
releaseActiveIO(socket);
|
|
975
1002
|
}
|
|
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
1003
|
}
|
|
981
1004
|
function requestLoopbackListen(port) {
|
|
982
1005
|
if (!loopbackPort)
|
|
@@ -1058,17 +1081,14 @@ export function createWaliNetworkSession(reply) {
|
|
|
1058
1081
|
listening: false,
|
|
1059
1082
|
backlog: 0,
|
|
1060
1083
|
pending: [],
|
|
1061
|
-
acceptWaiters: [],
|
|
1062
1084
|
loopbackConnectionId: message.connectionId,
|
|
1063
1085
|
peerClosed: false,
|
|
1064
1086
|
...(listener.bound ? { bound: listener.bound } : {}),
|
|
1087
|
+
// Clone listener options when the connection is created, not at accept.
|
|
1088
|
+
...(listener.receiveTimeoutMicros !== undefined ? { receiveTimeoutMicros: listener.receiveTimeoutMicros } : {}),
|
|
1065
1089
|
};
|
|
1066
1090
|
loopbackConnections.set(message.connectionId, accepted);
|
|
1067
|
-
|
|
1068
|
-
if (waiter)
|
|
1069
|
-
waiter(accepted);
|
|
1070
|
-
else
|
|
1071
|
-
listener.pending.push(accepted);
|
|
1091
|
+
listener.pending.push(accepted);
|
|
1072
1092
|
notifyDescriptors();
|
|
1073
1093
|
}
|
|
1074
1094
|
function postLoopback(message) {
|
|
@@ -1191,7 +1211,7 @@ export function createWaliNetworkSession(reply) {
|
|
|
1191
1211
|
const MSG_PEEK = 2, MSG_DONTWAIT = 0x40, MSG_WAITALL = 0x100;
|
|
1192
1212
|
if (maximum < 0)
|
|
1193
1213
|
return scalar(-22);
|
|
1194
|
-
if (flags & ~(MSG_PEEK | MSG_DONTWAIT | MSG_WAITALL))
|
|
1214
|
+
if (flags & ~(MSG_PEEK | MSG_DONTWAIT | MSG_WAITALL | (socket.kind === "dns" ? 0x20 : 0)))
|
|
1195
1215
|
return scalar(-95);
|
|
1196
1216
|
const waitAll = Boolean(flags & MSG_WAITALL) && (socket.kind === "tcp" || socket.kind === "local");
|
|
1197
1217
|
if ((flags & MSG_WAITALL) && socket.kind !== "dns" && !waitAll)
|
|
@@ -1218,6 +1238,7 @@ export function createWaliNetworkSession(reply) {
|
|
|
1218
1238
|
}
|
|
1219
1239
|
return { result: collected, output };
|
|
1220
1240
|
};
|
|
1241
|
+
const deadline = receiveDeadline(socket);
|
|
1221
1242
|
activeIO.set(socket, (activeIO.get(socket) ?? 0) + 1);
|
|
1222
1243
|
try {
|
|
1223
1244
|
for (;;) {
|
|
@@ -1252,13 +1273,17 @@ export function createWaliNetworkSession(reply) {
|
|
|
1252
1273
|
// WAITALL does not turn a nonblocking descriptor into a blocking one.
|
|
1253
1274
|
if (socket.nonblocking || Boolean(flags & MSG_DONTWAIT))
|
|
1254
1275
|
return partial(response);
|
|
1255
|
-
if (
|
|
1256
|
-
return partial(scalar(-
|
|
1257
|
-
|
|
1276
|
+
if (monotonicMilliseconds() >= deadline)
|
|
1277
|
+
return partial(scalar(-11));
|
|
1278
|
+
if (signals && (signals.pending() & ~mask) !== 0n) {
|
|
1279
|
+
const response = partial(scalar(-4));
|
|
1280
|
+
return response.result === -4 ? { ...response, restartable: deadline === Infinity } : response;
|
|
1281
|
+
}
|
|
1282
|
+
let delay = deadline - monotonicMilliseconds();
|
|
1258
1283
|
if (socket.kind === "timer" && socket.deadlineNanoseconds !== undefined) {
|
|
1259
|
-
delay = Number(timerRemaining(socket)) / 1_000_000;
|
|
1284
|
+
delay = Math.min(delay, Number(timerRemaining(socket)) / 1_000_000);
|
|
1260
1285
|
}
|
|
1261
|
-
parked(socket.kind === "tcp" || socket.kind === "dns"
|
|
1286
|
+
parked(deadline !== Infinity || socket.kind === "tcp" || socket.kind === "dns"
|
|
1262
1287
|
|| (socket.kind === "timer" && socket.deadlineNanoseconds !== undefined) ? 2 : 1);
|
|
1263
1288
|
// Install the wake before yielding; arrivals cannot slip between the
|
|
1264
1289
|
// scan and subscription. Signals use the existing caller-mask guard.
|
|
@@ -1312,7 +1337,10 @@ export function createWaliNetworkSession(reply) {
|
|
|
1312
1337
|
if (!response)
|
|
1313
1338
|
return { result: -11, output: new Uint8Array() };
|
|
1314
1339
|
const output = response.subarray(0, maximum);
|
|
1315
|
-
|
|
1340
|
+
// Linux recv(2) fills Emscripten libsyscall's explicit truncation TODO.
|
|
1341
|
+
// Packet length and copied length are distinct under input MSG_TRUNC.
|
|
1342
|
+
return { result: flags & 0x20 ? response.byteLength : output.byteLength, output,
|
|
1343
|
+
messageFlags: response.byteLength > maximum ? 0x20 : 0 };
|
|
1316
1344
|
}
|
|
1317
1345
|
if (maximum === 0)
|
|
1318
1346
|
return scalar(0);
|
|
@@ -1437,6 +1465,30 @@ export function createWaliNetworkSession(reply) {
|
|
|
1437
1465
|
socketDescriptorFlags.set(fd, closeOnExec ? 1 : 0);
|
|
1438
1466
|
return fd;
|
|
1439
1467
|
}
|
|
1468
|
+
/** Linux socket(7) timeouts use the existing owner, not transport watchdogs. */
|
|
1469
|
+
function receiveDeadline(socket) {
|
|
1470
|
+
const timeout = socket.kind === "event" || socket.kind === "timer" ? undefined : socket.receiveTimeoutMicros;
|
|
1471
|
+
return timeout === undefined ? Infinity : monotonicMilliseconds() + Number(timeout) / 1000;
|
|
1472
|
+
}
|
|
1473
|
+
function setSocketOption(socket, level, name, input) {
|
|
1474
|
+
if (socket.kind === "event" || socket.kind === "timer")
|
|
1475
|
+
return -88;
|
|
1476
|
+
if (level !== 1 || (name !== 20 && name !== 66))
|
|
1477
|
+
return -92;
|
|
1478
|
+
if (input.byteLength < 16)
|
|
1479
|
+
return -22;
|
|
1480
|
+
const view = new DataView(input.buffer, input.byteOffset, input.byteLength);
|
|
1481
|
+
const seconds = view.getBigInt64(0, true), micros = view.getBigInt64(8, true);
|
|
1482
|
+
if (micros < 0n || micros >= 1000000n)
|
|
1483
|
+
return -33; // EDOM, sock_set_timeout
|
|
1484
|
+
if (seconds < 0n)
|
|
1485
|
+
socket.receiveTimeoutMicros = 0n;
|
|
1486
|
+
else if (seconds === 0n && micros === 0n)
|
|
1487
|
+
delete socket.receiveTimeoutMicros;
|
|
1488
|
+
else
|
|
1489
|
+
socket.receiveTimeoutMicros = seconds * 1000000n + micros;
|
|
1490
|
+
return 0;
|
|
1491
|
+
}
|
|
1440
1492
|
// libsyscall.js only supports SO_ERROR. Additional read-only Linux queries
|
|
1441
1493
|
// below expose actual owner state, never a fabricated successful setting.
|
|
1442
1494
|
function socketOption(socket, level, name) {
|
|
@@ -1444,6 +1496,13 @@ export function createWaliNetworkSession(reply) {
|
|
|
1444
1496
|
return scalar(-88);
|
|
1445
1497
|
if (level !== 1)
|
|
1446
1498
|
return scalar(-92);
|
|
1499
|
+
if (name === 20 || name === 66) {
|
|
1500
|
+
const duration = socket.receiveTimeoutMicros ?? 0n;
|
|
1501
|
+
const output = new Uint8Array(16), view = new DataView(output.buffer);
|
|
1502
|
+
view.setBigInt64(0, duration / 1000000n, true);
|
|
1503
|
+
view.setBigInt64(8, duration % 1000000n, true);
|
|
1504
|
+
return { result: 0, output };
|
|
1505
|
+
}
|
|
1447
1506
|
let value;
|
|
1448
1507
|
switch (name) {
|
|
1449
1508
|
case 3:
|
|
@@ -1510,8 +1569,6 @@ export function createWaliNetworkSession(reply) {
|
|
|
1510
1569
|
if (socket.listening && socket.bound) {
|
|
1511
1570
|
listenersByPort.delete(socket.bound.port);
|
|
1512
1571
|
postLoopback({ type: "unlisten", port: socket.bound.port });
|
|
1513
|
-
for (const resolve of socket.acceptWaiters.splice(0))
|
|
1514
|
-
resolve(undefined);
|
|
1515
1572
|
for (const accepted of socket.pending.splice(0))
|
|
1516
1573
|
closeSocket(accepted);
|
|
1517
1574
|
}
|