actors.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. package run
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "os"
  7. "os/signal"
  8. )
  9. // ContextHandler returns an actor, i.e. an execute and interrupt func, that
  10. // terminates when the provided context is canceled.
  11. func ContextHandler(ctx context.Context) (execute func() error, interrupt func(error)) {
  12. ctx, cancel := context.WithCancel(ctx)
  13. return func() error {
  14. <-ctx.Done()
  15. return ctx.Err()
  16. }, func(error) {
  17. cancel()
  18. }
  19. }
  20. // SignalHandler returns an actor, i.e. an execute and interrupt func, that
  21. // terminates with ErrSignal when the process receives one of the provided
  22. // signals, or with ctx.Error() when the parent context is canceled. If no
  23. // signals are provided, the actor will terminate on any signal, per
  24. // [signal.Notify].
  25. func SignalHandler(ctx context.Context, signals ...os.Signal) (execute func() error, interrupt func(error)) {
  26. ctx, cancel := context.WithCancel(ctx)
  27. return func() error {
  28. testc := getTestSigChan(ctx)
  29. sigc := make(chan os.Signal, 1)
  30. signal.Notify(sigc, signals...)
  31. defer signal.Stop(sigc)
  32. select {
  33. case sig := <-testc:
  34. return &SignalError{Signal: sig}
  35. case sig := <-sigc:
  36. return &SignalError{Signal: sig}
  37. case <-ctx.Done():
  38. return ctx.Err()
  39. }
  40. }, func(error) {
  41. cancel()
  42. }
  43. }
  44. type testSigChanKey struct{}
  45. func getTestSigChan(ctx context.Context) <-chan os.Signal {
  46. c, _ := ctx.Value(testSigChanKey{}).(<-chan os.Signal) // can be nil
  47. return c
  48. }
  49. func putTestSigChan(ctx context.Context, c <-chan os.Signal) context.Context {
  50. return context.WithValue(ctx, testSigChanKey{}, c)
  51. }
  52. // SignalError is returned by the signal handler's execute function when it
  53. // terminates due to a received signal.
  54. //
  55. // SignalError has a design error that impacts comparison with errors.As.
  56. // Callers should prefer using errors.Is(err, ErrSignal) to check for signal
  57. // errors, and should only use errors.As in the rare case that they need to
  58. // program against the specific os.Signal value.
  59. type SignalError struct {
  60. Signal os.Signal
  61. }
  62. // Error implements the error interface.
  63. //
  64. // It was a design error to define this method on a value receiver rather than a
  65. // pointer receiver. For compatibility reasons it won't be changed.
  66. func (e SignalError) Error() string {
  67. return fmt.Sprintf("received signal %s", e.Signal)
  68. }
  69. // Is addresses a design error in the SignalError type, so that errors.Is with
  70. // ErrSignal will return true.
  71. func (e SignalError) Is(err error) bool {
  72. return errors.Is(err, ErrSignal)
  73. }
  74. // As fixes a design error in the SignalError type, so that errors.As with the
  75. // literal `&SignalError{}` will return true.
  76. func (e SignalError) As(target interface{}) bool {
  77. switch target.(type) {
  78. case *SignalError, SignalError:
  79. return true
  80. default:
  81. return false
  82. }
  83. }
  84. // ErrSignal is returned by SignalHandler when a signal triggers termination.
  85. var ErrSignal = errors.New("signal error")