RpcIpcMessagePort.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. "use strict";
  2. var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
  3. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  4. return new (P || (P = Promise))(function (resolve, reject) {
  5. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  6. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  7. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  8. step((generator = generator.apply(thisArg, _arguments || [])).next());
  9. });
  10. };
  11. Object.defineProperty(exports, "__esModule", { value: true });
  12. const child_process_1 = require("child_process");
  13. const RpcIpcMessagePortClosedError_1 = require("./error/RpcIpcMessagePortClosedError");
  14. function createRpcIpcMessagePort(process) {
  15. const messageListeners = new Set();
  16. const errorListeners = new Set();
  17. let closedError;
  18. const handleExit = (code, signal) => __awaiter(this, void 0, void 0, function* () {
  19. closedError = new RpcIpcMessagePortClosedError_1.RpcIpcMessagePortClosedError(code
  20. ? `Process ${process.pid} exited with code "${code}" [${signal}]`
  21. : `Process ${process.pid} exited [${signal}].`, code, signal);
  22. errorListeners.forEach((listener) => {
  23. if (closedError) {
  24. listener(closedError);
  25. }
  26. });
  27. yield port.close();
  28. });
  29. const handleMessage = (message) => {
  30. messageListeners.forEach((listener) => {
  31. listener(message);
  32. });
  33. };
  34. process.on('message', handleMessage);
  35. process.on('exit', handleExit);
  36. const port = {
  37. dispatchMessage: (message) => __awaiter(this, void 0, void 0, function* () {
  38. return new Promise((resolve, reject) => {
  39. if (!process.connected) {
  40. reject(closedError ||
  41. new RpcIpcMessagePortClosedError_1.RpcIpcMessagePortClosedError(`Process ${process.pid} doesn't have open IPC channels`));
  42. }
  43. if (process.send) {
  44. process.send(Object.assign(Object.assign({}, message), { source: process.pid }), undefined, undefined, (sendError) => {
  45. if (sendError) {
  46. if (!closedError) {
  47. closedError = new RpcIpcMessagePortClosedError_1.RpcIpcMessagePortClosedError(`Cannot send the message - the message port has been closed for the process ${process.pid}.`);
  48. }
  49. reject(closedError);
  50. }
  51. else {
  52. resolve();
  53. }
  54. });
  55. }
  56. else {
  57. reject(new RpcIpcMessagePortClosedError_1.RpcIpcMessagePortClosedError(`Process ${process.pid} doesn't have IPC channels`));
  58. }
  59. });
  60. }),
  61. addMessageListener: (listener) => {
  62. messageListeners.add(listener);
  63. },
  64. removeMessageListener: (listener) => {
  65. messageListeners.delete(listener);
  66. },
  67. addErrorListener: (listener) => {
  68. errorListeners.add(listener);
  69. },
  70. removeErrorListener: (listener) => {
  71. errorListeners.delete(listener);
  72. },
  73. isOpen: () => !!process.connected,
  74. open: () => __awaiter(this, void 0, void 0, function* () {
  75. if (!process.connected || closedError) {
  76. throw (closedError ||
  77. new RpcIpcMessagePortClosedError_1.RpcIpcMessagePortClosedError(`Cannot open closed IPC channel for process ${process.pid}.`));
  78. }
  79. }),
  80. close: () => __awaiter(this, void 0, void 0, function* () {
  81. process.off('message', handleMessage);
  82. process.off('exit', handleExit);
  83. messageListeners.clear();
  84. errorListeners.clear();
  85. if (process.disconnect && process.connected) {
  86. process.disconnect();
  87. }
  88. }),
  89. };
  90. return port;
  91. }
  92. exports.createRpcIpcMessagePort = createRpcIpcMessagePort;
  93. function createRpcIpcForkedProcessMessagePort(filePath, memoryLimit = 2048, autoRecreate = true) {
  94. function createChildProcess() {
  95. return child_process_1.fork(filePath, [], {
  96. execArgv: [`--max-old-space-size=${memoryLimit}`],
  97. stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
  98. });
  99. }
  100. const messageListeners = new Set();
  101. const errorListeners = new Set();
  102. let childProcess = createChildProcess();
  103. let port = createRpcIpcMessagePort(childProcess);
  104. return {
  105. dispatchMessage: (message) => port.dispatchMessage(message),
  106. addMessageListener: (listener) => {
  107. messageListeners.add(listener);
  108. return port.addMessageListener(listener);
  109. },
  110. removeMessageListener: (listener) => {
  111. messageListeners.delete(listener);
  112. return port.removeMessageListener(listener);
  113. },
  114. addErrorListener: (listener) => {
  115. errorListeners.add(listener);
  116. return port.addErrorListener(listener);
  117. },
  118. removeErrorListener: (listener) => {
  119. errorListeners.delete(listener);
  120. return port.removeErrorListener(listener);
  121. },
  122. isOpen: () => port.isOpen(),
  123. open: () => __awaiter(this, void 0, void 0, function* () {
  124. if (!port.isOpen() && autoRecreate) {
  125. // recreate the process and add existing message listeners
  126. childProcess = createChildProcess();
  127. port = createRpcIpcMessagePort(childProcess);
  128. messageListeners.forEach((listener) => {
  129. port.addMessageListener(listener);
  130. });
  131. errorListeners.forEach((listener) => {
  132. port.addErrorListener(listener);
  133. });
  134. }
  135. else {
  136. return port.open();
  137. }
  138. }),
  139. close: () => __awaiter(this, void 0, void 0, function* () {
  140. yield port.close();
  141. messageListeners.clear();
  142. errorListeners.clear();
  143. if (childProcess) {
  144. childProcess.kill('SIGTERM');
  145. childProcess = undefined;
  146. }
  147. }),
  148. };
  149. }
  150. exports.createRpcIpcForkedProcessMessagePort = createRpcIpcForkedProcessMessagePort;