X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=gio%2Fgfilemonitor.c;h=c81f6fcb5bf4c9f98dbd6b63d286046e78e0eb0d;hb=2a2b11b1bb6c702d6b2ef1c37524a57688a94a4e;hp=4e52932200d1abf5da0d86dbbaa323a469c3e216;hpb=3d93bf6968884d75dd2706ef85e2014305eb92f2;p=platform%2Fupstream%2Fglib.git diff --git a/gio/gfilemonitor.c b/gio/gfilemonitor.c index 4e52932..c81f6fc 100644 --- a/gio/gfilemonitor.c +++ b/gio/gfilemonitor.c @@ -13,9 +13,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General - * Public License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place, Suite 330, - * Boston, MA 02111-1307, USA. + * Public License along with this library; if not, see . * * Author: Alexander Larsson */ @@ -24,13 +22,15 @@ #include #include "gfilemonitor.h" -#include "gio-marshal.h" #include "gioenumtypes.h" #include "gfile.h" #include "gvfs.h" #include "glibintl.h" -#include "gioalias.h" + +struct _FileChange; +typedef struct _FileChange FileChange; +static void file_change_free (FileChange *change); /** * SECTION:gfilemonitor @@ -40,10 +40,17 @@ * Monitors a file or directory for changes. * * To obtain a #GFileMonitor for a file or directory, use - * g_file_monitor_file() or g_file_monitor_directory(). + * g_file_monitor(), g_file_monitor_file(), or + * g_file_monitor_directory(). * - * To get informed about changes to the file or directory you - * are monitoring, connect to the #GFileMonitor::changed signal. + * To get informed about changes to the file or directory you are + * monitoring, connect to the #GFileMonitor::changed signal. The + * signal will be emitted in the + * [thread-default main context][g-main-context-push-thread-default] + * of the thread that the monitor was created in + * (though if the global default main context is blocked, this may + * cause notifications to be blocked even if the thread-default + * context is still running). **/ G_LOCK_DEFINE_STATIC(cancelled); @@ -53,8 +60,6 @@ enum { LAST_SIGNAL }; -G_DEFINE_ABSTRACT_TYPE (GFileMonitor, g_file_monitor, G_TYPE_OBJECT); - typedef struct { GFile *file; guint32 last_sent_change_time; /* 0 == not sent */ @@ -69,16 +74,28 @@ struct _GFileMonitorPrivate { /* Rate limiting change events */ GHashTable *rate_limiter; + GMutex mutex; + GSource *pending_file_change_source; + GSList *pending_file_changes; /* FileChange */ + GSource *timeout; guint32 timeout_fires_at; + + GMainContext *context; }; enum { PROP_0, PROP_RATE_LIMIT, - PROP_CANCELLED + PROP_CANCELLED, + PROP_CONTEXT }; +/* work around a limitation of the aliasing foo */ +#undef g_file_monitor + +G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GFileMonitor, g_file_monitor, G_TYPE_OBJECT) + static void g_file_monitor_set_property (GObject *object, guint prop_id, @@ -95,6 +112,12 @@ g_file_monitor_set_property (GObject *object, g_file_monitor_set_rate_limit (monitor, g_value_get_int (value)); break; + case PROP_CONTEXT: + monitor->priv->context = g_value_dup_boxed (value); + if (monitor->priv->context == NULL) + monitor->priv->context = g_main_context_ref_thread_default (); + break; + default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -158,6 +181,9 @@ g_file_monitor_finalize (GObject *object) g_hash_table_destroy (monitor->priv->rate_limiter); + g_main_context_unref (monitor->priv->context); + g_mutex_clear (&monitor->priv->mutex); + G_OBJECT_CLASS (g_file_monitor_parent_class)->finalize (object); } @@ -165,8 +191,19 @@ static void g_file_monitor_dispose (GObject *object) { GFileMonitor *monitor; + GFileMonitorPrivate *priv; monitor = G_FILE_MONITOR (object); + priv = monitor->priv; + + if (priv->pending_file_change_source) + { + g_source_destroy (priv->pending_file_change_source); + g_source_unref (priv->pending_file_change_source); + priv->pending_file_change_source = NULL; + } + g_slist_free_full (priv->pending_file_changes, (GDestroyNotify) file_change_free); + priv->pending_file_changes = NULL; /* Make sure we cancel on last unref */ g_file_monitor_cancel (monitor); @@ -178,9 +215,7 @@ static void g_file_monitor_class_init (GFileMonitorClass *klass) { GObjectClass *object_class; - - g_type_class_add_private (klass, sizeof (GFileMonitorPrivate)); - + object_class = G_OBJECT_CLASS (klass); object_class->finalize = g_file_monitor_finalize; object_class->dispose = g_file_monitor_dispose; @@ -191,10 +226,16 @@ g_file_monitor_class_init (GFileMonitorClass *klass) * GFileMonitor::changed: * @monitor: a #GFileMonitor. * @file: a #GFile. - * @other_file: a #GFile. + * @other_file: (allow-none): a #GFile or #NULL. * @event_type: a #GFileMonitorEvent. - * - * Emitted when a file has been changed. + * + * Emitted when @file has been changed. + * + * If using #G_FILE_MONITOR_SEND_MOVED flag and @event_type is + * #G_FILE_MONITOR_EVENT_MOVED, @file will be set to a #GFile containing the + * old path, and @other_file will be set to a #GFile containing the new path. + * + * In all the other cases, @other_file will be set to #NULL. **/ signals[CHANGED] = g_signal_new (I_("changed"), @@ -202,7 +243,7 @@ g_file_monitor_class_init (GFileMonitorClass *klass) G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GFileMonitorClass, changed), NULL, NULL, - _gio_marshal_VOID__OBJECT_OBJECT_ENUM, + NULL, G_TYPE_NONE, 3, G_TYPE_FILE, G_TYPE_FILE, G_TYPE_FILE_MONITOR_EVENT); @@ -224,17 +265,24 @@ g_file_monitor_class_init (GFileMonitorClass *klass) FALSE, G_PARAM_READABLE| G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB)); + + g_object_class_install_property (object_class, + PROP_CONTEXT, + g_param_spec_boxed ("context", + P_("Context"), + P_("The main context to dispatch from"), + G_TYPE_MAIN_CONTEXT, G_PARAM_WRITABLE | + G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); } static void g_file_monitor_init (GFileMonitor *monitor) { - monitor->priv = G_TYPE_INSTANCE_GET_PRIVATE (monitor, - G_TYPE_FILE_MONITOR, - GFileMonitorPrivate); + monitor->priv = g_file_monitor_get_instance_private (monitor); monitor->priv->rate_limit_msec = DEFAULT_RATE_LIMIT_MSECS; monitor->priv->rate_limiter = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, NULL, (GDestroyNotify) rate_limiter_free); + g_mutex_init (&monitor->priv->mutex); } /** @@ -293,21 +341,21 @@ g_file_monitor_cancel (GFileMonitor* monitor) /** * g_file_monitor_set_rate_limit: * @monitor: a #GFileMonitor. - * @limit_msecs: a integer with the limit in milliseconds to - * poll for changes. + * @limit_msecs: a non-negative integer with the limit in milliseconds + * to poll for changes * * Sets the rate limit to which the @monitor will report - * consecutive change events to the same file. - * - **/ + * consecutive change events to the same file. + */ void g_file_monitor_set_rate_limit (GFileMonitor *monitor, - int limit_msecs) + gint limit_msecs) { GFileMonitorPrivate *priv; - + g_return_if_fail (G_IS_FILE_MONITOR (monitor)); - + g_return_if_fail (limit_msecs >= 0); + priv = monitor->priv; if (priv->rate_limit_msec != limit_msecs) { @@ -316,26 +364,15 @@ g_file_monitor_set_rate_limit (GFileMonitor *monitor, } } -typedef struct { - GFileMonitor *monitor; +struct _FileChange { GFile *child; GFile *other_file; GFileMonitorEvent event_type; -} FileChange; - -static gboolean -emit_cb (gpointer data) -{ - FileChange *change = data; - g_signal_emit (change->monitor, signals[CHANGED], 0, - change->child, change->other_file, change->event_type); - return FALSE; -} +}; static void file_change_free (FileChange *change) { - g_object_unref (change->monitor); g_object_unref (change->child); if (change->other_file) g_object_unref (change->other_file); @@ -343,6 +380,37 @@ file_change_free (FileChange *change) g_slice_free (FileChange, change); } +static gboolean +emit_cb (gpointer data) +{ + GFileMonitor *monitor = G_FILE_MONITOR (data); + GSList *pending, *iter; + + g_mutex_lock (&monitor->priv->mutex); + pending = g_slist_reverse (monitor->priv->pending_file_changes); + monitor->priv->pending_file_changes = NULL; + if (monitor->priv->pending_file_change_source) + { + g_source_unref (monitor->priv->pending_file_change_source); + monitor->priv->pending_file_change_source = NULL; + } + g_mutex_unlock (&monitor->priv->mutex); + + g_object_ref (monitor); + for (iter = pending; iter; iter = iter->next) + { + FileChange *change = iter->data; + + g_signal_emit (monitor, signals[CHANGED], 0, + change->child, change->other_file, change->event_type); + file_change_free (change); + } + g_slist_free (pending); + g_object_unref (monitor); + + return FALSE; +} + static void emit_in_idle (GFileMonitor *monitor, GFile *child, @@ -351,10 +419,12 @@ emit_in_idle (GFileMonitor *monitor, { GSource *source; FileChange *change; + GFileMonitorPrivate *priv; + + priv = monitor->priv; change = g_slice_new (FileChange); - change->monitor = g_object_ref (monitor); change->child = g_object_ref (child); if (other_file) change->other_file = g_object_ref (other_file); @@ -362,18 +432,29 @@ emit_in_idle (GFileMonitor *monitor, change->other_file = NULL; change->event_type = event_type; - source = g_idle_source_new (); - g_source_set_priority (source, 0); - - g_source_set_callback (source, emit_cb, change, (GDestroyNotify)file_change_free); - g_source_attach (source, NULL); - g_source_unref (source); + g_mutex_lock (&monitor->priv->mutex); + if (!priv->pending_file_change_source) + { + source = g_idle_source_new (); + priv->pending_file_change_source = source; + g_source_set_priority (source, 0); + + /* We don't ref monitor here - instead dispose will free any + * pending idles. + */ + g_source_set_callback (source, emit_cb, monitor, NULL); + g_source_set_name (source, "[gio] emit_cb"); + g_source_attach (source, monitor->priv->context); + } + /* We reverse this in the processor */ + priv->pending_file_changes = g_slist_prepend (priv->pending_file_changes, change); + g_mutex_unlock (&monitor->priv->mutex); } static guint32 get_time_msecs (void) { - return g_thread_gettime() / (1000 * 1000); + return g_get_monotonic_time () / G_TIME_SPAN_MILLISECOND; } static guint32 @@ -445,7 +526,7 @@ calc_min_time (GFileMonitor *monitor, if (limiter->last_sent_change_time != 0) { - /* Set a timeout at 2*rate limit so that we can clear out the change from the hash eventualy */ + /* Set a timeout at 2*rate limit so that we can clear out the change from the hash eventually */ expire_at = limiter->last_sent_change_time + 2 * monitor->priv->rate_limit_msec; if (time_difference (time_now, expire_at) > 0) @@ -520,7 +601,7 @@ rate_limiter_timeout (gpointer timeout_data) { source = g_timeout_source_new (data.min_time + 1); /* + 1 to make sure we've really passed the time */ g_source_set_callback (source, rate_limiter_timeout, monitor, NULL); - g_source_attach (source, NULL); + g_source_attach (source, monitor->priv->context); monitor->priv->timeout = source; monitor->priv->timeout_fires_at = data.time_now + data.min_time; @@ -572,7 +653,7 @@ update_rate_limiter_timeout (GFileMonitor *monitor, { source = g_timeout_source_new (data.min_time + 1); /* + 1 to make sure we've really passed the time */ g_source_set_callback (source, rate_limiter_timeout, monitor, NULL); - g_source_attach (source, NULL); + g_source_attach (source, monitor->priv->context); monitor->priv->timeout = source; monitor->priv->timeout_fires_at = data.time_now + data.min_time; @@ -590,7 +671,8 @@ update_rate_limiter_timeout (GFileMonitor *monitor, * has taken place. Should be called from file monitor * implementations only. * - * The signal will be emitted from an idle handler. + * The signal will be emitted from an idle handler (in the + * [thread-default main context][g-main-context-push-thread-default]). **/ void g_file_monitor_emit_event (GFileMonitor *monitor, @@ -651,7 +733,7 @@ g_file_monitor_emit_event (GFileMonitor *monitor, limiter->last_sent_change_time = time_now; limiter->send_delayed_change_at = 0; - /* Set a timeout of 2*rate limit so that we can clear out the change from the hash eventualy */ + /* Set a timeout of 2*rate limit so that we can clear out the change from the hash eventually */ update_rate_limiter_timeout (monitor, time_now + 2 * monitor->priv->rate_limit_msec); } @@ -662,6 +744,3 @@ g_file_monitor_emit_event (GFileMonitor *monitor, update_rate_limiter_timeout (monitor, limiter->send_virtual_changes_done_at); } } - -#define __G_FILE_MONITOR_C__ -#include "gioaliasdef.c"