workerPool.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. /**
  2. * Helper class to push actions to a pool of workers.
  3. */
  4. export class WorkerPool {
  5. /**
  6. * Constructor
  7. * @param workers Array of workers to use for actions
  8. */
  9. constructor(workers) {
  10. this._pendingActions = new Array();
  11. this._workerInfos = workers.map((worker) => ({
  12. workerPromise: Promise.resolve(worker),
  13. idle: true,
  14. }));
  15. }
  16. /**
  17. * Terminates all workers and clears any pending actions.
  18. */
  19. dispose() {
  20. for (const workerInfo of this._workerInfos) {
  21. workerInfo.workerPromise.then((worker) => {
  22. worker.terminate();
  23. });
  24. }
  25. this._workerInfos.length = 0;
  26. this._pendingActions.length = 0;
  27. }
  28. /**
  29. * Pushes an action to the worker pool. If all the workers are active, the action will be
  30. * pended until a worker has completed its action.
  31. * @param action The action to perform. Call onComplete when the action is complete.
  32. */
  33. push(action) {
  34. if (!this._executeOnIdleWorker(action)) {
  35. this._pendingActions.push(action);
  36. }
  37. }
  38. _executeOnIdleWorker(action) {
  39. for (const workerInfo of this._workerInfos) {
  40. if (workerInfo.idle) {
  41. this._execute(workerInfo, action);
  42. return true;
  43. }
  44. }
  45. return false;
  46. }
  47. _execute(workerInfo, action) {
  48. workerInfo.idle = false;
  49. workerInfo.workerPromise.then((worker) => {
  50. action(worker, () => {
  51. const nextAction = this._pendingActions.shift();
  52. if (nextAction) {
  53. this._execute(workerInfo, nextAction);
  54. }
  55. else {
  56. workerInfo.idle = true;
  57. }
  58. });
  59. });
  60. }
  61. }
  62. /**
  63. * Similar to the WorkerPool class except it creates and destroys workers automatically with a maximum of `maxWorkers` workers.
  64. * Workers are terminated when it is idle for at least `idleTimeElapsedBeforeRelease` milliseconds.
  65. */
  66. export class AutoReleaseWorkerPool extends WorkerPool {
  67. constructor(maxWorkers, createWorkerAsync, options = AutoReleaseWorkerPool.DefaultOptions) {
  68. super([]);
  69. this._maxWorkers = maxWorkers;
  70. this._createWorkerAsync = createWorkerAsync;
  71. this._options = options;
  72. }
  73. push(action) {
  74. if (!this._executeOnIdleWorker(action)) {
  75. if (this._workerInfos.length < this._maxWorkers) {
  76. const workerInfo = {
  77. workerPromise: this._createWorkerAsync(),
  78. idle: false,
  79. };
  80. this._workerInfos.push(workerInfo);
  81. this._execute(workerInfo, action);
  82. }
  83. else {
  84. this._pendingActions.push(action);
  85. }
  86. }
  87. }
  88. _execute(workerInfo, action) {
  89. // Reset the idle timeout.
  90. if (workerInfo.timeoutId) {
  91. clearTimeout(workerInfo.timeoutId);
  92. delete workerInfo.timeoutId;
  93. }
  94. super._execute(workerInfo, (worker, onComplete) => {
  95. action(worker, () => {
  96. onComplete();
  97. if (workerInfo.idle) {
  98. // Schedule the worker to be terminated after the elapsed time.
  99. workerInfo.timeoutId = setTimeout(() => {
  100. workerInfo.workerPromise.then((worker) => {
  101. worker.terminate();
  102. });
  103. const indexOf = this._workerInfos.indexOf(workerInfo);
  104. if (indexOf !== -1) {
  105. this._workerInfos.splice(indexOf, 1);
  106. }
  107. }, this._options.idleTimeElapsedBeforeRelease);
  108. }
  109. });
  110. });
  111. }
  112. }
  113. /**
  114. * Default options for the constructor.
  115. * Override to change the defaults.
  116. */
  117. AutoReleaseWorkerPool.DefaultOptions = {
  118. idleTimeElapsedBeforeRelease: 1000,
  119. };
  120. //# sourceMappingURL=workerPool.js.map