~ubuntu-branches/debian/squeeze/glib2.0/squeeze

« back to all changes in this revision

Viewing changes to gio/fen/fen-data.c

  • Committer: Bazaar Package Importer
  • Author(s): Gustavo Noronha Silva
  • Date: 2009-02-15 13:00:43 UTC
  • mfrom: (1.3.1 upstream) (69.1.10 intrepid)
  • Revision ID: james.westby@ubuntu.com-20090215130043-q47fbt3owmt42m2f
Tags: 2.18.4-2
* Release to unstable
* debian/rules:
- bump SHVER, since we are already forcing a 2.18.0 dependecy on the
  symbols introduced in the development versions
* debian/control.in:
- added Homepage and Vcs-* control fields

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
/**
 
592
 * fdata_add_event:
 
593
 *
 
594
 */
 
595
void
 
596
fdata_add_event (fdata *f, fnode_event_t *ev)
 
597
{
 
598
    node_op_t op = {NULL, NULL, pre_del_cb, NULL};
 
599
    fnode_event_t *tail;
 
600
 
 
601
    if (!FN_IS_LIVING(f)) {
 
602
        fnode_event_delete (ev);
 
603
        return;
 
604
    }
 
605
    
 
606
    FD_W ("%s %d\n", __func__, ev->e);
 
607
    g_get_current_time (&ev->t);
 
608
    /*
 
609
     * If created/deleted events of child node happened, then we use parent
 
610
     * event queue to handle.
 
611
     * If child node emits deleted event, it seems no changes for the parent
 
612
     * node, but the attr is changed. So we may try to cancel processing the
 
613
     * coming changed events of the parent node.
 
614
     */
 
615
    tail = (fnode_event_t*)g_queue_peek_tail (f->eventq);
 
616
    switch (ev->e) {
 
617
    case FILE_RENAME_FROM:
 
618
    case FILE_RENAME_TO:
 
619
    case FILE_ACCESS:
 
620
        fnode_event_delete (ev);
 
621
        g_assert_not_reached ();
 
622
        return;
 
623
    case FILE_DELETE:
 
624
        /* clear changed event number */
 
625
        f->changed_event_num = 0;
 
626
        /*
 
627
         * We will cancel all previous events.
 
628
         */
 
629
        if (tail) {
 
630
            g_queue_pop_tail (f->eventq);
 
631
            do {
 
632
                fnode_event_delete (tail);
 
633
            } while ((tail = (fnode_event_t*)g_queue_pop_tail (f->eventq)) != NULL);
 
634
        }
 
635
        /*
 
636
         * Given a node "f" is deleted, process it ASAP.
 
637
         */
 
638
        fdata_emit_events (f, ev->e);
 
639
        fnode_event_delete (ev);
 
640
        fdata_adjust_deleted (f);
 
641
        return;
 
642
    case FILE_MODIFIED:
 
643
    case UNMOUNTED:
 
644
    case MOUNTEDOVER:
 
645
        /* clear changed event number */
 
646
        f->changed_event_num ++;
 
647
    case FILE_ATTRIB:
 
648
    default:
 
649
        /*
 
650
         * If in the time range, we will try optimizing
 
651
         * (changed+) to (changed)
 
652
         * (attrchanged changed) to ([changed, attrchanged])
 
653
         * (event attrchanged) to ([event, attrchanged])
 
654
         */
 
655
        if (tail) {
 
656
            do {
 
657
                if (tail->e == ev->e) {
 
658
                    if (g_timeval_lt (&ev->t, &tail->t)) {
 
659
                        g_queue_peek_tail (f->eventq);
 
660
                        /* Add the increment */
 
661
                        g_time_val_add (&ev->t, PAIR_EVENTS_INC_TIMEVAL);
 
662
                        /* skip the previous event */
 
663
                        FD_W ("SKIPPED -- %s\n", _event_string (tail->e));
 
664
                        fnode_event_delete (tail);
 
665
                    } else {
 
666
                        break;
 
667
                    }
 
668
                } else if (ev->e == FILE_MODIFIED && tail->e == FILE_ATTRIB) {
 
669
                    ev->has_twin = TRUE;
 
670
                    fnode_event_delete (tail);
 
671
                } else if (ev->e == FILE_ATTRIB && f->change_update_id > 0) {
 
672
                    tail->has_twin = TRUE;
 
673
                    /* skip the current event */
 
674
                    fnode_event_delete (ev);
 
675
                    return;
 
676
                } else {
 
677
                    break;
 
678
                }
 
679
            } while ((tail = (fnode_event_t*)g_queue_peek_tail (f->eventq)) != NULL);
 
680
        }
 
681
    }
 
682
 
 
683
    /* must add the threshold time */
 
684
    g_time_val_add (&ev->t, PAIR_EVENTS_TIMEVAL);
 
685
    
 
686
    g_queue_push_tail (f->eventq, ev);
 
687
 
 
688
    /* starting process_events */
 
689
    if (f->eventq_id == 0) {
 
690
        f->eventq_id = g_timeout_add (PROCESS_EVENTQ_TIME,
 
691
          process_events,
 
692
          (gpointer)f);
 
693
        g_assert (f->eventq_id > 0);
 
694
    }
 
695
    FD_W ("%s 0x%p id:%-4d %s\n", __func__, f, f->eventq_id, FN_NAME(f));
 
696
}
 
697
 
 
698
gboolean
 
699
fdata_class_init (void (*user_emit_cb) (fdata*, int),
 
700
  void (*user_emit_once_cb) (fdata*, int,  gpointer),
 
701
  int (*user_event_converter) (int event))
 
702
{
 
703
    FD_W ("%s\n", __func__);
 
704
    if (user_emit_cb == NULL) {
 
705
        return FALSE;
 
706
    }
 
707
    if (user_emit_once_cb == NULL) {
 
708
        return FALSE;
 
709
    }
 
710
    if (user_event_converter == NULL) {
 
711
        return FALSE;
 
712
    }
 
713
    emit_cb = user_emit_cb;
 
714
    emit_once_cb = user_emit_once_cb;
 
715
    _event_converter = user_event_converter;
 
716
    
 
717
    if (!port_class_init (fdata_add_event)) {
 
718
        FD_W ("port_class_init failed.");
 
719
        return FALSE;
 
720
    }
 
721
    return TRUE;
 
722
}