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 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
- To cap the **total** memory across all buffers, pass `maxTotalBytes` when
237
- creating the memory (or to `init()`):
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 { SharedMemory, init } from "bun-shared-memory";
241
-
242
- // Total budget of 4 GiB shared across all buffers. Each buffer's own
243
- // maxByteLength is min(4 GiB, maxTotalBytes), and the sum of all buffers'
244
- // physical sizes is additionally capped at maxTotalBytes.
245
- const mem = new SharedMemory({ maxTotalBytes: 4 * 1024 * 1024 * 1024 });
246
-
247
- // Or set it when initializing the shared-memory handshake:
248
- await init({ maxTotalBytes: 4 * 1024 * 1024 * 1024 });
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.0",
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