knitting 0.1.46 → 0.1.51

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 (62) hide show
  1. package/README.md +326 -95
  2. package/map.md +52 -8
  3. package/package.json +4 -3
  4. package/prebuilds/darwin-arm64-node-127/knitting_shared_memory.node +0 -0
  5. package/prebuilds/darwin-arm64-node-137/knitting_shared_memory.node +0 -0
  6. package/prebuilds/darwin-x64-node-127/knitting_shared_memory.node +0 -0
  7. package/prebuilds/darwin-x64-node-137/knitting_shared_memory.node +0 -0
  8. package/prebuilds/linux-x64-node-127/knitting_shared_memory.node +0 -0
  9. package/prebuilds/linux-x64-node-137/knitting_shared_memory.node +0 -0
  10. package/prebuilds/win32-x64/knitting_windows_shared_memory.dll +0 -0
  11. package/prebuilds/win32-x64-node-127/knitting_shared_memory.node +0 -0
  12. package/prebuilds/win32-x64-node-127/knitting_shm.node +0 -0
  13. package/prebuilds/win32-x64-node-137/knitting_shared_memory.node +0 -0
  14. package/prebuilds/win32-x64-node-137/knitting_shm.node +0 -0
  15. package/scripts/build-native-addons.ts +31 -5
  16. package/src/api.d.ts +3 -2
  17. package/src/api.js +135 -34
  18. package/src/common/task-source.d.ts +1 -0
  19. package/src/common/task-source.js +5 -0
  20. package/src/connections/bun.d.ts +2 -0
  21. package/src/connections/bun.js +64 -9
  22. package/src/connections/deno.d.ts +2 -0
  23. package/src/connections/deno.js +64 -9
  24. package/src/connections/file-descriptor.d.ts +2 -0
  25. package/src/connections/file-descriptor.js +24 -2
  26. package/src/connections/node.d.ts +2 -1
  27. package/src/connections/node.js +17 -14
  28. package/src/connections/posix.d.ts +1 -0
  29. package/src/connections/posix.js +6 -0
  30. package/src/connections/process-shared-buffer.d.ts +3 -1
  31. package/src/connections/process-shared-buffer.js +19 -13
  32. package/src/connections/types.d.ts +2 -0
  33. package/src/connections/windows.d.ts +28 -0
  34. package/src/connections/windows.js +224 -0
  35. package/src/knitting_shared_memory.cc +319 -26
  36. package/src/knitting_windows_shared_memory.cc +156 -0
  37. package/src/memory/lock.js +8 -168
  38. package/src/memory/payloadCodec.js +28 -34
  39. package/src/memory/shared-buffer-io.d.ts +2 -0
  40. package/src/memory/shared-buffer-io.js +23 -0
  41. package/src/runtime/inline-executor.d.ts +2 -2
  42. package/src/runtime/inline-executor.js +15 -3
  43. package/src/runtime/pool.d.ts +3 -14
  44. package/src/runtime/pool.js +12 -543
  45. package/src/runtime/process-worker.d.ts +92 -0
  46. package/src/runtime/process-worker.js +670 -0
  47. package/src/runtime/tx-queue.d.ts +1 -1
  48. package/src/runtime/tx-queue.js +3 -1
  49. package/src/shared/abortSignal.d.ts +1 -1
  50. package/src/shared/abortSignal.js +10 -2
  51. package/src/types.d.ts +67 -8
  52. package/src/worker/bootstrap.d.ts +5 -0
  53. package/src/worker/bootstrap.js +78 -0
  54. package/src/worker/composable-runners.js +0 -8
  55. package/src/worker/loop.js +23 -156
  56. package/src/worker/process-worker-bootstrap.d.ts +8 -0
  57. package/src/worker/process-worker-bootstrap.js +160 -0
  58. package/src/worker/safety/startup.d.ts +2 -1
  59. package/src/worker/safety/startup.js +5 -2
  60. package/src/worker/task-loader.d.ts +2 -1
  61. package/src/worker/task-loader.js +14 -2
  62. package/src/worker/timers.js +19 -5
