11from __future__ import annotations
22
33import asyncio
4- import multiprocessing
54import os
65import sys
76import threading
87from contextlib import suppress
98from io import TextIOWrapper
109from os import path
1110from subprocess import Popen , TimeoutExpired
11+ from time import monotonic
1212from typing import Callable , Iterable
1313
1414from rich .console import RenderableType
6363 PreviewContainer ,
6464)
6565from rovr .footer import Clipboard , MetadataContainer , ProcessContainer
66- from rovr .functions import drive_workers , multiprocessing_utils
66+ from rovr .functions import drive_workers
6767from rovr .functions .cwd import chdir , getcwd
6868from rovr .functions .path import (
6969 dump_exc ,
@@ -641,19 +641,19 @@ def cd(
641641 @work (thread = True )
642642 def watch_for_changes_and_update (self ) -> None :
643643 cwd = getcwd ()
644- file_list : FileList = self .query_one (FileList )
645644 pins_path = path .join (RovrVars .ROVRCONFIG , "pins.json" )
646645 with suppress (OSError ):
647646 self ._pins_mtime = path .getmtime (pins_path )
648647 state_path = path .join (RovrVars .ROVRSTATE , "state.toml" )
649- state_mtime = None
650648 with suppress (OSError ):
651649 state_mtime = path .getmtime (state_path )
652- drive_update_every = int (config ["interface" ]["drive_watcher_frequency" ])
653- count : int = - 2
650+ drive_update_every = max (1.0 , config ["interface" ]["drive_watcher_frequency" ])
651+ next_drive_check = 0.0
652+ drive_watcher = drive_workers .DriveWatcher (
653+ sys .platform , config ["settings" ]["drive_exclude" ], drive_update_every
654+ )
654655 style_available : bool = self .CUSTOM_STYLE_AVAILABLE
655656 custom_style_path = path .join (RovrVars .ROVRCONFIG , "style.tcss" )
656- new_drives : list [str ] | None = None
657657 cwd_mtime : float | None = None
658658 pin_sidebar = self .query_one (PinnedSidebar )
659659
@@ -663,14 +663,11 @@ def watch_for_changes_and_update(self) -> None:
663663
664664 while True :
665665 if self ._shutdown_event .wait (timeout = 1 ):
666- return
666+ break
667667 if i_should_shut_down ():
668- return
668+ break
669669 if (file_list := self .file_list ).parent is None or not file_list .is_running :
670670 continue
671- count += 1
672- if count >= drive_update_every :
673- count = 0
674671 try :
675672 new_cwd = getcwd ()
676673 if not self .file_list .file_list_pause_check :
@@ -683,12 +680,10 @@ def watch_for_changes_and_update(self) -> None:
683680 else :
684681 # only rescan when the directory mtime changed;
685682 # renames/creates/deletes always bump it
686- new_cwd_mtime = None
687683 with suppress (OSError ):
688684 new_cwd_mtime = path .getmtime (cwd )
689685 if new_cwd_mtime != cwd_mtime :
690686 cwd_mtime = new_cwd_mtime
691- items = None
692687 with suppress (OSError ):
693688 items = get_filtered_dir_names (
694689 cwd ,
@@ -710,11 +705,9 @@ def watch_for_changes_and_update(self) -> None:
710705 )
711706
712707 if i_should_shut_down ():
713- return
708+ break
714709
715710 # check pins.json
716- new_mtime = None
717- reload_called : bool = False
718711 with suppress (OSError ):
719712 new_mtime = path .getmtime (pins_path )
720713 if new_mtime != self ._pins_mtime :
@@ -726,12 +719,10 @@ def watch_for_changes_and_update(self) -> None:
726719 # really is no issue here, thanks to any AI
727720 # models raising false issues on thread safety
728721 pin_sidebar .reload_pins ()
729- reload_called = True
730722 if i_should_shut_down ():
731- return
723+ break
732724
733725 # check state.toml
734- new_state_mtime = None
735726 with suppress (OSError ):
736727 new_state_mtime = path .getmtime (state_path )
737728 if new_state_mtime != state_mtime :
@@ -741,53 +732,30 @@ def watch_for_changes_and_update(self) -> None:
741732 self .app .call_from_thread (state_manager ._load_state )
742733 self .app .call_from_thread (state_manager .restore_state )
743734 if i_should_shut_down ():
744- return
735+ break
745736
746737 # check drives
747- if count == 0 and not reload_called :
748- try :
749- if self .MULTIPROCESSING_PROCESS_ALLOWED :
750- # Run drive check in a separate process using multiprocessing.Process
751- # Using Queue to get the result back from the process
752- result_queue : multiprocessing .Queue [list [str ]] = (
753- multiprocessing .Queue ()
754- )
755-
756- process = multiprocessing .Process (
757- target = drive_workers .get_mounted_drives_worker ,
758- args = (result_queue , sys .platform , config ),
759- )
760- multiprocessing_utils .start_process (process )
761- process .join (timeout = 2.0 )
762-
763- if process .is_alive ():
764- # Timeout - terminate the process
765- process .terminate ()
766- process .join (timeout = 0.5 )
767- if process .is_alive ():
768- process .kill ()
769- elif not result_queue .empty ():
770- # Process completed successfully
771- new_drives = result_queue .get_nowait ()
772- else :
773- new_drives = drive_workers .get_mounted_drives (
774- sys .platform , config
775- )
776- if new_drives is not None and new_drives != pin_sidebar .DRIVES :
777- pin_sidebar .reload_pins ()
778- except Exception as exc :
779- if multiprocessing_process_error_checker (self , exc ):
780- count = - 1 # try again immediately on next loop
781- else :
782- self .notify (
783- f"{ type (exc ).__name__ } : { exc } " ,
784- title = "Drives Watcher" ,
785- severity = "warning" ,
786- markup = False ,
787- )
788- dump_exc (self , exc )
738+ try :
739+ new_drives = None
740+ if self .MULTIPROCESSING_PROCESS_ALLOWED :
741+ new_drives = drive_watcher .poll ()
742+ elif monotonic () >= next_drive_check :
743+ drive_watcher .close ()
744+ next_drive_check = monotonic () + drive_update_every
745+ new_drives = drive_workers .get_mounted_drives (sys .platform , config )
746+ if new_drives is not None and new_drives != pin_sidebar .DRIVES :
747+ pin_sidebar .reload_pins (drives = new_drives )
748+ except Exception as exc :
749+ if not multiprocessing_process_error_checker (self , exc ):
750+ self .notify (
751+ f"{ type (exc ).__name__ } : { exc } " ,
752+ title = "Drives Watcher" ,
753+ severity = "warning" ,
754+ markup = False ,
755+ )
756+ dump_exc (self , exc )
789757 if i_should_shut_down ():
790- return
758+ break
791759
792760 # check highlighted file mtime
793761 if not self .file_list .file_list_pause_check :
@@ -823,7 +791,7 @@ def watch_for_changes_and_update(self) -> None:
823791 dir_entry
824792 )
825793 if i_should_shut_down ():
826- return
794+ break
827795
828796 if not self .CUSTOM_STYLE_AVAILABLE :
829797 if not style_available and path .exists (custom_style_path ):
@@ -835,6 +803,7 @@ def watch_for_changes_and_update(self) -> None:
835803 )
836804 elif not path .exists (custom_style_path ):
837805 style_available = False
806+ drive_watcher .close ()
838807
839808 @work (exclusive = True )
840809 async def on_resize (self , event : events .Resize ) -> None :
0 commit comments