diff options
| author | Michael Albinus | 2015-11-15 17:45:32 +0000 |
|---|---|---|
| committer | Michael Albinus | 2015-11-25 15:07:11 +0100 |
| commit | e95b309ae4f5fbdebdb7067daca9d091925047cf (patch) | |
| tree | e2671f96cbc0e803ee927af06c3781eb03c52788 | |
| parent | 41d9bd0c3b19d839b72fdd20e613cb6ab3b1b1f4 (diff) | |
| download | emacs-e95b309ae4f5fbdebdb7067daca9d091925047cf.tar.gz emacs-e95b309ae4f5fbdebdb7067daca9d091925047cf.zip | |
More work on kqueue
* lisp/filenotify.el (file-notify-callback): Handle also the
`rename' event from kqueue.
(file-notify-add-watch): Do not register an entry twice.
* src/kqueue.c (kqueue_directory_listing): New function.
(kqueue_generate_event): New argument FILE1. Adapt callees.
(kqueue_compare_dir_list): Rewrite in order to make it more robust.
| -rw-r--r-- | lisp/filenotify.el | 21 | ||||
| -rw-r--r-- | src/kqueue.c | 190 |
2 files changed, 149 insertions, 62 deletions
diff --git a/lisp/filenotify.el b/lisp/filenotify.el index f7c97569825..23029427760 100644 --- a/lisp/filenotify.el +++ b/lisp/filenotify.el | |||
| @@ -189,7 +189,7 @@ EVENT is the cadr of the event in `file-notify-handle-event' | |||
| 189 | ((memq action | 189 | ((memq action |
| 190 | '(attribute-changed changed created deleted renamed)) | 190 | '(attribute-changed changed created deleted renamed)) |
| 191 | action) | 191 | action) |
| 192 | ((eq action 'moved) | 192 | ((memq action '(moved rename)) |
| 193 | (setq file1 (file-notify--event-file1-name event)) | 193 | (setq file1 (file-notify--event-file1-name event)) |
| 194 | 'renamed) | 194 | 'renamed) |
| 195 | ((eq action 'ignored) | 195 | ((eq action 'ignored) |
| @@ -329,7 +329,7 @@ FILE is the name of the file whose event is being reported." | |||
| 329 | (if (file-directory-p file) | 329 | (if (file-directory-p file) |
| 330 | file | 330 | file |
| 331 | (file-name-directory file)))) | 331 | (file-name-directory file)))) |
| 332 | desc func l-flags registered) | 332 | desc func l-flags registered entry) |
| 333 | 333 | ||
| 334 | (unless (file-directory-p dir) | 334 | (unless (file-directory-p dir) |
| 335 | (signal 'file-notify-error `("Directory does not exist" ,dir))) | 335 | (signal 'file-notify-error `("Directory does not exist" ,dir))) |
| @@ -378,18 +378,15 @@ FILE is the name of the file whose event is being reported." | |||
| 378 | (setq desc (funcall func dir l-flags 'file-notify-callback))) | 378 | (setq desc (funcall func dir l-flags 'file-notify-callback))) |
| 379 | 379 | ||
| 380 | ;; Modify `file-notify-descriptors'. | 380 | ;; Modify `file-notify-descriptors'. |
| 381 | (setq registered (gethash desc file-notify-descriptors)) | 381 | (setq file (unless (file-directory-p file) (file-name-nondirectory file)) |
| 382 | (puthash | 382 | desc (file-notify--descriptor desc file) |
| 383 | desc | 383 | registered (gethash desc file-notify-descriptors) |
| 384 | `(,dir | 384 | entry `(,file . ,callback)) |
| 385 | (,(unless (file-directory-p file) (file-name-nondirectory file)) | 385 | (unless (member entry (cdr registered)) |
| 386 | . ,callback) | 386 | (puthash desc `(,dir ,entry . ,(cdr registered)) file-notify-descriptors)) |
| 387 | . ,(cdr registered)) | ||
| 388 | file-notify-descriptors) | ||
| 389 | 387 | ||
| 390 | ;; Return descriptor. | 388 | ;; Return descriptor. |
| 391 | (file-notify--descriptor | 389 | desc)) |
| 392 | desc (unless (file-directory-p file) (file-name-nondirectory file))))) | ||
| 393 | 390 | ||
| 394 | (defun file-notify-rm-watch (descriptor) | 391 | (defun file-notify-rm-watch (descriptor) |
| 395 | "Remove an existing watch specified by its DESCRIPTOR. | 392 | "Remove an existing watch specified by its DESCRIPTOR. |
diff --git a/src/kqueue.c b/src/kqueue.c index 0425a142a98..2097b7ed492 100644 --- a/src/kqueue.c +++ b/src/kqueue.c | |||
| @@ -35,16 +35,42 @@ static int kqueuefd = -1; | |||
| 35 | /* This is a list, elements are (DESCRIPTOR FILE FLAGS CALLBACK [DIRLIST]) */ | 35 | /* This is a list, elements are (DESCRIPTOR FILE FLAGS CALLBACK [DIRLIST]) */ |
| 36 | static Lisp_Object watch_list; | 36 | static Lisp_Object watch_list; |
| 37 | 37 | ||
| 38 | /* Generate a temporary list from the directory_files_internal output. | ||
| 39 | Items are (INODE FILE_NAME LAST_MOD LAST_STATUS_MOD SIZE). */ | ||
| 40 | Lisp_Object | ||
| 41 | kqueue_directory_listing (Lisp_Object directory_files) | ||
| 42 | { | ||
| 43 | Lisp_Object dl, result = Qnil; | ||
| 44 | for (dl = directory_files; ! NILP (dl); dl = XCDR (dl)) { | ||
| 45 | result = Fcons | ||
| 46 | (list5 (/* inode. */ | ||
| 47 | XCAR (Fnthcdr (make_number (11), XCAR (dl))), | ||
| 48 | /* filename. */ | ||
| 49 | XCAR (XCAR (dl)), | ||
| 50 | /* last modification time. */ | ||
| 51 | XCAR (Fnthcdr (make_number (6), XCAR (dl))), | ||
| 52 | /* last status change time. */ | ||
| 53 | XCAR (Fnthcdr (make_number (7), XCAR (dl))), | ||
| 54 | /* size. */ | ||
| 55 | XCAR (Fnthcdr (make_number (8), XCAR (dl)))), | ||
| 56 | result); | ||
| 57 | } | ||
| 58 | return result; | ||
| 59 | } | ||
| 60 | |||
| 38 | /* Generate a file notification event. */ | 61 | /* Generate a file notification event. */ |
| 39 | static void | 62 | static void |
| 40 | kqueue_generate_event | 63 | kqueue_generate_event |
| 41 | (Lisp_Object ident, Lisp_Object actions, Lisp_Object file, Lisp_Object callback) | 64 | (Lisp_Object ident, Lisp_Object actions, Lisp_Object file, Lisp_Object file1, Lisp_Object callback) |
| 42 | { | 65 | { |
| 43 | struct input_event event; | 66 | struct input_event event; |
| 44 | EVENT_INIT (event); | 67 | EVENT_INIT (event); |
| 45 | event.kind = FILE_NOTIFY_EVENT; | 68 | event.kind = FILE_NOTIFY_EVENT; |
| 46 | event.frame_or_window = Qnil; | 69 | event.frame_or_window = Qnil; |
| 47 | event.arg = list2 (Fcons (ident, Fcons (actions, Fcons (file, Qnil))), | 70 | event.arg = list2 (Fcons (ident, Fcons (actions, |
| 71 | NILP (file1) | ||
| 72 | ? Fcons (file, Qnil) | ||
| 73 | : list2 (file, file1))), | ||
| 48 | callback); | 74 | callback); |
| 49 | 75 | ||
| 50 | /* Store it into the input event queue. */ | 76 | /* Store it into the input event queue. */ |
| @@ -53,73 +79,140 @@ kqueue_generate_event | |||
| 53 | 79 | ||
| 54 | /* This compares two directory listings in case of a `write' event for | 80 | /* This compares two directory listings in case of a `write' event for |
| 55 | a directory. The old directory listing is stored in watch_object, | 81 | a directory. The old directory listing is stored in watch_object, |
| 56 | it will be replaced by a new directory listing at the end. */ | 82 | it will be replaced by a new directory listing at the end of this |
| 83 | function. */ | ||
| 57 | static void | 84 | static void |
| 58 | kqueue_compare_dir_list (Lisp_Object watch_object) | 85 | kqueue_compare_dir_list |
| 86 | (Lisp_Object watch_object) | ||
| 59 | { | 87 | { |
| 60 | Lisp_Object dir, callback, old_dl, new_dl, dl, actions; | 88 | Lisp_Object dir, callback, actions; |
| 89 | Lisp_Object old_directory_files, old_dl, new_directory_files, new_dl, dl; | ||
| 61 | 90 | ||
| 62 | dir = XCAR (XCDR (watch_object)); | 91 | dir = XCAR (XCDR (watch_object)); |
| 63 | callback = XCAR (XCDR (XCDR (XCDR (watch_object)))); | 92 | callback = XCAR (Fnthcdr (make_number (3), watch_object)); |
| 64 | old_dl = XCAR (XCDR (XCDR (XCDR (XCDR (watch_object))))); | 93 | old_directory_files = XCAR (Fnthcdr (make_number (4), watch_object)); |
| 65 | new_dl = directory_files_internal (dir, Qnil, Qnil, Qnil, 1, Qnil); | 94 | old_dl = kqueue_directory_listing (old_directory_files); |
| 66 | 95 | new_directory_files = | |
| 67 | for (dl = old_dl; ! NILP (dl); dl = XCDR (dl)) { | 96 | directory_files_internal (dir, Qnil, Qnil, Qnil, 1, Qnil); |
| 97 | new_dl = kqueue_directory_listing (new_directory_files); | ||
| 98 | |||
| 99 | /* Parse through the old list. */ | ||
| 100 | dl = old_dl; | ||
| 101 | while (1) { | ||
| 68 | Lisp_Object old_entry, new_entry; | 102 | Lisp_Object old_entry, new_entry; |
| 69 | old_entry = XCAR (dl); | 103 | if (NILP (dl)) |
| 70 | new_entry = Fassoc (XCAR (old_entry), new_dl); | 104 | break; |
| 71 | 105 | ||
| 72 | /* We ignore "." and "..". */ | 106 | /* We ignore "." and "..". */ |
| 73 | if ((strcmp (".", SSDATA (XCAR (old_entry))) == 0) || | 107 | old_entry = XCAR (dl); |
| 74 | (strcmp ("..", SSDATA (XCAR (old_entry))) == 0)) | 108 | if ((strcmp (".", SSDATA (XCAR (XCDR (old_entry)))) == 0) || |
| 75 | continue; | 109 | (strcmp ("..", SSDATA (XCAR (XCDR (old_entry)))) == 0)) |
| 110 | goto the_end; | ||
| 76 | 111 | ||
| 77 | /* A file has disappeared. */ | 112 | /* Search for an entry with the same inode. */ |
| 78 | if (NILP (new_entry)) | 113 | new_entry = Fassoc (XCAR (old_entry), new_dl); |
| 79 | kqueue_generate_event | 114 | if (! NILP (Fequal (old_entry, new_entry))) { |
| 80 | (XCAR (watch_object), Fcons (Qdelete, Qnil), | 115 | /* Both entries are identical. Nothing happens. */ |
| 81 | XCAR (old_entry), callback); | 116 | new_dl = Fdelq (new_entry, new_dl); |
| 82 | 117 | goto the_end; | |
| 83 | else { | 118 | } |
| 84 | /* A file has changed. We compare last modification time. */ | 119 | |
| 85 | if (NILP | 120 | if (! NILP (new_entry)) { |
| 86 | (Fequal | 121 | /* Both entries have the same inode. */ |
| 87 | (XCAR (XCDR (XCDR (XCDR (XCDR (XCDR (XCDR (old_entry))))))), | 122 | if (strcmp (SSDATA (XCAR (XCDR (old_entry))), |
| 88 | XCAR (XCDR (XCDR (XCDR (XCDR (XCDR (XCDR (new_entry)))))))))) | 123 | SSDATA (XCAR (XCDR (new_entry)))) == 0) { |
| 124 | /* Both entries have the same file name. */ | ||
| 125 | if (! NILP (Fequal (XCAR (Fnthcdr (make_number (2), old_entry)), | ||
| 126 | XCAR (Fnthcdr (make_number (2), new_entry))))) | ||
| 127 | /* Modification time has been changed, the file has been written. */ | ||
| 128 | kqueue_generate_event | ||
| 129 | (XCAR (watch_object), Fcons (Qwrite, Qnil), | ||
| 130 | XCAR (XCDR (old_entry)), Qnil, callback); | ||
| 131 | if (! NILP (Fequal (XCAR (Fnthcdr (make_number (3), old_entry)), | ||
| 132 | XCAR (Fnthcdr (make_number (3), new_entry))))) | ||
| 133 | /* Status change time has been changed, the file attributes | ||
| 134 | have changed. */ | ||
| 135 | kqueue_generate_event | ||
| 136 | (XCAR (watch_object), Fcons (Qattrib, Qnil), | ||
| 137 | XCAR (XCDR (old_entry)), Qnil, callback); | ||
| 138 | |||
| 139 | } else { | ||
| 140 | /* The file has been renamed. */ | ||
| 89 | kqueue_generate_event | 141 | kqueue_generate_event |
| 90 | (XCAR (watch_object), Fcons (Qwrite, Qnil), | 142 | (XCAR (watch_object), Fcons (Qrename, Qnil), |
| 91 | XCAR (old_entry), callback); | 143 | XCAR (XCDR (old_entry)), XCAR (XCDR (new_entry)), callback); |
| 144 | } | ||
| 145 | new_dl = Fdelq (new_entry, new_dl); | ||
| 146 | goto the_end; | ||
| 147 | } | ||
| 92 | 148 | ||
| 93 | /* A file attribute has changed. We compare last status change time. */ | 149 | /* Search, whether there is a file with the same name (with |
| 94 | if (NILP | 150 | another inode). */ |
| 95 | (Fequal | 151 | Lisp_Object dl1; |
| 96 | (XCAR (XCDR (XCDR (XCDR (XCDR (XCDR (XCDR (XCDR (old_entry)))))))), | 152 | for (dl1 = new_dl; ! NILP (dl1); dl1 = XCDR (dl1)) { |
| 97 | XCAR (XCDR (XCDR (XCDR (XCDR (XCDR (XCDR (XCDR (new_entry))))))))))) | 153 | new_entry = XCAR (dl1); |
| 154 | if (strcmp (SSDATA (XCAR (XCDR (old_entry))), | ||
| 155 | SSDATA (XCAR (XCDR (new_entry)))) == 0) { | ||
| 98 | kqueue_generate_event | 156 | kqueue_generate_event |
| 99 | (XCAR (watch_object), Fcons (Qattrib, Qnil), | 157 | (XCAR (watch_object), Fcons (Qwrite, Qnil), |
| 100 | XCAR (old_entry), callback); | 158 | XCAR (XCDR (old_entry)), Qnil, callback); |
| 159 | new_dl = Fdelq (new_entry, new_dl); | ||
| 160 | goto the_end; | ||
| 161 | } | ||
| 101 | } | 162 | } |
| 163 | |||
| 164 | /* A file has been deleted. */ | ||
| 165 | kqueue_generate_event | ||
| 166 | (XCAR (watch_object), Fcons (Qdelete, Qnil), | ||
| 167 | XCAR (XCDR (old_entry)), Qnil, callback); | ||
| 168 | |||
| 169 | the_end: | ||
| 170 | dl = XCDR (dl); | ||
| 171 | old_dl = Fdelq (old_entry, old_dl); | ||
| 102 | } | 172 | } |
| 103 | 173 | ||
| 104 | for (dl = new_dl; ! NILP (dl); dl = XCDR (dl)) { | 174 | /* Parse through the shortened new list. */ |
| 105 | Lisp_Object old_entry, new_entry; | 175 | dl = new_dl; |
| 106 | new_entry = XCAR (dl); | 176 | while (1) { |
| 107 | old_entry = Fassoc (XCAR (new_entry), old_dl); | 177 | Lisp_Object new_entry; |
| 178 | if (NILP (dl)) | ||
| 179 | break; | ||
| 108 | 180 | ||
| 109 | /* We ignore "." and "..". */ | 181 | /* We ignore "." and "..". */ |
| 110 | if ((strcmp (".", SSDATA (XCAR (new_entry))) == 0) || | 182 | new_entry = XCAR (dl); |
| 111 | (strcmp ("..", SSDATA (XCAR (new_entry))) == 0)) | 183 | if ((strcmp (".", SSDATA (XCAR (XCDR (new_entry)))) == 0) || |
| 184 | (strcmp ("..", SSDATA (XCAR (XCDR (new_entry)))) == 0)) { | ||
| 185 | dl = XCDR (dl); | ||
| 186 | new_dl = Fdelq (new_entry, new_dl); | ||
| 112 | continue; | 187 | continue; |
| 188 | } | ||
| 113 | 189 | ||
| 114 | /* A new file has appeared. */ | 190 | /* A new file has appeared. */ |
| 115 | if (NILP (old_entry)) | 191 | kqueue_generate_event |
| 192 | (XCAR (watch_object), Fcons (Qcreate, Qnil), | ||
| 193 | XCAR (XCDR (new_entry)), Qnil, callback); | ||
| 194 | |||
| 195 | /* Check size of that file. */ | ||
| 196 | Lisp_Object size = XCAR (Fnthcdr (make_number (4), new_entry)); | ||
| 197 | if (FLOATP (size) || (XINT (size) > 0)) | ||
| 116 | kqueue_generate_event | 198 | kqueue_generate_event |
| 117 | (XCAR (watch_object), Fcons (Qcreate, Qnil), | 199 | (XCAR (watch_object), Fcons (Qwrite, Qnil), |
| 118 | XCAR (new_entry), callback); | 200 | XCAR (XCDR (new_entry)), Qnil, callback); |
| 201 | |||
| 202 | dl = XCDR (dl); | ||
| 203 | new_dl = Fdelq (new_entry, new_dl); | ||
| 119 | } | 204 | } |
| 120 | 205 | ||
| 206 | /* At this point, both old_dl and new_dl shall be empty. Let's make | ||
| 207 | a check for this (might be removed once the code is stable). */ | ||
| 208 | if (! NILP (old_dl)) | ||
| 209 | report_file_error ("Old list not empty", old_dl); | ||
| 210 | if (! NILP (new_dl)) | ||
| 211 | report_file_error ("New list not empty", new_dl); | ||
| 212 | |||
| 121 | /* Replace directory listing with the new one. */ | 213 | /* Replace directory listing with the new one. */ |
| 122 | XSETCDR (XCDR (XCDR (XCDR (watch_object))), Fcons (new_dl, Qnil)); | 214 | XSETCDR (XCDR (XCDR (XCDR (watch_object))), |
| 215 | Fcons (new_directory_files, Qnil)); | ||
| 123 | return; | 216 | return; |
| 124 | } | 217 | } |
| 125 | 218 | ||
| @@ -173,7 +266,7 @@ kqueue_callback (int fd, void *data) | |||
| 173 | 266 | ||
| 174 | /* Construct an event. */ | 267 | /* Construct an event. */ |
| 175 | if (! NILP (actions)) | 268 | if (! NILP (actions)) |
| 176 | kqueue_generate_event (monitor_object, actions, file, callback); | 269 | kqueue_generate_event (monitor_object, actions, file, Qnil, callback); |
| 177 | 270 | ||
| 178 | /* Cancel monitor if file or directory is deleted. */ | 271 | /* Cancel monitor if file or directory is deleted. */ |
| 179 | if (kev.fflags & (NOTE_DELETE | NOTE_RENAME)) | 272 | if (kev.fflags & (NOTE_DELETE | NOTE_RENAME)) |
| @@ -352,9 +445,6 @@ syms_of_kqueue (void) | |||
| 352 | 445 | ||
| 353 | #endif /* HAVE_KQUEUE */ | 446 | #endif /* HAVE_KQUEUE */ |
| 354 | 447 | ||
| 355 | /* TODO | ||
| 356 | * Add FILE1 in case of `rename'. */ | ||
| 357 | |||
| 358 | /* PROBLEMS | 448 | /* PROBLEMS |
| 359 | * https://bugs.launchpad.net/ubuntu/+source/libkqueue/+bug/1514837 | 449 | * https://bugs.launchpad.net/ubuntu/+source/libkqueue/+bug/1514837 |
| 360 | prevents tests on Ubuntu. */ | 450 | prevents tests on Ubuntu. */ |