@amritk/nish-x86_64-linux 0.26.0 → 0.28.0
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/INSTALL.md +13 -8
- package/bin/nish +0 -0
- package/docs/AI.md +27 -5
- package/package.json +1 -1
- package/runtime/core.d.ts +47 -0
- package/runtime/core.mjs +67 -0
- package/runtime/nish.d.ts +54 -5
- package/runtime/nish.h +67 -8
- package/runtime/nish.mjs +6 -0
- package/runtime/runtime-checksum.c +270 -0
- package/runtime/runtime-os.c +9 -4
- package/runtime/runtime-parallel.c +21 -0
- package/runtime/runtime-sandbox.c +470 -0
- package/runtime/runtime-sha256.c +239 -0
- package/runtime/runtime-simd.c +63 -2
- package/runtime/runtime-wasm.c +18 -0
- package/scripts/build.sh +60 -24
- package/std/README.md +16 -10
- package/std/compress/checksum.ts +88 -75
- package/std/compress/deflate.ts +224 -0
- package/std/compress/gzip.ts +52 -0
- package/std/crypto/bcrypt.ts +816 -280
- package/std/crypto/sha256.ts +335 -188
- package/std/json-equal.ts +335 -0
- package/std/json-format.ts +1070 -0
- package/std/json-number.ts +428 -0
- package/std/json-pointer.ts +245 -0
- package/std/json-value.ts +510 -17
- package/std/net/http-client.ts +1645 -0
- package/std/net/http-compress.ts +259 -16
- package/std/net/http1-client.ts +104 -19
- package/std/net/http1-server.ts +61 -12
- package/std/net/postgres.ts +576 -8
- package/std/net/websocket.ts +295 -14
- package/std/regex.ts +613 -52
- package/std/term-io.ts +35 -8
- package/std/yaml-json.ts +841 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/* Nish runtime, the checksum half: CRC-32 (ISO 3309, gzip's, RFC 1952 §8) on
|
|
2
|
+
* the CPU's carry-less multiply or CRC instructions (docs/wp38-simd.md §3.1).
|
|
3
|
+
* std/compress/checksum.ts is the checksum; this is a faster body for its one
|
|
4
|
+
* private walker, `crc32Walk`, which the compiler calls in its place when
|
|
5
|
+
* `nish_crc32_hw` says this machine can run it (src/kernels.ts).
|
|
6
|
+
*
|
|
7
|
+
* A translation unit of its own for the reason runtime-aes.c is one: it carries
|
|
8
|
+
* its own measured `.text*` ceiling in tests/runtime-budgets.js
|
|
9
|
+
* (RUNTIME_CHECKSUM_TEXT_BUDGET), and a program that never checksums links
|
|
10
|
+
* none of it once section GC has run.
|
|
11
|
+
*
|
|
12
|
+
* Two kernels, each compiled for its extensions alone with `target(...)`, so the
|
|
13
|
+
* binary stays a baseline binary:
|
|
14
|
+
*
|
|
15
|
+
* x86-64 PCLMULQDQ folding, four 128-bit lanes at a step, then a Barrett
|
|
16
|
+
* reduction to 32 bits; SSE4.1 for the lane extract. Never SSE4.2's
|
|
17
|
+
* `crc32` instruction, which is CRC-32C, another polynomial.
|
|
18
|
+
* AArch64 the ARMv8 CRC32 instructions (`crc32x` and its narrower kin),
|
|
19
|
+
* eight bytes a step, where AT_HWCAP has HWCAP_CRC32. PMULL folding
|
|
20
|
+
* is not used: on CI's AArch64 runner it measured 20.6 GB/s at 1 MiB
|
|
21
|
+
* against these instructions' 24.9 GB/s.
|
|
22
|
+
* the rest no kernel: std/compress/checksum.ts's slicing-by-8 tables, which
|
|
23
|
+
* the compiler keeps behind the branch, so a C table here would only
|
|
24
|
+
* be a second copy of that code.
|
|
25
|
+
*
|
|
26
|
+
* A machine without the instructions, a process run with `NISH_CRC32=soft`, and
|
|
27
|
+
* every other target (wasm32, i686, armv7, a big-endian AArch64, a toolchain
|
|
28
|
+
* without the intrinsic headers) run the Nish code: there `nish_crc32_hw`
|
|
29
|
+
* answers 0 and `nish_crc32_update` is a trap nothing reaches, so a link that
|
|
30
|
+
* names it still resolves.
|
|
31
|
+
*
|
|
32
|
+
* The folding is the method of V. Gopal, E. Ozturk, J. Guilford, G. Wolrich,
|
|
33
|
+
* W. Feghali, M. Dixon and D. Karakoyunlu, "Fast CRC Computation for Generic
|
|
34
|
+
* Polynomials Using PCLMULQDQ Instruction" (Intel, 2009), written here from the
|
|
35
|
+
* paper's description for the bit-reflected polynomial. Every constant below is
|
|
36
|
+
* x^e mod P, or floor(x^64 / P), bit-reflected over 33 bits; tests/simd/crc32.c
|
|
37
|
+
* computes each one again from P and holds this file to it.
|
|
38
|
+
*
|
|
39
|
+
* CRC-32 is no secret's checksum here, so nothing is wiped and nothing is held
|
|
40
|
+
* to constant time (docs/security/compress.md).
|
|
41
|
+
*/
|
|
42
|
+
#include <stdint.h>
|
|
43
|
+
|
|
44
|
+
#include "nish.h"
|
|
45
|
+
|
|
46
|
+
#if defined(__has_include)
|
|
47
|
+
#define NISH_CRC32_HAS(h) __has_include(h)
|
|
48
|
+
#else
|
|
49
|
+
#define NISH_CRC32_HAS(h) 0
|
|
50
|
+
#endif
|
|
51
|
+
|
|
52
|
+
#if defined(__x86_64__) && NISH_CRC32_HAS(<cpuid.h>) && NISH_CRC32_HAS(<wmmintrin.h>) && \
|
|
53
|
+
NISH_CRC32_HAS(<smmintrin.h>)
|
|
54
|
+
#include <cpuid.h>
|
|
55
|
+
#include <smmintrin.h>
|
|
56
|
+
#include <wmmintrin.h>
|
|
57
|
+
#define NISH_CRC32_X86 1
|
|
58
|
+
#elif defined(__aarch64__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ && NISH_CRC32_HAS(<arm_acle.h>)
|
|
59
|
+
#include <arm_acle.h>
|
|
60
|
+
#define NISH_CRC32_ARM 1
|
|
61
|
+
#endif
|
|
62
|
+
|
|
63
|
+
#if defined(NISH_CRC32_X86) || defined(NISH_CRC32_ARM)
|
|
64
|
+
|
|
65
|
+
/* Named here rather than through <stdlib.h>, as runtime-aes.c names it: the
|
|
66
|
+
file compiles for aarch64 on hosts with no aarch64 libc headers. */
|
|
67
|
+
char *getenv(const char *name);
|
|
68
|
+
|
|
69
|
+
/* The fold constants (the header comment says what each is): K1 and K2 fold a
|
|
70
|
+
lane 512 bits on, K3 and K4 128 bits on, K5 64 bits on; P is the polynomial
|
|
71
|
+
and U floor(x^64 / P), both reflected, for the Barrett reduction. */
|
|
72
|
+
#define NISH_CRC32_K1 0x154442bd4ull /* x^(512+32) mod P */
|
|
73
|
+
#define NISH_CRC32_K2 0x1c6e41596ull /* x^(512-32) mod P */
|
|
74
|
+
#define NISH_CRC32_K3 0x1751997d0ull /* x^(128+32) mod P */
|
|
75
|
+
#define NISH_CRC32_K4 0x0ccaa009eull /* x^(128-32) mod P */
|
|
76
|
+
#define NISH_CRC32_K5 0x163cd6124ull /* x^64 mod P */
|
|
77
|
+
#define NISH_CRC32_P 0x1db710641ull
|
|
78
|
+
#define NISH_CRC32_U 0x1f7011641ull
|
|
79
|
+
|
|
80
|
+
/* Unaligned loads from an array's data, which may alias anything. */
|
|
81
|
+
typedef uint64_t nish_crc32_u64u __attribute__((aligned(1), may_alias));
|
|
82
|
+
typedef uint32_t nish_crc32_u32u __attribute__((aligned(1), may_alias));
|
|
83
|
+
|
|
84
|
+
#if defined(NISH_CRC32_X86)
|
|
85
|
+
|
|
86
|
+
#define NISH_CRC32_TARGET __attribute__((target("pclmul,sse4.1")))
|
|
87
|
+
|
|
88
|
+
/* The register advanced over one byte, four bits at a time: the bytes the
|
|
89
|
+
folds leave over, fewer than sixteen, and inputs shorter than one lane. */
|
|
90
|
+
static const uint32_t nish_crc32_nibble[16] = {
|
|
91
|
+
0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
|
|
92
|
+
0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c,
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
static uint32_t nish_crc32_bytes(uint32_t c, const uint8_t *p, uint64_t n) {
|
|
96
|
+
for (uint64_t i = 0; i < n; i++) {
|
|
97
|
+
c ^= p[i];
|
|
98
|
+
c = (c >> 4) ^ nish_crc32_nibble[c & 15];
|
|
99
|
+
c = (c >> 4) ^ nish_crc32_nibble[c & 15];
|
|
100
|
+
}
|
|
101
|
+
return c;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* `x` folded over 128 bits onto `next`: its low half times one constant, its
|
|
105
|
+
high half times the other, both added to the lane that follows. */
|
|
106
|
+
static inline __attribute__((always_inline)) NISH_CRC32_TARGET __m128i nish_crc32_fold(__m128i x, __m128i k,
|
|
107
|
+
__m128i next) {
|
|
108
|
+
return _mm_xor_si128(_mm_xor_si128(_mm_clmulepi64_si128(x, k, 0x00), _mm_clmulepi64_si128(x, k, 0x11)), next);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* The register `c` (not inverted: the state between bytes) advanced over
|
|
112
|
+
p[0..n). */
|
|
113
|
+
static NISH_CRC32_TARGET uint32_t nish_crc32_clmul(uint32_t c, const uint8_t *p, uint64_t n) {
|
|
114
|
+
if (n < 16) return nish_crc32_bytes(c, p, n);
|
|
115
|
+
const __m128i k34 = _mm_set_epi64x((long long)NISH_CRC32_K4, (long long)NISH_CRC32_K3);
|
|
116
|
+
__m128i x = _mm_xor_si128(_mm_loadu_si128((const __m128i *)p), _mm_cvtsi32_si128((int)c));
|
|
117
|
+
p += 16;
|
|
118
|
+
n -= 16;
|
|
119
|
+
if (n >= 48) {
|
|
120
|
+
const __m128i k12 = _mm_set_epi64x((long long)NISH_CRC32_K2, (long long)NISH_CRC32_K1);
|
|
121
|
+
__m128i x1 = _mm_loadu_si128((const __m128i *)p);
|
|
122
|
+
__m128i x2 = _mm_loadu_si128((const __m128i *)(p + 16));
|
|
123
|
+
__m128i x3 = _mm_loadu_si128((const __m128i *)(p + 32));
|
|
124
|
+
p += 48;
|
|
125
|
+
n -= 48;
|
|
126
|
+
while (n >= 64) {
|
|
127
|
+
x = nish_crc32_fold(x, k12, _mm_loadu_si128((const __m128i *)p));
|
|
128
|
+
x1 = nish_crc32_fold(x1, k12, _mm_loadu_si128((const __m128i *)(p + 16)));
|
|
129
|
+
x2 = nish_crc32_fold(x2, k12, _mm_loadu_si128((const __m128i *)(p + 32)));
|
|
130
|
+
x3 = nish_crc32_fold(x3, k12, _mm_loadu_si128((const __m128i *)(p + 48)));
|
|
131
|
+
p += 64;
|
|
132
|
+
n -= 64;
|
|
133
|
+
}
|
|
134
|
+
x = nish_crc32_fold(x, k34, x1);
|
|
135
|
+
x = nish_crc32_fold(x, k34, x2);
|
|
136
|
+
x = nish_crc32_fold(x, k34, x3);
|
|
137
|
+
}
|
|
138
|
+
while (n >= 16) {
|
|
139
|
+
x = nish_crc32_fold(x, k34, _mm_loadu_si128((const __m128i *)p));
|
|
140
|
+
p += 16;
|
|
141
|
+
n -= 16;
|
|
142
|
+
}
|
|
143
|
+
/* 128 bits to 64: the low half times x^96 onto the high half... */
|
|
144
|
+
const __m128i low32 = _mm_set_epi32(0, -1, 0, -1);
|
|
145
|
+
x = _mm_xor_si128(_mm_srli_si128(x, 8), _mm_clmulepi64_si128(x, k34, 0x10));
|
|
146
|
+
/* ...then its low 32 bits times x^64 onto the 64 above them. */
|
|
147
|
+
x = _mm_xor_si128(_mm_srli_si128(x, 4),
|
|
148
|
+
_mm_clmulepi64_si128(_mm_and_si128(x, low32), _mm_set_epi64x(0, (long long)NISH_CRC32_K5), 0x00));
|
|
149
|
+
/* Barrett: q = (low 32 bits times U) mod x^32, and x + q P leaves the
|
|
150
|
+
remainder in bits 32..63. */
|
|
151
|
+
const __m128i pu = _mm_set_epi64x((long long)NISH_CRC32_U, (long long)NISH_CRC32_P);
|
|
152
|
+
__m128i q = _mm_and_si128(_mm_clmulepi64_si128(_mm_and_si128(x, low32), pu, 0x10), low32);
|
|
153
|
+
x = _mm_xor_si128(x, _mm_clmulepi64_si128(q, pu, 0x00));
|
|
154
|
+
return nish_crc32_bytes((uint32_t)_mm_extract_epi32(x, 1), p, n);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
#else /* NISH_CRC32_ARM */
|
|
158
|
+
|
|
159
|
+
#define NISH_CRC32_TARGET __attribute__((target("+crc")))
|
|
160
|
+
|
|
161
|
+
/* The register `c` (not inverted) advanced over p[0..n) by the CRC32
|
|
162
|
+
instructions, eight bytes a step. */
|
|
163
|
+
static inline __attribute__((always_inline)) NISH_CRC32_TARGET uint32_t nish_crc32_insn(uint32_t c, const uint8_t *p,
|
|
164
|
+
uint64_t n) {
|
|
165
|
+
for (; n >= 8; p += 8, n -= 8) c = __crc32d(c, *(const nish_crc32_u64u *)p);
|
|
166
|
+
if (n & 4) {
|
|
167
|
+
c = __crc32w(c, *(const nish_crc32_u32u *)p);
|
|
168
|
+
p += 4;
|
|
169
|
+
}
|
|
170
|
+
if (n & 2) {
|
|
171
|
+
c = __crc32h(c, (uint16_t)(p[0] | p[1] << 8));
|
|
172
|
+
p += 2;
|
|
173
|
+
}
|
|
174
|
+
if (n & 1) c = __crc32b(c, p[0]);
|
|
175
|
+
return c;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
static NISH_CRC32_TARGET uint32_t nish_crc32_arm(uint32_t c, const uint8_t *p, uint64_t n) {
|
|
179
|
+
return nish_crc32_insn(c, p, n);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
#endif
|
|
183
|
+
|
|
184
|
+
/* Which path this process runs, as `nish_crc32_answer` holds it. */
|
|
185
|
+
#define NISH_CRC32_UNSET 0
|
|
186
|
+
#define NISH_CRC32_KERNEL 1 /* PCLMULQDQ on x86-64; the CRC32 instructions on AArch64 */
|
|
187
|
+
#define NISH_CRC32_SOFT 2 /* std/compress/checksum.ts's tables */
|
|
188
|
+
|
|
189
|
+
#if defined(NISH_CRC32_X86)
|
|
190
|
+
/* CPUID leaf 1's ECX: PCLMULQDQ (bit 1) and SSE4.1 (bit 19), through `cpuid.h`
|
|
191
|
+
rather than `__builtin_cpu_supports`, for runtime-aes.c's reason. */
|
|
192
|
+
static int32_t nish_crc32_cpu(void) {
|
|
193
|
+
unsigned a, b, c, d;
|
|
194
|
+
if (!__get_cpuid(1, &a, &b, &c, &d)) return NISH_CRC32_SOFT;
|
|
195
|
+
return (c & bit_PCLMUL) != 0 && (c & bit_SSE4_1) != 0 ? NISH_CRC32_KERNEL : NISH_CRC32_SOFT;
|
|
196
|
+
}
|
|
197
|
+
#elif defined(__linux__)
|
|
198
|
+
/* AT_HWCAP (16): HWCAP_CRC32 is bit 7 (arch/arm64/include/uapi/asm/hwcap.h). */
|
|
199
|
+
unsigned long getauxval(unsigned long type);
|
|
200
|
+
|
|
201
|
+
static int32_t nish_crc32_cpu(void) {
|
|
202
|
+
return (getauxval(16) & (1ul << 7)) != 0 ? NISH_CRC32_KERNEL : NISH_CRC32_SOFT;
|
|
203
|
+
}
|
|
204
|
+
#elif defined(__APPLE__)
|
|
205
|
+
/* Darwin answers by name; a name it does not know leaves `on` at 0. */
|
|
206
|
+
typedef __SIZE_TYPE__ nish_crc32_size;
|
|
207
|
+
int sysctlbyname(const char *name, void *old, nish_crc32_size *oldlen, void *new_, nish_crc32_size newlen);
|
|
208
|
+
|
|
209
|
+
static int32_t nish_crc32_sysctl(const char *name) {
|
|
210
|
+
int on = 0;
|
|
211
|
+
nish_crc32_size size = sizeof on;
|
|
212
|
+
return sysctlbyname(name, &on, &size, 0, 0) == 0 && on != 0;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
static int32_t nish_crc32_cpu(void) {
|
|
216
|
+
return nish_crc32_sysctl("hw.optional.armv8_crc32") ? NISH_CRC32_KERNEL : NISH_CRC32_SOFT;
|
|
217
|
+
}
|
|
218
|
+
#else
|
|
219
|
+
/* Another AArch64 system: no question asked, so the tables. */
|
|
220
|
+
static int32_t nish_crc32_cpu(void) {
|
|
221
|
+
return NISH_CRC32_SOFT;
|
|
222
|
+
}
|
|
223
|
+
#endif
|
|
224
|
+
|
|
225
|
+
/* The answer, fixed by the first call: relaxed atomics, as runtime-aes.c's,
|
|
226
|
+
because two `--threads` workers may make that call at once and reach the
|
|
227
|
+
same answer. */
|
|
228
|
+
static int32_t nish_crc32_answer;
|
|
229
|
+
|
|
230
|
+
static int32_t nish_crc32_path(void) {
|
|
231
|
+
int32_t answer = __atomic_load_n(&nish_crc32_answer, __ATOMIC_RELAXED);
|
|
232
|
+
if (answer == NISH_CRC32_UNSET) {
|
|
233
|
+
/* `soft` pins the tables and never asks the CPU; anything else, or
|
|
234
|
+
nothing, means the best this machine has. */
|
|
235
|
+
const char *pin = getenv("NISH_CRC32");
|
|
236
|
+
int soft = pin != 0 && pin[0] == 's' && pin[1] == 'o' && pin[2] == 'f' && pin[3] == 't' && pin[4] == 0;
|
|
237
|
+
answer = soft ? NISH_CRC32_SOFT : nish_crc32_cpu();
|
|
238
|
+
__atomic_store_n(&nish_crc32_answer, answer, __ATOMIC_RELAXED);
|
|
239
|
+
}
|
|
240
|
+
return answer;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
int32_t nish_crc32_hw(void) {
|
|
244
|
+
return nish_crc32_path() != NISH_CRC32_SOFT;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
uint32_t nish_crc32_update(uint32_t crc, const nish_array *buf, int32_t off, int32_t len) {
|
|
248
|
+
const uint8_t *p = (const uint8_t *)buf->data + off;
|
|
249
|
+
#if defined(NISH_CRC32_X86)
|
|
250
|
+
return ~nish_crc32_clmul(~crc, p, (uint64_t)len);
|
|
251
|
+
#else
|
|
252
|
+
return ~nish_crc32_arm(~crc, p, (uint64_t)len);
|
|
253
|
+
#endif
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
#else
|
|
257
|
+
|
|
258
|
+
/* A target with none of the instructions this file uses: the tables always,
|
|
259
|
+
and a kernel that a program whose every call is behind `nish_crc32_hw` never
|
|
260
|
+
reaches. */
|
|
261
|
+
int32_t nish_crc32_hw(void) {
|
|
262
|
+
return 0;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
uint32_t nish_crc32_update(uint32_t crc, const nish_array *buf, int32_t off, int32_t len) {
|
|
266
|
+
(void)crc, (void)buf, (void)off, (void)len;
|
|
267
|
+
__builtin_trap();
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
#endif
|
package/runtime/runtime-os.c
CHANGED
|
@@ -775,15 +775,20 @@ int64_t nish_total_memory(void) {
|
|
|
775
775
|
}
|
|
776
776
|
|
|
777
777
|
/* `setNice(n)`: this process's nice value set to `n`, 0 or a negative errno.
|
|
778
|
-
|
|
779
|
-
below
|
|
780
|
-
|
|
781
|
-
so it is the calling thread's and every thread started after it. */
|
|
778
|
+
Lower-only, even as root: `n` takes 0..19 (-EINVAL outside it, with no system
|
|
779
|
+
call), and a value below the current one is -EACCES (Linux, Darwin) without
|
|
780
|
+
a `setpriority` call, so the priority never rises. Linux keeps the value per
|
|
781
|
+
thread, so it is the calling thread's and every thread started after it. */
|
|
782
782
|
int32_t nish_set_nice(int32_t n) {
|
|
783
783
|
#ifdef __wasi__
|
|
784
784
|
(void)n;
|
|
785
785
|
return -ENOSYS;
|
|
786
786
|
#else
|
|
787
|
+
if (n < 0 || n > 19) return -EINVAL;
|
|
788
|
+
errno = 0;
|
|
789
|
+
int current = getpriority(PRIO_PROCESS, 0);
|
|
790
|
+
if (current == -1 && errno != 0) return -errno;
|
|
791
|
+
if (n < current) return -EACCES;
|
|
787
792
|
return setpriority(PRIO_PROCESS, 0, n) == 0 ? 0 : -errno;
|
|
788
793
|
#endif
|
|
789
794
|
}
|
|
@@ -79,6 +79,11 @@
|
|
|
79
79
|
* decides how many threads a loop is divided into. */
|
|
80
80
|
static int64_t nish_cpu_cached = 0;
|
|
81
81
|
|
|
82
|
+
#ifdef NISH_PAR_REAL
|
|
83
|
+
/* What `nish_cpu_prime` read before `--sandbox` confined the process, or 0. */
|
|
84
|
+
static int64_t nish_cpu_ceiling = 0;
|
|
85
|
+
#endif
|
|
86
|
+
|
|
82
87
|
/* The CPUs a region may use: `nish_cpu_online` (runtime-os.c), which
|
|
83
88
|
* `availableParallelism` reads in every build, and 1 in the sequential one,
|
|
84
89
|
* which divides nothing. */
|
|
@@ -86,7 +91,10 @@ int64_t nish_cpu_count(void) {
|
|
|
86
91
|
int64_t n = __atomic_load_n(&nish_cpu_cached, __ATOMIC_RELAXED);
|
|
87
92
|
if (n != 0) return n;
|
|
88
93
|
#ifdef NISH_PAR_REAL
|
|
94
|
+
/* Under `--sandbox` the count is read again for `NISH_NUM_THREADS`, and
|
|
95
|
+
* the primed ceiling bounds it where the cgroup read is now refused. */
|
|
89
96
|
n = nish_cpu_online();
|
|
97
|
+
if (nish_cpu_ceiling != 0 && nish_cpu_ceiling < n) n = nish_cpu_ceiling;
|
|
90
98
|
#else
|
|
91
99
|
n = 1;
|
|
92
100
|
#endif
|
|
@@ -94,6 +102,19 @@ int64_t nish_cpu_count(void) {
|
|
|
94
102
|
return n;
|
|
95
103
|
}
|
|
96
104
|
|
|
105
|
+
/* `--sandbox` calls this from `.preinit_array`, before it confines the
|
|
106
|
+
* process: `nish_cpu_online` reads the cgroup quota from /sys/fs/cgroup, which
|
|
107
|
+
* Landlock refuses a program without `fs.read`, and a pool divided without it
|
|
108
|
+
* would oversubscribe a container. It keeps the count as a ceiling rather
|
|
109
|
+
* than the answer, because a dynamically linked glibc has not yet set
|
|
110
|
+
* `environ` there, so `NISH_NUM_THREADS` reads as unset; `nish_cpu_count`
|
|
111
|
+
* reads the count again when the first region asks and keeps the smaller. */
|
|
112
|
+
void nish_cpu_prime(void) {
|
|
113
|
+
#ifdef NISH_PAR_REAL
|
|
114
|
+
nish_cpu_ceiling = nish_cpu_online();
|
|
115
|
+
#endif
|
|
116
|
+
}
|
|
117
|
+
|
|
97
118
|
#ifdef NISH_PAR_REAL
|
|
98
119
|
/* Non-zero inside a region, so a body that itself calls `nish_parallel_range`
|
|
99
120
|
* runs its range on one thread instead of multiplying the thread count by the
|