@gjsify/sab-native 0.4.12
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/lib/esm/_virtual/_rolldown/runtime.js +1 -0
- package/lib/esm/index.js +1 -0
- package/lib/types/index.d.ts +186 -0
- package/lib/types/shared-buffer.gjs.spec.d.ts +2 -0
- package/meson.build +57 -0
- package/package.json +65 -0
- package/prebuilds/linux-aarch64/GjsifySabNative-1.0.gir +430 -0
- package/prebuilds/linux-aarch64/GjsifySabNative-1.0.typelib +0 -0
- package/prebuilds/linux-aarch64/libgjsifysabnative.so +0 -0
- package/prebuilds/linux-ppc64/GjsifySabNative-1.0.gir +430 -0
- package/prebuilds/linux-ppc64/GjsifySabNative-1.0.typelib +0 -0
- package/prebuilds/linux-ppc64/libgjsifysabnative.so +0 -0
- package/prebuilds/linux-riscv64/GjsifySabNative-1.0.gir +429 -0
- package/prebuilds/linux-riscv64/GjsifySabNative-1.0.typelib +0 -0
- package/prebuilds/linux-riscv64/libgjsifysabnative.so +0 -0
- package/prebuilds/linux-s390x/GjsifySabNative-1.0.gir +430 -0
- package/prebuilds/linux-s390x/GjsifySabNative-1.0.typelib +0 -0
- package/prebuilds/linux-s390x/libgjsifysabnative.so +0 -0
- package/prebuilds/linux-x86_64/GjsifySabNative-1.0.gir +430 -0
- package/prebuilds/linux-x86_64/GjsifySabNative-1.0.typelib +0 -0
- package/prebuilds/linux-x86_64/libgjsifysabnative.so +0 -0
- package/src/vala/fd-passing.vala +101 -0
- package/src/vala/sab-helpers.c +461 -0
- package/src/vala/sab-helpers.h +150 -0
- package/src/vala/shared-buffer.vala +201 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* SharedBuffer — JS-visible wrapper around a memfd-backed mmap region.
|
|
3
|
+
*
|
|
4
|
+
* Lifecycle
|
|
5
|
+
* ─────────
|
|
6
|
+
* Two construction paths:
|
|
7
|
+
*
|
|
8
|
+
* 1. `SharedBuffer.create(size)` — allocates a fresh memfd, ftruncates to
|
|
9
|
+
* @size, and mmaps MAP_SHARED. The region owns the fd; it is closed
|
|
10
|
+
* and the mapping freed when the SharedBuffer instance is collected.
|
|
11
|
+
*
|
|
12
|
+
* 2. `SharedBuffer.from_fd(fd, size)` — mmaps a fd received from another
|
|
13
|
+
* process (typically via SCM_RIGHTS in @gjsify/worker_threads). The
|
|
14
|
+
* original fd is dup'd, so the caller can close their copy safely.
|
|
15
|
+
*
|
|
16
|
+
* Wire protocol
|
|
17
|
+
* ─────────────
|
|
18
|
+
* All multi-byte accessors are explicitly LE on the wire (little-endian),
|
|
19
|
+
* so a SharedBuffer written on x86_64 and read on s390x sees the same
|
|
20
|
+
* values. The C shim handles byte-swapping where needed.
|
|
21
|
+
*
|
|
22
|
+
* Atomics
|
|
23
|
+
* ───────
|
|
24
|
+
* `atomic_*_i32` family operates with SEQ_CST memory ordering via GCC
|
|
25
|
+
* `__atomic_*` builtins. `futex_wait` / `futex_wake` give cross-process
|
|
26
|
+
* wait/notify via Linux SYS_futex (FUTEX_*_PRIVATE flavour).
|
|
27
|
+
*
|
|
28
|
+
* Buffer ownership: read_bytes returns a GLib.Bytes that borrows the
|
|
29
|
+
* mmap'd memory (no copy) and ref-counts the region so the mapping
|
|
30
|
+
* survives until every outstanding bytes reference is released.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
namespace GjsifySabNative {
|
|
34
|
+
|
|
35
|
+
/* ── Opaque pointer wrappers + C entry points ───────────────────────── *
|
|
36
|
+
* Same pattern as @gjsify/http2-native: opaque region travels as void*
|
|
37
|
+
* across the FFI boundary; Vala destructor explicitly calls free. */
|
|
38
|
+
|
|
39
|
+
[CCode (cname = "gjsify_sab_region_new_anonymous",
|
|
40
|
+
cheader_filename = "sab-helpers.h")]
|
|
41
|
+
private extern void* _region_new_anonymous (size_t size);
|
|
42
|
+
|
|
43
|
+
[CCode (cname = "gjsify_sab_region_new_from_fd",
|
|
44
|
+
cheader_filename = "sab-helpers.h")]
|
|
45
|
+
private extern void* _region_new_from_fd (int fd, size_t size);
|
|
46
|
+
|
|
47
|
+
[CCode (cname = "gjsify_sab_region_free",
|
|
48
|
+
cheader_filename = "sab-helpers.h")]
|
|
49
|
+
private extern void _region_free (void* region);
|
|
50
|
+
|
|
51
|
+
[CCode (cname = "gjsify_sab_region_get_fd",
|
|
52
|
+
cheader_filename = "sab-helpers.h")]
|
|
53
|
+
private extern int _region_get_fd (void* region);
|
|
54
|
+
|
|
55
|
+
[CCode (cname = "gjsify_sab_region_get_size",
|
|
56
|
+
cheader_filename = "sab-helpers.h")]
|
|
57
|
+
private extern size_t _region_get_size (void* region);
|
|
58
|
+
|
|
59
|
+
/* Read/write */
|
|
60
|
+
|
|
61
|
+
[CCode (cname = "gjsify_sab_region_read_u8", cheader_filename = "sab-helpers.h")]
|
|
62
|
+
private extern uint8 _region_read_u8 (void* region, size_t offset);
|
|
63
|
+
[CCode (cname = "gjsify_sab_region_write_u8", cheader_filename = "sab-helpers.h")]
|
|
64
|
+
private extern void _region_write_u8 (void* region, size_t offset, uint8 v);
|
|
65
|
+
[CCode (cname = "gjsify_sab_region_read_u32_le", cheader_filename = "sab-helpers.h")]
|
|
66
|
+
private extern uint32 _region_read_u32_le (void* region, size_t offset);
|
|
67
|
+
[CCode (cname = "gjsify_sab_region_write_u32_le", cheader_filename = "sab-helpers.h")]
|
|
68
|
+
private extern void _region_write_u32_le (void* region, size_t offset, uint32 v);
|
|
69
|
+
[CCode (cname = "gjsify_sab_region_read_i32_le", cheader_filename = "sab-helpers.h")]
|
|
70
|
+
private extern int32 _region_read_i32_le (void* region, size_t offset);
|
|
71
|
+
[CCode (cname = "gjsify_sab_region_write_i32_le", cheader_filename = "sab-helpers.h")]
|
|
72
|
+
private extern void _region_write_i32_le (void* region, size_t offset, int32 v);
|
|
73
|
+
[CCode (cname = "gjsify_sab_region_read_u64_le", cheader_filename = "sab-helpers.h")]
|
|
74
|
+
private extern uint64 _region_read_u64_le (void* region, size_t offset);
|
|
75
|
+
[CCode (cname = "gjsify_sab_region_write_u64_le", cheader_filename = "sab-helpers.h")]
|
|
76
|
+
private extern void _region_write_u64_le (void* region, size_t offset, uint64 v);
|
|
77
|
+
|
|
78
|
+
/* Bulk */
|
|
79
|
+
[CCode (cname = "gjsify_sab_region_read_bytes", cheader_filename = "sab-helpers.h")]
|
|
80
|
+
private extern GLib.Bytes _region_read_bytes (void* region, size_t offset, size_t length);
|
|
81
|
+
[CCode (cname = "gjsify_sab_region_write_bytes", cheader_filename = "sab-helpers.h")]
|
|
82
|
+
private extern void _region_write_bytes (void* region, size_t offset, GLib.Bytes data);
|
|
83
|
+
|
|
84
|
+
/* Atomics */
|
|
85
|
+
[CCode (cname = "gjsify_sab_region_atomic_add_i32", cheader_filename = "sab-helpers.h")]
|
|
86
|
+
private extern int32 _region_atomic_add_i32 (void* region, size_t offset, int32 v);
|
|
87
|
+
[CCode (cname = "gjsify_sab_region_atomic_sub_i32", cheader_filename = "sab-helpers.h")]
|
|
88
|
+
private extern int32 _region_atomic_sub_i32 (void* region, size_t offset, int32 v);
|
|
89
|
+
[CCode (cname = "gjsify_sab_region_atomic_load_i32", cheader_filename = "sab-helpers.h")]
|
|
90
|
+
private extern int32 _region_atomic_load_i32 (void* region, size_t offset);
|
|
91
|
+
[CCode (cname = "gjsify_sab_region_atomic_store_i32", cheader_filename = "sab-helpers.h")]
|
|
92
|
+
private extern void _region_atomic_store_i32 (void* region, size_t offset, int32 v);
|
|
93
|
+
[CCode (cname = "gjsify_sab_region_atomic_xchg_i32", cheader_filename = "sab-helpers.h")]
|
|
94
|
+
private extern int32 _region_atomic_xchg_i32 (void* region, size_t offset, int32 v);
|
|
95
|
+
[CCode (cname = "gjsify_sab_region_atomic_cmpxchg_i32", cheader_filename = "sab-helpers.h")]
|
|
96
|
+
private extern bool _region_atomic_cmpxchg_i32 (void* region, size_t offset, ref int32 expected, int32 desired);
|
|
97
|
+
|
|
98
|
+
/* Futex */
|
|
99
|
+
[CCode (cname = "gjsify_sab_region_futex_wait", cheader_filename = "sab-helpers.h")]
|
|
100
|
+
private extern int _region_futex_wait (void* region, size_t offset, int32 expected, int64 timeout_ms);
|
|
101
|
+
[CCode (cname = "gjsify_sab_region_futex_wake", cheader_filename = "sab-helpers.h")]
|
|
102
|
+
private extern int _region_futex_wake (void* region, size_t offset, int count);
|
|
103
|
+
|
|
104
|
+
/* ── Public GObject wrapper ─────────────────────────────────────────── */
|
|
105
|
+
|
|
106
|
+
public class SharedBuffer : GLib.Object {
|
|
107
|
+
/* Opaque region pointer. Lifetime: bound to this instance, freed
|
|
108
|
+
* in the destructor. */
|
|
109
|
+
private void* _region;
|
|
110
|
+
|
|
111
|
+
/* Default constructor — never used directly from JS (factory only). */
|
|
112
|
+
private SharedBuffer.with_region (void* region) {
|
|
113
|
+
this._region = region;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Allocate a fresh anonymous shared-memory region. Returns null if
|
|
118
|
+
* the underlying memfd_create / mmap fails (errno preserved on
|
|
119
|
+
* the calling thread).
|
|
120
|
+
*/
|
|
121
|
+
public static SharedBuffer? create (size_t size) {
|
|
122
|
+
void* r = _region_new_anonymous (size);
|
|
123
|
+
if (r == null) return null;
|
|
124
|
+
return new SharedBuffer.with_region (r);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Map an existing shared-memory fd into this process. The fd is
|
|
129
|
+
* dup'd; the caller retains ownership of their copy and may close
|
|
130
|
+
* it after this call returns.
|
|
131
|
+
*/
|
|
132
|
+
public static SharedBuffer? from_fd (int fd, size_t size) {
|
|
133
|
+
void* r = _region_new_from_fd (fd, size);
|
|
134
|
+
if (r == null) return null;
|
|
135
|
+
return new SharedBuffer.with_region (r);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
public int fd { get { return _region_get_fd (_region); } }
|
|
139
|
+
public size_t byte_length { get { return _region_get_size (_region); } }
|
|
140
|
+
|
|
141
|
+
/* Read / write — slow path, one accessor per integer width.
|
|
142
|
+
* For bulk operations use read_bytes / write_bytes. */
|
|
143
|
+
|
|
144
|
+
public uint8 get_u8 (size_t offset) { return _region_read_u8 (_region, offset); }
|
|
145
|
+
public void set_u8 (size_t offset, uint8 v) { _region_write_u8 (_region, offset, v); }
|
|
146
|
+
public uint32 get_u32_le (size_t offset) { return _region_read_u32_le (_region, offset); }
|
|
147
|
+
public void set_u32_le (size_t offset, uint32 v) { _region_write_u32_le(_region, offset, v); }
|
|
148
|
+
public int32 get_i32_le (size_t offset) { return _region_read_i32_le (_region, offset); }
|
|
149
|
+
public void set_i32_le (size_t offset, int32 v) { _region_write_i32_le(_region, offset, v); }
|
|
150
|
+
public uint64 get_u64_le (size_t offset) { return _region_read_u64_le (_region, offset); }
|
|
151
|
+
public void set_u64_le (size_t offset, uint64 v) { _region_write_u64_le(_region, offset, v); }
|
|
152
|
+
|
|
153
|
+
/* Bulk: zero-copy read (Bytes borrows the mmap), memcpy write. */
|
|
154
|
+
public GLib.Bytes read_bytes (size_t offset, size_t length) { return _region_read_bytes (_region, offset, length); }
|
|
155
|
+
public void write_bytes (size_t offset, GLib.Bytes data) { _region_write_bytes (_region, offset, data); }
|
|
156
|
+
|
|
157
|
+
/* Atomics — SEQ_CST. fetch-style: return value BEFORE the op. */
|
|
158
|
+
public int32 atomic_add_i32 (size_t offset, int32 v) { return _region_atomic_add_i32 (_region, offset, v); }
|
|
159
|
+
public int32 atomic_sub_i32 (size_t offset, int32 v) { return _region_atomic_sub_i32 (_region, offset, v); }
|
|
160
|
+
public int32 atomic_load_i32 (size_t offset) { return _region_atomic_load_i32 (_region, offset); }
|
|
161
|
+
public void atomic_store_i32 (size_t offset, int32 v) { _region_atomic_store_i32(_region, offset, v); }
|
|
162
|
+
public int32 atomic_xchg_i32 (size_t offset, int32 v) { return _region_atomic_xchg_i32 (_region, offset, v); }
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Strong compare-and-swap on a 32-bit slot.
|
|
166
|
+
* Returns the actual previous value. CAS succeeded iff the
|
|
167
|
+
* returned value === @expected.
|
|
168
|
+
*/
|
|
169
|
+
public int32 atomic_cmpxchg_i32 (size_t offset, int32 expected, int32 desired) {
|
|
170
|
+
int32 actual = expected;
|
|
171
|
+
_region_atomic_cmpxchg_i32 (_region, offset, ref actual, desired);
|
|
172
|
+
return actual;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Linux futex_wait. Compares *((int32_t*)region+offset) to @expected;
|
|
177
|
+
* if equal, blocks until woken or timeout. timeout_ms < 0 = infinite.
|
|
178
|
+
*
|
|
179
|
+
* Return: 0 woken, -1 not-equal (EAGAIN), -2 timed-out, -3 interrupted,
|
|
180
|
+
* other negative = generic errno.
|
|
181
|
+
*/
|
|
182
|
+
public int futex_wait (size_t offset, int32 expected, int64 timeout_ms) {
|
|
183
|
+
return _region_futex_wait (_region, offset, expected, timeout_ms);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Wake up to @count waiters on this address. Returns number actually
|
|
188
|
+
* woken (can be less than count).
|
|
189
|
+
*/
|
|
190
|
+
public int futex_wake (size_t offset, int count) {
|
|
191
|
+
return _region_futex_wake (_region, offset, count);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
~SharedBuffer () {
|
|
195
|
+
if (_region != null) {
|
|
196
|
+
_region_free (_region);
|
|
197
|
+
_region = null;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|