gopkg.in/openshift/source-to-image.v1@v1.2.0/pkg/util/interrupt/interrupt.go (about) 1 /* 2 Derived from: 3 4 https://github.com/kubernetes/kubernetes 5 6 Copyright 2016 The Kubernetes Authors All rights reserved. 7 8 Licensed under the Apache License, Version 2.0 (the "License"); 9 you may not use this file except in compliance with the License. 10 You may obtain a copy of the License at 11 12 http://www.apache.org/licenses/LICENSE-2.0 13 14 Unless required by applicable law or agreed to in writing, software 15 distributed under the License is distributed on an "AS IS" BASIS, 16 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 See the License for the specific language governing permissions and 18 limitations under the License. 19 */ 20 21 package interrupt 22 23 import ( 24 "os" 25 "os/signal" 26 "sync" 27 "syscall" 28 ) 29 30 // terminationSignals are signals that cause the program to exit in the 31 // supported platforms (linux, darwin, windows). 32 var terminationSignals = []os.Signal{syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT} 33 34 // A Handler executes notification functions after a critical section (the 35 // function passed to its Run method), even in the presence of process 36 // termination via a termination signal. A final handler is executed if a 37 // termination signal is caught. The handler guarantees that the provided notify 38 // functions will be called at most once. A Handler is not reusable in the sense 39 // that its Run method should be called only once: calling it more times will 40 // not execute any of notify nor final functions. 41 type Handler struct { 42 notify []func() 43 final func(os.Signal) 44 once sync.Once 45 } 46 47 // New creates a new Handler. The final function will be called only if a 48 // termination signal is caught, after all notify functions are run. If final is 49 // nil, it defaults to os.Exit(2). The final function may call os.Exit if 50 // exiting is desired. The notify functions will be called when a termination 51 // signal is caught, or after the argument to the Run method completes. 52 func New(final func(os.Signal), notify ...func()) *Handler { 53 return &Handler{ 54 final: final, 55 notify: notify, 56 } 57 } 58 59 // Run calls fn in the current goroutine, while waiting for a termination signal 60 // in a separate goroutine. If a signal is caught while this method is running, 61 // the notify functions will be called sequentially in order, and then the final 62 // function is called. Otherwise, only the notify functions are called after fn 63 // returns. The fn function may not complete, for example in case of a call to 64 // os.Exit. 65 func (h *Handler) Run(fn func() error) error { 66 ch := make(chan os.Signal, 1) 67 signal.Notify(ch, terminationSignals...) 68 defer close(ch) 69 defer signal.Stop(ch) 70 go func() { 71 sig, ok := <-ch 72 if !ok { 73 return 74 } 75 h.signal(sig) 76 }() 77 defer h.close() 78 return fn() 79 } 80 81 // close calls the notify functions, used when no signal was caught and the Run 82 // method returned. 83 func (h *Handler) close() { 84 h.once.Do(func() { 85 for _, fn := range h.notify { 86 fn() 87 } 88 }) 89 } 90 91 // signal calls the notify functions and final, used when a signal was caught 92 // while the Run method was running. If final is nil, os.Exit will be called as 93 // a default. 94 func (h *Handler) signal(s os.Signal) { 95 h.once.Do(func() { 96 for _, fn := range h.notify { 97 fn() 98 } 99 if h.final == nil { 100 os.Exit(2) 101 } 102 h.final(s) 103 }) 104 }