From bb00825cd290d2468c1c90be36f98f8b19adf20c Mon Sep 17 00:00:00 2001 From: Arksine Date: Sun, 21 Mar 2021 18:24:01 -0400 Subject: [PATCH] file_manager: bundle inotify delete events When a directory is deleted this attempts to suppress delete events for all children. Clients only need to be notified of the parent node, as its deletion implies all children have been deleted. Signed-off-by: Eric Callahan --- moonraker/components/file_manager.py | 52 +++++++++++++++------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/moonraker/components/file_manager.py b/moonraker/components/file_manager.py index f015a14..2bbd935 100644 --- a/moonraker/components/file_manager.py +++ b/moonraker/components/file_manager.py @@ -614,15 +614,16 @@ class INotifyHandler: self.watches[new_path] = wd self.watched_dirs[wd] = (new_root, new_path) - def _process_deleted_files(self, dir_path): + def _process_deleted_items(self, dir_path): if dir_path not in self.pending_delete_events: return - root, files, hdl = self.pending_delete_events.pop(dir_path) - for fname in files: - file_path = os.path.join(dir_path, fname) - self._clear_metadata(root, file_path) + root, items, hdl = self.pending_delete_events.pop(dir_path) + for (item_name, isdir) in items: + item_path = os.path.join(dir_path, item_name) + item_type = "dir" if isdir else "file" + self._clear_metadata(root, item_path, isdir) self._notify_filelist_changed( - "delete_file", root, file_path) + f"delete_{item_type}", root, item_path) def _remove_stale_cookie(self, cookie): pending_evt = self.pending_move_events.pop(cookie, None) @@ -729,6 +730,24 @@ class INotifyHandler: else: self._process_file_event(evt, root, child_path) + def _schedule_delete_event(self, root, item_path, is_dir): + if is_dir: + self.remove_watch(item_path, need_low_level_rm=False) + # Remove pending delete events for children if they exist + pending_evt = self.pending_delete_events.pop(item_path, None) + if pending_evt is not None: + delete_hdl = pending_evt[2] + self.ioloop.remove_timeout(delete_hdl) + parent_path, item_name = os.path.split(item_path) + items = set() + if parent_path in self.pending_delete_events: + root, items, delete_hdl = self.pending_delete_events[parent_path] + self.ioloop.remove_timeout(delete_hdl) + items.add((item_name, is_dir)) + delete_hdl = self.ioloop.call_later( + INOTIFY_DELETE_TIME, self._process_deleted_items, parent_path) + self.pending_delete_events[parent_path] = (root, items, delete_hdl) + def _process_dir_event(self, evt, root, child_path): if evt.name and evt.name[0] == ".": # ignore changes to the hidden directories @@ -740,14 +759,7 @@ class INotifyHandler: "create_dir", root, child_path) elif evt.mask & iFlags.DELETE: logging.debug(f"Inotify directory delete: {root}, {evt.name}") - self.remove_watch(child_path, need_low_level_rm=False) - pending_evt = self.pending_delete_events.pop(child_path, None) - if pending_evt is not None: - delete_hdl = pending_evt[2] - self.ioloop.remove_timeout(delete_hdl) - self._clear_metadata(root, child_path, True) - self._notify_filelist_changed( - "delete_dir", root, child_path) + self._schedule_delete_event(root, child_path, True) elif evt.mask & iFlags.MOVED_FROM: logging.debug(f"Inotify directory move from: {root}, {evt.name}") hdl = self.ioloop.call_later( @@ -791,19 +803,11 @@ class INotifyHandler: if root == "gcodes" and ext == ".ufp": # Don't notify deleted ufp files return - dir_path, fname = os.path.split(child_path) - files = set() - if dir_path in self.pending_delete_events: - root, files, delete_hdl = self.pending_delete_events[dir_path] - self.ioloop.remove_timeout(delete_hdl) - files.add(fname) - delete_hdl = self.ioloop.call_later( - INOTIFY_MOVE_TIME, self._process_deleted_files, dir_path) - self.pending_delete_events[dir_path] = (root, files, delete_hdl) + self._schedule_delete_event(root, child_path, False) elif evt.mask & iFlags.MOVED_FROM: logging.debug(f"Inotify file move from: {root}, {evt.name}") hdl = self.ioloop.call_later( - INOTIFY_DELETE_TIME, self._remove_stale_cookie, evt.cookie) + INOTIFY_MOVE_TIME, self._remove_stale_cookie, evt.cookie) self.pending_move_events[evt.cookie] = ( root, child_path, hdl, False) self._clear_metadata(root, child_path)