func (ts *FileLockTestSuite) TestWithLockSimple(c *C) { called := false path := filepath.Join(c.MkDir(), "lock") err := lockfile.WithLock(path, func() error { called = true return nil }) c.Assert(err, IsNil) c.Assert(called, Equals, true) }
func (ts *FileLockTestSuite) TestWithLockErrOnLockHeld(c *C) { var err, err1, err2 error var callCount int slowFunc := func() error { time.Sleep(time.Millisecond * 100) callCount++ return nil } path := filepath.Join(c.MkDir(), "lock") ch := make(chan bool) go func() { err1 = lockfile.WithLock(path, slowFunc) ch <- true }() err2 = lockfile.WithLock(path, slowFunc) // wait for the goroutine <-ch // find which err is set (depends on the order in which go // runs the goroutine) if err1 != nil { err = err1 } else { err = err2 } // only one of the functions errored c.Assert(err1 != nil && err2 != nil, Equals, false) // the other returned a proper error c.Assert(err, Equals, lockfile.ErrAlreadyLocked) // and we did not call it too often c.Assert(callCount, Equals, 1) }
// withMutexAndRetry runs the given function with a filelock mutex and provides // automatic re-try and helpful messages if the lock is already taken func withMutexAndRetry(f func() error) error { if sys.Getuid() != 0 { return snappy.ErrNeedRoot } for { err := lockfile.WithLock(dirs.SnapLockFile, f) // if already locked, auto-retry if err == lockfile.ErrAlreadyLocked { var msg string if isAutoUpdateRunning() { // FIXME: we could even do a // journalctl -u snappy-autopilot // here // TRANSLATORS: please keep each line under 80 characters. msg = i18n.G( `Snappy is updating your system in the background. This may take some minutes. Will try again in %d seconds... Press ctrl-c to cancel. `) } else { msg = i18n.G( `Another snappy is running, will try again in %d seconds... Press ctrl-c to cancel. `) } // wait a wee bit wait := 5 fmt.Printf(msg, wait) time.Sleep(time.Duration(wait) * time.Second) continue } return err } }