From c243aabfa8d280adf75024c8e6c0e68a7932f6cf Mon Sep 17 00:00:00 2001 From: =?utf8?q?Mattias=20Engdeg=C3=A5rd?= Date: Sat, 20 Apr 2019 10:19:52 +0200 Subject: [PATCH] Make file-notify-rm-watch robust against reentry Allow file-notify callbacks to call `file-notify-rm-watch', harmlessly, after receiving a `stopped' event without triggering recursion. * lisp/filenotify.el (file-notify--watch): Note that `callback' can be nil. (file-notify--rm-descriptor): Set the `callback' field to nil before sending `stopped'. (file-notify-rm-watch): Don't do anything if the `callback' field is nil. --- lisp/filenotify.el | 57 +++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 26 deletions(-) diff --git a/lisp/filenotify.el b/lisp/filenotify.el index 3f9bb960a9b..62dd1cd9117 100644 --- a/lisp/filenotify.el +++ b/lisp/filenotify.el @@ -49,7 +49,7 @@ could use another implementation.") directory ;; Watched relative filename, nil if watching the directory. filename - ;; Function to propagate events to. + ;; Function to propagate events to, or nil if watch is being removed. callback) (defun file-notify--watch-absolute-filename (watch) @@ -72,12 +72,15 @@ struct.") DESCRIPTOR should be an object returned by `file-notify-add-watch'. If it is registered in `file-notify-descriptors', a stopped event is sent." (when-let* ((watch (gethash descriptor file-notify-descriptors))) - ;; Send `stopped' event. - (unwind-protect - (funcall - (file-notify--watch-callback watch) - `(,descriptor stopped ,(file-notify--watch-absolute-filename watch))) - (remhash descriptor file-notify-descriptors)))) + (let ((callback (file-notify--watch-callback watch))) + ;; Make sure this is the last time the callback is invoked. + (setf (file-notify--watch-callback watch) nil) + ;; Send `stopped' event. + (unwind-protect + (funcall + callback + `(,descriptor stopped ,(file-notify--watch-absolute-filename watch))) + (remhash descriptor file-notify-descriptors))))) ;; This function is used by `inotify', `kqueue', `gfilenotify' and ;; `w32notify' events. @@ -381,25 +384,27 @@ FILE is the name of the file whose event is being reported." "Remove an existing watch specified by its DESCRIPTOR. DESCRIPTOR should be an object returned by `file-notify-add-watch'." (when-let* ((watch (gethash descriptor file-notify-descriptors))) - (let ((handler (find-file-name-handler - (file-notify--watch-directory watch) - 'file-notify-rm-watch))) - (condition-case nil - (if handler - ;; A file name handler could exist even if there is no - ;; local file notification support. - (funcall handler 'file-notify-rm-watch descriptor) - - (funcall - (cond - ((eq file-notify--library 'inotify) 'inotify-rm-watch) - ((eq file-notify--library 'kqueue) 'kqueue-rm-watch) - ((eq file-notify--library 'gfilenotify) 'gfile-rm-watch) - ((eq file-notify--library 'w32notify) 'w32notify-rm-watch)) - descriptor)) - (file-notify-error nil))) - ;; Modify `file-notify-descriptors'. - (file-notify--rm-descriptor descriptor))) + ;; If we are called from a `stopped' event, do nothing. + (when (file-notify--watch-callback watch) + (let ((handler (find-file-name-handler + (file-notify--watch-directory watch) + 'file-notify-rm-watch))) + (condition-case nil + (if handler + ;; A file name handler could exist even if there is no + ;; local file notification support. + (funcall handler 'file-notify-rm-watch descriptor) + + (funcall + (cond + ((eq file-notify--library 'inotify) 'inotify-rm-watch) + ((eq file-notify--library 'kqueue) 'kqueue-rm-watch) + ((eq file-notify--library 'gfilenotify) 'gfile-rm-watch) + ((eq file-notify--library 'w32notify) 'w32notify-rm-watch)) + descriptor)) + (file-notify-error nil))) + ;; Modify `file-notify-descriptors' and send a `stopped' event. + (file-notify--rm-descriptor descriptor)))) (defun file-notify-valid-p (descriptor) "Check a watch specified by its DESCRIPTOR. -- 2.39.2