|
|
@@ -1,23 +1,26 @@
|
|
|
+import { Log } from "@/util/log"
|
|
|
+
|
|
|
export namespace State {
|
|
|
interface Entry {
|
|
|
state: any
|
|
|
dispose?: (state: any) => Promise<void>
|
|
|
}
|
|
|
|
|
|
- const entries = new Map<string, Map<any, Entry>>()
|
|
|
+ const log = Log.create({ service: "state" })
|
|
|
+ const recordsByKey = new Map<string, Map<any, Entry>>()
|
|
|
|
|
|
export function create<S>(root: () => string, init: () => S, dispose?: (state: Awaited<S>) => Promise<void>) {
|
|
|
return () => {
|
|
|
const key = root()
|
|
|
- let collection = entries.get(key)
|
|
|
- if (!collection) {
|
|
|
- collection = new Map<string, Entry>()
|
|
|
- entries.set(key, collection)
|
|
|
+ let entries = recordsByKey.get(key)
|
|
|
+ if (!entries) {
|
|
|
+ entries = new Map<string, Entry>()
|
|
|
+ recordsByKey.set(key, entries)
|
|
|
}
|
|
|
- const exists = collection.get(init)
|
|
|
+ const exists = entries.get(init)
|
|
|
if (exists) return exists.state as S
|
|
|
const state = init()
|
|
|
- collection.set(init, {
|
|
|
+ entries.set(init, {
|
|
|
state,
|
|
|
dispose,
|
|
|
})
|
|
|
@@ -26,9 +29,38 @@ export namespace State {
|
|
|
}
|
|
|
|
|
|
export async function dispose(key: string) {
|
|
|
- for (const [_, entry] of entries.get(key)?.entries() ?? []) {
|
|
|
+ const entries = recordsByKey.get(key)
|
|
|
+ if (!entries) return
|
|
|
+
|
|
|
+ log.info("waiting for state disposal to complete", { key })
|
|
|
+
|
|
|
+ let disposalFinished = false
|
|
|
+
|
|
|
+ setTimeout(() => {
|
|
|
+ if (!disposalFinished) {
|
|
|
+ log.warn(
|
|
|
+ "state disposal is taking an unusually long time - if it does not complete in a reasonable time, please report this as a bug",
|
|
|
+ { key },
|
|
|
+ )
|
|
|
+ }
|
|
|
+ }, 10000).unref()
|
|
|
+
|
|
|
+ const tasks: Promise<void>[] = []
|
|
|
+ for (const entry of entries.values()) {
|
|
|
if (!entry.dispose) continue
|
|
|
- await entry.dispose(await entry.state)
|
|
|
+
|
|
|
+ const task = Promise.resolve(entry.state)
|
|
|
+ .then((state) => entry.dispose!(state))
|
|
|
+ .catch((error) => {
|
|
|
+ log.error("Error while disposing state:", { error, key })
|
|
|
+ })
|
|
|
+
|
|
|
+ tasks.push(task)
|
|
|
}
|
|
|
+
|
|
|
+ await Promise.all(tasks)
|
|
|
+
|
|
|
+ disposalFinished = true
|
|
|
+ log.info("state disposal completed", { key })
|
|
|
}
|
|
|
}
|