diff options
| author | Michael Albinus | 2016-01-22 19:56:09 +0100 |
|---|---|---|
| committer | Michael Albinus | 2016-01-22 19:56:09 +0100 |
| commit | 7bf54d01159eb09bae3c9cd86f2af0812d9afdf6 (patch) | |
| tree | f00e00ba1ed5a492ec94faf8d07a6ca2f857a9c5 | |
| parent | f7dc6d8b5bb318e02a4016d93f8b34de0716f4dc (diff) | |
| download | emacs-7bf54d01159eb09bae3c9cd86f2af0812d9afdf6.tar.gz emacs-7bf54d01159eb09bae3c9cd86f2af0812d9afdf6.zip | |
Backport kqueue integration from master
* configure.ac (--with-file-notification): Add kqueue.
(top): Remove special test for "${HAVE_NS}" and
${with_file_notification}, this is handled inside gfilenotify
tests. Add kqueue tests. Use NOTIFY_CFLAGS and NOTIFY_LIBS
instead of library specific variables. Add error message for
gfile on Nextstep.
* doc/lispref/os.texi (File Notifications): Add kqueue as backend.
Fix some glitches in the example.
* etc/NEWS: Mention kqueue.
* lisp/filenotify.el (file-notify--library)
(file-notify-descriptors, file-notify-callback)
(file-notify-add-watch, file-notify-rm-watch)
(file-notify-valid-p): Add kqueue support.
(file-notify--rm-descriptor): Remove WHAT arg.
* src/Makefile.in: Use NOTIFY_CFLAGS and NOTIFY_LIBS.
* src/emacs.c (main): Call globals_of_kqueue and syms_of_kqueue.
* src/inotify.c (inotifyevent_to_event): Extract file name from
watch_object if the event doesn't provide it.
(Finotify_add_watch): Add file name to watch_object.
* src/keyboard.c (make_lispy_event): Check also for HAVE_KQUEUE.
* src/kqueue.c: New file.
* src/lisp.h: Declare extern globals_of_kqueue and syms_of_kqueue.
* test/automated/file-notify-tests.el
(file-notify--test-expected-events): Remove.
(file-notify--test-cleanup): Do not set that variable.
(file-notify--test-timeout) Use different timeouts for
different libraries.
(file-notify--test-library): New defun.
(file-notify--test-event-test): Make stronger checks.
(file-notify--test-with-events): EVENTS can also be a list of
lists. Flush outstanding events before running the body.
Make timeout heuristically depend on the number of events.
(file-notify-test01-add-watch, file-notify-test02-events)
(file-notify-test04-file-validity, file-notify-test05-dir-validity):
Rewrite in order to call file monitors but directory monitors.
(file-notify-test02-events, file-notify-test04-file-validity): Do
not skip cygwin tests. Add additional test for file creation.
Adapt expected result for different backends.
(file-notify-test03-autorevert): Some of the tests don't work for
w32notify.
(file-notify-test06-many-events): New test.
| -rw-r--r-- | configure.ac | 61 | ||||
| -rw-r--r-- | doc/lispref/os.texi | 41 | ||||
| -rw-r--r-- | etc/NEWS | 7 | ||||
| -rw-r--r-- | lisp/filenotify.el | 101 | ||||
| -rw-r--r-- | src/Makefile.in | 11 | ||||
| -rw-r--r-- | src/emacs.c | 16 | ||||
| -rw-r--r-- | src/inotify.c | 9 | ||||
| -rw-r--r-- | src/keyboard.c | 4 | ||||
| -rw-r--r-- | src/kqueue.c | 521 | ||||
| -rw-r--r-- | src/lisp.h | 16 | ||||
| -rw-r--r-- | test/automated/file-notify-tests.el | 585 |
11 files changed, 1092 insertions, 280 deletions
diff --git a/configure.ac b/configure.ac index b5e6b77c713..76193fae6dd 100644 --- a/configure.ac +++ b/configure.ac | |||
| @@ -356,17 +356,18 @@ OPTION_DEFAULT_ON([zlib],[don't compile with zlib decompression support]) | |||
| 356 | OPTION_DEFAULT_OFF([modules],[compile with dynamic modules support]) | 356 | OPTION_DEFAULT_OFF([modules],[compile with dynamic modules support]) |
| 357 | 357 | ||
| 358 | AC_ARG_WITH([file-notification],[AS_HELP_STRING([--with-file-notification=LIB], | 358 | AC_ARG_WITH([file-notification],[AS_HELP_STRING([--with-file-notification=LIB], |
| 359 | [use a file notification library (LIB one of: yes, gfile, inotify, w32, no)])], | 359 | [use a file notification library (LIB one of: yes, inotify, kqueue, gfile, w32, no)])], |
| 360 | [ case "${withval}" in | 360 | [ case "${withval}" in |
| 361 | y | ye | yes ) val=yes ;; | 361 | y | ye | yes ) val=yes ;; |
| 362 | n | no ) val=no ;; | 362 | n | no ) val=no ;; |
| 363 | g | gf | gfi | gfil | gfile ) val=gfile ;; | ||
| 364 | i | in | ino | inot | inoti | inotif | inotify ) val=inotify ;; | 363 | i | in | ino | inot | inoti | inotif | inotify ) val=inotify ;; |
| 364 | k | kq | kqu | kque | kqueu | kqueue ) val=kqueue ;; | ||
| 365 | g | gf | gfi | gfil | gfile ) val=gfile ;; | ||
| 365 | w | w3 | w32 ) val=w32 ;; | 366 | w | w3 | w32 ) val=w32 ;; |
| 366 | * ) AC_MSG_ERROR(['--with-file-notification=$withval' is invalid; | 367 | * ) AC_MSG_ERROR(['--with-file-notification=$withval' is invalid; |
| 367 | this option's value should be 'yes', 'no', 'gfile', 'inotify' or 'w32'. | 368 | this option's value should be 'yes', 'no', 'inotify', 'kqueue', 'gfile' or 'w32'. |
| 368 | 'yes' is a synonym for 'w32' on MS-Windows, for 'no' on Nextstep, | 369 | 'yes' is a synonym for 'w32' on MS-Windows, for 'no' on Nextstep, |
| 369 | otherwise for the first of 'inotify' or 'gfile' that is usable.]) | 370 | otherwise for the first of 'inotify', 'kqueue' or 'gfile' that is usable.]) |
| 370 | ;; | 371 | ;; |
| 371 | esac | 372 | esac |
| 372 | with_file_notification=$val | 373 | with_file_notification=$val |
| @@ -2712,12 +2713,6 @@ AC_SUBST(LIBGNUTLS_CFLAGS) | |||
| 2712 | NOTIFY_OBJ= | 2713 | NOTIFY_OBJ= |
| 2713 | NOTIFY_SUMMARY=no | 2714 | NOTIFY_SUMMARY=no |
| 2714 | 2715 | ||
| 2715 | dnl FIXME? Don't auto-detect on NS, but do allow someone to specify | ||
| 2716 | dnl a particular library. This doesn't make much sense? | ||
| 2717 | if test "${HAVE_NS}" = yes && test ${with_file_notification} = yes; then | ||
| 2718 | with_file_notification=no | ||
| 2719 | fi | ||
| 2720 | |||
| 2721 | dnl MS Windows native file monitor is available for mingw32 only. | 2716 | dnl MS Windows native file monitor is available for mingw32 only. |
| 2722 | case $with_file_notification,$opsys in | 2717 | case $with_file_notification,$opsys in |
| 2723 | w32,cygwin) | 2718 | w32,cygwin) |
| @@ -2748,16 +2743,44 @@ case $with_file_notification,$NOTIFY_OBJ in | |||
| 2748 | fi ;; | 2743 | fi ;; |
| 2749 | esac | 2744 | esac |
| 2750 | 2745 | ||
| 2746 | dnl kqueue is available on BSD-like systems. | ||
| 2747 | case $with_file_notification,$NOTIFY_OBJ in | ||
| 2748 | kqueue,* | yes,) | ||
| 2749 | EMACS_CHECK_MODULES([KQUEUE], [libkqueue]) | ||
| 2750 | if test "$HAVE_KQUEUE" = "yes"; then | ||
| 2751 | AC_DEFINE(HAVE_KQUEUE, 1, [Define to 1 to use kqueue.]) | ||
| 2752 | CPPFLAGS="$CPPFLAGS -I/usr/include/kqueue" | ||
| 2753 | NOTIFY_CFLAGS=$KQUEUE_CFLAGS | ||
| 2754 | NOTIFY_LIBS=$KQUEUE_LIBS | ||
| 2755 | NOTIFY_OBJ=kqueue.o | ||
| 2756 | NOTIFY_SUMMARY="yes -lkqueue" | ||
| 2757 | else | ||
| 2758 | AC_SEARCH_LIBS(kqueue, []) | ||
| 2759 | if test "$ac_cv_search_kqueue" != no; then | ||
| 2760 | AC_DEFINE(HAVE_KQUEUE, 1, [Define to 1 to use kqueue.]) | ||
| 2761 | NOTIFY_OBJ=kqueue.o | ||
| 2762 | NOTIFY_SUMMARY="yes (kqueue)" | ||
| 2763 | fi | ||
| 2764 | fi ;; | ||
| 2765 | esac | ||
| 2766 | |||
| 2751 | dnl g_file_monitor exists since glib 2.18. G_FILE_MONITOR_EVENT_MOVED | 2767 | dnl g_file_monitor exists since glib 2.18. G_FILE_MONITOR_EVENT_MOVED |
| 2752 | dnl has been added in glib 2.24. It has been tested under | 2768 | dnl has been added in glib 2.24. It has been tested under |
| 2753 | dnl GNU/Linux only. | 2769 | dnl GNU/Linux only. |
| 2754 | case $with_file_notification,$NOTIFY_OBJ in | 2770 | case $with_file_notification,$NOTIFY_OBJ in |
| 2755 | gfile,* | yes,) | 2771 | gfile,* | yes,) |
| 2756 | EMACS_CHECK_MODULES([GFILENOTIFY], [gio-2.0 >= 2.24]) | 2772 | if test "${HAVE_NS}" = yes; then |
| 2757 | if test "$HAVE_GFILENOTIFY" = "yes"; then | 2773 | AC_MSG_ERROR(['--with-file-notification=gfile' is not supported in NextStep builds. |
| 2758 | AC_DEFINE(HAVE_GFILENOTIFY, 1, [Define to 1 if using GFile.]) | 2774 | Consider kqueue instead.]) |
| 2759 | NOTIFY_OBJ=gfilenotify.o | 2775 | else |
| 2760 | NOTIFY_SUMMARY="yes -lgio (gfile)" | 2776 | EMACS_CHECK_MODULES([GFILENOTIFY], [gio-2.0 >= 2.24]) |
| 2777 | if test "$HAVE_GFILENOTIFY" = "yes"; then | ||
| 2778 | AC_DEFINE(HAVE_GFILENOTIFY, 1, [Define to 1 if using GFile.]) | ||
| 2779 | NOTIFY_CFLAGS=$GFILENOTIFY_CFLAGS | ||
| 2780 | NOTIFY_LIBS=$GFILENOTIFY_LIBS | ||
| 2781 | NOTIFY_OBJ=gfilenotify.o | ||
| 2782 | NOTIFY_SUMMARY="yes -lgio (gfile)" | ||
| 2783 | fi | ||
| 2761 | fi ;; | 2784 | fi ;; |
| 2762 | esac | 2785 | esac |
| 2763 | 2786 | ||
| @@ -2769,9 +2792,9 @@ esac | |||
| 2769 | if test -n "$NOTIFY_OBJ"; then | 2792 | if test -n "$NOTIFY_OBJ"; then |
| 2770 | AC_DEFINE(USE_FILE_NOTIFY, 1, [Define to 1 if using file notifications.]) | 2793 | AC_DEFINE(USE_FILE_NOTIFY, 1, [Define to 1 if using file notifications.]) |
| 2771 | fi | 2794 | fi |
| 2795 | AC_SUBST(NOTIFY_CFLAGS) | ||
| 2796 | AC_SUBST(NOTIFY_LIBS) | ||
| 2772 | AC_SUBST(NOTIFY_OBJ) | 2797 | AC_SUBST(NOTIFY_OBJ) |
| 2773 | AC_SUBST(GFILENOTIFY_CFLAGS) | ||
| 2774 | AC_SUBST(GFILENOTIFY_LIBS) | ||
| 2775 | 2798 | ||
| 2776 | dnl Do not put whitespace before the #include statements below. | 2799 | dnl Do not put whitespace before the #include statements below. |
| 2777 | dnl Older compilers (eg sunos4 cc) choke on it. | 2800 | dnl Older compilers (eg sunos4 cc) choke on it. |
| @@ -4141,8 +4164,8 @@ OLDCFLAGS="$CFLAGS" | |||
| 4141 | OLDLIBS="$LIBS" | 4164 | OLDLIBS="$LIBS" |
| 4142 | CFLAGS="$CFLAGS $GTK_CFLAGS $RSVG_CFLAGS $DBUS_CFLAGS $SETTINGS_CFLAGS" | 4165 | CFLAGS="$CFLAGS $GTK_CFLAGS $RSVG_CFLAGS $DBUS_CFLAGS $SETTINGS_CFLAGS" |
| 4143 | LIBS="$LIBS $GTK_LIBS $RSVG_LIBS $DBUS_LIBS $SETTINGS_LIBS" | 4166 | LIBS="$LIBS $GTK_LIBS $RSVG_LIBS $DBUS_LIBS $SETTINGS_LIBS" |
| 4144 | CFLAGS="$CFLAGS $GFILENOTIFY_CFLAGS $CAIRO_CFLAGS" | 4167 | CFLAGS="$CFLAGS $NOTIFY_CFLAGS $CAIRO_CFLAGS" |
| 4145 | LIBS="$LIBS $GFILENOTIFY_LIBS $CAIRO_LIBS" | 4168 | LIBS="$LIBS $NOTIFY_LIBS $CAIRO_LIBS" |
| 4146 | AC_MSG_CHECKING([whether GLib is linked in]) | 4169 | AC_MSG_CHECKING([whether GLib is linked in]) |
| 4147 | AC_LINK_IFELSE([AC_LANG_PROGRAM( | 4170 | AC_LINK_IFELSE([AC_LANG_PROGRAM( |
| 4148 | [[#include <glib.h> | 4171 | [[#include <glib.h> |
diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi index 7206cd4ef86..8e3720eb947 100644 --- a/doc/lispref/os.texi +++ b/doc/lispref/os.texi | |||
| @@ -2653,9 +2653,9 @@ This function removes the tray notification given by its unique | |||
| 2653 | 2653 | ||
| 2654 | Several operating systems support watching of filesystems for changes | 2654 | Several operating systems support watching of filesystems for changes |
| 2655 | of files. If configured properly, Emacs links a respective library | 2655 | of files. If configured properly, Emacs links a respective library |
| 2656 | like @file{gfilenotify}, @file{inotify}, or @file{w32notify} | 2656 | like @file{inotify}, @file{kqueue}, @file{gfilenotify}, or |
| 2657 | statically. These libraries enable watching of filesystems on the | 2657 | @file{w32notify} statically. These libraries enable watching of |
| 2658 | local machine. | 2658 | filesystems on the local machine. |
| 2659 | 2659 | ||
| 2660 | It is also possible to watch filesystems on remote machines, | 2660 | It is also possible to watch filesystems on remote machines, |
| 2661 | @pxref{Remote Files,, Remote Files, emacs, The GNU Emacs Manual} | 2661 | @pxref{Remote Files,, Remote Files, emacs, The GNU Emacs Manual} |
| @@ -2726,7 +2726,8 @@ watching @var{file} has been stopped | |||
| 2726 | Note that the @file{w32notify} library does not report | 2726 | Note that the @file{w32notify} library does not report |
| 2727 | @code{attribute-changed} events. When some file's attribute, like | 2727 | @code{attribute-changed} events. When some file's attribute, like |
| 2728 | permissions or modification time, has changed, this library reports a | 2728 | permissions or modification time, has changed, this library reports a |
| 2729 | @code{changed} event. | 2729 | @code{changed} event. Likewise, the @file{kqueue} library does not |
| 2730 | report reliably file attribute changes when watching a directory. | ||
| 2730 | 2731 | ||
| 2731 | The @code{stopped} event reports, that watching the file has been | 2732 | The @code{stopped} event reports, that watching the file has been |
| 2732 | stopped. This could be because @code{file-notify-rm-watch} was called | 2733 | stopped. This could be because @code{file-notify-rm-watch} was called |
| @@ -2765,7 +2766,7 @@ being reported. For example: | |||
| 2765 | @group | 2766 | @group |
| 2766 | (write-region "bla" nil "/tmp/foo") | 2767 | (write-region "bla" nil "/tmp/foo") |
| 2767 | @result{} Event (35025468 created "/tmp/.#foo") | 2768 | @result{} Event (35025468 created "/tmp/.#foo") |
| 2768 | Event (35025468 changed "/tmp/foo") [2 times] | 2769 | Event (35025468 changed "/tmp/foo") |
| 2769 | Event (35025468 deleted "/tmp/.#foo") | 2770 | Event (35025468 deleted "/tmp/.#foo") |
| 2770 | @end group | 2771 | @end group |
| 2771 | 2772 | ||
| @@ -2811,14 +2812,14 @@ also makes it invalid. | |||
| 2811 | @example | 2812 | @example |
| 2812 | @group | 2813 | @group |
| 2813 | (make-directory "/tmp/foo") | 2814 | (make-directory "/tmp/foo") |
| 2814 | @result{} nil | 2815 | @result{} Event (35025468 created "/tmp/foo") |
| 2815 | @end group | 2816 | @end group |
| 2816 | 2817 | ||
| 2817 | @group | 2818 | @group |
| 2818 | (setq desc | 2819 | (setq desc |
| 2819 | (file-notify-add-watch | 2820 | (file-notify-add-watch |
| 2820 | "/tmp/foo" '(change) 'my-notify-callback)) | 2821 | "/tmp/foo" '(change) 'my-notify-callback)) |
| 2821 | @result{} 35025468 | 2822 | @result{} 11359632 |
| 2822 | @end group | 2823 | @end group |
| 2823 | 2824 | ||
| 2824 | @group | 2825 | @group |
| @@ -2828,32 +2829,34 @@ also makes it invalid. | |||
| 2828 | 2829 | ||
| 2829 | @group | 2830 | @group |
| 2830 | (write-region "bla" nil "/tmp/foo/bla") | 2831 | (write-region "bla" nil "/tmp/foo/bla") |
| 2831 | @result{} Event (35025468 created "/tmp/foo/.#bla") | 2832 | @result{} Event (11359632 created "/tmp/foo/.#bla") |
| 2832 | Event (35025468 created "/tmp/foo/bla") | 2833 | Event (11359632 created "/tmp/foo/bla") |
| 2833 | Event (35025468 changed "/tmp/foo/bla") | 2834 | Event (11359632 changed "/tmp/foo/bla") |
| 2834 | Event (35025468 changed "/tmp/foo/.#bla") | 2835 | Event (11359632 deleted "/tmp/foo/.#bla") |
| 2835 | @end group | 2836 | @end group |
| 2836 | 2837 | ||
| 2837 | @group | 2838 | @group |
| 2838 | ;; Deleting a file in the directory doesn't invalidate the watch. | 2839 | ;; Deleting a file in the directory doesn't invalidate the watch. |
| 2839 | (delete-file "/tmp/foo/bla") | 2840 | (delete-file "/tmp/foo/bla") |
| 2840 | @result{} Event (35025468 deleted "/tmp/foo/bla") | 2841 | @result{} Event (11359632 deleted "/tmp/foo/bla") |
| 2841 | @end group | 2842 | @end group |
| 2842 | 2843 | ||
| 2843 | @group | 2844 | @group |
| 2844 | (write-region "bla" nil "/tmp/foo/bla") | 2845 | (write-region "bla" nil "/tmp/foo/bla") |
| 2845 | @result{} Event (35025468 created "/tmp/foo/.#bla") | 2846 | @result{} Event (11359632 created "/tmp/foo/.#bla") |
| 2846 | Event (35025468 created "/tmp/foo/bla") | 2847 | Event (11359632 created "/tmp/foo/bla") |
| 2847 | Event (35025468 changed "/tmp/foo/bla") | 2848 | Event (11359632 changed "/tmp/foo/bla") |
| 2848 | Event (35025468 changed "/tmp/foo/.#bla") | 2849 | Event (11359632 deleted "/tmp/foo/.#bla") |
| 2849 | @end group | 2850 | @end group |
| 2850 | 2851 | ||
| 2851 | @group | 2852 | @group |
| 2852 | ;; Deleting the directory invalidates the watch. | 2853 | ;; Deleting the directory invalidates the watch. |
| 2854 | ;; Events arrive for different watch descriptors. | ||
| 2853 | (delete-directory "/tmp/foo" 'recursive) | 2855 | (delete-directory "/tmp/foo" 'recursive) |
| 2854 | @result{} Event (35025468 deleted "/tmp/foo/bla") | 2856 | @result{} Event (35025468 deleted "/tmp/foo") |
| 2855 | Event (35025468 deleted "/tmp/foo") | 2857 | Event (11359632 deleted "/tmp/foo/bla") |
| 2856 | Event (35025468 stopped "/tmp/foo") | 2858 | Event (11359632 deleted "/tmp/foo") |
| 2859 | Event (11359632 stopped "/tmp/foo") | ||
| 2857 | @end group | 2860 | @end group |
| 2858 | 2861 | ||
| 2859 | @group | 2862 | @group |
| @@ -64,6 +64,10 @@ If gnustep-config is not available, the old heuristics are used. | |||
| 64 | unless gfile is explicitly requested via --with-file-notification='gfile'. | 64 | unless gfile is explicitly requested via --with-file-notification='gfile'. |
| 65 | 65 | ||
| 66 | --- | 66 | --- |
| 67 | ** 'configure' detects the kqueue file notification library on *BSD | ||
| 68 | and Mac OS X machines. | ||
| 69 | |||
| 70 | --- | ||
| 67 | ** The configure option '--with-pkg-config-prog' has been removed. | 71 | ** The configure option '--with-pkg-config-prog' has been removed. |
| 68 | Use './configure PKG_CONFIG=/full/name/of/pkg-config' if you need to. | 72 | Use './configure PKG_CONFIG=/full/name/of/pkg-config' if you need to. |
| 69 | 73 | ||
| @@ -1121,6 +1125,9 @@ notifications, if Emacs is compiled with file notification support. | |||
| 1121 | ** File Notifications | 1125 | ** File Notifications |
| 1122 | 1126 | ||
| 1123 | +++ | 1127 | +++ |
| 1128 | *** The kqueue library is integrated for *BSD and Mac OS X machines. | ||
| 1129 | |||
| 1130 | +++ | ||
| 1124 | *** The new event `stopped' signals, that a file notification watch is | 1131 | *** The new event `stopped' signals, that a file notification watch is |
| 1125 | not active any longer. | 1132 | not active any longer. |
| 1126 | 1133 | ||
diff --git a/lisp/filenotify.el b/lisp/filenotify.el index ebf4dd277c8..faa801ee6e7 100644 --- a/lisp/filenotify.el +++ b/lisp/filenotify.el | |||
| @@ -22,15 +22,16 @@ | |||
| 22 | ;;; Commentary | 22 | ;;; Commentary |
| 23 | 23 | ||
| 24 | ;; This package is an abstraction layer from the different low-level | 24 | ;; This package is an abstraction layer from the different low-level |
| 25 | ;; file notification packages `gfilenotify', `inotify' and | 25 | ;; file notification packages `inotify', `kqueue', `gfilenotify' and |
| 26 | ;; `w32notify'. | 26 | ;; `w32notify'. |
| 27 | 27 | ||
| 28 | ;;; Code: | 28 | ;;; Code: |
| 29 | 29 | ||
| 30 | (defconst file-notify--library | 30 | (defconst file-notify--library |
| 31 | (cond | 31 | (cond |
| 32 | ((featurep 'gfilenotify) 'gfilenotify) | ||
| 33 | ((featurep 'inotify) 'inotify) | 32 | ((featurep 'inotify) 'inotify) |
| 33 | ((featurep 'kqueue) 'kqueue) | ||
| 34 | ((featurep 'gfilenotify) 'gfilenotify) | ||
| 34 | ((featurep 'w32notify) 'w32notify)) | 35 | ((featurep 'w32notify) 'w32notify)) |
| 35 | "Non-nil when Emacs has been compiled with file notification support. | 36 | "Non-nil when Emacs has been compiled with file notification support. |
| 36 | The value is the name of the low-level file notification package | 37 | The value is the name of the low-level file notification package |
| @@ -40,25 +41,24 @@ could use another implementation.") | |||
| 40 | (defvar file-notify-descriptors (make-hash-table :test 'equal) | 41 | (defvar file-notify-descriptors (make-hash-table :test 'equal) |
| 41 | "Hash table for registered file notification descriptors. | 42 | "Hash table for registered file notification descriptors. |
| 42 | A key in this hash table is the descriptor as returned from | 43 | A key in this hash table is the descriptor as returned from |
| 43 | `gfilenotify', `inotify', `w32notify' or a file name handler. | 44 | `inotify', `kqueue', `gfilenotify', `w32notify' or a file name |
| 44 | The value in the hash table is a list | 45 | handler. The value in the hash table is a list |
| 45 | 46 | ||
| 46 | (DIR (FILE . CALLBACK) (FILE . CALLBACK) ...) | 47 | (DIR (FILE . CALLBACK) (FILE . CALLBACK) ...) |
| 47 | 48 | ||
| 48 | Several values for a given DIR happen only for `inotify', when | 49 | Several values for a given DIR happen only for `inotify', when |
| 49 | different files from the same directory are watched.") | 50 | different files from the same directory are watched.") |
| 50 | 51 | ||
| 51 | (defun file-notify--rm-descriptor (descriptor &optional what) | 52 | (defun file-notify--rm-descriptor (descriptor) |
| 52 | "Remove DESCRIPTOR from `file-notify-descriptors'. | 53 | "Remove DESCRIPTOR from `file-notify-descriptors'. |
| 53 | DESCRIPTOR should be an object returned by `file-notify-add-watch'. | 54 | DESCRIPTOR should be an object returned by `file-notify-add-watch'. |
| 54 | If it is registered in `file-notify-descriptors', a stopped event is sent. | 55 | If it is registered in `file-notify-descriptors', a stopped event is sent." |
| 55 | WHAT is a file or directory name to be removed, needed just for `inotify'." | ||
| 56 | (let* ((desc (if (consp descriptor) (car descriptor) descriptor)) | 56 | (let* ((desc (if (consp descriptor) (car descriptor) descriptor)) |
| 57 | (file (if (consp descriptor) (cdr descriptor))) | 57 | (file (if (consp descriptor) (cdr descriptor))) |
| 58 | (registered (gethash desc file-notify-descriptors)) | 58 | (registered (gethash desc file-notify-descriptors)) |
| 59 | (dir (car registered))) | 59 | (dir (car registered))) |
| 60 | 60 | ||
| 61 | (when (and (consp registered) (or (null what) (string-equal dir what))) | 61 | (when (consp registered) |
| 62 | ;; Send `stopped' event. | 62 | ;; Send `stopped' event. |
| 63 | (dolist (entry (cdr registered)) | 63 | (dolist (entry (cdr registered)) |
| 64 | (funcall (cdr entry) | 64 | (funcall (cdr entry) |
| @@ -76,7 +76,8 @@ WHAT is a file or directory name to be removed, needed just for `inotify'." | |||
| 76 | (remhash desc file-notify-descriptors) | 76 | (remhash desc file-notify-descriptors) |
| 77 | (puthash desc registered file-notify-descriptors)))))) | 77 | (puthash desc registered file-notify-descriptors)))))) |
| 78 | 78 | ||
| 79 | ;; This function is used by `gfilenotify', `inotify' and `w32notify' events. | 79 | ;; This function is used by `inotify', `kqueue', `gfilenotify' and |
| 80 | ;; `w32notify' events. | ||
| 80 | ;;;###autoload | 81 | ;;;###autoload |
| 81 | (defun file-notify-handle-event (event) | 82 | (defun file-notify-handle-event (event) |
| 82 | "Handle file system monitoring event. | 83 | "Handle file system monitoring event. |
| @@ -159,7 +160,7 @@ EVENT is the cadr of the event in `file-notify-handle-event' | |||
| 159 | (setq actions nil)) | 160 | (setq actions nil)) |
| 160 | 161 | ||
| 161 | ;; Loop over actions. In fact, more than one action happens only | 162 | ;; Loop over actions. In fact, more than one action happens only |
| 162 | ;; for `inotify'. | 163 | ;; for `inotify' and `kqueue'. |
| 163 | (dolist (action actions) | 164 | (dolist (action actions) |
| 164 | 165 | ||
| 165 | ;; Send pending event, if it doesn't match. | 166 | ;; Send pending event, if it doesn't match. |
| @@ -184,19 +185,17 @@ EVENT is the cadr of the event in `file-notify-handle-event' | |||
| 184 | ;; Map action. We ignore all events which cannot be mapped. | 185 | ;; Map action. We ignore all events which cannot be mapped. |
| 185 | (setq action | 186 | (setq action |
| 186 | (cond | 187 | (cond |
| 187 | ;; gfilenotify. | 188 | ((memq action |
| 188 | ((memq action '(attribute-changed changed created deleted)) | 189 | '(attribute-changed changed created deleted renamed)) |
| 189 | action) | 190 | action) |
| 190 | ((eq action 'moved) | 191 | ((memq action '(moved rename)) |
| 191 | (setq file1 (file-notify--event-file1-name event)) | 192 | (setq file1 (file-notify--event-file1-name event)) |
| 192 | 'renamed) | 193 | 'renamed) |
| 193 | |||
| 194 | ;; inotify, w32notify. | ||
| 195 | ((eq action 'ignored) | 194 | ((eq action 'ignored) |
| 196 | (setq stopped t actions nil)) | 195 | (setq stopped t actions nil)) |
| 197 | ((eq action 'attrib) 'attribute-changed) | 196 | ((memq action '(attrib link)) 'attribute-changed) |
| 198 | ((memq action '(create added)) 'created) | 197 | ((memq action '(create added)) 'created) |
| 199 | ((memq action '(modify modified)) 'changed) | 198 | ((memq action '(modify modified write)) 'changed) |
| 200 | ((memq action '(delete delete-self move-self removed)) 'deleted) | 199 | ((memq action '(delete delete-self move-self removed)) 'deleted) |
| 201 | ;; Make the event pending. | 200 | ;; Make the event pending. |
| 202 | ((memq action '(moved-from renamed-from)) | 201 | ((memq action '(moved-from renamed-from)) |
| @@ -236,7 +235,6 @@ EVENT is the cadr of the event in `file-notify-handle-event' | |||
| 236 | (setq pending-event nil)) | 235 | (setq pending-event nil)) |
| 237 | 236 | ||
| 238 | ;; Check for stopped. | 237 | ;; Check for stopped. |
| 239 | ;;(message "file-notify-callback %S %S" file registered) | ||
| 240 | (setq | 238 | (setq |
| 241 | stopped | 239 | stopped |
| 242 | (or | 240 | (or |
| @@ -244,10 +242,13 @@ EVENT is the cadr of the event in `file-notify-handle-event' | |||
| 244 | (and | 242 | (and |
| 245 | (memq action '(deleted renamed)) | 243 | (memq action '(deleted renamed)) |
| 246 | (= (length (cdr registered)) 1) | 244 | (= (length (cdr registered)) 1) |
| 247 | (string-equal | 245 | (or |
| 248 | (file-name-nondirectory file) | 246 | (string-equal |
| 249 | (or (file-name-nondirectory (car registered)) | 247 | (file-name-nondirectory file) |
| 250 | (car (cadr registered))))))) | 248 | (file-name-nondirectory (car registered))) |
| 249 | (string-equal | ||
| 250 | (file-name-nondirectory file) | ||
| 251 | (car (cadr registered))))))) | ||
| 251 | 252 | ||
| 252 | ;; Apply callback. | 253 | ;; Apply callback. |
| 253 | (when (and action | 254 | (when (and action |
| @@ -258,10 +259,17 @@ EVENT is the cadr of the event in `file-notify-handle-event' | |||
| 258 | ;; File matches. | 259 | ;; File matches. |
| 259 | (string-equal | 260 | (string-equal |
| 260 | (nth 0 entry) (file-name-nondirectory file)) | 261 | (nth 0 entry) (file-name-nondirectory file)) |
| 262 | ;; Directory matches. | ||
| 263 | (string-equal | ||
| 264 | (file-name-nondirectory file) | ||
| 265 | (file-name-nondirectory (car registered))) | ||
| 261 | ;; File1 matches. | 266 | ;; File1 matches. |
| 262 | (and (stringp file1) | 267 | (and (stringp file1) |
| 263 | (string-equal | 268 | (string-equal |
| 264 | (nth 0 entry) (file-name-nondirectory file1))))) | 269 | (nth 0 entry) (file-name-nondirectory file1))))) |
| 270 | ;;(message | ||
| 271 | ;;"file-notify-callback %S %S %S %S %S" | ||
| 272 | ;;(file-notify--descriptor desc file) action file file1 registered) | ||
| 265 | (if file1 | 273 | (if file1 |
| 266 | (funcall | 274 | (funcall |
| 267 | callback | 275 | callback |
| @@ -272,11 +280,10 @@ EVENT is the cadr of the event in `file-notify-handle-event' | |||
| 272 | 280 | ||
| 273 | ;; Modify `file-notify-descriptors'. | 281 | ;; Modify `file-notify-descriptors'. |
| 274 | (when stopped | 282 | (when stopped |
| 275 | (file-notify--rm-descriptor | 283 | (file-notify-rm-watch (file-notify--descriptor desc file)))))) |
| 276 | (file-notify--descriptor desc file) file))))) | ||
| 277 | 284 | ||
| 278 | ;; `gfilenotify' and `w32notify' return a unique descriptor for every | 285 | ;; `kqueue', `gfilenotify' and `w32notify' return a unique descriptor |
| 279 | ;; `file-notify-add-watch', while `inotify' returns a unique | 286 | ;; for every `file-notify-add-watch', while `inotify' returns a unique |
| 280 | ;; descriptor per inode only. | 287 | ;; descriptor per inode only. |
| 281 | (defun file-notify-add-watch (file flags callback) | 288 | (defun file-notify-add-watch (file flags callback) |
| 282 | "Add a watch for filesystem events pertaining to FILE. | 289 | "Add a watch for filesystem events pertaining to FILE. |
| @@ -329,7 +336,7 @@ FILE is the name of the file whose event is being reported." | |||
| 329 | (if (file-directory-p file) | 336 | (if (file-directory-p file) |
| 330 | file | 337 | file |
| 331 | (file-name-directory file)))) | 338 | (file-name-directory file)))) |
| 332 | desc func l-flags registered) | 339 | desc func l-flags registered entry) |
| 333 | 340 | ||
| 334 | (unless (file-directory-p dir) | 341 | (unless (file-directory-p dir) |
| 335 | (signal 'file-notify-error `("Directory does not exist" ,dir))) | 342 | (signal 'file-notify-error `("Directory does not exist" ,dir))) |
| @@ -338,7 +345,12 @@ FILE is the name of the file whose event is being reported." | |||
| 338 | ;; A file name handler could exist even if there is no local | 345 | ;; A file name handler could exist even if there is no local |
| 339 | ;; file notification support. | 346 | ;; file notification support. |
| 340 | (setq desc (funcall | 347 | (setq desc (funcall |
| 341 | handler 'file-notify-add-watch dir flags callback)) | 348 | handler 'file-notify-add-watch |
| 349 | ;; kqueue does not report file changes in | ||
| 350 | ;; directory monitor. So we must watch the file | ||
| 351 | ;; itself. | ||
| 352 | (if (eq file-notify--library 'kqueue) file dir) | ||
| 353 | flags callback)) | ||
| 342 | 354 | ||
| 343 | ;; Check, whether Emacs has been compiled with file notification | 355 | ;; Check, whether Emacs has been compiled with file notification |
| 344 | ;; support. | 356 | ;; support. |
| @@ -349,8 +361,9 @@ FILE is the name of the file whose event is being reported." | |||
| 349 | ;; Determine low-level function to be called. | 361 | ;; Determine low-level function to be called. |
| 350 | (setq func | 362 | (setq func |
| 351 | (cond | 363 | (cond |
| 352 | ((eq file-notify--library 'gfilenotify) 'gfile-add-watch) | ||
| 353 | ((eq file-notify--library 'inotify) 'inotify-add-watch) | 364 | ((eq file-notify--library 'inotify) 'inotify-add-watch) |
| 365 | ((eq file-notify--library 'kqueue) 'kqueue-add-watch) | ||
| 366 | ((eq file-notify--library 'gfilenotify) 'gfile-add-watch) | ||
| 354 | ((eq file-notify--library 'w32notify) 'w32notify-add-watch))) | 367 | ((eq file-notify--library 'w32notify) 'w32notify-add-watch))) |
| 355 | 368 | ||
| 356 | ;; Determine respective flags. | 369 | ;; Determine respective flags. |
| @@ -362,30 +375,32 @@ FILE is the name of the file whose event is being reported." | |||
| 362 | (cond | 375 | (cond |
| 363 | ((eq file-notify--library 'inotify) | 376 | ((eq file-notify--library 'inotify) |
| 364 | '(create delete delete-self modify move-self move)) | 377 | '(create delete delete-self modify move-self move)) |
| 378 | ((eq file-notify--library 'kqueue) | ||
| 379 | '(create delete write extend rename)) | ||
| 365 | ((eq file-notify--library 'w32notify) | 380 | ((eq file-notify--library 'w32notify) |
| 366 | '(file-name directory-name size last-write-time))))) | 381 | '(file-name directory-name size last-write-time))))) |
| 367 | (when (memq 'attribute-change flags) | 382 | (when (memq 'attribute-change flags) |
| 368 | (push (cond | 383 | (push (cond |
| 369 | ((eq file-notify--library 'inotify) 'attrib) | 384 | ((eq file-notify--library 'inotify) 'attrib) |
| 385 | ((eq file-notify--library 'kqueue) 'attrib) | ||
| 370 | ((eq file-notify--library 'w32notify) 'attributes)) | 386 | ((eq file-notify--library 'w32notify) 'attributes)) |
| 371 | l-flags))) | 387 | l-flags))) |
| 372 | 388 | ||
| 373 | ;; Call low-level function. | 389 | ;; Call low-level function. |
| 374 | (setq desc (funcall func dir l-flags 'file-notify-callback))) | 390 | (setq desc (funcall |
| 391 | func (if (eq file-notify--library 'kqueue) file dir) | ||
| 392 | l-flags 'file-notify-callback))) | ||
| 375 | 393 | ||
| 376 | ;; Modify `file-notify-descriptors'. | 394 | ;; Modify `file-notify-descriptors'. |
| 377 | (setq registered (gethash desc file-notify-descriptors)) | 395 | (setq file (unless (file-directory-p file) (file-name-nondirectory file)) |
| 378 | (puthash | 396 | desc (if (consp desc) (car desc) desc) |
| 379 | desc | 397 | registered (gethash desc file-notify-descriptors) |
| 380 | `(,dir | 398 | entry `(,file . ,callback)) |
| 381 | (,(unless (file-directory-p file) (file-name-nondirectory file)) | 399 | (unless (member entry (cdr registered)) |
| 382 | . ,callback) | 400 | (puthash desc `(,dir ,entry . ,(cdr registered)) file-notify-descriptors)) |
| 383 | . ,(cdr registered)) | ||
| 384 | file-notify-descriptors) | ||
| 385 | 401 | ||
| 386 | ;; Return descriptor. | 402 | ;; Return descriptor. |
| 387 | (file-notify--descriptor | 403 | (file-notify--descriptor desc file))) |
| 388 | desc (unless (file-directory-p file) (file-name-nondirectory file))))) | ||
| 389 | 404 | ||
| 390 | (defun file-notify-rm-watch (descriptor) | 405 | (defun file-notify-rm-watch (descriptor) |
| 391 | "Remove an existing watch specified by its DESCRIPTOR. | 406 | "Remove an existing watch specified by its DESCRIPTOR. |
| @@ -410,8 +425,9 @@ DESCRIPTOR should be an object returned by `file-notify-add-watch'." | |||
| 410 | 425 | ||
| 411 | (funcall | 426 | (funcall |
| 412 | (cond | 427 | (cond |
| 413 | ((eq file-notify--library 'gfilenotify) 'gfile-rm-watch) | ||
| 414 | ((eq file-notify--library 'inotify) 'inotify-rm-watch) | 428 | ((eq file-notify--library 'inotify) 'inotify-rm-watch) |
| 429 | ((eq file-notify--library 'kqueue) 'kqueue-rm-watch) | ||
| 430 | ((eq file-notify--library 'gfilenotify) 'gfile-rm-watch) | ||
| 415 | ((eq file-notify--library 'w32notify) 'w32notify-rm-watch)) | 431 | ((eq file-notify--library 'w32notify) 'w32notify-rm-watch)) |
| 416 | desc)) | 432 | desc)) |
| 417 | (file-notify-error nil))) | 433 | (file-notify-error nil))) |
| @@ -441,8 +457,9 @@ DESCRIPTOR should be an object returned by `file-notify-add-watch'." | |||
| 441 | (funcall handler 'file-notify-valid-p descriptor) | 457 | (funcall handler 'file-notify-valid-p descriptor) |
| 442 | (funcall | 458 | (funcall |
| 443 | (cond | 459 | (cond |
| 444 | ((eq file-notify--library 'gfilenotify) 'gfile-valid-p) | ||
| 445 | ((eq file-notify--library 'inotify) 'inotify-valid-p) | 460 | ((eq file-notify--library 'inotify) 'inotify-valid-p) |
| 461 | ((eq file-notify--library 'kqueue) 'kqueue-valid-p) | ||
| 462 | ((eq file-notify--library 'gfilenotify) 'gfile-valid-p) | ||
| 446 | ((eq file-notify--library 'w32notify) 'w32notify-valid-p)) | 463 | ((eq file-notify--library 'w32notify) 'w32notify-valid-p)) |
| 447 | desc)) | 464 | desc)) |
| 448 | t)))) | 465 | t)))) |
diff --git a/src/Makefile.in b/src/Makefile.in index 4a52757fec6..fab10aeed47 100644 --- a/src/Makefile.in +++ b/src/Makefile.in | |||
| @@ -163,12 +163,13 @@ SETTINGS_LIBS = @SETTINGS_LIBS@ | |||
| 163 | ## gtkutil.o if USE_GTK, else empty. | 163 | ## gtkutil.o if USE_GTK, else empty. |
| 164 | GTK_OBJ=@GTK_OBJ@ | 164 | GTK_OBJ=@GTK_OBJ@ |
| 165 | 165 | ||
| 166 | ## gfilenotify.o if HAVE_GFILENOTIFY. | ||
| 167 | ## inotify.o if HAVE_INOTIFY. | 166 | ## inotify.o if HAVE_INOTIFY. |
| 167 | ## kqueue.o if HAVE_KQUEUE. | ||
| 168 | ## gfilenotify.o if HAVE_GFILENOTIFY. | ||
| 168 | ## w32notify.o if HAVE_W32NOTIFY. | 169 | ## w32notify.o if HAVE_W32NOTIFY. |
| 169 | NOTIFY_OBJ = @NOTIFY_OBJ@ | 170 | NOTIFY_OBJ = @NOTIFY_OBJ@ |
| 170 | GFILENOTIFY_CFLAGS = @GFILENOTIFY_CFLAGS@ | 171 | NOTIFY_CFLAGS = @NOTIFY_CFLAGS@ |
| 171 | GFILENOTIFY_LIBS = @GFILENOTIFY_LIBS@ | 172 | NOTIFY_LIBS = @NOTIFY_LIBS@ |
| 172 | 173 | ||
| 173 | ## -ltermcap, or -lncurses, or -lcurses, or "". | 174 | ## -ltermcap, or -lncurses, or -lcurses, or "". |
| 174 | LIBS_TERMCAP=@LIBS_TERMCAP@ | 175 | LIBS_TERMCAP=@LIBS_TERMCAP@ |
| @@ -367,7 +368,7 @@ ALL_CFLAGS=-Demacs $(MYCPPFLAGS) -I. -I$(srcdir) \ | |||
| 367 | $(WEBKIT_CFLAGS) \ | 368 | $(WEBKIT_CFLAGS) \ |
| 368 | $(SETTINGS_CFLAGS) $(FREETYPE_CFLAGS) $(FONTCONFIG_CFLAGS) \ | 369 | $(SETTINGS_CFLAGS) $(FREETYPE_CFLAGS) $(FONTCONFIG_CFLAGS) \ |
| 369 | $(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \ | 370 | $(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \ |
| 370 | $(LIBGNUTLS_CFLAGS) $(GFILENOTIFY_CFLAGS) $(CAIRO_CFLAGS) \ | 371 | $(LIBGNUTLS_CFLAGS) $(NOTIFY_CFLAGS) $(CAIRO_CFLAGS) \ |
| 371 | $(WARN_CFLAGS) $(WERROR_CFLAGS) $(CFLAGS) | 372 | $(WARN_CFLAGS) $(WERROR_CFLAGS) $(CFLAGS) |
| 372 | ALL_OBJC_CFLAGS=$(ALL_CFLAGS) $(GNU_OBJC_CFLAGS) | 373 | ALL_OBJC_CFLAGS=$(ALL_CFLAGS) $(GNU_OBJC_CFLAGS) |
| 373 | 374 | ||
| @@ -482,7 +483,7 @@ LIBES = $(LIBS) $(W32_LIBS) $(LIBS_GNUSTEP) $(LIBX_BASE) $(LIBIMAGE) \ | |||
| 482 | $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \ | 483 | $(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \ |
| 483 | $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \ | 484 | $(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \ |
| 484 | $(LIBGNUTLS_LIBS) $(LIB_PTHREAD) \ | 485 | $(LIBGNUTLS_LIBS) $(LIB_PTHREAD) \ |
| 485 | $(GFILENOTIFY_LIBS) $(LIB_MATH) $(LIBZ) $(LIBMODULES) | 486 | $(NOTIFY_LIBS) $(LIB_MATH) $(LIBZ) $(LIBMODULES) |
| 486 | 487 | ||
| 487 | $(leimdir)/leim-list.el: bootstrap-emacs$(EXEEXT) | 488 | $(leimdir)/leim-list.el: bootstrap-emacs$(EXEEXT) |
| 488 | $(MAKE) -C ../leim leim-list.el EMACS="$(bootstrap_exe)" | 489 | $(MAKE) -C ../leim leim-list.el EMACS="$(bootstrap_exe)" |
diff --git a/src/emacs.c b/src/emacs.c index 6de0fffb904..ddaa82c1f64 100644 --- a/src/emacs.c +++ b/src/emacs.c | |||
| @@ -1360,6 +1360,10 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem | |||
| 1360 | tzset (); | 1360 | tzset (); |
| 1361 | #endif /* MSDOS */ | 1361 | #endif /* MSDOS */ |
| 1362 | 1362 | ||
| 1363 | #ifdef HAVE_KQUEUE | ||
| 1364 | globals_of_kqueue (); | ||
| 1365 | #endif | ||
| 1366 | |||
| 1363 | #ifdef HAVE_GFILENOTIFY | 1367 | #ifdef HAVE_GFILENOTIFY |
| 1364 | globals_of_gfilenotify (); | 1368 | globals_of_gfilenotify (); |
| 1365 | #endif | 1369 | #endif |
| @@ -1538,14 +1542,18 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem | |||
| 1538 | 1542 | ||
| 1539 | syms_of_gnutls (); | 1543 | syms_of_gnutls (); |
| 1540 | 1544 | ||
| 1541 | #ifdef HAVE_GFILENOTIFY | ||
| 1542 | syms_of_gfilenotify (); | ||
| 1543 | #endif /* HAVE_GFILENOTIFY */ | ||
| 1544 | |||
| 1545 | #ifdef HAVE_INOTIFY | 1545 | #ifdef HAVE_INOTIFY |
| 1546 | syms_of_inotify (); | 1546 | syms_of_inotify (); |
| 1547 | #endif /* HAVE_INOTIFY */ | 1547 | #endif /* HAVE_INOTIFY */ |
| 1548 | 1548 | ||
| 1549 | #ifdef HAVE_KQUEUE | ||
| 1550 | syms_of_kqueue (); | ||
| 1551 | #endif /* HAVE_KQUEUE */ | ||
| 1552 | |||
| 1553 | #ifdef HAVE_GFILENOTIFY | ||
| 1554 | syms_of_gfilenotify (); | ||
| 1555 | #endif /* HAVE_GFILENOTIFY */ | ||
| 1556 | |||
| 1549 | #ifdef HAVE_DBUS | 1557 | #ifdef HAVE_DBUS |
| 1550 | syms_of_dbusbind (); | 1558 | syms_of_dbusbind (); |
| 1551 | #endif /* HAVE_DBUS */ | 1559 | #endif /* HAVE_DBUS */ |
diff --git a/src/inotify.c b/src/inotify.c index 47652ff35bd..e0619e584f7 100644 --- a/src/inotify.c +++ b/src/inotify.c | |||
| @@ -46,8 +46,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | |||
| 46 | static int inotifyfd = -1; | 46 | static int inotifyfd = -1; |
| 47 | 47 | ||
| 48 | /* Assoc list of files being watched. | 48 | /* Assoc list of files being watched. |
| 49 | Format: | 49 | Format: (watch-descriptor name callback) |
| 50 | (watch-descriptor . callback) | ||
| 51 | */ | 50 | */ |
| 52 | static Lisp_Object watch_list; | 51 | static Lisp_Object watch_list; |
| 53 | 52 | ||
| @@ -106,12 +105,14 @@ inotifyevent_to_event (Lisp_Object watch_object, struct inotify_event const *ev) | |||
| 106 | name = make_unibyte_string (ev->name, min (len, ev->len)); | 105 | name = make_unibyte_string (ev->name, min (len, ev->len)); |
| 107 | name = DECODE_FILE (name); | 106 | name = DECODE_FILE (name); |
| 108 | } | 107 | } |
| 108 | else | ||
| 109 | name = XCAR (XCDR (watch_object)); | ||
| 109 | 110 | ||
| 110 | return list2 (list4 (make_watch_descriptor (ev->wd), | 111 | return list2 (list4 (make_watch_descriptor (ev->wd), |
| 111 | mask_to_aspects (ev->mask), | 112 | mask_to_aspects (ev->mask), |
| 112 | name, | 113 | name, |
| 113 | make_number (ev->cookie)), | 114 | make_number (ev->cookie)), |
| 114 | XCDR (watch_object)); | 115 | Fnth (make_number (2), watch_object)); |
| 115 | } | 116 | } |
| 116 | 117 | ||
| 117 | /* This callback is called when the FD is available for read. The inotify | 118 | /* This callback is called when the FD is available for read. The inotify |
| @@ -325,7 +326,7 @@ is managed internally and there is no corresponding inotify_init. Use | |||
| 325 | watch_list = Fdelete (watch_object, watch_list); | 326 | watch_list = Fdelete (watch_object, watch_list); |
| 326 | 327 | ||
| 327 | /* Store watch object in watch list. */ | 328 | /* Store watch object in watch list. */ |
| 328 | watch_object = Fcons (watch_descriptor, callback); | 329 | watch_object = list3 (watch_descriptor, encoded_file_name, callback); |
| 329 | watch_list = Fcons (watch_object, watch_list); | 330 | watch_list = Fcons (watch_object, watch_list); |
| 330 | 331 | ||
| 331 | return watch_descriptor; | 332 | return watch_descriptor; |
diff --git a/src/keyboard.c b/src/keyboard.c index 58831f548ca..c93876effd9 100644 --- a/src/keyboard.c +++ b/src/keyboard.c | |||
| @@ -5965,12 +5965,12 @@ make_lispy_event (struct input_event *event) | |||
| 5965 | #endif | 5965 | #endif |
| 5966 | 5966 | ||
| 5967 | 5967 | ||
| 5968 | #if defined HAVE_GFILENOTIFY || defined HAVE_INOTIFY | 5968 | #if defined HAVE_INOTIFY || defined HAVE_KQUEUE || defined HAVE_GFILENOTIFY |
| 5969 | case FILE_NOTIFY_EVENT: | 5969 | case FILE_NOTIFY_EVENT: |
| 5970 | { | 5970 | { |
| 5971 | return Fcons (Qfile_notify, event->arg); | 5971 | return Fcons (Qfile_notify, event->arg); |
| 5972 | } | 5972 | } |
| 5973 | #endif /* defined HAVE_GFILENOTIFY || defined HAVE_INOTIFY */ | 5973 | #endif /* HAVE_INOTIFY || HAVE_KQUEUE || HAVE_GFILENOTIFY */ |
| 5974 | 5974 | ||
| 5975 | case CONFIG_CHANGED_EVENT: | 5975 | case CONFIG_CHANGED_EVENT: |
| 5976 | return list3 (Qconfig_changed_event, | 5976 | return list3 (Qconfig_changed_event, |
diff --git a/src/kqueue.c b/src/kqueue.c new file mode 100644 index 00000000000..a69d06da3ae --- /dev/null +++ b/src/kqueue.c | |||
| @@ -0,0 +1,521 @@ | |||
| 1 | /* Filesystem notifications support with kqueue API. | ||
| 2 | |||
| 3 | Copyright (C) 2015-2016 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | This file is part of GNU Emacs. | ||
| 6 | |||
| 7 | GNU Emacs is free software: you can redistribute it and/or modify | ||
| 8 | it under the terms of the GNU General Public License as published by | ||
| 9 | the Free Software Foundation, either version 3 of the License, or | ||
| 10 | (at your option) any later version. | ||
| 11 | |||
| 12 | GNU Emacs is distributed in the hope that it will be useful, | ||
| 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 15 | GNU General Public License for more details. | ||
| 16 | |||
| 17 | You should have received a copy of the GNU General Public License | ||
| 18 | along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ | ||
| 19 | |||
| 20 | #include <config.h> | ||
| 21 | |||
| 22 | #ifdef HAVE_KQUEUE | ||
| 23 | #include <stdio.h> | ||
| 24 | #include <sys/types.h> | ||
| 25 | #include <sys/event.h> | ||
| 26 | #include <sys/time.h> | ||
| 27 | #include <sys/file.h> | ||
| 28 | #include "lisp.h" | ||
| 29 | #include "keyboard.h" | ||
| 30 | #include "process.h" | ||
| 31 | |||
| 32 | |||
| 33 | /* File handle for kqueue. */ | ||
| 34 | static int kqueuefd = -1; | ||
| 35 | |||
| 36 | /* This is a list, elements are (DESCRIPTOR FILE FLAGS CALLBACK [DIRLIST]). */ | ||
| 37 | static Lisp_Object watch_list; | ||
| 38 | |||
| 39 | /* Generate a list from the directory_files_internal output. | ||
| 40 | Items are (INODE FILE-NAME LAST-MOD LAST-STATUS-MOD SIZE). */ | ||
| 41 | Lisp_Object | ||
| 42 | kqueue_directory_listing (Lisp_Object directory_files) | ||
| 43 | { | ||
| 44 | Lisp_Object dl, result = Qnil; | ||
| 45 | |||
| 46 | for (dl = directory_files; ! NILP (dl); dl = XCDR (dl)) { | ||
| 47 | /* We ignore "." and "..". */ | ||
| 48 | if ((strcmp (".", SSDATA (XCAR (XCAR (dl)))) == 0) || | ||
| 49 | (strcmp ("..", SSDATA (XCAR (XCAR (dl)))) == 0)) | ||
| 50 | continue; | ||
| 51 | |||
| 52 | result = Fcons | ||
| 53 | (list5 (/* inode. */ | ||
| 54 | Fnth (make_number (11), XCAR (dl)), | ||
| 55 | /* filename. */ | ||
| 56 | XCAR (XCAR (dl)), | ||
| 57 | /* last modification time. */ | ||
| 58 | Fnth (make_number (6), XCAR (dl)), | ||
| 59 | /* last status change time. */ | ||
| 60 | Fnth (make_number (7), XCAR (dl)), | ||
| 61 | /* size. */ | ||
| 62 | Fnth (make_number (8), XCAR (dl))), | ||
| 63 | result); | ||
| 64 | } | ||
| 65 | return result; | ||
| 66 | } | ||
| 67 | |||
| 68 | /* Generate a file notification event. */ | ||
| 69 | static void | ||
| 70 | kqueue_generate_event | ||
| 71 | (Lisp_Object watch_object, Lisp_Object actions, | ||
| 72 | Lisp_Object file, Lisp_Object file1) | ||
| 73 | { | ||
| 74 | Lisp_Object flags, action, entry; | ||
| 75 | struct input_event event; | ||
| 76 | |||
| 77 | /* Check, whether all actions shall be monitored. */ | ||
| 78 | flags = Fnth (make_number (2), watch_object); | ||
| 79 | action = actions; | ||
| 80 | do { | ||
| 81 | if (NILP (action)) | ||
| 82 | break; | ||
| 83 | entry = XCAR (action); | ||
| 84 | if (NILP (Fmember (entry, flags))) { | ||
| 85 | action = XCDR (action); | ||
| 86 | actions = Fdelq (entry, actions); | ||
| 87 | } else | ||
| 88 | action = XCDR (action); | ||
| 89 | } while (1); | ||
| 90 | |||
| 91 | /* Store it into the input event queue. */ | ||
| 92 | if (! NILP (actions)) { | ||
| 93 | EVENT_INIT (event); | ||
| 94 | event.kind = FILE_NOTIFY_EVENT; | ||
| 95 | event.frame_or_window = Qnil; | ||
| 96 | event.arg = list2 (Fcons (XCAR (watch_object), | ||
| 97 | Fcons (actions, | ||
| 98 | NILP (file1) | ||
| 99 | ? Fcons (file, Qnil) | ||
| 100 | : list2 (file, file1))), | ||
| 101 | Fnth (make_number (3), watch_object)); | ||
| 102 | kbd_buffer_store_event (&event); | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | /* This compares two directory listings in case of a `write' event for | ||
| 107 | a directory. Generate resulting file notification events. The old | ||
| 108 | directory listing is retrieved from watch_object, it will be | ||
| 109 | replaced by the new directory listing at the end of this | ||
| 110 | function. */ | ||
| 111 | static void | ||
| 112 | kqueue_compare_dir_list | ||
| 113 | (Lisp_Object watch_object) | ||
| 114 | { | ||
| 115 | Lisp_Object dir, pending_dl, deleted_dl; | ||
| 116 | Lisp_Object old_directory_files, old_dl, new_directory_files, new_dl, dl; | ||
| 117 | |||
| 118 | dir = XCAR (XCDR (watch_object)); | ||
| 119 | pending_dl = Qnil; | ||
| 120 | deleted_dl = Qnil; | ||
| 121 | |||
| 122 | old_directory_files = Fnth (make_number (4), watch_object); | ||
| 123 | old_dl = kqueue_directory_listing (old_directory_files); | ||
| 124 | |||
| 125 | /* When the directory is not accessible anymore, it has been deleted. */ | ||
| 126 | if (NILP (Ffile_directory_p (dir))) { | ||
| 127 | kqueue_generate_event (watch_object, Fcons (Qdelete, Qnil), dir, Qnil); | ||
| 128 | return; | ||
| 129 | } | ||
| 130 | new_directory_files = | ||
| 131 | directory_files_internal (dir, Qnil, Qnil, Qnil, 1, Qnil); | ||
| 132 | new_dl = kqueue_directory_listing (new_directory_files); | ||
| 133 | |||
| 134 | /* Parse through the old list. */ | ||
| 135 | dl = old_dl; | ||
| 136 | while (1) { | ||
| 137 | Lisp_Object old_entry, new_entry, dl1; | ||
| 138 | if (NILP (dl)) | ||
| 139 | break; | ||
| 140 | |||
| 141 | /* Search for an entry with the same inode. */ | ||
| 142 | old_entry = XCAR (dl); | ||
| 143 | new_entry = assq_no_quit (XCAR (old_entry), new_dl); | ||
| 144 | if (! NILP (Fequal (old_entry, new_entry))) { | ||
| 145 | /* Both entries are identical. Nothing to do. */ | ||
| 146 | new_dl = Fdelq (new_entry, new_dl); | ||
| 147 | goto the_end; | ||
| 148 | } | ||
| 149 | |||
| 150 | /* Both entries have the same inode. */ | ||
| 151 | if (! NILP (new_entry)) { | ||
| 152 | /* Both entries have the same file name. */ | ||
| 153 | if (strcmp (SSDATA (XCAR (XCDR (old_entry))), | ||
| 154 | SSDATA (XCAR (XCDR (new_entry)))) == 0) { | ||
| 155 | /* Modification time has been changed, the file has been written. */ | ||
| 156 | if (NILP (Fequal (Fnth (make_number (2), old_entry), | ||
| 157 | Fnth (make_number (2), new_entry)))) | ||
| 158 | kqueue_generate_event | ||
| 159 | (watch_object, Fcons (Qwrite, Qnil), XCAR (XCDR (old_entry)), Qnil); | ||
| 160 | /* Status change time has been changed, the file attributes | ||
| 161 | have changed. */ | ||
| 162 | if (NILP (Fequal (Fnth (make_number (3), old_entry), | ||
| 163 | Fnth (make_number (3), new_entry)))) | ||
| 164 | kqueue_generate_event | ||
| 165 | (watch_object, Fcons (Qattrib, Qnil), | ||
| 166 | XCAR (XCDR (old_entry)), Qnil); | ||
| 167 | |||
| 168 | } else { | ||
| 169 | /* The file has been renamed. */ | ||
| 170 | kqueue_generate_event | ||
| 171 | (watch_object, Fcons (Qrename, Qnil), | ||
| 172 | XCAR (XCDR (old_entry)), XCAR (XCDR (new_entry))); | ||
| 173 | deleted_dl = Fcons (new_entry, deleted_dl); | ||
| 174 | } | ||
| 175 | new_dl = Fdelq (new_entry, new_dl); | ||
| 176 | goto the_end; | ||
| 177 | } | ||
| 178 | |||
| 179 | /* Search, whether there is a file with the same name but another | ||
| 180 | inode. */ | ||
| 181 | for (dl1 = new_dl; ! NILP (dl1); dl1 = XCDR (dl1)) { | ||
| 182 | new_entry = XCAR (dl1); | ||
| 183 | if (strcmp (SSDATA (XCAR (XCDR (old_entry))), | ||
| 184 | SSDATA (XCAR (XCDR (new_entry)))) == 0) { | ||
| 185 | pending_dl = Fcons (new_entry, pending_dl); | ||
| 186 | new_dl = Fdelq (new_entry, new_dl); | ||
| 187 | goto the_end; | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | /* Check, whether this a pending file. */ | ||
| 192 | new_entry = assq_no_quit (XCAR (old_entry), pending_dl); | ||
| 193 | |||
| 194 | if (NILP (new_entry)) { | ||
| 195 | /* Check, whether this is an already deleted file (by rename). */ | ||
| 196 | for (dl1 = deleted_dl; ! NILP (dl1); dl1 = XCDR (dl1)) { | ||
| 197 | new_entry = XCAR (dl1); | ||
| 198 | if (strcmp (SSDATA (XCAR (XCDR (old_entry))), | ||
| 199 | SSDATA (XCAR (XCDR (new_entry)))) == 0) { | ||
| 200 | deleted_dl = Fdelq (new_entry, deleted_dl); | ||
| 201 | goto the_end; | ||
| 202 | } | ||
| 203 | } | ||
| 204 | /* The file has been deleted. */ | ||
| 205 | kqueue_generate_event | ||
| 206 | (watch_object, Fcons (Qdelete, Qnil), XCAR (XCDR (old_entry)), Qnil); | ||
| 207 | |||
| 208 | } else { | ||
| 209 | /* The file has been renamed. */ | ||
| 210 | kqueue_generate_event | ||
| 211 | (watch_object, Fcons (Qrename, Qnil), | ||
| 212 | XCAR (XCDR (old_entry)), XCAR (XCDR (new_entry))); | ||
| 213 | pending_dl = Fdelq (new_entry, pending_dl); | ||
| 214 | } | ||
| 215 | |||
| 216 | the_end: | ||
| 217 | dl = XCDR (dl); | ||
| 218 | old_dl = Fdelq (old_entry, old_dl); | ||
| 219 | } | ||
| 220 | |||
| 221 | /* Parse through the resulting new list. */ | ||
| 222 | dl = new_dl; | ||
| 223 | while (1) { | ||
| 224 | Lisp_Object entry; | ||
| 225 | if (NILP (dl)) | ||
| 226 | break; | ||
| 227 | |||
| 228 | /* A new file has appeared. */ | ||
| 229 | entry = XCAR (dl); | ||
| 230 | kqueue_generate_event | ||
| 231 | (watch_object, Fcons (Qcreate, Qnil), XCAR (XCDR (entry)), Qnil); | ||
| 232 | |||
| 233 | /* Check size of that file. */ | ||
| 234 | Lisp_Object size = Fnth (make_number (4), entry); | ||
| 235 | if (FLOATP (size) || (XINT (size) > 0)) | ||
| 236 | kqueue_generate_event | ||
| 237 | (watch_object, Fcons (Qwrite, Qnil), XCAR (XCDR (entry)), Qnil); | ||
| 238 | |||
| 239 | dl = XCDR (dl); | ||
| 240 | new_dl = Fdelq (entry, new_dl); | ||
| 241 | } | ||
| 242 | |||
| 243 | /* Parse through the resulting pending_dl list. */ | ||
| 244 | dl = pending_dl; | ||
| 245 | while (1) { | ||
| 246 | Lisp_Object entry; | ||
| 247 | if (NILP (dl)) | ||
| 248 | break; | ||
| 249 | |||
| 250 | /* A file is still pending. Assume it was a write. */ | ||
| 251 | entry = XCAR (dl); | ||
| 252 | kqueue_generate_event | ||
| 253 | (watch_object, Fcons (Qwrite, Qnil), XCAR (XCDR (entry)), Qnil); | ||
| 254 | |||
| 255 | dl = XCDR (dl); | ||
| 256 | pending_dl = Fdelq (entry, pending_dl); | ||
| 257 | } | ||
| 258 | |||
| 259 | /* At this point, old_dl, new_dl and pending_dl shall be empty. | ||
| 260 | deleted_dl might not be empty when there was a rename to a | ||
| 261 | nonexistent file. Let's make a check for this (might be removed | ||
| 262 | once the code is stable). */ | ||
| 263 | if (! NILP (old_dl)) | ||
| 264 | report_file_error ("Old list not empty", old_dl); | ||
| 265 | if (! NILP (new_dl)) | ||
| 266 | report_file_error ("New list not empty", new_dl); | ||
| 267 | if (! NILP (pending_dl)) | ||
| 268 | report_file_error ("Pending events list not empty", pending_dl); | ||
| 269 | // if (! NILP (deleted_dl)) | ||
| 270 | // report_file_error ("Deleted events list not empty", deleted_dl); | ||
| 271 | |||
| 272 | /* Replace old directory listing with the new one. */ | ||
| 273 | XSETCDR (Fnthcdr (make_number (3), watch_object), | ||
| 274 | Fcons (new_directory_files, Qnil)); | ||
| 275 | return; | ||
| 276 | } | ||
| 277 | |||
| 278 | /* This is the callback function for arriving input on kqueuefd. It | ||
| 279 | shall create a Lisp event, and put it into the Emacs input queue. */ | ||
| 280 | static void | ||
| 281 | kqueue_callback (int fd, void *data) | ||
| 282 | { | ||
| 283 | for (;;) { | ||
| 284 | struct kevent kev; | ||
| 285 | static const struct timespec nullts = { 0, 0 }; | ||
| 286 | Lisp_Object descriptor, watch_object, file, actions; | ||
| 287 | |||
| 288 | /* Read one event. */ | ||
| 289 | int ret = kevent (kqueuefd, NULL, 0, &kev, 1, &nullts); | ||
| 290 | if (ret < 1) { | ||
| 291 | /* All events read. */ | ||
| 292 | return; | ||
| 293 | } | ||
| 294 | |||
| 295 | /* Determine descriptor and file name. */ | ||
| 296 | descriptor = make_number (kev.ident); | ||
| 297 | watch_object = assq_no_quit (descriptor, watch_list); | ||
| 298 | if (CONSP (watch_object)) | ||
| 299 | file = XCAR (XCDR (watch_object)); | ||
| 300 | else | ||
| 301 | continue; | ||
| 302 | |||
| 303 | /* Determine event actions. */ | ||
| 304 | actions = Qnil; | ||
| 305 | if (kev.fflags & NOTE_DELETE) | ||
| 306 | actions = Fcons (Qdelete, actions); | ||
| 307 | if (kev.fflags & NOTE_WRITE) { | ||
| 308 | /* Check, whether this is a directory event. */ | ||
| 309 | if (NILP (Fnth (make_number (4), watch_object))) | ||
| 310 | actions = Fcons (Qwrite, actions); | ||
| 311 | else | ||
| 312 | kqueue_compare_dir_list (watch_object); | ||
| 313 | } | ||
| 314 | if (kev.fflags & NOTE_EXTEND) | ||
| 315 | actions = Fcons (Qextend, actions); | ||
| 316 | if (kev.fflags & NOTE_ATTRIB) | ||
| 317 | actions = Fcons (Qattrib, actions); | ||
| 318 | if (kev.fflags & NOTE_LINK) | ||
| 319 | actions = Fcons (Qlink, actions); | ||
| 320 | /* It would be useful to know the target of the rename operation. | ||
| 321 | At this point, it is not possible. Happens only when the upper | ||
| 322 | directory is monitored. */ | ||
| 323 | if (kev.fflags & NOTE_RENAME) | ||
| 324 | actions = Fcons (Qrename, actions); | ||
| 325 | |||
| 326 | /* Create the event. */ | ||
| 327 | if (! NILP (actions)) | ||
| 328 | kqueue_generate_event (watch_object, actions, file, Qnil); | ||
| 329 | |||
| 330 | /* Cancel monitor if file or directory is deleted or renamed. */ | ||
| 331 | if (kev.fflags & (NOTE_DELETE | NOTE_RENAME)) | ||
| 332 | Fkqueue_rm_watch (descriptor); | ||
| 333 | } | ||
| 334 | return; | ||
| 335 | } | ||
| 336 | |||
| 337 | DEFUN ("kqueue-add-watch", Fkqueue_add_watch, Skqueue_add_watch, 3, 3, 0, | ||
| 338 | doc: /* Add a watch for filesystem events pertaining to FILE. | ||
| 339 | |||
| 340 | This arranges for filesystem events pertaining to FILE to be reported | ||
| 341 | to Emacs. Use `kqueue-rm-watch' to cancel the watch. | ||
| 342 | |||
| 343 | Returned value is a descriptor for the added watch. If the file cannot be | ||
| 344 | watched for some reason, this function signals a `file-notify-error' error. | ||
| 345 | |||
| 346 | FLAGS is a list of events to be watched for. It can include the | ||
| 347 | following symbols: | ||
| 348 | |||
| 349 | `create' -- FILE was created | ||
| 350 | `delete' -- FILE was deleted | ||
| 351 | `write' -- FILE has changed | ||
| 352 | `extend' -- FILE was extended | ||
| 353 | `attrib' -- a FILE attribute was changed | ||
| 354 | `link' -- a FILE's link count was changed | ||
| 355 | `rename' -- FILE was moved to FILE1 | ||
| 356 | |||
| 357 | When any event happens, Emacs will call the CALLBACK function passing | ||
| 358 | it a single argument EVENT, which is of the form | ||
| 359 | |||
| 360 | (DESCRIPTOR ACTIONS FILE [FILE1]) | ||
| 361 | |||
| 362 | DESCRIPTOR is the same object as the one returned by this function. | ||
| 363 | ACTIONS is a list of events. | ||
| 364 | |||
| 365 | FILE is the name of the file whose event is being reported. FILE1 | ||
| 366 | will be reported only in case of the `rename' event. This is possible | ||
| 367 | only when the upper directory of the renamed file is watched. */) | ||
| 368 | (Lisp_Object file, Lisp_Object flags, Lisp_Object callback) | ||
| 369 | { | ||
| 370 | Lisp_Object watch_object, dir_list; | ||
| 371 | int fd, oflags; | ||
| 372 | u_short fflags = 0; | ||
| 373 | struct kevent kev; | ||
| 374 | |||
| 375 | /* Check parameters. */ | ||
| 376 | CHECK_STRING (file); | ||
| 377 | file = Fdirectory_file_name (Fexpand_file_name (file, Qnil)); | ||
| 378 | if (NILP (Ffile_exists_p (file))) | ||
| 379 | report_file_error ("File does not exist", file); | ||
| 380 | |||
| 381 | CHECK_LIST (flags); | ||
| 382 | |||
| 383 | if (! FUNCTIONP (callback)) | ||
| 384 | wrong_type_argument (Qinvalid_function, callback); | ||
| 385 | |||
| 386 | if (kqueuefd < 0) | ||
| 387 | { | ||
| 388 | /* Create kqueue descriptor. */ | ||
| 389 | kqueuefd = kqueue (); | ||
| 390 | if (kqueuefd < 0) | ||
| 391 | report_file_notify_error ("File watching is not available", Qnil); | ||
| 392 | |||
| 393 | /* Start monitoring for possible I/O. */ | ||
| 394 | add_read_fd (kqueuefd, kqueue_callback, NULL); | ||
| 395 | |||
| 396 | watch_list = Qnil; | ||
| 397 | } | ||
| 398 | |||
| 399 | /* Open file. */ | ||
| 400 | file = ENCODE_FILE (file); | ||
| 401 | oflags = O_NONBLOCK; | ||
| 402 | #if O_EVTONLY | ||
| 403 | oflags |= O_EVTONLY; | ||
| 404 | #else | ||
| 405 | oflags |= O_RDONLY; | ||
| 406 | #endif | ||
| 407 | #if O_SYMLINK | ||
| 408 | oflags |= O_SYMLINK; | ||
| 409 | #else | ||
| 410 | oflags |= O_NOFOLLOW; | ||
| 411 | #endif | ||
| 412 | fd = emacs_open (SSDATA (file), oflags, 0); | ||
| 413 | if (fd == -1) | ||
| 414 | report_file_error ("File cannot be opened", file); | ||
| 415 | |||
| 416 | /* Assemble filter flags */ | ||
| 417 | if (! NILP (Fmember (Qdelete, flags))) fflags |= NOTE_DELETE; | ||
| 418 | if (! NILP (Fmember (Qwrite, flags))) fflags |= NOTE_WRITE; | ||
| 419 | if (! NILP (Fmember (Qextend, flags))) fflags |= NOTE_EXTEND; | ||
| 420 | if (! NILP (Fmember (Qattrib, flags))) fflags |= NOTE_ATTRIB; | ||
| 421 | if (! NILP (Fmember (Qlink, flags))) fflags |= NOTE_LINK; | ||
| 422 | if (! NILP (Fmember (Qrename, flags))) fflags |= NOTE_RENAME; | ||
| 423 | |||
| 424 | /* Register event. */ | ||
| 425 | EV_SET (&kev, fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, | ||
| 426 | fflags, 0, NULL); | ||
| 427 | |||
| 428 | if (kevent (kqueuefd, &kev, 1, NULL, 0, NULL) < 0) { | ||
| 429 | emacs_close (fd); | ||
| 430 | report_file_error ("Cannot watch file", file); | ||
| 431 | } | ||
| 432 | |||
| 433 | /* Store watch object in watch list. */ | ||
| 434 | Lisp_Object watch_descriptor = make_number (fd); | ||
| 435 | if (NILP (Ffile_directory_p (file))) | ||
| 436 | watch_object = list4 (watch_descriptor, file, flags, callback); | ||
| 437 | else { | ||
| 438 | dir_list = directory_files_internal (file, Qnil, Qnil, Qnil, 1, Qnil); | ||
| 439 | watch_object = list5 (watch_descriptor, file, flags, callback, dir_list); | ||
| 440 | } | ||
| 441 | watch_list = Fcons (watch_object, watch_list); | ||
| 442 | |||
| 443 | return watch_descriptor; | ||
| 444 | } | ||
| 445 | |||
| 446 | DEFUN ("kqueue-rm-watch", Fkqueue_rm_watch, Skqueue_rm_watch, 1, 1, 0, | ||
| 447 | doc: /* Remove an existing WATCH-DESCRIPTOR. | ||
| 448 | |||
| 449 | WATCH-DESCRIPTOR should be an object returned by `kqueue-add-watch'. */) | ||
| 450 | (Lisp_Object watch_descriptor) | ||
| 451 | { | ||
| 452 | Lisp_Object watch_object = assq_no_quit (watch_descriptor, watch_list); | ||
| 453 | |||
| 454 | if (! CONSP (watch_object)) | ||
| 455 | xsignal2 (Qfile_notify_error, build_string ("Not a watch descriptor"), | ||
| 456 | watch_descriptor); | ||
| 457 | |||
| 458 | eassert (INTEGERP (watch_descriptor)); | ||
| 459 | int fd = XINT (watch_descriptor); | ||
| 460 | if ( fd >= 0) | ||
| 461 | emacs_close (fd); | ||
| 462 | |||
| 463 | /* Remove watch descriptor from watch list. */ | ||
| 464 | watch_list = Fdelq (watch_object, watch_list); | ||
| 465 | |||
| 466 | if (NILP (watch_list) && (kqueuefd >= 0)) { | ||
| 467 | delete_read_fd (kqueuefd); | ||
| 468 | emacs_close (kqueuefd); | ||
| 469 | kqueuefd = -1; | ||
| 470 | } | ||
| 471 | |||
| 472 | return Qt; | ||
| 473 | } | ||
| 474 | |||
| 475 | DEFUN ("kqueue-valid-p", Fkqueue_valid_p, Skqueue_valid_p, 1, 1, 0, | ||
| 476 | doc: /* "Check a watch specified by its WATCH-DESCRIPTOR. | ||
| 477 | |||
| 478 | WATCH-DESCRIPTOR should be an object returned by `kqueue-add-watch'. | ||
| 479 | |||
| 480 | A watch can become invalid if the file or directory it watches is | ||
| 481 | deleted, or if the watcher thread exits abnormally for any other | ||
| 482 | reason. Removing the watch by calling `kqueue-rm-watch' also makes it | ||
| 483 | invalid. */) | ||
| 484 | (Lisp_Object watch_descriptor) | ||
| 485 | { | ||
| 486 | return NILP (assq_no_quit (watch_descriptor, watch_list)) ? Qnil : Qt; | ||
| 487 | } | ||
| 488 | |||
| 489 | |||
| 490 | void | ||
| 491 | globals_of_kqueue (void) | ||
| 492 | { | ||
| 493 | watch_list = Qnil; | ||
| 494 | } | ||
| 495 | |||
| 496 | void | ||
| 497 | syms_of_kqueue (void) | ||
| 498 | { | ||
| 499 | defsubr (&Skqueue_add_watch); | ||
| 500 | defsubr (&Skqueue_rm_watch); | ||
| 501 | defsubr (&Skqueue_valid_p); | ||
| 502 | |||
| 503 | /* Event types. */ | ||
| 504 | DEFSYM (Qcreate, "create"); | ||
| 505 | DEFSYM (Qdelete, "delete"); /* NOTE_DELETE */ | ||
| 506 | DEFSYM (Qwrite, "write"); /* NOTE_WRITE */ | ||
| 507 | DEFSYM (Qextend, "extend"); /* NOTE_EXTEND */ | ||
| 508 | DEFSYM (Qattrib, "attrib"); /* NOTE_ATTRIB */ | ||
| 509 | DEFSYM (Qlink, "link"); /* NOTE_LINK */ | ||
| 510 | DEFSYM (Qrename, "rename"); /* NOTE_RENAME */ | ||
| 511 | |||
| 512 | staticpro (&watch_list); | ||
| 513 | |||
| 514 | Fprovide (intern_c_string ("kqueue"), Qnil); | ||
| 515 | } | ||
| 516 | |||
| 517 | #endif /* HAVE_KQUEUE */ | ||
| 518 | |||
| 519 | /* PROBLEMS | ||
| 520 | * https://bugs.launchpad.net/ubuntu/+source/libkqueue/+bug/1514837 | ||
| 521 | prevents tests on Ubuntu. */ | ||
diff --git a/src/lisp.h b/src/lisp.h index b0a8d75c439..8aa034e9e57 100644 --- a/src/lisp.h +++ b/src/lisp.h | |||
| @@ -4319,17 +4319,23 @@ extern void init_font (void); | |||
| 4319 | extern void syms_of_fontset (void); | 4319 | extern void syms_of_fontset (void); |
| 4320 | #endif | 4320 | #endif |
| 4321 | 4321 | ||
| 4322 | /* Defined in inotify.c */ | ||
| 4323 | #ifdef HAVE_INOTIFY | ||
| 4324 | extern void syms_of_inotify (void); | ||
| 4325 | #endif | ||
| 4326 | |||
| 4327 | /* Defined in kqueue.c */ | ||
| 4328 | #ifdef HAVE_KQUEUE | ||
| 4329 | extern void globals_of_kqueue (void); | ||
| 4330 | extern void syms_of_kqueue (void); | ||
| 4331 | #endif | ||
| 4332 | |||
| 4322 | /* Defined in gfilenotify.c */ | 4333 | /* Defined in gfilenotify.c */ |
| 4323 | #ifdef HAVE_GFILENOTIFY | 4334 | #ifdef HAVE_GFILENOTIFY |
| 4324 | extern void globals_of_gfilenotify (void); | 4335 | extern void globals_of_gfilenotify (void); |
| 4325 | extern void syms_of_gfilenotify (void); | 4336 | extern void syms_of_gfilenotify (void); |
| 4326 | #endif | 4337 | #endif |
| 4327 | 4338 | ||
| 4328 | /* Defined in inotify.c */ | ||
| 4329 | #ifdef HAVE_INOTIFY | ||
| 4330 | extern void syms_of_inotify (void); | ||
| 4331 | #endif | ||
| 4332 | |||
| 4333 | #ifdef HAVE_W32NOTIFY | 4339 | #ifdef HAVE_W32NOTIFY |
| 4334 | /* Defined on w32notify.c. */ | 4340 | /* Defined on w32notify.c. */ |
| 4335 | extern void syms_of_w32notify (void); | 4341 | extern void syms_of_w32notify (void); |
diff --git a/test/automated/file-notify-tests.el b/test/automated/file-notify-tests.el index 7bf7b0b3d15..de64f5086d2 100644 --- a/test/automated/file-notify-tests.el +++ b/test/automated/file-notify-tests.el | |||
| @@ -61,11 +61,14 @@ | |||
| 61 | (defvar file-notify--test-results nil) | 61 | (defvar file-notify--test-results nil) |
| 62 | (defvar file-notify--test-event nil) | 62 | (defvar file-notify--test-event nil) |
| 63 | (defvar file-notify--test-events nil) | 63 | (defvar file-notify--test-events nil) |
| 64 | (defvar file-notify--test-expected-events nil) | ||
| 65 | 64 | ||
| 66 | (defun file-notify--test-timeout () | 65 | (defun file-notify--test-timeout () |
| 67 | "Timeout to wait for arriving events, in seconds." | 66 | "Timeout to wait for arriving events, in seconds." |
| 68 | (if (file-remote-p temporary-file-directory) 6 3)) | 67 | (cond |
| 68 | ((file-remote-p temporary-file-directory) 6) | ||
| 69 | ((string-equal (file-notify--test-library) "w32notify") 20) | ||
| 70 | ((eq system-type 'cygwin) 10) | ||
| 71 | (t 3))) | ||
| 69 | 72 | ||
| 70 | (defun file-notify--test-cleanup () | 73 | (defun file-notify--test-cleanup () |
| 71 | "Cleanup after a test." | 74 | "Cleanup after a test." |
| @@ -89,8 +92,7 @@ | |||
| 89 | file-notify--test-tmpfile1 nil | 92 | file-notify--test-tmpfile1 nil |
| 90 | file-notify--test-desc nil | 93 | file-notify--test-desc nil |
| 91 | file-notify--test-results nil | 94 | file-notify--test-results nil |
| 92 | file-notify--test-events nil | 95 | file-notify--test-events nil) |
| 93 | file-notify--test-expected-events nil) | ||
| 94 | (when file-notify--test-event | 96 | (when file-notify--test-event |
| 95 | (error "file-notify--test-event should not be set but bound dynamically"))) | 97 | (error "file-notify--test-event should not be set but bound dynamically"))) |
| 96 | 98 | ||
| @@ -133,6 +135,18 @@ being the result.") | |||
| 133 | ;; Return result. | 135 | ;; Return result. |
| 134 | (cdr file-notify--test-remote-enabled-checked)) | 136 | (cdr file-notify--test-remote-enabled-checked)) |
| 135 | 137 | ||
| 138 | (defun file-notify--test-library () | ||
| 139 | "The used library for the test, as a string. | ||
| 140 | In the remote case, it is the process name which runs on the | ||
| 141 | remote host, or nil." | ||
| 142 | (if (null (file-remote-p temporary-file-directory)) | ||
| 143 | (symbol-name file-notify--library) | ||
| 144 | (and (consp file-notify--test-remote-enabled-checked) | ||
| 145 | (processp (cdr file-notify--test-remote-enabled-checked)) | ||
| 146 | (replace-regexp-in-string | ||
| 147 | "<[[:digit:]]+>\\'" "" | ||
| 148 | (process-name (cdr file-notify--test-remote-enabled-checked)))))) | ||
| 149 | |||
| 136 | (defmacro file-notify--deftest-remote (test docstring) | 150 | (defmacro file-notify--deftest-remote (test docstring) |
| 137 | "Define ert `TEST-remote' for remote files." | 151 | "Define ert `TEST-remote' for remote files." |
| 138 | (declare (indent 1)) | 152 | (declare (indent 1)) |
| @@ -151,12 +165,7 @@ being the result.") | |||
| 151 | "Test availability of `file-notify'." | 165 | "Test availability of `file-notify'." |
| 152 | (skip-unless (file-notify--test-local-enabled)) | 166 | (skip-unless (file-notify--test-local-enabled)) |
| 153 | ;; Report the native library which has been used. | 167 | ;; Report the native library which has been used. |
| 154 | (if (null (file-remote-p temporary-file-directory)) | 168 | (message "Library: `%s'" (file-notify--test-library)) |
| 155 | (message "Local library: `%s'" file-notify--library) | ||
| 156 | (message "Remote command: `%s'" | ||
| 157 | (replace-regexp-in-string | ||
| 158 | "<[[:digit:]]+>\\'" "" | ||
| 159 | (process-name (cdr file-notify--test-remote-enabled-checked))))) | ||
| 160 | (should | 169 | (should |
| 161 | (setq file-notify--test-desc | 170 | (setq file-notify--test-desc |
| 162 | (file-notify-add-watch temporary-file-directory '(change) 'ignore))) | 171 | (file-notify-add-watch temporary-file-directory '(change) 'ignore))) |
| @@ -190,12 +199,13 @@ being the result.") | |||
| 190 | (file-notify-add-watch | 199 | (file-notify-add-watch |
| 191 | temporary-file-directory '(change attribute-change) 'ignore))) | 200 | temporary-file-directory '(change attribute-change) 'ignore))) |
| 192 | (file-notify-rm-watch file-notify--test-desc) | 201 | (file-notify-rm-watch file-notify--test-desc) |
| 193 | ;; The file does not need to exist, just the upper directory. | 202 | (write-region "any text" nil file-notify--test-tmpfile nil 'no-message) |
| 194 | (should | 203 | (should |
| 195 | (setq file-notify--test-desc | 204 | (setq file-notify--test-desc |
| 196 | (file-notify-add-watch | 205 | (file-notify-add-watch |
| 197 | file-notify--test-tmpfile '(change attribute-change) 'ignore))) | 206 | file-notify--test-tmpfile '(change attribute-change) 'ignore))) |
| 198 | (file-notify-rm-watch file-notify--test-desc) | 207 | (file-notify-rm-watch file-notify--test-desc) |
| 208 | (delete-file file-notify--test-tmpfile) | ||
| 199 | 209 | ||
| 200 | ;; Check error handling. | 210 | ;; Check error handling. |
| 201 | (should-error (file-notify-add-watch 1 2 3 4) | 211 | (should-error (file-notify-add-watch 1 2 3 4) |
| @@ -236,16 +246,17 @@ is bound somewhere." | |||
| 236 | (should | 246 | (should |
| 237 | (or (string-equal (file-notify--event-file-name file-notify--test-event) | 247 | (or (string-equal (file-notify--event-file-name file-notify--test-event) |
| 238 | file-notify--test-tmpfile) | 248 | file-notify--test-tmpfile) |
| 239 | (string-equal (directory-file-name | 249 | (string-equal (file-notify--event-file-name file-notify--test-event) |
| 240 | (file-name-directory | 250 | file-notify--test-tmpfile1) |
| 241 | (file-notify--event-file-name file-notify--test-event))) | 251 | (string-equal (file-notify--event-file-name file-notify--test-event) |
| 242 | file-notify--test-tmpfile))) | 252 | temporary-file-directory))) |
| 243 | ;; Check the second file name if exists. | 253 | ;; Check the second file name if exists. |
| 244 | (when (eq (nth 1 file-notify--test-event) 'renamed) | 254 | (when (eq (nth 1 file-notify--test-event) 'renamed) |
| 245 | (should | 255 | (should |
| 246 | (string-equal | 256 | (or (string-equal (file-notify--event-file1-name file-notify--test-event) |
| 247 | (file-notify--event-file1-name file-notify--test-event) | 257 | file-notify--test-tmpfile1) |
| 248 | file-notify--test-tmpfile1)))) | 258 | (string-equal (file-notify--event-file1-name file-notify--test-event) |
| 259 | temporary-file-directory))))) | ||
| 249 | 260 | ||
| 250 | (defun file-notify--test-event-handler (event) | 261 | (defun file-notify--test-event-handler (event) |
| 251 | "Run a test over FILE-NOTIFY--TEST-EVENT. | 262 | "Run a test over FILE-NOTIFY--TEST-EVENT. |
| @@ -254,7 +265,7 @@ and the event to `file-notify--test-events'." | |||
| 254 | (let* ((file-notify--test-event event) | 265 | (let* ((file-notify--test-event event) |
| 255 | (result | 266 | (result |
| 256 | (ert-run-test (make-ert-test :body 'file-notify--test-event-test)))) | 267 | (ert-run-test (make-ert-test :body 'file-notify--test-event-test)))) |
| 257 | ;; Do not add temporary files, this would confuse the checks. | 268 | ;; Do not add lock files, this would confuse the checks. |
| 258 | (unless (string-match | 269 | (unless (string-match |
| 259 | (regexp-quote ".#") | 270 | (regexp-quote ".#") |
| 260 | (file-notify--event-file-name file-notify--test-event)) | 271 | (file-notify--event-file-name file-notify--test-event)) |
| @@ -278,137 +289,246 @@ TIMEOUT is the maximum time to wait for, in seconds." | |||
| 278 | 289 | ||
| 279 | (defmacro file-notify--test-with-events (events &rest body) | 290 | (defmacro file-notify--test-with-events (events &rest body) |
| 280 | "Run BODY collecting events and then compare with EVENTS. | 291 | "Run BODY collecting events and then compare with EVENTS. |
| 281 | Don't wait longer than timeout seconds for the events to be delivered." | 292 | EVENTS is either a simple list of events, or a list of lists of |
| 293 | events, which represent different possible results. Don't wait | ||
| 294 | longer than timeout seconds for the events to be delivered." | ||
| 282 | (declare (indent 1)) | 295 | (declare (indent 1)) |
| 283 | (let ((outer (make-symbol "outer"))) | 296 | (let ((outer (make-symbol "outer"))) |
| 284 | `(let ((,outer file-notify--test-events)) | 297 | `(let* ((,outer file-notify--test-events) |
| 285 | (setq file-notify--test-expected-events | 298 | (events (if (consp (car ,events)) ,events (list ,events))) |
| 286 | (append file-notify--test-expected-events ,events)) | 299 | (max-length (apply 'max (mapcar 'length events))) |
| 300 | create-lockfiles result) | ||
| 301 | ;; Flush pending events. | ||
| 302 | (file-notify--wait-for-events | ||
| 303 | (file-notify--test-timeout) | ||
| 304 | (input-pending-p)) | ||
| 287 | (let (file-notify--test-events) | 305 | (let (file-notify--test-events) |
| 288 | ,@body | 306 | ,@body |
| 289 | (file-notify--wait-for-events | 307 | (file-notify--wait-for-events |
| 290 | (file-notify--test-timeout) | 308 | ;; More events need more time. Use some fudge factor. |
| 291 | (= (length ,events) (length file-notify--test-events))) | 309 | (* (ceiling max-length 100) (file-notify--test-timeout)) |
| 292 | (should (equal ,events (mapcar #'cadr file-notify--test-events))) | 310 | (= max-length (length file-notify--test-events))) |
| 311 | ;; One of the possible results shall match. | ||
| 312 | (should | ||
| 313 | (dolist (elt events result) | ||
| 314 | (setq result | ||
| 315 | (or result | ||
| 316 | (equal elt (mapcar #'cadr file-notify--test-events)))))) | ||
| 293 | (setq ,outer (append ,outer file-notify--test-events))) | 317 | (setq ,outer (append ,outer file-notify--test-events))) |
| 294 | (setq file-notify--test-events ,outer)))) | 318 | (setq file-notify--test-events ,outer)))) |
| 295 | 319 | ||
| 296 | (ert-deftest file-notify-test02-events () | 320 | (ert-deftest file-notify-test02-events () |
| 297 | "Check file creation/change/removal notifications." | 321 | "Check file creation/change/removal notifications." |
| 298 | (skip-unless (file-notify--test-local-enabled)) | 322 | (skip-unless (file-notify--test-local-enabled)) |
| 299 | ;; Under cygwin there are so bad timings that it doesn't make sense to test. | ||
| 300 | (skip-unless (not (eq system-type 'cygwin))) | ||
| 301 | |||
| 302 | (setq file-notify--test-tmpfile (file-notify--test-make-temp-name) | ||
| 303 | file-notify--test-tmpfile1 (file-notify--test-make-temp-name)) | ||
| 304 | 323 | ||
| 305 | (unwind-protect | 324 | (unwind-protect |
| 306 | (progn | 325 | (progn |
| 307 | ;; Check creation, change and deletion. | 326 | ;; Check file creation, change and deletion. It doesn't work |
| 308 | (setq file-notify--test-desc | 327 | ;; for cygwin and kqueue, because we don't use an implicit |
| 309 | (file-notify-add-watch | 328 | ;; directory monitor (kqueue), or the timings are too bad (cygwin). |
| 310 | file-notify--test-tmpfile | 329 | (unless (or (eq system-type 'cygwin) |
| 311 | '(change) 'file-notify--test-event-handler)) | 330 | (string-equal (file-notify--test-library) "kqueue")) |
| 312 | (file-notify--test-with-events '(created changed deleted) | 331 | (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)) |
| 332 | (should | ||
| 333 | (setq file-notify--test-desc | ||
| 334 | (file-notify-add-watch | ||
| 335 | file-notify--test-tmpfile | ||
| 336 | '(change) 'file-notify--test-event-handler))) | ||
| 337 | (file-notify--test-with-events | ||
| 338 | (cond | ||
| 339 | ;; cygwin recognizes only `deleted' and `stopped' events. | ||
| 340 | ((eq system-type 'cygwin) | ||
| 341 | '(deleted stopped)) | ||
| 342 | (t '(created changed deleted stopped))) | ||
| 343 | (write-region | ||
| 344 | "another text" nil file-notify--test-tmpfile nil 'no-message) | ||
| 345 | (read-event nil nil 0.1) | ||
| 346 | (delete-file file-notify--test-tmpfile)) | ||
| 347 | ;; `file-notify-rm-watch' fires the `stopped' event. Suppress it. | ||
| 348 | (let (file-notify--test-events) | ||
| 349 | (file-notify-rm-watch file-notify--test-desc))) | ||
| 350 | |||
| 351 | ;; Check file change and deletion. | ||
| 352 | (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)) | ||
| 353 | (write-region "any text" nil file-notify--test-tmpfile nil 'no-message) | ||
| 354 | (should | ||
| 355 | (setq file-notify--test-desc | ||
| 356 | (file-notify-add-watch | ||
| 357 | file-notify--test-tmpfile | ||
| 358 | '(change) 'file-notify--test-event-handler))) | ||
| 359 | (file-notify--test-with-events | ||
| 360 | (cond | ||
| 361 | ;; cygwin recognizes only `deleted' and `stopped' events. | ||
| 362 | ((eq system-type 'cygwin) | ||
| 363 | '(deleted stopped)) | ||
| 364 | ;; inotify and kqueue raise just one `changed' event. | ||
| 365 | ((or (string-equal "inotify" (file-notify--test-library)) | ||
| 366 | (string-equal "kqueue" (file-notify--test-library))) | ||
| 367 | '(changed deleted stopped)) | ||
| 368 | ;; gfilenotify raises one or two `changed' events | ||
| 369 | ;; randomly, no chance to test. So we accept both cases. | ||
| 370 | ((string-equal "gfilenotify" (file-notify--test-library)) | ||
| 371 | '((changed deleted stopped) | ||
| 372 | (changed changed deleted stopped))) | ||
| 373 | (t '(changed changed deleted stopped))) | ||
| 374 | (read-event nil nil 0.1) | ||
| 313 | (write-region | 375 | (write-region |
| 314 | "any text" nil file-notify--test-tmpfile nil 'no-message) | 376 | "another text" nil file-notify--test-tmpfile nil 'no-message) |
| 377 | (read-event nil nil 0.1) | ||
| 315 | (delete-file file-notify--test-tmpfile)) | 378 | (delete-file file-notify--test-tmpfile)) |
| 316 | ;; `file-notify-rm-watch' fires the `stopped' event. Suppress it. | 379 | ;; `file-notify-rm-watch' fires the `stopped' event. Suppress it. |
| 317 | (let (file-notify--test-events) | 380 | (let (file-notify--test-events) |
| 318 | (file-notify-rm-watch file-notify--test-desc)) | 381 | (file-notify-rm-watch file-notify--test-desc)) |
| 319 | 382 | ||
| 320 | ;; Check creation, change and deletion. There must be a | 383 | ;; Check file creation, change and deletion when watching a |
| 321 | ;; `stopped' event when deleting the directory. It doesn't | 384 | ;; directory. There must be a `stopped' event when deleting |
| 322 | ;; work for w32notify. | 385 | ;; the directory. |
| 323 | (unless (eq file-notify--library 'w32notify) | 386 | (let ((temporary-file-directory |
| 324 | (make-directory file-notify--test-tmpfile) | 387 | (make-temp-file "file-notify-test-parent" t))) |
| 325 | (setq file-notify--test-desc | 388 | (should |
| 326 | (file-notify-add-watch | 389 | (setq file-notify--test-tmpfile (file-notify--test-make-temp-name) |
| 327 | file-notify--test-tmpfile | 390 | file-notify--test-desc |
| 328 | '(change) 'file-notify--test-event-handler)) | 391 | (file-notify-add-watch |
| 392 | temporary-file-directory | ||
| 393 | '(change) 'file-notify--test-event-handler))) | ||
| 329 | (file-notify--test-with-events | 394 | (file-notify--test-with-events |
| 330 | ;; There are two `deleted' events, for the file and for | 395 | (cond |
| 331 | ;; the directory. | 396 | ;; w32notify does raise a `stopped' event when a |
| 332 | '(created changed deleted deleted stopped) | 397 | ;; watched directory is deleted. |
| 398 | ((string-equal (file-notify--test-library) "w32notify") | ||
| 399 | '(created changed deleted)) | ||
| 400 | ;; cygwin recognizes only `deleted' and `stopped' events. | ||
| 401 | ((eq system-type 'cygwin) | ||
| 402 | '(deleted stopped)) | ||
| 403 | ;; There are two `deleted' events, for the file and for | ||
| 404 | ;; the directory. Except for kqueue. | ||
| 405 | ((string-equal (file-notify--test-library) "kqueue") | ||
| 406 | '(created changed deleted stopped)) | ||
| 407 | (t '(created changed deleted deleted stopped))) | ||
| 408 | (read-event nil nil 0.1) | ||
| 333 | (write-region | 409 | (write-region |
| 334 | "any text" nil (expand-file-name "foo" file-notify--test-tmpfile) | 410 | "any text" nil file-notify--test-tmpfile nil 'no-message) |
| 335 | nil 'no-message) | 411 | (read-event nil nil 0.1) |
| 336 | (delete-directory file-notify--test-tmpfile 'recursive)) | 412 | (delete-directory temporary-file-directory 'recursive)) |
| 337 | ;; `file-notify-rm-watch' fires the `stopped' event. Suppress it. | 413 | ;; `file-notify-rm-watch' fires the `stopped' event. Suppress it. |
| 338 | (let (file-notify--test-events) | 414 | (let (file-notify--test-events) |
| 339 | (file-notify-rm-watch file-notify--test-desc))) | 415 | (file-notify-rm-watch file-notify--test-desc))) |
| 340 | 416 | ||
| 341 | ;; Check copy. | 417 | ;; Check copy of files inside a directory. |
| 342 | (setq file-notify--test-desc | 418 | (let ((temporary-file-directory |
| 343 | (file-notify-add-watch | 419 | (make-temp-file "file-notify-test-parent" t))) |
| 344 | file-notify--test-tmpfile | 420 | (should |
| 345 | '(change) 'file-notify--test-event-handler)) | 421 | (setq file-notify--test-tmpfile (file-notify--test-make-temp-name) |
| 346 | (should file-notify--test-desc) | 422 | file-notify--test-tmpfile1 (file-notify--test-make-temp-name) |
| 347 | (file-notify--test-with-events | 423 | file-notify--test-desc |
| 348 | ;; w32notify does not distinguish between `changed' and | 424 | (file-notify-add-watch |
| 349 | ;; `attribute-changed'. | 425 | temporary-file-directory |
| 350 | (if (eq file-notify--library 'w32notify) | 426 | '(change) 'file-notify--test-event-handler))) |
| 351 | '(created changed changed deleted) | 427 | (file-notify--test-with-events |
| 352 | '(created changed deleted)) | 428 | (cond |
| 353 | (write-region | 429 | ;; w32notify does not distinguish between `changed' and |
| 354 | "any text" nil file-notify--test-tmpfile nil 'no-message) | 430 | ;; `attribute-changed'. |
| 355 | (copy-file file-notify--test-tmpfile file-notify--test-tmpfile1) | 431 | ((string-equal (file-notify--test-library) "w32notify") |
| 356 | ;; The next two events shall not be visible. | 432 | '(created changed created changed changed changed changed |
| 357 | (set-file-modes file-notify--test-tmpfile 000) | 433 | deleted deleted)) |
| 358 | (read-event nil nil 0.1) ; In order to distinguish the events. | 434 | ;; cygwin recognizes only `deleted' and `stopped' events. |
| 359 | (set-file-times file-notify--test-tmpfile '(0 0)) | 435 | ((eq system-type 'cygwin) |
| 360 | (delete-file file-notify--test-tmpfile) | 436 | '(deleted stopped)) |
| 361 | (delete-file file-notify--test-tmpfile1)) | 437 | ;; There are three `deleted' events, for two files and |
| 362 | ;; `file-notify-rm-watch' fires the `stopped' event. Suppress it. | 438 | ;; for the directory. Except for kqueue. |
| 363 | (let (file-notify--test-events) | 439 | ((string-equal (file-notify--test-library) "kqueue") |
| 364 | (file-notify-rm-watch file-notify--test-desc)) | 440 | '(created changed created changed deleted stopped)) |
| 441 | (t '(created changed created changed | ||
| 442 | deleted deleted deleted stopped))) | ||
| 443 | (read-event nil nil 0.1) | ||
| 444 | (write-region | ||
| 445 | "any text" nil file-notify--test-tmpfile nil 'no-message) | ||
| 446 | (read-event nil nil 0.1) | ||
| 447 | (copy-file file-notify--test-tmpfile file-notify--test-tmpfile1) | ||
| 448 | ;; The next two events shall not be visible. | ||
| 449 | (read-event nil nil 0.1) | ||
| 450 | (set-file-modes file-notify--test-tmpfile 000) | ||
| 451 | (read-event nil nil 0.1) | ||
| 452 | (set-file-times file-notify--test-tmpfile '(0 0)) | ||
| 453 | (read-event nil nil 0.1) | ||
| 454 | (delete-directory temporary-file-directory 'recursive)) | ||
| 455 | ;; `file-notify-rm-watch' fires the `stopped' event. Suppress it. | ||
| 456 | (let (file-notify--test-events) | ||
| 457 | (file-notify-rm-watch file-notify--test-desc))) | ||
| 365 | 458 | ||
| 366 | ;; Check rename. | 459 | ;; Check rename of files inside a directory. |
| 367 | (setq file-notify--test-desc | 460 | (let ((temporary-file-directory |
| 368 | (file-notify-add-watch | 461 | (make-temp-file "file-notify-test-parent" t))) |
| 369 | file-notify--test-tmpfile | 462 | (should |
| 370 | '(change) 'file-notify--test-event-handler)) | 463 | (setq file-notify--test-tmpfile (file-notify--test-make-temp-name) |
| 371 | (should file-notify--test-desc) | 464 | file-notify--test-tmpfile1 (file-notify--test-make-temp-name) |
| 372 | (file-notify--test-with-events '(created changed renamed) | 465 | file-notify--test-desc |
| 373 | (write-region | 466 | (file-notify-add-watch |
| 374 | "any text" nil file-notify--test-tmpfile nil 'no-message) | 467 | temporary-file-directory |
| 375 | (rename-file file-notify--test-tmpfile file-notify--test-tmpfile1) | 468 | '(change) 'file-notify--test-event-handler))) |
| 376 | ;; After the rename, we won't get events anymore. | 469 | (file-notify--test-with-events |
| 377 | (delete-file file-notify--test-tmpfile1)) | 470 | (cond |
| 378 | ;; `file-notify-rm-watch' fires the `stopped' event. Suppress it. | 471 | ;; w32notify does not distinguish between `changed' and |
| 379 | (let (file-notify--test-events) | 472 | ;; `attribute-changed'. |
| 380 | (file-notify-rm-watch file-notify--test-desc)) | 473 | ((string-equal (file-notify--test-library) "w32notify") |
| 474 | '(created changed renamed deleted)) | ||
| 475 | ;; cygwin recognizes only `deleted' and `stopped' events. | ||
| 476 | ((eq system-type 'cygwin) | ||
| 477 | '(deleted stopped)) | ||
| 478 | ;; There are two `deleted' events, for the file and for | ||
| 479 | ;; the directory. Except for kqueue. | ||
| 480 | ((string-equal (file-notify--test-library) "kqueue") | ||
| 481 | '(created changed renamed deleted stopped)) | ||
| 482 | (t '(created changed renamed deleted deleted stopped))) | ||
| 483 | (read-event nil nil 0.1) | ||
| 484 | (write-region | ||
| 485 | "any text" nil file-notify--test-tmpfile nil 'no-message) | ||
| 486 | (read-event nil nil 0.1) | ||
| 487 | (rename-file file-notify--test-tmpfile file-notify--test-tmpfile1) | ||
| 488 | ;; After the rename, we won't get events anymore. | ||
| 489 | (read-event nil nil 0.1) | ||
| 490 | (delete-directory temporary-file-directory 'recursive)) | ||
| 491 | ;; `file-notify-rm-watch' fires the `stopped' event. Suppress it. | ||
| 492 | (let (file-notify--test-events) | ||
| 493 | (file-notify-rm-watch file-notify--test-desc))) | ||
| 381 | 494 | ||
| 382 | ;; Check attribute change. It doesn't work for w32notify. | 495 | ;; Check attribute change. Does not work for cygwin. |
| 383 | (unless (eq file-notify--library 'w32notify) | 496 | (unless (eq system-type 'cygwin) |
| 384 | (setq file-notify--test-desc | 497 | (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)) |
| 385 | (file-notify-add-watch | 498 | (write-region |
| 386 | file-notify--test-tmpfile | 499 | "any text" nil file-notify--test-tmpfile nil 'no-message) |
| 387 | '(attribute-change) 'file-notify--test-event-handler)) | 500 | (should |
| 388 | (file-notify--test-with-events | 501 | (setq file-notify--test-desc |
| 389 | (if (file-remote-p temporary-file-directory) | 502 | (file-notify-add-watch |
| 390 | ;; In the remote case, `write-region' raises also an | 503 | file-notify--test-tmpfile |
| 391 | ;; `attribute-changed' event. | 504 | '(attribute-change) 'file-notify--test-event-handler))) |
| 392 | '(attribute-changed attribute-changed attribute-changed) | 505 | (file-notify--test-with-events |
| 393 | '(attribute-changed attribute-changed)) | 506 | (cond |
| 394 | ;; We must use short delays between the operations. | 507 | ;; w32notify does not distinguish between `changed' and |
| 395 | ;; Otherwise, not all events arrive us in the remote case. | 508 | ;; `attribute-changed'. |
| 396 | (write-region | 509 | ((string-equal (file-notify--test-library) "w32notify") |
| 397 | "any text" nil file-notify--test-tmpfile nil 'no-message) | 510 | '(changed changed changed changed)) |
| 398 | (read-event nil nil 0.1) | 511 | ;; For kqueue and in the remote case, `write-region' |
| 399 | (set-file-modes file-notify--test-tmpfile 000) | 512 | ;; raises also an `attribute-changed' event. |
| 400 | (read-event nil nil 0.1) | 513 | ((or (string-equal (file-notify--test-library) "kqueue") |
| 401 | (set-file-times file-notify--test-tmpfile '(0 0)) | 514 | (file-remote-p temporary-file-directory)) |
| 402 | (read-event nil nil 0.1) | 515 | '(attribute-changed attribute-changed attribute-changed)) |
| 403 | (delete-file file-notify--test-tmpfile)) | 516 | (t '(attribute-changed attribute-changed))) |
| 517 | (read-event nil nil 0.1) | ||
| 518 | (write-region | ||
| 519 | "any text" nil file-notify--test-tmpfile nil 'no-message) | ||
| 520 | (read-event nil nil 0.1) | ||
| 521 | (set-file-modes file-notify--test-tmpfile 000) | ||
| 522 | (read-event nil nil 0.1) | ||
| 523 | (set-file-times file-notify--test-tmpfile '(0 0)) | ||
| 524 | (read-event nil nil 0.1) | ||
| 525 | (delete-file file-notify--test-tmpfile)) | ||
| 404 | ;; `file-notify-rm-watch' fires the `stopped' event. Suppress it. | 526 | ;; `file-notify-rm-watch' fires the `stopped' event. Suppress it. |
| 405 | (let (file-notify--test-events) | 527 | (let (file-notify--test-events) |
| 406 | (file-notify-rm-watch file-notify--test-desc))) | 528 | (file-notify-rm-watch file-notify--test-desc))) |
| 407 | 529 | ||
| 408 | ;; Check the global sequence again just to make sure that | 530 | ;; Check the global sequence again just to make sure that |
| 409 | ;; `file-notify--test-events' has been set correctly. | 531 | ;; `file-notify--test-events' has been set correctly. |
| 410 | (should (equal (mapcar #'cadr file-notify--test-events) | ||
| 411 | file-notify--test-expected-events)) | ||
| 412 | (should file-notify--test-results) | 532 | (should file-notify--test-results) |
| 413 | (dolist (result file-notify--test-results) | 533 | (dolist (result file-notify--test-results) |
| 414 | (when (ert-test-failed-p result) | 534 | (when (ert-test-failed-p result) |
| @@ -476,28 +596,31 @@ Don't wait longer than timeout seconds for the events to be delivered." | |||
| 476 | (should (string-match "another text" (buffer-string))) | 596 | (should (string-match "another text" (buffer-string))) |
| 477 | 597 | ||
| 478 | ;; Stop file notification. Autorevert shall still work via polling. | 598 | ;; Stop file notification. Autorevert shall still work via polling. |
| 479 | (file-notify-rm-watch auto-revert-notify-watch-descriptor) | 599 | ;; It doesn't work for `w32notify'. |
| 480 | (file-notify--wait-for-events | 600 | (unless (string-equal (file-notify--test-library) "w32notify") |
| 481 | timeout (null auto-revert-use-notify)) | 601 | (file-notify-rm-watch auto-revert-notify-watch-descriptor) |
| 482 | (should-not auto-revert-use-notify) | ||
| 483 | (should-not auto-revert-notify-watch-descriptor) | ||
| 484 | |||
| 485 | ;; Modify file. We wait for two seconds, in order to have | ||
| 486 | ;; another timestamp. One second seems to be too short. | ||
| 487 | (with-current-buffer (get-buffer-create "*Messages*") | ||
| 488 | (narrow-to-region (point-max) (point-max))) | ||
| 489 | (sleep-for 2) | ||
| 490 | (write-region | ||
| 491 | "foo bla" nil file-notify--test-tmpfile nil 'no-message) | ||
| 492 | |||
| 493 | ;; Check, that the buffer has been reverted. | ||
| 494 | (with-current-buffer (get-buffer-create "*Messages*") | ||
| 495 | (file-notify--wait-for-events | 602 | (file-notify--wait-for-events |
| 496 | timeout | 603 | timeout (null auto-revert-use-notify)) |
| 497 | (string-match | 604 | (should-not auto-revert-use-notify) |
| 498 | (format-message "Reverting buffer `%s'." (buffer-name buf)) | 605 | (should-not auto-revert-notify-watch-descriptor) |
| 499 | (buffer-string)))) | 606 | |
| 500 | (should (string-match "foo bla" (buffer-string))))) | 607 | ;; Modify file. We wait for two seconds, in order to |
| 608 | ;; have another timestamp. One second seems to be too | ||
| 609 | ;; short. | ||
| 610 | (with-current-buffer (get-buffer-create "*Messages*") | ||
| 611 | (narrow-to-region (point-max) (point-max))) | ||
| 612 | (sleep-for 2) | ||
| 613 | (write-region | ||
| 614 | "foo bla" nil file-notify--test-tmpfile nil 'no-message) | ||
| 615 | |||
| 616 | ;; Check, that the buffer has been reverted. | ||
| 617 | (with-current-buffer (get-buffer-create "*Messages*") | ||
| 618 | (file-notify--wait-for-events | ||
| 619 | timeout | ||
| 620 | (string-match | ||
| 621 | (format-message "Reverting buffer `%s'." (buffer-name buf)) | ||
| 622 | (buffer-string)))) | ||
| 623 | (should (string-match "foo bla" (buffer-string)))))) | ||
| 501 | 624 | ||
| 502 | ;; Cleanup. | 625 | ;; Cleanup. |
| 503 | (with-current-buffer "*Messages*" (widen)) | 626 | (with-current-buffer "*Messages*" (widen)) |
| @@ -510,51 +633,94 @@ Don't wait longer than timeout seconds for the events to be delivered." | |||
| 510 | (ert-deftest file-notify-test04-file-validity () | 633 | (ert-deftest file-notify-test04-file-validity () |
| 511 | "Check `file-notify-valid-p' for files." | 634 | "Check `file-notify-valid-p' for files." |
| 512 | (skip-unless (file-notify--test-local-enabled)) | 635 | (skip-unless (file-notify--test-local-enabled)) |
| 513 | ;; Under cygwin there are so bad timings that it doesn't make sense to test. | ||
| 514 | (skip-unless (not (eq system-type 'cygwin))) | ||
| 515 | 636 | ||
| 516 | (unwind-protect | 637 | (unwind-protect |
| 517 | (progn | 638 | (progn |
| 518 | (setq file-notify--test-tmpfile (file-notify--test-make-temp-name) | 639 | (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)) |
| 519 | file-notify--test-desc | 640 | (write-region "any text" nil file-notify--test-tmpfile nil 'no-message) |
| 520 | (file-notify-add-watch | 641 | (should |
| 521 | file-notify--test-tmpfile | 642 | (setq file-notify--test-desc |
| 522 | '(change) #'file-notify--test-event-handler)) | 643 | (file-notify-add-watch |
| 523 | (file-notify--test-with-events '(created changed deleted) | 644 | file-notify--test-tmpfile |
| 645 | '(change) #'file-notify--test-event-handler))) | ||
| 646 | (should (file-notify-valid-p file-notify--test-desc)) | ||
| 647 | ;; After calling `file-notify-rm-watch', the descriptor is not | ||
| 648 | ;; valid anymore. | ||
| 649 | (file-notify-rm-watch file-notify--test-desc) | ||
| 650 | (should-not (file-notify-valid-p file-notify--test-desc)) | ||
| 651 | (delete-file file-notify--test-tmpfile)) | ||
| 652 | |||
| 653 | ;; Cleanup. | ||
| 654 | (file-notify--test-cleanup)) | ||
| 655 | |||
| 656 | (unwind-protect | ||
| 657 | (progn | ||
| 658 | (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)) | ||
| 659 | (write-region "any text" nil file-notify--test-tmpfile nil 'no-message) | ||
| 660 | (should | ||
| 661 | (setq file-notify--test-desc | ||
| 662 | (file-notify-add-watch | ||
| 663 | file-notify--test-tmpfile | ||
| 664 | '(change) #'file-notify--test-event-handler))) | ||
| 665 | (file-notify--test-with-events | ||
| 666 | (cond | ||
| 667 | ;; cygwin recognizes only `deleted' and `stopped' events. | ||
| 668 | ((eq system-type 'cygwin) | ||
| 669 | '(deleted stopped)) | ||
| 670 | ;; inotify and kqueue raise just one `changed' event. | ||
| 671 | ((or (string-equal "inotify" (file-notify--test-library)) | ||
| 672 | (string-equal "kqueue" (file-notify--test-library))) | ||
| 673 | '(changed deleted stopped)) | ||
| 674 | ;; gfilenotify raises one or two `changed' events | ||
| 675 | ;; randomly, no chance to test. So we accept both cases. | ||
| 676 | ((string-equal "gfilenotify" (file-notify--test-library)) | ||
| 677 | '((changed deleted stopped) | ||
| 678 | (changed changed deleted stopped))) | ||
| 679 | (t '(changed changed deleted stopped))) | ||
| 524 | (should (file-notify-valid-p file-notify--test-desc)) | 680 | (should (file-notify-valid-p file-notify--test-desc)) |
| 681 | (read-event nil nil 0.1) | ||
| 525 | (write-region | 682 | (write-region |
| 526 | "any text" nil file-notify--test-tmpfile nil 'no-message) | 683 | "another text" nil file-notify--test-tmpfile nil 'no-message) |
| 684 | (read-event nil nil 0.1) | ||
| 527 | (delete-file file-notify--test-tmpfile)) | 685 | (delete-file file-notify--test-tmpfile)) |
| 528 | ;; After deleting the file, the descriptor is still valid. | 686 | ;; After deleting the file, the descriptor is not valid anymore. |
| 529 | (should (file-notify-valid-p file-notify--test-desc)) | 687 | (should-not (file-notify-valid-p file-notify--test-desc)) |
| 530 | ;; After removing the watch, the descriptor must not be valid | 688 | (file-notify-rm-watch file-notify--test-desc)) |
| 531 | ;; anymore. | ||
| 532 | (file-notify-rm-watch file-notify--test-desc) | ||
| 533 | (should-not (file-notify-valid-p file-notify--test-desc))) | ||
| 534 | 689 | ||
| 535 | ;; Cleanup. | 690 | ;; Cleanup. |
| 536 | (file-notify--test-cleanup)) | 691 | (file-notify--test-cleanup)) |
| 537 | 692 | ||
| 538 | (unwind-protect | 693 | (unwind-protect |
| 539 | ;; The batch-mode operation of w32notify is fragile (there's no | 694 | ;; w32notify does not send a `stopped' event when deleting a |
| 540 | ;; input threads to send the message to). | 695 | ;; directory. The test does not work, therefore. |
| 541 | ;(unless (and noninteractive (eq file-notify--library 'w32notify)) | 696 | (unless (string-equal (file-notify--test-library) "w32notify") |
| 542 | (unless (eq file-notify--library 'w32notify) | 697 | (let ((temporary-file-directory |
| 543 | (let ((temporary-file-directory | ||
| 544 | (make-temp-file "file-notify-test-parent" t))) | 698 | (make-temp-file "file-notify-test-parent" t))) |
| 545 | (setq file-notify--test-tmpfile (file-notify--test-make-temp-name) | 699 | (should |
| 546 | file-notify--test-desc | 700 | (setq file-notify--test-tmpfile (file-notify--test-make-temp-name) |
| 547 | (file-notify-add-watch | 701 | file-notify--test-desc |
| 548 | file-notify--test-tmpfile | 702 | (file-notify-add-watch |
| 549 | '(change) #'file-notify--test-event-handler)) | 703 | temporary-file-directory |
| 550 | (file-notify--test-with-events '(created changed deleted stopped) | 704 | '(change) #'file-notify--test-event-handler))) |
| 551 | (should (file-notify-valid-p file-notify--test-desc)) | 705 | (file-notify--test-with-events |
| 552 | (write-region | 706 | (cond |
| 553 | "any text" nil file-notify--test-tmpfile nil 'no-message) | 707 | ;; cygwin recognizes only `deleted' and `stopped' events. |
| 708 | ((eq system-type 'cygwin) | ||
| 709 | '(deleted stopped)) | ||
| 710 | ;; There are two `deleted' events, for the file and for | ||
| 711 | ;; the directory. Except for kqueue. | ||
| 712 | ((string-equal (file-notify--test-library) "kqueue") | ||
| 713 | '(created changed deleted stopped)) | ||
| 714 | (t '(created changed deleted deleted stopped))) | ||
| 715 | (should (file-notify-valid-p file-notify--test-desc)) | ||
| 716 | (read-event nil nil 0.1) | ||
| 717 | (write-region | ||
| 718 | "any text" nil file-notify--test-tmpfile nil 'no-message) | ||
| 719 | (read-event nil nil 0.1) | ||
| 554 | (delete-directory temporary-file-directory t)) | 720 | (delete-directory temporary-file-directory t)) |
| 555 | ;; After deleting the parent directory, the descriptor must | 721 | ;; After deleting the parent directory, the descriptor must |
| 556 | ;; not be valid anymore. | 722 | ;; not be valid anymore. |
| 557 | (should-not (file-notify-valid-p file-notify--test-desc)))) | 723 | (should-not (file-notify-valid-p file-notify--test-desc)))) |
| 558 | 724 | ||
| 559 | ;; Cleanup. | 725 | ;; Cleanup. |
| 560 | (file-notify--test-cleanup))) | 726 | (file-notify--test-cleanup))) |
| @@ -571,10 +737,11 @@ Don't wait longer than timeout seconds for the events to be delivered." | |||
| 571 | (setq file-notify--test-tmpfile | 737 | (setq file-notify--test-tmpfile |
| 572 | (file-name-as-directory (file-notify--test-make-temp-name))) | 738 | (file-name-as-directory (file-notify--test-make-temp-name))) |
| 573 | (make-directory file-notify--test-tmpfile) | 739 | (make-directory file-notify--test-tmpfile) |
| 574 | (setq file-notify--test-desc | 740 | (should |
| 575 | (file-notify-add-watch | 741 | (setq file-notify--test-desc |
| 576 | file-notify--test-tmpfile | 742 | (file-notify-add-watch |
| 577 | '(change) #'file-notify--test-event-handler)) | 743 | file-notify--test-tmpfile |
| 744 | '(change) #'file-notify--test-event-handler))) | ||
| 578 | (should (file-notify-valid-p file-notify--test-desc)) | 745 | (should (file-notify-valid-p file-notify--test-desc)) |
| 579 | ;; After removing the watch, the descriptor must not be valid | 746 | ;; After removing the watch, the descriptor must not be valid |
| 580 | ;; anymore. | 747 | ;; anymore. |
| @@ -590,20 +757,22 @@ Don't wait longer than timeout seconds for the events to be delivered." | |||
| 590 | (unwind-protect | 757 | (unwind-protect |
| 591 | ;; The batch-mode operation of w32notify is fragile (there's no | 758 | ;; The batch-mode operation of w32notify is fragile (there's no |
| 592 | ;; input threads to send the message to). | 759 | ;; input threads to send the message to). |
| 593 | (unless (and noninteractive (eq file-notify--library 'w32notify)) | 760 | (unless (and noninteractive |
| 761 | (string-equal (file-notify--test-library) "w32notify")) | ||
| 594 | (setq file-notify--test-tmpfile | 762 | (setq file-notify--test-tmpfile |
| 595 | (file-name-as-directory (file-notify--test-make-temp-name))) | 763 | (file-name-as-directory (file-notify--test-make-temp-name))) |
| 596 | (make-directory file-notify--test-tmpfile) | 764 | (make-directory file-notify--test-tmpfile) |
| 597 | (setq file-notify--test-desc | 765 | (should |
| 598 | (file-notify-add-watch | 766 | (setq file-notify--test-desc |
| 599 | file-notify--test-tmpfile | 767 | (file-notify-add-watch |
| 600 | '(change) #'file-notify--test-event-handler)) | 768 | file-notify--test-tmpfile |
| 769 | '(change) #'file-notify--test-event-handler))) | ||
| 601 | (should (file-notify-valid-p file-notify--test-desc)) | 770 | (should (file-notify-valid-p file-notify--test-desc)) |
| 602 | ;; After deleting the directory, the descriptor must not be | 771 | ;; After deleting the directory, the descriptor must not be |
| 603 | ;; valid anymore. | 772 | ;; valid anymore. |
| 604 | (delete-directory file-notify--test-tmpfile t) | 773 | (delete-directory file-notify--test-tmpfile t) |
| 605 | (file-notify--wait-for-events | 774 | (file-notify--wait-for-events |
| 606 | (file-notify--test-timeout) | 775 | (file-notify--test-timeout) |
| 607 | (not (file-notify-valid-p file-notify--test-desc))) | 776 | (not (file-notify-valid-p file-notify--test-desc))) |
| 608 | (should-not (file-notify-valid-p file-notify--test-desc))) | 777 | (should-not (file-notify-valid-p file-notify--test-desc))) |
| 609 | 778 | ||
| @@ -613,6 +782,62 @@ Don't wait longer than timeout seconds for the events to be delivered." | |||
| 613 | (file-notify--deftest-remote file-notify-test05-dir-validity | 782 | (file-notify--deftest-remote file-notify-test05-dir-validity |
| 614 | "Check `file-notify-valid-p' via file notification for remote directories.") | 783 | "Check `file-notify-valid-p' via file notification for remote directories.") |
| 615 | 784 | ||
| 785 | (ert-deftest file-notify-test06-many-events () | ||
| 786 | "Check that events are not dropped." | ||
| 787 | :tags '(:expensive-test) | ||
| 788 | (skip-unless (file-notify--test-local-enabled)) | ||
| 789 | ;; Under cygwin events arrive in random order. Impossible to define a test. | ||
| 790 | (skip-unless (not (eq system-type 'cygwin))) | ||
| 791 | |||
| 792 | (setq file-notify--test-tmpfile (file-notify--test-make-temp-name)) | ||
| 793 | (make-directory file-notify--test-tmpfile) | ||
| 794 | (should | ||
| 795 | (setq file-notify--test-desc | ||
| 796 | (file-notify-add-watch | ||
| 797 | file-notify--test-tmpfile | ||
| 798 | '(change) 'file-notify--test-event-handler))) | ||
| 799 | (unwind-protect | ||
| 800 | (let ((n 1000) | ||
| 801 | source-file-list target-file-list | ||
| 802 | (default-directory file-notify--test-tmpfile)) | ||
| 803 | (dotimes (i n) | ||
| 804 | ;; It matters which direction we rename, at least for | ||
| 805 | ;; kqueue. This backend parses directories in alphabetic | ||
| 806 | ;; order (x%d before y%d). So we rename both directions. | ||
| 807 | (if (zerop (mod i 2)) | ||
| 808 | (progn | ||
| 809 | (push (expand-file-name (format "x%d" i)) source-file-list) | ||
| 810 | (push (expand-file-name (format "y%d" i)) target-file-list)) | ||
| 811 | (push (expand-file-name (format "y%d" i)) source-file-list) | ||
| 812 | (push (expand-file-name (format "x%d" i)) target-file-list))) | ||
| 813 | (file-notify--test-with-events (make-list (+ n n) 'created) | ||
| 814 | (let ((source-file-list source-file-list) | ||
| 815 | (target-file-list target-file-list)) | ||
| 816 | (while (and source-file-list target-file-list) | ||
| 817 | (read-event nil nil 0.1) | ||
| 818 | (write-region "" nil (pop source-file-list) nil 'no-message) | ||
| 819 | (read-event nil nil 0.1) | ||
| 820 | (write-region "" nil (pop target-file-list) nil 'no-message)))) | ||
| 821 | (file-notify--test-with-events | ||
| 822 | (cond | ||
| 823 | ;; w32notify fires both `deleted' and `renamed' events. | ||
| 824 | ((string-equal (file-notify--test-library) "w32notify") | ||
| 825 | (let (r) | ||
| 826 | (dotimes (_i n r) | ||
| 827 | (setq r (append '(deleted renamed) r))))) | ||
| 828 | (t (make-list n 'renamed))) | ||
| 829 | (let ((source-file-list source-file-list) | ||
| 830 | (target-file-list target-file-list)) | ||
| 831 | (while (and source-file-list target-file-list) | ||
| 832 | (rename-file (pop source-file-list) (pop target-file-list) t)))) | ||
| 833 | (file-notify--test-with-events (make-list n 'deleted) | ||
| 834 | (dolist (file target-file-list) | ||
| 835 | (delete-file file)))) | ||
| 836 | (file-notify--test-cleanup))) | ||
| 837 | |||
| 838 | (file-notify--deftest-remote file-notify-test06-many-events | ||
| 839 | "Check that events are not dropped for remote directories.") | ||
| 840 | |||
| 616 | (defun file-notify-test-all (&optional interactive) | 841 | (defun file-notify-test-all (&optional interactive) |
| 617 | "Run all tests for \\[file-notify]." | 842 | "Run all tests for \\[file-notify]." |
| 618 | (interactive "p") | 843 | (interactive "p") |
| @@ -623,7 +848,7 @@ Don't wait longer than timeout seconds for the events to be delivered." | |||
| 623 | ;; TODO: | 848 | ;; TODO: |
| 624 | 849 | ||
| 625 | ;; * For w32notify, no stopped events arrive when a directory is removed. | 850 | ;; * For w32notify, no stopped events arrive when a directory is removed. |
| 626 | ;; * Try to handle arriving events under cygwin reliably. | 851 | ;; * Check, why cygwin recognizes only `deleted' and `stopped' events. |
| 627 | 852 | ||
| 628 | (provide 'file-notify-tests) | 853 | (provide 'file-notify-tests) |
| 629 | ;;; file-notify-tests.el ends here | 854 | ;;; file-notify-tests.el ends here |