/
githubmirror
/
deno
Обзор
Документация
Войти
/
githubmirror
/
deno
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
tests/napi/async_test.js
103 строки
4 KB
Bartek Iwańczuk
fix(ext/napi): don't resurrect a released threadsafe function in acquire (#36054)
15 июл 2026, 14:21
Не верифицирован
15 июл 2026, 14:21
58d4d24
Код
Авторство
О чём код?
// Copyright 2018-2026 the Deno authors. MIT license. import { assertEquals, loadTestLibrary } from "./common.js"; const asyncTask = loadTestLibrary(); Deno.test("napi async task schedule", async () => { let called = false; await new Promise((resolve) => { asyncTask.test_async_work(() => { called = true; resolve(); }); }); assertEquals(called, true); }); Deno.test("napi async work with threadsafe function from execute", async () => { // This tests that the execute callback runs on a worker thread by calling // a threadsafe function from it. Previously this would deadlock because // execute ran on the main thread. let called = false; await new Promise((resolve) => { asyncTask.test_async_work_with_tsfn(() => { called = true; resolve(); }); }); assertEquals(called, true); }); Deno.test("napi tsfn call_js_cb receives valid env after close race", async () => { // Reproduces a crash seen with node-pty: when a tsfn is released while // calls are still pending, the call_js_cb must receive a valid env (not // null). The native test spawns two threads (one calling the tsfn and // one releasing it) to trigger the race. The call_js_cb asserts env is // not null and uses it, which would SIGSEGV before the fix. await new Promise((resolve) => { asyncTask.test_tsfn_close_race(() => { resolve(); }); }); }); Deno.test("napi tsfn call_js_cb receives valid env after abort race", async () => { // Same race as above, but uses napi_tsfn_abort mode to close the tsfn. // Abort forces immediate close regardless of thread_count. await new Promise((resolve) => { asyncTask.test_tsfn_abort_race(() => { resolve(); }); }); }); Deno.test("napi tsfn abort with outstanding thread does not double free", async () => { // With napi_tsfn_abort, a previous version freed the tsfn regardless of // thread_count, leaving other reference holders with a dangling pointer. // A subsequent release could then spawn a second drop of the same box, // causing a use-after-free and a double free (assert in TsFn::drop). Here // the tsfn is created with initial_thread_count = 2 and both threads must // release before it is freed; the finalizer must run exactly once. await new Promise((resolve) => { asyncTask.test_tsfn_abort_outstanding(() => { resolve(); }); }); }); Deno.test("napi tsfn acquire and release", async function () { await new Promise((resolve) => { asyncTask.test_tsfn_acquire_release(resolve); }); }); Deno.test("napi tsfn acquire racing final release does not double free", async () => { // A previous version of `acquire` did a plain `fetch_add` after a non-atomic // `is_closing` check, so it could resurrect a tsfn whose thread_count had // already reached zero (racing the final release). The resurrecting thread's // later release then spawned a second drop of the same box — a use-after-free // and double free (assert in TsFn::drop). Each call runs one acquire-vs-final // -release race; loop it to hit the narrow window. The finalizer must run // exactly once per iteration, so every promise resolves and none double free. for (let i = 0; i < 500; i++) { await new Promise((resolve) => { asyncTask.test_tsfn_acquire_release_race(() => { resolve(); }); }); } }); Deno.test("napi tsfn get context", function () { assertEquals(asyncTask.test_tsfn_get_context(), true); }); Deno.test("napi cancel async work", async function () { const result = await new Promise((resolve) => { asyncTask.test_cancel_async_work((cancelled) => { resolve(cancelled); }); }); assertEquals(result, true); });