Handle watching of several files in the same directory for inotify.

Fixes: debbugs:18880

* filenotify.el (file-notify-descriptors, file-notify-handle-event):
Adapt docstring.
(file-notify--descriptor): New defun.
(file-notify-callback, file-notify-add-watch, file-notify-rm-watch):
Adapt docstring.  Handle multiple values for
`file-notify-descriptors' entries.

* net/tramp.el (tramp-handle-file-notify-rm-watch): Do not check
`file-notify-descriptors', the implementation has been changed.
This commit is contained in:
Michael Albinus 2015-01-13 11:26:39 +01:00
parent 67edddfce6
commit b1ea160728
3 changed files with 213 additions and 155 deletions

View file

@ -1,3 +1,15 @@
2015-01-13 Michael Albinus <michael.albinus@gmx.de>
* filenotify.el (file-notify-descriptors, file-notify-handle-event):
Adapt docstring.
(file-notify--descriptor): New defun.
(file-notify-callback, file-notify-add-watch, file-notify-rm-watch):
Adapt docstring. Handle multiple values for
`file-notify-descriptors' entries. (Bug#18880)
* net/tramp.el (tramp-handle-file-notify-rm-watch): Do not check
`file-notify-descriptors', the implementation has been changed.
2015-01-13 Juri Linkov <juri@linkov.net> 2015-01-13 Juri Linkov <juri@linkov.net>
* comint.el (comint-history-isearch-search) * comint.el (comint-history-isearch-search)

View file