@@ -0,0 +1,156 @@
1
+ #if !defined(_WIN32)
2
+ #error "knitting_windows_shared_memory.cc only supports Windows."
3
+ #endif
4
+
5
+ #define WIN32_LEAN_AND_MEAN
6
+ #include <windows.h>
7
+
8
+ #include <cstdint>
9
+
10
+ namespace {
11
+
12
+ constexpr uint32_t MODE_CREATE = 1;
13
+ constexpr uint32_t MODE_OPEN = 2;
14
+ constexpr size_t CACHE_LINE_SIZE = 64;
15
+
16
+ struct KnittingWindowsSharedMemory {
17
+ void* address;
18
+ uintptr_t handle;
19
+ uint64_t size;
20
+ uint32_t base_address_mod64;
21
+ uint32_t reserved;
22
+ };
23
+
24
+ static_assert(sizeof(KnittingWindowsSharedMemory) == 32);
25
+
26
+ bool HasName(const wchar_t* name) {
27
+ return name != nullptr && name[0] != L'\0';
28
+ }
29
+
30
+ uint32_t LastErrorOrInvalidParameter() {
31
+ DWORD error = GetLastError();
32
+ return error == ERROR_SUCCESS ? ERROR_INVALID_PARAMETER : error;
33
+ }
34
+
35
+ uint32_t FillMapping(
36
+ HANDLE handle,
37
+ uint64_t size,
38
+ KnittingWindowsSharedMemory* out
39
+ ) {
40
+ if (handle == nullptr || out == nullptr || size == 0) {
41
+ if (handle != nullptr) CloseHandle(handle);
42
+ return ERROR_INVALID_PARAMETER;
43
+ }
44
+
45
+ void* address = MapViewOfFile(
46
+ handle,
47
+ FILE_MAP_ALL_ACCESS,
48
+ 0,
49
+ 0,
50
+ static_cast<SIZE_T>(size)
51
+ );
52
+ if (address == nullptr) {
53
+ DWORD error = GetLastError();
54
+ CloseHandle(handle);
55
+ return error == ERROR_SUCCESS ? ERROR_INVALID_PARAMETER : error;
56
+ }
57
+
58
+ out->address = address;
59
+ out->handle = reinterpret_cast<uintptr_t>(handle);
60
+ out->size = size;
61
+ out->base_address_mod64 =
62
+ static_cast<uint32_t>(reinterpret_cast<uintptr_t>(address) % CACHE_LINE_SIZE);
63
+ out->reserved = 0;
64
+ return ERROR_SUCCESS;
65
+ }
66
+
67
+ } // namespace
68
+
69
+ extern "C" __declspec(dllexport) uint32_t
70
+ knitting_windows_create_shared_memory(
71
+ uint64_t size,
72
+ const wchar_t* name,
73
+ uint32_t mode,
74
+ KnittingWindowsSharedMemory* out
75
+ ) {
76
+ if (!HasName(name) || size == 0 || out == nullptr) {
77
+ return ERROR_INVALID_PARAMETER;
78
+ }
79
+
80
+ HANDLE handle = nullptr;
81
+ if (mode == MODE_OPEN) {
82
+ handle = OpenFileMappingW(FILE_MAP_ALL_ACCESS, FALSE, name);
83
+ if (handle == nullptr) return LastErrorOrInvalidParameter();
84
+ } else if (mode == MODE_CREATE) {
85
+ handle = CreateFileMappingW(
86
+ INVALID_HANDLE_VALUE,
87
+ nullptr,
88
+ PAGE_READWRITE,
89
+ static_cast<DWORD>(size >> 32),
90
+ static_cast<DWORD>(size & 0xffffffffULL),
91
+ name
92
+ );
93
+ if (handle == nullptr) return LastErrorOrInvalidParameter();
94
+ if (GetLastError() == ERROR_ALREADY_EXISTS) {
95
+ CloseHandle(handle);
96
+ return ERROR_ALREADY_EXISTS;
97
+ }
98
+ } else {
99
+ return ERROR_INVALID_PARAMETER;
100
+ }
101
+
102
+ return FillMapping(handle, size, out);
103
+ }
104
+
105
+ extern "C" __declspec(dllexport) uint32_t
106
+ knitting_windows_map_shared_memory(
107
+ uintptr_t handle_value,
108
+ uint64_t size,
109
+ const wchar_t* name,
110
+ KnittingWindowsSharedMemory* out
111
+ ) {
112
+ HANDLE handle = nullptr;
113
+ if (HasName(name)) {
114
+ handle = OpenFileMappingW(FILE_MAP_ALL_ACCESS, FALSE, name);
115
+ if (handle == nullptr) return LastErrorOrInvalidParameter();
116
+ } else if (handle_value != 0) {
117
+ BOOL ok = DuplicateHandle(
118
+ GetCurrentProcess(),
119
+ reinterpret_cast<HANDLE>(handle_value),
120
+ GetCurrentProcess(),
121
+ &handle,
122
+ 0,
123
+ FALSE,
124
+ DUPLICATE_SAME_ACCESS
125
+ );
126
+ if (!ok) return LastErrorOrInvalidParameter();
127
+ } else {
128
+ return ERROR_INVALID_PARAMETER;
129
+ }
130
+
131
+ return FillMapping(handle, size, out);
132
+ }
133
+
134
+ extern "C" __declspec(dllexport) uint32_t
135
+ knitting_windows_close_shared_memory(KnittingWindowsSharedMemory* mapping) {
136
+ if (mapping == nullptr) return ERROR_INVALID_PARAMETER;
137
+
138
+ uint32_t result = ERROR_SUCCESS;
139
+ if (mapping->address != nullptr && !UnmapViewOfFile(mapping->address)) {
140
+ result = LastErrorOrInvalidParameter();
141
+ }
142
+
143
+ if (mapping->handle != 0) {
144
+ HANDLE handle = reinterpret_cast<HANDLE>(mapping->handle);
145
+ if (!CloseHandle(handle) && result == ERROR_SUCCESS) {
146
+ result = LastErrorOrInvalidParameter();
147
+ }
148
+ }
149
+
150
+ mapping->address = nullptr;
151
+ mapping->handle = 0;
152
+ mapping->size = 0;
153
+ mapping->base_address_mod64 = 0;
154
+ mapping->reserved = 0;
155
+ return result;
156
+ }
@@ -564,171 +564,9 @@ export const lock2 = ({ headers, headerSlotStrideU32, LockBoundSector, payload,
564
564
  return modified;
565
565
  };
566
566
  }
