src/Pure/General/file_watcher.scala
changeset 75393 87ebf5a50283
parent 74140 8a5e02ef975c
child 75394 42267c650205
equal deleted inserted replaced
75388:b3ca4a6ed74b 75393:87ebf5a50283
    14 import java.nio.file.StandardWatchEventKinds.{ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY}
    14 import java.nio.file.StandardWatchEventKinds.{ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY}
    15 
    15 
    16 import scala.jdk.CollectionConverters._
    16 import scala.jdk.CollectionConverters._
    17 
    17 
    18 
    18 
    19 class File_Watcher private[File_Watcher]  // dummy template
    19 class File_Watcher private[File_Watcher] {
    20 {
    20   // dummy template
    21   def register(dir: JFile): Unit = {}
    21   def register(dir: JFile): Unit = {}
    22   def register_parent(file: JFile): Unit = {}
    22   def register_parent(file: JFile): Unit = {}
    23   def deregister(dir: JFile): Unit = {}
    23   def deregister(dir: JFile): Unit = {}
    24   def purge(retain: Set[JFile]): Unit = {}
    24   def purge(retain: Set[JFile]): Unit = {}
    25   def shutdown(): Unit = {}
    25   def shutdown(): Unit = {}
    26 }
    26 }
    27 
    27 
    28 object File_Watcher
    28 object File_Watcher {
    29 {
    29   val none: File_Watcher = new File_Watcher {
    30   val none: File_Watcher = new File_Watcher
       
    31   {
       
    32     override def toString: String = "File_Watcher.none"
    30     override def toString: String = "File_Watcher.none"
    33   }
    31   }
    34 
    32 
    35   def apply(handle: Set[JFile] => Unit, delay: => Time = Time.seconds(0.5)): File_Watcher =
    33   def apply(handle: Set[JFile] => Unit, delay: => Time = Time.seconds(0.5)): File_Watcher =
    36     if (Platform.is_windows) none else new Impl(handle, delay)
    34     if (Platform.is_windows) none else new Impl(handle, delay)
    40 
    38 
    41   sealed case class State(
    39   sealed case class State(
    42     dirs: Map[JFile, WatchKey] = Map.empty,
    40     dirs: Map[JFile, WatchKey] = Map.empty,
    43     changed: Set[JFile] = Set.empty)
    41     changed: Set[JFile] = Set.empty)
    44 
    42 
    45   class Impl private[File_Watcher](handle: Set[JFile] => Unit, delay: Time) extends File_Watcher
    43   class Impl private[File_Watcher](handle: Set[JFile] => Unit, delay: Time) extends File_Watcher {
    46   {
       
    47     private val state = Synchronized(File_Watcher.State())
    44     private val state = Synchronized(File_Watcher.State())
    48     private val watcher = FileSystems.getDefault.newWatchService()
    45     private val watcher = FileSystems.getDefault.newWatchService()
    49 
    46 
    50     override def toString: String =
    47     override def toString: String =
    51       state.value.dirs.keySet.mkString("File_Watcher(", ", ", ")")
    48       state.value.dirs.keySet.mkString("File_Watcher(", ", ", ")")
    60           case _ =>
    57           case _ =>
    61             val key = dir.toPath.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY)
    58             val key = dir.toPath.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY)
    62             st.copy(dirs = st.dirs + (dir -> key))
    59             st.copy(dirs = st.dirs + (dir -> key))
    63         })
    60         })
    64 
    61 
    65     override def register_parent(file: JFile): Unit =
    62     override def register_parent(file: JFile): Unit = {
    66     {
       
    67       val dir = file.getParentFile
    63       val dir = file.getParentFile
    68       if (dir != null && dir.isDirectory) register(dir)
    64       if (dir != null && dir.isDirectory) register(dir)
    69     }
    65     }
    70 
    66 
    71     override def deregister(dir: JFile): Unit =
    67     override def deregister(dir: JFile): Unit =
    83           (for ((dir, key) <- st.dirs.iterator if !retain(dir)) yield { key.cancel(); dir })))
    79           (for ((dir, key) <- st.dirs.iterator if !retain(dir)) yield { key.cancel(); dir })))
    84 
    80 
    85 
    81 
    86     /* changed directory entries */
    82     /* changed directory entries */
    87 
    83 
    88     private val delay_changed = Delay.last(delay)
    84     private val delay_changed = Delay.last(delay) {
    89     {
       
    90       val changed = state.change_result(st => (st.changed, st.copy(changed = Set.empty)))
    85       val changed = state.change_result(st => (st.changed, st.copy(changed = Set.empty)))
    91       handle(changed)
    86       handle(changed)
    92     }
    87     }
    93 
    88 
    94     private val watcher_thread = Isabelle_Thread.fork(name = "file_watcher", daemon = true)
    89     private val watcher_thread = Isabelle_Thread.fork(name = "file_watcher", daemon = true) {
    95     {
       
    96       try {
    90       try {
    97         while (true) {
    91         while (true) {
    98           val key = watcher.take
    92           val key = watcher.take
    99           val has_changed =
    93           val has_changed =
   100             state.change_result(st =>
    94             state.change_result(st => {
   101               {
       
   102                 val (remove, changed) =
    95                 val (remove, changed) =
   103                   st.dirs.collectFirst({ case (dir, key1) if key == key1 => dir }) match {
    96                   st.dirs.collectFirst({ case (dir, key1) if key == key1 => dir }) match {
   104                     case Some(dir) =>
    97                     case Some(dir) =>
   105                       val events: Iterable[WatchEvent[JPath]] =
    98                       val events: Iterable[WatchEvent[JPath]] =
   106                         key.pollEvents.asInstanceOf[JList[WatchEvent[JPath]]].asScala
    99                         key.pollEvents.asInstanceOf[JList[WatchEvent[JPath]]].asScala
   124     }
   117     }
   125 
   118 
   126 
   119 
   127     /* shutdown */
   120     /* shutdown */
   128 
   121 
   129     override def shutdown(): Unit =
   122     override def shutdown(): Unit = {
   130     {
       
   131       watcher_thread.interrupt()
   123       watcher_thread.interrupt()
   132       watcher_thread.join()
   124       watcher_thread.join()
   133       delay_changed.revoke()
   125       delay_changed.revoke()
   134     }
   126     }
   135   }
   127   }