Avoid possible livelock when stopping FileSystemWatcher in parallel

Previously, if the file watcher thread tried to stop the
FileSystemWatcher when another thread was already stopping it a
livelock could occur. The livelock occurred because the file watcher
thread would attempt to lock a monitor that was being held by a thread
that had joined the file watcher thread and was waiting for it to die.

This commit avoid the livelock by narrowing the synchronization that's
used when stopping the FileSystemWatcher. The monitor is used to
obtain a reference to the file watcher thread in a thread-safe manner,
but it is released prior to joining the file watcher thread and
waiting for it to die. This will allow a parallel attempt by the
file watcher thread to stop itself to succeed.

Closes gh-10496
This commit is contained in:
Andy Wilkinson 2017-10-02 20:03:17 +01:00
parent 205c25bf0f
commit 71c15cb65e

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2016 the original author or authors.
* Copyright 2012-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -183,22 +183,23 @@ public class FileSystemWatcher {
* @param remainingScans the number of remaining scans
*/
void stopAfter(int remainingScans) {
Thread thread = null;
synchronized (this.monitor) {
Thread thread = this.watchThread;
thread = this.watchThread;
if (thread != null) {
this.remainingScans.set(remainingScans);
if (remainingScans <= 0) {
thread.interrupt();
}
if (Thread.currentThread() != thread) {
try {
thread.join();
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
this.watchThread = null;
}
this.watchThread = null;
}
if (Thread.currentThread() != thread) {
try {
thread.join();
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}