Improve handling of pending file notification events

* lisp/filenotify.el (file-notify--check-pending-rename): New defun.
(file-notify--handle-event): Use it.  Start a timer checking
pending events.  (Bug#81531)

* test/lisp/filenotify-tests.el (file-notify--test-with-actions):
Read pending event.
(file-notify-test05-file-validity, file-notify-test12-symlinks):
Adapt tests.
(file-notify-test13-unmount): Rename.
This commit is contained in:
Michael Albinus 2026-09-01 09:58:34 +02:00
parent 6698677f0d
commit 21d0f8023a
2 changed files with 45 additions and 28 deletions

View file

@ -243,6 +243,16 @@ It is nil or a `file-notify--rename' defstruct where the cookie can be nil.")
(list desc action file file1) (list desc action file file1)
(list desc action file)))))) (list desc action file))))))
(defun file-notify--check-pending-rename ()
"Fire a `deleted' event from a pending `rename'."
(when file-notify--pending-rename
(file-notify--call-handler
(file-notify--rename-watch file-notify--pending-rename)
(file-notify--rename-desc file-notify--pending-rename)
'deleted
(file-notify--rename-from-file file-notify--pending-rename)
(setq file-notify--pending-rename nil))))
(defun file-notify--handle-event (desc actions file file1-or-cookie) (defun file-notify--handle-event (desc actions file file1-or-cookie)
"Handle an event returned from file notification. "Handle an event returned from file notification.
DESC is the back-end descriptor. ACTIONS is a list of: DESC is the back-end descriptor. ACTIONS is a list of:
@ -290,7 +300,8 @@ DESC is the back-end descriptor. ACTIONS is a list of:
((eq action 'renamed-from) ((eq action 'renamed-from)
(setq file-notify--pending-rename (setq file-notify--pending-rename
(file-notify--rename-make watch desc file file1-or-cookie) (file-notify--rename-make watch desc file file1-or-cookie)
action nil)) action nil)
(run-at-time 0.1 nil #'file-notify--check-pending-rename))
;; Look for pending event. ;; Look for pending event.
((eq action 'renamed-to) ((eq action 'renamed-to)
(if file-notify--pending-rename (if file-notify--pending-rename
@ -323,14 +334,7 @@ DESC is the back-end descriptor. ACTIONS is a list of:
(string-equal (string-equal
file (file-notify--watch-absolute-filename watch)))) file (file-notify--watch-absolute-filename watch))))
;; Fire pending `renamed-from' event. ;; Fire pending `renamed-from' event.
(when file-notify--pending-rename (file-notify--check-pending-rename)
(file-notify--call-handler
(file-notify--rename-watch file-notify--pending-rename)
(file-notify--rename-desc file-notify--pending-rename)
'deleted
(file-notify--rename-from-file file-notify--pending-rename)
nil)
(setq file-notify--pending-rename nil))
(setq actions nil) (setq actions nil)
;; Make sure this is the last time the callback was invoked. ;; Make sure this is the last time the callback was invoked.
(when (eq action 'stopped) (when (eq action 'stopped)

View file

@ -641,6 +641,8 @@ delivered."
(* (ceiling max-length 100) (file-notify--test-timeout)) (* (ceiling max-length 100) (file-notify--test-timeout))
(or (= max-length (length file-notify--test-events)) (or (= max-length (length file-notify--test-events))
(memq 'stopped (file-notify--test-event-actions)))) (memq 'stopped (file-notify--test-event-actions))))
;; Read possible pending event.
(file-notify--test-wait-event)
;; Check the result sequence just to make sure that all actions ;; Check the result sequence just to make sure that all actions
;; are as expected. ;; are as expected.
(dolist (result file-notify--test-results) (dolist (result file-notify--test-results)
@ -1070,7 +1072,6 @@ delivered."
(write-region "another text" nil file-notify--test-tmpfile nil 'no-message) (write-region "another text" nil file-notify--test-tmpfile nil 'no-message)
(file-notify--test-wait-event) (file-notify--test-wait-event)
(delete-file file-notify--test-tmpfile)) (delete-file file-notify--test-tmpfile))
(file-notify--test-wait-event)
;; After deleting the file, the descriptor is not valid anymore. ;; After deleting the file, the descriptor is not valid anymore.
(should-not (file-notify-valid-p file-notify--test-desc)) (should-not (file-notify-valid-p file-notify--test-desc))
(file-notify-rm-watch file-notify--test-desc) (file-notify-rm-watch file-notify--test-desc)
@ -1693,18 +1694,13 @@ the file watch."
(t '(attribute-changed))) (t '(attribute-changed)))
(set-file-times file-notify--test-tmpfile '(0 0) 'nofollow)) (set-file-times file-notify--test-tmpfile '(0 0) 'nofollow))
;; Deleting the target should not raise any event. ;; Deleting the target should remove the watch.
(file-notify--test-with-actions nil (file-notify--test-with-actions '(deleted stopped)
(delete-file file-notify--test-tmpfile1) (delete-file file-notify--test-tmpfile1)
(delete-file file-notify--test-tmpfile)) (delete-file file-notify--test-tmpfile))
;; Sanity check. (file-notify-rm-watch file-notify--test-desc)
(file-notify--test-wait-for-events
(file-notify--test-timeout)
(not (input-pending-p)))
(should-not file-notify--test-events)
;; The environment shall be cleaned up. ;; The environment shall be cleaned up.
(file-notify-rm-watch file-notify--test-desc)
(file-notify--test-cleanup-p))) (file-notify--test-cleanup-p)))
(with-file-notify-test (with-file-notify-test
@ -1724,21 +1720,38 @@ the file watch."
(should (file-notify-valid-p file-notify--test-desc)) (should (file-notify-valid-p file-notify--test-desc))
;; None of the actions on a file in the symlinked directory ;; None of the actions on a file in the symlinked directory
;; will be reported. ;; will be reported, except ...
(file-notify--test-with-actions nil (file-notify--test-with-actions
(cond
;; GKqueueFileMonitor reports deleted only.
((eq (file-notify--test-monitor) 'GKqueueFileMonitor)
'(deleted))
;; GInotifyFileMonitor still reports changes.
((and (string-equal (file-notify--test-library) "gio")
(eq (file-notify--test-monitor) 'GInotifyFileMonitor))
'(changed changed attribute-changed changed changed
attribute-changed attribute-changed attribute-changed
attribute-changed attribute-changed deleted))
((and (string-equal (file-notify--test-library) "gfilenotify")
(eq (file-notify--test-monitor) 'GInotifyFileMonitor))
'(changed changed changed changed attribute-changed
attribute-changed attribute-changed attribute-changed deleted))
(t nil))
(write-region "another text" nil tmpfile nil 'no-message) (write-region "another text" nil tmpfile nil 'no-message)
(file-notify--test-wait-event)
(write-region "another text" nil tmpfile1 nil 'no-message) (write-region "another text" nil tmpfile1 nil 'no-message)
(file-notify--test-wait-event)
(set-file-times tmpfile '(0 0)) (set-file-times tmpfile '(0 0))
(file-notify--test-wait-event)
(set-file-times tmpfile '(0 0) 'nofollow) (set-file-times tmpfile '(0 0) 'nofollow)
(file-notify--test-wait-event)
(set-file-times tmpfile1 '(0 0)) (set-file-times tmpfile1 '(0 0))
(file-notify--test-wait-event)
(set-file-times tmpfile1 '(0 0) 'nofollow) (set-file-times tmpfile1 '(0 0) 'nofollow)
(file-notify--test-wait-event)
(delete-file tmpfile) (delete-file tmpfile)
(file-notify--test-wait-event)
(delete-file tmpfile1)) (delete-file tmpfile1))
;; Sanity check.
(file-notify--test-wait-for-events
(file-notify--test-timeout)
(not (input-pending-p)))
(should-not file-notify--test-events)
;; The environment shall be cleaned up. ;; The environment shall be cleaned up.
(delete-directory file-notify--test-tmpdir 'recursive) (delete-directory file-notify--test-tmpdir 'recursive)
@ -1748,7 +1761,7 @@ the file watch."
(file-notify--deftest-remote file-notify-test12-symlinks (file-notify--deftest-remote file-notify-test12-symlinks
"Check `file-notify-test12-symlinks' for remote files.") "Check `file-notify-test12-symlinks' for remote files.")
(ert-deftest file-notify-test12-unmount () (ert-deftest file-notify-test13-unmount ()
"Check that file notification stop after unmounting the filesystem." "Check that file notification stop after unmounting the filesystem."
:tags '(:expensive-test) :tags '(:expensive-test)
(skip-unless (file-notify--test-local-enabled)) (skip-unless (file-notify--test-local-enabled))
@ -1799,8 +1812,8 @@ the file watch."
;; The environment shall be cleaned up. ;; The environment shall be cleaned up.
(file-notify--test-cleanup-p))) (file-notify--test-cleanup-p)))
(file-notify--deftest-remote file-notify-test12-unmount (file-notify--deftest-remote file-notify-test13-unmount
"Check `file-notify-test12-unmount' for remote files.") "Check `file-notify-test13-unmount' for remote files.")
(defun file-notify-test-all (&optional interactive) (defun file-notify-test-all (&optional interactive)
"Run all tests for \\[file-notify]." "Run all tests for \\[file-notify]."