Skip to content
Closed
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
1e298bb
framework-test: provision ephemeral embedded databases per app
Arshia001 Jul 3, 2026
3c32fe9
Bump wasmer-examples: add js-hedgedoc (HedgeDoc 1.11.0 + PostgreSQL)
Arshia001 Jul 3, 2026
d24856d
Disable native addon loading on native edge binaries (WASIX parity)
Arshia001 Jul 6, 2026
deb743d
framework-test: MySQL provider, database setup hook, preserve tracked…
Arshia001 Jul 6, 2026
69870ee
Report process.platform as 'linux' under WASIX
Arshia001 Jul 6, 2026
97e5975
framework-test: readiness waits for the route's expected status
Arshia001 Jul 6, 2026
3870199
Bump wasmer-examples: add js-uptime-kuma (Uptime Kuma 2.4.0 + MySQL)
Arshia001 Jul 6, 2026
f995cf2
Plan: mark Uptime Kuma done in ECO-355 phase DB
Arshia001 Jul 6, 2026
4785495
framework-test: mysql_native_password harness user; add js-firekylin
Arshia001 Jul 6, 2026
cf16f38
Bump libuv-wasix: plain read() for IPC streams under WASIX
Arshia001 Jul 6, 2026
023a843
Makefile: update js-firekylin WASIX skip rationale
Arshia001 Jul 6, 2026
f7298f6
Bump libuv-wasix: enable SO_REUSEPORT under WASIX
Arshia001 Jul 6, 2026
34524ab
cluster: reuseport scheduling strategy under WASIX; unskip js-firekylin
Arshia001 Jul 6, 2026
be96052
cluster: move the WASIX reuseport strategy out of lib/ into native
Arshia001 Jul 7, 2026
b9865e8
test: unskip cluster/fork tests that pass under WASIX now
Arshia001 Jul 7, 2026
7fa823f
cluster: extend the WASIX reuseport strategy to UDP; empty the skip list
Arshia001 Jul 7, 2026
dbd46ba
test: skip test-http-chunk-problem on WASIX (cold coreutils compile)
Arshia001 Jul 7, 2026
d826a70
test: skip test-http-full-response on WASIX (cold guest-shell compile)
Arshia001 Jul 7, 2026
9dda93f
test: move external-tool tests to the WASIX slow bucket instead of sk…
Arshia001 Jul 7, 2026
598d41a
test: skip js-remix-staticsite on the WASIX edge stage (platform=linu…
Arshia001 Jul 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ WASIX_SKIP_UNIX_SOCKET_TESTS := \
parallel/test-tls-connect-pipe.js \
parallel/test-tls-net-connect-prefer-path.js \
parallel/test-tls-wrap-econnreset-pipe.js \
parallel/test-http-client-response-domain.js
parallel/test-http-client-response-domain.js \
parallel/test-pipe-abstract-socket-http.js
WASIX_SKIP_CLUSTER_FORK_TESTS := \
parallel/test-dgram-bind-socket-close-before-cluster-reply.js \
parallel/test-dgram-cluster-close-during-bind.js \
Expand Down
2 changes: 1 addition & 1 deletion deps/libuv-wasix
45 changes: 45 additions & 0 deletions lib/internal/cluster/child.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const {
} = primordials;

const assert = require('internal/assert');
const net = require('net');
const path = require('path');
const EventEmitter = require('events');
const { owner_symbol } = require('internal/async_hooks').symbols;
Expand Down Expand Up @@ -108,6 +109,18 @@ cluster._getServer = function(obj, options, cb) {
if (handle) {
// Shared listen socket
shared(reply, { handle, indexesKey, index }, cb);
} else if (reply.reusePort) {
// WASIX: create our own listen handle with SO_REUSEPORT; the host
// kernel balances connections between the workers.
reusePortListen(reply, {
address,
port: options.port,
addressType: options.addressType,
fd: options.fd,
flags: options.flags,
indexesKey,
index,
}, cb);
} else {
// Round-robin.
rr(reply, { indexesKey, index }, cb);
Expand Down Expand Up @@ -139,6 +152,38 @@ function removeIndexesKey(indexesKey, index) {
}
}

// WASIX reuseport listen: no handle can cross the IPC channel, so the worker
// binds its own listen handle with SO_REUSEPORT (see
// internal/cluster/reuse_port_handle.js for the primary side).
function reusePortListen(message, options, cb) {
if (message.errno)
return cb(message.errno, null);

const { address, port, addressType, fd, indexesKey, index } = options;
const { constants: TCPConstants } = internalBinding('tcp_wrap');
const flags = (options.flags | TCPConstants.UV_TCP_REUSEPORT) >>> 0;
const rval = net._createServerHandle(address, port, addressType, fd, flags);

if (typeof rval === 'number')
return cb(rval, null);

const handle = rval;
const key = message.key;
// Same close bookkeeping as shared(): tell the primary so it can drop the
// worker from the ReusePortHandle registry.
const close = handle.close;

handle.close = function() {
send({ act: 'close', key });
handles.delete(key);
removeIndexesKey(indexesKey, index);
return ReflectApply(close, handle, arguments);
};
assert(handles.has(key) === false);
handles.set(key, handle);
cb(0, handle);
}

// Shared listen socket.
function shared(message, { handle, indexesKey, index }, cb) {
const key = message.key;
Expand Down
12 changes: 11 additions & 1 deletion lib/internal/cluster/primary.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ const path = require('path');
const EventEmitter = require('events');
const RoundRobinHandle = require('internal/cluster/round_robin_handle');
const SharedHandle = require('internal/cluster/shared_handle');
const ReusePortHandle = require('internal/cluster/reuse_port_handle');
const { isWasix } = internalBinding('process_methods');
const Worker = require('internal/cluster/worker');
const { getInspectPort, isUsingInspector } = require('internal/util/inspector');
const { internal, sendHelper } = require('internal/cluster/utils');
Expand Down Expand Up @@ -294,7 +296,15 @@ function queryServer(worker, message) {
// UDP is exempt from round-robin connection balancing for what should
// be obvious reasons: it's connectionless. There is nothing to send to
// the workers except raw datagrams and that's pointless.
if (schedulingPolicy !== SCHED_RR ||
if (isWasix &&
message.addressType !== 'udp4' &&
message.addressType !== 'udp6' &&
(message.fd == null || message.fd < 0) &&
typeof message.port === 'number' && message.port >= 0) {
// WASIX cannot pass handles between processes; workers bind their own
// SO_REUSEPORT listeners and the host kernel balances connections.
handle = new ReusePortHandle(key, address, message);
} else if (schedulingPolicy !== SCHED_RR ||
message.addressType === 'udp4' ||
message.addressType === 'udp6') {
handle = new SharedHandle(key, address, message);
Expand Down
38 changes: 38 additions & 0 deletions lib/internal/cluster/reuse_port_handle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
'use strict';

const {
SafeMap,
} = primordials;

const assert = require('internal/assert');

module.exports = ReusePortHandle;

// WASIX cluster scheduling strategy: listen handles cannot be passed between
// processes (no SCM_RIGHTS over the IPC channel), but SO_REUSEPORT is fully
// supported and the host kernel balances connections between listeners. The
// primary therefore binds nothing; each worker is told (via the reusePort
// reply flag) to create its own listen handle with UV_TCP_REUSEPORT.
function ReusePortHandle(key, address, message) {
this.key = key;
this.workers = new SafeMap();
this.errno = 0;
}

ReusePortHandle.prototype.add = function(worker, send) {
assert(!this.workers.has(worker.id));
this.workers.set(worker.id, worker);
send(this.errno, { reusePort: true }, null);
};

ReusePortHandle.prototype.remove = function(worker) {
if (!this.workers.has(worker.id))
return false;

this.workers.delete(worker.id);
return this.workers.size === 0;
};

ReusePortHandle.prototype.has = function(worker) {
return this.workers.has(worker.id);
};
Loading
Loading