mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2024-12-02 08:22:22 +00:00
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.
This commit is contained in:
parent
13f3508443
commit
65ba5a98d4
@ -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)
|
||||
|
81
src/kqueue.c
81
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))
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user