~ubuntu-branches/ubuntu/wily/qca2/wily-proposed

« back to all changes in this revision

Viewing changes to qca/src/support/synchronizer.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jan Niehusmann
  • Date: 2007-10-27 18:51:54 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20071027185154-4ir9ys3h2q9fofrw
Tags: 2.0.0-2
Upload to unstable

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Copyright (C) 2005  Justin Karneges <justin@affinix.com>
3
 
 *
4
 
 * This library is free software; you can redistribute it and/or
5
 
 * modify it under the terms of the GNU Lesser General Public
6
 
 * License as published by the Free Software Foundation; either
7
 
 * version 2.1 of the License, or (at your option) any later version.
8
 
 *
9
 
 * This 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
 
 * Lesser General Public License for more details.
13
 
 *
14
 
 * You should have received a copy of the GNU Lesser General Public
15
 
 * License along with this library; if not, write to the Free Software
16
 
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
17
 
 *
18
 
 */
19
 
 
20
 
#include "qca_support.h"
21
 
 
22
 
#include <QAbstractEventDispatcher>
23
 
#include <QCoreApplication>
24
 
#include <QEvent>
25
 
#include <QMutex>
26
 
#include <QPair>
27
 
#include <QTime>
28
 
#include <QWaitCondition>
29
 
 
30
 
//#define TIMERFIXER_DEBUG
31
 
 
32
 
