knitting 0.1.46

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 (121) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +632 -0
  3. package/knitting.d.ts +4 -0
  4. package/knitting.js +5 -0
  5. package/map.md +264 -0
  6. package/package.json +77 -0
  7. package/prebuilds/darwin-arm64-node-127/knitting_shared_memory.node +0 -0
  8. package/prebuilds/darwin-arm64-node-127/knitting_shm.node +0 -0
  9. package/prebuilds/darwin-arm64-node-137/knitting_shared_memory.node +0 -0
  10. package/prebuilds/darwin-arm64-node-137/knitting_shm.node +0 -0
  11. package/prebuilds/darwin-x64-node-127/knitting_shared_memory.node +0 -0
  12. package/prebuilds/darwin-x64-node-127/knitting_shm.node +0 -0
  13. package/prebuilds/darwin-x64-node-137/knitting_shared_memory.node +0 -0
  14. package/prebuilds/darwin-x64-node-137/knitting_shm.node +0 -0
  15. package/prebuilds/linux-x64-node-127/knitting_shared_memory.node +0 -0
  16. package/prebuilds/linux-x64-node-127/knitting_shm.node +0 -0
  17. package/prebuilds/linux-x64-node-137/knitting_shared_memory.node +0 -0
  18. package/prebuilds/linux-x64-node-137/knitting_shm.node +0 -0
  19. package/process-shared-buffer.d.ts +1 -0
  20. package/process-shared-buffer.js +1 -0
  21. package/scripts/build-native-addons.ts +295 -0
  22. package/src/api.d.ts +55 -0
  23. package/src/api.js +384 -0
  24. package/src/common/envelope.d.ts +11 -0
  25. package/src/common/envelope.js +8 -0
  26. package/src/common/module-url.d.ts +1 -0
  27. package/src/common/module-url.js +24 -0
  28. package/src/common/node-compat.d.ts +20 -0
  29. package/src/common/node-compat.js +24 -0
  30. package/src/common/path-canonical.d.ts +6 -0
  31. package/src/common/path-canonical.js +41 -0
  32. package/src/common/runtime.d.ts +15 -0
  33. package/src/common/runtime.js +91 -0
  34. package/src/common/shared-buffer-region.d.ts +11 -0
  35. package/src/common/shared-buffer-region.js +21 -0
  36. package/src/common/shared-buffer-text.d.ts +16 -0
  37. package/src/common/shared-buffer-text.js +65 -0
  38. package/src/common/task-source.d.ts +2 -0
  39. package/src/common/task-source.js +79 -0
  40. package/src/common/task-symbol.d.ts +1 -0
  41. package/src/common/task-symbol.js +1 -0
  42. package/src/common/with-resolvers.d.ts +9 -0
  43. package/src/common/with-resolvers.js +23 -0
  44. package/src/common/worker-runtime.d.ts +40 -0
  45. package/src/common/worker-runtime.js +52 -0
  46. package/src/connections/bun.d.ts +20 -0
  47. package/src/connections/bun.js +159 -0
  48. package/src/connections/deno.d.ts +20 -0
  49. package/src/connections/deno.js +150 -0
  50. package/src/connections/file-descriptor.d.ts +37 -0
  51. package/src/connections/file-descriptor.js +139 -0
  52. package/src/connections/index.d.ts +3 -0
  53. package/src/connections/index.js +3 -0
  54. package/src/connections/node-addons.d.ts +5 -0
  55. package/src/connections/node-addons.js +43 -0
  56. package/src/connections/node.d.ts +29 -0
  57. package/src/connections/node.js +59 -0
  58. package/src/connections/posix.d.ts +31 -0
  59. package/src/connections/posix.js +71 -0
  60. package/src/connections/process-shared-buffer.d.ts +67 -0
  61. package/src/connections/process-shared-buffer.js +267 -0
  62. package/src/connections/types.d.ts +48 -0
  63. package/src/connections/types.js +37 -0
  64. package/src/error.d.ts +13 -0
  65. package/src/error.js +49 -0
  66. package/src/ipc/tools/ring-queue.d.ts +33 -0
  67. package/src/ipc/tools/ring-queue.js +159 -0
  68. package/src/ipc/transport/shared-memory.d.ts +25 -0
  69. package/src/ipc/transport/shared-memory.js +35 -0
  70. package/src/knitting_shared_memory.cc +436 -0
  71. package/src/knitting_shm.cc +476 -0
  72. package/src/memory/byte-carpet.d.ts +73 -0
  73. package/src/memory/byte-carpet.js +157 -0
  74. package/src/memory/lock.d.ts +190 -0
  75. package/src/memory/lock.js +856 -0
  76. package/src/memory/payload-config.d.ts +22 -0
  77. package/src/memory/payload-config.js +67 -0
  78. package/src/memory/payloadCodec.d.ts +46 -0
  79. package/src/memory/payloadCodec.js +1157 -0
  80. package/src/memory/regionRegistry.d.ts +17 -0
  81. package/src/memory/regionRegistry.js +285 -0
  82. package/src/memory/shared-buffer-io.d.ts +53 -0
  83. package/src/memory/shared-buffer-io.js +380 -0
  84. package/src/permission/index.d.ts +2 -0
  85. package/src/permission/index.js +2 -0
  86. package/src/permission/protocol.d.ts +166 -0
  87. package/src/permission/protocol.js +640 -0
  88. package/src/runtime/balancer.d.ts +19 -0
  89. package/src/runtime/balancer.js +149 -0
  90. package/src/runtime/dispatcher.d.ts +34 -0
  91. package/src/runtime/dispatcher.js +142 -0
  92. package/src/runtime/inline-executor.d.ts +10 -0
  93. package/src/runtime/inline-executor.js +270 -0
  94. package/src/runtime/pool.d.ts +43 -0
  95. package/src/runtime/pool.js +922 -0
  96. package/src/runtime/tx-queue.d.ts +25 -0
  97. package/src/runtime/tx-queue.js +144 -0
  98. package/src/shared/abortSignal.d.ts +23 -0
  99. package/src/shared/abortSignal.js +126 -0
  100. package/src/types.d.ts +283 -0
  101. package/src/types.js +2 -0
  102. package/src/worker/composable-runners.d.ts +12 -0
  103. package/src/worker/composable-runners.js +105 -0
  104. package/src/worker/loop.d.ts +2 -0
  105. package/src/worker/loop.js +453 -0
  106. package/src/worker/rx-queue.d.ts +22 -0
  107. package/src/worker/rx-queue.js +124 -0
  108. package/src/worker/safety/index.d.ts +4 -0
  109. package/src/worker/safety/index.js +4 -0
  110. package/src/worker/safety/performance.d.ts +1 -0
  111. package/src/worker/safety/performance.js +17 -0
  112. package/src/worker/safety/process.d.ts +2 -0
  113. package/src/worker/safety/process.js +79 -0
  114. package/src/worker/safety/startup.d.ts +16 -0
  115. package/src/worker/safety/startup.js +30 -0
  116. package/src/worker/safety/worker-data.d.ts +2 -0
  117. package/src/worker/safety/worker-data.js +36 -0
  118. package/src/worker/task-loader.d.ts +26 -0
  119. package/src/worker/task-loader.js +66 -0
  120. package/src/worker/timers.d.ts +18 -0
  121. package/src/worker/timers.js +97 -0
