| author | wenzelm | 
| Fri, 10 Sep 2021 15:57:09 +0200 | |
| changeset 74284 | 8d1e27a23dd1 | 
| parent 74140 | 8a5e02ef975c | 
| child 75393 | 87ebf5a50283 | 
| permissions | -rw-r--r-- | 
| 64699 | 1 | /* Title: Pure/General/file_watcher.scala | 
| 2 | Author: Makarius | |
| 3 | ||
| 4 | Watcher for file-system events. | |
| 5 | */ | |
| 6 | ||
| 7 | package isabelle | |
| 8 | ||
| 9 | ||
| 73909 | 10 | import java.util.{List => JList}
 | 
| 64699 | 11 | import java.io.{File => JFile}
 | 
| 12 | import java.nio.file.FileSystems | |
| 13 | import java.nio.file.{WatchKey, WatchEvent, Path => JPath}
 | |
| 14 | import java.nio.file.StandardWatchEventKinds.{ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY}
 | |
| 15 | ||
| 73353 | 16 | import scala.jdk.CollectionConverters._ | 
| 64699 | 17 | |
| 18 | ||
| 64807 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 19 | class File_Watcher private[File_Watcher] // dummy template | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 20 | {
 | 
| 73340 | 21 |   def register(dir: JFile): Unit = {}
 | 
| 22 |   def register_parent(file: JFile): Unit = {}
 | |
| 23 |   def deregister(dir: JFile): Unit = {}
 | |
| 24 |   def purge(retain: Set[JFile]): Unit = {}
 | |
| 25 |   def shutdown(): Unit = {}
 | |
| 64807 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 26 | } | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 27 | |
| 64699 | 28 | object File_Watcher | 
| 29 | {
 | |
| 64858 | 30 | val none: File_Watcher = new File_Watcher | 
| 64859 | 31 |   {
 | 
| 32 | override def toString: String = "File_Watcher.none" | |
| 33 | } | |
| 64858 | 34 | |
| 64699 | 35 | def apply(handle: Set[JFile] => Unit, delay: => Time = Time.seconds(0.5)): File_Watcher = | 
| 64858 | 36 | if (Platform.is_windows) none else new Impl(handle, delay) | 
| 64699 | 37 | |
| 38 | ||
| 64807 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 39 | /* proper implementation */ | 
| 64699 | 40 | |
| 41 | sealed case class State( | |
| 42 | dirs: Map[JFile, WatchKey] = Map.empty, | |
| 43 | changed: Set[JFile] = Set.empty) | |
| 44 | ||
| 64807 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 45 | class Impl private[File_Watcher](handle: Set[JFile] => Unit, delay: Time) extends File_Watcher | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 46 |   {
 | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 47 | private val state = Synchronized(File_Watcher.State()) | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 48 | private val watcher = FileSystems.getDefault.newWatchService() | 
| 64699 | 49 | |
| 64859 | 50 | override def toString: String = | 
| 51 |       state.value.dirs.keySet.mkString("File_Watcher(", ", ", ")")
 | |
| 52 | ||
| 64699 | 53 | |
| 64807 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 54 | /* registered directories */ | 
| 64699 | 55 | |
| 64807 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 56 | override def register(dir: JFile): Unit = | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 57 | state.change(st => | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 58 |         st.dirs.get(dir) match {
 | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 59 | case Some(key) if key.isValid => st | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 60 | case _ => | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 61 | val key = dir.toPath.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY) | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 62 | st.copy(dirs = st.dirs + (dir -> key)) | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 63 | }) | 
| 64699 | 64 | |
| 64807 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 65 | override def register_parent(file: JFile): Unit = | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 66 |     {
 | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 67 | val dir = file.getParentFile | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 68 | if (dir != null && dir.isDirectory) register(dir) | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 69 | } | 
| 64782 | 70 | |
| 64807 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 71 | override def deregister(dir: JFile): Unit = | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 72 | state.change(st => | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 73 |         st.dirs.get(dir) match {
 | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 74 | case None => st | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 75 | case Some(key) => | 
| 73367 | 76 | key.cancel() | 
| 64807 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 77 | st.copy(dirs = st.dirs - dir) | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 78 | }) | 
| 64699 | 79 | |
| 64867 | 80 | override def purge(retain: Set[JFile]): Unit = | 
| 81 | state.change(st => | |
| 82 | st.copy(dirs = st.dirs -- | |
| 73367 | 83 |           (for ((dir, key) <- st.dirs.iterator if !retain(dir)) yield { key.cancel(); dir })))
 | 
| 64867 | 84 | |
| 64699 | 85 | |
| 64807 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 86 | /* changed directory entries */ | 
| 64699 | 87 | |
| 71704 | 88 | private val delay_changed = Delay.last(delay) | 
| 64807 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 89 |     {
 | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 90 | val changed = state.change_result(st => (st.changed, st.copy(changed = Set.empty))) | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 91 | handle(changed) | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 92 | } | 
| 64699 | 93 | |
| 71692 | 94 | private val watcher_thread = Isabelle_Thread.fork(name = "file_watcher", daemon = true) | 
| 64807 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 95 |     {
 | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 96 |       try {
 | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 97 |         while (true) {
 | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 98 | val key = watcher.take | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 99 | val has_changed = | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 100 | state.change_result(st => | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 101 |               {
 | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 102 | val (remove, changed) = | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 103 |                   st.dirs.collectFirst({ case (dir, key1) if key == key1 => dir }) match {
 | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 104 | case Some(dir) => | 
| 73353 | 105 | val events: Iterable[WatchEvent[JPath]] = | 
| 73909 | 106 | key.pollEvents.asInstanceOf[JList[WatchEvent[JPath]]].asScala | 
| 64807 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 107 | val remove = if (key.reset) None else Some(dir) | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 108 | val changed = | 
| 73359 | 109 |                         events.iterator.foldLeft(Set.empty[JFile]) {
 | 
| 64807 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 110 | case (set, event) => set + dir.toPath.resolve(event.context).toFile | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 111 | } | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 112 | (remove, changed) | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 113 | case None => | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 114 | key.pollEvents | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 115 | key.reset | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 116 | (None, Set.empty[JFile]) | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 117 | } | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 118 | (changed.nonEmpty, st.copy(dirs = st.dirs -- remove, changed = st.changed ++ changed)) | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 119 | }) | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 120 | if (has_changed) delay_changed.invoke() | 
| 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 121 | } | 
| 64700 | 122 | } | 
| 64807 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 123 |       catch { case Exn.Interrupt() => }
 | 
| 64699 | 124 | } | 
| 125 | ||
| 126 | ||
| 64807 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 127 | /* shutdown */ | 
| 64699 | 128 | |
| 73340 | 129 | override def shutdown(): Unit = | 
| 64807 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 130 |     {
 | 
| 73367 | 131 | watcher_thread.interrupt() | 
| 74140 | 132 | watcher_thread.join() | 
| 73367 | 133 | delay_changed.revoke() | 
| 64807 
7d556bb6046b
dummy File_Watcher for Windows (spurious crashes seen on Windows 7);
 wenzelm parents: 
64782diff
changeset | 134 | } | 
| 64699 | 135 | } | 
| 136 | } |