SyncMessenger.js 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.SyncMessenger = void 0;
  4. /**
  5. * @param condition Condition to wait for, when true, the function returns.
  6. * @param ms Maximum time to wait in milliseconds.
  7. */
  8. const sleepUntil = (condition, ms = 100) => {
  9. const start = Date.now();
  10. while (!condition()) {
  11. const now = Date.now();
  12. if (now - start > ms)
  13. throw new Error('Timeout');
  14. }
  15. };
  16. /**
  17. * `SyncMessenger` allows to execute asynchronous code synchronously. The
  18. * asynchronous code is executed in a Worker thread, while the main thread is
  19. * blocked until the asynchronous code is finished.
  20. *
  21. * First four 4-byte words is the header, where the first word is used for Atomics
  22. * notifications. The second word is used for spin-locking the main thread until
  23. * the asynchronous code is finished. The third word is used to specify payload
  24. * length. The fourth word is currently unused.
  25. *
  26. * The maximum payload size is the size of the SharedArrayBuffer minus the
  27. * header size.
  28. */
  29. class SyncMessenger {
  30. constructor(sab) {
  31. this.sab = sab;
  32. this.int32 = new Int32Array(sab);
  33. this.uint8 = new Uint8Array(sab);
  34. this.headerSize = 4 * 4;
  35. this.dataSize = sab.byteLength - this.headerSize;
  36. }
  37. callSync(data) {
  38. const requestLength = data.length;
  39. const headerSize = this.headerSize;
  40. const int32 = this.int32;
  41. int32[1] = 0;
  42. int32[2] = requestLength;
  43. this.uint8.set(data, headerSize);
  44. Atomics.notify(int32, 0);
  45. sleepUntil(() => int32[1] === 1);
  46. const responseLength = int32[2];
  47. const response = this.uint8.slice(headerSize, headerSize + responseLength);
  48. return response;
  49. }
  50. serveAsync(callback) {
  51. const headerSize = this.headerSize;
  52. (async () => {
  53. try {
  54. const int32 = this.int32;
  55. const res = Atomics.wait(int32, 0, 0);
  56. if (res !== 'ok')
  57. throw new Error(`Unexpected Atomics.wait result: ${res}`);
  58. const requestLength = this.int32[2];
  59. const request = this.uint8.slice(headerSize, headerSize + requestLength);
  60. const response = await callback(request);
  61. const responseLength = response.length;
  62. int32[2] = responseLength;
  63. this.uint8.set(response, headerSize);
  64. int32[1] = 1;
  65. }
  66. catch (_a) { }
  67. this.serveAsync(callback);
  68. })().catch(() => { });
  69. }
  70. }
  71. exports.SyncMessenger = SyncMessenger;
  72. //# sourceMappingURL=SyncMessenger.js.map