diff --git a/v2/manager.go b/v2/manager.go index b56989bb..d44f1818 100644 --- a/v2/manager.go +++ b/v2/manager.go @@ -560,6 +560,23 @@ func (c *Manager) freeze(path string, state State) error { } } +func (c *Manager) isCgroupEmpty() bool { + // In case of any error we return true so that we exit and don't leak resources + out := make(map[string]interface{}) + err := readKVStatsFile(c.path, "cgroup.events", out) + if err != nil { + return true + } + if v, ok := out["populated"]; ok { + populated, ok := v.(uint64) + if !ok { + return true + } + return populated == 0 + } + return true +} + // MemoryEventFD returns inotify file descriptor and 'memory.events' inotify watch descriptor func (c *Manager) MemoryEventFD() (int, uint32, error) { fpath := filepath.Join(c.path, "memory.events") @@ -568,9 +585,16 @@ func (c *Manager) MemoryEventFD() (int, uint32, error) { return 0, 0, errors.New("failed to create inotify fd") } wd, err := syscall.InotifyAddWatch(fd, fpath, unix.IN_MODIFY) - if wd < 0 { + if err != nil { syscall.Close(fd) - return 0, 0, fmt.Errorf("failed to add inotify watch for %q", fpath) + return 0, 0, fmt.Errorf("failed to add inotify watch for %q: %w", fpath, err) + } + // monitor to detect process exit/cgroup deletion + evpath := filepath.Join(c.path, "cgroup.events") + _, err = syscall.InotifyAddWatch(fd, evpath, unix.IN_MODIFY) + if err != nil { + syscall.Close(fd) + return 0, 0, fmt.Errorf("failed to add inotify watch for %q: %w", evpath, err) } return fd, uint32(wd), nil @@ -578,22 +602,21 @@ func (c *Manager) MemoryEventFD() (int, uint32, error) { func (c *Manager) EventChan() (<-chan Event, <-chan error) { ec := make(chan Event) - errCh := make(chan error) + errCh := make(chan error, 1) go c.waitForEvents(ec, errCh) - return ec, nil + return ec, errCh } func (c *Manager) waitForEvents(ec chan<- Event, errCh chan<- error) { - fd, wd, err := c.MemoryEventFD() - - defer syscall.InotifyRmWatch(fd, wd) - defer syscall.Close(fd) + defer close(errCh) + fd, _, err := c.MemoryEventFD() if err != nil { errCh <- err return } + defer syscall.Close(fd) for { buffer := make([]byte, syscall.SizeofInotifyEvent*10) @@ -643,7 +666,14 @@ func (c *Manager) waitForEvents(ec chan<- Event, errCh chan<- error) { } ec <- e } else { - errCh <- err + // When cgroup is deleted read may return -ENODEV instead of -ENOENT from open + _, statErr := os.Lstat(filepath.Join(c.path, "memory.events")) + if !os.IsNotExist(statErr) { + errCh <- err + } + return + } + if c.isCgroupEmpty() { return } }