Tizen 2.1 base
[platform/upstream/glib2.0.git] / gio / fen / fen-data.c
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim:set expandtab ts=4 shiftwidth=4: */
3 /* 
4  * Copyright (C) 2008 Sun Microsystems, Inc. All rights reserved.
5  * Use is subject to license terms.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General
18  * Public License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20  * Boston, MA 02111-1307, USA.
21  *
22  * Authors: Lin Ma <lin.ma@sun.com>
23  */
24
25 #include "config.h"
26 #include <port.h>
27 #include <sys/types.h>
28 #include <sys/time.h>
29 #include <sys/stat.h>
30 #include <errno.h>
31 #include <glib.h>
32 #include "fen-data.h"
33 #include "fen-kernel.h"
34 #include "fen-missing.h"
35 #include "fen-dump.h"
36
37 #define PROCESS_EVENTQ_TIME     10      /* in milliseconds */
38 #define PAIR_EVENTS_TIMEVAL     00000   /* in microseconds */
39 #define PAIR_EVENTS_INC_TIMEVAL 0000    /* in microseconds */
40 #define SCAN_CHANGINGS_TIME     50      /* in milliseconds */
41 #define SCAN_CHANGINGS_MAX_TIME (4*100) /* in milliseconds */
42 #define SCAN_CHANGINGS_MIN_TIME (4*100) /* in milliseconds */
43 #define INIT_CHANGES_NUM        2
44 #define BASE_NUM        2
45
46 #ifdef GIO_COMPILATION
47 #define FD_W if (fd_debug_enabled) g_warning
48 static gboolean fd_debug_enabled = FALSE;
49 #else
50 #include "gam_error.h"
51 #define FD_W(...) GAM_DEBUG(DEBUG_INFO, __VA_ARGS__)
52 #endif
53
54 G_LOCK_EXTERN (fen_lock);
55 static GList *deleting_data = NULL;
56 static guint deleting_data_id = 0;
57
58 static void (*emit_once_cb) (fdata *f, int events, gpointer sub);
59 static void (*emit_cb) (fdata *f, int events);
60 static int (*_event_converter) (int event);
61
62 static gboolean fdata_delete (fdata* f);
63 static gint fdata_sub_find (gpointer a, gpointer b);
64 static void scan_children (node_t *f);
65 static void scan_known_children (node_t* f);
66
67 node_t*
68 _add_missing_cb (node_t* parent, gpointer user_data)
69 {
70     g_assert (parent);
71     FD_W ("%s p:0x%p %s\n", __func__, parent, (gchar*)user_data);
72     return _add_node (parent, (gchar*)user_data);
73 }
74
75 gboolean
76 _pre_del_cb (node_t* node, gpointer user_data)
77 {
78     fdata* data;
79     
80     g_assert (node);
81     data = _node_get_data (node);
82     FD_W ("%s node:0x%p %s\n", __func__, node, NODE_NAME(node));
83     if (data != NULL) {
84         if (!FN_IS_PASSIVE(data)) {
85             return FALSE;
86         }
87         fdata_delete (data);
88     }
89     return TRUE;
90 }
91
92 static guint
93 _pow (guint x, guint y)
94 {
95     guint z = 1;
96     g_assert (x >= 0 && y >= 0);
97     for (; y > 0; y--) {
98         z *= x;
99     }
100     return z;
101 }
102
103 static guint
104 get_scalable_scan_time (fdata* data)
105 {
106     guint sleep_time;
107     /* Caculate from num = 0 */
108     sleep_time = _pow (BASE_NUM, data->changed_event_num) * SCAN_CHANGINGS_TIME;
109     if (sleep_time < SCAN_CHANGINGS_MIN_TIME) {
110         sleep_time = SCAN_CHANGINGS_MIN_TIME;
111     } else if (sleep_time > SCAN_CHANGINGS_MAX_TIME) {
112         sleep_time = SCAN_CHANGINGS_MAX_TIME;
113         data->change_update_id = INIT_CHANGES_NUM;
114     }
115     FD_W ("SCALABE SCAN num:time [ %4u : %4u ] %s\n", data->changed_event_num, sleep_time, FN_NAME(data));
116     return sleep_time;
117 }
118
119 static gboolean
120 g_timeval_lt (GTimeVal *val1, GTimeVal *val2)
121 {
122     if (val1->tv_sec < val2->tv_sec)
123         return TRUE;
124   
125     if (val1->tv_sec > val2->tv_sec)
126         return FALSE;
127   
128     /* val1->tv_sec == val2->tv_sec */
129     if (val1->tv_usec < val2->tv_usec)
130         return TRUE;
131   
132     return FALSE;
133 }
134
135 /*
136  * If all active children nodes are ported, then cancel monitor the parent node
137  *
138  * Unsafe, need lock.
139  */
140 static void
141 scan_known_children (node_t* f)
142 {
143         GDir *dir;
144         GError *err = NULL;
145     fdata* pdata;
146     
147     FD_W ("%s %s [0x%p]\n", __func__, NODE_NAME(f), f);
148     pdata = _node_get_data (f);
149     /*
150      * Currect fdata must is directly monitored. Be sure it is 1 level monitor.
151      */
152         dir = g_dir_open (NODE_NAME(f), 0, &err);
153         if (dir) {
154                 const char *basename;
155         
156                 while ((basename = g_dir_read_name (dir)))
157                 {
158             node_t* childf = NULL;
159             fdata* data;
160                         GList *idx;
161                         /*
162              * If the node is existed, and isn't ported, then emit created
163              * event. Ignore others.
164              */
165             childf = _children_find (f, basename);
166             if (childf &&
167               (data = _node_get_data (childf)) != NULL &&
168               !FN_IS_PASSIVE (data)) {
169                 if (!is_monitoring (data) &&
170                   _port_add (&data->fobj, &data->len, data)) {
171                     _fdata_emit_events (data, FN_EVENT_CREATED);
172                 }
173             }
174         }
175                 g_dir_close (dir);
176     } else {
177         FD_W (err->message);
178         g_error_free (err);
179     }
180 }
181
182 static void
183 scan_children (node_t *f)
184 {
185         GDir *dir;
186         GError *err = NULL;
187     fdata* pdata;
188     
189     FD_W ("%s %s [0x%p]\n", __func__, NODE_NAME(f), f);
190     pdata = _node_get_data (f);
191     /*
192      * Currect fdata must is directly monitored. Be sure it is 1 level monitor.
193      */
194         dir = g_dir_open (NODE_NAME(f), 0, &err);
195         if (dir) {
196                 const char *basename;
197         
198                 while ((basename = g_dir_read_name (dir)))
199                 {
200             node_t* childf = NULL;
201             fdata* data;
202                         GList *idx;
203
204             childf = _children_find (f, basename);
205             if (childf == NULL) {
206                 gchar *filename;
207
208                 filename = g_build_filename (NODE_NAME(f), basename, NULL);
209                 childf = _add_node (f, filename);
210                 g_assert (childf);
211                 data = _fdata_new (childf, FALSE);
212                 g_free (filename);
213             }
214             if ((data = _node_get_data (childf)) == NULL) {
215                 data = _fdata_new (childf, FALSE);
216             }
217             /* Be sure data isn't ported and add to port successfully */
218             /* Don't need delete it, it will be deleted by the parent */
219             if (is_monitoring (data)) {
220                 /* Ignored */
221             } else if (/* !_is_ported (data) && */
222                 _port_add (&data->fobj, &data->len, data)) {
223                 _fdata_emit_events (data, FN_EVENT_CREATED);
224             }
225         }
226                 g_dir_close (dir);
227     } else {
228         FD_W (err->message);
229         g_error_free (err);
230     }
231 }
232
233 static gboolean
234 scan_deleting_data (gpointer data)
235 {
236     fdata *f;
237     GList* i;
238     GList* deleted_list = NULL;
239     gboolean ret = TRUE;
240
241     if (G_TRYLOCK (fen_lock)) {
242         for (i = deleting_data; i; i = i->next) {
243             f = (fdata*)i->data;
244             if (fdata_delete (f)) {
245                 deleted_list = g_list_prepend (deleted_list, i);
246             }
247         }
248
249         for (i = deleted_list; i; i = i->next) {
250             deleting_data = g_list_remove_link (deleting_data,
251               (GList *)i->data);
252             g_list_free_1 ((GList *)i->data);
253         }
254         g_list_free (deleted_list);
255
256         if (deleting_data == NULL) {
257             deleting_data_id = 0;
258             ret = FALSE;
259         }
260         G_UNLOCK (fen_lock);
261     }
262     return ret;
263 }
264
265 gboolean
266 is_monitoring (fdata* data)
267 {
268     return _is_ported (data) || data->change_update_id > 0;
269 }
270
271 fdata*
272 _get_parent_data (fdata* data)
273 {
274     if (FN_NODE(data) && !IS_TOPNODE(FN_NODE(data))) {
275         return _node_get_data (FN_NODE(data)->parent);
276     }
277     return NULL;
278 }
279
280 node_t*
281 _get_parent_node (fdata* data)
282 {
283     if (FN_NODE(data)) {
284         return (FN_NODE(data)->parent);
285     }
286     return NULL;
287 }
288
289 fdata *
290 _fdata_new (node_t* node, gboolean is_mondir)
291 {
292         fdata *f = NULL;
293
294     g_assert (node);
295         if ((f = g_new0 (fdata, 1)) != NULL) {
296         FN_NODE(f) = node;
297                 FN_NAME(f) = g_strdup (NODE_NAME(node));
298         f->is_dir = is_mondir;
299         f->eventq = g_queue_new ();
300         FD_W ("[ %s ] 0x%p %s\n", __func__, f, FN_NAME(f));
301         _node_set_data (node, f);
302         }
303         return f;
304 }
305
306 static gboolean
307 fdata_delete (fdata *f)
308 {
309     fnode_event_t *ev;
310
311     FD_W ("[ TRY %s ] 0x%p id[%4d:%4d] %s\n", __func__, f, f->eventq_id, f->change_update_id, FN_NAME(f));
312     g_assert (FN_IS_PASSIVE(f));
313
314     _port_remove (f);
315     /* _missing_remove (f); */
316
317     if (f->node != NULL) {
318         _node_set_data (f->node, NULL);
319         f->node = NULL;
320     }
321
322     if (f->change_update_id > 0 || f->eventq_id > 0) {
323         if (FN_IS_LIVING(f)) {
324             f->is_cancelled = TRUE;
325             deleting_data = g_list_prepend (deleting_data, f);
326             if (deleting_data_id == 0) {
327                 deleting_data_id = g_idle_add (scan_deleting_data, NULL);
328                 g_assert (deleting_data_id > 0);
329             }
330         }
331         return FALSE;
332     }
333     FD_W ("[ %s ] 0x%p %s\n", __func__, f, FN_NAME(f));
334
335     while ((ev = g_queue_pop_head (f->eventq)) != NULL) {
336         _fnode_event_delete (ev);
337     }
338
339     g_queue_free (f->eventq);
340     g_free (FN_NAME(f));
341     g_free (f);
342     return TRUE;
343 }
344
345 void
346 _fdata_reset (fdata* data)
347 {
348     fnode_event_t *ev;
349
350     g_assert (data);
351
352     while ((ev = g_queue_pop_head (data->eventq)) != NULL) {
353         _fnode_event_delete (ev);
354     }
355 }
356
357 static gint
358 fdata_sub_find (gpointer a, gpointer b)
359 {
360     if (a != b) {
361         return 1;
362     } else {
363         return 0;
364     }
365 }
366
367 void
368 _fdata_sub_add (fdata *f, gpointer sub)
369 {
370     FD_W ("[%s] [data: 0x%p ] [s: 0x%p ] %s\n", __func__, f, sub, FN_NAME(f));
371     g_assert (g_list_find_custom (f->subs, sub, (GCompareFunc)fdata_sub_find) == NULL);
372     f->subs = g_list_prepend (f->subs, sub);
373 }
374
375 void
376 _fdata_sub_remove (fdata *f, gpointer sub)
377 {
378     GList *l;
379     FD_W ("[%s] [data: 0x%p ] [s: 0x%p ] %s\n", __func__, f, sub, FN_NAME(f));
380     g_assert (g_list_find_custom (f->subs, sub, (GCompareFunc)fdata_sub_find) != NULL);
381     l = g_list_find_custom (f->subs, sub, (GCompareFunc)fdata_sub_find);
382     g_assert (l);
383     g_assert (sub == l->data);
384     f->subs = g_list_delete_link (f->subs, l);
385 }
386
387 /*
388  * Adjust self on failing to Port
389  */
390 void
391 _fdata_adjust_deleted (fdata* f)
392 {
393     node_t* parent;
394     fdata* pdata;
395     node_op_t op = {NULL, NULL, _pre_del_cb, NULL};
396
397     /*
398      * It's a top node. We move it to missing list.
399      */
400     parent = _get_parent_node (f);
401     pdata = _get_parent_data (f);
402     if (!FN_IS_PASSIVE(f) ||
403       _children_num (FN_NODE(f)) > 0 ||
404       (pdata && !FN_IS_PASSIVE(pdata))) {
405         if (parent) {
406             if (pdata == NULL) {
407                 pdata = _fdata_new (parent, FALSE);
408             }
409             g_assert (pdata);
410             if (!_port_add (&pdata->fobj, &pdata->len, pdata)) {
411                 _fdata_adjust_deleted (pdata);
412             }
413         } else {
414             /* f is root */
415             g_assert (IS_TOPNODE(FN_NODE(f)));
416             _missing_add (f);
417         }
418     } else {
419 #ifdef GIO_COMPILATION
420         _pending_remove_node (FN_NODE(f), &op);
421 #else
422         _remove_node (FN_NODE(f), &op);
423 #endif
424     }
425 }
426
427 static gboolean
428 fdata_adjust_changed (fdata *f)
429 {
430     fnode_event_t *ev;
431     struct stat buf;
432     node_t* parent;
433     fdata* pdata;
434
435     G_LOCK (fen_lock);
436     parent = _get_parent_node (f);
437     pdata = _get_parent_data (f);
438
439     if (!FN_IS_LIVING(f) ||
440       (_children_num (FN_NODE(f)) == 0 &&
441         FN_IS_PASSIVE(f) &&
442         pdata && FN_IS_PASSIVE(pdata))) {
443         f->change_update_id = 0;
444         G_UNLOCK (fen_lock);
445         return FALSE;
446     }
447
448     FD_W ("[ %s ] %s\n", __func__, FN_NAME(f));
449     if (FN_STAT (FN_NAME(f), &buf) != 0) {
450         FD_W ("LSTAT [%-20s] %s\n", FN_NAME(f), g_strerror (errno));
451         goto L_delete;
452     }
453     f->is_dir = S_ISDIR (buf.st_mode) ? TRUE : FALSE;
454     if (f->len != buf.st_size) {
455         /* FD_W ("LEN [%lld:%lld] %s\n", f->len, buf.st_size, FN_NAME(f)); */
456         f->len = buf.st_size;
457         ev = _fnode_event_new (FILE_MODIFIED, TRUE, f);
458         if (ev != NULL) {
459             ev->is_pending = TRUE;
460             _fdata_add_event (f, ev);
461         }
462         /* Fdata is still changing, so scalable scan */
463         f->change_update_id = g_timeout_add (get_scalable_scan_time (f),
464           (GSourceFunc)fdata_adjust_changed,
465           (gpointer)f);
466         G_UNLOCK (fen_lock);
467         return FALSE;
468     } else {
469         f->changed_event_num = 0;
470         f->fobj.fo_atime = buf.st_atim;
471         f->fobj.fo_mtime = buf.st_mtim;
472         f->fobj.fo_ctime = buf.st_ctim;
473         if (FN_IS_DIR(f)) {
474             if (FN_IS_MONDIR(f)) {
475                 scan_children (FN_NODE(f));
476             } else {
477                 scan_known_children (FN_NODE(f));
478                 if ((_children_num (FN_NODE(f)) == 0 &&
479                       FN_IS_PASSIVE(f) &&
480                       pdata && FN_IS_PASSIVE(pdata))) {
481                     _port_remove (f);
482                     goto L_exit;
483                 }
484             }
485         }
486         if (!_port_add_simple (&f->fobj, f)) {
487         L_delete:
488             ev = _fnode_event_new (FILE_DELETE, FALSE, f);
489             if (ev != NULL) {
490                 _fdata_add_event (f, ev);
491             }
492         }
493     }
494 L_exit:
495     f->change_update_id = 0;
496     G_UNLOCK (fen_lock);
497     return FALSE;
498 }
499
500 void
501 _fdata_emit_events_once (fdata *f, int event, gpointer sub)
502 {
503     emit_once_cb (f, _event_converter (event), sub);
504 }
505
506 void
507 _fdata_emit_events (fdata *f, int event)
508 {
509     emit_cb (f, _event_converter (event));
510 }
511
512 static gboolean
513 process_events (gpointer udata)
514 {
515     node_op_t op = {NULL, NULL, _pre_del_cb, NULL};
516     fdata* f;
517     fnode_event_t* ev;
518     int e;
519
520     /* FD_W ("IN <======== %s\n", __func__); */
521
522     f = (fdata*)udata;
523     FD_W ("%s 0x%p id:%-4d %s\n", __func__, f, f->eventq_id, FN_NAME(f));
524     
525     G_LOCK (fen_lock);
526
527     if (!FN_IS_LIVING(f)) {
528         f->eventq_id = 0;
529         G_UNLOCK (fen_lock);
530         return FALSE;
531     }
532     
533     if ((ev = (fnode_event_t*)g_queue_pop_head (f->eventq)) != NULL) {
534         /* Send events to clients. */
535         e = ev->e;
536         if (!ev->is_pending) {
537 #ifdef GIO_COMPILATION
538             if (ev->has_twin) {
539                 _fdata_emit_events (f, FILE_ATTRIB);
540             }
541 #endif
542             _fdata_emit_events (f, ev->e);
543         }
544         
545         _fnode_event_delete (ev);
546         ev = NULL;
547
548         /* Adjust node state. */
549         /*
550          * Node the node has been created, so we can delete create event in
551          * optimizing. To reduce the statings, we add it to Port on discoving
552          * it then emit CREATED event. So we don't need to do anything here.
553          */
554         switch (e) {
555         case FILE_MODIFIED:
556         case MOUNTEDOVER:
557         case UNMOUNTED:
558             /* If the event is a changed event, then pending process it */
559             if (f->change_update_id == 0) {
560                 f->change_update_id = g_timeout_add (get_scalable_scan_time(f),
561                   (GSourceFunc)fdata_adjust_changed,
562                   (gpointer)f);
563                 g_assert (f->change_update_id > 0);
564             }
565             break;
566         case FILE_ATTRIB:
567             g_assert (f->change_update_id == 0);
568             if (!_port_add (&f->fobj, &f->len, f)) {
569                 ev = _fnode_event_new (FILE_DELETE, FALSE, f);
570                 if (ev != NULL) {
571                     _fdata_add_event (f, ev);
572                 }
573             }
574             break;
575         case FILE_DELETE: /* Ignored */
576             break;
577         default:
578             g_assert_not_reached ();
579             break;
580         }
581         /* Process one event a time */
582         G_UNLOCK (fen_lock); 
583         return TRUE;
584     }
585     f->eventq_id = 0;
586     G_UNLOCK (fen_lock); 
587     /* FD_W ("OUT ========> %s\n", __func__); */
588     return FALSE;
589 }
590
591 void
592 _fdata_add_event (fdata *f, fnode_event_t *ev)
593 {
594     node_op_t op = {NULL, NULL, _pre_del_cb, NULL};
595     fnode_event_t *tail;
596
597     if (!FN_IS_LIVING(f)) {
598         _fnode_event_delete (ev);
599         return;
600     }
601     
602     FD_W ("%s %d\n", __func__, ev->e);
603     g_get_current_time (&ev->t);
604     /*
605      * If created/deleted events of child node happened, then we use parent
606      * event queue to handle.
607      * If child node emits deleted event, it seems no changes for the parent
608      * node, but the attr is changed. So we may try to cancel processing the
609      * coming changed events of the parent node.
610      */
611     tail = (fnode_event_t*)g_queue_peek_tail (f->eventq);
612     switch (ev->e) {
613     case FILE_RENAME_FROM:
614     case FILE_RENAME_TO:
615     case FILE_ACCESS:
616         _fnode_event_delete (ev);
617         g_assert_not_reached ();
618         return;
619     case FILE_DELETE:
620         /* clear changed event number */
621         f->changed_event_num = 0;
622         /*
623          * We will cancel all previous events.
624          */
625         if (tail) {
626             g_queue_pop_tail (f->eventq);
627             do {
628                 _fnode_event_delete (tail);
629             } while ((tail = (fnode_event_t*)g_queue_pop_tail (f->eventq)) != NULL);
630         }
631         /*
632          * Given a node "f" is deleted, process it ASAP.
633          */
634         _fdata_emit_events (f, ev->e);
635         _fnode_event_delete (ev);
636         _fdata_adjust_deleted (f);
637         return;
638     case FILE_MODIFIED:
639     case UNMOUNTED:
640     case MOUNTEDOVER:
641         /* clear changed event number */
642         f->changed_event_num ++;
643     case FILE_ATTRIB:
644     default:
645         /*
646          * If in the time range, we will try optimizing
647          * (changed+) to (changed)
648          * (attrchanged changed) to ([changed, attrchanged])
649          * (event attrchanged) to ([event, attrchanged])
650          */
651         if (tail) {
652             do {
653                 if (tail->e == ev->e) {
654                     if (g_timeval_lt (&ev->t, &tail->t)) {
655                         g_queue_peek_tail (f->eventq);
656                         /* Add the increment */
657                         g_time_val_add (&ev->t, PAIR_EVENTS_INC_TIMEVAL);
658                         /* skip the previous event */
659                         FD_W ("SKIPPED -- %s\n", _event_string (tail->e));
660                         _fnode_event_delete (tail);
661                     } else {
662                         break;
663                     }
664                 } else if (ev->e == FILE_MODIFIED && tail->e == FILE_ATTRIB) {
665                     ev->has_twin = TRUE;
666                     _fnode_event_delete (tail);
667                 } else if (ev->e == FILE_ATTRIB && f->change_update_id > 0) {
668                     tail->has_twin = TRUE;
669                     /* skip the current event */
670                     _fnode_event_delete (ev);
671                     return;
672                 } else {
673                     break;
674                 }
675             } while ((tail = (fnode_event_t*)g_queue_peek_tail (f->eventq)) != NULL);
676         }
677     }
678
679     /* must add the threshold time */
680     g_time_val_add (&ev->t, PAIR_EVENTS_TIMEVAL);
681     
682     g_queue_push_tail (f->eventq, ev);
683
684     /* starting process_events */
685     if (f->eventq_id == 0) {
686         f->eventq_id = g_timeout_add (PROCESS_EVENTQ_TIME,
687           process_events,
688           (gpointer)f);
689         g_assert (f->eventq_id > 0);
690     }
691     FD_W ("%s 0x%p id:%-4d %s\n", __func__, f, f->eventq_id, FN_NAME(f));
692 }
693
694 gboolean
695 _fdata_class_init (void (*user_emit_cb) (fdata*, int),
696   void (*user_emit_once_cb) (fdata*, int,  gpointer),
697   int (*user_event_converter) (int event))
698 {
699     FD_W ("%s\n", __func__);
700     if (user_emit_cb == NULL) {
701         return FALSE;
702     }
703     if (user_emit_once_cb == NULL) {
704         return FALSE;
705     }
706     if (user_event_converter == NULL) {
707         return FALSE;
708     }
709     emit_cb = user_emit_cb;
710     emit_once_cb = user_emit_once_cb;
711     _event_converter = user_event_converter;
712     
713     if (!_port_class_init (_fdata_add_event)) {
714         FD_W ("_port_class_init failed.");
715         return FALSE;
716     }
717     return TRUE;
718 }