UNPKG

nstdlib-nightly

Version:

Node.js standard library converted to runtime-agnostic ES modules.

138 lines (113 loc) 3.5 kB
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/internal/cluster/round_robin_handle.js import * as assert from "nstdlib/lib/internal/assert"; import * as net from "nstdlib/lib/net"; import { sendHelper } from "nstdlib/lib/internal/cluster/utils"; import { append, init, isEmpty, peek, remove, } from "nstdlib/lib/internal/linkedlist"; import { constants } from "nstdlib/stub/binding/tcp_wrap"; export default RoundRobinHandle; function RoundRobinHandle( key, address, { port, fd, flags, backlog, readableAll, writableAll }, ) { this.key = key; this.all = new Map(); this.free = new Map(); this.handles = init({ __proto__: null }); this.handle = null; this.server = net.createServer(assert.fail); if (fd >= 0) this.server.listen({ fd, backlog }); else if (port >= 0) { this.server.listen({ port, host: address, // Currently, net module only supports `ipv6Only` option in `flags`. ipv6Only: Boolean(flags & constants.UV_TCP_IPV6ONLY), backlog, }); } else this.server.listen({ path: address, backlog, readableAll, writableAll, }); // UNIX socket path. this.server.once("listening", () => { this.handle = this.server._handle; this.handle.onconnection = (err, handle) => this.distribute(err, handle); this.server._handle = null; this.server = null; }); } RoundRobinHandle.prototype.add = function (worker, send) { assert(this.all.has(worker.id) === false); this.all.set(worker.id, worker); const done = () => { if (this.handle.getsockname) { const out = {}; this.handle.getsockname(out); // TODO(bnoordhuis) Check err. send(null, { sockname: out }, null); } else { send(null, null, null); // UNIX socket. } this.handoff(worker); // In case there are connections pending. }; if (this.server === null) return done(); // Still busy binding. this.server.once("listening", done); this.server.once("error", (err) => { send(err.errno, null); }); }; RoundRobinHandle.prototype.remove = function (worker) { const existed = this.all.delete(worker.id); if (!existed) return false; this.free.delete(worker.id); if (this.all.size !== 0) return false; while (!isEmpty(this.handles)) { const handle = peek(this.handles); handle.close(); remove(handle); } this.handle.close(); this.handle = null; return true; }; RoundRobinHandle.prototype.distribute = function (err, handle) { // If `accept` fails just skip it (handle is undefined) if (err) { return; } append(this.handles, handle); // eslint-disable-next-line node-core/no-array-destructuring const [workerEntry] = this.free; // this.free is a SafeMap if (Array.isArray(workerEntry)) { const { 0: workerId, 1: worker } = workerEntry; this.free.delete(workerId); this.handoff(worker); } }; RoundRobinHandle.prototype.handoff = function (worker) { if (!this.all.has(worker.id)) { return; // Worker is closing (or has closed) the server. } const handle = peek(this.handles); if (handle === null) { this.free.set(worker.id, worker); // Add to ready queue again. return; } remove(handle); const message = { act: "newconn", key: this.key }; sendHelper(worker.process, message, handle, (reply) => { if (reply.accepted) handle.close(); else this.distribute(0, handle); // Worker is shutting down. Send to another. this.handoff(worker); }); };