ActiveState/tail — pull request #18 by miraclesu (2013-09-23 07:23:20+00:00)
Title: Fixed stop hang & stop err

In the follow case, `Stop` will hang:

```
package main

import (
"fmt"
"github.com/ActiveState/tail"
"sync"
"time"
)

func main() {
t, err := tail.TailFile("/tmp/no.log", tail.Config{
Follow: true,
MustExist: false,
})
if err != nil {
fmt.Println(err)
return
}

CheckTick := time.Tick(2 * time.Second)
exit := make(chan bool)
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
for {
select {
case l := <-t.Lines:
fmt.Println(l)
case <-exit:
return
case <-CheckTick:
fmt.Println("hang on stop")
err := t.Stop()
fmt.Printf("stop done, err = %v\n", err)
t1, _ := tail.TailFile("/tmp/no1.log", tail.Config{
Follow: true,
MustExist: false,
})
t = t1
}
}
}()
time.Sleep(3 * time.Second)
exit <- true
wg.Wait()
fmt.Println("stop done. Exit now.")
}
```
note: the file will tail not exist but the dir exist.

In this case, [`BlockUntilExists`](https://github.com/ActiveState/tail/blob/master/watch/inotify.go#L48) will wait `Event` or `Dying()`, but the param is a struct, so the `tomb.Tomb` in this method is a copy value and [`Daing()`](http://bazaar.launchpad.net/~niemeyer/tomb/trunk/view/head:/tomb.go#L101) will call [`init()`](http://bazaar.launchpad.net/~niemeyer/tomb/trunk/view/head:/tomb.go#L82), it will make a *new dying channel* and this channel never closed. As we see, `BlockUntilExists` never return until the log file appear and `reopen` never done, so [`Done`](https://github.com/ActiveState/tail/blob/master/tail.go#L151) never called and `Stop` will hang.