'use strict'; const EventEmitter = require('events'); const util = require('util'); const { internalBinding } = require('internal/bootstrap/loaders'); const { MessagePort, MessageChannel } = internalBinding('messaging'); const { handle_onclose } = internalBinding('symbols'); util.inherits(MessagePort, EventEmitter); const kOnMessageListener = Symbol('kOnMessageListener'); const debug = util.debuglog('worker'); // A MessagePort consists of a handle (that wraps around an // uv_async_t) which can receive information from other threads and emits // .onmessage events, and a function used for sending data to a MessagePort // in some other thread. MessagePort.prototype[kOnMessageListener] = function onmessage(payload) { debug('received message', payload); // Emit the deserialized object to userland. this.emit('message', payload); }; // This is for compatibility with the Web's MessagePort API. It makes sense to // provide it as an `EventEmitter` in Node.js, but if somebody overrides // `onmessage`, we'll switch over to the Web API model. Object.defineProperty(MessagePort.prototype, 'onmessage', { enumerable: true, configurable: true, get() { return this[kOnMessageListener]; }, set(value) { this[kOnMessageListener] = value; if (typeof value === 'function') { this.ref(); this.start(); } else { this.unref(); this.stop(); } } }); // This is called from inside the `MessagePort` constructor. function oninit() { setupPortReferencing(this, this, 'message'); } Object.defineProperty(MessagePort.prototype, 'oninit', { enumerable: true, writable: false, value: oninit }); // This is called after the underlying `uv_async_t` has been closed. function onclose() { if (typeof this.onclose === 'function') { // Not part of the Web standard yet, but there aren't many reasonable // alternatives in a non-EventEmitter usage setting. // Refs: https://github.com/whatwg/html/issues/1766 this.onclose(); } this.emit('close'); } Object.defineProperty(MessagePort.prototype, handle_onclose, { enumerable: false, writable: false, value: onclose }); const originalClose = MessagePort.prototype.close; MessagePort.prototype.close = function(cb) { if (typeof cb === 'function') this.once('close', cb); originalClose.call(this); }; function setupPortReferencing(port, eventEmitter, eventName) { // Keep track of whether there are any workerMessage listeners: // If there are some, ref() the channel so it keeps the event loop alive. // If there are none or all are removed, unref() the channel so the worker // can shutdown gracefully. port.unref(); eventEmitter.on('newListener', (name) => { if (name === eventName && eventEmitter.listenerCount(eventName) === 0) { port.ref(); port.start(); } }); eventEmitter.on('removeListener', (name) => { if (name === eventName && eventEmitter.listenerCount(eventName) === 0) { port.stop(); port.unref(); } }); } module.exports = { MessagePort, MessageChannel };