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

File modification events are getting lost in PollingWatchService #86

Closed
mechkg opened this issue Nov 28, 2017 · 4 comments
Closed

File modification events are getting lost in PollingWatchService #86

mechkg opened this issue Nov 28, 2017 · 4 comments
Assignees
Labels

Comments

@mechkg
Copy link
Contributor

mechkg commented Nov 28, 2017

While investigating sbt/sbt#3775 I think I have narrowed the bug down to the interaction between PollingWatchService.pollEvents and PollingWatchService.PollingThread.addEvent.

Events often get lost without being properly consumed (#82 is probably related).

UPDATE: the conclusion below is wrong, see comments further down.

As far as I understand the only reason that could happen is if some events get added between these two statements:

val events = thread.keysWithEvents.map { k =>
k -> k.pollEvents().asScala.asInstanceOf[Seq[WatchEvent[JPath]]]
}
thread.keysWithEvents.clear()

so thread.keysWithEvents.clear deletes events that weren't consumed. This should be prevented by the synchronized guard.

It is difficult to set up my own modified sbt build to properly test this but at a glance I find this line suspicious:

keysWithEvents.notifyAll()

I don't understand the purpose of the notifyAll call at this point and I suspect that it could break the intended synchronization behaviour.

@dwijnand
Copy link
Member

Thank you for the insight @mechkg, I'll look into what you've found (it works well with sbt/sbt#3687, sbt/sbt#3695, #82, and sbt/sbt#3775 which I'm look at).

@mechkg mechkg changed the title Severe race condition in PollingWatchService File modification events are getting lost in PollingWatchService Nov 28, 2017
@mechkg
Copy link
Contributor Author

mechkg commented Nov 29, 2017

In PollingWatchService.pollEvents events are gathered as follows:

   val events = thread.keysWithEvents.map { k =>
        k -> k.pollEvents().asScala.asInstanceOf[Seq[WatchEvent[JPath]]]
      }

PollingWatchKey.pollEvents in turn does this:

  origin.keysWithEvents -= this

So keysWithEvents is being modified by PollingWatchKey.pollEvents at the same time as it's being mapped over in PollingWatchService.pollEvents which looks very dangerous. Usually in Java this would result in a ConcurrentModificationException but it does not in this case, I wonder why?

@mechkg
Copy link
Contributor Author

mechkg commented Nov 29, 2017

Simply removing this line:

 origin.keysWithEvents -= this

apparently makes the problem go away. It looks like it is not necessary because keysWithEvents is getting cleared at the end of PollingWatchService.pollEvents anyway, but I am not sure if any other code depends on this behaviour?

@dwijnand
Copy link
Member

dwijnand commented Dec 1, 2017

Fixed with #90.

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

No branches or pull requests

2 participants