Merge branch 'inotify-kqueue' into 'main'

Introduce a new GFileMonitor backend: libinotify-kqueue

See merge request GNOME/glib!3657
This commit is contained in:
Philip Withnall 2024-09-19 10:13:33 +00:00
commit 1adc303f47
7 changed files with 150 additions and 13 deletions

View File

@ -1340,10 +1340,10 @@ _g_io_modules_ensure_loaded (void)
g_type_ensure (g_memory_settings_backend_get_type ()); g_type_ensure (g_memory_settings_backend_get_type ());
g_type_ensure (g_keyfile_settings_backend_get_type ()); g_type_ensure (g_keyfile_settings_backend_get_type ());
g_type_ensure (g_power_profile_monitor_dbus_get_type ()); g_type_ensure (g_power_profile_monitor_dbus_get_type ());
#if defined(HAVE_INOTIFY_INIT1) #if defined(FILE_MONITOR_BACKEND_INOTIFY) || defined(FILE_MONITOR_BACKEND_LIBINOTIFY_KQUEUE)
g_type_ensure (g_inotify_file_monitor_get_type ()); g_type_ensure (g_inotify_file_monitor_get_type ());
#endif #endif
#if defined(HAVE_KQUEUE) #if defined(FILE_MONITOR_BACKEND_KQUEUE)
g_type_ensure (g_kqueue_file_monitor_get_type ()); g_type_ensure (g_kqueue_file_monitor_get_type ());
#endif #endif
#ifdef G_OS_WIN32 #ifdef G_OS_WIN32

View File