@ -41,13 +41,21 @@ could use another implementation.")
"Hash table for registered file notification descriptors. "Hash table for registered file notification descriptors.
A key in this hash table is the descriptor as returned from A key in this hash table is the descriptor as returned from
`gfilenotify', `inotify', `w32notify' or a file name handler. `gfilenotify', `inotify', `w32notify' or a file name handler.
The value in the hash table is the cons cell (DIR FILE CALLBACK).") The value in the hash table is a list
\(DIR (FILE . CALLBACK) (FILE . CALLBACK) ...)
Several values for a given DIR happen only for `inotify', when
different files from the same directory are watched.")
;; This function is used by `gfilenotify', `inotify' and `w32notify' events. ;; This function is used by `gfilenotify', `inotify' and `w32notify' events.
;;;###autoload ;;;###autoload
(defun file-notify-handle-event (event) (defun file-notify-handle-event (event)
"Handle file system monitoring event. "Handle file system monitoring event.
If EVENT is a filewatch event, call its callback. If EVENT is a filewatch event, call its callback. It has the format
\(file-notify (DESCRIPTOR ACTIONS FILE COOKIE) CALLBACK)
Otherwise, signal a `file-notify-error'." Otherwise, signal a `file-notify-error'."
(interactive "e") (interactive "e")
(if (and (eq (car event) 'file-notify) (if (and (eq (car event) 'file-notify)
@ -81,12 +89,23 @@ This is available in case a file has been moved."
This is available in case a file has been moved." This is available in case a file has been moved."
(nth 3 event)) (nth 3 event))
;; `inotify' returns the same descriptor when the file (directory)
;; uses the same inode. We want to distinguish, and apply a virtual
;; descriptor which make the difference.
(defun file-notify--descriptor (descriptor file)
"Return the descriptor to be used in `file-notify-*-watch'.
For `gfilenotify' and `w32notify' it is the same descriptor as
used in the low-level file notification package."
(if (eq file-notify--library 'inotify)
(cons descriptor file)
descriptor))
;; The callback function used to map between specific flags of the ;; The callback function used to map between specific flags of the
;; respective file notifications, and the ones we return. ;; respective file notifications, and the ones we return.
(defun file-notify-callback (event) (defun file-notify-callback (event)
"Handle an EVENT returned from file notification. "Handle an EVENT returned from file notification.
EVENT is the same one as in `file-notify-handle-event' except the EVENT is the cdr of the event in `file-notify-handle-event'
car of that event, which is the symbol `file-notify'." \(DESCRIPTOR ACTIONS FILE COOKIE)."
(let* ((desc (car event)) (let* ((desc (car event))
(registered (gethash desc file-notify-descriptors)) (registered (gethash desc file-notify-descriptors))
(pending-event (assoc desc file-notify--pending-events)) (pending-event (assoc desc file-notify--pending-events))
@ -97,8 +116,12 @@ car of that event, which is the symbol `file-notify'."
;; Make actions a list. ;; Make actions a list.
(unless (consp actions) (setq actions (cons actions nil))) (unless (consp actions) (setq actions (cons actions nil)))
;; Loop over registered entries. In fact, more than one entry
;; happens only for `inotify'.
(dolist (entry (cdr registered))
;; Check, that event is meant for us. ;; Check, that event is meant for us.
(unless (setq callback (nth 2 registered)) (unless (setq callback (cdr entry))
(setq actions nil)) (setq actions nil))
;; Loop over actions. In fact, more than one action happens only ;; Loop over actions. In fact, more than one action happens only
@ -127,7 +150,8 @@ car of that event, which is the symbol `file-notify'."
(setq action (setq action
(cond (cond
;; gfilenotify. ;; gfilenotify.
((memq action '(attribute-changed changed created deleted)) action) ((memq action '(attribute-changed changed created deleted))
action)
((eq action 'moved) ((eq action 'moved)
(setq file1 (file-notify--event-file1-name event)) (setq file1 (file-notify--event-file1-name event))
'renamed) 'renamed)
@ -158,13 +182,13 @@ car of that event, which is the symbol `file-notify'."
((eq action 'modified) 'changed) ((eq action 'modified) 'changed)
((eq action 'removed) 'deleted) ((eq action 'removed) 'deleted)
;; Make the event pending. ;; Make the event pending.
((eq 'renamed-from action) ((eq action 'renamed-from)
(add-to-list 'file-notify--pending-events (add-to-list 'file-notify--pending-events
(list desc action file (list desc action file
(file-notify--event-cookie event))) (file-notify--event-cookie event)))
nil) nil)
;; Look for pending event. ;; Look for pending event.
((eq 'renamed-to action) ((eq action 'renamed-to)
(if (null pending-event) (if (null pending-event)
'created 'created
(setq file1 file (setq file1 file
@ -178,18 +202,27 @@ car of that event, which is the symbol `file-notify'."
(or (or
;; If there is no relative file name for that watch, ;; If there is no relative file name for that watch,
;; we watch the whole directory. ;; we watch the whole directory.
(null (nth 1 registered)) (null (nth 0 entry))
;; File matches. ;; File matches.
(string-equal (string-equal
(nth 1 registered) (file-name-nondirectory file)) (nth 0 entry) (file-name-nondirectory file))
;; File1 matches. ;; File1 matches.
(and (stringp file1) (and (stringp file1)
(string-equal (string-equal
(nth 1 registered) (file-name-nondirectory file1))))) (nth 0 entry) (file-name-nondirectory file1)))))
(if file1 (if file1
(funcall callback (list desc action file file1)) (funcall
(funcall callback (list desc action file))))))) callback
`(,(file-notify--descriptor desc (nth 0 entry))
,action ,file ,file1))
(funcall
callback
`(,(file-notify--descriptor desc (nth 0 entry))
,action ,file))))))))
;; `gfilenotify' and `w32notify' return a unique descriptor for every
;; `file-notify-add-watch', while `inotify' returns a unique
;; descriptor per inode only.
(defun file-notify-add-watch (file flags callback) (defun file-notify-add-watch (file flags callback)
"Add a watch for filesystem events pertaining to FILE. "Add a watch for filesystem events pertaining to FILE.
This arranges for filesystem events pertaining to FILE to be reported This arranges for filesystem events pertaining to FILE to be reported
@ -206,7 +239,7 @@ include the following symbols:
`attribute-change' -- watch for file attributes changes, like `attribute-change' -- watch for file attributes changes, like
permissions or modification time permissions or modification time
If FILE is a directory, 'change' watches for file creation or If FILE is a directory, `change' watches for file creation or
deletion in that directory. This does not work recursively. deletion in that directory. This does not work recursively.
When any event happens, Emacs will call the CALLBACK function passing When any event happens, Emacs will call the CALLBACK function passing
@ -240,15 +273,8 @@ FILE is the name of the file whose event is being reported."
(if (file-directory-p file) (if (file-directory-p file)
file file
(file-name-directory file)))) (file-name-directory file))))
desc func l-flags) desc func l-flags registered)
;; Check, whether this has been registered already.
; (maphash
; (lambda (key value)
; (when (equal (cons file callback) value) (setq desc key)))
; file-notify-descriptors)
(unless desc
(if handler (if handler
;; A file name handler could exist even if there is no local ;; A file name handler could exist even if there is no local
;; file notification support. ;; file notification support.
@ -286,36 +312,57 @@ FILE is the name of the file whose event is being reported."
((eq file-notify--library 'w32notify) 'attributes))))) ((eq file-notify--library 'w32notify) 'attributes)))))
;; Call low-level function. ;; Call low-level function.
(setq desc (funcall func dir l-flags 'file-notify-callback)))) (setq desc (funcall func dir l-flags 'file-notify-callback)))
;; Modify `file-notify-descriptors'.
(setq registered (gethash desc file-notify-descriptors))
(puthash
desc
`(,dir
(,(unless (file-directory-p file) (file-name-nondirectory file))
. ,callback)
. ,(cdr registered))
file-notify-descriptors)
;; Return descriptor. ;; Return descriptor.
(puthash desc (file-notify--descriptor
(list (directory-file-name desc (unless (file-directory-p file) (file-name-nondirectory file)))))
(if (file-directory-p dir) dir (file-name-directory dir)))
(unless (file-directory-p file)
(file-name-nondirectory file))
callback)
file-notify-descriptors)
desc))
(defun file-notify-rm-watch (descriptor) (defun file-notify-rm-watch (descriptor)
"Remove an existing watch specified by its DESCRIPTOR. "Remove an existing watch specified by its DESCRIPTOR.
DESCRIPTOR should be an object returned by `file-notify-add-watch'." DESCRIPTOR should be an object returned by `file-notify-add-watch'."
(let ((file (car (gethash descriptor file-notify-descriptors))) (let* ((desc (if (consp descriptor) (car descriptor) descriptor))
handler) (file (if (consp descriptor) (cdr descriptor)))
(dir (car (gethash desc file-notify-descriptors)))
handler registered)
(when (stringp file) (when (stringp dir)
(setq handler (find-file-name-handler file 'file-notify-rm-watch)) (setq handler (find-file-name-handler dir 'file-notify-rm-watch))
;; Modify `file-notify-descriptors'.
(if (not file)
(remhash desc file-notify-descriptors)
(setq registered (gethash desc file-notify-descriptors))
(setcdr registered
(delete (assoc file (cdr registered)) (cdr registered)))
(if (null (cdr registered))
(remhash desc file-notify-descriptors)
(puthash desc registered file-notify-descriptors)))
;; Call low-level function.
(when (null (cdr registered))
(if handler (if handler
(funcall handler 'file-notify-rm-watch descriptor) ;; A file name handler could exist even if there is no local
;; file notification support.
(funcall handler 'file-notify-rm-watch desc)
(funcall (funcall
(cond (cond
((eq file-notify--library 'gfilenotify) 'gfile-rm-watch) ((eq file-notify--library 'gfilenotify) 'gfile-rm-watch)
((eq file-notify--library 'inotify) 'inotify-rm-watch) ((eq file-notify--library 'inotify) 'inotify-rm-watch)
((eq file-notify--library 'w32notify) 'w32notify-rm-watch)) ((eq file-notify--library 'w32notify) 'w32notify-rm-watch))
descriptor))) desc))))))
(remhash descriptor file-notify-descriptors)))
;; The end: ;; The end:
(provide 'filenotify) (provide 'filenotify)

View file

@ -64,7 +64,6 @@
(defvar bkup-backup-directory-info) (defvar bkup-backup-directory-info)
(defvar directory-sep-char) (defvar directory-sep-char)
(defvar eshell-path-env) (defvar eshell-path-env)
(defvar file-notify-descriptors)
(defvar ls-lisp-use-insert-directory-program) (defvar ls-lisp-use-insert-directory-program)
(defvar outline-regexp) (defvar outline-regexp)
@ -3415,7 +3414,7 @@ of."
(defun tramp-handle-file-notify-rm-watch (proc) (defun tramp-handle-file-notify-rm-watch (proc)
"Like `file-notify-rm-watch' for Tramp files." "Like `file-notify-rm-watch' for Tramp files."
;; The descriptor must be a process object. ;; The descriptor must be a process object.
(unless (and (processp proc) (gethash proc file-notify-descriptors)) (unless (processp proc)
(tramp-error proc 'file-notify-error "Not a valid descriptor %S" proc)) (tramp-error proc 'file-notify-error "Not a valid descriptor %S" proc))
(tramp-message proc 6 "Kill %S" proc) (tramp-message proc 6 "Kill %S" proc)
(kill-process proc)) (kill-process proc))