@scriptc/runtime 0.0.25 → 0.0.27

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/src/scr_net.c CHANGED
@@ -389,6 +389,16 @@ struct ScrNetServer {
389
389
  bool closing; /* close() called; 'close' fires when conns drain */
390
390
  bool close_emitted; /* settled: off the registry, listeners dropped */
391
391
  bool emit_listening;
392
+ /* The writable http.Server timeout property values. Kept on the shared
393
+ * server handle because http/https servers retain that handle identity;
394
+ * plain net servers never expose these fields through the static type
395
+ * surface. Indices match lower-server.ts's selector ABI. */
396
+ double http_timeouts[5];
397
+ /* A dynamic write may store any JS value on these ordinary writable
398
+ * properties. Numeric writes stay in http_timeouts; every other value
399
+ * is retained here so dynamic reads preserve kind and identity. */
400
+ ScrDyn *http_timeout_dyn[5];
401
+ bool http_timeout_surface; /* true only for HTTP/1 and HTTPS servers */
392
402
  bool defer_conn; /* TLS: 'connection' fires post-handshake, not at accept */
393
403
  bool bound_v6; /* the bound family (address()'s 'IPv6'/'IPv4' split) */
394
404
  ScrStr *bound_host; /* the explicit bind host (NULL = the host-less any:
@@ -556,6 +566,7 @@ void scr_net_server_release(ScrNetServer *s) {
556
566
  scr_net_ls_drop(&s->close_ls);
557
567
  scr_net_ls_drop(&s->listening_cbs);
558
568
  scr_closure_release(s->close_override);
569
+ for (size_t i = 0; i < 5; i++) scr_dyn_release(s->http_timeout_dyn[i]);
559
570
  scr_str_release(s->bound_host);
560
571
  scr_str_release(s->pending_err);
561
572
  if (s->fd >= 0) scr_net_close_fd_raw(s->fd);
@@ -1200,6 +1211,11 @@ static ScrNetServer *scr_net_server_new(void) {
1200
1211
  s->kind = SCR_NET_K_SERVER;
1201
1212
  s->rc = 1;
1202
1213
  s->fd = -1;
1214
+ s->http_timeouts[0] = 0; /* timeout */
1215
+ s->http_timeouts[1] = 5000; /* keepAliveTimeout */
1216
+ s->http_timeouts[2] = 60000; /* headersTimeout */
1217
+ s->http_timeouts[3] = 300000; /* requestTimeout */
1218
+ s->http_timeouts[4] = 1000; /* keepAliveTimeoutBuffer */
1203
1219
  #ifdef SCR_RC_AUDIT
1204
1220
  scr_net_live++;
1205
1221
  #endif
@@ -1378,6 +1394,79 @@ void scr_net_listen_opts(ScrNetServer *s, double port, ScrStr *host /*borrowed*/
1378
1394
 
1379
1395
  double scr_net_server_port(ScrNetServer *s) { return (double)s->port; }
1380
1396
 
1397
+ double scr_net_server_timeout_get(ScrNetServer *s, double field) {
1398
+ static const char *const names[] = {
1399
+ "timeout", "keepAliveTimeout", "headersTimeout", "requestTimeout",
1400
+ "keepAliveTimeoutBuffer",
1401
+ };
1402
+ int i = (int)field;
1403
+ if (i < 0 || i >= 5) abort(); /* compiler/runtime ABI violation */
1404
+ if (s->http_timeout_dyn[i] != NULL) {
1405
+ scr_dyn_arg_type_fail(names[i], "of type number", s->http_timeout_dyn[i]);
1406
+ return 0; /* pending exception; typed reads validate an any-written value */
1407
+ }
1408
+ return s->http_timeouts[i];
1409
+ }
1410
+
1411
+ void scr_net_server_timeout_set(ScrNetServer *s, double field, double value) {
1412
+ int i = (int)field;
1413
+ if (i < 0 || i >= 5) abort(); /* compiler/runtime ABI violation */
1414
+ scr_dyn_release(s->http_timeout_dyn[i]);
1415
+ s->http_timeout_dyn[i] = NULL;
1416
+ s->http_timeouts[i] = value;
1417
+ }
1418
+
1419
+ void scr_net_server_enable_http_timeout_surface(ScrNetServer *s) {
1420
+ s->http_timeout_surface = true;
1421
+ }
1422
+
1423
+ /* Constructor-option validation is intentionally separate from the plain
1424
+ * writable-property setter above. Node validates these option values as
1425
+ * non-negative safe integers, while later property assignments store the
1426
+ * number directly. */
1427
+ bool scr_net_server_timeout_option_check(double field, double value) {
1428
+ static const char *const names[] = {
1429
+ "timeout", "keepAliveTimeout", "headersTimeout", "requestTimeout",
1430
+ "keepAliveTimeoutBuffer",
1431
+ };
1432
+ int i = (int)field;
1433
+ if (i < 0 || i >= 5) abort(); /* compiler/runtime ABI violation */
1434
+ char recv[48], msg[224];
1435
+ if (!(isfinite(value) && trunc(value) == value)) {
1436
+ scr_num_received(value, recv);
1437
+ int len = snprintf(msg, sizeof msg,
1438
+ "The value of \"%s\" is out of range. It must be an integer. Received %s",
1439
+ names[i], recv);
1440
+ scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)len, "ERR_OUT_OF_RANGE");
1441
+ return false;
1442
+ }
1443
+ if (value < 0 || value > 9007199254740991.0) {
1444
+ scr_num_received(value, recv);
1445
+ int len = snprintf(msg, sizeof msg,
1446
+ "The value of \"%s\" is out of range. It must be >= 0 && <= 9007199254740991. Received %s",
1447
+ names[i], recv);
1448
+ scr_throw_error_msg_code(SCR_ERR_RANGE, msg, (size_t)len, "ERR_OUT_OF_RANGE");
1449
+ return false;
1450
+ }
1451
+ return true;
1452
+ }
1453
+
1454
+ void scr_net_server_timeout_option_set(ScrNetServer *s, double field, const ScrDyn *value) {
1455
+ int i = (int)field;
1456
+ if (i < 0 || i >= 5) abort(); /* compiler/runtime ABI violation */
1457
+ if (value->kind == SCR_DYN_UNDEF) return; /* optional field is absent */
1458
+ if (value->kind != SCR_DYN_NUM) {
1459
+ static const char *const names[] = {
1460
+ "timeout", "keepAliveTimeout", "headersTimeout", "requestTimeout",
1461
+ "keepAliveTimeoutBuffer",
1462
+ };
1463
+ scr_dyn_arg_type_fail(names[i], "of type number", value);
1464
+ return;
1465
+ }
1466
+ if (!scr_net_server_timeout_option_check(field, value->v.num)) return;
1467
+ scr_net_server_timeout_set(s, field, value->v.num);
1468
+ }
1469
+
1381
1470
  /* address()'s other two fields — the bound host ('::'/'0.0.0.0' for the
1382
1471
  * host-less any, the normalized explicit host otherwise) and the family
1383
1472
  * string. Answer the any-form defaults before listen (Node answers null
@@ -3299,13 +3388,28 @@ static ScrDyn *scr_net_dynh_srv_invoke(void *h, ScrDyn *self, const char *method
3299
3388
  return NULL;
3300
3389
  }
3301
3390
 
3391
+ static int scr_net_dynh_srv_timeout_field(const char *key) {
3392
+ if (strcmp(key, "timeout") == 0) return 0;
3393
+ if (strcmp(key, "keepAliveTimeout") == 0) return 1;
3394
+ if (strcmp(key, "headersTimeout") == 0) return 2;
3395
+ if (strcmp(key, "requestTimeout") == 0) return 3;
3396
+ if (strcmp(key, "keepAliveTimeoutBuffer") == 0) return 4;
3397
+ return -1;
3398
+ }
3399
+
3302
3400
  static ScrDyn *scr_net_dynh_srv_get(void *h, const char *key, size_t key_len) {
3303
3401
  ScrNetServer *s = (ScrNetServer *)h;
3304
3402
  (void)key_len;
3305
3403
  if (strcmp(key, "listening") == 0) return scr_dyn_new_bool(s->listening);
3404
+ int timeout_field = scr_net_dynh_srv_timeout_field(key);
3405
+ if (timeout_field >= 0 && s->http_timeout_surface) {
3406
+ if (s->http_timeout_dyn[timeout_field] != NULL) {
3407
+ return scr_dyn_retain(s->http_timeout_dyn[timeout_field]);
3408
+ }
3409
+ return scr_dyn_new_num(scr_net_server_timeout_get(s, (double)timeout_field));
3410
+ }
3306
3411
  {
3307
- static const char *const known[] = { "maxConnections", "connections", "maxHeadersCount",
3308
- "timeout", "keepAliveTimeout", "headersTimeout", "requestTimeout", NULL };
3412
+ static const char *const known[] = { "maxConnections", "connections", "maxHeadersCount", NULL };
3309
3413
  for (size_t i = 0; known[i]; i++) {
3310
3414
  if (strcmp(key, known[i]) == 0) {
3311
3415
  scr_net_dynh_srv_unsupported(key, NULL);
@@ -3317,7 +3421,18 @@ static ScrDyn *scr_net_dynh_srv_get(void *h, const char *key, size_t key_len) {
3317
3421
  }
3318
3422
 
3319
3423
  static bool scr_net_dynh_srv_set(void *h, const char *key, size_t key_len, const ScrDyn *value) {
3320
- (void)h; (void)key; (void)key_len; (void)value;
3424
+ ScrNetServer *s = (ScrNetServer *)h;
3425
+ (void)key_len;
3426
+ int timeout_field = scr_net_dynh_srv_timeout_field(key);
3427
+ if (timeout_field >= 0 && s->http_timeout_surface) {
3428
+ if (value->kind == SCR_DYN_NUM) {
3429
+ scr_net_server_timeout_set(s, (double)timeout_field, value->v.num);
3430
+ } else {
3431
+ scr_dyn_release(s->http_timeout_dyn[timeout_field]);
3432
+ s->http_timeout_dyn[timeout_field] = scr_dyn_retain((ScrDyn *)value);
3433
+ }
3434
+ return true;
3435
+ }
3321
3436
  return false;
3322
3437
  }
3323
3438
 
package/src/scr_object.c CHANGED
@@ -33,7 +33,7 @@ void scr_record_key_miss(ScrStr *k) {
33
33
  }
34
34
 
35
35
  #ifdef SCR_RC_AUDIT
36
- static long scr_live_objects = 0;
36
+ static SCR_TL long scr_live_objects = 0;
37
37
  long scr_obj_live_count(void) { return scr_live_objects; }
38
38
  void scr_obj_alloc_note(void) { scr_live_objects++; }
39
39
  void scr_obj_free_note(void) { scr_live_objects--; }
package/src/scr_regex.c CHANGED
@@ -82,8 +82,8 @@ void *lre_realloc(void *opaque, void *ptr, size_t size) {
82
82
  * allocation audit sees the bytecode gone).
83
83
  */
