@nxtedition/scheduler 3.0.4 → 3.0.5

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.
Files changed (2) hide show
  1. package/lib/index.js +10 -18
  2. package/package.json +2 -2
package/lib/index.js CHANGED
@@ -1,15 +1,8 @@
1
- import os from 'node:os'
2
-
3
1
  const RUNNING_INDEX = 0
4
2
  const CONCURRENCY_INDEX = 1
5
3
 
6
4
  const maxInt = 2147483647
7
5
 
8
- // On x86/x64, aligned 32-bit reads do not tear, so a plain array access is safe
9
- // and avoids the overhead of Atomics.load() in V8. On other architectures (e.g. ARM),
10
- // use Atomics.load() to prevent tearing.
11
- const useAtomicLoad = os.arch() !== 'x64' && os.arch() !== 'ia32'
12
-
13
6
  class FastQueue {
14
7
  idx = 0
15
8
  cnt = 0
@@ -152,22 +145,21 @@ export class Scheduler {
152
145
  const queue = this.#queues[p + 3]
153
146
 
154
147
  if (this.#stateView) {
155
- // NOTE: The read of stateView followed by Atomics.add is a TOCTOU race. Multiple
156
- // workers may simultaneously read a value below the concurrency limit and all
157
- // proceed, briefly exceeding it by up to N-1 (where N is the number of workers).
158
- // This is by design — the concurrency limit is a soft / best-effort constraint.
159
- // Small, transient over-subscriptions are acceptable and self-correcting on the
160
- // next release() cycle.
161
- // Plain array read on x86 (aligned 32-bit reads don't tear); Atomics.load elsewhere.
162
- const running = useAtomicLoad
163
- ? Atomics.load(this.#stateView, RUNNING_INDEX)
164
- : this.#stateView[RUNNING_INDEX]
165
- if (this.#running < 1 || running < this.#concurrency) {
148
+ // Make sure we are always running at least one local job even if we might globally over subscribe.
149
+ if (this.#running < 1) {
166
150
  Atomics.add(this.#stateView, RUNNING_INDEX, 1)
167
151
  this.#running += 1
168
152
  fn(opaque)
169
153
  return
170
154
  }
155
+
156
+ if (Atomics.add(this.#stateView, RUNNING_INDEX, 1) >= this.#concurrency) {
157
+ Atomics.sub(this.#stateView, RUNNING_INDEX, 1)
158
+ } else {
159
+ this.#running += 1
160
+ fn(opaque)
161
+ return
162
+ }
171
163
  } else if ((this.#running < 1 && this.#concurrency > 0) || this.#running < this.#concurrency) {
172
164
  this.#running += 1
173
165
  fn(opaque)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nxtedition/scheduler",
3
- "version": "3.0.4",
3
+ "version": "3.0.5",
4
4
  "type": "module",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -27,5 +27,5 @@
27
27
  "rimraf": "^6.1.2",
28
28
  "typescript": "^5.9.3"
29
29
  },
30
- "gitHead": "17807cefcac092e20ebf99befddf0e742e5fc0e2"
30
+ "gitHead": "6174bfadecfd6232d4fd5c526fecf17d24528ff3"
31
31
  }