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.
- package/README.md +326 -95
- package/map.md +52 -8
- package/package.json +4 -3
- package/prebuilds/darwin-arm64-node-127/knitting_shared_memory.node +0 -0
- package/prebuilds/darwin-arm64-node-137/knitting_shared_memory.node +0 -0
- package/prebuilds/darwin-x64-node-127/knitting_shared_memory.node +0 -0
- package/prebuilds/darwin-x64-node-137/knitting_shared_memory.node +0 -0
- package/prebuilds/linux-x64-node-127/knitting_shared_memory.node +0 -0
- package/prebuilds/linux-x64-node-137/knitting_shared_memory.node +0 -0
- package/prebuilds/win32-x64/knitting_windows_shared_memory.dll +0 -0
- package/prebuilds/win32-x64-node-127/knitting_shared_memory.node +0 -0
- package/prebuilds/win32-x64-node-127/knitting_shm.node +0 -0
- package/prebuilds/win32-x64-node-137/knitting_shared_memory.node +0 -0
- package/prebuilds/win32-x64-node-137/knitting_shm.node +0 -0
- package/scripts/build-native-addons.ts +31 -5
- package/src/api.d.ts +3 -2
- package/src/api.js +135 -34
- package/src/common/task-source.d.ts +1 -0
- package/src/common/task-source.js +5 -0
- package/src/connections/bun.d.ts +2 -0
- package/src/connections/bun.js +64 -9
- package/src/connections/deno.d.ts +2 -0
- package/src/connections/deno.js +64 -9
- package/src/connections/file-descriptor.d.ts +2 -0
- package/src/connections/file-descriptor.js +24 -2
- package/src/connections/node.d.ts +2 -1
- package/src/connections/node.js +17 -14
- package/src/connections/posix.d.ts +1 -0
- package/src/connections/posix.js +6 -0
- package/src/connections/process-shared-buffer.d.ts +3 -1
- package/src/connections/process-shared-buffer.js +19 -13
- package/src/connections/types.d.ts +2 -0
- package/src/connections/windows.d.ts +28 -0
- package/src/connections/windows.js +224 -0
- package/src/knitting_shared_memory.cc +319 -26
- package/src/knitting_windows_shared_memory.cc +156 -0
- package/src/memory/lock.js +8 -168
- package/src/memory/payloadCodec.js +28 -34
- package/src/memory/shared-buffer-io.d.ts +2 -0
- package/src/memory/shared-buffer-io.js +23 -0
- package/src/runtime/inline-executor.d.ts +2 -2
- package/src/runtime/inline-executor.js +15 -3
- package/src/runtime/pool.d.ts +3 -14
- package/src/runtime/pool.js +12 -543
- package/src/runtime/process-worker.d.ts +92 -0
- package/src/runtime/process-worker.js +670 -0
- package/src/runtime/tx-queue.d.ts +1 -1
- package/src/runtime/tx-queue.js +3 -1
- package/src/shared/abortSignal.d.ts +1 -1
- package/src/shared/abortSignal.js +10 -2
- package/src/types.d.ts +67 -8
- package/src/worker/bootstrap.d.ts +5 -0
- package/src/worker/bootstrap.js +78 -0
- package/src/worker/composable-runners.js +0 -8
- package/src/worker/loop.js +23 -156
- package/src/worker/process-worker-bootstrap.d.ts +8 -0
- package/src/worker/process-worker-bootstrap.js +160 -0
- package/src/worker/safety/startup.d.ts +2 -1
- package/src/worker/safety/startup.js +5 -2
- package/src/worker/task-loader.d.ts +2 -1
- package/src/worker/task-loader.js +14 -2
- 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
|
+
}
|
package/src/memory/lock.js
CHANGED
|
@@ -564,171 +564,9 @@ export const lock2 = ({ headers, headerSlotStrideU32, LockBoundSector, payload,
|
|
|
564
564
|
return modified;
|
|
565
565
|
};
|
|
566
566
|
}
|
|
567
|
-
|
|
568
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
|
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
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
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
|
-
|
|
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,
|
|
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 =
|
|
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 {
|
|
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 =
|
|
73
|
-
|
|
74
|
-
|
|
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 */);
|
package/src/runtime/pool.d.ts
CHANGED
|
@@ -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
|
-
|
|
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 {};
|