1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2024-12-03 08:30:09 +00:00

Add kqueue support

* 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.

* src/Makefile.in: Use NOTIFY_CFLAGS and NOTIFY_LIBS.

* src/emacs.c (main): Call globals_of_kqueue and syms_of_kqueue.

* src/kqueue.c: New file.

* src/lisp.h: Declare extern globals_of_kqueue and syms_of_kqueue.
This commit is contained in:
Michael Albinus 2015-11-09 10:00:56 +01:00
parent c6457cef92
commit e3354e2265
5 changed files with 400 additions and 33 deletions

View File

@ -355,17 +355,18 @@ OPTION_DEFAULT_ON([gnutls],[don't use -lgnutls for SSL/TLS support])
OPTION_DEFAULT_ON([zlib],[don't compile with zlib decompression support])
AC_ARG_WITH([file-notification],[AS_HELP_STRING([--with-file-notification=LIB],
[use a file notification library (LIB one of: yes, gfile, inotify, w32, no)])],
[use a file notification library (LIB one of: yes, inotify, kqueue, gfile, w32, no)])],
[ case "${withval}" in
y | ye | yes ) val=yes ;;
n | no ) val=no ;;
g | gf | gfi | gfil | gfile ) val=gfile ;;
i | in | ino | inot | inoti | inotif | inotify ) val=inotify ;;
k | kq | kqu | kque | kqueu | kqueue ) val=kqueue ;;
g | gf | gfi | gfil | gfile ) val=gfile ;;
w | w3 | w32 ) val=w32 ;;
* ) AC_MSG_ERROR(['--with-file-notification=$withval' is invalid;
this option's value should be 'yes', 'no', 'gfile', 'inotify' or 'w32'.
this option's value should be 'yes', 'no', 'inotify', 'kqeue', 'gfile' or 'w32'.
'yes' is a synonym for 'w32' on MS-Windows, for 'no' on Nextstep,
otherwise for the first of 'inotify' or 'gfile' that is usable.])
otherwise for the first of 'inotify', 'kqueue' or 'gfile' that is usable.])
;;
esac
with_file_notification=$val
@ -2690,12 +2691,6 @@ AC_SUBST(LIBGNUTLS_CFLAGS)
NOTIFY_OBJ=
NOTIFY_SUMMARY=no
dnl FIXME? Don't auto-detect on NS, but do allow someone to specify
dnl a particular library. This doesn't make much sense?
if test "${HAVE_NS}" = yes && test ${with_file_notification} = yes; then
with_file_notification=no
fi
dnl MS Windows native file monitor is available for mingw32 only.
case $with_file_notification,$opsys in
w32,cygwin)
@ -2726,16 +2721,34 @@ case $with_file_notification,$NOTIFY_OBJ in
fi ;;
esac
dnl kqueue is available on BSD-like systems.
case $with_file_notification,$NOTIFY_OBJ in
kqueue,* | yes,)
EMACS_CHECK_MODULES([KQUEUE], [libkqueue])
if test "$HAVE_KQUEUE" = "yes"; then
AC_DEFINE(HAVE_KQUEUE, 1, [Define to 1 to use kqueue.])
CPPFLAGS="$CPPFLAGS -I/usr/include/kqueue"
NOTIFY_CFLAGS=$KQUEUE_CFLAGS
NOTIFY_LIBS=$KQUEUE_LIBS
NOTIFY_OBJ=kqueue.o
NOTIFY_SUMMARY="yes -lkqueue"
fi ;;
esac
dnl g_file_monitor exists since glib 2.18. G_FILE_MONITOR_EVENT_MOVED
dnl has been added in glib 2.24. It has been tested under
dnl GNU/Linux only.
case $with_file_notification,$NOTIFY_OBJ in
gfile,* | yes,)
EMACS_CHECK_MODULES([GFILENOTIFY], [gio-2.0 >= 2.24])
if test "$HAVE_GFILENOTIFY" = "yes"; then
AC_DEFINE(HAVE_GFILENOTIFY, 1, [Define to 1 if using GFile.])
NOTIFY_OBJ=gfilenotify.o
NOTIFY_SUMMARY="yes -lgio (gfile)"
if test "${HAVE_NS}" != yes; then
EMACS_CHECK_MODULES([GFILENOTIFY], [gio-2.0 >= 2.24])
if test "$HAVE_GFILENOTIFY" = "yes"; then
AC_DEFINE(HAVE_GFILENOTIFY, 1, [Define to 1 if using GFile.])
NOTIFY_CFLAGS=$GFILENOTIFY_CFLAGS
NOTIFY_LIBS=$GFILENOTIFY_LIBS
NOTIFY_OBJ=gfilenotify.o
NOTIFY_SUMMARY="yes -lgio (gfile)"
fi
fi ;;
esac
@ -2747,9 +2760,9 @@ esac
if test -n "$NOTIFY_OBJ"; then
AC_DEFINE(USE_FILE_NOTIFY, 1, [Define to 1 if using file notifications.])
fi
AC_SUBST(NOTIFY_CFLAGS)
AC_SUBST(NOTIFY_LIBS)
AC_SUBST(NOTIFY_OBJ)
AC_SUBST(GFILENOTIFY_CFLAGS)
AC_SUBST(GFILENOTIFY_LIBS)
dnl Do not put whitespace before the #include statements below.
dnl Older compilers (eg sunos4 cc) choke on it.
@ -4066,8 +4079,8 @@ OLDCFLAGS="$CFLAGS"
OLDLIBS="$LIBS"
CFLAGS="$CFLAGS $GTK_CFLAGS $RSVG_CFLAGS $DBUS_CFLAGS $SETTINGS_CFLAGS"
LIBS="$LIBS $GTK_LIBS $RSVG_LIBS $DBUS_LIBS $SETTINGS_LIBS"
CFLAGS="$CFLAGS $GFILENOTIFY_CFLAGS $CAIRO_CFLAGS"
LIBS="$LIBS $GFILENOTIFY_LIBS $CAIRO_LIBS"
CFLAGS="$CFLAGS $NOTIFY_CFLAGS $CAIRO_CFLAGS"
LIBS="$LIBS $NOTIFY_LIBS $CAIRO_LIBS"
AC_MSG_CHECKING([whether GLib is linked in])
AC_LINK_IFELSE([AC_LANG_PROGRAM(
[[#include <glib.h>

View File

@ -160,12 +160,13 @@ SETTINGS_LIBS = @SETTINGS_LIBS@
## gtkutil.o if USE_GTK, else empty.
GTK_OBJ=@GTK_OBJ@
## gfilenotify.o if HAVE_GFILENOTIFY.
## inotify.o if HAVE_INOTIFY.
## kqueue.o if HAVE_KQUEUE.
## gfilenotify.o if HAVE_GFILENOTIFY.
## w32notify.o if HAVE_W32NOTIFY.
NOTIFY_OBJ = @NOTIFY_OBJ@
GFILENOTIFY_CFLAGS = @GFILENOTIFY_CFLAGS@
GFILENOTIFY_LIBS = @GFILENOTIFY_LIBS@
NOTIFY_CFLAGS = @NOTIFY_CFLAGS@
NOTIFY_LIBS = @NOTIFY_LIBS@
## -ltermcap, or -lncurses, or -lcurses, or "".
LIBS_TERMCAP=@LIBS_TERMCAP@
@ -355,7 +356,7 @@ ALL_CFLAGS=-Demacs $(MYCPPFLAGS) -I. -I$(srcdir) \
$(XRANDR_CFLAGS) $(XINERAMA_CFLAGS) $(XFIXES_CFLAGS) \
$(SETTINGS_CFLAGS) $(FREETYPE_CFLAGS) $(FONTCONFIG_CFLAGS) \
$(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \
$(LIBGNUTLS_CFLAGS) $(GFILENOTIFY_CFLAGS) $(CAIRO_CFLAGS) \
$(LIBGNUTLS_CFLAGS) $(NOTIFY_CFLAGS) $(CAIRO_CFLAGS) \
$(WARN_CFLAGS) $(WERROR_CFLAGS) $(CFLAGS)
ALL_OBJC_CFLAGS=$(ALL_CFLAGS) $(GNU_OBJC_CFLAGS)
@ -468,7 +469,7 @@ LIBES = $(LIBS) $(W32_LIBS) $(LIBS_GNUSTEP) $(LIBX_BASE) $(LIBIMAGE) \
$(LIBS_TERMCAP) $(GETLOADAVG_LIBS) $(SETTINGS_LIBS) $(LIBSELINUX_LIBS) \
$(FREETYPE_LIBS) $(FONTCONFIG_LIBS) $(LIBOTF_LIBS) $(M17N_FLT_LIBS) \
$(LIBGNUTLS_LIBS) $(LIB_PTHREAD) \
$(GFILENOTIFY_LIBS) $(LIB_MATH) $(LIBZ)
$(NOTIFY_LIBS) $(LIB_MATH) $(LIBZ)
$(leimdir)/leim-list.el: bootstrap-emacs$(EXEEXT)
$(MAKE) -C ../leim leim-list.el EMACS="$(bootstrap_exe)"

View File

@ -1350,6 +1350,10 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
tzset ();
#endif /* MSDOS */
#ifdef HAVE_KQUEUE
globals_of_kqueue ();
#endif
#ifdef HAVE_GFILENOTIFY
globals_of_gfilenotify ();
#endif
@ -1520,14 +1524,18 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
syms_of_gnutls ();
#ifdef HAVE_GFILENOTIFY
syms_of_gfilenotify ();
#endif /* HAVE_GFILENOTIFY */
#ifdef HAVE_INOTIFY
syms_of_inotify ();
#endif /* HAVE_INOTIFY */
#ifdef HAVE_KQUEUE
syms_of_kqueue ();
#endif /* HAVE_KQUEUE */
#ifdef HAVE_GFILENOTIFY
syms_of_gfilenotify ();
#endif /* HAVE_GFILENOTIFY */
#ifdef HAVE_DBUS
syms_of_dbusbind ();
#endif /* HAVE_DBUS */

339
src/kqueue.c Normal file
View File

@ -0,0 +1,339 @@
/* Filesystem notifications support with glib API.
Copyright (C) 2013-2015 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include <config.h>
#ifdef HAVE_KQUEUE
#include <stdio.h>
#include <sys/event.h>
#include "lisp.h"
#include "coding.h"
#include "termhooks.h"
#include "keyboard.h"
/* File handle for kqueue. */
static int kqueuefd = -1;
/* This is a list, elements are triples (DESCRIPTOR FILE FLAGS CALLBACK) */
static Lisp_Object watch_list;
#if 0
/* This is the callback function for arriving signals from
g_file_monitor. It shall create a Lisp event, and put it into
Emacs input queue. */
static gboolean
dir_monitor_callback (GFileMonitor *monitor,
GFile *file,
GFile *other_file,
GFileMonitorEvent event_type,
gpointer user_data)
{
Lisp_Object symbol, monitor_object, watch_object, flags;
char *name = g_file_get_parse_name (file);
char *oname = other_file ? g_file_get_parse_name (other_file) : NULL;
/* Determine event symbol. */
switch (event_type)
{
case G_FILE_MONITOR_EVENT_CHANGED:
symbol = Qchanged;
break;
case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
symbol = Qchanges_done_hint;
break;
case G_FILE_MONITOR_EVENT_DELETED:
symbol = Qdeleted;
break;
case G_FILE_MONITOR_EVENT_CREATED:
symbol = Qcreated;
break;
case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
symbol = Qattribute_changed;
break;
case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
symbol = Qpre_unmount;
break;
case G_FILE_MONITOR_EVENT_UNMOUNTED:
symbol = Qunmounted;
break;
case G_FILE_MONITOR_EVENT_MOVED:
symbol = Qmoved;
break;
default:
goto cleanup;
}
/* Determine callback function. */
monitor_object = make_pointer_integer (monitor);
eassert (INTEGERP (monitor_object));
watch_object = assq_no_quit (monitor_object, watch_list);
if (CONSP (watch_object))
{
struct input_event event;
Lisp_Object otail = oname ? list1 (build_string (oname)) : Qnil;
/* Check, whether event_type is expected. */
flags = XCAR (XCDR (XCDR (watch_object)));
if ((!NILP (Fmember (Qchange, flags)) &&
!NILP (Fmember (symbol, list5 (Qchanged, Qchanges_done_hint,
Qdeleted, Qcreated, Qmoved)))) ||
(!NILP (Fmember (Qattribute_change, flags)) &&
((EQ (symbol, Qattribute_changed)))))
{
/* Construct an event. */
EVENT_INIT (event);
event.kind = FILE_NOTIFY_EVENT;
event.frame_or_window = Qnil;
event.arg = list2 (Fcons (monitor_object,
Fcons (symbol,
Fcons (build_string (name),
otail))),
XCAR (XCDR (XCDR (XCDR (watch_object)))));
/* Store it into the input event queue. */
kbd_buffer_store_event (&event);
// XD_DEBUG_MESSAGE ("%s", XD_OBJECT_TO_STRING (event.arg));
}
/* Cancel monitor if file or directory is deleted. */
if (!NILP (Fmember (symbol, list2 (Qdeleted, Qmoved))) &&
(strcmp (name, SSDATA (XCAR (XCDR (watch_object)))) == 0) &&
!g_file_monitor_is_cancelled (monitor))
g_file_monitor_cancel (monitor);
}
/* Cleanup. */
cleanup:
g_free (name);
g_free (oname);
return TRUE;
}
#endif /* 0 */
DEFUN ("kqueue-add-watch", Fkqueue_add_watch, Skqueue_add_watch, 3, 3, 0,
doc: /* Add a watch for filesystem events pertaining to FILE.
This arranges for filesystem events pertaining to FILE to be reported
to Emacs. Use `gfile-rm-watch' to cancel the watch.
Value is a descriptor for the added watch. If the file cannot be
watched for some reason, this function signals a `file-notify-error' error.
FLAGS is a list of conditions to set what will be watched for. It can
include the following symbols:
`change' -- watch for file changes
`attribute-change' -- watch for file attributes changes, like
permissions or modification time
`watch-mounts' -- watch for mount events
`send-moved' -- pair `deleted' and `created' events caused by
file renames and send a single `renamed' event
instead
When any event happens, Emacs will call the CALLBACK function passing
it a single argument EVENT, which is of the form
(DESCRIPTOR ACTION FILE [FILE1])
DESCRIPTOR is the same object as the one returned by this function.
ACTION is the description of the event. It could be any one of the
following:
`changed' -- FILE has changed
`changes-done-hint' -- a hint that this was probably the last change
in a set of changes
`deleted' -- FILE was deleted
`created' -- FILE was created
`attribute-changed' -- a FILE attribute was changed
`pre-unmount' -- the FILE location will soon be unmounted
`unmounted' -- the FILE location was unmounted
`moved' -- FILE was moved to FILE1
FILE is the name of the file whose event is being reported. FILE1
will be reported only in case of the `moved' event. */)
(Lisp_Object file, Lisp_Object flags, Lisp_Object callback)
{
Lisp_Object watch_object;
GFile *gfile;
GFileMonitor *monitor;
GFileMonitorFlags gflags = G_FILE_MONITOR_NONE;
GError *gerror = NULL;
/* Check parameters. */
CHECK_STRING (file);
file = Fdirectory_file_name (Fexpand_file_name (file, Qnil));
if (NILP (Ffile_exists_p (file)))
report_file_error ("File does not exist", file);
CHECK_LIST (flags);
if (!FUNCTIONP (callback))
wrong_type_argument (Qinvalid_function, callback);
/* Create GFile name. */
// gfile = g_file_new_for_path (SSDATA (ENCODE_FILE (file)));
/* Assemble flags. */
// if (!NILP (Fmember (Qwatch_mounts, flags)))
// gflags |= G_FILE_MONITOR_WATCH_MOUNTS;
// if (!NILP (Fmember (Qsend_moved, flags)))
// gflags |= G_FILE_MONITOR_SEND_MOVED;
if (kqueuefd < 0)
{
kqueuefd = kqueue ();
if (kqueuefd < 0)
report_file_notify_error ("File watching is not available", Qnil);
watch_list = Qnil;
// add_read_fd (inotifyfd, &inotify_callback, NULL);
}
}
#if 0
mask = aspect_to_inotifymask (aspect);
encoded_file_name = ENCODE_FILE (file_name);
watchdesc = inotify_add_watch (inotifyfd, SSDATA (encoded_file_name), mask);
if (watchdesc == -1)
report_file_notify_error ("Could not add watch for file", file_name);
/* Enable watch. */
monitor = g_file_monitor (gfile, gflags, NULL, &gerror);
g_object_unref (gfile);
if (gerror)
{
char msg[1024];
strcpy (msg, gerror->message);
g_error_free (gerror);
xsignal1 (Qfile_notify_error, build_string (msg));
}
if (! monitor)
xsignal2 (Qfile_notify_error, build_string ("Cannot watch file"), file);
Lisp_Object watch_descriptor = make_pointer_integer (monitor);
/* Check the dicey assumption that make_pointer_integer is safe. */
if (! INTEGERP (watch_descriptor))
{
g_object_unref (monitor);
xsignal2 (Qfile_notify_error, build_string ("Unsupported file watcher"),
file);
}
/* The default rate limit is 800 msec. We adapt this. */
g_file_monitor_set_rate_limit (monitor, 100);
/* Subscribe to the "changed" signal. */
g_signal_connect (monitor, "changed",
(GCallback) dir_monitor_callback, NULL);
/* Store watch object in watch list. */
watch_object = list4 (watch_descriptor, file, flags, callback);
watch_list = Fcons (watch_object, watch_list);
return watch_descriptor;
}
DEFUN ("gfile-rm-watch", Fgfile_rm_watch, Sgfile_rm_watch, 1, 1, 0,
doc: /* Remove an existing WATCH-DESCRIPTOR.
WATCH-DESCRIPTOR should be an object returned by `gfile-add-watch'. */)
(Lisp_Object watch_descriptor)
{
Lisp_Object watch_object = assq_no_quit (watch_descriptor, watch_list);
if (! CONSP (watch_object))
xsignal2 (Qfile_notify_error, build_string ("Not a watch descriptor"),
watch_descriptor);
eassert (INTEGERP (watch_descriptor));
GFileMonitor *monitor = XINTPTR (watch_descriptor);
if (!g_file_monitor_is_cancelled (monitor) &&
!g_file_monitor_cancel (monitor))
xsignal2 (Qfile_notify_error, build_string ("Could not rm watch"),
watch_descriptor);
/* Remove watch descriptor from watch list. */
watch_list = Fdelq (watch_object, watch_list);
/* Cleanup. */
g_object_unref (monitor);
return Qt;
}
DEFUN ("gfile-valid-p", Fgfile_valid_p, Sgfile_valid_p, 1, 1, 0,
doc: /* "Check a watch specified by its WATCH-DESCRIPTOR.
WATCH-DESCRIPTOR should be an object returned by `gfile-add-watch'.
A watch can become invalid if the file or directory it watches is
deleted, or if the watcher thread exits abnormally for any other
reason. Removing the watch by calling `gfile-rm-watch' also makes it
invalid. */)
(Lisp_Object watch_descriptor)
{
Lisp_Object watch_object = Fassoc (watch_descriptor, watch_list);
if (NILP (watch_object))
return Qnil;
else
{
GFileMonitor *monitor = XINTPTR (watch_descriptor);
return g_file_monitor_is_cancelled (monitor) ? Qnil : Qt;
}
}
#endif /* 0 */
void
globals_of_kqueue (void)
{
watch_list = Qnil;
}
void
syms_of_kqueue (void)
{
defsubr (&Skqueue_add_watch);
// defsubr (&Skqueue_rm_watch);
// defsubr (&Skqueue_valid_p);
/* Filter objects. */
DEFSYM (Qchange, "change");
DEFSYM (Qattribute_change, "attribute-change");
DEFSYM (Qwatch_mounts, "watch-mounts"); /* G_FILE_MONITOR_WATCH_MOUNTS */
DEFSYM (Qsend_moved, "send-moved"); /* G_FILE_MONITOR_SEND_MOVED */
/* Event types. */
DEFSYM (Qdelete, "delete"); /* NOTE_DELETE */
DEFSYM (Qwrite, "write"); /* NOTE_WRITE */
DEFSYM (Qextend, "extend"); /* NOTE_EXTEND */
DEFSYM (Qattrib, "attrib"); /* NOTE_ATTRIB */
DEFSYM (Qlink, "link"); /* NOTE_LINK */
DEFSYM (Qrename, "rename"); /* NOTE_RENAME */
staticpro (&watch_list);
Fprovide (intern_c_string ("kqueue"), Qnil);
}
#endif /* HAVE_KQUEUE */

View File

@ -4257,17 +4257,23 @@ extern void init_font (void);
extern void syms_of_fontset (void);
#endif
/* Defined in inotify.c */
#ifdef HAVE_INOTIFY
extern void syms_of_inotify (void);
#endif
/* Defined in kqueue.c */
#ifdef HAVE_KQUEUE
extern void globals_of_kqueue (void);
extern void syms_of_kqueue (void);
#endif
/* Defined in gfilenotify.c */
#ifdef HAVE_GFILENOTIFY
extern void globals_of_gfilenotify (void);
extern void syms_of_gfilenotify (void);
#endif
/* Defined in inotify.c */
#ifdef HAVE_INOTIFY
extern void syms_of_inotify (void);
#endif
#ifdef HAVE_W32NOTIFY
/* Defined on w32notify.c. */
extern void syms_of_w32notify (void);