@ -1,6 +1,7 @@
/* /*
Copyright (C) 2005 John McCutchan Copyright (C) 2005 John McCutchan
Copyright © 2015 Canonical Limited Copyright © 2015 Canonical Limited
Copyright © 2024 Future Crew LLC
SPDX-License-Identifier: LGPL-2.1-or-later SPDX-License-Identifier: LGPL-2.1-or-later
@ -20,6 +21,7 @@
Authors: Authors:
Ryan Lortie <desrt@desrt.ca> Ryan Lortie <desrt@desrt.ca>
John McCutchan <john@johnmccutchan.com> John McCutchan <john@johnmccutchan.com>
Gleb Popov <arrowd@FreeBSD.org>
*/ */
#include "config.h" #include "config.h"
@ -32,6 +34,9 @@
#include <glib.h> #include <glib.h>
#include "inotify-kernel.h" #include "inotify-kernel.h"
#include <sys/inotify.h> #include <sys/inotify.h>
#ifdef HAVE_SYS_UIO_H
#include <sys/uio.h>
#endif
#ifdef HAVE_SYS_FILIO_H #ifdef HAVE_SYS_FILIO_H
#include <sys/filio.h> #include <sys/filio.h>
#endif #endif
@ -95,11 +100,11 @@ typedef struct
{ {
GSource source; GSource source;
GQueue queue; GQueue queue; /* (element-type ik_event_t) */
gpointer fd_tag; gpointer fd_tag;
gint fd; gint fd;
GHashTable *unmatched_moves; GHashTable *unmatched_moves; /* (element-type guint ik_event_t) */
gboolean is_bored; gboolean is_bored;
} InotifyKernelSource; } InotifyKernelSource;
@ -230,6 +235,7 @@ ik_source_dispatch (GSource *source,
if (iks->is_bored || g_source_query_unix_fd (source, iks->fd_tag)) if (iks->is_bored || g_source_query_unix_fd (source, iks->fd_tag))
{ {
#if defined(FILE_MONITOR_BACKEND_INOTIFY)
gchar stack_buffer[4096]; gchar stack_buffer[4096];
gsize buffer_len; gsize buffer_len;
gchar *buffer; gchar *buffer;
@ -269,12 +275,10 @@ ik_source_dispatch (GSource *source,
{ {
ik_event_t *pair; ik_event_t *pair;
pair = g_hash_table_lookup (iks->unmatched_moves, GUINT_TO_POINTER (event->cookie)); if (g_hash_table_steal_extended (iks->unmatched_moves, GUINT_TO_POINTER (event->cookie), NULL, (gpointer*)&pair))
if (pair != NULL)
{ {
g_assert (!pair->pair); g_assert (!pair->pair);
g_hash_table_remove (iks->unmatched_moves, GUINT_TO_POINTER (event->cookie));
event->is_second_in_pair = TRUE; event->is_second_in_pair = TRUE;
event->pair = pair; event->pair = pair;
pair->pair = event; pair->pair = event;
@ -312,6 +316,76 @@ ik_source_dispatch (GSource *source,
if (buffer != stack_buffer) if (buffer != stack_buffer)
g_free (buffer); g_free (buffer);
#elif defined(FILE_MONITOR_BACKEND_LIBINOTIFY_KQUEUE)
struct iovec *received[5];
int num_events = libinotify_direct_readv (iks->fd, received, G_N_ELEMENTS(received), /* no_block=*/ 1);
if (num_events < 0)
{
int errsv = errno;
g_warning ("Failed to read inotify events: %s", g_strerror (errsv));
/* fall through and skip the next few blocks */
}
for (int i = 0; i < num_events; i++)
{
struct iovec *cur_event = received[i];
while (cur_event->iov_base)
{
struct inotify_event *kevent = (struct inotify_event *) cur_event->iov_base;
ik_event_t *event;
event = ik_event_new (kevent, now);
if (event->mask & IN_MOVED_TO)
{
ik_event_t *pair;
if (g_hash_table_steal_extended (iks->unmatched_moves, GUINT_TO_POINTER (event->cookie), NULL, (gpointer*)&pair))
{
g_assert (!pair->pair);
event->is_second_in_pair = TRUE;
event->pair = pair;
pair->pair = event;
cur_event++;
continue;
}
interesting = TRUE;
}
else if (event->mask & IN_MOVED_FROM)
{
gboolean new;
new = g_hash_table_insert (iks->unmatched_moves, GUINT_TO_POINTER (event->cookie), event);
if G_UNLIKELY (!new)
g_warning ("inotify: got IN_MOVED_FROM event with already-pending cookie %#x", event->cookie);
interesting = TRUE;
}
g_queue_push_tail (&iks->queue, event);
cur_event++;
}
libinotify_free_iovec (received[i]);
}
if (num_events == 0)
{
/* We can end up reading nothing if we arrived here due to a
* boredom timer but the stream of events stopped meanwhile.
*
* In that case, we need to switch back to polling the file
* descriptor in the usual way.
*/
g_assert (iks->is_bored);
interesting = TRUE;
}
#endif
} }
while (ik_source_can_dispatch_now (iks, now)) while (ik_source_can_dispatch_now (iks, now))
@ -369,13 +443,30 @@ ik_source_dispatch (GSource *source,
return TRUE; return TRUE;
} }
static void
ik_source_finalize (GSource *source)
{
InotifyKernelSource *iks;
iks = (InotifyKernelSource *) source;
#if defined(FILE_MONITOR_BACKEND_INOTIFY)
close (iks->fd);
#elif defined(FILE_MONITOR_BACKEND_LIBINOTIFY_KQUEUE)
libinotify_direct_close (iks->fd);
#endif
iks->fd = -1;
}
static InotifyKernelSource * static InotifyKernelSource *
ik_source_new (gboolean (* callback) (ik_event_t *event)) ik_source_new (gboolean (* callback) (ik_event_t *event))
{ {
static GSourceFuncs source_funcs = { static GSourceFuncs source_funcs = {
NULL, NULL, NULL, NULL,
ik_source_dispatch, ik_source_dispatch,
NULL, NULL, NULL ik_source_finalize,
NULL, NULL
}; };
InotifyKernelSource *iks; InotifyKernelSource *iks;
GSource *source; GSource *source;
@ -387,23 +478,31 @@ ik_source_new (gboolean (* callback) (ik_event_t *event))
g_source_set_static_name (source, "inotify kernel source"); g_source_set_static_name (source, "inotify kernel source");
iks->unmatched_moves = g_hash_table_new (NULL, NULL); iks->unmatched_moves = g_hash_table_new (NULL, NULL);
#if defined(FILE_MONITOR_BACKEND_INOTIFY)
iks->fd = inotify_init1 (IN_CLOEXEC | IN_NONBLOCK); iks->fd = inotify_init1 (IN_CLOEXEC | IN_NONBLOCK);
#elif defined(FILE_MONITOR_BACKEND_LIBINOTIFY_KQUEUE)
iks->fd = inotify_init1 (IN_CLOEXEC | IN_NONBLOCK | IN_DIRECT);
#endif
#ifdef FILE_MONITOR_BACKEND_INOTIFY
if (iks->fd < 0) if (iks->fd < 0)
{ {
should_set_nonblock = TRUE; should_set_nonblock = TRUE;
iks->fd = inotify_init (); iks->fd = inotify_init ();
} }
#endif
if (iks->fd >= 0) if (iks->fd >= 0)
{ {
GError *error = NULL; GError *error = NULL;
#ifdef FILE_MONITOR_BACKEND_INOTIFY
if (should_set_nonblock) if (should_set_nonblock)
{ {
g_unix_set_fd_nonblocking (iks->fd, TRUE, &error); g_unix_set_fd_nonblocking (iks->fd, TRUE, &error);
g_assert_no_error (error); g_assert_no_error (error);
} }
#endif
iks->fd_tag = g_source_add_unix_fd (source, iks->fd, G_IO_IN); iks->fd_tag = g_source_add_unix_fd (source, iks->fd, G_IO_IN);
} }

View File

@ -28,6 +28,9 @@ inotify_sources = [
'ginotifyfilemonitor.c', 'ginotifyfilemonitor.c',
] ]
# necessary for the libinotify-kqueue backend
libinotify_kqueue_dep = dependency('libinotify', required: file_monitor_backend == 'libinotify-kqueue')
inotify_lib = static_library('inotify', inotify_lib = static_library('inotify',
sources : [inotify_sources], sources : [inotify_sources],
include_directories : [configinc, glibinc], include_directories : [configinc, glibinc],
@ -36,6 +39,7 @@ inotify_lib = static_library('inotify',
libglib_dep, libglib_dep,
libgobject_dep, libgobject_dep,
gmodule_inc_dep, gmodule_inc_dep,
libinotify_kqueue_dep,
], ],
gnu_symbol_visibility : 'hidden', gnu_symbol_visibility : 'hidden',
pic : true, pic : true,

View File

@ -789,19 +789,41 @@ gioenumtypes_c = custom_target('gioenumtypes_c',
gioenumtypes_dep = declare_dependency(sources : [gioenumtypes_h, glib_enumtypes_h, gio_visibility_h]) gioenumtypes_dep = declare_dependency(sources : [gioenumtypes_h, glib_enumtypes_h, gio_visibility_h])
file_monitor_backend = get_option('file_monitor_backend')
if file_monitor_backend == 'auto'
if glib_conf.has('HAVE_SYS_INOTIFY_H') and have_func_inotify_init1
file_monitor_backend = 'inotify'
elif have_func_kqueue and have_func_kevent
file_monitor_backend = 'kqueue'
elif host_system == 'windows'
file_monitor_backend = 'win32'
endif
endif
# inotify # inotify
if glib_conf.has('HAVE_SYS_INOTIFY_H') and have_func_inotify_init1 if file_monitor_backend == 'inotify'
glib_conf.set('FILE_MONITOR_BACKEND_INOTIFY', 1)
subdir('inotify')
internal_deps += [ inotify_lib ]
endif
# libinotify-kqueue
# uses the same code as inotify, but changes some functions behavior via #ifdef's
if file_monitor_backend == 'libinotify-kqueue'
glib_conf.set('FILE_MONITOR_BACKEND_LIBINOTIFY_KQUEUE', 1)
subdir('inotify') subdir('inotify')
internal_deps += [ inotify_lib ] internal_deps += [ inotify_lib ]
endif endif
# kevent # kevent
if have_func_kqueue and have_func_kevent if file_monitor_backend == 'kqueue'
glib_conf.set('FILE_MONITOR_BACKEND_KQUEUE', 1)
subdir('kqueue') subdir('kqueue')
internal_deps += [ kqueue_lib ] internal_deps += [ kqueue_lib ]
endif endif
if host_system == 'windows' if file_monitor_backend == 'win32'
glib_conf.set('FILE_MONITOR_BACKEND_WIN32', 1)
subdir('win32') subdir('win32')
internal_deps += [ giowin32_lib ] internal_deps += [ giowin32_lib ]
endif endif

View File

@ -116,11 +116,15 @@ static const gchar DONT_CARE[] = "";
static Environment static Environment
get_environment (GFileMonitor *monitor) get_environment (GFileMonitor *monitor)
{ {
if (g_str_equal (G_OBJECT_TYPE_NAME (monitor), "GInotifyFileMonitor")) #if defined(FILE_MONITOR_BACKEND_INOTIFY)
return INOTIFY; return INOTIFY;
if (g_str_equal (G_OBJECT_TYPE_NAME (monitor), "GKqueueFileMonitor")) #elif defined(FILE_MONITOR_BACKEND_KQUEUE)
return KQUEUE; return KQUEUE;
#elif defined(FILE_MONITOR_BACKEND_LIBINOTIFY_KQUEUE)
return INOTIFY | KQUEUE;
#else
return NONE; return NONE;
#endif
} }
static void static void

View File

@ -409,6 +409,7 @@ headers = [
'strings.h', 'strings.h',
'sys/auxv.h', 'sys/auxv.h',
'sys/event.h', 'sys/event.h',
'sys/uio.h',
'sys/filio.h', 'sys/filio.h',
'sys/inotify.h', 'sys/inotify.h',
'sys/mkdev.h', 'sys/mkdev.h',
@ -2738,4 +2739,5 @@ summary({
'libelf' : get_option('libelf'), 'libelf' : get_option('libelf'),
'multiarch' : get_option('multiarch'), 'multiarch' : get_option('multiarch'),
'introspection' : enable_gir, 'introspection' : enable_gir,
'file_monitor_backend' : get_option('file_monitor_backend'),
}, section: 'Options') }, section: 'Options')

View File

@ -149,3 +149,9 @@ option('introspection',
type: 'feature', type: 'feature',
value: 'auto', value: 'auto',
description: 'Enable generating introspection data (requires gobject-introspection)') description: 'Enable generating introspection data (requires gobject-introspection)')
option('file_monitor_backend',
type : 'combo',
choices : ['auto', 'inotify', 'kqueue', 'libinotify-kqueue', 'win32'],
value : 'auto',
description : 'The name of the system API to use as a GFileMonitor backend')