Exemplo n.º 1
0
func (s *fslockSuite) TestTomb(c *gc.C) {
	const timeToDie = 200 * time.Millisecond
	die := tomb.Tomb{}

	dir := c.MkDir()
	lock, err := fslock.NewLock(dir, "testing", s.lockConfig)
	c.Assert(err, gc.IsNil)
	// Just use one lock, and try to lock it twice.
	err = lock.Lock("very busy")
	c.Assert(err, gc.IsNil)

	checkTomb := func() error {
		select {
		case <-die.Dying():
			return tomb.ErrDying
		default:
			// no-op to fall through to return.
		}
		return nil
	}

	go func() {
		time.Sleep(timeToDie)
		die.Killf("time to die")
	}()

	err = lock.LockWithFunc("won't happen", checkTomb)
	c.Assert(errors.Cause(err), gc.Equals, tomb.ErrDying)
	msg, err := lock.Message()
	c.Assert(err, gc.IsNil)
	c.Assert(msg, gc.Equals, "very busy")
}
Exemplo n.º 2
0
func (fw *InotifyFileWatcher) BlockUntilExists(t tomb.Tomb) error {
	w, err := fsnotify.NewWatcher()
	if err != nil {
		return err
	}
	defer w.Close()

	dirname := filepath.Dir(fw.Filename)

	// Watch for new files to be created in the parent directory.
	err = w.WatchFlags(dirname, fsnotify.FSN_CREATE)
	if err != nil {
		return err
	}
	defer w.RemoveWatch(filepath.Dir(fw.Filename))

	// Do a real check now as the file might have been created before
	// calling `WatchFlags` above.
	if _, err = os.Stat(fw.Filename); !os.IsNotExist(err) {
		// file exists, or stat returned an error.
		return err
	}

	for {
		select {
		case evt := <-w.Event:
			if evt.Name == fw.Filename {
				return nil
			}
		case <-t.Dying():
			return tomb.ErrDying
		}
	}
	panic("unreachable")
}
Exemplo n.º 3
0
Arquivo: inotify.go Projeto: srid/tail
func (fw *InotifyFileWatcher) ChangeEvents(t tomb.Tomb, fi os.FileInfo) chan bool {
	w, err := fsnotify.NewWatcher()
	if err != nil {
		panic(err)
	}
	err = w.Watch(fw.Filename)
	if err != nil {
		panic(err)
	}

	ch := make(chan bool)

	fw.Size = fi.Size()

	go func() {
		defer w.Close()
		defer w.RemoveWatch(fw.Filename)
		defer close(ch)

		for {
			prevSize := fw.Size

			var evt *fsnotify.FileEvent

			select {
			case evt = <-w.Event:
			case <-t.Dying():
				return
			}

			switch {
			case evt.IsDelete():
				fallthrough

			case evt.IsRename():
				return

			case evt.IsModify():
				fi, err := os.Stat(fw.Filename)
				if err != nil {
					// XXX: no panic here
					panic(err)
				}
				fw.Size = fi.Size()

				if prevSize > 0 && prevSize > fw.Size {
					return
				}

				// send only if channel is empty.
				select {
				case ch <- true:
				default:
				}
			}
		}
	}()

	return ch
}
Exemplo n.º 4
0
func (fw *InotifyFileWatcher) ChangeEvents(t tomb.Tomb, fi os.FileInfo) *FileChanges {
	changes := NewFileChanges()

	w, err := fsnotify.NewWatcher()
	if err != nil {
		panic(err)
	}
	err = w.Watch(fw.Filename)
	if err != nil {
		panic(err)
	}

	fw.Size = fi.Size()

	go func() {
		defer w.Close()
		defer w.RemoveWatch(fw.Filename)
		defer changes.Close()

		for {
			prevSize := fw.Size

			var evt *fsnotify.FileEvent

			select {
			case evt = <-w.Event:
			case <-t.Dying():
				return
			}

			switch {
			case evt.IsDelete():
				fallthrough

			case evt.IsRename():
				changes.NotifyDeleted()
				return

			case evt.IsModify():
				fi, err := os.Stat(fw.Filename)
				if err != nil {
					// XXX: no panic here
					panic(err)
				}
				fw.Size = fi.Size()

				if prevSize > 0 && prevSize > fw.Size {
					changes.NotifyTruncated()
				} else {
					changes.NotifyModified()
				}
				prevSize = fw.Size
			}
		}
	}()

	return changes
}
Exemplo n.º 5
0
func (fw *PollingFileWatcher) ChangeEvents(t *tomb.Tomb, origFi os.FileInfo) *FileChanges {
	changes := NewFileChanges()
	var prevModTime time.Time

	// XXX: use tomb.Tomb to cleanly manage these goroutines. replace
	// the fatal (below) with tomb's Kill.

	fw.Size = origFi.Size()

	go func() {
		defer changes.Close()

		prevSize := fw.Size
		for {
			select {
			case <-t.Dying():
				return
			default:
			}

			time.Sleep(POLL_DURATION)
			fi, err := os.Stat(fw.Filename)
			if err != nil {
				if os.IsNotExist(err) {
					// File does not exist (has been deleted).
					changes.NotifyDeleted()
					return
				}
				// XXX: report this error back to the user
				util.Fatal("Failed to stat file %v: %v", fw.Filename, err)
			}

			// File got moved/renamed?
			if !os.SameFile(origFi, fi) {
				changes.NotifyDeleted()
				return
			}

			// File got truncated?
			fw.Size = fi.Size()
			if prevSize > 0 && prevSize > fw.Size {
				changes.NotifyTruncated()
				prevSize = fw.Size
				continue
			}
			prevSize = fw.Size

			// File was appended to (changed)?
			modTime := fi.ModTime()
			if modTime != prevModTime {
				prevModTime = modTime
				changes.NotifyModified()
			}
		}
	}()

	return changes
}
Exemplo n.º 6
0
// Stop stops the watcher. If an error is returned by the
// watcher, t is killed with the error.
func Stop(w Stopper, t *tomb.Tomb) {
	if err := w.Stop(); err != nil {
		if err != tomb.ErrStillAlive && err != tomb.ErrDying {
			// tomb.Kill() checks for the two errors above
			// by value, so we shouldn't wrap them, but we
			// wrap any other error.
			err = errors.Trace(err)
		}
		t.Kill(err)
	}
}
Exemplo n.º 7
0
Arquivo: polling.go Projeto: srid/tail
func (fw *PollingFileWatcher) BlockUntilExists(t tomb.Tomb) error {
	for {
		if _, err := os.Stat(fw.Filename); err == nil {
			return nil
		} else if !os.IsNotExist(err) {
			return err
		}
		select {
		case <-time.After(POLL_DURATION):
			continue
		case <-t.Dying():
			return tomb.ErrDying
		}
	}
	panic("unreachable")
}
Exemplo n.º 8
0
// delayedTomb returns a tomb that starts dying a given duration
// after t starts dying.
func delayedTomb(t *tomb.Tomb, d time.Duration) *tomb.Tomb {
	var delayed tomb.Tomb
	go func() {
		select {
		case <-t.Dying():
			time.Sleep(d)
			delayed.Kill(nil)
		case <-delayed.Dying():
			return
		}
	}()
	return &delayed
}
Exemplo n.º 9
0
// copyEvents copies channel events from "in" to "out", coalescing.
func copyEvents(out chan<- struct{}, in <-chan struct{}, tomb *tomb.Tomb) {
	var outC chan<- struct{}
	for {
		select {
		case <-tomb.Dying():
			return
		case _, ok := <-in:
			if !ok {
				return
			}
			outC = out
		case outC <- struct{}{}:
			outC = nil
		}
	}
}
Exemplo n.º 10
0
func testState(t *testing.T, tb *tomb.Tomb, wantDying, wantDead bool, wantErr error) {
	select {
	case <-tb.Dying():
		if !wantDying {
			t.Error("<-Dying: should block")
		}
	default:
		if wantDying {
			t.Error("<-Dying: should not block")
		}
	}
	seemsDead := false
	select {
	case <-tb.Dead():
		if !wantDead {
			t.Error("<-Dead: should block")
		}
		seemsDead = true
	default:
		if wantDead {
			t.Error("<-Dead: should not block")
		}
	}
	if err := tb.Err(); err != wantErr {
		t.Errorf("Err: want %#v, got %#v", wantErr, err)
	}
	if wantDead && seemsDead {
		waitErr := tb.Wait()
		switch {
		case waitErr == tomb.ErrStillAlive:
			t.Errorf("Wait should not return ErrStillAlive")
		case !reflect.DeepEqual(waitErr, wantErr):
			t.Errorf("Wait: want %#v, got %#v", wantErr, waitErr)
		}
	}
}
Exemplo n.º 11
0
// propagateTearDown tears down the handler, but ensures any error is
// propagated through the tomb's Kill method.
func propagateTearDown(handler tearDowner, t *tomb.Tomb) {
	if err := handler.TearDown(); err != nil {
		t.Kill(err)
	}
}
Exemplo n.º 12
0
// Stop stops the watcher. If an error is returned by the
// watcher, t is killed with the error.
func Stop(w Stopper, t *tomb.Tomb) {
	if err := w.Stop(); err != nil {
		t.Kill(err)
	}
}
Exemplo n.º 13
0
Arquivo: polling.go Projeto: srid/tail
func (fw *PollingFileWatcher) ChangeEvents(t tomb.Tomb, origFi os.FileInfo) chan bool {
	ch := make(chan bool)
	stop := make(chan bool)
	var once sync.Once
	var prevModTime time.Time

	// XXX: use tomb.Tomb to cleanly manage these goroutines. replace
	// the panic (below) with tomb's Kill.

	stopAndClose := func() {
		go func() {
			close(ch)
			stop <- true
		}()
	}

	fw.Size = origFi.Size()

	go func() {
		prevSize := fw.Size
		for {
			select {
			case <-stop:
				return
			case <-t.Dying():
				once.Do(stopAndClose)
				continue
			default:
			}

			time.Sleep(POLL_DURATION)
			fi, err := os.Stat(fw.Filename)
			if err != nil {
				if os.IsNotExist(err) {
					once.Do(stopAndClose)
					continue
				}
				/// XXX: do not panic here.
				panic(err)
			}

			// File got moved/rename within POLL_DURATION?
			if !os.SameFile(origFi, fi) {
				once.Do(stopAndClose)
				continue
			}

			// Was the file truncated?
			fw.Size = fi.Size()
			if prevSize > 0 && prevSize > fw.Size {
				once.Do(stopAndClose)
				continue
			}

			// If the file was changed since last check, notify.
			modTime := fi.ModTime()
			if modTime != prevModTime {
				prevModTime = modTime
				select {
				case ch <- true:
				default:
				}
			}
		}
	}()

	return ch
}
Exemplo n.º 14
0
func (fw *InotifyFileWatcher) ChangeEvents(t *tomb.Tomb, fi os.FileInfo) *FileChanges {
	changes := NewFileChanges()

	w, err := inotifyTracker.NewWatcher()
	if err != nil {
		util.Fatal("Error creating fsnotify watcher: %v", err)
	}
	err = w.Watch(fw.Filename)
	if err != nil {
		util.Fatal("Error watching %v: %v", fw.Filename, err)
	}

	fw.Size = fi.Size()

	go func() {
		defer inotifyTracker.CloseWatcher(w)
		defer changes.Close()

		for {
			prevSize := fw.Size

			var evt *fsnotify.FileEvent
			var ok bool

			select {
			case evt, ok = <-w.Event:
				if !ok {
					return
				}
			case <-t.Dying():
				return
			}

			switch {
			case evt.IsDelete():
				fallthrough

			case evt.IsRename():
				changes.NotifyDeleted()
				return

			case evt.IsModify():
				fi, err := os.Stat(fw.Filename)
				if err != nil {
					if os.IsNotExist(err) {
						changes.NotifyDeleted()
						return
					}
					// XXX: report this error back to the user
					util.Fatal("Failed to stat file %v: %v", fw.Filename, err)
				}
				fw.Size = fi.Size()

				if prevSize > 0 && prevSize > fw.Size {
					changes.NotifyTruncated()
				} else {
					changes.NotifyModified()
				}
				prevSize = fw.Size
			}
		}
	}()

	return changes
}
Exemplo n.º 15
0
// TearDown the handler, but ensure any error is propagated
func handlerTearDown(handler WatchHandler, t *tomb.Tomb) {
	if err := handler.TearDown(); err != nil {
		t.Kill(err)
	}
}