Source file src/runtime/exithook.go

     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  

View as plain text