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