reaper.go 1.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. package subreaper
  2. import (
  3. "os"
  4. "os/signal"
  5. "sync"
  6. "syscall"
  7. "github.com/Sirupsen/logrus"
  8. "github.com/docker/containerd/osutils"
  9. )
  10. var (
  11. subscriptions = map[int]*Subscription{}
  12. subLock = sync.Mutex{}
  13. counter = 0
  14. once = sync.Once{}
  15. )
  16. type Subscription struct {
  17. id int
  18. exit osutils.Exit
  19. c chan osutils.Exit
  20. wg sync.WaitGroup
  21. }
  22. func (s *Subscription) SetPid(pid int) {
  23. go func() {
  24. for exit := range s.c {
  25. if exit.Pid == pid {
  26. s.exit = exit
  27. s.wg.Done()
  28. Unsubscribe(s)
  29. break
  30. }
  31. }
  32. }()
  33. }
  34. func (s *Subscription) Wait() int {
  35. s.wg.Wait()
  36. return s.exit.Status
  37. }
  38. func Subscribe() *Subscription {
  39. subLock.Lock()
  40. defer subLock.Unlock()
  41. Start()
  42. counter++
  43. s := &Subscription{
  44. id: counter,
  45. c: make(chan osutils.Exit, 1024),
  46. }
  47. s.wg.Add(1)
  48. subscriptions[s.id] = s
  49. return s
  50. }
  51. func Unsubscribe(sub *Subscription) {
  52. subLock.Lock()
  53. defer subLock.Unlock()
  54. if _, ok := subscriptions[sub.id]; ok {
  55. close(sub.c)
  56. delete(subscriptions, sub.id)
  57. }
  58. }
  59. func Start() error {
  60. var err error
  61. once.Do(func() {
  62. err = osutils.SetSubreaper(1)
  63. if err != nil {
  64. return
  65. }
  66. s := make(chan os.Signal, 2048)
  67. signal.Notify(s, syscall.SIGCHLD)
  68. go childReaper(s)
  69. })
  70. return err
  71. }
  72. func childReaper(s chan os.Signal) {
  73. for range s {
  74. exits, err := osutils.Reap()
  75. if err == nil {
  76. notify(exits)
  77. } else {
  78. logrus.WithField("error", err).Warn("containerd: reap child processes")
  79. }
  80. }
  81. }
  82. func notify(exits []osutils.Exit) {
  83. subLock.Lock()
  84. for _, exit := range exits {
  85. for _, sub := range subscriptions {
  86. sub.c <- exit
  87. }
  88. }
  89. subLock.Unlock()
  90. }