567
- if (!shouldSettle) {
568
- if (!onResolved) {
569
- return () => {
570
- let diff = (a_load(hostBits, 0) ^ LastWorker) | 0;
571
- if (diff === 0)
572
- return 0;
573
- let modified = 0;
574
- let consumedBits = 0 | 0;
575
- let last = lastResolved;
576
- if (last === 32) {
577
- const idx = 31 - clz32(diff);
578
- const selectedBit = 1 << idx;
579
- const task = getTask(idx);
580
- decodeTask(task, idx);
581
- consumedBits = (consumedBits ^ selectedBit) | 0;
582
- settleTask(task);
583
- diff ^= selectedBit;
584
- modified++;
585
- if ((modified & 7) === 0 && consumedBits !== 0) {
586
- LastWorker = (LastWorker ^ consumedBits) | 0;
587
- a_store(workerBits, 0, LastWorker);
588
- consumedBits = 0 | 0;
589
- }
590
- last = idx;
591
- }
592
- while (diff !== 0) {
593
- const lowerMask = last === 31 ? 0x7fffffff : ((1 << last) - 1);
594
- let pick = diff & lowerMask;
595
- if (pick === 0)
596
- pick = diff;
597
- const idx = 31 - clz32(pick);
598
- const selectedBit = 1 << idx;
599
- const task = getTask(idx);
600
- decodeTask(task, idx);
601
- consumedBits = (consumedBits ^ selectedBit) | 0;
602
- settleTask(task);
603
- diff ^= selectedBit;
604
- modified++;
605
- if ((modified & 7) === 0 && consumedBits !== 0) {
606
- LastWorker = (LastWorker ^ consumedBits) | 0;
607
- a_store(workerBits, 0, LastWorker);
608
- consumedBits = 0 | 0;
609
- }
610
- last = idx;
611
- }
612
- if (consumedBits !== 0) {
613
- LastWorker = (LastWorker ^ consumedBits) | 0;
614
- a_store(workerBits, 0, LastWorker);
615
- }
616
- lastResolved = last;
617
- return modified;
618
- };
619
- }
620
- const onResolvedTask = onResolved;
621
- return () => {
622
- let diff = (a_load(hostBits, 0) ^ LastWorker) | 0;
623
- if (diff === 0)
624
- return 0;
625
- let modified = 0;
626
- let consumedBits = 0 | 0;
627
- let last = lastResolved;
628
- if (last === 32) {
629
- const idx = 31 - clz32(diff);
630
- const selectedBit = 1 << idx;
631
- const task = getTask(idx);
632
- decodeTask(task, idx);
633
- consumedBits = (consumedBits ^ selectedBit) | 0;
634
- settleTask(task);
635
- onResolvedTask(task);
636
- diff ^= selectedBit;
637
- modified++;
638
- if ((modified & 7) === 0 && consumedBits !== 0) {
639
- LastWorker = (LastWorker ^ consumedBits) | 0;
640
- a_store(workerBits, 0, LastWorker);
641
- consumedBits = 0 | 0;
642
- }
643
- last = idx;
644
- }
645
- while (diff !== 0) {
646
- const lowerMask = last === 31 ? 0x7fffffff : ((1 << last) - 1);
647
- let pick = diff & lowerMask;
648
- if (pick === 0)
649
- pick = diff;
650
- const idx = 31 - clz32(pick);
651
- const selectedBit = 1 << idx;
652
- const task = getTask(idx);
653
- decodeTask(task, idx);
654
- consumedBits = (consumedBits ^ selectedBit) | 0;
655
- settleTask(task);
656
- onResolvedTask(task);
657
- diff ^= selectedBit;
658
- modified++;
659
- if ((modified & 7) === 0 && consumedBits !== 0) {
660
- LastWorker = (LastWorker ^ consumedBits) | 0;
661
- a_store(workerBits, 0, LastWorker);
662
- consumedBits = 0 | 0;
663
- }
664
- last = idx;
665
- }
666
- if (consumedBits !== 0) {
667
- LastWorker = (LastWorker ^ consumedBits) | 0;
668
- a_store(workerBits, 0, LastWorker);
669
- }
670
- lastResolved = last;
671
- return modified;
672
- };
673
- }
567
+ const hasOnResolved = onResolved !== undefined;
568
+ const onResolvedTask = onResolved ?? def;
674
569
  const shouldSettleTask = shouldSettle;
