Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

avoid deadlocks on close. Fixes #187 #188

Closed
wants to merge 1 commit into from
Closed

avoid deadlocks on close. Fixes #187 #188

wants to merge 1 commit into from

Conversation

glycerine
Copy link

Raw channel work (not inside a select) is subject to deadlock on shutdown.

Hence Close() can deadlock.

This is already fixed in the linux inotify.go code.

Fixes #187

@glycerine
Copy link
Author

probably addresses #145 as well.

@glycerine
Copy link
Author

confirmed. this patch makes test in #145 pass.

if index > 10 {
return
}
index += 1

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should replace index += 1 with index++

select {
case event := <-watcher.Events:
log.Println("event:", event)
notifications += 1

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should replace notifications += 1 with notifications++

@glycerine
Copy link
Author

Ping.

Please merge this.

raw channel work (not inside a select) should
always be prohibited in production code, as
it readily causes deadlocks on shutdown.

Also adds the test TestWatcherClose from #145.
This request duplicates that test, with two
lines fixed to address the houndcli-bot review
concerns.

Fixes #187
Fixes #145
Copy link
Contributor

@nhooyr nhooyr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@glycerine I'd like to get this merged ASAP as its a serious bug. If it's cool with you may I take over?

@nathany I believe this covers #211 and #145 so after this is merged, those should probably be closed.

if err != nil {
select {
case w.Errors <- err:
case <-w.done:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

given the only way this function can return is if w.done is closed, I think this should instead be the default case.


func consumeEvent(t *testing.T, watcher *Watcher, wg *sync.WaitGroup) {
defer func() {
//time.Sleep(time.Second)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

debugging line should be removed.

func TestWatcherClose(t *testing.T) {
dir, err := ioutil.TempDir("./", "example")
if err != nil {
panic(err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this panic and the others should be t.Fatal

if err != nil {
panic(err)
}
ioutil.TempFile(dir, "example")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extraneous line.

if err != nil {
panic(err)
}
_, err = ioutil.TempFile(dir, "example")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unnecessary line.

watcher.Close()
wg.Done()
}()
log.Println("Came here")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

debugging line.

maxNotifications := 1
for {
if notifications >= maxNotifications {
log.Println("Reached max notifications")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

debugging line.

}
select {
case event := <-watcher.Events:
log.Println("event:", event)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should use t.Log.

index++
}
}
func TestWatcherClose(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this test could be made much simpler. We just need the watcher to be listening on some file, modify the file and then close the watcher without listening on either the errors or events channels.

// Send "quit" message to the reader goroutine:
w.done <- true
// send a "quit" message to the reader goroutine
close(w.done)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should change w.done to be a chan struct{}.

@glycerine
Copy link
Author

@glycerine I'd like to get this merged ASAP as its a serious bug. If it's cool with you may I take over?

by all means.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

osx kqueue.go deadlocks on Close
4 participants