File indexing completed on 2024-12-01 13:02:42

0001 /*
0002  * Copyright (C) 2005  Justin Karneges <justin@affinix.com>
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Lesser General Public
0006  * License as published by the Free Software Foundation; either
0007  * version 2.1 of the License, or (at your option) any later version.
0008  *
0009  * This library is distributed in the hope that it will be useful,
0010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012  * Lesser General Public License for more details.
0013  *
0014  * You should have received a copy of the GNU Lesser General Public
0015  * License along with this library; if not, write to the Free Software
0016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
0017  * 02110-1301  USA
0018  *
0019  */
0020 
0021 #include "qca_safetimer.h"
0022 #include "qca_support.h"
0023 
0024 #include <QAbstractEventDispatcher>
0025 #include <QCoreApplication>
0026 #include <QElapsedTimer>
0027 #include <QEvent>
0028 #include <QMutex>
0029 #include <QPair>
0030 #include <QWaitCondition>
0031 
0032 // #define TIMERFIXER_DEBUG
0033 
0034 #ifdef TIMERFIXER_DEBUG
0035 #include <stdio.h>
0036 #endif
0037 
0038 namespace QCA {
0039 
0040 //----------------------------------------------------------------------------
0041 // TimerFixer
0042 //----------------------------------------------------------------------------
0043 class TimerFixer : public QObject
0044 {
0045     Q_OBJECT
0046 public:
0047     struct TimerInfo
0048     {
0049         int           id;
0050         int           interval;
0051         QElapsedTimer time;
0052         bool          fixInterval;
0053 
0054         TimerInfo()
0055             : fixInterval(false)
0056         {
0057         }
0058     };
0059 
0060     TimerFixer         *fixerParent;
0061     QList<TimerFixer *> fixerChildren;
0062 
0063     QObject                  *target;
0064     QAbstractEventDispatcher *ed;
0065     QList<TimerInfo>          timers;
0066 
0067     static bool haveFixer(QObject *obj)
0068     {
0069         return obj->findChild<TimerFixer *>() ? true : false;
0070     }
0071 
0072     TimerFixer(QObject *_target, TimerFixer *_fp = nullptr)
0073         : QObject(_target)
0074     {
0075         ed = nullptr;
0076 
0077         target      = _target;
0078         fixerParent = _fp;
0079         if (fixerParent)
0080             fixerParent->fixerChildren.append(this);
0081 
0082 #ifdef TIMERFIXER_DEBUG
0083         printf("TimerFixer[%p] pairing with %p (%s)\n", this, target, target->metaObject()->className());
0084 #endif
0085         edlink();
0086         target->installEventFilter(this);
0087 
0088         const QObjectList list = target->children();
0089         for (int n = 0; n < list.count(); ++n)
0090             hook(list[n]);
0091     }
0092 
0093     ~TimerFixer() override
0094     {
0095         if (fixerParent)
0096             fixerParent->fixerChildren.removeAll(this);
0097 
0098         QList<TimerFixer *> list = fixerChildren;
0099         for (int n = 0; n < list.count(); ++n)
0100             delete list[n];
0101         list.clear();
0102 
0103         updateTimerList(); // do this just to trip debug output
0104 
0105         target->removeEventFilter(this);
0106         edunlink();
0107 #ifdef TIMERFIXER_DEBUG
0108         printf("TimerFixer[%p] unpaired with %p (%s)\n", this, target, target->metaObject()->className());
0109 #endif
0110     }
0111 
0112     bool event(QEvent *e) override
0113     {
0114         switch (e->type()) {
0115         case QEvent::ThreadChange: // this happens second
0116             // printf("TimerFixer[%p] self changing threads\n", this);
0117             edunlink();
0118             QMetaObject::invokeMethod(this, "fixTimers", Qt::QueuedConnection);
0119             break;
0120         default:
0121             break;
0122         }
0123 
0124         return QObject::event(e);
0125     }
0126 
0127     bool eventFilter(QObject *, QEvent *e) override
0128     {
0129         switch (e->type()) {
0130         case QEvent::ChildAdded:
0131             hook(((QChildEvent *)e)->child());
0132             break;
0133         case QEvent::ChildRemoved:
0134             unhook(((QChildEvent *)e)->child());
0135             break;
0136         case QEvent::Timer:
0137             handleTimerEvent(((QTimerEvent *)e)->timerId());
0138             break;
0139         case QEvent::ThreadChange: // this happens first
0140 #ifdef TIMERFIXER_DEBUG
0141             printf("TimerFixer[%p] target changing threads\n", this);
0142 #endif
0143             break;
0144         default:
0145             break;
0146         }
0147 
0148         return false;
0149     }
0150 
0151 private Q_SLOTS:
0152     void edlink()
0153     {
0154         ed = QAbstractEventDispatcher::instance();
0155         // printf("TimerFixer[%p] linking to dispatcher %p\n", this, ed);
0156         connect(ed, &QAbstractEventDispatcher::aboutToBlock, this, &TimerFixer::ed_aboutToBlock);
0157     }
0158 
0159     void edunlink()
0160     {
0161         // printf("TimerFixer[%p] unlinking from dispatcher %p\n", this, ed);
0162         if (ed) {
0163             disconnect(ed, &QAbstractEventDispatcher::aboutToBlock, this, &TimerFixer::ed_aboutToBlock);
0164             ed = nullptr;
0165         }
0166     }
0167 
0168     void ed_aboutToBlock()
0169     {
0170         // printf("TimerFixer[%p] aboutToBlock\n", this);
0171         updateTimerList();
0172     }
0173 
0174     void fixTimers()
0175     {
0176         updateTimerList();
0177         edlink();
0178 
0179         for (int n = 0; n < timers.count(); ++n) {
0180             TimerInfo &info = timers[n];
0181 
0182             QThread                  *objectThread = target->thread();
0183             QAbstractEventDispatcher *ed           = QAbstractEventDispatcher::instance(objectThread);
0184 
0185             const int timeLeft = qMax(info.interval - static_cast<int>(info.time.elapsed()), 0);
0186             info.fixInterval   = true;
0187             ed->unregisterTimer(info.id);
0188             info.id = ed->registerTimer(timeLeft, Qt::CoarseTimer, target);
0189 
0190 #ifdef TIMERFIXER_DEBUG
0191             printf("TimerFixer[%p] adjusting [%d] to %d\n", this, info.id, timeLeft);
0192 #endif
0193         }
0194     }
0195 
0196 private:
0197     void hook(QObject *obj)
0198     {
0199         // don't watch a fixer or any object that already has one
0200         // SafeTimer has own method to fix timers, skip it too
0201         if (obj == this || qobject_cast<TimerFixer *>(obj) || haveFixer(obj) || qobject_cast<SafeTimer *>(obj))
0202             return;
0203 
0204         new TimerFixer(obj, this);
0205     }
0206 
0207     void unhook(QObject *obj)
0208     {
0209         TimerFixer *t = nullptr;
0210         for (int n = 0; n < fixerChildren.count(); ++n) {
0211             if (fixerChildren[n]->target == obj)
0212                 t = fixerChildren[n];
0213         }
0214         delete t;
0215     }
0216 
0217     void handleTimerEvent(int id)
0218     {
0219         bool found = false;
0220         int  n;
0221         for (n = 0; n < timers.count(); ++n) {
0222             if (timers[n].id == id) {
0223                 found = true;
0224                 break;
0225             }
0226         }
0227         if (!found) {
0228             // printf("*** unrecognized timer [%d] activated ***\n", id);
0229             return;
0230         }
0231 
0232         TimerInfo &info = timers[n];
0233 #ifdef TIMERFIXER_DEBUG
0234         printf("TimerFixer[%p] timer [%d] activated!\n", this, info.id);
0235 #endif
0236 
0237         if (info.fixInterval) {
0238 #ifdef TIMERFIXER_DEBUG
0239             printf("restoring correct interval (%d)\n", info.interval);
0240 #endif
0241             info.fixInterval = false;
0242             ed->unregisterTimer(info.id);
0243             info.id = ed->registerTimer(info.interval, Qt::CoarseTimer, target);
0244         }
0245 
0246         info.time.start();
0247     }
0248 
0249     void updateTimerList()
0250     {
0251         QList<QAbstractEventDispatcher::TimerInfo> edtimers;
0252         if (ed)
0253             edtimers = ed->registeredTimers(target);
0254 
0255         // removed?
0256         for (int n = 0; n < timers.count(); ++n) {
0257             bool found = false;
0258             int  id    = timers[n].id;
0259             for (int i = 0; i < edtimers.count(); ++i) {
0260                 if (edtimers[i].timerId == id) {
0261                     found = true;
0262                     break;
0263                 }
0264             }
0265 
0266             if (!found) {
0267                 timers.removeAt(n);
0268                 --n;
0269 #ifdef TIMERFIXER_DEBUG
0270                 printf("TimerFixer[%p] timer [%d] removed\n", this, id);
0271 #endif
0272             }
0273         }
0274 
0275         // added?
0276         for (int n = 0; n < edtimers.count(); ++n) {
0277             int  id    = edtimers[n].timerId;
0278             bool found = false;
0279             for (int i = 0; i < timers.count(); ++i) {
0280                 if (timers[i].id == id) {
0281                     found = true;
0282                     break;
0283                 }
0284             }
0285 
0286             if (!found) {
0287                 TimerInfo info;
0288                 info.id       = id;
0289                 info.interval = edtimers[n].interval;
0290                 info.time.start();
0291                 timers += info;
0292 #ifdef TIMERFIXER_DEBUG
0293                 printf("TimerFixer[%p] timer [%d] added (interval=%d)\n", this, info.id, info.interval);
0294 #endif
0295             }
0296         }
0297     }
0298 };
0299 
0300 //----------------------------------------------------------------------------
0301 // Synchronizer
0302 //----------------------------------------------------------------------------
0303 class SynchronizerAgent : public QObject
0304 {
0305     Q_OBJECT
0306 public:
0307     SynchronizerAgent(QObject *parent = nullptr)
0308         : QObject(parent)
0309     {
0310         QMetaObject::invokeMethod(this, "started", Qt::QueuedConnection);
0311     }
0312 
0313 Q_SIGNALS:
0314     void started();
0315 };
0316 
0317 class Synchronizer::Private : public QThread
0318 {
0319     Q_OBJECT
0320 public:
0321     Synchronizer *q;
0322 
0323     bool active;
0324     bool do_quit;
0325     bool cond_met;
0326 
0327     QObject           *obj;
0328     QEventLoop        *loop;
0329     SynchronizerAgent *agent;
0330     TimerFixer        *fixer;
0331     QMutex             m;
0332     QWaitCondition     w;
0333     QThread           *orig_thread;
0334 
0335     Private(QObject *_obj, Synchronizer *_q)
0336         : QThread(_q)
0337         , q(_q)
0338         , active(false)
0339         , do_quit(false)
0340         , cond_met(false)
0341         , obj(_obj)
0342         , loop(nullptr)
0343         , agent(nullptr)
0344         , fixer(nullptr)
0345         , m()
0346         , w()
0347         , orig_thread(nullptr)
0348     {
0349         // SafeTimer has own method to fix timers, skip it too
0350         if (!qobject_cast<SafeTimer *>(obj))
0351             fixer = new TimerFixer(obj);
0352     }
0353 
0354     ~Private() override
0355     {
0356         stop();
0357         delete fixer;
0358     }
0359 
0360     void start()
0361     {
0362         if (active)
0363             return;
0364 
0365         m.lock();
0366         active  = true;
0367         do_quit = false;
0368         QThread::start();
0369         w.wait(&m);
0370         m.unlock();
0371     }
0372 
0373     void stop()
0374     {
0375         if (!active)
0376             return;
0377 
0378         m.lock();
0379         do_quit = true;
0380         w.wakeOne();
0381         m.unlock();
0382         wait();
0383         active = false;
0384     }
0385 
0386     bool waitForCondition(int msecs)
0387     {
0388         unsigned long time = ULONG_MAX;
0389         if (msecs != -1)
0390             time = msecs;
0391 
0392         // move object to the worker thread
0393         cond_met    = false;
0394         orig_thread = QThread::currentThread();
0395         q->setParent(nullptr); // don't follow the object
0396         QObject *orig_parent = obj->parent();
0397         obj->setParent(nullptr); // unparent the target or the move will fail
0398         obj->moveToThread(this);
0399 
0400         // tell the worker thread to start, wait for completion
0401         m.lock();
0402         w.wakeOne();
0403         if (!w.wait(&m, time)) {
0404             if (loop) {
0405                 // if we timed out, tell the worker to quit
0406                 QMetaObject::invokeMethod(loop, "quit");
0407                 w.wait(&m);
0408             }
0409         }
0410 
0411         // at this point the worker is done.  cleanup and return
0412         m.unlock();
0413 
0414         // restore parents
0415         obj->setParent(orig_parent);
0416         q->setParent(obj);
0417 
0418         return cond_met;
0419     }
0420 
0421     void conditionMet()
0422     {
0423         if (!loop)
0424             return;
0425         loop->quit();
0426         cond_met = true;
0427     }
0428 
0429 protected:
0430     void run() override
0431     {
0432         m.lock();
0433         QEventLoop eventLoop;
0434 
0435         while (true) {
0436             // thread now sleeps, waiting for work
0437             w.wakeOne();
0438             w.wait(&m);
0439             if (do_quit) {
0440                 m.unlock();
0441                 break;
0442             }
0443 
0444             loop  = &eventLoop;
0445             agent = new SynchronizerAgent;
0446             connect(agent, &SynchronizerAgent::started, this, &Private::agent_started, Qt::DirectConnection);
0447 
0448             // run the event loop
0449             eventLoop.exec();
0450 
0451             delete agent;
0452             agent = nullptr;
0453 
0454             // eventloop done, flush pending events
0455             QCoreApplication::instance()->sendPostedEvents();
0456             QCoreApplication::instance()->sendPostedEvents(nullptr, QEvent::DeferredDelete);
0457 
0458             // and move the object back
0459             obj->moveToThread(orig_thread);
0460 
0461             m.lock();
0462             loop = nullptr;
0463             w.wakeOne();
0464         }
0465     }
0466 
0467 private Q_SLOTS:
0468     void agent_started()
0469     {
0470         m.unlock();
0471     }
0472 };
0473 
0474 Synchronizer::Synchronizer(QObject *parent)
0475     : QObject(parent)
0476 {
0477     d = new Private(parent, this);
0478 }
0479 
0480 Synchronizer::~Synchronizer()
0481 {
0482     delete d;
0483 }
0484 
0485 bool Synchronizer::waitForCondition(int msecs)
0486 {
0487     d->start();
0488     return d->waitForCondition(msecs);
0489 }
0490 
0491 void Synchronizer::conditionMet()
0492 {
0493     d->conditionMet();
0494 }
0495 
0496 }
0497 
0498 #include "synchronizer.moc"