From: Michael Albinus Date: Wed, 18 Nov 2015 13:47:25 +0000 (+0000) Subject: Further fixes for kqueue. X-Git-Url: https://code.delx.au/gnu-emacs/commitdiff_plain/65ba5a98d47f4305f95f960efdf424684754a11d Further fixes for kqueue. * lisp/filenotify.el (file-notify-callback): Raise also event if directory name matches. (file-notify-add-watch): Add `create' to the flags for `kqueue'. * src/kqueue.c (kqueue_generate_event): Use watch_object as argument instead of ident. Remove callback argument. Adapt callees. Check actions whether they are monitored flags. * test/automated/file-notify-tests.el (file-notify--test-library): New defun. (file-notify-test00-availability, file-notify-test02-events) (file-notify-test04-file-validity) (file-notify-test05-dir-validity): Use it. (file-notify-test02-events, file-notify-test04-file-validity): Add `read-event' calls between different file actions, in order to give the backends a chance to rais an event. Needed especially for kqueue. In case of deleting a directory, there are two `deleted' events. --- diff --git a/lisp/filenotify.el b/lisp/filenotify.el index eb869cf66a..5072bf414b 100644 --- a/lisp/filenotify.el +++ b/lisp/filenotify.el @@ -258,6 +258,10 @@ EVENT is the cadr of the event in `file-notify-handle-event' ;; File matches. (string-equal (nth 0 entry) (file-name-nondirectory file)) + ;; Directory matches. + (string-equal + (file-name-nondirectory file) + (file-name-nondirectory (car registered))) ;; File1 matches. (and (stringp file1) (string-equal @@ -364,7 +368,7 @@ FILE is the name of the file whose event is being reported." ((eq file-notify--library 'inotify) '(create delete delete-self modify move-self move)) ((eq file-notify--library 'kqueue) - '(delete write extend rename)) + '(create delete write extend rename)) ((eq file-notify--library 'w32notify) '(file-name directory-name size last-write-time))))) (when (memq 'attribute-change flags) diff --git a/src/kqueue.c b/src/kqueue.c index 5caef67b92..e2c9dabcb2 100644 --- a/src/kqueue.c +++ b/src/kqueue.c @@ -67,21 +67,39 @@ kqueue_directory_listing (Lisp_Object directory_files) /* Generate a file notification event. */ static void kqueue_generate_event -(Lisp_Object ident, Lisp_Object actions, Lisp_Object file, Lisp_Object file1, - Lisp_Object callback) +(Lisp_Object watch_object, Lisp_Object actions, + Lisp_Object file, Lisp_Object file1) { + Lisp_Object flags, action, entry; struct input_event event; - EVENT_INIT (event); - event.kind = FILE_NOTIFY_EVENT; - event.frame_or_window = Qnil; - event.arg = list2 (Fcons (ident, Fcons (actions, - NILP (file1) - ? Fcons (file, Qnil) - : list2 (file, file1))), - callback); + + /* Check, whether all actions shall be monitored. */ + flags = Fnth (make_number (2), watch_object); + action = actions; + do { + if (NILP (action)) + break; + entry = XCAR (action); + if (NILP (Fmember (entry, flags))) { + action = XCDR (action); + actions = Fdelq (entry, actions); + } else + action = XCDR (action); + } while (1); /* Store it into the input event queue. */ - kbd_buffer_store_event (&event); + if (! NILP (actions)) { + EVENT_INIT (event); + event.kind = FILE_NOTIFY_EVENT; + event.frame_or_window = Qnil; + event.arg = list2 (Fcons (XCAR (watch_object), + Fcons (actions, + NILP (file1) + ? Fcons (file, Qnil) + : list2 (file, file1))), + Fnth (make_number (3), watch_object)); + kbd_buffer_store_event (&event); + } } /* This compares two directory listings in case of a `write' event for @@ -93,19 +111,16 @@ static void kqueue_compare_dir_list (Lisp_Object watch_object) { - Lisp_Object dir, callback; - Lisp_Object old_directory_files, old_dl, new_directory_files, new_dl, dl; + Lisp_Object dir, old_directory_files, old_dl, new_directory_files, new_dl, dl; dir = XCAR (XCDR (watch_object)); - callback = Fnth (make_number (3), watch_object); old_directory_files = Fnth (make_number (4), watch_object); old_dl = kqueue_directory_listing (old_directory_files); /* When the directory is not accessible anymore, it has been deleted. */ if (NILP (Ffile_directory_p (dir))) { - kqueue_generate_event - (XCAR (watch_object), Fcons (Qdelete, Qnil), dir, Qnil, callback); + kqueue_generate_event (watch_object, Fcons (Qdelete, Qnil), dir, Qnil); return; } new_directory_files = @@ -137,21 +152,20 @@ kqueue_compare_dir_list if (NILP (Fequal (Fnth (make_number (2), old_entry), Fnth (make_number (2), new_entry)))) kqueue_generate_event - (XCAR (watch_object), Fcons (Qwrite, Qnil), - XCAR (XCDR (old_entry)), Qnil, callback); + (watch_object, Fcons (Qwrite, Qnil), XCAR (XCDR (old_entry)), Qnil); /* Status change time has been changed, the file attributes have changed. */ if (NILP (Fequal (Fnth (make_number (3), old_entry), Fnth (make_number (3), new_entry)))) kqueue_generate_event - (XCAR (watch_object), Fcons (Qattrib, Qnil), - XCAR (XCDR (old_entry)), Qnil, callback); + (watch_object, Fcons (Qattrib, Qnil), + XCAR (XCDR (old_entry)), Qnil); } else { /* The file has been renamed. */ kqueue_generate_event - (XCAR (watch_object), Fcons (Qrename, Qnil), - XCAR (XCDR (old_entry)), XCAR (XCDR (new_entry)), callback); + (watch_object, Fcons (Qrename, Qnil), + XCAR (XCDR (old_entry)), XCAR (XCDR (new_entry))); } new_dl = Fdelq (new_entry, new_dl); goto the_end; @@ -164,8 +178,7 @@ kqueue_compare_dir_list if (strcmp (SSDATA (XCAR (XCDR (old_entry))), SSDATA (XCAR (XCDR (new_entry)))) == 0) { kqueue_generate_event - (XCAR (watch_object), Fcons (Qwrite, Qnil), - XCAR (XCDR (old_entry)), Qnil, callback); + (watch_object, Fcons (Qwrite, Qnil), XCAR (XCDR (old_entry)), Qnil); new_dl = Fdelq (new_entry, new_dl); goto the_end; } @@ -173,8 +186,7 @@ kqueue_compare_dir_list /* The file has been deleted. */ kqueue_generate_event - (XCAR (watch_object), Fcons (Qdelete, Qnil), - XCAR (XCDR (old_entry)), Qnil, callback); + (watch_object, Fcons (Qdelete, Qnil), XCAR (XCDR (old_entry)), Qnil); the_end: dl = XCDR (dl); @@ -191,15 +203,13 @@ kqueue_compare_dir_list /* A new file has appeared. */ new_entry = XCAR (dl); kqueue_generate_event - (XCAR (watch_object), Fcons (Qcreate, Qnil), - XCAR (XCDR (new_entry)), Qnil, callback); + (watch_object, Fcons (Qcreate, Qnil), XCAR (XCDR (new_entry)), Qnil); /* Check size of that file. */ Lisp_Object size = Fnth (make_number (4), new_entry); if (FLOATP (size) || (XINT (size) > 0)) kqueue_generate_event - (XCAR (watch_object), Fcons (Qwrite, Qnil), - XCAR (XCDR (new_entry)), Qnil, callback); + (watch_object, Fcons (Qwrite, Qnil), XCAR (XCDR (new_entry)), Qnil); dl = XCDR (dl); new_dl = Fdelq (new_entry, new_dl); @@ -226,7 +236,7 @@ kqueue_callback (int fd, void *data) for (;;) { struct kevent kev; static const struct timespec nullts = { 0, 0 }; - Lisp_Object descriptor, watch_object, file, callback, actions; + Lisp_Object descriptor, watch_object, file, actions; /* Read one event. */ int ret = kevent (kqueuefd, NULL, 0, &kev, 1, &nullts); @@ -235,14 +245,11 @@ kqueue_callback (int fd, void *data) return; } - /* Determine descriptor, file name and callback function. */ + /* Determine descriptor and file name. */ descriptor = make_number (kev.ident); watch_object = assq_no_quit (descriptor, watch_list); - - if (CONSP (watch_object)) { + if (CONSP (watch_object)) file = XCAR (XCDR (watch_object)); - callback = Fnth (make_number (3), watch_object); - } else continue; @@ -271,7 +278,7 @@ kqueue_callback (int fd, void *data) /* Create the event. */ if (! NILP (actions)) - kqueue_generate_event (descriptor, actions, file, Qnil, callback); + kqueue_generate_event (watch_object, actions, file, Qnil); /* Cancel monitor if file or directory is deleted or renamed. */ if (kev.fflags & (NOTE_DELETE | NOTE_RENAME)) diff --git a/test/automated/file-notify-tests.el b/test/automated/file-notify-tests.el index 67e929a647..6946541b90 100644 --- a/test/automated/file-notify-tests.el +++ b/test/automated/file-notify-tests.el @@ -133,6 +133,18 @@ being the result.") ;; Return result. (cdr file-notify--test-remote-enabled-checked)) +(defun file-notify--test-library () + "The used libray for the test, as string. +In the remote case, it is the process name which runs on the +remote host, or nil." + (if (null (file-remote-p temporary-file-directory)) + (symbol-name file-notify--library) + (and (consp file-notify--test-remote-enabled-checked) + (processp (cdr file-notify--test-remote-enabled-checked)) + (replace-regexp-in-string + "<[[:digit:]]+>\\'" "" + (process-name (cdr file-notify--test-remote-enabled-checked)))))) + (defmacro file-notify--deftest-remote (test docstring) "Define ert `TEST-remote' for remote files." (declare (indent 1)) @@ -150,12 +162,7 @@ being the result.") "Test availability of `file-notify'." (skip-unless (file-notify--test-local-enabled)) ;; Report the native library which has been used. - (if (null (file-remote-p temporary-file-directory)) - (message "Local library: `%s'" file-notify--library) - (message "Remote command: `%s'" - (replace-regexp-in-string - "<[[:digit:]]+>\\'" "" - (process-name (cdr file-notify--test-remote-enabled-checked))))) + (message "Library: `%s'" (file-notify--test-library)) (should (setq file-notify--test-desc (file-notify-add-watch temporary-file-directory '(change) 'ignore))) @@ -311,6 +318,7 @@ Don't wait longer than timeout seconds for the events to be delivered." (file-notify--test-with-events '(created changed deleted) (write-region "any text" nil file-notify--test-tmpfile nil 'no-message) + (read-event nil nil 0.1) (delete-file file-notify--test-tmpfile)) ;; `file-notify-rm-watch' fires the `stopped' event. Suppress it. (let (file-notify--test-events) @@ -319,7 +327,7 @@ Don't wait longer than timeout seconds for the events to be delivered." ;; Check creation, change and deletion. There must be a ;; `stopped' event when deleting the directory. It doesn't ;; work for w32notify. - (unless (eq file-notify--library 'w32notify) + (unless (string-equal (file-notify--test-library) "w32notify") (make-directory file-notify--test-tmpfile) (setq file-notify--test-desc (file-notify-add-watch @@ -327,11 +335,14 @@ Don't wait longer than timeout seconds for the events to be delivered." '(change) 'file-notify--test-event-handler)) (file-notify--test-with-events ;; There are two `deleted' events, for the file and for - ;; the directory. - '(created changed deleted deleted stopped) + ;; the directory. Except for kqueue. + (if (string-equal (file-notify--test-library) "kqueue") + '(created changed deleted stopped) + '(created changed deleted deleted stopped)) (write-region "any text" nil (expand-file-name "foo" file-notify--test-tmpfile) nil 'no-message) + (read-event nil nil 0.1) (delete-directory file-notify--test-tmpfile 'recursive)) ;; `file-notify-rm-watch' fires the `stopped' event. Suppress it. (let (file-notify--test-events) @@ -346,17 +357,21 @@ Don't wait longer than timeout seconds for the events to be delivered." (file-notify--test-with-events ;; w32notify does not distinguish between `changed' and ;; `attribute-changed'. - (if (eq file-notify--library 'w32notify) + (if (string-equal (file-notify--test-library) "w32notify") '(created changed changed deleted) '(created changed deleted)) (write-region "any text" nil file-notify--test-tmpfile nil 'no-message) + (read-event nil nil 0.1) (copy-file file-notify--test-tmpfile file-notify--test-tmpfile1) ;; The next two events shall not be visible. + (read-event nil nil 0.1) (set-file-modes file-notify--test-tmpfile 000) - (read-event nil nil 0.1) ; In order to distinguish the events. + (read-event nil nil 0.1) (set-file-times file-notify--test-tmpfile '(0 0)) + (read-event nil nil 0.1) (delete-file file-notify--test-tmpfile) + (read-event nil nil 0.1) (delete-file file-notify--test-tmpfile1)) ;; `file-notify-rm-watch' fires the `stopped' event. Suppress it. (let (file-notify--test-events) @@ -371,15 +386,18 @@ Don't wait longer than timeout seconds for the events to be delivered." (file-notify--test-with-events '(created changed renamed) (write-region "any text" nil file-notify--test-tmpfile nil 'no-message) + (read-event nil nil 0.1) (rename-file file-notify--test-tmpfile file-notify--test-tmpfile1) ;; After the rename, we won't get events anymore. + (read-event nil nil 0.1) (delete-file file-notify--test-tmpfile1)) ;; `file-notify-rm-watch' fires the `stopped' event. Suppress it. (let (file-notify--test-events) (file-notify-rm-watch file-notify--test-desc)) - ;; Check attribute change. It doesn't work for w32notify. - (unless (eq file-notify--library 'w32notify) + ;; Check attribute change. It doesn't work for kqueue and w32notify. + (unless (or (string-equal (file-notify--test-library) "kqueue") + (string-equal (file-notify--test-library) "w32notify")) (setq file-notify--test-desc (file-notify-add-watch file-notify--test-tmpfile @@ -523,6 +541,7 @@ Don't wait longer than timeout seconds for the events to be delivered." (should (file-notify-valid-p file-notify--test-desc)) (write-region "any text" nil file-notify--test-tmpfile nil 'no-message) + (read-event nil nil 0.1) (delete-file file-notify--test-tmpfile)) ;; After deleting the file, the descriptor is still valid. (should (file-notify-valid-p file-notify--test-desc)) @@ -537,8 +556,7 @@ Don't wait longer than timeout seconds for the events to be delivered." (unwind-protect ;; The batch-mode operation of w32notify is fragile (there's no ;; input threads to send the message to). - ;(unless (and noninteractive (eq file-notify--library 'w32notify)) - (unless (eq file-notify--library 'w32notify) + (unless (string-equal (file-notify--test-library) "w32notify") (let ((temporary-file-directory (make-temp-file "file-notify-test-parent" t))) (setq file-notify--test-tmpfile (file-notify--test-make-temp-name) @@ -546,10 +564,16 @@ Don't wait longer than timeout seconds for the events to be delivered." (file-notify-add-watch file-notify--test-tmpfile '(change) #'file-notify--test-event-handler)) - (file-notify--test-with-events '(created changed deleted stopped) + (file-notify--test-with-events + ;; There are two `deleted' events, for the file and for + ;; the directory. Except for kqueue. + (if (string-equal (file-notify--test-library) "kqueue") + '(created changed deleted stopped) + '(created changed deleted deleted stopped)) (should (file-notify-valid-p file-notify--test-desc)) (write-region "any text" nil file-notify--test-tmpfile nil 'no-message) + (read-event nil nil 0.1) (delete-directory temporary-file-directory t)) ;; After deleting the parent directory, the descriptor must ;; not be valid anymore. @@ -589,7 +613,8 @@ Don't wait longer than timeout seconds for the events to be delivered." (unwind-protect ;; The batch-mode operation of w32notify is fragile (there's no ;; input threads to send the message to). - (unless (and noninteractive (eq file-notify--library 'w32notify)) + (unless (and noninteractive + (string-equal (file-notify--test-library) "w32notify")) (setq file-notify--test-tmpfile (file-name-as-directory (file-notify--test-make-temp-name))) (make-directory file-notify--test-tmpfile)