From: Andreas Politz <politza@hochschule-trier.de>
To: Michael Albinus <michael.albinus@gmx.de>
Cc: 26126@debbugs.gnu.org
Subject: bug#26126: 26.0.50; file-notify-rm-watch removes arbitrary watches
Date: Sun, 19 Mar 2017 23:05:25 +0100 [thread overview]
Message-ID: <8737e8excq.fsf@luca> (raw)
In-Reply-To: <87y3w2gywc.fsf@detlef> (Michael Albinus's message of "Sat, 18 Mar 2017 20:36:51 +0100")
[-- Attachment #1: Type: text/plain, Size: 689 bytes --]
I think there are two ways to fix the main problem discussed here.
1. Change inotify.c and make it return/receive a unique descriptor per client.
2. Wrap inotify's descriptor inside file-notify-add-watch, similar to
how it's currently done. This is what I was refering to as
boxing/unboxing earlier.
The 2nd approach is problematic in the context of file-name-handler, so
I attempted to solve it the other way: Instead of a single number, use a
cons of
(INOTIFY-DESCRIPTOR . ID)
, which is unique across all active watches. I.e. this makes inotify
work like all the other back-ends/handler. Here is a first draft of a
corresponding patch, let me know what you think.
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: inotify.diff --]
[-- Type: text/x-diff, Size: 12977 bytes --]
diff --git a/lisp/filenotify.el b/lisp/filenotify.el
index 7eb6229976..beae94c2c2 100644
--- a/lisp/filenotify.el
+++ b/lisp/filenotify.el
@@ -55,9 +55,8 @@ file-notify--rm-descriptor
"Remove DESCRIPTOR from `file-notify-descriptors'.
DESCRIPTOR should be an object returned by `file-notify-add-watch'.
If it is registered in `file-notify-descriptors', a stopped event is sent."
- (let* ((desc (if (consp descriptor) (car descriptor) descriptor))
- (registered (gethash desc file-notify-descriptors))
- (file (if (consp descriptor) (cdr descriptor) (cl-caadr registered)))
+ (let* ((registered (gethash descriptor file-notify-descriptors))
+ (file (cl-caadr registered))
(dir (car registered)))
(when (consp registered)
@@ -69,12 +68,12 @@ file-notify--rm-descriptor
;; Modify `file-notify-descriptors'.
(if (not file)
- (remhash desc file-notify-descriptors)
+ (remhash descriptor 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))))))
+ (remhash descriptor file-notify-descriptors)
+ (puthash descriptor registered file-notify-descriptors))))))
;; This function is used by `inotify', `kqueue', `gfilenotify' and
;; `w32notify' events.
@@ -102,9 +101,9 @@ file-notify--pending-event
(defun file-notify--event-watched-file (event)
"Return file or directory being watched.
Could be different from the directory watched by the backend library."
- (let* ((desc (if (consp (car event)) (caar event) (car event)))
- (registered (gethash desc file-notify-descriptors))
- (file (if (consp (car event)) (cdar event) (cl-caadr registered)))
+ (let* ((descriptor (car event))
+ (registered (gethash descriptor file-notify-descriptors))
+ (file (cl-caadr registered))
(dir (car registered)))
(if file (expand-file-name file dir) dir)))
@@ -133,17 +132,11 @@ file-notify--event-cookie
;; `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 (desc file)
+(defun file-notify--descriptor (desc _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 (and (natnump desc) (eq file-notify--library 'inotify))
- (cons desc
- (and (stringp file)
- (car (assoc
- (file-name-nondirectory file)
- (gethash desc file-notify-descriptors)))))
- desc))
+ desc)
;; The callback function used to map between specific flags of the
;; respective file notifications, and the ones we return.
@@ -396,7 +389,6 @@ file-notify-add-watch
;; Modify `file-notify-descriptors'.
(setq file (unless (file-directory-p file) (file-name-nondirectory file))
- desc (if (consp desc) (car desc) desc)
registered (gethash desc file-notify-descriptors)
entry `(,file . ,callback))
(unless (member entry (cdr registered))
@@ -408,9 +400,8 @@ file-notify-add-watch
(defun file-notify-rm-watch (descriptor)
"Remove an existing watch specified by its DESCRIPTOR.
DESCRIPTOR should be an object returned by `file-notify-add-watch'."
- (let* ((desc (if (consp descriptor) (car descriptor) descriptor))
- (file (if (consp descriptor) (cdr descriptor)))
- (registered (gethash desc file-notify-descriptors))
+ (let* ((file nil)
+ (registered (gethash descriptor file-notify-descriptors))
(dir (car registered))
(handler (and (stringp dir)
(find-file-name-handler dir 'file-notify-rm-watch))))
@@ -432,7 +423,7 @@ file-notify-rm-watch
((eq file-notify--library 'kqueue) 'kqueue-rm-watch)
((eq file-notify--library 'gfilenotify) 'gfile-rm-watch)
((eq file-notify--library 'w32notify) 'w32notify-rm-watch))
- desc))
+ descriptor))
(file-notify-error nil)))
;; Modify `file-notify-descriptors'.
@@ -441,9 +432,8 @@ file-notify-rm-watch
(defun file-notify-valid-p (descriptor)
"Check a watch specified by its DESCRIPTOR.
DESCRIPTOR should be an object returned by `file-notify-add-watch'."
- (let* ((desc (if (consp descriptor) (car descriptor) descriptor))
- (file (if (consp descriptor) (cdr descriptor)))
- (registered (gethash desc file-notify-descriptors))
+ (let* ((file nil)
+ (registered (gethash descriptor file-notify-descriptors))
(dir (car registered))
handler)
@@ -464,7 +454,7 @@ file-notify-valid-p
((eq file-notify--library 'kqueue) 'kqueue-valid-p)
((eq file-notify--library 'gfilenotify) 'gfile-valid-p)
((eq file-notify--library 'w32notify) 'w32notify-valid-p))
- desc))
+ descriptor))
t))))
;; The end:
diff --git a/src/inotify.c b/src/inotify.c
index 61ef615328..302f52225e 100644
--- a/src/inotify.c
+++ b/src/inotify.c
@@ -51,10 +51,47 @@ static int inotifyfd = -1;
static Lisp_Object watch_list;
static Lisp_Object
-make_watch_descriptor (int wd)
+add_watch_descriptor (int wd, Lisp_Object filename, Lisp_Object callback)
{
- /* TODO replace this with a Misc Object! */
- return make_number (wd);
+ Lisp_Object descriptor = make_number (wd);
+ Lisp_Object elt = Fassoc (descriptor, watch_list);
+ Lisp_Object list = Fcdr (elt);
+ Lisp_Object watch, watch_id;
+ int id = 0;
+
+ while (! NILP (list))
+ {
+ id = max (id, 1 + XINT (XCAR (XCAR (list))));
+ list = XCDR (list);
+ }
+
+ watch_id = make_number (id);
+ watch = list3 (watch_id, filename, callback);
+
+ if (NILP (elt))
+ watch_list = Fcons (Fcons (descriptor, Fcons (watch, Qnil)),
+ watch_list);
+ else
+ XSETCDR (elt, Fcons (watch, XCDR (elt)));
+
+ return Fcons (descriptor, watch_id);
+}
+
+static void
+remove_watch_descriptor (Lisp_Object descriptor, Lisp_Object id)
+{
+ Lisp_Object watches = Fassoc (descriptor, watch_list);
+
+ if (CONSP (watches))
+ {
+ Lisp_Object watch = Fassoc (id, XCDR (watches));
+
+ if (CONSP (watch))
+ XSETCDR (watches, Fdelete (watch, XCDR (watches)));
+
+ if (NILP (XCDR (watches)))
+ watch_list = Fdelete (watches, watch_list);
+ }
}
static Lisp_Object
@@ -96,7 +133,7 @@ mask_to_aspects (uint32_t mask) {
}
static Lisp_Object
-inotifyevent_to_event (Lisp_Object watch_object, struct inotify_event const *ev)
+inotifyevent_to_event (Lisp_Object watch, struct inotify_event const *ev)
{
Lisp_Object name = Qnil;
if (ev->len > 0)
@@ -106,13 +143,13 @@ inotifyevent_to_event (Lisp_Object watch_object, struct inotify_event const *ev)
name = DECODE_FILE (name);
}
else
- name = XCAR (XCDR (watch_object));
+ name = XCAR (XCDR (watch));
- return list2 (list4 (make_watch_descriptor (ev->wd),
+ return list2 (list4 (Fcons (make_number (ev->wd), XCAR (watch)),
mask_to_aspects (ev->mask),
name,
make_number (ev->cookie)),
- Fnth (make_number (2), watch_object));
+ Fnth (make_number (2), watch));
}
/* This callback is called when the FD is available for read. The inotify
@@ -121,7 +158,6 @@ static void
inotify_callback (int fd, void *_)
{
struct input_event event;
- Lisp_Object watch_object;
int to_read;
char *buffer;
ssize_t n;
@@ -146,20 +182,23 @@ inotify_callback (int fd, void *_)
while (i < (size_t)n)
{
struct inotify_event *ev = (struct inotify_event *) &buffer[i];
+ Lisp_Object descriptor = make_number (ev->wd);
+ Lisp_Object watches = Fassoc (descriptor, watch_list);
- watch_object = Fassoc (make_watch_descriptor (ev->wd), watch_list);
- if (!NILP (watch_object))
+ if (CONSP (watches))
{
- event.arg = inotifyevent_to_event (watch_object, ev);
+ for (Lisp_Object elt = XCDR (watches); CONSP (elt); elt = XCDR (elt))
+ {
+ event.arg = inotifyevent_to_event (XCAR (elt), ev);
+ if (!NILP (event.arg))
+ kbd_buffer_store_event (&event);
+ }
/* If event was removed automatically: Drop it from watch list. */
if (ev->mask & IN_IGNORED)
- watch_list = Fdelete (watch_object, watch_list);
-
- if (!NILP (event.arg))
- kbd_buffer_store_event (&event);
+ for (Lisp_Object elt = XCDR (watches); CONSP (elt); elt = XCDR (elt))
+ remove_watch_descriptor (descriptor, XCAR (elt));
}
-
i += sizeof (*ev) + ev->len;
}
@@ -292,14 +331,12 @@ renames (moved-from and moved-to).
See inotify(7) and inotify_add_watch(2) for further information. The inotify fd
is managed internally and there is no corresponding inotify_init. Use
`inotify-rm-watch' to remove a watch.
- */)
+ */)
(Lisp_Object file_name, Lisp_Object aspect, Lisp_Object callback)
{
uint32_t mask;
- Lisp_Object watch_object;
Lisp_Object encoded_file_name;
- Lisp_Object watch_descriptor;
- int watchdesc = -1;
+ int wd = -1;
CHECK_STRING (file_name);
@@ -314,22 +351,11 @@ is managed internally and there is no corresponding inotify_init. Use
mask = aspect_to_inotifymask (aspect);
encoded_file_name = ENCODE_FILE (file_name);
- watchdesc = inotify_add_watch (inotifyfd, SSDATA (encoded_file_name), mask);
- if (watchdesc == -1)
+ wd = inotify_add_watch (inotifyfd, SSDATA (encoded_file_name), mask);
+ if (wd == -1)
report_file_notify_error ("Could not add watch for file", file_name);
- watch_descriptor = make_watch_descriptor (watchdesc);
-
- /* Delete existing watch object. */
- watch_object = Fassoc (watch_descriptor, watch_list);
- if (!NILP (watch_object))
- watch_list = Fdelete (watch_object, watch_list);
-
- /* Store watch object in watch list. */
- watch_object = list3 (watch_descriptor, encoded_file_name, callback);
- watch_list = Fcons (watch_object, watch_list);
-
- return watch_descriptor;
+ return add_watch_descriptor (wd, file_name, callback);
}
DEFUN ("inotify-rm-watch", Finotify_rm_watch, Sinotify_rm_watch, 1, 1, 0,
@@ -341,16 +367,24 @@ See inotify_rm_watch(2) for more information.
*/)
(Lisp_Object watch_descriptor)
{
- Lisp_Object watch_object;
- int wd = XINT (watch_descriptor);
- if (inotify_rm_watch (inotifyfd, wd) == -1)
- report_file_notify_error ("Could not rm watch", watch_descriptor);
+ Lisp_Object descriptor, id;
- /* Remove watch descriptor from watch list. */
- watch_object = Fassoc (watch_descriptor, watch_list);
- if (!NILP (watch_object))
- watch_list = Fdelete (watch_object, watch_list);
+ if (! (CONSP (watch_descriptor)
+ && INTEGERP (XCAR (watch_descriptor))
+ && INTEGERP (XCDR (watch_descriptor))))
+ report_file_notify_error ("Invalid descriptor ", watch_descriptor);
+
+ descriptor = XCAR (watch_descriptor);
+ id = XCDR (watch_descriptor);
+ remove_watch_descriptor (descriptor, id);
+
+ if (NILP (Fassoc (descriptor, watch_list)))
+ {
+ int wd = XINT (descriptor);
+ if (inotify_rm_watch (inotifyfd, wd) == -1)
+ report_file_notify_error ("Could not rm watch", watch_descriptor);
+ }
/* Cleanup if no more files are watched. */
if (NILP (watch_list))
@@ -374,10 +408,34 @@ reason. Removing the watch by calling `inotify-rm-watch' also makes
it invalid. */)
(Lisp_Object watch_descriptor)
{
- Lisp_Object watch_object = Fassoc (watch_descriptor, watch_list);
- return NILP (watch_object) ? Qnil : Qt;
+ Lisp_Object watches;
+
+ if (! (CONSP (watch_descriptor)
+ && INTEGERP (XCAR (watch_descriptor))
+ && INTEGERP (XCDR (watch_descriptor))))
+ return Qnil;
+
+ watches = Fassoc (XCAR (watch_descriptor), watch_list);
+
+ if (! CONSP (watches))
+ return Qnil;
+ return CONSP (Fassoc (XCDR (watch_descriptor), XCDR (watches)));
+}
+
+#ifdef DEBUG
+DEFUN ("inotify-watch-list", Finotify_watch_list, Sinotify_watch_list, 0, 0, 0,
+ doc: /* Return a copy of the internal watch_list. */)
+{
+ return Fcopy_sequence (watch_list);
}
+DEFUN ("inotify-allocated-p", Finotify_allocated_p, Sinotify_allocated_p, 0, 0, 0,
+ doc: /* Return non-nil, if a inotify instance is allocated. */)
+{
+ return inotifyfd < 0 ? Qnil : Qt;
+}
+#endif
+
void
syms_of_inotify (void)
{
@@ -414,6 +472,10 @@ syms_of_inotify (void)
defsubr (&Sinotify_rm_watch);
defsubr (&Sinotify_valid_p);
+#ifdef DEBUG
+ defsubr (&Sinotify_watch_list);
+ defsubr (&Sinotify_allocated_p);
+#endif
staticpro (&watch_list);
Fprovide (intern_c_string ("inotify"), Qnil);
[-- Attachment #3: Type: text/plain, Size: 250 bytes --]
Apart from that, the following is a list containing all the problems
I've found that I still think are relevant. Some of which we already
discussed earlier.
* Don't discriminate remote handler based on the local library used.
Already discussed.
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #4: remote-handler.diff --]
[-- Type: text/x-diff, Size: 768 bytes --]
diff -u --label /home/politza/src/emacs/lisp/filenotify.el --label \#\<buffer\ filenotify.el\> /home/politza/src/emacs/lisp/filenotify.el /tmp/buffer-content-142424Gt
--- /home/politza/src/emacs/lisp/filenotify.el
+++ #<buffer filenotify.el>
@@ -342,10 +342,7 @@
;; file notification support.
(setq desc (funcall
handler 'file-notify-add-watch
- ;; kqueue does not report file changes in
- ;; directory monitor. So we must watch the file
- ;; itself.
- (if (eq file-notify--library 'kqueue) file dir)
+ dir
flags callback))
;; Check, whether Emacs has been compiled with file notification
Diff finished. Sun Mar 19 23:05:00 2017
[-- Attachment #5: Type: text/plain, Size: 303 bytes --]
* The value of file-notify--pending-event is reset after the first
client was processed in the outer loop in file-notify-callback,
resulting in clients watching for the same file being treated
differently. Note that this problem would be solved by not having
multiple clients per descriptor.
[-- Attachment #6: ERT Test for pending events with 2 clients --]
[-- Type: application/emacs-lisp, Size: 1346 bytes --]
[-- Attachment #7: Type: text/plain, Size: 534 bytes --]
* inotify_add_watch internally uses a single watch per directory, which
may be shared by multiple clients (using filenotify.el). The problem
here seems to be that these clients may use different FLAGS as
argument to file-notify-add-watch. Currently, the last added client's
FLAGS become the effective mask for the underlying C-descriptor,
meaning that clients added before may not receive change or
attribute-change events if the mask was modified accordingly.
* It seems to me that the right word here is "unified".
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #8: os.texi diff --]
[-- Type: text/x-diff, Size: 482 bytes --]
diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi
index 9b6752c5e1..4f7d47305f 100644
--- a/doc/lispref/os.texi
+++ b/doc/lispref/os.texi
@@ -2707,7 +2707,7 @@ File Notifications
Since all these libraries emit different events on notified file
changes, there is the Emacs library @code{filenotify} which provides a
-unique interface.
+unified interface.
@defun file-notify-add-watch file flags callback
Add a watch for filesystem events pertaining to @var{file}. This
[-- Attachment #9: Type: text/plain, Size: 5 bytes --]
-ap
next prev parent reply other threads:[~2017-03-19 22:05 UTC|newest]
Thread overview: 63+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-03-16 14:14 bug#26126: 26.0.50; file-notify-rm-watch removes arbitrary watches Andreas Politz
2017-03-17 14:41 ` Michael Albinus
2017-03-17 14:59 ` Andreas Politz
2017-03-17 16:08 ` Michael Albinus
2017-03-17 17:45 ` Andreas Politz
2017-03-18 8:30 ` Michael Albinus
2017-03-18 13:32 ` Andreas Politz
2017-03-18 19:36 ` Michael Albinus
2017-03-18 20:37 ` Andreas Politz
2017-03-19 9:39 ` Michael Albinus
2017-03-19 11:14 ` Andreas Politz
2017-03-19 19:23 ` Michael Albinus
2017-03-20 20:39 ` Andreas Politz
2017-03-21 8:44 ` Michael Albinus
2017-03-21 15:37 ` Eli Zaretskii
2017-03-21 18:59 ` Andreas Politz
2017-03-22 13:23 ` Michael Albinus
2017-03-22 15:44 ` Eli Zaretskii
2017-03-22 16:01 ` Michael Albinus
2017-03-22 16:13 ` Eli Zaretskii
2017-03-22 16:23 ` Michael Albinus
2017-03-24 19:54 ` Andreas Politz
2017-03-25 12:50 ` Michael Albinus
2017-03-25 13:59 ` Andreas Politz
2017-03-25 14:08 ` Michael Albinus
2017-03-25 16:27 ` Andreas Politz
2017-03-25 16:37 ` Michael Albinus
2017-03-25 17:12 ` Andreas Politz
2017-03-25 18:36 ` Michael Albinus
2017-03-25 19:34 ` Andreas Politz
2017-03-26 7:08 ` Michael Albinus
2017-03-21 15:56 ` Andreas Politz
2017-03-22 12:56 ` Michael Albinus
2017-03-22 17:34 ` Andreas Politz
2017-03-22 18:49 ` Michael Albinus
2017-03-19 22:05 ` Andreas Politz [this message]
2017-03-21 13:05 ` Michael Albinus
2017-03-21 15:06 ` Andreas Politz
2017-03-21 15:54 ` Eli Zaretskii
2017-03-22 13:17 ` Michael Albinus
2017-03-22 17:43 ` Andreas Politz
2017-03-22 18:57 ` Michael Albinus
2017-03-22 20:02 ` Eli Zaretskii
2017-03-23 7:36 ` Michael Albinus
2017-03-23 15:22 ` Eli Zaretskii
2017-03-23 16:10 ` Michael Albinus
2017-03-22 19:40 ` Michael Albinus
2017-03-24 20:44 ` Andreas Politz
2017-03-25 6:35 ` Eli Zaretskii
2017-03-25 8:57 ` Andreas Politz
2017-03-25 14:17 ` Eli Zaretskii
2017-03-25 16:34 ` Andreas Politz
2017-03-25 14:04 ` Michael Albinus
2017-03-25 16:19 ` Andreas Politz
2017-03-25 17:09 ` Michael Albinus
2017-03-25 17:26 ` Andreas Politz
2017-03-25 18:18 ` Andreas Politz
2017-03-25 18:40 ` Michael Albinus
2017-03-25 16:21 ` Andreas Politz
2017-03-18 19:28 ` Andreas Politz
2017-03-18 19:49 ` Michael Albinus
2017-03-18 20:48 ` Andreas Politz
2017-03-30 18:15 ` Paul Eggert
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/emacs/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=8737e8excq.fsf@luca \
--to=politza@hochschule-trier.de \
--cc=26126@debbugs.gnu.org \
--cc=michael.albinus@gmx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).