675
- if (!onResolved) {
676
- return () => {
677
- let diff = (a_load(hostBits, 0) ^ LastWorker) | 0;
678
- if (diff === 0)
679
- return 0;
680
- let modified = 0;
681
- let consumedBits = 0 | 0;
682
- let last = lastResolved;
683
- if (last === 32) {
684
- const idx = 31 - clz32(diff);
685
- const selectedBit = 1 << idx;
686
- const task = getTask(idx);
687
- decodeTask(task, idx);
688
- consumedBits = (consumedBits ^ selectedBit) | 0;
689
- if (shouldSettleTask(task)) {
690
- settleTask(task);
691
- }
692
- diff ^= selectedBit;
693
- modified++;
694
- if ((modified & 7) === 0 && consumedBits !== 0) {
695
- LastWorker = (LastWorker ^ consumedBits) | 0;
696
- a_store(workerBits, 0, LastWorker);
697
- consumedBits = 0 | 0;
698
- }
699
- last = idx;
700
- }
701
- while (diff !== 0) {
702
- const lowerMask = last === 31 ? 0x7fffffff : ((1 << last) - 1);
703
- let pick = diff & lowerMask;
704
- if (pick === 0)
705
- pick = diff;
706
- const idx = 31 - clz32(pick);
707
- const selectedBit = 1 << idx;
708
- const task = getTask(idx);
709
- decodeTask(task, idx);
710
- consumedBits = (consumedBits ^ selectedBit) | 0;
711
- if (shouldSettleTask(task)) {
712
- settleTask(task);
713
- }
714
- diff ^= selectedBit;
715
- modified++;
716
- if ((modified & 7) === 0 && consumedBits !== 0) {
717
- LastWorker = (LastWorker ^ consumedBits) | 0;
718
- a_store(workerBits, 0, LastWorker);
719
- consumedBits = 0 | 0;
720
- }
721
- last = idx;
722
- }
723
- if (consumedBits !== 0) {
724
- LastWorker = (LastWorker ^ consumedBits) | 0;
725
- a_store(workerBits, 0, LastWorker);
726
- }
727
- lastResolved = last;
728
- return modified;
729
- };
730
- }
731
- const onResolvedTask = onResolved;
732
570
  return () => {
733
571
  let diff = (a_load(hostBits, 0) ^ LastWorker) | 0;
734
572
  if (diff === 0)
@@ -742,9 +580,10 @@ export const lock2 = ({ headers, headerSlotStrideU32, LockBoundSector, payload,
742
580
  const task = getTask(idx);
743
581
  decodeTask(task, idx);
744
582
  consumedBits = (consumedBits ^ selectedBit) | 0;
745
- if (shouldSettleTask(task)) {
583
+ if (shouldSettleTask === undefined || shouldSettleTask(task)) {
746
584
  settleTask(task);
747
- onResolvedTask(task);
585
+ if (hasOnResolved)
586
+ onResolvedTask(task);
748
587
  }
749
588
  diff ^= selectedBit;
750
589
  modified++;
@@ -765,9 +604,10 @@ export const lock2 = ({ headers, headerSlotStrideU32, LockBoundSector, payload,
765
604
  const task = getTask(idx);
766
605
  decodeTask(task, idx);
767
606
  consumedBits = (consumedBits ^ selectedBit) | 0;
768
- if (shouldSettleTask(task)) {
607
+ if (shouldSettleTask === undefined || shouldSettleTask(task)) {
769
608
  settleTask(task);
770
- onResolvedTask(task);
609
+ if (hasOnResolved)
610
+ onResolvedTask(task);
771
611
  }
772
612
  diff ^= selectedBit;
773
613
  modified++;
@@ -92,22 +92,14 @@ const decodeExternalPayload = (raw) => {
92
92
  : { codec: codecId, metadata };
93
93
  };
94
94
  const PROCESS_SHARED_BUFFER_NUMERIC_WORDS = 8;
95
- const PROCESS_SHARED_BUFFER_NUMERIC_BYTES = PROCESS_SHARED_BUFFER_NUMERIC_WORDS * Uint32Array.BYTES_PER_ELEMENT;
96
95
  const NUMERIC_SENTINEL = 0xffffffff;
97
- const readProcessSharedBufferNumericPayload = (bytes) => {
98
- const out = new Uint32Array(PROCESS_SHARED_BUFFER_NUMERIC_WORDS);
99
- const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
100
- for (let i = 0; i < PROCESS_SHARED_BUFFER_NUMERIC_WORDS; i++) {
101
- out[i] = view.getUint32(i * Uint32Array.BYTES_PER_ELEMENT, true);
102
- }
103
- return out;
104
- };
105
- const decodeProcessSharedBufferNumeric = (bytes) => {
106
- const metadata = readProcessSharedBufferNumericPayload(bytes);
96
+ const decodeProcessSharedBufferNumericWords = (words) => {
107
97
  const codec = externalPayloadGlobal.__KNITTING_PAYLOAD_CODECS__?.[PROCESS_SHARED_BUFFER_CODEC_ID];
108
- return typeof codec?.decodeNumeric === "function"
109
- ? codec.decodeNumeric(metadata)
110
- : { codec: PROCESS_SHARED_BUFFER_CODEC_ID, metadata };
98
+ if (typeof codec?.decodeNumeric === "function")
99
+ return codec.decodeNumeric(words);
100
+ // `words` is a reusable scratch; copy it on the (unreachable in practice)
101
+ // codec-missing diagnostic path so the escaped value never aliases it.
102
+ return { codec: PROCESS_SHARED_BUFFER_CODEC_ID, metadata: Array.from(words) };
111
103
  };
112
104
  const tryEncodePrimitiveTask = (task) => {
113
105
  const value = task.value;
@@ -299,7 +291,7 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
299
291
  payloadConfig: resolvedPayloadConfig,
300
292
  textCompat: textCompat?.payload,
301
293
  });
302
- const { maxBytes: staticMaxBytes, writeBinary: writeStaticBinary, writeBuffer: writeStaticBuffer, writeArrayBuffer: writeStaticArrayBuffer, writeExactUint8Array: writeStaticExactUint8Array, write8Binary: writeStatic8Binary, writeUtf8: writeStaticUtf8, } = requireStaticIO(headersBuffer, headerSlotStrideU32, textCompat?.headers);
294
+ const { maxBytes: staticMaxBytes, writeBinary: writeStaticBinary, writeBuffer: writeStaticBuffer, writeArrayBuffer: writeStaticArrayBuffer, writeExactUint8Array: writeStaticExactUint8Array, writeU32Words: writeStaticU32Words, write8Binary: writeStatic8Binary, writeUtf8: writeStaticUtf8, } = requireStaticIO(headersBuffer, headerSlotStrideU32, textCompat?.headers);
303
295
  const dynamicLimitError = (task, actualBytes, label) => encoderError({
304
296
  task,
305
297
  type: ErrorKnitting.Serializable,
@@ -523,14 +515,11 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
523
515
  task.value = null;
524
516
  return true;
525
517
  };
526
- const processSharedBufferScratch = new Uint8Array(PROCESS_SHARED_BUFFER_NUMERIC_BYTES);
527
- const processSharedBufferScratchView = new DataView(processSharedBufferScratch.buffer);
528
- const writeProcessSharedBufferWord = (index, value) => {
529
- processSharedBufferScratchView.setUint32(index * Uint32Array.BYTES_PER_ELEMENT, value, true);
530
- };
518
+ const processSharedBufferWords = new Uint32Array(PROCESS_SHARED_BUFFER_NUMERIC_WORDS);
531
519
  const tryEncodeProcessSharedBufferNumeric = (task, slotIndex, value) => {
532
520
  const descriptor = value.descriptor;
533
521
  if (descriptor === undefined ||
522
+ descriptor.name !== undefined ||
534
523
  !isU32(descriptor.fd) ||
535
524
  !isU32(descriptor.size) ||
536
525
  !isU32(descriptor.byteLength) ||
@@ -543,19 +532,20 @@ export const encodePayload = ({ lockSector, payload, sab, payloadConfig, headers
543
532
  !isU32(baseAddressMod64)) {
544
533
  return false;
545
534
  }
546
- writeProcessSharedBufferWord(0, descriptor.fd);
547
- writeProcessSharedBufferWord(1, descriptor.size);
548
- writeProcessSharedBufferWord(2, descriptor.byteLength);
549
- writeProcessSharedBufferWord(3, value.byteOffset);
550
- writeProcessSharedBufferWord(4, value.byteLength);
551
- writeProcessSharedBufferWord(5, runtimeCode(descriptor.runtime));
552
- writeProcessSharedBufferWord(6, kindCode(descriptor.kind));
553
- writeProcessSharedBufferWord(7, baseAddressMod64 === undefined ? NUMERIC_SENTINEL : baseAddressMod64);
554
- const written = writeStaticBinary(processSharedBufferScratch, slotIndex);
555
- if (written !== PROCESS_SHARED_BUFFER_NUMERIC_BYTES)
556
- return false;
535
+ processSharedBufferWords[0] = descriptor.fd;
536
+ processSharedBufferWords[1] = descriptor.size;
537
+ processSharedBufferWords[2] = descriptor.byteLength;
538
+ processSharedBufferWords[3] = value.byteOffset;
539
+ processSharedBufferWords[4] = value.byteLength;
540
+ processSharedBufferWords[5] = runtimeCode(descriptor.runtime);
541
+ processSharedBufferWords[6] = kindCode(descriptor.kind);
542
+ processSharedBufferWords[7] = baseAddressMod64 === undefined
543
+ ? NUMERIC_SENTINEL
544
+ : baseAddressMod64;
557
545
  task[TaskIndex.Type] = PayloadBuffer.ProcessSharedBuffer;
558
- task[TaskIndex.PayloadLen] = written;
546
+ // Static region is a Uint32Array shared in-process; write the descriptor
547
+ // words straight in instead of staging bytes through a DataView + copy.
548
+ task[TaskIndex.PayloadLen] = writeStaticU32Words(processSharedBufferWords, PROCESS_SHARED_BUFFER_NUMERIC_WORDS, slotIndex);
559
549
  task.value = null;
560
550
  return true;
561
551
  };
@@ -968,7 +958,11 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
968
958
  payloadConfig: resolvedPayloadConfig,
969
959
  textCompat: textCompat?.payload,
970
960
  });
971
- const { readUtf8: readStaticUtf8, readBytesCopy: readStaticBytesCopy, readBytesBufferCopy: readStaticBufferCopy, readBufferCopy: readStaticBuffer, readUint8ArrayCopy: readStaticUint8ArrayCopy, readBytesArrayBufferCopy: readStaticArrayBufferCopy, readArrayBufferCopy: readStaticArrayBuffer, read8BytesFloatCopy: readStatic8BytesFloatCopy, } = requireStaticIO(headersBuffer, headerSlotStrideU32, textCompat?.headers);
961
+ const { readUtf8: readStaticUtf8, readBytesBufferCopy: readStaticBufferCopy, readBufferCopy: readStaticBuffer, readUint8ArrayCopy: readStaticUint8ArrayCopy, readBytesArrayBufferCopy: readStaticArrayBufferCopy, readArrayBufferCopy: readStaticArrayBuffer, read8BytesFloatCopy: readStatic8BytesFloatCopy, readU32Words: readStaticU32Words, } = requireStaticIO(headersBuffer, headerSlotStrideU32, textCompat?.headers);
962
+ // Reusable scratch for the ProcessSharedBuffer raw-word decode. Safe to share:
963
+ // decode is single-consumer and not re-entrant, and the words are consumed
964
+ // synchronously when building the ProcessSharedBuffer.
965
+ const processSharedBufferWords = new Uint32Array(PROCESS_SHARED_BUFFER_NUMERIC_WORDS);
972
966
  // TODO: remove slotIndex and make that all their callers
973
967
  // store the slot in their Task, to just get it when it comes
974
968
  // to the static versions of decoding
@@ -1117,7 +1111,7 @@ export const decodePayload = ({ lockSector, payload, sab, payloadConfig, headers
1117
1111
  task.value = decodeExternalPayload(readStaticUtf8(0, task[TaskIndex.PayloadLen], slotIndex));
1118
1112
  return;
1119
1113
  case PayloadBuffer.ProcessSharedBuffer:
1120
- task.value = decodeProcessSharedBufferNumeric(readStaticBytesCopy(0, task[TaskIndex.PayloadLen], slotIndex));
1114
+ task.value = decodeProcessSharedBufferNumericWords(readStaticU32Words(processSharedBufferWords, PROCESS_SHARED_BUFFER_NUMERIC_WORDS, slotIndex));
1121
1115
  return;
1122
1116
  case PayloadBuffer.Date:
1123
1117
  Uint32View[0] = task[TaskIndex.Start];
@@ -37,6 +37,8 @@ export declare const createSharedStaticBufferIO: ({ headersBuffer, slotStrideU32
37
37
  writeArrayBuffer: (src: ArrayBuffer, at: number, start?: number) => number;
38
38
  writeExactUint8Array: (src: Uint8Array, at: number, start?: number) => number;
39
39
  writeUint8Array: (src: Uint8Array, at: number, start?: number) => number;
40
+ writeU32Words: (words: ArrayLike<number>, count: number, at: number) => number;
41
+ readU32Words: (out: Uint32Array, count: number, at: number) => Uint32Array;
40
42
  write8Binary: (src: Float64Array, at: number, start?: number) => number;
41
43
  readBytesCopy: (start: number, end: number, at: number) => Uint8Array<ArrayBuffer>;
42
44
  readBytesView: (start: number, end: number, at: number) => Uint8Array<SharedArrayBuffer>;
@@ -269,6 +269,27 @@ export const createSharedStaticBufferIO = ({ headersBuffer, slotStrideU32, textC
269
269
  for (let i = 0; i < LockBound.slots; i++) {
270
270
  slotByteOffsets[i] = slotStartBytes(i) - baseByteOffset;
271
271
  }
272
+ // The static region is itself a Uint32Array, shared in-process with native
273
+ // endianness. Fixed-shape numeric payloads (e.g. ProcessSharedBuffer
274
+ // descriptors) can be written/read as raw words straight into the slot,
275
+ // skipping the byte scratch + DataView + copy that the generic paths require.
276
+ const baseU32 = new Uint32Array(buffer, baseByteOffset, (buffer.byteLength - baseByteOffset) >>> 2);
277
+ const slotU32Offsets = new Uint32Array(LockBound.slots);
278
+ for (let i = 0; i < LockBound.slots; i++) {
279
+ slotU32Offsets[i] = slotByteOffsets[i] >>> 2;
280
+ }
281
+ const writeU32Words = (words, count, at) => {
282
+ const base = slotU32Offsets[at];
283
+ for (let i = 0; i < count; i++)
284
+ baseU32[base + i] = words[i];
285
+ return count * u32Bytes;
286
+ };
287
+ const readU32Words = (out, count, at) => {
288
+ const base = slotU32Offsets[at];
289
+ for (let i = 0; i < count; i++)
290
+ out[i] = baseU32[base + i];
291
+ return out;
292
+ };
272
293
  const canWrite = (start, length) => (start | 0) >= 0 && (start + length) <= writableBytes;
273
294
  const writeUtf8 = (str, at) => {
274
295
  const start = slotByteOffsets[at];
@@ -364,6 +385,8 @@ export const createSharedStaticBufferIO = ({ headersBuffer, slotStrideU32, textC
364
385
  writeArrayBuffer,
365
386
  writeExactUint8Array,
366
387
  writeUint8Array,
388
+ writeU32Words,
389
+ readU32Words,
367
390
  write8Binary,
368
391
  readBytesCopy,
369
392
  readBytesView,
@@ -1,6 +1,6 @@
1
- import type { WorkerCall, tasks } from "../types.js";
1
+ import type { ComposedWithKey, tasks, WorkerCall } from "../types.js";
2
2
  export declare const createInlineExecutor: ({ tasks, genTaskID, batchSize, }: {
3
- tasks: tasks;
3
+ tasks: tasks | ComposedWithKey[];
4
4
  genTaskID: () => number;
5
5
  batchSize?: number;
6
6
  }) => {
@@ -69,9 +69,21 @@ const composeInlineCallable = (fn, timeout, useAbortToolkit = false) => {
69
69
  };
70
70
  };
71
71
  export const createInlineExecutor = ({ tasks, genTaskID, batchSize, }) => {
72
- const entries = Object.values(tasks)
73
- .sort((a, b) => a.id - b.id);
74
- const runners = entries.map((entry) => composeInlineCallable(entry.f, entry.timeout, entry.abortSignal !== undefined));
72
+ const entries = Array.isArray(tasks)
73
+ ? tasks
74
+ : Object.values(tasks).sort((a, b) => a.id - b.id);
75
+ const runners = entries.map((entry) => {
76
+ // Imported tasks must never execute on the host inline lane: their module
77
+ // import is meant to stay inside the worker so worker permission policies
78
+ // apply. The pool already routes them to worker lanes, but guard here as
79
+ // defense in depth so a dispatch regression can't silently import on host.
80
+ if (entry.imported === true) {
81
+ return () => {
82
+ throw new Error("Imported task cannot run on the host inline lane");
83
+ };
84
+ }
85
+ return composeInlineCallable(entry.f, entry.timeout, entry.abortSignal !== undefined);
86
+ });
75
87
  const initCap = 16;
76
88
  let fnByIndex = new Int32Array(initCap);
77
89
  let stateByIndex = new Int8Array(initCap).fill(-1 /* SlotStateMacro.Free */);
@@ -1,23 +1,13 @@
1
+ import { type ProcessSharedMemoryBacking } from "./process-worker.js";
1
2
  import { type Sab } from "../ipc/transport/shared-memory.js";
2
3
  import { lock2 } from "../memory/lock.js";
3
4
  import type { DebugOptions, DispatcherSettings, WorkerContext, WorkerData, WorkerSettings } from "../types.js";
4
5
  import "../worker/loop.js";
5
6
  import { type PayloadBufferOptions } from "../memory/payload-config.js";
6
- type ProcessSharedMemoryNativeMapping = {
7
- sab: SharedArrayBuffer;
8
- fd: number;
9
- size: number;
10
- baseAddressMod64?: number;
11
- };
12
- type ProcessSharedMemoryBacking = ProcessSharedMemoryNativeMapping & {
13
- runtime: "node";
14
- buffer: SharedArrayBuffer;
15
- kind: "shared-array-buffer";
16
- byteLength: number;
17
- };
18
- export declare const spawnWorkerContext: ({ list, ids, sab, thread, debug, totalNumberOfThread, source, at, workerOptions, workerExecArgv, permission, host, payload, payloadInitialBytes, payloadMaxBytes, bufferMode, maxPayloadBytes, abortSignalCapacity, usesAbortSignal, }: {
7
+ export declare const spawnWorkerContext: ({ list, ids, names, sab, thread, debug, totalNumberOfThread, source, at, workerOptions, workerExecArgv, permission, host, payload, payloadInitialBytes, payloadMaxBytes, bufferMode, maxPayloadBytes, abortSignalCapacity, usesAbortSignal, }: {
19
8
  list: string[];
20
9
  ids: number[];
10
+ names: string[];
21
11
  at: number[];
22
12
  sab?: Sab;
23
13
  thread: number;
@@ -40,4 +30,3 @@ export declare const spawnWorkerContext: ({ list, ids, sab, thread, debug, total
40
30
  processSharedMemoryBackings?: readonly ProcessSharedMemoryBacking[];
41
31
  };
42
32
  export type CreateContext = WorkerContext;
43
- export {};