@@ -0,0 +1,436 @@
1
+ #if defined(__linux__) && !defined(_GNU_SOURCE)
2
+ #define _GNU_SOURCE
3
+ #endif
4
+
5
+ #if !defined(__linux__) && !defined(__APPLE__) && !defined(_WIN32)
6
+ #error "knitting_shared_memory.cc currently supports Linux, macOS, and Windows."
7
+ #endif
8
+
9
+ #include <node.h>
10
+ #include <v8.h>
11
+
12
+ #include <atomic>
13
+ #include <cerrno>
14
+ #include <cstdio>
15
+ #include <cstdint>
16
+ #include <cstring>
17
+ #include <memory>
18
+ #include <string>
19
+
20
+ #ifdef _WIN32
21
+ #define WIN32_LEAN_AND_MEAN
22
+ #include <windows.h>
23
+
24
+ #include <mutex>
25
+ #include <unordered_map>
26
+ #else
27
+ #include <fcntl.h>
28
+ #include <sys/mman.h>
29
+ #include <sys/stat.h>
30
+ #ifdef __linux__
31
+ #include <sys/syscall.h>
32
+ #endif
33
+ #include <unistd.h>
34
+ #endif
35
+
36
+ #ifndef MFD_CLOEXEC
37
+ #define MFD_CLOEXEC 0x0001U
38
+ #endif
39
+
40
+ namespace knitting_shared_memory {
41
+
42
+ constexpr size_t CACHE_LINE_SIZE = 64;
43
+
44
+ // Owns the OS resources behind one V8 SharedArrayBuffer. The JavaScript object
45
+ // keeps the V8 backing store alive; when it is collected, this mapping is
46
+ // unmapped and its per-view OS resource is closed.
47
+ struct SharedMapping {
48
+ #ifdef _WIN32
49
+ HANDLE handle = nullptr;
50
+ #else
51
+ int fd = -1;
52
+ #endif
53
+ };
54
+
55
+ size_t AlignUp(size_t value, size_t alignment) {
56
+ return (value + alignment - 1) & ~(alignment - 1);
57
+ }
58
+
59
+ #ifndef _WIN32
60
+ bool SetCloseOnExec(int fd) {
61
+ int flags = fcntl(fd, F_GETFD);
62
+ if (flags == -1) return false;
63
+ return fcntl(fd, F_SETFD, flags | FD_CLOEXEC) != -1;
64
+ }
65
+
66
+ int CreateSharedMemoryFd(const char* name) {
67
+ #ifdef __linux__
68
+ return static_cast<int>(syscall(SYS_memfd_create, name, MFD_CLOEXEC));
69
+ #else
70
+ (void)name;
71
+ static std::atomic<unsigned long> counter{0};
72
+ char shm_name[128];
73
+
74
+ for (int attempt = 0; attempt < 16; attempt++) {
75
+ unsigned long next = counter.fetch_add(1);
76
+ std::snprintf(
77
+ shm_name,
78
+ sizeof(shm_name),
79
+ "/knit_n_%05lx_%06lx_%02d",
80
+ static_cast<unsigned long>(getpid()) & 0xfffffUL,
81
+ next & 0xffffffUL,
82
+ attempt
83
+ );
84
+
85
+ int fd = shm_open(shm_name, O_CREAT | O_EXCL | O_RDWR, 0600);
86
+ if (fd >= 0) {
87
+ shm_unlink(shm_name);
88
+ if (SetCloseOnExec(fd)) return fd;
89
+
90
+ int saved = errno;
91
+ close(fd);
92
+ errno = saved;
93
+ return -1;
94
+ }
95
+
96
+ if (errno != EEXIST) return -1;
97
+ }
98
+
99
+ errno = EEXIST;
100
+ return -1;
101
+ #endif
102
+ }
103
+ #endif
104
+
105
+ void ThrowErrno(v8::Isolate* isolate, const char* message, int err = errno) {
106
+ std::string full = std::string(message) + ": " + std::strerror(err);
107
+ isolate->ThrowException(v8::Exception::Error(
108
+ v8::String::NewFromUtf8(isolate, full.c_str()).ToLocalChecked()
109
+ ));
110
+ }
111
+
112
+ #ifdef _WIN32
113
+ void ThrowWindowsError(
114
+ v8::Isolate* isolate,
115
+ const char* message,
116
+ DWORD err = GetLastError()
117
+ ) {
118
+ char* raw = nullptr;
119
+ DWORD len = FormatMessageA(
120
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
121
+ FORMAT_MESSAGE_FROM_SYSTEM |
122
+ FORMAT_MESSAGE_IGNORE_INSERTS,
123
+ nullptr,
124
+ err,
125
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
126
+ reinterpret_cast<char*>(&raw),
127
+ 0,
128
+ nullptr
129
+ );
130
+
131
+ std::string detail = len > 0 && raw != nullptr
132
+ ? std::string(raw, len)
133
+ : "Windows error " + std::to_string(static_cast<unsigned long>(err));
134
+ if (raw != nullptr) LocalFree(raw);
135
+
136
+ while (!detail.empty() && (detail.back() == '\n' || detail.back() == '\r')) {
137
+ detail.pop_back();
138
+ }
139
+
140
+ std::string full = std::string(message) + ": " + detail;
141
+ isolate->ThrowException(v8::Exception::Error(
142
+ v8::String::NewFromUtf8(isolate, full.c_str()).ToLocalChecked()
143
+ ));
144
+ }
145
+ #endif
146
+
147
+ void ThrowType(v8::Isolate* isolate, const char* message) {
148
+ isolate->ThrowException(v8::Exception::TypeError(
149
+ v8::String::NewFromUtf8(isolate, message).ToLocalChecked()
150
+ ));
151
+ }
152
+
153
+ void ThrowRange(v8::Isolate* isolate, const char* message) {
154
+ isolate->ThrowException(v8::Exception::RangeError(
155
+ v8::String::NewFromUtf8(isolate, message).ToLocalChecked()
156
+ ));
157
+ }
158
+
159
+ #ifdef _WIN32
160
+ std::atomic<int> next_mapping_id{1};
161
+ std::mutex registry_mutex;
162
+ std::unordered_map<int, HANDLE> registry_handles;
163
+
164
+ bool DuplicateHandleInCurrentProcess(HANDLE source, HANDLE* out) {
165
+ return DuplicateHandle(
166
+ GetCurrentProcess(),
167
+ source,
168
+ GetCurrentProcess(),
169
+ out,
170
+ 0,
171
+ FALSE,
172
+ DUPLICATE_SAME_ACCESS
173
+ ) != 0;
174
+ }
175
+
176
+ int RegisterPublicMappingHandle(HANDLE handle) {
177
+ HANDLE public_handle = nullptr;
178
+ if (!DuplicateHandleInCurrentProcess(handle, &public_handle)) {
179
+ return -1;
180
+ }
181
+
182
+ int id = next_mapping_id.fetch_add(1);
183
+ if (id <= 0) {
184
+ CloseHandle(public_handle);
185
+ return -1;
186
+ }
187
+
188
+ std::lock_guard<std::mutex> lock(registry_mutex);
189
+ registry_handles.emplace(id, public_handle);
190
+ return id;
191
+ }
192
+
193
+ HANDLE DuplicateRegisteredMappingHandle(int id) {
194
+ HANDLE source = nullptr;
195
+ {
196
+ std::lock_guard<std::mutex> lock(registry_mutex);
197
+ auto found = registry_handles.find(id);
198
+ if (found == registry_handles.end()) return nullptr;
199
+ source = found->second;
200
+ }
201
+
202
+ HANDLE duplicate = nullptr;
203
+ if (!DuplicateHandleInCurrentProcess(source, &duplicate)) {
204
+ return nullptr;
205
+ }
206
+ return duplicate;
207
+ }
208
+ #endif
209
+
210
+ void MappingDeleter(void* data, size_t length, void* deleter_data) {
211
+ #ifdef _WIN32
212
+ if (data != nullptr) {
213
+ UnmapViewOfFile(data);
214
+ }
215
+ #else
216
+ if (data != nullptr && length > 0) {
217
+ munmap(data, length);
218
+ }
219
+ #endif
220
+
221
+ SharedMapping* mapping = static_cast<SharedMapping*>(deleter_data);
222
+ if (mapping != nullptr) {
223
+ #ifdef _WIN32
224
+ if (mapping->handle != nullptr) CloseHandle(mapping->handle);
225
+ #else
226
+ if (mapping->fd >= 0) close(mapping->fd);
227
+ #endif
228
+ delete mapping;
229
+ }
230
+ }
231
+
232
+ void SetValue(
233
+ v8::Isolate* isolate,
234
+ v8::Local<v8::Context> context,
235
+ v8::Local<v8::Object> object,
236
+ const char* key,
237
+ v8::Local<v8::Value> value
238
+ ) {
239
+ object->Set(
240
+ context,
241
+ v8::String::NewFromUtf8(isolate, key).ToLocalChecked(),
242
+ value
243
+ ).Check();
244
+ }
245
+
246
+ void ReturnMappedRegion(
247
+ const v8::FunctionCallbackInfo<v8::Value>& args,
248
+ #ifdef _WIN32
249
+ HANDLE handle,
250
+ int fd,
251
+ #else
252
+ int fd,
253
+ #endif
254
+ size_t size
255
+ ) {
256
+ v8::Isolate* isolate = args.GetIsolate();
257
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
258
+
259
+ #ifdef _WIN32
260
+ void* mapped = MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, size);
261
+ if (mapped == nullptr) {
262
+ DWORD saved = GetLastError();
263
+ CloseHandle(handle);
264
+ ThrowWindowsError(isolate, "MapViewOfFile failed", saved);
265
+ return;
266
+ }
267
+ #else
268
+ void* mapped = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
269
+ if (mapped == MAP_FAILED) {
270
+ int saved = errno;
271
+ close(fd);
272
+ ThrowErrno(isolate, "mmap failed", saved);
273
+ return;
274
+ }
275
+ #endif
276
+
277
+ auto* mapping = new SharedMapping();
278
+ #ifdef _WIN32
279
+ mapping->handle = handle;
280
+ #else
281
+ mapping->fd = fd;
282
+ #endif
283
+
284
+ auto backing = v8::SharedArrayBuffer::NewBackingStore(
285
+ mapped,
286
+ size,
287
+ MappingDeleter,
288
+ mapping
289
+ );
290
+ auto shared = std::shared_ptr<v8::BackingStore>(std::move(backing));
291
+ v8::Local<v8::SharedArrayBuffer> sab =
292
+ v8::SharedArrayBuffer::New(isolate, shared);
293
+
294
+ v8::Local<v8::Object> out = v8::Object::New(isolate);
295
+ SetValue(isolate, context, out, "sab", sab);
296
+ SetValue(isolate, context, out, "fd", v8::Integer::New(isolate, fd));
297
+ SetValue(
298
+ isolate,
299
+ context,
300
+ out,
301
+ "size",
302
+ v8::Number::New(isolate, static_cast<double>(size))
303
+ );
304
+ SetValue(
305
+ isolate,
306
+ context,
307
+ out,
308
+ "baseAddressMod64",
309
+ v8::Integer::New(
310
+ isolate,
311
+ static_cast<int>(reinterpret_cast<uintptr_t>(mapped) % CACHE_LINE_SIZE)
312
+ )
313
+ );
314
+
315
+ args.GetReturnValue().Set(out);
316
+ }
317
+
318
+ void CreateSharedMemory(const v8::FunctionCallbackInfo<v8::Value>& args) {
319
+ v8::Isolate* isolate = args.GetIsolate();
320
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
321
+
322
+ if (args.Length() < 1 || !args[0]->IsNumber()) {
323
+ ThrowType(isolate, "createSharedMemory(size) requires size");
324
+ return;
325
+ }
326
+
327
+ v8::Maybe<int64_t> maybe_size = args[0]->IntegerValue(context);
328
+ if (maybe_size.IsNothing() || maybe_size.FromJust() <= 0) {
329
+ ThrowRange(isolate, "size must be positive");
330
+ return;
331
+ }
332
+
333
+ size_t size = AlignUp(static_cast<size_t>(maybe_size.FromJust()), CACHE_LINE_SIZE);
334
+
335
+ #ifdef _WIN32
336
+ uint64_t wide_size = static_cast<uint64_t>(size);
337
+ HANDLE handle = CreateFileMappingW(
338
+ INVALID_HANDLE_VALUE,
339
+ nullptr,
340
+ PAGE_READWRITE,
341
+ static_cast<DWORD>(wide_size >> 32),
342
+ static_cast<DWORD>(wide_size & 0xffffffffULL),
343
+ nullptr
344
+ );
345
+ if (handle == nullptr) {
346
+ ThrowWindowsError(isolate, "CreateFileMappingW failed");
347
+ return;
348
+ }
349
+
350
+ int fd = RegisterPublicMappingHandle(handle);
351
+ if (fd < 0) {
352
+ DWORD saved = GetLastError();
353
+ CloseHandle(handle);
354
+ ThrowWindowsError(isolate, "DuplicateHandle failed", saved);
355
+ return;
356
+ }
357
+
358
+ ReturnMappedRegion(args, handle, fd, size);
359
+ #else
360
+ int fd = CreateSharedMemoryFd("knitting_shared_memory");
361
+ if (fd == -1) {
362
+ ThrowErrno(isolate, "shared memory fd create failed");
363
+ return;
364
+ }
365
+
366
+ if (ftruncate(fd, static_cast<off_t>(size)) == -1) {
367
+ int saved = errno;
368
+ close(fd);
369
+ ThrowErrno(isolate, "ftruncate failed", saved);
370
+ return;
371
+ }
372
+
373
+ ReturnMappedRegion(args, fd, size);
374
+ #endif
375
+ }
376
+
377
+ void MapSharedMemory(const v8::FunctionCallbackInfo<v8::Value>& args) {
378
+ v8::Isolate* isolate = args.GetIsolate();
379
+ v8::Local<v8::Context> context = isolate->GetCurrentContext();
380
+
381
+ if (args.Length() < 2 || !args[0]->IsNumber() || !args[1]->IsNumber()) {
382
+ ThrowType(isolate, "mapSharedMemory(fd, size) requires fd and size");
383
+ return;
384
+ }
385
+
386
+ v8::Maybe<int32_t> maybe_fd = args[0]->Int32Value(context);
387
+ v8::Maybe<int64_t> maybe_size = args[1]->IntegerValue(context);
388
+ if (
389
+ maybe_fd.IsNothing() ||
390
+ maybe_size.IsNothing() ||
391
+ maybe_fd.FromJust() < 0 ||
392
+ maybe_size.FromJust() <= 0
393
+ ) {
394
+ ThrowRange(isolate, "fd and size must be positive");
395
+ return;
396
+ }
397
+
398
+ size_t size = AlignUp(static_cast<size_t>(maybe_size.FromJust()), CACHE_LINE_SIZE);
399
+
400
+ #ifdef _WIN32
401
+ int fd = maybe_fd.FromJust();
402
+ HANDLE handle = DuplicateRegisteredMappingHandle(fd);
403
+ if (handle == nullptr) {
404
+ ThrowWindowsError(isolate, "DuplicateHandle failed");
405
+ return;
406
+ }
407
+
408
+ ReturnMappedRegion(args, handle, fd, size);
409
+ #else
410
+ // Duplicate so each returned SAB owns exactly one fd. The caller can keep
411
+ // using or transferring its original descriptor independently.
412
+ int fd = dup(maybe_fd.FromJust());
413
+ if (fd == -1) {
414
+ ThrowErrno(isolate, "dup(fd) failed");
415
+ return;
416
+ }
417
+
418
+ if (!SetCloseOnExec(fd)) {
419
+ int saved = errno;
420
+ close(fd);
421
+ ThrowErrno(isolate, "fcntl(F_SETFD) failed", saved);
422
+ return;
423
+ }
424
+
425
+ ReturnMappedRegion(args, fd, size);
426
+ #endif
427
+ }
428
+
429
+ void Initialize(v8::Local<v8::Object> exports) {
430
+ NODE_SET_METHOD(exports, "createSharedMemory", CreateSharedMemory);
431
+ NODE_SET_METHOD(exports, "mapSharedMemory", MapSharedMemory);
432
+ }
433
+
434
+ NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
435
+
436
+ } // namespace knitting_shared_memory