bun-shared-memory 0.1.0 → 0.1.2
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/README.md +40 -14
- package/package.json +5 -1
- package/src/refcount.ts +4 -0
package/README.md
CHANGED
|
@@ -34,8 +34,15 @@ See [SPEC.md](./SPEC.md) for the full specification.
|
|
|
34
34
|
|
|
35
35
|
## Usage
|
|
36
36
|
|
|
37
|
+
Always call `init()` first — on both the main thread and in every worker. It
|
|
38
|
+
sets up the shared-memory handshake and (optionally) configures the buffers.
|
|
39
|
+
|
|
37
40
|
```ts
|
|
38
|
-
import { Shared } from "bun-shared-memory";
|
|
41
|
+
import { Shared, init } from "bun-shared-memory";
|
|
42
|
+
|
|
43
|
+
// Main thread: call init() once at startup. It creates the shared memory
|
|
44
|
+
// (with the given buffer sizes) and answers buffer requests from workers.
|
|
45
|
+
await init({ initialSize: 64 * 1024, maxByteLength: 4 * 1024 * 1024 * 1024 });
|
|
39
46
|
|
|
40
47
|
// Get a wrapper to a named value in shared memory.
|
|
41
48
|
// If it doesn't exist, create it with the provided initial value.
|
|
@@ -67,6 +74,10 @@ const user2 = await Shared<User>("user-2", async () => {
|
|
|
67
74
|
});
|
|
68
75
|
```
|
|
69
76
|
|
|
77
|
+
In a worker, `await init()` requests the shared buffers from the main thread
|
|
78
|
+
and reconstructs the same memory, so `Shared()` reads/writes the same data as
|
|
79
|
+
the main thread. See [Worker integration](#worker-integration) below.
|
|
80
|
+
|
|
70
81
|
## Synchronization
|
|
71
82
|
|
|
72
83
|
All shared-memory reads and writes are protected by an `Atomics`-based mutex, so concurrent access from multiple workers is safe.
|
|
@@ -147,7 +158,7 @@ defragmented — no manual setup needed.
|
|
|
147
158
|
import { Shared, init } from "bun-shared-memory";
|
|
148
159
|
|
|
149
160
|
// Main thread: init() starts the coordinator automatically.
|
|
150
|
-
init();
|
|
161
|
+
await init();
|
|
151
162
|
const user = Shared("user-1", { name: "Alice" });
|
|
152
163
|
const copy = user.read(); // acquires a ref; released when `copy` is GC'd
|
|
153
164
|
```
|
|
@@ -192,8 +203,11 @@ workers, and each worker requests the shared buffers from the main thread.
|
|
|
192
203
|
// main.ts
|
|
193
204
|
import { Shared, init } from "bun-shared-memory";
|
|
194
205
|
|
|
206
|
+
// Always call init() first. It creates the shared memory (with the given
|
|
207
|
+
// buffer sizes) and answers buffer requests from every new worker.
|
|
208
|
+
await init({ initialSize: 64 * 1024, maxByteLength: 4 * 1024 * 1024 * 1024 });
|
|
209
|
+
|
|
195
210
|
const user = Shared("user-1", { name: "Alice", age: 30 });
|
|
196
|
-
init(); // answer buffer requests from every new worker
|
|
197
211
|
```
|
|
198
212
|
|
|
199
213
|
```ts
|
|
@@ -223,6 +237,11 @@ buffer-request listener and starts the refcount coordinator (auto-maintenance
|
|
|
223
237
|
> `Shared()` in a worker without `init()` throws an error rather than silently
|
|
224
238
|
> creating a separate, unshared memory.
|
|
225
239
|
|
|
240
|
+
The coordinator's maintenance timer is unref'd, so it does not keep the
|
|
241
|
+
process alive — the process exits naturally once no other work is pending
|
|
242
|
+
(e.g. after the last worker closes). You don't need to call `stopInit()` or
|
|
243
|
+
`process.exit()` to terminate.
|
|
244
|
+
|
|
226
245
|
A complete runnable example is in [`examples/worker/`](examples/worker/).
|
|
227
246
|
|
|
228
247
|
## Memory budget
|
|
@@ -233,21 +252,28 @@ index entry). A single `SharedArrayBuffer` can be at most **4 GiB**. By default
|
|
|
233
252
|
each buffer grows independently up to its own `maxByteLength`, so the _total_
|
|
234
253
|
memory can be several times that.
|
|
235
254
|
|
|
236
|
-
|
|
237
|
-
|
|
255
|
+
Configure the buffers when calling `init()` — pass `initialSize` (each
|
|
256
|
+
buffer's starting size, avoiding a grow on first use) and `maxByteLength`
|
|
257
|
+
(each buffer's cap). To cap the **total** memory across all buffers, pass
|
|
258
|
+
`maxTotalBytes`:
|
|
238
259
|
|
|
239
260
|
```ts
|
|
240
|
-
import {
|
|
241
|
-
|
|
242
|
-
//
|
|
243
|
-
//
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
261
|
+
import { init } from "bun-shared-memory";
|
|
262
|
+
|
|
263
|
+
// Each buffer starts at 64 KiB and grows up to 4 GiB; the sum of all buffers'
|
|
264
|
+
// physical sizes is additionally capped at 4 GiB (maxTotalBytes).
|
|
265
|
+
await init({
|
|
266
|
+
initialSize: 64 * 1024,
|
|
267
|
+
maxByteLength: 4 * 1024 * 1024 * 1024,
|
|
268
|
+
maxTotalBytes: 4 * 1024 * 1024 * 1024,
|
|
269
|
+
});
|
|
249
270
|
```
|
|
250
271
|
|
|
272
|
+
`maxTotalBytes` caps the **sum** of all buffers' physical sizes, so the total
|
|
273
|
+
stays within the budget. If a write would exceed the budget, a `RangeError` is
|
|
274
|
+
thrown. Without `maxTotalBytes`, each buffer simply grows up to its own
|
|
275
|
+
`maxByteLength`.
|
|
276
|
+
|
|
251
277
|
The budget is shared dynamically — buffers grow as needed, but only while the
|
|
252
278
|
sum of all buffers stays within `maxTotalBytes`. If a write would exceed the
|
|
253
279
|
budget, a `RangeError` is thrown.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bun-shared-memory",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Share data between Bun workers/threads using SharedArrayBuffer (zero-copy, 4-buffer architecture, Atomics-based synchronization).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "index.ts",
|
|
@@ -30,6 +30,10 @@
|
|
|
30
30
|
"zero-copy"
|
|
31
31
|
],
|
|
32
32
|
"license": "MIT",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/Kapsonfire/bun-shared-memory.git"
|
|
36
|
+
},
|
|
33
37
|
"engines": {
|
|
34
38
|
"bun": ">=1.4.0"
|
|
35
39
|
},
|
package/src/refcount.ts
CHANGED
|
@@ -316,6 +316,10 @@ export class RefCountCoordinator {
|
|
|
316
316
|
this.mem.withLock(() => this.mem.defragment());
|
|
317
317
|
}
|
|
318
318
|
}, intervalMs);
|
|
319
|
+
// Unref the timer so it does not keep the process alive. The coordinator
|
|
320
|
+
// keeps draining events and defragmenting while the process runs, but the
|
|
321
|
+
// process can still exit naturally when no other work is pending.
|
|
322
|
+
(this.maintenanceTimer as unknown as { unref?: () => void }).unref?.();
|
|
319
323
|
return () => this.stopAutoMaintenance();
|
|
320
324
|
}
|
|
321
325
|
|