UNPKG

@otpjs/gen

Version:
219 lines (218 loc) 8.63 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.registerName = exports.cast = exports.call = exports.Symbols = void 0; exports.reply = reply; exports.start = start; exports.unregisterName = void 0; var otp = _interopRequireWildcard(require("@otpjs/core")); var match = _interopRequireWildcard(require("@otpjs/matching")); var _types = require("@otpjs/types"); var proc_lib = _interopRequireWildcard(require("@otpjs/proc_lib")); var Symbols = _interopRequireWildcard(require("./symbols")); exports.Symbols = Symbols; function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); } function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; } const { monitor, link, nolink, $gen_call, $gen_cast, already_started } = Symbols; function log(ctx, ...args) { return ctx.log.extend('gen')(...args); } const { ok, error, nodedown, DOWN } = otp.Symbols; const { _ } = match.Symbols; const DEFAULT_TIMEOUT = 5000; const isLocalName = match.compile((0, _types.t)('local', _)); const isUndefined = match.compile(undefined); const isPid = _types.Pid.isPid; function where(ctx, name) { if (isLocalName(name)) { const localName = getName(name); log(ctx, 'where(name: %o, localName: %o)', name, localName); return ctx.whereis(localName); } else { log(ctx, 'where(name: %o, notLocal)', name); return undefined; } } async function start(ctx, linking, name, init_it, options = {}) { const response = where(ctx, name); switch (true) { case isUndefined(response): return doSpawn(ctx, linking, name, init_it, options); case isPid(response): default: return (0, _types.t)(error, (0, _types.t)(already_started, response)); } } const doSpawn = match.clauses(function routeSpawn(route) { route(link, _, _, _).to(doSpawnLink); route(link, _, _, _, _).to(doSpawnLink); route(monitor, _, _, _).to(doSpawnMonitor); route(monitor, _, _, _, _).to(doSpawnMonitor); route(nolink, _, _, _).to(doSpawnNoLink); route(nolink, _, _, _, _).to(doSpawnNoLink); route(_, _, _, _).to(doSpawnNoLink); route(_, _, _, _, _).to(doSpawnNoLink); async function doSpawnLink(ctx, linking, name, init_it, options) { const timeout = 'timeout' in options ? options.timeout : Infinity; log(ctx, 'doSpawn() : proc_lib.startLink()'); return await proc_lib.startLink(ctx, initializer(name, init_it, options), timeout); } /* istanbul ignore next */ function doSpawnMonitor(ctx, linking, name, init_it, options) { log(ctx, 'doSpawn() : proc_lib.startMonitor()'); throw new _types.OTPError((0, _types.t)('not_yet_implemented', link)); } function doSpawnNoLink(ctx, linking, name, init_it, options) { const timeout = 'timeout' in options ? options.timeout : Infinity; log(ctx, 'doSpawn() : proc_lib.start()'); return proc_lib.start(ctx, initializer(name, init_it, options), timeout); } }); function initializer(name, initIt, options) { const decision = match.buildCase(is => { is(true, success); is((0, _types.t)(false, _types.Pid.isPid), alreadyStarted); }); return async function initialize(ctx, starter) { const registration = registerName(ctx, name); const next = decision.for(registration); return next(ctx, registration, initIt, starter); }; function success(ctx, _result, initIt, starter) { log(ctx, 'initialize() : initIt(%o)', starter); return initIt(ctx, starter); } function alreadyStarted(ctx, [, pid], _initIt, starter) { return proc_lib.initAck(ctx, starter, (0, _types.t)(error, (0, _types.t)(already_started, pid))); } } const registerName = match.clauses(function routeRegisterName(route) { route(isLocalName).to(registerLocalName); route(_).to(() => true); function registerLocalName(ctx, name) { try { ctx.register(getName(name)); return true; } catch (err) { const pid = where(ctx, name); log(ctx, 'registerName(name: %o, error: %o, pid: %o)', name, err, pid); return (0, _types.t)(false, pid); } } }); exports.registerName = registerName; const unregisterName = match.clauses(route => { route(isLocalName).to(unregisterLocal); route(_types.Pid.isPid).to(doNothing); function unregisterLocal(ctx, [, name]) { try { ctx.unregister(name); } finally { return ok; } } function doNothing() { return ok; } }); exports.unregisterName = unregisterName; const getName = match.clauses(function routeGetName(route) { route((0, _types.t)('local', _)).to(([, name]) => name); route(_types.Pid.isPid).to(pid => pid); }); const callReplyPattern = ref => match.compile((0, _types.t)(ref, _)); const downPattern = (mref, pid) => match.compile((0, _types.t)(DOWN, mref, _, pid, _)); const call = match.clauses(function routeCall(route) { route(_types.Pid.isPid, _).to(doCall); route(_types.Pid.isPid, _, _).to(doCall); route(_, _).to(doRemoteCall); route(_, _, _).to(doRemoteCall); }); exports.call = call; function doRemoteCall(ctx, pid, message, timeout = DEFAULT_TIMEOUT) { const fun = pid => doCall(ctx, pid, message, timeout); log(ctx, 'call(%o) : isNotPid', pid); return doForProcess(ctx, pid, fun); } async function doCall(ctx, pid, message, timeout = DEFAULT_TIMEOUT) { const self = ctx.self(); const ref = ctx.ref(); const mref = ctx.monitor(pid); const isReply = callReplyPattern(ref); const isDown = downPattern(mref, pid); ctx.send(pid, (0, _types.t)($gen_call, (0, _types.t)(self, ref), message)); log(ctx, 'doCall(%o, %o) : receive(%o, %o)', pid, ref, isReply, isDown); return ctx.receiveBlock((given, after) => { given(isReply).then(([ref, response]) => { ctx.demonitor(mref); log(ctx, 'doCall(%o, %o) : response : %o', pid, ref, response); return response; }); given(isDown).then(([, ref,, pid, reason]) => { log(ctx, 'doCall(%o, %o) : throw OTPError(%o)', pid, ref, reason); throw new _types.OTPError(reason); }); after(timeout).then(() => { throw (0, _types.OTPError)(otp.Symbols.timeout); }); }); } const cast = match.clauses(function routeCast(route) { route(_types.Pid.isPid, _).to(doCast); route(_, _).to(doRemoteCast); function doCast(ctx, pid, message) { return ctx.send(pid, (0, _types.t)($gen_cast, message)); } function doRemoteCast(ctx, pid, message) { const fun = pid => doCast(ctx, pid, message); return doForProcess(ctx, pid, fun); } }); exports.cast = cast; const isKeyedSymbol = v => typeof v === 'symbol' && Symbol.keyFor(v) !== undefined; function doForProcess(ctx, process, fun) { // TODO: look up process (which is not a Pid) // As of the time of this comment, core/node handles routing remote messages const compare = match.caseOf(process); if (compare(isKeyedSymbol)) { const result = ctx.whereis(process); log(ctx, 'doForProcess(%o) : found : %o', process, result); if (result === undefined) { throw (0, _types.OTPError)('noproc'); } else { log(ctx, 'fun(%o)', result); return fun(result); } } else if (compare((0, _types.t)(_, _))) { const [name, node] = process; if (ctx.nodes().includes(node)) { return fun(process); } else { throw (0, _types.OTPError)((0, _types.t)(nodedown, node)); } } else { const error = new _types.OTPError('not_implemented'); log(ctx, 'doForProcess(%o) : not_found', process); log(ctx, 'doForProcess(%o) : error : %o', error); throw error; } } function reply(ctx, [pid, ref], reply) { log(ctx, 'ctx.send(%o, %o)', pid, (0, _types.t)(ref, reply)); ctx.send(pid, (0, _types.t)(ref, reply)); }