gdbus-tool: simplify some logic
[platform/upstream/glib.git] / gio / inotify / inotify-kernel.c
1 /*
2    Copyright (C) 2005 John McCutchan
3
4    The Gnome Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Library General Public License as
6    published by the Free Software Foundation; either version 2 of the
7    License, or (at your option) any later version.
8
9    The Gnome Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Library General Public License for more details.
13
14    You should have received a copy of the GNU Library General Public
15    License along with the Gnome Library; see the file COPYING.LIB.  If not,
16    see <http://www.gnu.org/licenses/>.
17
18    Authors:.
19                 John McCutchan <john@johnmccutchan.com>
20 */
21
22 #include "config.h"
23
24 #include <stdio.h>
25 #include <sys/ioctl.h>
26 #include <unistd.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <glib.h>
30 #include "inotify-kernel.h"
31 #include <sys/inotify.h>
32
33 #include "glib-private.h"
34
35 /* Timings for pairing MOVED_TO / MOVED_FROM events */
36 #define PROCESS_EVENTS_TIME 1000 /* 1000 milliseconds (1 hz) */
37 #define DEFAULT_HOLD_UNTIL_TIME 0 /* 0 millisecond */
38 #define MOVE_HOLD_UNTIL_TIME 500 /* 500 microseconds or 0.5 milliseconds */
39
40 static int inotify_instance_fd = -1;
41 static GQueue *events_to_process = NULL;
42 static GQueue *event_queue = NULL;
43 static GHashTable * cookie_hash = NULL;
44 static GIOChannel *inotify_read_ioc;
45 static GPollFD ik_poll_fd;
46 static gboolean ik_poll_fd_enabled = TRUE;
47 static void (*user_cb)(ik_event_t *event);
48
49 static gboolean ik_read_callback (gpointer user_data);
50 static gboolean ik_process_eq_callback (gpointer user_data);
51
52 static guint32 ik_move_matches = 0;
53 static guint32 ik_move_misses = 0;
54
55 static gboolean process_eq_running = FALSE;
56
57 /* We use the lock from inotify-helper.c
58  *
59  * There are two places that we take this lock
60  *
61  * 1) In ik_read_callback
62  *
63  * 2) ik_process_eq_callback.
64  *
65  *
66  * The rest of locking is taken care of in inotify-helper.c
67  */
68 G_LOCK_EXTERN (inotify_lock);
69
70 typedef struct ik_event_internal {
71   ik_event_t *event;
72   gboolean seen;
73   gboolean sent;
74   GTimeVal hold_until;
75   struct ik_event_internal *pair;
76 } ik_event_internal_t;
77
78 /* In order to perform non-sleeping inotify event chunking we need
79  * a custom GSource
80  */
81 static gboolean
82 ik_source_prepare (GSource *source,
83                    gint    *timeout)
84 {
85   return FALSE;
86 }
87
88 static gboolean
89 ik_source_timeout (gpointer data)
90 {
91   GSource *source = (GSource *)data;
92   
93   /* Re-active the PollFD */
94   g_source_add_poll (source, &ik_poll_fd);
95   g_source_unref (source);
96   ik_poll_fd_enabled = TRUE;
97   
98   return FALSE;
99 }
100
101 #define MAX_PENDING_COUNT 2
102 #define PENDING_THRESHOLD(qsize) ((qsize) >> 1)
103 #define PENDING_MARGINAL_COST(p) ((unsigned int)(1 << (p)))
104 #define MAX_QUEUED_EVENTS 2048
105 #define AVERAGE_EVENT_SIZE sizeof (struct inotify_event) + 16
106 #define TIMEOUT_MILLISECONDS 10
107
108 static gboolean
109 ik_source_check (GSource *source)
110 {
111   static int prev_pending = 0, pending_count = 0;
112   
113   /* We already disabled the PollFD or
114    * nothing to be read from inotify */
115   if (!ik_poll_fd_enabled || !(ik_poll_fd.revents & G_IO_IN))
116     return FALSE;
117
118   if (pending_count < MAX_PENDING_COUNT)
119     {
120       GSource *timeout_source;
121       unsigned int pending;
122       
123       if (ioctl (inotify_instance_fd, FIONREAD, &pending) == -1)
124         goto do_read;
125       
126       pending /= AVERAGE_EVENT_SIZE;
127       
128       /* Don't wait if the number of pending events is too close
129        * to the maximum queue size.
130        */
131       if (pending > PENDING_THRESHOLD (MAX_QUEUED_EVENTS))
132         goto do_read;
133       
134       /* With each successive iteration, the minimum rate for
135        * further sleep doubles. 
136        */
137       if (pending-prev_pending < PENDING_MARGINAL_COST (pending_count))
138         goto do_read;
139       
140       prev_pending = pending;
141       pending_count++;
142       
143       /* We are going to wait to read the events: */
144       
145       /* Remove the PollFD from the source */
146       g_source_remove_poll (source, &ik_poll_fd);
147       /* To avoid threading issues we need to flag that we've done that */
148       ik_poll_fd_enabled = FALSE;
149       /* Set a timeout to re-add the PollFD to the source */
150       g_source_ref (source);
151
152       timeout_source = g_timeout_source_new (TIMEOUT_MILLISECONDS);
153       g_source_set_callback (timeout_source, ik_source_timeout, source, NULL);
154       g_source_attach (timeout_source, GLIB_PRIVATE_CALL (g_get_worker_context) ());
155       g_source_unref (timeout_source);
156
157       return FALSE;
158     }
159
160 do_read:
161   /* We are ready to read events from inotify */
162
163   prev_pending = 0;
164   pending_count = 0;
165   
166   return TRUE;
167 }
168
169 static gboolean
170 ik_source_dispatch (GSource     *source,
171                     GSourceFunc  callback,
172                     gpointer     user_data)
173 {
174   if (callback)
175     return callback (user_data);
176   return TRUE;
177 }
178
179 static GSourceFuncs ik_source_funcs =
180 {
181   ik_source_prepare,
182   ik_source_check,
183   ik_source_dispatch,
184   NULL
185 };
186
187 gboolean _ik_startup (void (*cb)(ik_event_t *event))
188 {
189   static gboolean initialized = FALSE;
190   GSource *source;
191   
192   user_cb = cb;
193   /* Ignore multi-calls */
194   if (initialized) 
195     return inotify_instance_fd >= 0;
196
197   initialized = TRUE;
198
199   inotify_instance_fd = inotify_init1 (IN_CLOEXEC);
200
201   if (inotify_instance_fd < 0)
202     inotify_instance_fd = inotify_init ();
203
204   if (inotify_instance_fd < 0)
205     return FALSE;
206
207   inotify_read_ioc = g_io_channel_unix_new (inotify_instance_fd);
208   ik_poll_fd.fd = inotify_instance_fd;
209   ik_poll_fd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
210   g_io_channel_set_encoding (inotify_read_ioc, NULL, NULL);
211   g_io_channel_set_flags (inotify_read_ioc, G_IO_FLAG_NONBLOCK, NULL);
212
213   source = g_source_new (&ik_source_funcs, sizeof (GSource));
214   g_source_set_name (source, "GIO Inotify");
215   g_source_add_poll (source, &ik_poll_fd);
216   g_source_set_callback (source, ik_read_callback, NULL, NULL);
217   g_source_attach (source, GLIB_PRIVATE_CALL (g_get_worker_context) ());
218   g_source_unref (source);
219
220   cookie_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
221   event_queue = g_queue_new ();
222   events_to_process = g_queue_new ();
223   
224   return TRUE;
225 }
226
227 static ik_event_internal_t *
228 ik_event_internal_new (ik_event_t *event)
229 {
230   ik_event_internal_t *internal_event = g_new0 (ik_event_internal_t, 1);
231   GTimeVal tv;
232   
233   g_assert (event);
234   
235   g_get_current_time (&tv);
236   g_time_val_add (&tv, DEFAULT_HOLD_UNTIL_TIME);
237   internal_event->event = event;
238   internal_event->hold_until = tv;
239   
240   return internal_event;
241 }
242
243 static ik_event_t *
244 ik_event_new (char *buffer)
245 {
246   struct inotify_event *kevent = (struct inotify_event *)buffer;
247   ik_event_t *event = g_new0 (ik_event_t, 1);
248   
249   g_assert (buffer);
250   
251   event->wd = kevent->wd;
252   event->mask = kevent->mask;
253   event->cookie = kevent->cookie;
254   event->len = kevent->len;
255   if (event->len)
256     event->name = g_strdup (kevent->name);
257   else
258     event->name = g_strdup ("");
259   
260   return event;
261 }
262
263 void
264 _ik_event_free (ik_event_t *event)
265 {
266   if (event->pair)
267     _ik_event_free (event->pair);
268   g_free (event->name);
269   g_free (event);
270 }
271
272 gint32
273 _ik_watch (const char *path, 
274            guint32     mask, 
275            int        *err)
276 {
277   gint32 wd = -1;
278   
279   g_assert (path != NULL);
280   g_assert (inotify_instance_fd >= 0);
281   
282   wd = inotify_add_watch (inotify_instance_fd, path, mask);
283   
284   if (wd < 0)
285     {
286       int e = errno;
287       /* FIXME: debug msg failed to add watch */
288       if (err)
289         *err = e;
290       return wd;
291     }
292   
293   g_assert (wd >= 0);
294   return wd;
295 }
296
297 int
298 _ik_ignore (const char *path, 
299             gint32      wd)
300 {
301   g_assert (wd >= 0);
302   g_assert (inotify_instance_fd >= 0);
303   
304   if (inotify_rm_watch (inotify_instance_fd, wd) < 0)
305     {
306       /* int e = errno; */
307       /* failed to rm watch */
308       return -1;
309     }
310   
311   return 0;
312 }
313
314 static void
315 ik_read_events (gsize  *buffer_size_out, 
316                 gchar **buffer_out)
317 {
318   static gchar *buffer = NULL;
319   static gsize buffer_size;
320   
321   /* Initialize the buffer on our first call */
322   if (buffer == NULL)
323     {
324       buffer_size = AVERAGE_EVENT_SIZE;
325       buffer_size *= MAX_QUEUED_EVENTS;
326       buffer = g_malloc (buffer_size);
327     }
328
329   *buffer_size_out = 0;
330   *buffer_out = NULL;
331   
332   memset (buffer, 0, buffer_size);
333
334   if (g_io_channel_read_chars (inotify_read_ioc, (char *)buffer, buffer_size, buffer_size_out, NULL) != G_IO_STATUS_NORMAL) {
335     /* error reading */
336   }
337   *buffer_out = buffer;
338 }
339
340 static gboolean
341 ik_read_callback (gpointer user_data)
342 {
343   gchar *buffer;
344   gsize buffer_size, buffer_i, events;
345   
346   G_LOCK (inotify_lock);
347   ik_read_events (&buffer_size, &buffer);
348   
349   buffer_i = 0;
350   events = 0;
351   while (buffer_i < buffer_size)
352     {
353       struct inotify_event *event;
354       gsize event_size;
355       event = (struct inotify_event *)&buffer[buffer_i];
356       event_size = sizeof(struct inotify_event) + event->len;
357       g_queue_push_tail (events_to_process, ik_event_internal_new (ik_event_new (&buffer[buffer_i])));
358       buffer_i += event_size;
359       events++;
360     }
361   
362   /* If the event process callback is off, turn it back on */
363   if (!process_eq_running && events)
364     {
365       GSource *timeout_source;
366
367       process_eq_running = TRUE;
368       timeout_source = g_timeout_source_new (PROCESS_EVENTS_TIME);
369       g_source_set_callback (timeout_source, ik_process_eq_callback, NULL, NULL);
370       g_source_attach (timeout_source, GLIB_PRIVATE_CALL (g_get_worker_context ()));
371       g_source_unref (timeout_source);
372     }
373   
374   G_UNLOCK (inotify_lock);
375   
376   return TRUE;
377 }
378
379 static gboolean
380 g_timeval_lt (GTimeVal *val1,
381               GTimeVal *val2)
382 {
383   if (val1->tv_sec < val2->tv_sec)
384     return TRUE;
385
386   if (val1->tv_sec > val2->tv_sec)
387     return FALSE;
388
389   /* val1->tv_sec == val2->tv_sec */
390   if (val1->tv_usec < val2->tv_usec)
391     return TRUE;
392
393   return FALSE;
394 }
395
396 static gboolean
397 g_timeval_le (GTimeVal *val1,
398               GTimeVal *val2)
399 {
400   if (val1->tv_sec < val2->tv_sec)
401     return TRUE;
402
403   if (val1->tv_sec > val2->tv_sec)
404     return FALSE;
405
406   /* val1->tv_sec == val2->tv_sec */
407   if (val1->tv_usec <= val2->tv_usec)
408     return TRUE;
409
410   return FALSE;
411 }
412
413 static void
414 ik_pair_events (ik_event_internal_t *event1, 
415                 ik_event_internal_t *event2)
416 {
417   g_assert (event1 && event2);
418   /* We should only be pairing events that have the same cookie */
419   g_assert (event1->event->cookie == event2->event->cookie);
420   /* We shouldn't pair an event that already is paired */
421   g_assert (event1->pair == NULL && event2->pair == NULL);
422
423   /* Pair the internal structures and the ik_event_t structures */
424   event1->pair = event2;
425   event1->event->pair = event2->event;
426   event2->event->is_second_in_pair = TRUE;
427
428   if (g_timeval_lt (&event1->hold_until, &event2->hold_until))
429     event1->hold_until = event2->hold_until;
430
431   event2->hold_until = event1->hold_until;
432 }
433
434 static void
435 ik_event_add_microseconds (ik_event_internal_t *event, 
436                            glong                ms)
437 {
438   g_assert (event);
439   g_time_val_add (&event->hold_until, ms);
440 }
441
442 static gboolean
443 ik_event_ready (ik_event_internal_t *event)
444 {
445   GTimeVal tv;
446   g_assert (event);
447   
448   g_get_current_time (&tv);
449   
450   /* An event is ready if,
451    *
452    * it has no cookie -- there is nothing to be gained by holding it
453    * or, it is already paired -- we don't need to hold it anymore
454    * or, we have held it long enough
455    */
456   return
457     event->event->cookie == 0 ||
458     event->pair != NULL ||
459     g_timeval_le (&event->hold_until, &tv);
460 }
461
462 static void
463 ik_pair_moves (gpointer data, 
464                gpointer user_data)
465 {
466   ik_event_internal_t *event = (ik_event_internal_t *)data;
467   
468   if (event->seen == TRUE || event->sent == TRUE)
469     return;
470   
471   if (event->event->cookie != 0)
472     {
473       /* When we get a MOVED_FROM event we delay sending the event by
474        * MOVE_HOLD_UNTIL_TIME microseconds. We need to do this because a
475        * MOVED_TO pair _might_ be coming in the near future */
476       if (event->event->mask & IN_MOVED_FROM)
477         {
478           g_hash_table_insert (cookie_hash, GINT_TO_POINTER (event->event->cookie), event);
479           /* because we don't deliver move events there is no point in waiting for the match right now. */
480           ik_event_add_microseconds (event, MOVE_HOLD_UNTIL_TIME);
481         }
482       else if (event->event->mask & IN_MOVED_TO)
483         {
484           /* We need to check if we are waiting for this MOVED_TO events cookie to pair it with
485            * a MOVED_FROM */
486           ik_event_internal_t *match = NULL;
487           match = g_hash_table_lookup (cookie_hash, GINT_TO_POINTER (event->event->cookie));
488           if (match)
489             {
490               g_hash_table_remove (cookie_hash, GINT_TO_POINTER (event->event->cookie));
491               ik_pair_events (match, event);
492             }
493         }
494     }
495   event->seen = TRUE;
496 }
497
498 static void
499 ik_process_events (void)
500 {
501   g_queue_foreach (events_to_process, ik_pair_moves, NULL);
502
503   while (!g_queue_is_empty (events_to_process))
504     {
505       ik_event_internal_t *event = g_queue_peek_head (events_to_process);
506       
507       /* This must have been sent as part of a MOVED_TO/MOVED_FROM */
508       if (event->sent)
509         {
510           /* Pop event */
511           g_queue_pop_head (events_to_process);
512           /* Free the internal event structure */
513           g_free (event);
514           continue;
515         }
516       
517       /* The event isn't ready yet */
518       if (!ik_event_ready (event))
519         break;
520       
521       /* Pop it */
522       event = g_queue_pop_head (events_to_process);
523       
524       /* Check if this is a MOVED_FROM that is also sitting in the cookie_hash */
525       if (event->event->cookie && event->pair == NULL &&
526           g_hash_table_lookup (cookie_hash, GINT_TO_POINTER (event->event->cookie)))
527         g_hash_table_remove (cookie_hash, GINT_TO_POINTER (event->event->cookie));
528       
529       if (event->pair)
530         {
531           /* We send out paired MOVED_FROM/MOVED_TO events in the same event buffer */
532           /* g_assert (event->event->mask == IN_MOVED_FROM && event->pair->event->mask == IN_MOVED_TO); */
533           /* Copy the paired data */
534           event->pair->sent = TRUE;
535           event->sent = TRUE;
536           ik_move_matches++;
537         }
538       else if (event->event->cookie)
539         {
540           /* If we couldn't pair a MOVED_FROM and MOVED_TO together, we change
541            * the event masks */
542           /* Changeing MOVED_FROM to DELETE and MOVED_TO to create lets us make
543            * the gaurantee that you will never see a non-matched MOVE event */
544           event->event->original_mask = event->event->mask;
545
546           if (event->event->mask & IN_MOVED_FROM)
547             {
548               event->event->mask = IN_DELETE|(event->event->mask & IN_ISDIR);
549               ik_move_misses++; /* not super accurate, if we aren't watching the destination it still counts as a miss */
550             }
551           if (event->event->mask & IN_MOVED_TO)
552             event->event->mask = IN_CREATE|(event->event->mask & IN_ISDIR);
553         }
554       
555       /* Push the ik_event_t onto the event queue */
556       g_queue_push_tail (event_queue, event->event);
557       /* Free the internal event structure */
558       g_free (event);
559     }
560 }
561
562 static gboolean
563 ik_process_eq_callback (gpointer user_data)
564 {
565   gboolean res;
566   
567   /* Try and move as many events to the event queue */
568   G_LOCK (inotify_lock);
569   ik_process_events ();
570   
571   while (!g_queue_is_empty (event_queue))
572     {
573       ik_event_t *event = g_queue_pop_head (event_queue);
574       
575       user_cb (event);
576     }
577
578   res = TRUE;
579   
580   if (g_queue_get_length (events_to_process) == 0)
581     {
582       process_eq_running = FALSE;
583       res = FALSE;
584     }
585   
586   G_UNLOCK (inotify_lock);
587   
588   return res;
589 }