X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/5154781141c2305c24103beb358453d30a838921..0a2aedfe6d650e825a50f25f972bac20d669f5cb:/src/kqueue.c diff --git a/src/kqueue.c b/src/kqueue.c index ca0e3e7e1c..f45bd0c4c2 100644 --- a/src/kqueue.c +++ b/src/kqueue.c @@ -1,12 +1,13 @@ /* Filesystem notifications support with kqueue API. - Copyright (C) 2015 Free Software Foundation, Inc. + +Copyright (C) 2015-2016 Free Software Foundation, Inc. This file is part of GNU Emacs. GNU Emacs is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. +the Free Software Foundation, either version 3 of the License, or (at +your option) any later version. GNU Emacs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -28,6 +29,10 @@ along with GNU Emacs. If not, see . */ #include "keyboard.h" #include "process.h" +#ifdef HAVE_SYS_RESOURCE_H +#include +#endif /* HAVE_SYS_RESOURCE_H */ + /* File handle for kqueue. */ static int kqueuefd = -1; @@ -66,9 +71,8 @@ kqueue_directory_listing (Lisp_Object directory_files) /* Generate a file notification event. */ static void -kqueue_generate_event -(Lisp_Object watch_object, Lisp_Object actions, - Lisp_Object file, Lisp_Object file1) +kqueue_generate_event (Lisp_Object watch_object, Lisp_Object actions, + Lisp_Object file, Lisp_Object file1) { Lisp_Object flags, action, entry; struct input_event event; @@ -108,14 +112,14 @@ kqueue_generate_event replaced by the new directory listing at the end of this function. */ static void -kqueue_compare_dir_list -(Lisp_Object watch_object) +kqueue_compare_dir_list (Lisp_Object watch_object) { - Lisp_Object dir, pending_events; + Lisp_Object dir, pending_dl, deleted_dl; Lisp_Object old_directory_files, old_dl, new_directory_files, new_dl, dl; dir = XCAR (XCDR (watch_object)); - pending_events = Qnil; + pending_dl = Qnil; + deleted_dl = Qnil; old_directory_files = Fnth (make_number (4), watch_object); old_dl = kqueue_directory_listing (old_directory_files); @@ -168,6 +172,7 @@ kqueue_compare_dir_list kqueue_generate_event (watch_object, Fcons (Qrename, Qnil), XCAR (XCDR (old_entry)), XCAR (XCDR (new_entry))); + deleted_dl = Fcons (new_entry, deleted_dl); } new_dl = Fdelq (new_entry, new_dl); goto the_end; @@ -179,24 +184,35 @@ kqueue_compare_dir_list new_entry = XCAR (dl1); if (strcmp (SSDATA (XCAR (XCDR (old_entry))), SSDATA (XCAR (XCDR (new_entry)))) == 0) { - pending_events = Fcons (new_entry, pending_events); + pending_dl = Fcons (new_entry, pending_dl); new_dl = Fdelq (new_entry, new_dl); goto the_end; } } - new_entry = assq_no_quit (XCAR (old_entry), pending_events); - if (NILP (new_entry)) + /* Check, whether this a pending file. */ + new_entry = assq_no_quit (XCAR (old_entry), pending_dl); + + if (NILP (new_entry)) { + /* Check, whether this is an already deleted file (by rename). */ + for (dl1 = deleted_dl; ! NILP (dl1); dl1 = XCDR (dl1)) { + new_entry = XCAR (dl1); + if (strcmp (SSDATA (XCAR (XCDR (old_entry))), + SSDATA (XCAR (XCDR (new_entry)))) == 0) { + deleted_dl = Fdelq (new_entry, deleted_dl); + goto the_end; + } + } /* The file has been deleted. */ kqueue_generate_event (watch_object, Fcons (Qdelete, Qnil), XCAR (XCDR (old_entry)), Qnil); - else { + + } else { /* The file has been renamed. */ kqueue_generate_event (watch_object, Fcons (Qrename, Qnil), XCAR (XCDR (old_entry)), XCAR (XCDR (new_entry))); - new_dl = Fdelq (new_entry, new_dl); - pending_events = Fdelq (new_entry, pending_events); + pending_dl = Fdelq (new_entry, pending_dl); } the_end: @@ -226,8 +242,8 @@ kqueue_compare_dir_list new_dl = Fdelq (entry, new_dl); } - /* Parse through the resulting pending_events_list. */ - dl = pending_events; + /* Parse through the resulting pending_dl list. */ + dl = pending_dl; while (1) { Lisp_Object entry; if (NILP (dl)) @@ -239,18 +255,21 @@ kqueue_compare_dir_list (watch_object, Fcons (Qwrite, Qnil), XCAR (XCDR (entry)), Qnil); dl = XCDR (dl); - pending_events = Fdelq (entry, pending_events); + pending_dl = Fdelq (entry, pending_dl); } - /* At this point, old_dl, new_dl and pending_events shall be empty. - Let's make a check for this (might be removed once the code is - stable). */ + /* At this point, old_dl, new_dl and pending_dl shall be empty. + deleted_dl might not be empty when there was a rename to a + nonexistent file. Let's make a check for this (might be removed + once the code is stable). */ if (! NILP (old_dl)) report_file_error ("Old list not empty", old_dl); if (! NILP (new_dl)) report_file_error ("New list not empty", new_dl); - if (! NILP (pending_events)) - report_file_error ("Pending events not empty", new_dl); + if (! NILP (pending_dl)) + report_file_error ("Pending events list not empty", pending_dl); + // if (! NILP (deleted_dl)) + // report_file_error ("Deleted events list not empty", deleted_dl); /* Replace old directory listing with the new one. */ XSETCDR (Fnthcdr (make_number (3), watch_object), @@ -351,9 +370,12 @@ only when the upper directory of the renamed file is watched. */) (Lisp_Object file, Lisp_Object flags, Lisp_Object callback) { Lisp_Object watch_object, dir_list; - int fd, oflags; + int maxfd, fd, oflags; u_short fflags = 0; struct kevent kev; +#ifdef HAVE_GETRLIMIT + struct rlimit rlim; +#endif /* HAVE_GETRLIMIT */ /* Check parameters. */ CHECK_STRING (file); @@ -366,6 +388,21 @@ only when the upper directory of the renamed file is watched. */) if (! FUNCTIONP (callback)) wrong_type_argument (Qinvalid_function, callback); + /* Check available file descriptors. */ +#ifdef HAVE_GETRLIMIT + if (! getrlimit (RLIMIT_NOFILE, &rlim)) + maxfd = rlim.rlim_cur; + else +#endif /* HAVE_GETRLIMIT */ + maxfd = 256; + + /* We assume 50 file descriptors are sufficient for the rest of Emacs. */ + if ((maxfd - 50) < XINT (Flength (watch_list))) + xsignal2 + (Qfile_notify_error, + build_string ("File watching not possible, no file descriptor left"), + Flength (watch_list)); + if (kqueuefd < 0) { /* Create kqueue descriptor. */