atomics-optimization.ts 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. import Piscina from '..';
  2. import { test } from 'tap';
  3. import { resolve } from 'path';
  4. test('coverage test for Atomics optimization', async ({ equal }) => {
  5. const pool = new Piscina({
  6. filename: resolve(__dirname, 'fixtures/notify-then-sleep-or.js'),
  7. minThreads: 2,
  8. maxThreads: 2,
  9. concurrentTasksPerWorker: 2
  10. });
  11. const tasks = [];
  12. let v : number;
  13. // Post 4 tasks, and wait for all of them to be ready.
  14. const i32array = new Int32Array(new SharedArrayBuffer(4));
  15. for (let index = 0; index < 4; index++) {
  16. tasks.push(pool.runTask({ i32array, index }));
  17. }
  18. // Wait for 2 tasks to enter 'wait' state.
  19. do {
  20. v = Atomics.load(i32array, 0);
  21. if (popcount8(v) >= 2) break;
  22. Atomics.wait(i32array, 0, v);
  23. } while (true);
  24. // The check above could also be !== 2 but it's hard to get things right
  25. // sometimes and this gives us a nice assertion. Basically, at this point
  26. // exactly 2 tasks should be in Atomics.wait() state.
  27. equal(popcount8(v), 2);
  28. // Wake both tasks up as simultaneously as possible. The other 2 tasks should
  29. // then start executing.
  30. Atomics.store(i32array, 0, 0);
  31. Atomics.notify(i32array, 0, Infinity);
  32. // Wait for the other 2 tasks to enter 'wait' state.
  33. do {
  34. v = Atomics.load(i32array, 0);
  35. if (popcount8(v) >= 2) break;
  36. Atomics.wait(i32array, 0, v);
  37. } while (true);
  38. // At this point, the first two tasks are definitely finished and have
  39. // definitely posted results back to the main thread, and the main thread
  40. // has definitely not received them yet, meaning that the Atomics check will
  41. // be used. Making sure that that works is the point of this test.
  42. // Wake up the remaining 2 tasks in order to make sure that the test finishes.
  43. // Do the same consistency check beforehand as above.
  44. equal(popcount8(v), 2);
  45. Atomics.store(i32array, 0, 0);
  46. Atomics.notify(i32array, 0, Infinity);
  47. await Promise.all(tasks);
  48. });
  49. // Inefficient but straightforward 8-bit popcount
  50. function popcount8 (v : number) : number {
  51. v &= 0xff;
  52. if (v & 0b11110000) return popcount8(v >>> 4) + popcount8(v & 0xb00001111);
  53. if (v & 0b00001100) return popcount8(v >>> 2) + popcount8(v & 0xb00000011);
  54. if (v & 0b00000010) return popcount8(v >>> 1) + popcount8(v & 0xb00000001);
  55. return v;
  56. }
  57. test('avoids unbounded recursion', async () => {
  58. const pool = new Piscina({
  59. filename: resolve(__dirname, 'fixtures/simple-isworkerthread.ts'),
  60. minThreads: 2,
  61. maxThreads: 2
  62. });
  63. const tasks = [];
  64. for (let i = 1; i <= 10000; i++) {
  65. tasks.push(pool.runTask(null));
  66. }
  67. await Promise.all(tasks);
  68. });