git-watchtower 1.14.18 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/git-watchtower.js +2 -2
- package/package.json +1 -1
- package/src/utils/async.js +38 -13
package/bin/git-watchtower.js
CHANGED
|
@@ -1753,7 +1753,7 @@ const pollMutex = new Mutex();
|
|
|
1753
1753
|
async function pollGitChanges() {
|
|
1754
1754
|
// Skip if a poll is already in progress (don't queue)
|
|
1755
1755
|
if (pollMutex.isLocked()) return;
|
|
1756
|
-
await pollMutex.acquire();
|
|
1756
|
+
const pollToken = await pollMutex.acquire();
|
|
1757
1757
|
store.setState({ isPolling: true, pollingStatus: 'fetching' });
|
|
1758
1758
|
|
|
1759
1759
|
// Casino mode: start slot reels spinning (no sound - too annoying)
|
|
@@ -2122,7 +2122,7 @@ async function pollGitChanges() {
|
|
|
2122
2122
|
}
|
|
2123
2123
|
} finally {
|
|
2124
2124
|
store.setState({ isPolling: false });
|
|
2125
|
-
pollMutex.release();
|
|
2125
|
+
pollMutex.release(pollToken);
|
|
2126
2126
|
render();
|
|
2127
2127
|
}
|
|
2128
2128
|
}
|
package/package.json
CHANGED
package/src/utils/async.js
CHANGED
|
@@ -6,22 +6,33 @@
|
|
|
6
6
|
/**
|
|
7
7
|
* Simple mutex for preventing concurrent operations
|
|
8
8
|
* Use this to prevent race conditions in polling and server operations
|
|
9
|
+
*
|
|
10
|
+
* Mutual exclusion is enforced by ownership tokens: acquire() returns a
|
|
11
|
+
* unique Symbol, and release() requires the caller to hand that same
|
|
12
|
+
* token back. Previously release() was just a zero-arg "drain the next
|
|
13
|
+
* waiter" operation — so a double-release or a stray release() without
|
|
14
|
+
* a matching acquire() would advance the queue twice, handing the lock
|
|
15
|
+
* to two owners concurrently and silently breaking the invariant the
|
|
16
|
+
* mutex exists to protect. Tokens make that misuse throw instead.
|
|
9
17
|
*/
|
|
10
18
|
class Mutex {
|
|
11
19
|
constructor() {
|
|
12
|
-
|
|
20
|
+
/** @type {symbol | null} */
|
|
21
|
+
this._heldBy = null;
|
|
22
|
+
/** @type {Array<(token: symbol) => void>} */
|
|
13
23
|
this.queue = [];
|
|
14
24
|
}
|
|
15
25
|
|
|
16
26
|
/**
|
|
17
|
-
* Acquire the lock.
|
|
18
|
-
*
|
|
27
|
+
* Acquire the lock. Resolves with an opaque token that must be passed
|
|
28
|
+
* back to release(). Hold on to it and don't share it across callers.
|
|
29
|
+
* @returns {Promise<symbol>}
|
|
19
30
|
*/
|
|
20
31
|
async acquire() {
|
|
21
32
|
return new Promise((resolve) => {
|
|
22
|
-
if (
|
|
23
|
-
this.
|
|
24
|
-
resolve();
|
|
33
|
+
if (this._heldBy === null) {
|
|
34
|
+
this._heldBy = Symbol('mutex-token');
|
|
35
|
+
resolve(this._heldBy);
|
|
25
36
|
} else {
|
|
26
37
|
this.queue.push(resolve);
|
|
27
38
|
}
|
|
@@ -29,14 +40,28 @@ class Mutex {
|
|
|
29
40
|
}
|
|
30
41
|
|
|
31
42
|
/**
|
|
32
|
-
* Release the lock.
|
|
43
|
+
* Release the lock. The token must be the one returned by the
|
|
44
|
+
* corresponding acquire(). Throws for:
|
|
45
|
+
* - release() on an unlocked mutex (release-without-acquire)
|
|
46
|
+
* - release() with a token that doesn't match the current holder
|
|
47
|
+
* (double-release, or release from the wrong caller)
|
|
48
|
+
*
|
|
49
|
+
* @param {symbol} token
|
|
33
50
|
*/
|
|
34
|
-
release() {
|
|
51
|
+
release(token) {
|
|
52
|
+
if (this._heldBy === null) {
|
|
53
|
+
throw new Error('Mutex.release(): called on an unlocked mutex');
|
|
54
|
+
}
|
|
55
|
+
if (token !== this._heldBy) {
|
|
56
|
+
throw new Error('Mutex.release(): token does not match the current holder');
|
|
57
|
+
}
|
|
35
58
|
if (this.queue.length > 0) {
|
|
36
59
|
const next = this.queue.shift();
|
|
37
|
-
|
|
60
|
+
const nextToken = Symbol('mutex-token');
|
|
61
|
+
this._heldBy = nextToken;
|
|
62
|
+
next(nextToken);
|
|
38
63
|
} else {
|
|
39
|
-
this.
|
|
64
|
+
this._heldBy = null;
|
|
40
65
|
}
|
|
41
66
|
}
|
|
42
67
|
|
|
@@ -47,11 +72,11 @@ class Mutex {
|
|
|
47
72
|
* @returns {Promise<T>}
|
|
48
73
|
*/
|
|
49
74
|
async withLock(fn) {
|
|
50
|
-
await this.acquire();
|
|
75
|
+
const token = await this.acquire();
|
|
51
76
|
try {
|
|
52
77
|
return await fn();
|
|
53
78
|
} finally {
|
|
54
|
-
this.release();
|
|
79
|
+
this.release(token);
|
|
55
80
|
}
|
|
56
81
|
}
|
|
57
82
|
|
|
@@ -60,7 +85,7 @@ class Mutex {
|
|
|
60
85
|
* @returns {boolean}
|
|
61
86
|
*/
|
|
62
87
|
isLocked() {
|
|
63
|
-
return this.
|
|
88
|
+
return this._heldBy !== null;
|
|
64
89
|
}
|
|
65
90
|
|
|
66
91
|
/**
|