@scriptc/runtime 0.0.23 → 0.0.25
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/package.json +1 -1
- package/src/scr_array.c +65 -0
- package/src/scr_async.c +286 -5
- package/src/scr_bytes.c +634 -4
- package/src/scr_bytes_io.c +4 -2
- package/src/scr_file_handle.c +491 -0
- package/src/scr_island.c +10 -0
- package/src/scr_json.c +51 -19
- package/src/scr_lib.c +680 -30
- package/src/scr_net.c +12 -5
- package/src/scr_number.c +9 -0
- package/src/scr_regex.c +12 -1
- package/src/scr_runtime.h +101 -8
- package/src/scr_string.c +13 -2
- package/src/scr_text_decoder_data.h +7389 -0
- package/src/scr_util.c +984 -0
- package/src/scr_win_stats.h +23 -0
package/package.json
CHANGED
package/src/scr_array.c
CHANGED
|
@@ -68,6 +68,16 @@ static void scr_elem_release(const ScrArr *a, uint64_t slot) {
|
|
|
68
68
|
else if (a->elem == SCR_ELEM_REF) a->elem_release(p);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
static uint64_t scr_elem_retain_slot(const ScrArr *a, uint64_t slot) {
|
|
72
|
+
if (!scr_elem_is_ref(a->elem)) return slot;
|
|
73
|
+
void *p = scr_slot_to_ptr(slot);
|
|
74
|
+
if (a->elem == SCR_ELEM_STR) p = scr_str_retain((ScrStr *)p);
|
|
75
|
+
else if (a->elem == SCR_ELEM_ARR) p = scr_arr_retain((ScrArr *)p);
|
|
76
|
+
else if (a->elem == SCR_ELEM_BYTES) p = scr_bytes_retain((ScrBytes *)p);
|
|
77
|
+
else p = a->elem_retain(p);
|
|
78
|
+
return scr_slot_from_ptr(p);
|
|
79
|
+
}
|
|
80
|
+
|
|
71
81
|
/* ── lifecycle ─────────────────────────────────────────────────────────── */
|
|
72
82
|
|
|
73
83
|
static void scr_arr_grow(ScrArr *a, size_t need) {
|
|
@@ -259,6 +269,61 @@ double scr_arr_push_ref(ScrArr *a, void *v) {
|
|
|
259
269
|
return scr_arr_push_slot(a, scr_slot_from_ptr(v));
|
|
260
270
|
}
|
|
261
271
|
|
|
272
|
+
/* ── unshift / reverse ──────────────────────────────────────────────────
|
|
273
|
+
* Single-element unshift takes ownership, matching push. The emitter calls
|
|
274
|
+
* it from right to left after evaluating every variadic argument, preserving
|
|
275
|
+
* JS argument order without a temporary array. The spread form borrows and
|
|
276
|
+
* retains its source, snapshots the count, and handles self-spread after the
|
|
277
|
+
* tail move by reading the relocated original block. */
|
|
278
|
+
static double scr_arr_unshift_slot(ScrArr *a, uint64_t slot) {
|
|
279
|
+
scr_arr_grow(a, a->len + 1);
|
|
280
|
+
memmove(a->data + 1, a->data, a->len * sizeof(uint64_t));
|
|
281
|
+
a->data[0] = slot;
|
|
282
|
+
a->len++;
|
|
283
|
+
return (double)a->len;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
double scr_arr_unshift_f64(ScrArr *a, double v) {
|
|
287
|
+
return scr_arr_unshift_slot(a, scr_slot_from_f64(v));
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
double scr_arr_unshift_bool(ScrArr *a, bool v) {
|
|
291
|
+
return scr_arr_unshift_slot(a, (uint64_t)(v ? 1 : 0));
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
double scr_arr_unshift_ref(ScrArr *a, void *v) {
|
|
295
|
+
return scr_arr_unshift_slot(a, scr_slot_from_ptr(v));
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
double scr_arr_unshift_spread(ScrArr *a, const ScrArr *src) {
|
|
299
|
+
size_t old_len = a->len;
|
|
300
|
+
size_t add = src->len;
|
|
301
|
+
if (add == 0) return (double)old_len;
|
|
302
|
+
if (add > SIZE_MAX - old_len) scr_arr_oom();
|
|
303
|
+
scr_arr_grow(a, old_len + add);
|
|
304
|
+
memmove(a->data + add, a->data, old_len * sizeof(uint64_t));
|
|
305
|
+
if (src == a) {
|
|
306
|
+
for (size_t i = 0; i < add; i++) {
|
|
307
|
+
a->data[i] = scr_elem_retain_slot(a, a->data[add + i]);
|
|
308
|
+
}
|
|
309
|
+
} else {
|
|
310
|
+
for (size_t i = 0; i < add; i++) {
|
|
311
|
+
a->data[i] = scr_elem_retain_slot(src, src->data[i]);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
a->len = old_len + add;
|
|
315
|
+
return (double)a->len;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
ScrArr *scr_arr_reverse(ScrArr *a) {
|
|
319
|
+
for (size_t i = 0; i < a->len / 2; i++) {
|
|
320
|
+
uint64_t tmp = a->data[i];
|
|
321
|
+
a->data[i] = a->data[a->len - 1 - i];
|
|
322
|
+
a->data[a->len - 1 - i] = tmp;
|
|
323
|
+
}
|
|
324
|
+
return scr_arr_retain(a);
|
|
325
|
+
}
|
|
326
|
+
|
|
262
327
|
static uint64_t scr_arr_pop_slot(ScrArr *a) {
|
|
263
328
|
if (a->len == 0) {
|
|
264
329
|
scr_trap("scriptc: RangeError: pop() on an empty array\n");
|
package/src/scr_async.c
CHANGED
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
#include <windows.h>
|
|
40
40
|
#else
|
|
41
41
|
#include <poll.h>
|
|
42
|
+
#include <pthread.h>
|
|
42
43
|
#include <signal.h>
|
|
43
44
|
#include <ucontext.h>
|
|
44
45
|
#include <unistd.h>
|
|
@@ -627,8 +628,9 @@ ScrArr *scr_active_resources(void) {
|
|
|
627
628
|
* left queued on the uncaught paths) never run — the teardown releases
|
|
628
629
|
* them, like Node dropping the queue at exit. */
|
|
629
630
|
typedef struct ScrNtick {
|
|
630
|
-
ScrClosure *cb;
|
|
631
|
-
|
|
631
|
+
ScrClosure *cb; /* owned; NULL for a raw C-hook entry */
|
|
632
|
+
ScrFsRenameFn error_cb; /* process-write success adapter, or NULL */
|
|
633
|
+
void (*raw)(void); /* hook when cb == NULL (stream markers) */
|
|
632
634
|
struct ScrNtick *next;
|
|
633
635
|
} ScrNtick;
|
|
634
636
|
|
|
@@ -649,6 +651,20 @@ void scr_next_tick(ScrClosure *cb /*moves*/) {
|
|
|
649
651
|
scr_nt_tail = t;
|
|
650
652
|
}
|
|
651
653
|
|
|
654
|
+
/* stdout/stderr write completions are Node nextTicks too, but their
|
|
655
|
+
* callback receives the success `null` argument. Reuse the error-first
|
|
656
|
+
* adapter ABI also used by fs.rename: emitted adapters construct the
|
|
657
|
+
* program's Error | null union, and NULL selects its null arm. */
|
|
658
|
+
void scr_process_write_callback(ScrClosure *cb /*moves*/, ScrFsRenameFn fn) {
|
|
659
|
+
ScrNtick *t = calloc(1, sizeof *t);
|
|
660
|
+
if (!t) scr_oom();
|
|
661
|
+
t->cb = cb;
|
|
662
|
+
t->error_cb = fn;
|
|
663
|
+
if (scr_nt_tail) scr_nt_tail->next = t;
|
|
664
|
+
else scr_nt_head = t;
|
|
665
|
+
scr_nt_tail = t;
|
|
666
|
+
}
|
|
667
|
+
|
|
652
668
|
/* A RAW C-hook tick: the stream unit enqueues one marker per deferred
|
|
653
669
|
* stream emission so those emissions interleave with user nextTicks in
|
|
654
670
|
* true FIFO order — in Node they ARE nextTicks (resume_, emitReadable_,
|
|
@@ -1441,6 +1457,11 @@ ScrPromise *scr_fsp_write_file(ScrStr *path, ScrStr *data) {
|
|
|
1441
1457
|
return scr_promise_settled_void();
|
|
1442
1458
|
}
|
|
1443
1459
|
|
|
1460
|
+
ScrPromise *scr_fsp_write_file_mode(ScrStr *path, ScrStr *data, double mode) {
|
|
1461
|
+
scr_fs_write_file_mode(path, data, mode);
|
|
1462
|
+
return scr_promise_settled_void();
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1444
1465
|
ScrPromise *scr_fsp_mkdir(ScrStr *path) {
|
|
1445
1466
|
scr_fs_mkdir(path);
|
|
1446
1467
|
return scr_promise_settled_void();
|
|
@@ -1486,6 +1507,248 @@ ScrPromise *scr_fsp_stat(ScrStr *path) {
|
|
|
1486
1507
|
return scr_promise_settled_ref(st, &scr_stats_retain_v, &scr_stats_release_v, NULL);
|
|
1487
1508
|
}
|
|
1488
1509
|
|
|
1510
|
+
ScrPromise *scr_fsp_rename(ScrStr *oldpath, ScrStr *newpath) {
|
|
1511
|
+
scr_fs_rename(oldpath, newpath);
|
|
1512
|
+
return scr_promise_settled_void();
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
/* fs.rename(old, new, cb): the static callback surface. The OS operation
|
|
1516
|
+
* starts immediately on a native worker, matching libuv's key contract:
|
|
1517
|
+
* while JS/main is synchronously occupied, the filesystem request can still
|
|
1518
|
+
* make progress. Only immutable path bytes cross onto the worker. Error
|
|
1519
|
+
* construction, callback invocation, and every RC mutation stay on the main
|
|
1520
|
+
* runtime thread when a later loop turn observes completion.
|
|
1521
|
+
*
|
|
1522
|
+
* A compact operation queue is also the liveness handle: an outstanding
|
|
1523
|
+
* callback-style request keeps the loop alive. Four persistent workers mirror
|
|
1524
|
+
* libuv's default filesystem concurrency without creating one OS thread per
|
|
1525
|
+
* request. A platform mutex publishes each result and the syscall's filesystem
|
|
1526
|
+
* effects before the loop removes it from the completion queue. */
|
|
1527
|
+
#ifndef SCR_LIB
|
|
1528
|
+
typedef struct ScrFsRenameOp {
|
|
1529
|
+
ScrStr *oldpath;
|
|
1530
|
+
ScrStr *newpath;
|
|
1531
|
+
ScrClosure *cb;
|
|
1532
|
+
ScrFsRenameFn fn;
|
|
1533
|
+
int error;
|
|
1534
|
+
struct ScrFsRenameOp *next;
|
|
1535
|
+
} ScrFsRenameOp;
|
|
1536
|
+
|
|
1537
|
+
static ScrFsRenameOp *scr_fs_rename_work = NULL;
|
|
1538
|
+
static ScrFsRenameOp **scr_fs_rename_work_tail = &scr_fs_rename_work;
|
|
1539
|
+
static ScrFsRenameOp *scr_fs_rename_done = NULL;
|
|
1540
|
+
static ScrFsRenameOp **scr_fs_rename_done_tail = &scr_fs_rename_done;
|
|
1541
|
+
static size_t scr_fs_rename_pending_count = 0;
|
|
1542
|
+
static size_t scr_fs_rename_worker_count = 0;
|
|
1543
|
+
static bool scr_fs_rename_stopping = false;
|
|
1544
|
+
static bool scr_fs_rename_shutdown_registered = false;
|
|
1545
|
+
|
|
1546
|
+
#ifdef _WIN32
|
|
1547
|
+
static HANDLE scr_fs_rename_workers[4];
|
|
1548
|
+
static SRWLOCK scr_fs_rename_lock = SRWLOCK_INIT;
|
|
1549
|
+
static CONDITION_VARIABLE scr_fs_rename_cond = CONDITION_VARIABLE_INIT;
|
|
1550
|
+
static void scr_fs_rename_lock_enter(void) { AcquireSRWLockExclusive(&scr_fs_rename_lock); }
|
|
1551
|
+
static void scr_fs_rename_lock_leave(void) { ReleaseSRWLockExclusive(&scr_fs_rename_lock); }
|
|
1552
|
+
static void scr_fs_rename_wait(void) {
|
|
1553
|
+
(void)SleepConditionVariableSRW(&scr_fs_rename_cond, &scr_fs_rename_lock, INFINITE, 0);
|
|
1554
|
+
}
|
|
1555
|
+
static void scr_fs_rename_signal(void) { WakeConditionVariable(&scr_fs_rename_cond); }
|
|
1556
|
+
static void scr_fs_rename_broadcast(void) { WakeAllConditionVariable(&scr_fs_rename_cond); }
|
|
1557
|
+
#else
|
|
1558
|
+
static pthread_t scr_fs_rename_workers[4];
|
|
1559
|
+
static pthread_mutex_t scr_fs_rename_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
1560
|
+
static pthread_cond_t scr_fs_rename_cond = PTHREAD_COND_INITIALIZER;
|
|
1561
|
+
static void scr_fs_rename_lock_enter(void) { (void)pthread_mutex_lock(&scr_fs_rename_lock); }
|
|
1562
|
+
static void scr_fs_rename_lock_leave(void) { (void)pthread_mutex_unlock(&scr_fs_rename_lock); }
|
|
1563
|
+
static void scr_fs_rename_wait(void) {
|
|
1564
|
+
(void)pthread_cond_wait(&scr_fs_rename_cond, &scr_fs_rename_lock);
|
|
1565
|
+
}
|
|
1566
|
+
static void scr_fs_rename_signal(void) { (void)pthread_cond_signal(&scr_fs_rename_cond); }
|
|
1567
|
+
static void scr_fs_rename_broadcast(void) { (void)pthread_cond_broadcast(&scr_fs_rename_cond); }
|
|
1568
|
+
#endif
|
|
1569
|
+
|
|
1570
|
+
static void scr_fs_rename_op_release(ScrFsRenameOp *op) {
|
|
1571
|
+
scr_str_release(op->oldpath);
|
|
1572
|
+
scr_str_release(op->newpath);
|
|
1573
|
+
scr_closure_release(op->cb);
|
|
1574
|
+
free(op);
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
static void scr_fs_rename_worker_loop(void) {
|
|
1578
|
+
for (;;) {
|
|
1579
|
+
scr_fs_rename_lock_enter();
|
|
1580
|
+
while (scr_fs_rename_work == NULL && !scr_fs_rename_stopping) scr_fs_rename_wait();
|
|
1581
|
+
if (scr_fs_rename_stopping) {
|
|
1582
|
+
scr_fs_rename_lock_leave();
|
|
1583
|
+
return;
|
|
1584
|
+
}
|
|
1585
|
+
ScrFsRenameOp *op = scr_fs_rename_work;
|
|
1586
|
+
scr_fs_rename_work = op->next;
|
|
1587
|
+
if (scr_fs_rename_work == NULL) scr_fs_rename_work_tail = &scr_fs_rename_work;
|
|
1588
|
+
scr_fs_rename_lock_leave();
|
|
1589
|
+
|
|
1590
|
+
op->error = scr_fs_rename_raw(op->oldpath, op->newpath);
|
|
1591
|
+
|
|
1592
|
+
scr_fs_rename_lock_enter();
|
|
1593
|
+
op->next = NULL;
|
|
1594
|
+
*scr_fs_rename_done_tail = op;
|
|
1595
|
+
scr_fs_rename_done_tail = &op->next;
|
|
1596
|
+
scr_fs_rename_lock_leave();
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
#ifdef _WIN32
|
|
1601
|
+
static DWORD WINAPI scr_fs_rename_worker(LPVOID payload) {
|
|
1602
|
+
(void)payload;
|
|
1603
|
+
scr_fs_rename_worker_loop();
|
|
1604
|
+
return 0;
|
|
1605
|
+
}
|
|
1606
|
+
#else
|
|
1607
|
+
static void *scr_fs_rename_worker(void *payload) {
|
|
1608
|
+
(void)payload;
|
|
1609
|
+
scr_fs_rename_worker_loop();
|
|
1610
|
+
return NULL;
|
|
1611
|
+
}
|
|
1612
|
+
#endif
|
|
1613
|
+
|
|
1614
|
+
/* Runs before the ordinary runtime atexit cleanup/audit (it is registered
|
|
1615
|
+
* lazily by the first rename request, after scr_init/scr_lib_init). Fatal
|
|
1616
|
+
* top-level or callback exceptions can stop the loop with requests still in
|
|
1617
|
+
* flight; join the workers before releasing their retained path/callback
|
|
1618
|
+
* payloads so sanitized builds keep the program's real exit verdict. */
|
|
1619
|
+
static void scr_fs_renames_shutdown(void) {
|
|
1620
|
+
scr_fs_rename_lock_enter();
|
|
1621
|
+
scr_fs_rename_stopping = true;
|
|
1622
|
+
scr_fs_rename_broadcast();
|
|
1623
|
+
scr_fs_rename_lock_leave();
|
|
1624
|
+
|
|
1625
|
+
for (size_t i = 0; i < scr_fs_rename_worker_count; i++) {
|
|
1626
|
+
#ifdef _WIN32
|
|
1627
|
+
(void)WaitForSingleObject(scr_fs_rename_workers[i], INFINITE);
|
|
1628
|
+
CloseHandle(scr_fs_rename_workers[i]);
|
|
1629
|
+
#else
|
|
1630
|
+
(void)pthread_join(scr_fs_rename_workers[i], NULL);
|
|
1631
|
+
#endif
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
scr_fs_rename_lock_enter();
|
|
1635
|
+
ScrFsRenameOp *work = scr_fs_rename_work;
|
|
1636
|
+
ScrFsRenameOp *done = scr_fs_rename_done;
|
|
1637
|
+
scr_fs_rename_work = NULL;
|
|
1638
|
+
scr_fs_rename_work_tail = &scr_fs_rename_work;
|
|
1639
|
+
scr_fs_rename_done = NULL;
|
|
1640
|
+
scr_fs_rename_done_tail = &scr_fs_rename_done;
|
|
1641
|
+
scr_fs_rename_pending_count = 0;
|
|
1642
|
+
scr_fs_rename_lock_leave();
|
|
1643
|
+
while (work != NULL) {
|
|
1644
|
+
ScrFsRenameOp *next = work->next;
|
|
1645
|
+
scr_fs_rename_op_release(work);
|
|
1646
|
+
work = next;
|
|
1647
|
+
}
|
|
1648
|
+
while (done != NULL) {
|
|
1649
|
+
ScrFsRenameOp *next = done->next;
|
|
1650
|
+
scr_fs_rename_op_release(done);
|
|
1651
|
+
done = next;
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
static bool scr_fs_renames_pending(void) { return scr_fs_rename_pending_count != 0; }
|
|
1656
|
+
|
|
1657
|
+
static bool scr_fs_renames_dispatch(void) {
|
|
1658
|
+
scr_fs_rename_lock_enter();
|
|
1659
|
+
ScrFsRenameOp *op = scr_fs_rename_done;
|
|
1660
|
+
if (op != NULL) {
|
|
1661
|
+
scr_fs_rename_done = op->next;
|
|
1662
|
+
if (scr_fs_rename_done == NULL) scr_fs_rename_done_tail = &scr_fs_rename_done;
|
|
1663
|
+
}
|
|
1664
|
+
scr_fs_rename_lock_leave();
|
|
1665
|
+
if (op == NULL) return false;
|
|
1666
|
+
scr_fs_rename_pending_count--;
|
|
1667
|
+
ScrCaught *caught = NULL;
|
|
1668
|
+
ScrError *err = NULL;
|
|
1669
|
+
if (op->error != 0) {
|
|
1670
|
+
scr_fs_rename_error(op->error, op->oldpath, op->newpath);
|
|
1671
|
+
caught = scr_exc_take();
|
|
1672
|
+
if (caught && caught->kind == SCR_EXC_OBJ && scr_error_is(caught->payload)) {
|
|
1673
|
+
err = (ScrError *)caught->payload;
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
op->fn(op->cb, err);
|
|
1677
|
+
scr_caught_release(caught);
|
|
1678
|
+
scr_fs_rename_op_release(op);
|
|
1679
|
+
return true;
|
|
1680
|
+
}
|
|
1681
|
+
#else
|
|
1682
|
+
static bool scr_fs_renames_pending(void) { return false; }
|
|
1683
|
+
static bool scr_fs_renames_dispatch(void) { return false; }
|
|
1684
|
+
#endif
|
|
1685
|
+
|
|
1686
|
+
void scr_fs_rename_async(ScrStr *oldpath, ScrStr *newpath,
|
|
1687
|
+
ScrClosure *cb, ScrFsRenameFn fn) {
|
|
1688
|
+
#ifdef SCR_LIB
|
|
1689
|
+
(void)oldpath; (void)newpath; (void)fn;
|
|
1690
|
+
scr_closure_release(cb);
|
|
1691
|
+
scr_trap("scriptc: internal error: fs.rename reached in a library build — please report this");
|
|
1692
|
+
#else
|
|
1693
|
+
if (!scr_fs_rename_shutdown_registered) {
|
|
1694
|
+
if (atexit(scr_fs_renames_shutdown) != 0) {
|
|
1695
|
+
scr_trap("scriptc: could not register fs.rename worker cleanup\n");
|
|
1696
|
+
}
|
|
1697
|
+
scr_fs_rename_shutdown_registered = true;
|
|
1698
|
+
}
|
|
1699
|
+
ScrFsRenameOp *op = calloc(1, sizeof *op);
|
|
1700
|
+
if (!op) scr_trap("scriptc: out of memory\n");
|
|
1701
|
+
op->oldpath = scr_str_retain(oldpath);
|
|
1702
|
+
op->newpath = scr_str_retain(newpath);
|
|
1703
|
+
op->cb = cb;
|
|
1704
|
+
op->fn = fn;
|
|
1705
|
+
scr_fs_rename_pending_count++;
|
|
1706
|
+
scr_fs_rename_lock_enter();
|
|
1707
|
+
*scr_fs_rename_work_tail = op;
|
|
1708
|
+
scr_fs_rename_work_tail = &op->next;
|
|
1709
|
+
int create_error = 0;
|
|
1710
|
+
if (scr_fs_rename_worker_count < 4) {
|
|
1711
|
+
#ifdef _WIN32
|
|
1712
|
+
HANDLE worker = CreateThread(NULL, 0, scr_fs_rename_worker, NULL, 0, NULL);
|
|
1713
|
+
if (worker != NULL) {
|
|
1714
|
+
scr_fs_rename_workers[scr_fs_rename_worker_count] = worker;
|
|
1715
|
+
scr_fs_rename_worker_count++;
|
|
1716
|
+
} else {
|
|
1717
|
+
create_error = EAGAIN;
|
|
1718
|
+
}
|
|
1719
|
+
#else
|
|
1720
|
+
pthread_t worker;
|
|
1721
|
+
create_error = pthread_create(&worker, NULL, scr_fs_rename_worker, NULL);
|
|
1722
|
+
if (create_error == 0) {
|
|
1723
|
+
scr_fs_rename_workers[scr_fs_rename_worker_count] = worker;
|
|
1724
|
+
scr_fs_rename_worker_count++;
|
|
1725
|
+
}
|
|
1726
|
+
#endif
|
|
1727
|
+
}
|
|
1728
|
+
if (create_error != 0 && scr_fs_rename_worker_count == 0) {
|
|
1729
|
+
/* Submission failure is still callback-asynchronous. No worker exists,
|
|
1730
|
+
* so publish every queued request as the same resource error. */
|
|
1731
|
+
while (scr_fs_rename_work != NULL) {
|
|
1732
|
+
ScrFsRenameOp *failed = scr_fs_rename_work;
|
|
1733
|
+
scr_fs_rename_work = failed->next;
|
|
1734
|
+
failed->error = create_error;
|
|
1735
|
+
failed->next = NULL;
|
|
1736
|
+
*scr_fs_rename_done_tail = failed;
|
|
1737
|
+
scr_fs_rename_done_tail = &failed->next;
|
|
1738
|
+
}
|
|
1739
|
+
scr_fs_rename_work_tail = &scr_fs_rename_work;
|
|
1740
|
+
} else {
|
|
1741
|
+
scr_fs_rename_signal();
|
|
1742
|
+
}
|
|
1743
|
+
scr_fs_rename_lock_leave();
|
|
1744
|
+
#endif
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
void scr_fs_rename_thunk0(ScrClosure *cb, ScrError *err) {
|
|
1748
|
+
(void)err;
|
|
1749
|
+
((void (*)(ScrClosure *))cb->fn)(cb);
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1489
1752
|
/* ── node:timers/promises ────────────────────────────────────────────
|
|
1490
1753
|
* The promisified pair: a PENDING void promise a one-shot heap timer
|
|
1491
1754
|
* (setTimeout) or the immediate queue (setImmediate) fulfills — the
|
|
@@ -1763,10 +2026,12 @@ bool scr_loop_run(ScrPromise *top_level) {
|
|
|
1763
2026
|
scr_nt_head = t->next;
|
|
1764
2027
|
if (scr_nt_head == NULL) scr_nt_tail = NULL;
|
|
1765
2028
|
ScrClosure *cb = t->cb;
|
|
2029
|
+
ScrFsRenameFn error_cb = t->error_cb;
|
|
1766
2030
|
void (*raw)(void) = t->raw;
|
|
1767
2031
|
free(t);
|
|
1768
2032
|
if (cb) {
|
|
1769
|
-
|
|
2033
|
+
if (error_cb) error_cb(cb, NULL);
|
|
2034
|
+
else ((void (*)(ScrClosure *))cb->fn)(cb);
|
|
1770
2035
|
scr_closure_release(cb);
|
|
1771
2036
|
} else {
|
|
1772
2037
|
raw(); /* one stream tick, FIFO with the user ticks around it */
|
|
@@ -1807,6 +2072,16 @@ bool scr_loop_run(ScrPromise *top_level) {
|
|
|
1807
2072
|
* here would walk the whole live heap every turn, which is precisely
|
|
1808
2073
|
* what the generations exist to avoid. No-op on an empty buffer. */
|
|
1809
2074
|
scr_cyc_collect_scheduled();
|
|
2075
|
+
/* Completed callback-style filesystem work is delivered on the main
|
|
2076
|
+
* runtime thread. A worker may have finished while synchronous user code
|
|
2077
|
+
* occupied that thread. Deliver exactly one completion per checkpoint:
|
|
2078
|
+
* Node drains nextTicks and microtasks after each native callback before
|
|
2079
|
+
* invoking the next ready callback. */
|
|
2080
|
+
if (scr_fs_renames_pending()) {
|
|
2081
|
+
bool dispatched = scr_fs_renames_dispatch();
|
|
2082
|
+
if (scr_exc_pending()) return false;
|
|
2083
|
+
if (dispatched) continue;
|
|
2084
|
+
}
|
|
1810
2085
|
/* Stream tick dispatch (scr_stream.c, when linked): the deferred
|
|
1811
2086
|
* next-tick emissions ('data' flow kicks, 'readable'/'end'/'finish'/
|
|
1812
2087
|
* 'drain'/'error'/'close') fire now, FIRST — the nextTick station.
|
|
@@ -1874,7 +2149,8 @@ bool scr_loop_run(ScrPromise *top_level) {
|
|
|
1874
2149
|
(scr_events_pending_fn != NULL && scr_events_pending_fn()) ||
|
|
1875
2150
|
(scr_net_pending_fn != NULL && scr_net_pending_fn()) ||
|
|
1876
2151
|
(scr_dgram_pending_fn != NULL && scr_dgram_pending_fn()) ||
|
|
1877
|
-
(scr_watch_pending_fn != NULL && scr_watch_pending_fn())
|
|
2152
|
+
(scr_watch_pending_fn != NULL && scr_watch_pending_fn()) ||
|
|
2153
|
+
scr_fs_renames_pending();
|
|
1878
2154
|
if (held) {
|
|
1879
2155
|
scr_children_poll();
|
|
1880
2156
|
if (scr_exc_pending()) return false; /* uncaught throw in a listener */
|
|
@@ -1893,13 +2169,14 @@ bool scr_loop_run(ScrPromise *top_level) {
|
|
|
1893
2169
|
bool net = scr_net_pending_fn != NULL && scr_net_pending_fn();
|
|
1894
2170
|
bool dgram = scr_dgram_pending_fn != NULL && scr_dgram_pending_fn();
|
|
1895
2171
|
bool watch = scr_watch_pending_fn != NULL && scr_watch_pending_fn();
|
|
2172
|
+
bool renames = scr_fs_renames_pending();
|
|
1896
2173
|
/* Timer liveness counts only REF'd timers: an unref'd timer stays in
|
|
1897
2174
|
* the heap (and fires if the loop runs on for other reasons) but does
|
|
1898
2175
|
* not by itself keep the process alive — Node's unref semantics.
|
|
1899
2176
|
* Children follow the same rule: an unref'd child is still REAPED
|
|
1900
2177
|
* while the loop runs (kids drives the sweeps and sleeps above) but
|
|
1901
2178
|
* only reffed ones keep the process alive. */
|
|
1902
|
-
if (scr_reffed_timers == 0 && scr_reffed_immediates == 0 && !scr_children_reffed_pending() && !io && !events && !net && !dgram && !watch) break;
|
|
2179
|
+
if (scr_reffed_timers == 0 && scr_reffed_immediates == 0 && !scr_children_reffed_pending() && !io && !events && !net && !dgram && !watch && !renames) break;
|
|
1903
2180
|
/* Sleep to the earliest deadline, then run every due timer (each may
|
|
1904
2181
|
* enqueue microtasks, which the next iteration drains first). Who
|
|
1905
2182
|
* sleeps depends on what is pending:
|
|
@@ -1920,6 +2197,10 @@ bool scr_loop_run(ScrPromise *top_level) {
|
|
|
1920
2197
|
* - timers only: plain nanosleep to the deadline. */
|
|
1921
2198
|
double now = scr_now_ms();
|
|
1922
2199
|
double due = scr_ntimers > 0 ? scr_timers[0].deadline_ms : now + SCR_IO_POLL_MS;
|
|
2200
|
+
/* The rename worker has no platform poll handle yet. Bound the idle
|
|
2201
|
+
* wait exactly like the portable child fallback so completion is noticed
|
|
2202
|
+
* promptly even when another poller owns the sleep. */
|
|
2203
|
+
if (renames && due > now + SCR_CHILD_POLL_MS) due = now + SCR_CHILD_POLL_MS;
|
|
1923
2204
|
/* An armed island timer (AbortSignal.timeout) caps the sleep: it must
|
|
1924
2205
|
* fire on time even while the poller waits on socket readiness. */
|
|
1925
2206
|
if (scr_island_deadline_fn != NULL) {
|