diff --git a/eh_translation.ts b/eh_translation.ts index 5a56512..c34928b 100644 --- a/eh_translation.ts +++ b/eh_translation.ts @@ -35,9 +35,10 @@ export type EHTTextFile = { data: EHTData[]; }; -async function fetch_eht_file() { +async function fetch_eht_file(signal?: AbortSignal) { const re = await fetch( "https://github.com/EhTagTranslation/DatabaseReleases/raw/master/db.text.json", + { signal }, ); if (re.status != 200) throw Error("fetch failed"); return re.text(); @@ -45,10 +46,11 @@ async function fetch_eht_file() { export async function load_eht_file( location: string | undefined = undefined, + signal?: AbortSignal, ): Promise { const s = location === undefined - ? await fetch_eht_file() - : await Deno.readTextFile(location); + ? await fetch_eht_file(signal) + : await Deno.readTextFile(location, { signal }); return JSON.parse(s); } diff --git a/main.ts b/main.ts index 181022c..1f9c18c 100644 --- a/main.ts +++ b/main.ts @@ -6,6 +6,7 @@ import { ParsedUrl, parseUrl, UrlType } from "./url.ts"; import { sure_dir } from "./utils.ts"; import { EhDb } from "./db.ts"; import { load_eht_file, update_database_tag } from "./eh_translation.ts"; +import { get_abort_signal } from "./signal_handler.ts"; function show_help() { console.log("Usage: main.ts [options]"); @@ -69,7 +70,7 @@ async function download() { } await manager.run(); } finally { - manager.close(); + if (!manager.aborted) manager.close(); } } async function run() { @@ -77,7 +78,7 @@ async function run() { try { await manager.run(); } finally { - manager.close(); + if (!manager.aborted) manager.close(); } } function optimize() { @@ -87,11 +88,18 @@ function optimize() { } async function update_tag_translation() { const db = new EhDb(settings.db_path || settings.base); - const f = await load_eht_file( - args._.length > 1 ? args._[1].toString() : undefined, - ); - update_database_tag(db, f); - db.close(); + const signal = get_abort_signal(); + try { + const f = await load_eht_file( + args._.length > 1 ? args._[1].toString() : undefined, + signal, + ); + update_database_tag(db, f); + } catch (e) { + if (!signal.aborted) throw e; + } finally { + db.close(); + } } async function main() { await sure_dir(settings.base); diff --git a/signal_handler.ts b/signal_handler.ts index a1871b7..88316c4 100644 --- a/signal_handler.ts +++ b/signal_handler.ts @@ -28,3 +28,17 @@ export function add_exit_handler(m: TaskManager) { }); } } + +export function get_abort_signal(callback?: () => void): AbortSignal { + const a = new AbortController(); + const handler = () => { + console.log("Aborted."); + a.abort(); + if (callback) callback(); + }; + Deno.addSignalListener("SIGINT", handler); + if (Deno.build.os !== "windows") { + Deno.addSignalListener("SIGKILL", handler); + } + return a.signal; +} diff --git a/task_manager.ts b/task_manager.ts index aed226f..7351183 100644 --- a/task_manager.ts +++ b/task_manager.ts @@ -93,7 +93,10 @@ export class TaskManager { } } close() { - if (this.#closed) return; + if (this.#closed) { + console.trace("Manager closed multiple times."); + return; + } this.#closed = true; this.db.close(); } diff --git a/utils.ts b/utils.ts index 2b39e75..4353f9a 100644 --- a/utils.ts +++ b/utils.ts @@ -90,3 +90,18 @@ export async function asyncFilter( )).filter((i) => i !== fail); return t.map((t) => t[0]); } + +export async function asyncForEach( + arr: ArrayLike, + callback: ( + this: V | undefined, + element: T, + index: number, + array: ArrayLike, + ) => Promise, + thisArg?: V, +) { + for (let i = 0; i < arr.length; i++) { + await callback.apply(thisArg, [arr[i], i, arr]); + } +} diff --git a/utils_test.ts b/utils_test.ts index d0d4f12..2c5112a 100644 --- a/utils_test.ts +++ b/utils_test.ts @@ -1,6 +1,12 @@ import { assertEquals } from "std/testing/asserts.ts"; import { check_running } from "./pid_check.ts"; -import { asyncFilter, promiseState, PromiseStatus, sleep } from "./utils.ts"; +import { + asyncFilter, + asyncForEach, + promiseState, + PromiseStatus, + sleep, +} from "./utils.ts"; Deno.test("promiseState_test", async () => { const p1 = new Promise((res) => setTimeout(() => res(100), 100)); @@ -42,3 +48,12 @@ Deno.test("asyncFilter_test", async () => { assertEquals(v, e); await Promise.allSettled(e); }); + +Deno.test("asyncForEach", async () => { + const e = [new Promise((res) => setTimeout(() => res(100), 100))]; + const t = { test: 2 }; + await asyncForEach(e, async function (e) { + assertEquals(this, t); + await e; + }, t); +});