sandbox.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. import { ChildCommand, ParentCommand } from '../enums';
  2. const sandbox = (processFile, childPool) => {
  3. return async function process(job, token, signal) {
  4. let child;
  5. let msgHandler;
  6. let exitHandler;
  7. let abortHandler;
  8. try {
  9. const done = new Promise((resolve, reject) => {
  10. const initChild = async () => {
  11. try {
  12. exitHandler = (exitCode, signal) => {
  13. reject(new Error('Unexpected exit code: ' + exitCode + ' signal: ' + signal));
  14. };
  15. child = await childPool.retain(processFile);
  16. child.on('exit', exitHandler);
  17. msgHandler = async (msg) => {
  18. var _a, _b, _c, _d, _e;
  19. try {
  20. switch (msg.cmd) {
  21. case ParentCommand.Completed:
  22. resolve(msg.value);
  23. break;
  24. case ParentCommand.Failed:
  25. case ParentCommand.Error: {
  26. const err = new Error();
  27. Object.assign(err, msg.value);
  28. reject(err);
  29. break;
  30. }
  31. case ParentCommand.Progress:
  32. await job.updateProgress(msg.value);
  33. break;
  34. case ParentCommand.Log:
  35. await job.log(msg.value);
  36. break;
  37. case ParentCommand.MoveToDelayed:
  38. await job.moveToDelayed((_a = msg.value) === null || _a === void 0 ? void 0 : _a.timestamp, (_b = msg.value) === null || _b === void 0 ? void 0 : _b.token);
  39. break;
  40. case ParentCommand.MoveToWait:
  41. await job.moveToWait((_c = msg.value) === null || _c === void 0 ? void 0 : _c.token);
  42. break;
  43. case ParentCommand.MoveToWaitingChildren:
  44. {
  45. const value = await job.moveToWaitingChildren((_d = msg.value) === null || _d === void 0 ? void 0 : _d.token, (_e = msg.value) === null || _e === void 0 ? void 0 : _e.opts);
  46. child.send({
  47. requestId: msg.requestId,
  48. cmd: ChildCommand.MoveToWaitingChildrenResponse,
  49. value,
  50. });
  51. }
  52. break;
  53. case ParentCommand.Update:
  54. await job.updateData(msg.value);
  55. break;
  56. case ParentCommand.GetChildrenValues:
  57. {
  58. const value = await job.getChildrenValues();
  59. child.send({
  60. requestId: msg.requestId,
  61. cmd: ChildCommand.GetChildrenValuesResponse,
  62. value,
  63. });
  64. }
  65. break;
  66. case ParentCommand.GetIgnoredChildrenFailures:
  67. {
  68. const value = await job.getIgnoredChildrenFailures();
  69. child.send({
  70. requestId: msg.requestId,
  71. cmd: ChildCommand.GetIgnoredChildrenFailuresResponse,
  72. value,
  73. });
  74. }
  75. break;
  76. case ParentCommand.GetDependenciesCount:
  77. {
  78. const value = await job.getDependenciesCount(msg.value);
  79. child.send({
  80. requestId: msg.requestId,
  81. cmd: ChildCommand.GetDependenciesCountResponse,
  82. value,
  83. });
  84. }
  85. break;
  86. case ParentCommand.GetDependencies:
  87. {
  88. const value = await job.getDependencies(msg.value);
  89. child.send({
  90. requestId: msg.requestId,
  91. cmd: ChildCommand.GetDependenciesResponse,
  92. value,
  93. });
  94. }
  95. break;
  96. }
  97. }
  98. catch (err) {
  99. reject(err);
  100. }
  101. };
  102. child.on('message', msgHandler);
  103. child.send({
  104. cmd: ChildCommand.Start,
  105. job: job.asJSONSandbox(),
  106. token,
  107. });
  108. if (signal) {
  109. abortHandler = () => {
  110. try {
  111. child.send({
  112. cmd: ChildCommand.Cancel,
  113. value: signal.reason,
  114. });
  115. }
  116. catch (_a) {
  117. // Child process may have already exited
  118. }
  119. };
  120. if (signal.aborted) {
  121. abortHandler();
  122. }
  123. else {
  124. signal.addEventListener('abort', abortHandler, { once: true });
  125. }
  126. }
  127. }
  128. catch (error) {
  129. reject(error);
  130. }
  131. };
  132. initChild();
  133. });
  134. await done;
  135. return done;
  136. }
  137. finally {
  138. // Note: There is a potential race where the signal is aborted between
  139. // `await done` and this cleanup. This is safe because:
  140. // 1. abortHandler has a try-catch for child process already exited
  141. // 2. The listener is added with `once: true`, so it fires at most once
  142. // 3. removeEventListener here is defensive cleanup only
  143. if (signal && abortHandler) {
  144. signal.removeEventListener('abort', abortHandler);
  145. }
  146. if (child) {
  147. child.off('message', msgHandler);
  148. child.off('exit', exitHandler);
  149. if (child.exitCode === null && child.signalCode === null) {
  150. childPool.release(child);
  151. }
  152. }
  153. }
  154. };
  155. };
  156. export default sandbox;
  157. //# sourceMappingURL=sandbox.js.map