nexmon – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /* GIO - GLib Input, Output and Streaming Library
2 *
3 * Copyright (C) 2006-2007 Red Hat, Inc.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 *
18 * Author: Alexander Larsson <alexl@redhat.com>
19 */
20  
21 #include "config.h"
22  
23 #include "gioenumtypes.h"
24 #include "glocalfilemonitor.h"
25 #include "giomodule-priv.h"
26 #include "gioerror.h"
27 #include "glibintl.h"
28 #include "glocalfile.h"
29 #include "glib-private.h"
30  
31 #include <string.h>
32  
33 #define DEFAULT_RATE_LIMIT 800 * G_TIME_SPAN_MILLISECOND
34 #define VIRTUAL_CHANGES_DONE_DELAY 2 * G_TIME_SPAN_SECOND
35  
36 /* GFileMonitorSource is a GSource responsible for emitting the changed
37 * signals in the owner-context of the GFileMonitor.
38 *
39 * It contains functionality for cross-thread queuing of events. It
40 * also handles merging of CHANGED events and emission of CHANGES_DONE
41 * events.
42 *
43 * We use the "priv" pointer in the external struct to store it.
44 */
45 struct _GFileMonitorSource {
46 GSource source;
47  
48 GMutex lock;
49 gpointer instance;
50 GFileMonitorFlags flags;
51 gchar *dirname;
52 gchar *basename;
53 gchar *filename;
54 GSequence *pending_changes; /* sorted by ready time */
55 GHashTable *pending_changes_table;
56 GQueue event_queue;
57 gint64 rate_limit;
58 };
59  
60 /* PendingChange is a struct to keep track of a file that needs to have
61 * (at least) a CHANGES_DONE_HINT event sent for it in the near future.
62 *
63 * If 'dirty' is TRUE then a CHANGED event also needs to be sent.
64 *
65 * last_emission is the last time a CHANGED event was emitted. It is
66 * used to calculate the time to send the next event.
67 */
68 typedef struct {
69 gchar *child;
70 guint64 last_emission : 63;
71 guint64 dirty : 1;
72 } PendingChange;
73  
74 /* QueuedEvent is a signal that will be sent immediately, as soon as the
75 * source gets a chance to dispatch. The existence of any queued event
76 * implies that the source is ready now.
77 */
78 typedef struct
79 {
80 GFileMonitorEvent event_type;
81 GFile *child;
82 GFile *other;
83 } QueuedEvent;
84  
85 static gint64
86 pending_change_get_ready_time (const PendingChange *change,
87 GFileMonitorSource *fms)
88 {
89 if (change->dirty)
90 return change->last_emission + fms->rate_limit;
91 else
92 return change->last_emission + VIRTUAL_CHANGES_DONE_DELAY;
93 }
94  
95 static int
96 pending_change_compare_ready_time (gconstpointer a_p,
97 gconstpointer b_p,
98 gpointer user_data)
99 {
100 GFileMonitorSource *fms = user_data;
101 const PendingChange *a = a_p;
102 const PendingChange *b = b_p;
103 gint64 ready_time_a;
104 gint64 ready_time_b;
105  
106 ready_time_a = pending_change_get_ready_time (a, fms);
107 ready_time_b = pending_change_get_ready_time (b, fms);
108  
109 if (ready_time_a < ready_time_b)
110 return -1;
111 else
112 return ready_time_a > ready_time_b;
113 }
114  
115 static void
116 pending_change_free (gpointer data)
117 {
118 PendingChange *change = data;
119  
120 g_free (change->child);
121  
122 g_slice_free (PendingChange, change);
123 }
124  
125 static void
126 queued_event_free (QueuedEvent *event)
127 {
128 g_object_unref (event->child);
129 if (event->other)
130 g_object_unref (event->other);
131  
132 g_slice_free (QueuedEvent, event);
133 }
134  
135 static gint64
136 g_file_monitor_source_get_ready_time (GFileMonitorSource *fms)
137 {
138 GSequenceIter *iter;
139  
140 if (fms->event_queue.length)
141 return 0;
142  
143 iter = g_sequence_get_begin_iter (fms->pending_changes);
144 if (g_sequence_iter_is_end (iter))
145 return -1;
146  
147 return pending_change_get_ready_time (g_sequence_get (iter), fms);
148 }
149  
150 static void
151 g_file_monitor_source_update_ready_time (GFileMonitorSource *fms)
152 {
153 g_source_set_ready_time ((GSource *) fms, g_file_monitor_source_get_ready_time (fms));
154 }
155  
156 static GSequenceIter *
157 g_file_monitor_source_find_pending_change (GFileMonitorSource *fms,
158 const gchar *child)
159 {
160 return g_hash_table_lookup (fms->pending_changes_table, child);
161 }
162  
163 static void
164 g_file_monitor_source_add_pending_change (GFileMonitorSource *fms,
165 const gchar *child,
166 gint64 now)
167 {
168 PendingChange *change;
169 GSequenceIter *iter;
170  
171 change = g_slice_new (PendingChange);
172 change->child = g_strdup (child);
173 change->last_emission = now;
174 change->dirty = FALSE;
175  
176 iter = g_sequence_insert_sorted (fms->pending_changes, change, pending_change_compare_ready_time, fms);
177 g_hash_table_insert (fms->pending_changes_table, change->child, iter);
178 }
179  
180 static gboolean
181 g_file_monitor_source_set_pending_change_dirty (GFileMonitorSource *fms,
182 GSequenceIter *iter)
183 {
184 PendingChange *change;
185  
186 change = g_sequence_get (iter);
187  
188 /* if it was already dirty then this change is 'uninteresting' */
189 if (change->dirty)
190 return FALSE;
191  
192 change->dirty = TRUE;
193  
194 g_sequence_sort_changed (iter, pending_change_compare_ready_time, fms);
195  
196 return TRUE;
197 }
198  
199 static gboolean
200 g_file_monitor_source_get_pending_change_dirty (GFileMonitorSource *fms,
201 GSequenceIter *iter)
202 {
203 PendingChange *change;
204  
205 change = g_sequence_get (iter);
206  
207 return change->dirty;
208 }
209  
210 static void
211 g_file_monitor_source_remove_pending_change (GFileMonitorSource *fms,
212 GSequenceIter *iter,
213 const gchar *child)
214 {
215 /* must remove the hash entry first -- its key is owned by the data
216 * which will be freed when removing the sequence iter
217 */
218 g_hash_table_remove (fms->pending_changes_table, child);
219 g_sequence_remove (iter);
220 }
221  
222 static void
223 g_file_monitor_source_queue_event (GFileMonitorSource *fms,
224 GFileMonitorEvent event_type,
225 const gchar *child,
226 GFile *other)
227 {
228 QueuedEvent *event;
229  
230 event = g_slice_new (QueuedEvent);
231 event->event_type = event_type;
232 if (child)
233 event->child = g_local_file_new_from_dirname_and_basename (fms->dirname, child);
234 else if (fms->dirname)
235 event->child = _g_local_file_new (fms->dirname);
236 else if (fms->filename)
237 event->child = _g_local_file_new (fms->filename);
238 event->other = other;
239 if (other)
240 g_object_ref (other);
241  
242 g_queue_push_tail (&fms->event_queue, event);
243 }
244  
245 static gboolean
246 g_file_monitor_source_file_changed (GFileMonitorSource *fms,
247 const gchar *child,
248 gint64 now)
249 {
250 GSequenceIter *pending;
251 gboolean interesting;
252  
253 pending = g_file_monitor_source_find_pending_change (fms, child);
254  
255 /* If there is no pending change, emit one and create a record,
256 * else: just mark the existing record as dirty.
257 */
258 if (!pending)
259 {
260 g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGED, child, NULL);
261 g_file_monitor_source_add_pending_change (fms, child, now);
262 interesting = TRUE;
263 }
264 else
265 interesting = g_file_monitor_source_set_pending_change_dirty (fms, pending);
266  
267 g_file_monitor_source_update_ready_time (fms);
268  
269 return interesting;
270 }
271  
272 static void
273 g_file_monitor_source_file_changes_done (GFileMonitorSource *fms,
274 const gchar *child)
275 {
276 GSequenceIter *pending;
277  
278 pending = g_file_monitor_source_find_pending_change (fms, child);
279 if (pending)
280 {
281 /* If it is dirty, make sure we push out the last CHANGED event */
282 if (g_file_monitor_source_get_pending_change_dirty (fms, pending))
283 g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGED, child, NULL);
284  
285 g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, child, NULL);
286 g_file_monitor_source_remove_pending_change (fms, pending, child);
287 }
288 }
289  
290 static void
291 g_file_monitor_source_file_created (GFileMonitorSource *fms,
292 const gchar *child,
293 gint64 event_time)
294 {
295 /* Unlikely, but if we have pending changes for this filename, make
296 * sure we flush those out first, before creating the new ones.
297 */
298 g_file_monitor_source_file_changes_done (fms, child);
299  
300 /* Emit CREATE and add a pending changes record */
301 g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CREATED, child, NULL);
302 g_file_monitor_source_add_pending_change (fms, child, event_time);
303 }
304  
305 static void
306 g_file_monitor_source_send_event (GFileMonitorSource *fms,
307 GFileMonitorEvent event_type,
308 const gchar *child,
309 GFile *other)
310 {
311 /* always flush any pending changes before we queue a new event */
312 g_file_monitor_source_file_changes_done (fms, child);
313 g_file_monitor_source_queue_event (fms, event_type, child, other);
314 }
315  
316 static void
317 g_file_monitor_source_send_synthetic_created (GFileMonitorSource *fms,
318 const gchar *child)
319 {
320 g_file_monitor_source_file_changes_done (fms, child);
321 g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CREATED, child, NULL);
322 g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, child, NULL);
323 }
324  
325 static gboolean
326 is_basename (const gchar *name)
327 {
328 if (name[0] == '.' && ((name[1] == '.' && name[2] == '\0') || name[1] == '\0'))
329 return FALSE;
330  
331 return !strchr (name, '/');
332 }
333  
334 gboolean
335 g_file_monitor_source_handle_event (GFileMonitorSource *fms,
336 GFileMonitorEvent event_type,
337 const gchar *child,
338 const gchar *rename_to,
339 GFile *other,
340 gint64 event_time)
341 {
342 gboolean interesting = TRUE;
343  
344 g_assert (!child || is_basename (child));
345 g_assert (!rename_to || is_basename (rename_to));
346  
347 if (fms->basename && (!child || !g_str_equal (child, fms->basename))
348 && (!rename_to || !g_str_equal (rename_to, fms->basename)))
349 return TRUE;
350  
351 g_mutex_lock (&fms->lock);
352  
353 /* monitor is already gone -- don't bother */
354 if (!fms->instance)
355 {
356 g_mutex_unlock (&fms->lock);
357 return TRUE;
358 }
359  
360 switch (event_type)
361 {
362 case G_FILE_MONITOR_EVENT_CREATED:
363 g_assert (!other && !rename_to);
364 g_file_monitor_source_file_created (fms, child, event_time);
365 break;
366  
367 case G_FILE_MONITOR_EVENT_CHANGED:
368 g_assert (!other && !rename_to);
369 interesting = g_file_monitor_source_file_changed (fms, child, event_time);
370 break;
371  
372 case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
373 g_assert (!other && !rename_to);
374 g_file_monitor_source_file_changes_done (fms, child);
375 break;
376  
377 case G_FILE_MONITOR_EVENT_MOVED_IN:
378 g_assert (!rename_to);
379 if (fms->flags & G_FILE_MONITOR_WATCH_MOVES)
380 g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_MOVED_IN, child, other);
381 else
382 g_file_monitor_source_send_synthetic_created (fms, child);
383 break;
384  
385 case G_FILE_MONITOR_EVENT_MOVED_OUT:
386 g_assert (!rename_to);
387 if (fms->flags & G_FILE_MONITOR_WATCH_MOVES)
388 g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_MOVED_OUT, child, other);
389 else if (other && (fms->flags & G_FILE_MONITOR_SEND_MOVED))
390 g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_MOVED, child, other);
391 else
392 g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_DELETED, child, NULL);
393 break;
394  
395 case G_FILE_MONITOR_EVENT_RENAMED:
396 g_assert (!other && rename_to);
397 if (fms->flags & G_FILE_MONITOR_WATCH_MOVES)
398 {
399 GFile *other;
400  
401 other = g_local_file_new_from_dirname_and_basename (fms->dirname, rename_to);
402 g_file_monitor_source_file_changes_done (fms, rename_to);
403 g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_RENAMED, child, other);
404 g_object_unref (other);
405 }
406 else if (fms->flags & G_FILE_MONITOR_SEND_MOVED)
407 {
408 GFile *other;
409  
410 other = g_local_file_new_from_dirname_and_basename (fms->dirname, rename_to);
411 g_file_monitor_source_file_changes_done (fms, rename_to);
412 g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_MOVED, child, other);
413 g_object_unref (other);
414 }
415 else
416 {
417 g_file_monitor_source_send_event (fms, G_FILE_MONITOR_EVENT_DELETED, child, NULL);
418 g_file_monitor_source_send_synthetic_created (fms, rename_to);
419 }
420 break;
421  
422 case G_FILE_MONITOR_EVENT_DELETED:
423 case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
424 case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
425 case G_FILE_MONITOR_EVENT_UNMOUNTED:
426 g_assert (!other && !rename_to);
427 g_file_monitor_source_send_event (fms, event_type, child, NULL);
428 break;
429  
430 case G_FILE_MONITOR_EVENT_MOVED:
431 /* was never available in this API */
432 default:
433 g_assert_not_reached ();
434 }
435  
436 g_file_monitor_source_update_ready_time (fms);
437  
438 g_mutex_unlock (&fms->lock);
439  
440 return interesting;
441 }
442  
443 static gint64
444 g_file_monitor_source_get_rate_limit (GFileMonitorSource *fms)
445 {
446 gint64 rate_limit;
447  
448 g_mutex_lock (&fms->lock);
449 rate_limit = fms->rate_limit;
450 g_mutex_unlock (&fms->lock);
451  
452 return rate_limit;
453 }
454  
455 static gboolean
456 g_file_monitor_source_set_rate_limit (GFileMonitorSource *fms,
457 gint64 rate_limit)
458 {
459 gboolean changed;
460  
461 g_mutex_lock (&fms->lock);
462  
463 if (rate_limit != fms->rate_limit)
464 {
465 fms->rate_limit = rate_limit;
466  
467 g_sequence_sort (fms->pending_changes, pending_change_compare_ready_time, fms);
468 g_file_monitor_source_update_ready_time (fms);
469  
470 changed = TRUE;
471 }
472 else
473 changed = FALSE;
474  
475 g_mutex_unlock (&fms->lock);
476  
477 return changed;
478 }
479  
480 static gboolean
481 g_file_monitor_source_dispatch (GSource *source,
482 GSourceFunc callback,
483 gpointer user_data)
484 {
485 GFileMonitorSource *fms = (GFileMonitorSource *) source;
486 QueuedEvent *event;
487 GQueue event_queue;
488 gint64 now;
489  
490 /* make sure the monitor still exists */
491 if (!fms->instance)
492 return FALSE;
493  
494 now = g_source_get_time (source);
495  
496 /* Acquire the lock once and grab all events in one go, handling the
497 * queued events first. This avoids strange possibilities in cases of
498 * long delays, such as CHANGED events coming before CREATED events.
499 *
500 * We do this by converting the applicable pending changes into queued
501 * events (after the ones already queued) and then stealing the entire
502 * event queue in one go.
503 */
504 g_mutex_lock (&fms->lock);
505  
506 /* Create events for any pending changes that are due to fire */
507 while (!g_sequence_is_empty (fms->pending_changes))
508 {
509 GSequenceIter *iter = g_sequence_get_begin_iter (fms->pending_changes);
510 PendingChange *pending = g_sequence_get (iter);
511  
512 /* We've gotten to a pending change that's not ready. Stop. */
513 if (pending_change_get_ready_time (pending, fms) > now)
514 break;
515  
516 if (pending->dirty)
517 {
518 /* It's time to send another CHANGED and update the record */
519 g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGED, pending->child, NULL);
520 pending->last_emission = now;
521 pending->dirty = FALSE;
522  
523 g_sequence_sort_changed (iter, pending_change_compare_ready_time, fms);
524 }
525 else
526 {
527 /* It's time to send CHANGES_DONE and remove the pending record */
528 g_file_monitor_source_queue_event (fms, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT, pending->child, NULL);
529 g_file_monitor_source_remove_pending_change (fms, iter, pending->child);
530 }
531 }
532  
533 /* Steal the queue */
534 memcpy (&event_queue, &fms->event_queue, sizeof event_queue);
535 memset (&fms->event_queue, 0, sizeof fms->event_queue);
536  
537 g_file_monitor_source_update_ready_time (fms);
538  
539 g_mutex_unlock (&fms->lock);
540  
541 /* We now have our list of events to deliver */
542 while ((event = g_queue_pop_head (&event_queue)))
543 {
544 /* an event handler could destroy 'instance', so check each time */
545 if (fms->instance)
546 g_file_monitor_emit_event (fms->instance, event->child, event->other, event->event_type);
547  
548 queued_event_free (event);
549 }
550  
551 return TRUE;
552 }
553  
554 static void
555 g_file_monitor_source_dispose (GFileMonitorSource *fms)
556 {
557 g_mutex_lock (&fms->lock);
558  
559 if (fms->instance)
560 {
561 GHashTableIter iter;
562 gpointer seqiter;
563 QueuedEvent *event;
564  
565 g_hash_table_iter_init (&iter, fms->pending_changes_table);
566 while (g_hash_table_iter_next (&iter, NULL, &seqiter))
567 {
568 g_hash_table_iter_remove (&iter);
569 g_sequence_remove (seqiter);
570 }
571  
572 while ((event = g_queue_pop_head (&fms->event_queue)))
573 queued_event_free (event);
574  
575 g_assert (g_sequence_is_empty (fms->pending_changes));
576 g_assert (g_hash_table_size (fms->pending_changes_table) == 0);
577 g_assert (fms->event_queue.length == 0);
578 fms->instance = NULL;
579  
580 g_file_monitor_source_update_ready_time (fms);
581 }
582  
583 g_mutex_unlock (&fms->lock);
584  
585 g_source_destroy ((GSource *) fms);
586 }
587  
588 static void
589 g_file_monitor_source_finalize (GSource *source)
590 {
591 GFileMonitorSource *fms = (GFileMonitorSource *) source;
592  
593 /* should already have been cleared in dispose of the monitor */
594 g_assert (fms->instance == NULL);
595 g_assert (g_sequence_is_empty (fms->pending_changes));
596 g_assert (g_hash_table_size (fms->pending_changes_table) == 0);
597 g_assert (fms->event_queue.length == 0);
598  
599 g_hash_table_unref (fms->pending_changes_table);
600 g_sequence_free (fms->pending_changes);
601  
602 g_free (fms->dirname);
603 g_free (fms->basename);
604 g_free (fms->filename);
605  
606 g_mutex_clear (&fms->lock);
607 }
608  
609 static guint
610 str_hash0 (gconstpointer str)
611 {
612 return str ? g_str_hash (str) : 0;
613 }
614  
615 static gboolean
616 str_equal0 (gconstpointer a,
617 gconstpointer b)
618 {
619 return g_strcmp0 (a, b) == 0;
620 }
621  
622 static GFileMonitorSource *
623 g_file_monitor_source_new (gpointer instance,
624 const gchar *filename,
625 gboolean is_directory,
626 GFileMonitorFlags flags)
627 {
628 static GSourceFuncs source_funcs = {
629 NULL, NULL,
630 g_file_monitor_source_dispatch,
631 g_file_monitor_source_finalize
632 };
633 GFileMonitorSource *fms;
634 GSource *source;
635  
636 source = g_source_new (&source_funcs, sizeof (GFileMonitorSource));
637 fms = (GFileMonitorSource *) source;
638  
639 g_mutex_init (&fms->lock);
640 fms->instance = instance;
641 fms->pending_changes = g_sequence_new (pending_change_free);
642 fms->pending_changes_table = g_hash_table_new (str_hash0, str_equal0);
643 fms->rate_limit = DEFAULT_RATE_LIMIT;
644 fms->flags = flags;
645  
646 if (is_directory)
647 {
648 fms->dirname = g_strdup (filename);
649 fms->basename = NULL;
650 fms->filename = NULL;
651 }
652 else if (flags & G_FILE_MONITOR_WATCH_HARD_LINKS)
653 {
654 fms->dirname = NULL;
655 fms->basename = NULL;
656 fms->filename = g_strdup (filename);
657 }
658 else
659 {
660 fms->dirname = g_path_get_dirname (filename);
661 fms->basename = g_path_get_basename (filename);
662 fms->filename = NULL;
663 }
664  
665 return fms;
666 }
667  
668 G_DEFINE_ABSTRACT_TYPE (GLocalFileMonitor, g_local_file_monitor, G_TYPE_FILE_MONITOR)
669  
670 enum {
671 PROP_0,
672 PROP_RATE_LIMIT,
673 };
674  
675 static void
676 g_local_file_monitor_get_property (GObject *object, guint prop_id,
677 GValue *value, GParamSpec *pspec)
678 {
679 GLocalFileMonitor *monitor = G_LOCAL_FILE_MONITOR (object);
680 gint64 rate_limit;
681  
682 g_assert (prop_id == PROP_RATE_LIMIT);
683  
684 rate_limit = g_file_monitor_source_get_rate_limit (monitor->source);
685 rate_limit /= G_TIME_SPAN_MILLISECOND;
686  
687 g_value_set_int (value, rate_limit);
688 }
689  
690 static void
691 g_local_file_monitor_set_property (GObject *object, guint prop_id,
692 const GValue *value, GParamSpec *pspec)
693 {
694 GLocalFileMonitor *monitor = G_LOCAL_FILE_MONITOR (object);
695 gint64 rate_limit;
696  
697 g_assert (prop_id == PROP_RATE_LIMIT);
698  
699 rate_limit = g_value_get_int (value);
700 rate_limit *= G_TIME_SPAN_MILLISECOND;
701  
702 if (g_file_monitor_source_set_rate_limit (monitor->source, rate_limit))
703 g_object_notify (object, "rate-limit");
704 }
705  
706 #ifndef G_OS_WIN32
707 static void
708 g_local_file_monitor_mounts_changed (GUnixMountMonitor *mount_monitor,
709 gpointer user_data)
710 {
711 GLocalFileMonitor *local_monitor = user_data;
712 GUnixMountEntry *mount;
713 gboolean is_mounted;
714 GFile *file;
715  
716 /* Emulate unmount detection */
717 mount = g_unix_mount_at (local_monitor->source->dirname, NULL);
718  
719 is_mounted = mount != NULL;
720  
721 if (mount)
722 g_unix_mount_free (mount);
723  
724 if (local_monitor->was_mounted != is_mounted)
725 {
726 if (local_monitor->was_mounted && !is_mounted)
727 {
728 file = g_file_new_for_path (local_monitor->source->dirname);
729 g_file_monitor_emit_event (G_FILE_MONITOR (local_monitor), file, NULL, G_FILE_MONITOR_EVENT_UNMOUNTED);
730 g_object_unref (file);
731 }
732 local_monitor->was_mounted = is_mounted;
733 }
734 }
735 #endif
736  
737 static void
738 g_local_file_monitor_start (GLocalFileMonitor *local_monitor,
739 const gchar *filename,
740 gboolean is_directory,
741 GFileMonitorFlags flags,
742 GMainContext *context)
743 {
744 GLocalFileMonitorClass *class = G_LOCAL_FILE_MONITOR_GET_CLASS (local_monitor);
745 GFileMonitorSource *source;
746  
747 g_return_if_fail (G_IS_LOCAL_FILE_MONITOR (local_monitor));
748  
749 g_assert (!local_monitor->source);
750  
751 source = g_file_monitor_source_new (local_monitor, filename, is_directory, flags);
752 local_monitor->source = source; /* owns the ref */
753  
754 if (is_directory && !class->mount_notify && (flags & G_FILE_MONITOR_WATCH_MOUNTS))
755 {
756 #ifdef G_OS_WIN32
757 /*claim everything was mounted */
758 local_monitor->was_mounted = TRUE;
759 #else
760 GUnixMountEntry *mount;
761  
762 /* Emulate unmount detection */
763  
764 mount = g_unix_mount_at (local_monitor->source->dirname, NULL);
765  
766 local_monitor->was_mounted = mount != NULL;
767  
768 if (mount)
769 g_unix_mount_free (mount);
770  
771 local_monitor->mount_monitor = g_unix_mount_monitor_get ();
772 g_signal_connect_object (local_monitor->mount_monitor, "mounts-changed",
773 G_CALLBACK (g_local_file_monitor_mounts_changed), local_monitor, 0);
774 #endif
775 }
776  
777 G_LOCAL_FILE_MONITOR_GET_CLASS (local_monitor)->start (local_monitor,
778 source->dirname, source->basename, source->filename,
779 source);
780  
781 g_source_attach ((GSource *) source, context);
782 }
783  
784 static void
785 g_local_file_monitor_dispose (GObject *object)
786 {
787 GLocalFileMonitor *local_monitor = G_LOCAL_FILE_MONITOR (object);
788  
789 g_file_monitor_source_dispose (local_monitor->source);
790  
791 G_OBJECT_CLASS (g_local_file_monitor_parent_class)->dispose (object);
792 }
793  
794 static void
795 g_local_file_monitor_finalize (GObject *object)
796 {
797 GLocalFileMonitor *local_monitor = G_LOCAL_FILE_MONITOR (object);
798  
799 g_source_unref ((GSource *) local_monitor->source);
800  
801 G_OBJECT_CLASS (g_local_file_monitor_parent_class)->finalize (object);
802 }
803  
804 static void
805 g_local_file_monitor_init (GLocalFileMonitor* local_monitor)
806 {
807 }
808  
809 static void g_local_file_monitor_class_init (GLocalFileMonitorClass *class)
810 {
811 GObjectClass *gobject_class = G_OBJECT_CLASS (class);
812  
813 gobject_class->get_property = g_local_file_monitor_get_property;
814 gobject_class->set_property = g_local_file_monitor_set_property;
815 gobject_class->dispose = g_local_file_monitor_dispose;
816 gobject_class->finalize = g_local_file_monitor_finalize;
817  
818 g_object_class_override_property (gobject_class, PROP_RATE_LIMIT, "rate-limit");
819 }
820  
821 static GLocalFileMonitor *
822 g_local_file_monitor_new (gboolean is_remote_fs,
823 GError **error)
824 {
825 GType type = G_TYPE_INVALID;
826  
827 if (is_remote_fs)
828 type = _g_io_module_get_default_type (G_NFS_FILE_MONITOR_EXTENSION_POINT_NAME,
829 "GIO_USE_FILE_MONITOR",
830 G_STRUCT_OFFSET (GLocalFileMonitorClass, is_supported));
831  
832 if (type == G_TYPE_INVALID)
833 type = _g_io_module_get_default_type (G_LOCAL_FILE_MONITOR_EXTENSION_POINT_NAME,
834 "GIO_USE_FILE_MONITOR",
835 G_STRUCT_OFFSET (GLocalFileMonitorClass, is_supported));
836  
837 if (type == G_TYPE_INVALID)
838 {
839 g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
840 _("Unable to find default local file monitor type"));
841 return NULL;
842 }
843  
844 return g_object_new (type, NULL);
845 }
846  
847 GFileMonitor *
848 g_local_file_monitor_new_for_path (const gchar *pathname,
849 gboolean is_directory,
850 GFileMonitorFlags flags,
851 GError **error)
852 {
853 GLocalFileMonitor *monitor;
854 gboolean is_remote_fs;
855  
856 is_remote_fs = g_local_file_is_remote (pathname);
857  
858 monitor = g_local_file_monitor_new (is_remote_fs, error);
859  
860 if (monitor)
861 g_local_file_monitor_start (monitor, pathname, is_directory, flags, g_main_context_get_thread_default ());
862  
863 return G_FILE_MONITOR (monitor);
864 }
865  
866 GFileMonitor *
867 g_local_file_monitor_new_in_worker (const gchar *pathname,
868 gboolean is_directory,
869 GFileMonitorFlags flags,
870 GFileMonitorCallback callback,
871 gpointer user_data,
872 GError **error)
873 {
874 GLocalFileMonitor *monitor;
875 gboolean is_remote_fs;
876  
877 is_remote_fs = g_local_file_is_remote (pathname);
878  
879 monitor = g_local_file_monitor_new (is_remote_fs, error);
880  
881 if (monitor)
882 {
883 if (callback)
884 g_signal_connect (monitor, "changed", G_CALLBACK (callback), user_data);
885  
886 g_local_file_monitor_start (monitor, pathname, is_directory, flags, GLIB_PRIVATE_CALL(g_get_worker_context) ());
887 }
888  
889 return G_FILE_MONITOR (monitor);
890 }