mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2025-03-03 06:32:10 +01:00
Make GUnixMountMonitor per-context
GUnixMountMonitor was not threadsafe before. It was a global singleton which emitted signals in the first thread that happened to construct it. Move it to a per-context singleton model where each GMainContext gets its own GUnixMountMonitor. Monitor for the changes from the GLib worker thread and dispatch the results to each context with an active monitor. https://bugzilla.gnome.org/show_bug.cgi?id=742599
This commit is contained in:
parent
ae38d2bfa4
commit
548c165a9f
@ -68,6 +68,7 @@
|
||||
#include "gfilemonitor.h"
|
||||
#include "glibintl.h"
|
||||
#include "gthemedicon.h"
|
||||
#include "gcontextspecificgroup.h"
|
||||
|
||||
|
||||
#ifdef HAVE_MNTENT_H
|
||||
@ -1274,52 +1275,198 @@ static guint signals[LAST_SIGNAL];
|
||||
struct _GUnixMountMonitor {
|
||||
GObject parent;
|
||||
|
||||
GFileMonitor *fstab_monitor;
|
||||
GFileMonitor *mtab_monitor;
|
||||
|
||||
GList *mount_poller_mounts;
|
||||
|
||||
GSource *proc_mounts_watch_source;
|
||||
GMainContext *context;
|
||||
};
|
||||
|
||||
struct _GUnixMountMonitorClass {
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
static GUnixMountMonitor *the_mount_monitor = NULL;
|
||||
|
||||
G_DEFINE_TYPE (GUnixMountMonitor, g_unix_mount_monitor, G_TYPE_OBJECT);
|
||||
|
||||
static GContextSpecificGroup mount_monitor_group;
|
||||
static GFileMonitor *fstab_monitor;
|
||||
static GFileMonitor *mtab_monitor;
|
||||
static GSource *proc_mounts_watch_source;
|
||||
static GList *mount_poller_mounts;
|
||||
|
||||
static void
|
||||
fstab_file_changed (GFileMonitor *monitor,
|
||||
GFile *file,
|
||||
GFile *other_file,
|
||||
GFileMonitorEvent event_type,
|
||||
gpointer user_data)
|
||||
{
|
||||
if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
|
||||
event_type != G_FILE_MONITOR_EVENT_CREATED &&
|
||||
event_type != G_FILE_MONITOR_EVENT_DELETED)
|
||||
return;
|
||||
|
||||
g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTPOINTS_CHANGED]);
|
||||
}
|
||||
|
||||
static void
|
||||
mtab_file_changed (GFileMonitor *monitor,
|
||||
GFile *file,
|
||||
GFile *other_file,
|
||||
GFileMonitorEvent event_type,
|
||||
gpointer user_data)
|
||||
{
|
||||
if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
|
||||
event_type != G_FILE_MONITOR_EVENT_CREATED &&
|
||||
event_type != G_FILE_MONITOR_EVENT_DELETED)
|
||||
return;
|
||||
|
||||
g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTS_CHANGED]);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
proc_mounts_changed (GIOChannel *channel,
|
||||
GIOCondition cond,
|
||||
gpointer user_data)
|
||||
{
|
||||
if (cond & G_IO_ERR)
|
||||
g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTS_CHANGED]);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
mount_change_poller (gpointer user_data)
|
||||
{
|
||||
GList *current_mounts, *new_it, *old_it;
|
||||
gboolean has_changed = FALSE;
|
||||
|
||||
current_mounts = _g_get_unix_mounts ();
|
||||
|
||||
for ( new_it = current_mounts, old_it = mount_poller_mounts;
|
||||
new_it != NULL && old_it != NULL;
|
||||
new_it = g_list_next (new_it), old_it = g_list_next (old_it) )
|
||||
{
|
||||
if (g_unix_mount_compare (new_it->data, old_it->data) != 0)
|
||||
{
|
||||
has_changed = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!(new_it == NULL && old_it == NULL))
|
||||
has_changed = TRUE;
|
||||
|
||||
g_list_free_full (mount_poller_mounts, (GDestroyNotify) g_unix_mount_free);
|
||||
|
||||
mount_poller_mounts = current_mounts;
|
||||
|
||||
if (has_changed)
|
||||
{
|
||||
mount_poller_time = (guint64) g_get_monotonic_time ();
|
||||
g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTPOINTS_CHANGED]);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
mount_monitor_stop (void)
|
||||
{
|
||||
if (fstab_monitor)
|
||||
{
|
||||
g_file_monitor_cancel (fstab_monitor);
|
||||
g_object_unref (fstab_monitor);
|
||||
}
|
||||
|
||||
if (proc_mounts_watch_source != NULL)
|
||||
g_source_destroy (proc_mounts_watch_source);
|
||||
|
||||
if (mtab_monitor)
|
||||
{
|
||||
g_file_monitor_cancel (mtab_monitor);
|
||||
g_object_unref (mtab_monitor);
|
||||
}
|
||||
|
||||
g_list_free_full (mount_poller_mounts, (GDestroyNotify) g_unix_mount_free);
|
||||
}
|
||||
|
||||
static void
|
||||
mount_monitor_start (void)
|
||||
{
|
||||
GFile *file;
|
||||
|
||||
if (get_fstab_file () != NULL)
|
||||
{
|
||||
file = g_file_new_for_path (get_fstab_file ());
|
||||
fstab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
|
||||
g_object_unref (file);
|
||||
|
||||
g_signal_connect (fstab_monitor, "changed", (GCallback)fstab_file_changed, NULL);
|
||||
}
|
||||
|
||||
if (get_mtab_monitor_file () != NULL)
|
||||
{
|
||||
const gchar *mtab_path;
|
||||
|
||||
mtab_path = get_mtab_monitor_file ();
|
||||
/* /proc/mounts monitoring is special - can't just use GFileMonitor.
|
||||
* See 'man proc' for more details.
|
||||
*/
|
||||
if (g_strcmp0 (mtab_path, "/proc/mounts") == 0)
|
||||
{
|
||||
GIOChannel *proc_mounts_channel;
|
||||
GError *error = NULL;
|
||||
proc_mounts_channel = g_io_channel_new_file ("/proc/mounts", "r", &error);
|
||||
if (proc_mounts_channel == NULL)
|
||||
{
|
||||
g_warning ("Error creating IO channel for /proc/mounts: %s (%s, %d)",
|
||||
error->message, g_quark_to_string (error->domain), error->code);
|
||||
g_error_free (error);
|
||||
}
|
||||
else
|
||||
{
|
||||
proc_mounts_watch_source = g_io_create_watch (proc_mounts_channel, G_IO_ERR);
|
||||
g_source_set_callback (proc_mounts_watch_source,
|
||||
(GSourceFunc) proc_mounts_changed,
|
||||
NULL, NULL);
|
||||
g_source_attach (proc_mounts_watch_source,
|
||||
g_main_context_get_thread_default ());
|
||||
g_source_unref (proc_mounts_watch_source);
|
||||
g_io_channel_unref (proc_mounts_channel);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
file = g_file_new_for_path (mtab_path);
|
||||
mtab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
|
||||
g_object_unref (file);
|
||||
g_signal_connect (mtab_monitor, "changed", (GCallback)mtab_file_changed, NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
proc_mounts_watch_source = g_timeout_source_new_seconds (3);
|
||||
mount_poller_mounts = _g_get_unix_mounts ();
|
||||
mount_poller_time = (guint64)g_get_monotonic_time ();
|
||||
g_source_set_callback (proc_mounts_watch_source,
|
||||
mount_change_poller,
|
||||
NULL, NULL);
|
||||
g_source_attach (proc_mounts_watch_source,
|
||||
g_main_context_get_thread_default ());
|
||||
g_source_unref (proc_mounts_watch_source);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
g_unix_mount_monitor_finalize (GObject *object)
|
||||
{
|
||||
GUnixMountMonitor *monitor;
|
||||
|
||||
|
||||
monitor = G_UNIX_MOUNT_MONITOR (object);
|
||||
|
||||
if (monitor->fstab_monitor)
|
||||
{
|
||||
g_file_monitor_cancel (monitor->fstab_monitor);
|
||||
g_object_unref (monitor->fstab_monitor);
|
||||
}
|
||||
|
||||
if (monitor->proc_mounts_watch_source != NULL)
|
||||
g_source_destroy (monitor->proc_mounts_watch_source);
|
||||
|
||||
if (monitor->mtab_monitor)
|
||||
{
|
||||
g_file_monitor_cancel (monitor->mtab_monitor);
|
||||
g_object_unref (monitor->mtab_monitor);
|
||||
}
|
||||
|
||||
g_list_free_full (monitor->mount_poller_mounts, (GDestroyNotify)g_unix_mount_free);
|
||||
|
||||
the_mount_monitor = NULL;
|
||||
g_context_specific_group_remove (&mount_monitor_group, monitor->context, monitor, mount_monitor_stop);
|
||||
|
||||
G_OBJECT_CLASS (g_unix_mount_monitor_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
g_unix_mount_monitor_class_init (GUnixMountMonitorClass *klass)
|
||||
{
|
||||
@ -1358,156 +1505,9 @@ g_unix_mount_monitor_class_init (GUnixMountMonitorClass *klass)
|
||||
G_TYPE_NONE, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
fstab_file_changed (GFileMonitor *monitor,
|
||||
GFile *file,
|
||||
GFile *other_file,
|
||||
GFileMonitorEvent event_type,
|
||||
gpointer user_data)
|
||||
{
|
||||
GUnixMountMonitor *mount_monitor;
|
||||
|
||||
if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
|
||||
event_type != G_FILE_MONITOR_EVENT_CREATED &&
|
||||
event_type != G_FILE_MONITOR_EVENT_DELETED)
|
||||
return;
|
||||
|
||||
mount_monitor = user_data;
|
||||
g_signal_emit (mount_monitor, signals[MOUNTPOINTS_CHANGED], 0);
|
||||
}
|
||||
|
||||
static void
|
||||
mtab_file_changed (GFileMonitor *monitor,
|
||||
GFile *file,
|
||||
GFile *other_file,
|
||||
GFileMonitorEvent event_type,
|
||||
gpointer user_data)
|
||||
{
|
||||
GUnixMountMonitor *mount_monitor;
|
||||
|
||||
if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
|
||||
event_type != G_FILE_MONITOR_EVENT_CREATED &&
|
||||
event_type != G_FILE_MONITOR_EVENT_DELETED)
|
||||
return;
|
||||
|
||||
mount_monitor = user_data;
|
||||
g_signal_emit (mount_monitor, signals[MOUNTS_CHANGED], 0);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
proc_mounts_changed (GIOChannel *channel,
|
||||
GIOCondition cond,
|
||||
gpointer user_data)
|
||||
{
|
||||
GUnixMountMonitor *mount_monitor = G_UNIX_MOUNT_MONITOR (user_data);
|
||||
if (cond & G_IO_ERR)
|
||||
g_signal_emit (mount_monitor, signals[MOUNTS_CHANGED], 0);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
mount_change_poller (gpointer user_data)
|
||||
{
|
||||
GUnixMountMonitor *mount_monitor;
|
||||
GList *current_mounts, *new_it, *old_it;
|
||||
gboolean has_changed = FALSE;
|
||||
|
||||
mount_monitor = user_data;
|
||||
current_mounts = _g_get_unix_mounts ();
|
||||
|
||||
for ( new_it = current_mounts, old_it = mount_monitor->mount_poller_mounts;
|
||||
new_it != NULL && old_it != NULL;
|
||||
new_it = g_list_next (new_it), old_it = g_list_next (old_it) )
|
||||
{
|
||||
if (g_unix_mount_compare (new_it->data, old_it->data) != 0)
|
||||
{
|
||||
has_changed = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!(new_it == NULL && old_it == NULL))
|
||||
has_changed = TRUE;
|
||||
|
||||
g_list_free_full (mount_monitor->mount_poller_mounts,
|
||||
(GDestroyNotify)g_unix_mount_free);
|
||||
|
||||
mount_monitor->mount_poller_mounts = current_mounts;
|
||||
|
||||
if (has_changed)
|
||||
{
|
||||
mount_poller_time = (guint64)g_get_monotonic_time ();
|
||||
g_signal_emit (mount_monitor, signals[MOUNTS_CHANGED], 0);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
g_unix_mount_monitor_init (GUnixMountMonitor *monitor)
|
||||
{
|
||||
GFile *file;
|
||||
|
||||
if (get_fstab_file () != NULL)
|
||||
{
|
||||
file = g_file_new_for_path (get_fstab_file ());
|
||||
monitor->fstab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
|
||||
g_object_unref (file);
|
||||
|
||||
g_signal_connect (monitor->fstab_monitor, "changed", (GCallback)fstab_file_changed, monitor);
|
||||
}
|
||||
|
||||
if (get_mtab_monitor_file () != NULL)
|
||||
{
|
||||
const gchar *mtab_path;
|
||||
|
||||
mtab_path = get_mtab_monitor_file ();
|
||||
/* /proc/mounts monitoring is special - can't just use GFileMonitor.
|
||||
* See 'man proc' for more details.
|
||||
*/
|
||||
if (g_strcmp0 (mtab_path, "/proc/mounts") == 0)
|
||||
{
|
||||
GIOChannel *proc_mounts_channel;
|
||||
GError *error = NULL;
|
||||
proc_mounts_channel = g_io_channel_new_file ("/proc/mounts", "r", &error);
|
||||
if (proc_mounts_channel == NULL)
|
||||
{
|
||||
g_warning ("Error creating IO channel for /proc/mounts: %s (%s, %d)",
|
||||
error->message, g_quark_to_string (error->domain), error->code);
|
||||
g_error_free (error);
|
||||
}
|
||||
else
|
||||
{
|
||||
monitor->proc_mounts_watch_source = g_io_create_watch (proc_mounts_channel, G_IO_ERR);
|
||||
g_source_set_callback (monitor->proc_mounts_watch_source,
|
||||
(GSourceFunc) proc_mounts_changed,
|
||||
monitor,
|
||||
NULL);
|
||||
g_source_attach (monitor->proc_mounts_watch_source,
|
||||
g_main_context_get_thread_default ());
|
||||
g_source_unref (monitor->proc_mounts_watch_source);
|
||||
g_io_channel_unref (proc_mounts_channel);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
file = g_file_new_for_path (mtab_path);
|
||||
monitor->mtab_monitor = g_file_monitor_file (file, 0, NULL, NULL);
|
||||
g_object_unref (file);
|
||||
g_signal_connect (monitor->mtab_monitor, "changed", (GCallback)mtab_file_changed, monitor);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
monitor->proc_mounts_watch_source = g_timeout_source_new_seconds (3);
|
||||
monitor->mount_poller_mounts = _g_get_unix_mounts ();
|
||||
mount_poller_time = (guint64)g_get_monotonic_time ();
|
||||
g_source_set_callback (monitor->proc_mounts_watch_source,
|
||||
(GSourceFunc)mount_change_poller,
|
||||
monitor, NULL);
|
||||
g_source_attach (monitor->proc_mounts_watch_source,
|
||||
g_main_context_get_thread_default ());
|
||||
g_source_unref (monitor->proc_mounts_watch_source);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1537,12 +1537,16 @@ g_unix_mount_monitor_set_rate_limit (GUnixMountMonitor *mount_monitor,
|
||||
/**
|
||||
* g_unix_mount_monitor_get:
|
||||
*
|
||||
* Gets the #GUnixMountMonitor.
|
||||
* Gets the #GUnixMountMonitor for the current thread-default main
|
||||
* context.
|
||||
*
|
||||
* The mount monitor can be used to monitor for changes to the list of
|
||||
* mounted filesystems as well as the list of mount points (ie: fstab
|
||||
* entries).
|
||||
*
|
||||
* You must only call g_object_unref() on the return value from under
|
||||
* the same main context as you called this function.
|
||||
*
|
||||
* Returns: (transfer full): the #GUnixMountMonitor.
|
||||
*
|
||||
* Since: 2.44
|
||||
@ -1550,13 +1554,10 @@ g_unix_mount_monitor_set_rate_limit (GUnixMountMonitor *mount_monitor,
|
||||
GUnixMountMonitor *
|
||||
g_unix_mount_monitor_get (void)
|
||||
{
|
||||
if (the_mount_monitor == NULL)
|
||||
{
|
||||
the_mount_monitor = g_object_new (G_TYPE_UNIX_MOUNT_MONITOR, NULL);
|
||||
return the_mount_monitor;
|
||||
}
|
||||
|
||||
return g_object_ref (the_mount_monitor);
|
||||
return g_context_specific_group_get (&mount_monitor_group,
|
||||
G_TYPE_UNIX_MOUNT_MONITOR,
|
||||
G_STRUCT_OFFSET(GUnixMountMonitor, context),
|
||||
mount_monitor_start);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user