11use std:: io:: BufRead ;
22use std:: path:: Path ;
33use std:: sync:: atomic:: { AtomicUsize , Ordering } ;
4- use std:: sync:: Arc ;
4+ use std:: sync:: { Arc , RwLock } ;
55use std:: time:: Duration ;
66use std:: { fs, io, thread} ;
77
88use crc32fast:: Hasher ;
9+ use tokio:: task:: JoinHandle ;
910
1011use crate :: directory:: { WatchCallback , WatchCallbackList , WatchHandle } ;
12+ use crate :: indexer:: TOKIO_FILE_WATCHER_WORKER_RUNTIME ;
1113
1214const POLLING_INTERVAL : Duration = Duration :: from_millis ( if cfg ! ( test) { 1 } else { 500 } ) ;
1315
@@ -16,6 +18,8 @@ pub struct FileWatcher {
1618 path : Arc < Path > ,
1719 callbacks : Arc < WatchCallbackList > ,
1820 state : Arc < AtomicUsize > , // 0: new, 1: runnable, 2: terminated
21+ watch_handle : RwLock < Option < JoinHandle < ( ) > > > ,
22+ wakeup_channel : RwLock < Option < async_channel:: Sender < ( ) > > > ,
1923}
2024
2125impl FileWatcher {
@@ -24,6 +28,8 @@ impl FileWatcher {
2428 path : Arc :: from ( path) ,
2529 callbacks : Default :: default ( ) ,
2630 state : Default :: default ( ) ,
31+ watch_handle : RwLock :: new ( None ) ,
32+ wakeup_channel : RwLock :: new ( None ) ,
2733 }
2834 }
2935
@@ -40,29 +46,34 @@ impl FileWatcher {
4046 let callbacks = self . callbacks . clone ( ) ;
4147 let state = self . state . clone ( ) ;
4248
43- thread:: Builder :: new ( )
44- . name ( "thread-tantivy-meta-file-watcher" . to_string ( ) )
45- . spawn ( move || {
46- let mut current_checksum_opt = None ;
47-
48- while state. load ( Ordering :: SeqCst ) == 1 {
49- if let Ok ( checksum) = FileWatcher :: compute_checksum ( & path) {
50- let metafile_has_changed = current_checksum_opt
51- . map ( |current_checksum| current_checksum != checksum)
52- . unwrap_or ( true ) ;
53- if metafile_has_changed {
54- info ! ( "Meta file {:?} was modified" , path) ;
55- current_checksum_opt = Some ( checksum) ;
56- // We actually ignore callbacks failing here.
57- // We just wait for the end of their execution.
58- let _ = callbacks. broadcast ( ) . wait ( ) ;
59- }
49+ let ( tx, rx) = async_channel:: bounded ( 5 ) ;
50+ self . wakeup_channel . write ( ) . unwrap ( ) . replace ( tx) ;
51+ let task = async move {
52+ let mut current_checksum_opt = None ;
53+ while state. load ( Ordering :: SeqCst ) == 1 {
54+ if let Ok ( checksum) = FileWatcher :: compute_checksum ( & path) {
55+ let metafile_has_changed = current_checksum_opt
56+ . map ( |current_checksum| current_checksum != checksum)
57+ . unwrap_or ( true ) ;
58+ if metafile_has_changed {
59+ info ! ( "Meta file {:?} was modified" , path) ;
60+ current_checksum_opt = Some ( checksum) ;
61+ // We actually ignore callbacks failing here.
62+ // We just wait for the end of their execution.
63+ let _ = callbacks. broadcast ( ) . wait ( ) ;
6064 }
65+ }
6166
62- thread:: sleep ( POLLING_INTERVAL ) ;
67+ tokio:: select! {
68+ _ = tokio:: time:: sleep( POLLING_INTERVAL ) => { } ,
69+ _ = rx. recv( ) => {
70+ // Early wake up from sleep
71+ }
6372 }
64- } )
65- . expect ( "Failed to spawn meta file watcher thread" ) ;
73+ }
74+ } ;
75+ let watch_handle = TOKIO_FILE_WATCHER_WORKER_RUNTIME . spawn ( task) ;
76+ self . watch_handle . write ( ) . unwrap ( ) . replace ( watch_handle) ;
6677 }
6778
6879 pub fn watch ( & self , callback : WatchCallback ) -> WatchHandle {
@@ -91,6 +102,11 @@ impl FileWatcher {
91102
92103 pub fn graceful_stop ( & self ) {
93104 self . state . store ( 2 , Ordering :: SeqCst ) ;
105+ if let Some ( handle) = self . watch_handle . write ( ) . unwrap ( ) . take ( ) {
106+ let _ = self . wakeup_channel . write ( ) . unwrap ( ) . take ( ) ;
107+ handle. abort ( ) ;
108+ info ! ( "Meta file watcher thread joined/aborted" ) ;
109+ }
94110 }
95111}
96112
0 commit comments