diff --git a/gio/glocalfilemonitor.c b/gio/glocalfilemonitor.c
index f408d0707..68afd7b51 100644
--- a/gio/glocalfilemonitor.c
+++ b/gio/glocalfilemonitor.c
@@ -358,11 +358,28 @@ g_file_monitor_source_handle_event (GFileMonitorSource *fms,
 
   g_mutex_lock (&fms->lock);
 
-  /* NOTE: We process events even if the file monitor has already been disposed.
-   *       The reason is that we must not take a reference to the instance here
-   *       as destroying it from the event handling thread will lead to a
-   *       deadlock when taking the lock in _ih_sub_cancel.
+  /* NOTE:
+   *
+   * We process events even if the file monitor has already been disposed.
+   * The reason is that we must not take a reference to the instance here as
+   * destroying it from the event handling thread will lead to a deadlock when
+   * taking the lock in _ih_sub_cancel.
+   *
+   * This results in seemingly-unbounded growth of the `event_queue` with the
+   * calls to `g_file_monitor_source_queue_event()`. However, each of those sets
+   * the ready time on the #GSource, which means that it will be dispatched in
+   * a subsequent iteration of the #GMainContext it’s attached to. At that
+   * point, `g_file_monitor_source_dispatch()` will return %FALSE, and this will
+   * trigger finalisation of the source. That will clear the `event_queue`.
+   *
+   * If the source is no longer attached, this will return early to prevent
+   * unbounded queueing.
    */
+  if (g_source_is_destroyed ((GSource *) fms))
+    {
+      g_mutex_unlock (&fms->lock);
+      return TRUE;
+    }
 
   switch (event_type)
     {
@@ -595,9 +612,9 @@ g_file_monitor_source_dispose (GFileMonitorSource *fms)
 
   g_file_monitor_source_update_ready_time (fms);
 
-  g_mutex_unlock (&fms->lock);
-
   g_source_destroy ((GSource *) fms);
+
+  g_mutex_unlock (&fms->lock);
 }
 
 static void