-
Notifications
You must be signed in to change notification settings - Fork 145
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 monitor stops with FileSystemException when copying a large directory structure into the watched folder #193
Comments
I don't have access to a Windows system. Can you give me the full stack trace? The stack trace you posted does not help me figure out from where in better-files this was caught. Thank you for reporting. |
|
Thanks for the stack trace. Looks like Windows does not really like walking the directory when it is being unzipped into. Can you try this: val yourMonitor = new FileMonitor {
// Your current code
override def onCreate(file: File, count: Int) = ...
override def onModify(file: File, count: Int) = ...
override def onDelete(file: File, count: Int) = ...
override def onUnknownEvent(event: WatchEvent[_], count: Int) = ...
override def onException(exception: Throwable) = ...
// Modify the library
override def watch(file: File, depth: Int): Unit = {
def toWatch: Files = if (file.isDirectory) {
file.walk(depth).filter(f => f.isDirectory && f.exists)
} else {
when(file.exists)(file.parent).iterator // There is no way to watch a regular file; so watch its parent instead
}
toWatch.foreach(f => Try[Unit](f.register(service)).recover(PartialFunction(onException)).get)
}
} If that still does not work, try this: override def watch(file: File, depth: Int): Unit = {
val toWatch: Files = if (file.isDirectory) {
file.walk(depth).filter(f => f.isDirectory && f.exists)
} else {
when(file.exists)(file.parent).iterator // There is no way to watch a regular file; so watch its parent instead
}
try {
toWatch.foreach(f => Try[Unit](f.register(service)).recover(PartialFunction(onException)).get)
} catch {
case NonFatal(e) => onException(e)
}
} |
The last version seems to work. The watcher doesn't stop watching and the watcher notifies also about files for witch an exception was thrown:
|
@pathikrit I think this issue isn't really fixable on windows. Maybe the problem is also that my notebook disk is to slow. So if I copy a large directory structure in a watched directory, the copy process locks the files and directories during creation. But at the same time, the watcher tries to register a watcher for these newly created files and directories. Because of the fact the directories are locked, the watcher fails with an exception and now it isn't able to register a watcher for all inner files and directories. Even if you try to wait for the lock to release, the watcher can then register itself for the inner files and directories, but the listener will not be notified for the creation event, because the files and directories were created already. Anyway, I think the current implementation isn't correct, because it stops watching if an exception occurs. Instead the watcher should wait for the lock to release and then try to register the watcher again. With this implementation I get the best results on my system: override def watch(file: File, depth: Int): Unit = {
val toWatch: List[File] = if (file.isDirectory) {
import scala.collection.JavaConverters._
FileUtils.iterateFilesAndDirs(file.toJava, FalseFileFilter.FALSE, TrueFileFilter.TRUE)
.asScala
.toList
.filter(_.exists())
.map(f => File(f.toPath))
} else {
when(file.exists)(file.parent).toList
}
def iterate(files: List[File], tries: Int = 0, maxTries: Int = 1000, sleep: Long = 1L): Unit = {
files match {
case Nil => ()
case h :: t =>
try {
h.register(service)
} catch {
case NonFatal(_) if tries < maxTries && h.isWriteLocked() =>
Future {
Thread.sleep(sleep)
iterate(List(h), tries + 1, maxTries, sleep)
}
case NonFatal(e) => onException(e)
} finally {
iterate(t, 0, maxTries, sleep)
}
}
}
iterate(toWatch)
} I use With the current settings |
@pathikrit Please see gmethvin/directory-watcher#2 and gmethvin/directory-watcher#1 (comment) Maybe you could use https://github.com/gmethvin/directory-watcher to fix also #139 |
I like to keep better-files dependency free but if @gmethvin would like to port that to Scala and better-files, would love to have it in :) |
@pathikrit The FILE_TREE part of it could be ported potentially but I'm not sure if you're comfortable with the JNA dependency for the OS X implementation. Another option is to have a way to plug in alternate implementations for the file monitor in better-files, so I could create a library that provides the custom implementation. Regarding this bug, the real issue is that there's generally going to be a delay between the time a new directory is created and when we can register a listener on it. We'll miss any events that happen during the time we weren't listening. I don't think there's a foolproof way to eliminate that race condition without hooking into low-level OS functionality. |
@gmethvin I am fine with JNA dependency in better-files. Alternatively, yes, there is a pluggable interface for monitoring - see the better-files/core/src/main/scala/better/files/File.scala Lines 1261 to 1289 in 176d13c
|
@pathikrit Yes, I'm already using it here: https://github.com/gmethvin/directory-watcher/#better-files-integration-scala. I think the |
If I copy a large directory structure (the unzipped jmeter package as example) into a watched folder, then the
FileMonitor
stops with ajava.nio.file.FileSystemException
:I've tested this on Windows 7 with the following code:
The text was updated successfully, but these errors were encountered: