File size: 1,631 Bytes
98c9143
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
type CleanupHandler = () => void | Promise<void>

const cleanupHandlers = new Set<CleanupHandler>()

let cleanupPromise: Promise<void> | null = null
let cleanupState: "idle" | "running" | "done" = "idle"
let runtimeInitialized = false

function initializeProcessCleanupRuntime(): void {
  if (runtimeInitialized) {
    return
  }

  runtimeInitialized = true

  process.once("beforeExit", () => {
    void runProcessCleanups()
  })
  process.once("exit", runProcessCleanupsSync)
  process.once("SIGINT", () => {
    void shutdownProcess(0)
  })
  process.once("SIGTERM", () => {
    void shutdownProcess(0)
  })
}

function runProcessCleanupsSync(): void {
  if (cleanupState !== "idle") {
    return
  }

  cleanupState = "done"
  for (const handler of Array.from(cleanupHandlers)) {
    try {
      void handler()
    } catch {
      // Ignore best-effort cleanup failures during process exit.
    }
  }
}

async function runProcessCleanups(): Promise<void> {
  if (cleanupPromise) {
    return cleanupPromise
  }

  if (cleanupState === "done") {
    return
  }

  cleanupState = "running"
  cleanupPromise = (async () => {
    for (const handler of Array.from(cleanupHandlers)) {
      await handler()
    }
    cleanupState = "done"
  })()

  return cleanupPromise
}

async function shutdownProcess(exitCode: number): Promise<void> {
  try {
    await runProcessCleanups()
  } finally {
    process.exit(exitCode)
  }
}

export function registerProcessCleanup(handler: CleanupHandler): () => void {
  initializeProcessCleanupRuntime()
  cleanupHandlers.add(handler)

  return () => {
    cleanupHandlers.delete(handler)
  }
}