1 // Copyright 2022 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package runtime 6 7 // addExitHook registers the specified function 'f' to be run at 8 // program termination (e.g. when someone invokes os.Exit(), or when 9 // main.main returns). Hooks are run in reverse order of registration: 10 // first hook added is the last one run. 11 // 12 // CAREFUL: the expectation is that addExitHook should only be called 13 // from a safe context (e.g. not an error/panic path or signal 14 // handler, preemption enabled, allocation allowed, write barriers 15 // allowed, etc), and that the exit function 'f' will be invoked under 16 // similar circumstances. That is the say, we are expecting that 'f' 17 // uses normal / high-level Go code as opposed to one of the more 18 // restricted dialects used for the trickier parts of the runtime. 19 func addExitHook(f func(), runOnNonZeroExit bool) { 20 exitHooks.hooks = append(exitHooks.hooks, exitHook{f: f, runOnNonZeroExit: runOnNonZeroExit}) 21 } 22 23 // exitHook stores a function to be run on program exit, registered 24 // by the utility runtime.addExitHook. 25 type exitHook struct { 26 f func() // func to run 27 runOnNonZeroExit bool // whether to run on non-zero exit code 28 } 29 30 // exitHooks stores state related to hook functions registered to 31 // run when program execution terminates. 32 var exitHooks struct { 33 hooks []exitHook 34 runningExitHooks bool 35 } 36 37 // runExitHooks runs any registered exit hook functions (funcs 38 // previously registered using runtime.addExitHook). Here 'exitCode' 39 // is the status code being passed to os.Exit, or zero if the program 40 // is terminating normally without calling os.Exit. 41 func runExitHooks(exitCode int) { 42 if exitHooks.runningExitHooks { 43 throw("internal error: exit hook invoked exit") 44 } 45 exitHooks.runningExitHooks = true 46 47 runExitHook := func(f func()) (caughtPanic bool) { 48 defer func() { 49 if x := recover(); x != nil { 50 caughtPanic = true 51 } 52 }() 53 f() 54 return 55 } 56 57 finishPageTrace() 58 for i := range exitHooks.hooks { 59 h := exitHooks.hooks[len(exitHooks.hooks)-i-1] 60 if exitCode != 0 && !h.runOnNonZeroExit { 61 continue 62 } 63 if caughtPanic := runExitHook(h.f); caughtPanic { 64 throw("internal error: exit hook invoked panic") 65 } 66 } 67 exitHooks.hooks = nil 68 exitHooks.runningExitHooks = false 69 } 70