atexit.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. // Copyright 2019 Yunion
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package atexit
  15. import (
  16. "context"
  17. "os"
  18. "runtime/debug"
  19. "sort"
  20. "sync"
  21. "yunion.io/x/pkg/errors"
  22. "yunion.io/x/pkg/util/version"
  23. "yunion.io/x/onecloud/pkg/mcclient/modules/yunionconf"
  24. )
  25. // ExitHandlerFunc is the type of handler func
  26. type ExitHandlerFunc func(ExitHandler)
  27. // ExitHandler defines the spec of handler
  28. //
  29. // # Reason and Func are mandatory and must not be empty or nil
  30. //
  31. // Handlers with smaller Prio will be executed earlier than those with bigger
  32. // Prio at exit time. Handler func will receive a copy of the ExitHandler
  33. // struct previously registered
  34. type ExitHandler struct {
  35. Prio Prio
  36. Reason string
  37. Func ExitHandlerFunc
  38. Value interface{}
  39. }
  40. var (
  41. handlers = map[Prio][]ExitHandler{}
  42. handlersLock = &sync.Mutex{}
  43. once = &sync.Once{}
  44. )
  45. // Register registers ExitHandler
  46. //
  47. // Smaller prio number mean higher priority and exit handlers with higher
  48. // priority will be executed first. For handlers with equal priorities, those
  49. // registered first will be executed earlier at exit time
  50. func Register(eh ExitHandler) {
  51. if eh.Reason == "" {
  52. panic("handler reason must not be empty")
  53. }
  54. if eh.Func == nil {
  55. panic("handler func must not be nil")
  56. }
  57. handlersLock.Lock()
  58. defer handlersLock.Unlock()
  59. ehs, ok := handlers[eh.Prio]
  60. if ok {
  61. ehs = append(ehs, eh)
  62. } else {
  63. ehs = []ExitHandler{eh}
  64. }
  65. handlers[eh.Prio] = ehs
  66. }
  67. // Handle calls registered handlers sequentially according to priority and
  68. // registration order
  69. //
  70. // Panics caused by handler func will be caught, recorded, then next func will
  71. // be run
  72. func Handle() {
  73. once.Do(func() {
  74. handlersLock.Lock()
  75. defer handlersLock.Unlock()
  76. ints := make([]int, 0, len(handlers))
  77. for prio := range handlers {
  78. ints = append(ints, int(prio))
  79. }
  80. sort.Ints(ints)
  81. for _, i := range ints {
  82. prio := Prio(i)
  83. ehs := handlers[prio]
  84. for _, eh := range ehs {
  85. print("atexit: prio=", prio, ", reason=", eh.Reason, "\n")
  86. func() {
  87. defer func() {
  88. val := recover()
  89. if val != nil {
  90. print("panic ", val, "\n")
  91. debug.PrintStack()
  92. yunionconf.BugReport.SendBugReport(context.Background(), version.GetShortString(), string(debug.Stack()), errors.Errorf("%s", val))
  93. }
  94. }()
  95. eh.Func(eh)
  96. }()
  97. }
  98. }
  99. })
  100. }
  101. // Exit calls handlers then does os.Exit(code)
  102. func Exit(code int) {
  103. defer os.Exit(code)
  104. Handle()
  105. }