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"