namespace QCA {
33
 
 
34
 
//----------------------------------------------------------------------------
35
 
// TimerFixer
36
 
//----------------------------------------------------------------------------
37
 
class TimerFixer : public QObject
38
 
{
39
 
        Q_OBJECT
40
 
public:
41
 
        struct TimerInfo
42
 
        {
43
 
                int id;
44
 
                int interval;
45
 
                QTime time;
46
 
                bool fixInterval;
47
 
 
48
 
                TimerInfo() : fixInterval(false) {}
49
 
        };
50
 
 
51
 
        TimerFixer *fixerParent;
52
 
        QList<TimerFixer*> fixerChildren;
53
 
 
54
 
        QObject *target;
55
 
        QAbstractEventDispatcher *ed;
56
 
        QList<TimerInfo> timers;
57
 
 
58
 
        static bool haveFixer(QObject *obj)
59
 
        {
60
 
                return (qFindChild<TimerFixer *>(obj) ? true: false);
61
 
        }
62
 
 
63
 
        TimerFixer(QObject *_target, TimerFixer *_fp = 0) : QObject(_target)
64
 
        {
65
 
                ed = 0;
66
 
 
67
 
                target = _target;
68
 
                fixerParent = _fp;
69
 
                if(fixerParent)
70
 
                        fixerParent->fixerChildren.append(this);
71
 
 
72
 
#ifdef TIMERFIXER_DEBUG
73
 
                printf("TimerFixer[%p] pairing with %p (%s)\n", this, target, target->metaObject()->className());
74
 
#endif
75
 
                edlink();
76
 
                target->installEventFilter(this);
77
 
 
78
 
                QObjectList list = target->children();
79
 
                for(int n = 0; n < list.count(); ++n)
80
 
                        hook(list[n]);
81
 
        }
82
 
 
83
 
        ~TimerFixer()
84
 
        {
85
 
                if(fixerParent)
86
 
                        fixerParent->fixerChildren.removeAll(this);
87
 
 
88
 
                QList<TimerFixer*> list = fixerChildren;
89
 
                for(int n = 0; n < list.count(); ++n)
90
 
                        delete list[n];
91
 
                list.clear();
92
 
 
93
 
                updateTimerList(); // do this just to trip debug output
94
 
 
95
 
                target->removeEventFilter(this);
96
 
                edunlink();
97
 
#ifdef TIMERFIXER_DEBUG
98
 
                printf("TimerFixer[%p] unpaired with %p (%s)\n", this, target, target->metaObject()->className());
99
 
#endif
100
 
        }
101
 
 
102
 
        virtual bool event(QEvent *e)
103
 
        {
104
 
                switch(e->type())
105
 
                {
106
 
                        case QEvent::ThreadChange: // this happens second
107
 
                                //printf("TimerFixer[%p] self changing threads\n", this);
108
 
                                edunlink();
109
 
                                QMetaObject::invokeMethod(this, "fixTimers", Qt::QueuedConnection);
110
 
                                break;
111
 
                        default:
112
 
                                break;
113
 
                }
114
 
 
115
 
                return QObject::event(e);
116
 
        }
117
 
 
118
 
        virtual bool eventFilter(QObject *, QEvent *e)
119
 
        {
120
 
                switch(e->type())
121
 
                {
122
 
                        case QEvent::ChildAdded:
123
 
                                hook(((QChildEvent *)e)->child());
124
 
                                break;
125
 
                        case QEvent::ChildRemoved:
126
 
                                unhook(((QChildEvent *)e)->child());
127
 
                                break;
128
 
                        case QEvent::Timer:
129
 
                                handleTimerEvent(((QTimerEvent *)e)->timerId());
130
 
                                break;
131
 
                        case QEvent::ThreadChange: // this happens first
132
 
#ifdef TIMERFIXER_DEBUG
133
 
                                printf("TimerFixer[%p] target changing threads\n", this);
134
 
#endif
135
 
                                break;
136
 
                        default:
137
 
                                break;
138
 
                }
139
 
 
140
 
                return false;
141
 
        }
142
 
 
143
 
private slots:
144
 
        void edlink()
145
 
        {
146
 
                ed = QAbstractEventDispatcher::instance();
147
 
                //printf("TimerFixer[%p] linking to dispatcher %p\n", this, ed);
148
 
                connect(ed, SIGNAL(aboutToBlock()), SLOT(ed_aboutToBlock()));
149
 
        }
150
 
 
151
 
        void edunlink()
152
 
        {
153
 
                //printf("TimerFixer[%p] unlinking from dispatcher %p\n", this, ed);
154
 
                if(ed)
155
 
                {
156
 
                        disconnect(ed, SIGNAL(aboutToBlock()), this, SLOT(ed_aboutToBlock()));
157
 
                        ed = 0;
158
 
                }
159
 
        }
160
 
 
161
 
        void ed_aboutToBlock()
162
 
        {
163
 
                //printf("TimerFixer[%p] aboutToBlock\n", this);
164
 
                updateTimerList();
165
 
        }
166
 
 
167
 
        void fixTimers()
168
 
        {
169
 
                edlink();
170
 
                updateTimerList();
171
 
 
172
 
                for(int n = 0; n < timers.count(); ++n)
173
 
                {
174
 
                        TimerInfo &info = timers[n];
175
 
 
176
 
                        QThread *objectThread = target->thread();
177
 
                        QAbstractEventDispatcher *ed = QAbstractEventDispatcher::instance(objectThread);
178
 
 
179
 
                        int timeLeft = qMax(info.interval - info.time.elapsed(), 0);
180
 
                        info.fixInterval = true;
181
 
                        ed->unregisterTimer(info.id);
182
 
                        ed->registerTimer(info.id, timeLeft, target);
183
 
 
184
 
#ifdef TIMERFIXER_DEBUG
185
 
                        printf("TimerFixer[%p] adjusting [%d] to %d\n", this, info.id, timeLeft);
186
 
#endif
187
 
                }
188
 
        }
189
 
 
190
 
private:
191
 
        void hook(QObject *obj)
192
 
        {
193
 
                // don't watch a fixer or any object that already has one
194
 
                if(obj == this || qobject_cast<TimerFixer *>(obj) || haveFixer(obj))
195
 
                        return;
196
 
 
197
 
                new TimerFixer(obj, this);
198
 
        }
199
 
 
200
 
        void unhook(QObject *obj)
201
 
        {
202
 
                TimerFixer *t = 0;
203
 
                for(int n = 0; n < fixerChildren.count(); ++n)
204
 
                {
205
 
                        if(fixerChildren[n]->target == obj)
206
 
                                t = fixerChildren[n];
207
 
                }
208
 
                delete t;
209
 
        }
210
 
 
211
 
        void handleTimerEvent(int id)
212
 
        {
213
 
                bool found = false;
214
 
                int n;
215
 
                for(n = 0; n < timers.count(); ++n)
216
 
                {
217
 
                        if(timers[n].id == id)
218
 
                        {
219
 
                                found = true;
220
 
                                break;
221
 
                        }
222
 
                }
223
 
                if(!found)
224
 
                {
225
 
                        //printf("*** unrecognized timer [%d] activated ***\n", id);
226
 
                        return;
227
 
                }
228
 
 
229
 
                TimerInfo &info = timers[n];
230
 
#ifdef TIMERFIXER_DEBUG
231
 
                printf("TimerFixer[%p] timer [%d] activated!\n", this, info.id);
232
 
#endif
233
 
 
234
 
                if(info.fixInterval)
235
 
                {
236
 
#ifdef TIMERFIXER_DEBUG
237
 
                        printf("restoring correct interval (%d)\n", info.interval);
238
 
#endif
239
 
                        info.fixInterval = false;
240
 
                        ed->unregisterTimer(info.id);
241
 
                        ed->registerTimer(info.id, info.interval, target);
242
 
                }
243
 
 
244
 
                info.time.start();
245
 
        }
246
 
 
247
 
        void updateTimerList()
248
 
        {
249
 
                QList<QAbstractEventDispatcher::TimerInfo> edtimers;
250
 
                if(ed)
251
 
                        edtimers = ed->registeredTimers(target);
252
 
 
253
 
                // removed?
254
 
                for(int n = 0; n < timers.count(); ++n)
255
 
                {
256
 
                        bool found = false;
257
 
                        int id = timers[n].id;
258
 
                        for(int i = 0; i < edtimers.count(); ++i)
259
 
                        {
260
 
                                if(edtimers[i].first == id)
261
 
                                {
262
 
                                        found = true;
263
 
                                        break;
264
 
                                }
265
 
                        }
266
 
 
267
 
                        if(!found)
268
 
                        {
269
 
                                timers.removeAt(n);
270
 
                                --n;
271
 
#ifdef TIMERFIXER_DEBUG
272
 
                                printf("TimerFixer[%p] timer [%d] removed\n", this, id);
273
 
#endif
274
 
                        }
275
 
                }
276
 
 
277
 
                // added?
278
 
                for(int n = 0; n < edtimers.count(); ++n)
279
 
                {
280
 
                        int id = edtimers[n].first;
281
 
                        bool found = false;
282
 
                        for(int i = 0; i < timers.count(); ++i)
283
 
                        {
284
 
                                if(timers[i].id == id)
285
 
                                {
286
 
                                        found = true;
287
 
                                        break;
288
 
                                }
289
 
                        }
290
 
 
291
 
                        if(!found)
292
 
                        {
293
 
                                TimerInfo info;
294
 
                                info.id = id;
295
 
                                info.interval = edtimers[n].second;
296
 
                                info.time.start();
297
 
                                timers += info;
298
 
#ifdef TIMERFIXER_DEBUG
299
 
                                printf("TimerFixer[%p] timer [%d] added (interval=%d)\n", this, info.id, info.interval);
300
 
#endif
301
 
                        }
302
 
                }
303
 
        }
304
 
};
305
 
 
306
 
//----------------------------------------------------------------------------
307
 
// Synchronizer
308
 
//----------------------------------------------------------------------------
309
 
class Synchronizer::Private : public QThread
310
 
{
311
 
        Q_OBJECT
312
 
public:
313
 
        Synchronizer *q;
314
 
 
315
 
        bool active;
316
 
        bool do_quit;
317
 
        bool cond_met;
318
 
 
319
 
        QObject *obj;
320
 
        QEventLoop *loop;
321
 
        TimerFixer *fixer;
322
 
        QMutex m;
323
 
        QWaitCondition w;
324
 
        QThread *orig_thread;
325
 
 
326
 
        Private(QObject *_obj, Synchronizer *_q) : QThread(_q), q(_q)
327
 
        {
328
 
                active = false;
329
 
                obj = _obj;
330
 
                loop = 0;
331
 
                fixer = new TimerFixer(obj);
332
 
        }
333
 
 
334
 
        ~Private()
335
 
        {
336
 
                stop();
337
 
                delete fixer;
338
 
        }
339
 
 
340
 
        void start()
341
 
        {
342
 
                if(active)
343
 
                        return;
344
 
 
345
 
                m.lock();
346
 
                active = true;
347
 
                do_quit = false;
348
 
                QThread::start();
349
 
                w.wait(&m);
350
 
                m.unlock();
351
 
        }
352
 
 
353
 
        void stop()
354
 
        {
355
 
                if(!active)
356
 
                        return;
357
 
 
358
 
                m.lock();
359
 
                do_quit = true;
360
 
                w.wakeOne();
361
 
                m.unlock();
362
 
                wait();
363
 
                active = false;
364
 
        }
365
 
 
366
 
        bool waitForCondition(int msecs)
367
 
        {
368
 
                unsigned long time = ULONG_MAX;
369
 
                if(msecs != -1)
370
 
                        time = msecs;
371
 
 
372
 
                // move object to the worker thread
373
 
                cond_met = false;
374
 
                orig_thread = QThread::currentThread();
375
 
                q->setParent(0); // don't follow the object
376
 
                QObject *orig_parent = obj->parent();
377
 
                obj->setParent(0); // unparent the target or the move will fail
378
 
                obj->moveToThread(this);
379
 
 
380
 
                // tell the worker thread to start, wait for completion
381
 
                m.lock();
382
 
                w.wakeOne();
383
 
                if(!w.wait(&m, time))
384
 
                {
385
 
                        if(loop)
386
 
                        {
387
 
                                // if we timed out, tell the worker to quit
388
 
                                QMetaObject::invokeMethod(loop, "quit");
389
 
                                w.wait(&m);
390
 
                        }
391
 
                }
392
 
 
393
 
                // at this point the worker is done.  cleanup and return
394
 
                m.unlock();
395
 
 
396
 
                // restore parents
397
 
                obj->setParent(orig_parent);
398
 
                q->setParent(obj);
399
 
 
400
 
                return cond_met;
401
 
        }
402
 
 
403
 
        void conditionMet()
404
 
        {
405
 
                if(!loop)
406
 
                        return;
407
 
                loop->quit();
408
 
                cond_met = true;
409
 
        }
410
 
 
411
 
protected:
412
 
        virtual void run()
413
 
        {
414
 
                m.lock();
415
 
                QEventLoop eventLoop;
416
 
 
417
 
                while(1)
418
 
                {
419
 
                        // thread now sleeps, waiting for work
420
 
                        w.wakeOne();
421
 
                        w.wait(&m);
422
 
                        if(do_quit)
423
 
                        {
424
 
                                m.unlock();
425
 
                                break;
426
 
                        }
427
 
 
428
 
                        loop = &eventLoop;
429
 
                        m.unlock();
430
 
 
431
 
                        // run the event loop
432
 
                        eventLoop.exec();
433
 
 
434
 
                        // eventloop done, flush pending events
435
 
                        QCoreApplication::instance()->sendPostedEvents();
436
 
                        QCoreApplication::instance()->sendPostedEvents(0, QEvent::DeferredDelete);
437
 
 
438
 
                        // and move the object back
439
 
                        obj->moveToThread(orig_thread);
440
 
 
441
 
                        m.lock();
442
 
                        loop = 0;
443
 
                        w.wakeOne();
444
 
                }
445
 
        }
446
 
};
447
 
 
448
 
Synchronizer::Synchronizer(QObject *parent)
449
 
:QObject(parent)
450
 
{
451
 
        d = new Private(parent, this);
452
 
}
453
 
 
454
 
Synchronizer::~Synchronizer()
455
 
{
456
 
        delete d;
457
 
}
458
 
 
459
 
bool Synchronizer::waitForCondition(int msecs)
460
 
{
461
 
        d->start();
462
 
        return d->waitForCondition(msecs);
463
 
}
464
 
 
465
 
void Synchronizer::conditionMet()
466
 
{
467
 
        d->conditionMet();
468
 
}
469
 
 
470
 
}
471
 
 
472
 
#include "synchronizer.moc"