-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathex4.js
More file actions
123 lines (94 loc) · 3 KB
/
ex4.js
File metadata and controls
123 lines (94 loc) · 3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
'use strict';
/*
Problem:
Implement `runLockCompetition(rounds)`.
Goal:
- Use a lock on shared memory for two workers competing on one critical section.
- Ensure mutual exclusion (no overlap).
- Avoid CPU-burning spin by using Atomics.wait/Atomics.notify.
Return shape:
{
counter: number,
overlapDetected: boolean,
waitsObserved: number
}
Starter code is intentionally incorrect:
- Uses naive busy spinlock.
- Does not use Atomics.wait for blocking.
*/
const { Worker } = require('node:worker_threads');
function waitFor(worker, type) {
return new Promise((resolve, reject) => {
const onMessage = (msg) => {
if (msg && msg.type === type) {
cleanup();
resolve(msg);
}
};
const onError = (err) => {
cleanup();
reject(err);
};
const cleanup = () => {
worker.off('message', onMessage);
worker.off('error', onError);
};
worker.on('message', onMessage);
worker.on('error', onError);
});
}
async function runLockCompetition(rounds) {
if (!Number.isInteger(rounds) || rounds < 1) {
throw new TypeError('rounds must be an integer >= 1');
}
// [lock, counter, inCritical, overlapFlag, waitsObserved, startGate]
const sab = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 6);
const shared = new Int32Array(sab);
// Start with lock=1 so workers must block/contend after gate opens.
Atomics.store(shared, 0, 1);
Atomics.store(shared, 5, 0);
const workerCode = `
'use strict';
const { parentPort, workerData } = require('node:worker_threads');
const shared = new Int32Array(workerData.sab);
const rounds = workerData.rounds;
parentPort.postMessage({ type: 'ready' });
Atomics.wait(shared, 5, 0);
function acquireSpin() {
while (Atomics.compareExchange(shared, 0, 0, 1) !== 0) {
// Busy spin (intentionally wrong for this exercise).
}
}
function release() {
Atomics.store(shared, 0, 0);
Atomics.notify(shared, 0, 1);
}
for (let i = 0; i < rounds; i++) {
acquireSpin();
const before = Atomics.add(shared, 2, 1);
if (before !== 0) {
Atomics.store(shared, 3, 1);
}
const current = Atomics.load(shared, 1);
Atomics.store(shared, 1, current + 1);
Atomics.sub(shared, 2, 1);
release();
}
parentPort.postMessage({ type: 'done' });
`;
const w1 = new Worker(workerCode, { eval: true, workerData: { sab, rounds } });
const w2 = new Worker(workerCode, { eval: true, workerData: { sab, rounds } });
await Promise.all([waitFor(w1, 'ready'), waitFor(w2, 'ready')]);
Atomics.store(shared, 5, 1);
Atomics.notify(shared, 5, 2);
Atomics.store(shared, 0, 0);
Atomics.notify(shared, 0, 2);
await Promise.all([waitFor(w1, 'done'), waitFor(w2, 'done')]);
await Promise.all([w1.terminate(), w2.terminate()]);
return {
counter: Atomics.load(shared, 1),
overlapDetected: Atomics.load(shared, 3) === 1,
waitsObserved: Atomics.load(shared, 4),
};
}
module.exports = { runLockCompetition };