84
84
 
85
- static ScrRegex **scr_compiled = NULL;
86
- static size_t scr_compiled_len = 0, scr_compiled_cap = 0;
85
+ static SCR_TL ScrRegex **scr_compiled = NULL;
86
+ static SCR_TL size_t scr_compiled_len = 0, scr_compiled_cap = 0;
87
87
 
88
88
  static void scr_regex_free_bytecodes(void) {
89
89
  for (size_t i = 0; i < scr_compiled_len; i++) {
package/src/scr_runtime.h CHANGED
@@ -15,22 +15,36 @@
15
15
  #include <string.h> /* memcpy in the inline slot accessors */
16
16
  #include <sys/types.h> /* ssize_t in the transport ops table */
17
17
 
18
- /* ── win32 libc shims (scr_win.c; see the windows portability inventory) ──
19
- * The POSIX/BSD functions the runtime calls that mingw-w64's CRT does not
20
- * provide: stpcpy (POSIX.1-2008; scr_number.c's digit writer), the
21
- * arc4random_buf CSPRNG (Math.random and node:crypto; RtlGenRandom
22
- * underneath), gmtime_r (scr_http.c's Date header; the CRT's gmtime is
23
- * already per-thread), and strcasestr (scr_http.c's token scan).
24
- * scr_win.c — compiled into win32-target builds only (cc.ts) —
25
- * implements all of them. Declared here so every TU that calls them
26
- * through scr_runtime.h compiles unchanged; POSIX hosts never see
27
- * these. */
18
+ /* ── libc shims ─────────────────────────────────────────────────────────
19
+ * Win32's missing POSIX/BSD functions live in scr_win.c. Zig's musl sysroot
20
+ * additionally lacks arc4random_buf; scr_musl.c supplies it from Linux's
21
+ * getrandom syscall. Both files are selected by cc.ts only for their target. */
28
22
  #ifdef _WIN32
29
23
  #include <time.h> /* time_t / struct tm for the gmtime_r shim */
30
24
  char *stpcpy(char *dst, const char *src);
31
25
  void arc4random_buf(void *buf, size_t n);
32
26
  struct tm *gmtime_r(const time_t *t, struct tm *out);
33
27
  char *strcasestr(const char *hay, const char *needle);
28
+ #elif defined(SCR_MUSL)
29
+ void arc4random_buf(void *buf, size_t n);
30
+ #endif
31
+
32
+ /* ── thread-instanced library state ─────────────────────────────────────
33
+ * Archives built under the profile's abi.instance_per_thread compile every
34
+ * TU with -DSCR_THREAD_INSTANCES, and SCR_TL moves each unit's mutable
35
+ * state — and the emitted program's globals — into thread-local storage.
36
+ * A thread that calls the profile's init entry then owns a complete,
37
+ * independent instance: its own collector, result arena, panic-sink
38
+ * registration, poison flag, and program state. The instance's lifetime is
39
+ * the thread's; the one-thread-per-instance contract (an instance is never
40
+ * entered from two threads) is unchanged — this mode adds instances, not
41
+ * thread awareness. Expands to nothing everywhere else, so executable
42
+ * builds and classic library builds carry the exact bytes they always
43
+ * carried. Truly immutable tables (static const data) stay shared. */
44
+ #if defined(SCR_LIB) && defined(SCR_THREAD_INSTANCES)
45
+ #define SCR_TL _Thread_local
46
+ #else
47
+ #define SCR_TL
34
48
  #endif
35
49
 
36
50
  /* ── process ──────────────────────────────────────────────────────────── */
@@ -241,16 +255,22 @@ typedef struct ScrCycHdr {
241
255
  size_t buf_index; /* position there (O(1) removal when rc hits 0) */
242
256
  } ScrCycHdr;
243
257
 
244
- /* This layout is an ABI, not an implementation detail: the LLVM backend
245
- * inlines scr_cyc_mark_live as a raw `store i32 0` at obj-16 (it is a
246
- * static inline here, so there is no symbol to call) and reaches the header
247
- * at obj-32. Three sites emit it — llvm/shapes.ts, llvm/classes.ts,
248
- * llvm/emitter.ts. Nothing but `color` may share those four bytes: a field
249
- * placed in them is silently zeroed by every retain, which is invisible to
250
- * the type system and to the C compiler. Hence the assertions. */
251
- _Static_assert(sizeof(ScrCycHdr) == 32, "LLVM backend reads the header at obj-32");
258
+ /* This layout is an ABI, not an implementation detail. The LLVM backend
259
+ * inlines scr_cyc_mark_live as a raw `store i32 0`: color is at obj-16 on
260
+ * 64-bit targets and obj-12 on wasm32. Three sites emit it
261
+ * llvm/shapes.ts, llvm/classes.ts, and llvm/emitter.ts. Nothing but `color`
262
+ * may share those four bytes: a field placed in them is silently zeroed by
263
+ * every retain, which is invisible to the type system and to the C
264
+ * compiler. Hence the target-width assertions. */
265
+ #if UINTPTR_MAX == UINT64_MAX
266
+ _Static_assert(sizeof(ScrCycHdr) == 32, "LLVM backend expects a 32-byte cycle header");
252
267
  _Static_assert(offsetof(ScrCycHdr, color) == 16,
253
268
  "LLVM backend's inlined mark-live stores i32 0 at obj-16");
269
+ #elif UINTPTR_MAX == UINT32_MAX
270
+ _Static_assert(sizeof(ScrCycHdr) == 20, "LLVM backend expects a 20-byte cycle header");
271
+ _Static_assert(offsetof(ScrCycHdr, color) == 8,
272
+ "LLVM backend's inlined mark-live stores i32 0 at obj-12");
273
+ #endif
254
274
  _Static_assert(sizeof(((ScrCycHdr *)0)->color) == 4,
255
275
  "mark-live is an i32 store: color must own all four bytes");
256
276
  _Static_assert(SCR_CYC_BLACK == 0,
@@ -443,7 +463,7 @@ enum {
443
463
  SCR_ERR_DOMEX = 4, /* DOMException — ScrDomException, the wider layout */
444
464
  };
445
465
 
446
- extern ScrVt scr_error_vts[5]; /* indexed by SCR_ERR_*; main() stamps pre/post */
466
+ extern SCR_TL ScrVt scr_error_vts[5]; /* indexed by SCR_ERR_*; main() stamps pre/post */
447
467
 
448
468
  struct ScrDyn; /* full declaration below (the checked-dynamic tree section) */
449
469
 
@@ -1371,8 +1391,8 @@ typedef struct ScrEmitter {
1371
1391
  const char *cls; /* display name for the leak warning ("EventEmitter") */
1372
1392
  } ScrEmitter;
1373
1393
 
1374
- extern ScrVt scr_emitter_vt; /* main() stamps pre/post */
1375
- extern double scr_emitter_default_max;
1394
+ extern SCR_TL ScrVt scr_emitter_vt; /* main() stamps pre/post */
1395
+ extern SCR_TL double scr_emitter_default_max;
1376
1396
 
1377
1397
  ScrEmitter *scr_emitter_new(void); /* +1, collector-headered */
1378
1398
  void scr_emitter_init(void *obj); /* super() into the prefix (no-op today) */
@@ -1421,9 +1441,10 @@ ScrEmitter *scr_emitter_on_via(ScrEmitter *em, ScrStr *name, ScrClosure *orig /*
1421
1441
  * slots off the emit tuple and calls cb->fn behind the fixed signature
1422
1442
  * `void (ScrClosure *, void * ×k)` — the fixed-signature adapter the
1423
1443
  * backend provides. Such backends pass every user tuple argument
1424
- * pointer-classed at the emit site (f64 as its i64 bit pattern, bool
1425
- * zero-extended); the runtime's own emits (data/error/pipe/meta) carry
1426
- * pointers only, so the shims read every tuple either lane produces.
1444
+ * pointer-classed at the emit site: scalar values point at call-lived
1445
+ * typed stack slots and reference values ride directly. The runtime's own
1446
+ * emits (data/error/pipe/meta) carry pointers only, so the shims read every
1447
+ * tuple either lane produces on 32- and 64-bit targets.
1427
1448
  * SCR_EE_FIXED_MAX is the registry's audited arity ceiling — backends
1428
1449
  * refuse listeners past it rather than guess. */
1429
1450
  #define SCR_EE_FIXED_MAX 4
@@ -1973,7 +1994,7 @@ bool scr_process_kill_named(double pid, const ScrStr *signal);
1973
1994
  void scr_process_exit(double code);
1974
1995
  /* process._exiting: true once the exit sequence began (process.exit or
1975
1996
  * the exit-listener runner set the flag). Never throws. */
1976
- extern bool scr_process_in_exit;
1997
+ extern SCR_TL bool scr_process_in_exit;
1977
1998
  bool scr_process_exiting(void);
1978
1999
  /* umask(2): mask < 0 reads without setting; otherwise sets and answers
1979
2000
  * the previous mask. Never throws. */
@@ -3548,6 +3569,28 @@ void scr_promise_release_v(void *p);
3548
3569
  typedef struct ScrFiber ScrFiber;
3549
3570
  /* Spawn + run eagerly to the first suspension; returns the promise, +1. */
3550
3571
  ScrPromise *scr_async_spawn(void (*entry)(ScrFiber *, void *), void *argpack);
3572
+ /* Runtime-authored C waiters cannot themselves become LLVM switched
3573
+ * coroutines on wasm32. Spawn after `dependency` settles so their first
3574
+ * (and only) await is extraction from an already-settled promise, while
3575
+ * retaining the ordinary promise-job hop on every target. +1. */
3576
+ ScrPromise *scr_async_spawn_after(ScrPromise *dependency,
3577
+ void (*entry)(ScrFiber *, void *),
3578
+ void *argpack);
3579
+
3580
+ /* wasm32-wasi lowers async functions and generators through LLVM switched
3581
+ * coroutines instead of the native stack-switching implementations. The
3582
+ * first two adapters are emitted into the program module; the remaining
3583
+ * helpers let scr_async.c register, queue, and finish those coroutine
3584
+ * frames through the same ScrFiber scheduler. These declarations are part
3585
+ * of the LLVM/runtime ABI even though native targets never call them. */
3586
+ void scr_wasi_coro_resume(void *handle);
3587
+ void scr_wasi_coro_destroy(void *handle);
3588
+ void scr_wasi_coro_started(void *handle);
3589
+ void scr_wasi_await_prepare(ScrFiber *self, ScrPromise *p);
3590
+ void scr_wasi_await_hop_prepare(ScrFiber *self);
3591
+ bool scr_wasi_module_await_prepare(ScrFiber *self, ScrPromise *p);
3592
+ void scr_wasi_async_finish(ScrFiber *self);
3593
+ void scr_wasi_gen_finish(ScrFiber *self);
3551
3594
 
3552
3595
  double scr_await_f64(ScrPromise *p); /* rejected promises re-throw */
3553
3596
  bool scr_await_bool(ScrPromise *p);
@@ -5113,6 +5156,20 @@ void scr_net_listen(ScrNetServer *s, double port, ScrClosure *cb /*moves, nullab
5113
5156
  void scr_net_listen_opts(ScrNetServer *s, double port, ScrStr *host /*borrowed*/,
5114
5157
  bool ipv6_only, ScrClosure *cb /*moves, nullable*/);
5115
5158
  double scr_net_server_port(ScrNetServer *s); /* address().port */
5159
+ /* Writable http.Server timeout property storage. `field` is the compiler
5160
+ * ABI selector: timeout, keepAliveTimeout, headersTimeout, requestTimeout,
5161
+ * keepAliveTimeoutBuffer. Typed reads validate that no dynamic write left
5162
+ * a non-number in the ordinary JS property slot. */
5163
+ double scr_net_server_timeout_get(ScrNetServer *s, double field);
5164
+ void scr_net_server_timeout_set(ScrNetServer *s, double field, double value);
5165
+ /* Marks the shared net-server handle as an HTTP/1 or HTTPS server. The
5166
+ * dynamic handle uses this to keep HTTP-only fields off net/TLS/H2. */
5167
+ void scr_net_server_enable_http_timeout_surface(ScrNetServer *s);
5168
+ /* Constructor-option twin: undefined is absent; otherwise validates type
5169
+ * and a non-negative safe integer before storing, matching Node's option
5170
+ * ladder (plain property writes do not validate). */
5171
+ bool scr_net_server_timeout_option_check(double field, double value);
5172
+ void scr_net_server_timeout_option_set(ScrNetServer *s, double field, const struct ScrDyn *value);
5116
5173
  ScrStr *scr_net_server_addr_ip(ScrNetServer *s); /* +1 — address().address */
5117
5174
  ScrStr *scr_net_server_addr_family(ScrNetServer *s); /* +1 — address().family */
5118
5175
  void scr_net_server_close(ScrNetServer *s, ScrClosure *cb /*moves, nullable*/);
package/src/scr_string.c CHANGED
@@ -10,7 +10,7 @@
10
10
  * leaks nothing and frees nothing twice (double-free shows up as ASan
11
11
  * use-after-free on the rc field or a negative count here). */
12
12
  #ifdef SCR_RC_AUDIT
13
- static long scr_live_strings = 0;
13
+ static SCR_TL long scr_live_strings = 0;
14
14
  long scr_str_live_count(void) { return scr_live_strings; }
15
15
  #endif
16
16
 
@@ -41,8 +41,8 @@ typedef struct {
41
41
  size_t u16len; /* SCR_U16_UNKNOWN until computed */
42
42
  size_t cu, cb; /* cursor: byte offset cb starts the char at unit cu */
43
43
  } ScrSidx;
44
- static ScrSidx scr_sidx_tab[SCR_SIDX_N];
45
- static unsigned scr_sidx_clock;
44
+ static SCR_TL ScrSidx scr_sidx_tab[SCR_SIDX_N];
45
+ static SCR_TL unsigned scr_sidx_clock;
46
46
 
47
47
  static void scr_sidx_purge(const ScrStr *s) {
48
48
  for (int i = 0; i < SCR_SIDX_N; i++) {
@@ -90,7 +90,7 @@ ScrStr *scr_str_new(const char *bytes, size_t len) {
90
90
  * blocks instead of paging in fresh zero-filled memory 40k times. Disabled
91
91
  * in the audit lane so ASan sees every logical free as a real free. */
92
92
  #ifndef SCR_RC_AUDIT
93
- static ScrStr *scr_str_spare;
93
+ static SCR_TL ScrStr *scr_str_spare;
94
94
  #endif
95
95
 
96
96
  /* A spare-block reuse must not waste grossly (cap <= 4x the need) and only
package/src/scr_symbol.c CHANGED
@@ -55,7 +55,7 @@ ScrSym *scr_sym_new(ScrStr *desc) {
55
55
 
56
56
  /* ── the Symbol.for global registry ──────────────────────────────────── */
57
57
 
58
- static ScrSym *g_sym_registry = NULL;
58
+ static SCR_TL ScrSym *g_sym_registry = NULL;
59
59
 
60
60
  static void scr_sym_registry_cleanup(void) {
61
61
  ScrSym *s = g_sym_registry;
package/src/scr_tls.c CHANGED
@@ -1415,6 +1415,27 @@ ScrNetServer *scr_tls_create_server_dyn(const ScrDyn *opts /*borrowed*/,
1415
1415
  ScrNetServer *scr_https_create_server_dyn(const ScrDyn *opts /*borrowed*/,
1416
1416
  ScrClosure *handler /*moves, nullable*/,
1417
1417
  ScrHttpReqFn fn) {
1418
+ /* HTTPS ServerOptions extends http.ServerOptions. Consume the one HTTP
1419
+ * constructor field this runtime models before the shared TLS walker;
1420
+ * that walker deliberately owns only the TLS-side option surface. */
1421
+ bool has_timeout_buffer = false;
1422
+ double timeout_buffer = 0;
1423
+ if (opts != NULL && opts->kind == SCR_DYN_OBJ) {
1424
+ const ScrDyn *v = scr_dyn_obj_get(opts, "keepAliveTimeoutBuffer", 22);
1425
+ if (v != NULL && v->kind != SCR_DYN_UNDEF) {
1426
+ if (v->kind != SCR_DYN_NUM) {
1427
+ scr_dyn_arg_type_fail("keepAliveTimeoutBuffer", "of type number", v);
1428
+ scr_closure_release(handler);
1429
+ return NULL;
1430
+ }
1431
+ timeout_buffer = v->v.num;
1432
+ if (!scr_net_server_timeout_option_check(4, timeout_buffer)) {
1433
+ scr_closure_release(handler);
1434
+ return NULL;
1435
+ }
1436
+ has_timeout_buffer = true;
1437
+ }
1438
+ }
1418
1439
  ScrBytes *cert, *key;
1419
1440
  if (!scr_tls_srv_opts_walk(opts, "https.createServer", &cert, &key)) {
1420
1441
  scr_closure_release(handler);
@@ -1422,6 +1443,7 @@ ScrNetServer *scr_https_create_server_dyn(const ScrDyn *opts /*borrowed*/,
1422
1443
  }
1423
1444
  ScrNetServer *s = scr_https_create_server((const char *)cert->data, cert->len,
1424
1445
  (const char *)key->data, key->len, handler, fn);
1446
+ if (has_timeout_buffer) scr_net_server_timeout_set(s, 4, timeout_buffer);
1425
1447
  scr_bytes_release(cert);
1426
1448
  scr_bytes_release(key);
1427
1449
  return s;
package/src/scr_union.c CHANGED
@@ -15,7 +15,7 @@
15
15
  /* Live union count for the RC audit lane (-DSCR_RC_AUDIT); same contract
16
16
  * as scr_str_live_count in scr_string.c. */
17
17
  #ifdef SCR_RC_AUDIT
18
- static long scr_live_unions = 0;
18
+ static SCR_TL long scr_live_unions = 0;
19
19
  long scr_union_live_count(void) { return scr_live_unions; }
20
20
  #endif
21
21
 
package/vendor/README.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Vendored third-party code
2
2
 
3
+ ## libucontext (derived source)
4
+
5
+ - Project: https://github.com/kaniini/libucontext
6
+ - Commit: 49e671dd52ff6791295d8161ad3b6da7dc5f6f9d
7
+ - License: ISC (copyright and permission notice reproduced in `src/scr_musl.c`)
8
+
9
+ The x86_64 register-save/restore and `makecontext` logic is adapted into `src/scr_musl.c`, rather than carried as a separate library, to supply the legacy `ucontext` functions that musl declares but does not implement. It is compiled only for the explicit `x86_64-linux-musl` target and uses musl's public `ucontext_t` layout. The signal-mask compatibility wrapper is deliberately not included: scriptc fibers perform user-space register swaps and do not change a per-fiber signal mask.
10
+
3
11
  ## ryu/
4
12
 
5
13
  - Project: https://github.com/ulfjack/ryu