}
}
+void QStateMachinePrivate::_q_startDelayedEventTimer(int id, int delay)
+{
+ Q_Q(QStateMachine);
+ QMutexLocker locker(&delayedEventsMutex);
+ QHash<int, DelayedEvent>::iterator it = delayedEvents.find(id);
+ if (it != delayedEvents.end()) {
+ DelayedEvent &e = it.value();
+ Q_ASSERT(!e.timerId);
+ e.timerId = q->startTimer(delay);
+ if (!e.timerId) {
+ qWarning("QStateMachine::postDelayedEvent: failed to start timer (id=%d, delay=%d)", id, delay);
+ delayedEvents.erase(it);
+ delayedEventIdFreeList.release(id);
+ } else {
+ timerIdToDelayedEventId.insert(e.timerId, id);
+ }
+ } else {
+ // It's been cancelled already
+ delayedEventIdFreeList.release(id);
+ }
+}
+
+void QStateMachinePrivate::_q_killDelayedEventTimer(int id, int timerId)
+{
+ Q_Q(QStateMachine);
+ q->killTimer(timerId);
+ QMutexLocker locker(&delayedEventsMutex);
+ delayedEventIdFreeList.release(id);
+}
+
void QStateMachinePrivate::postInternalEvent(QEvent *e)
{
QMutexLocker locker(&internalEventMutex);
{
Q_Q(QStateMachine);
QMutexLocker locker(&delayedEventsMutex);
- QHash<int, QEvent*>::const_iterator it;
+ QHash<int, DelayedEvent>::const_iterator it;
for (it = delayedEvents.constBegin(); it != delayedEvents.constEnd(); ++it) {
- int id = it.key();
- QEvent *e = it.value();
- q->killTimer(id);
- delete e;
+ const DelayedEvent &e = it.value();
+ if (e.timerId) {
+ timerIdToDelayedEventId.remove(e.timerId);
+ q->killTimer(e.timerId);
+ delayedEventIdFreeList.release(it.key());
+ } else {
+ // Cancellation will be detected in pending _q_startDelayedEventTimer() call
+ }
+ delete e.event;
}
delayedEvents.clear();
}
qDebug() << this << ": posting event" << event << "with delay" << delay;
#endif
QMutexLocker locker(&d->delayedEventsMutex);
- int tid = startTimer(delay);
- d->delayedEvents[tid] = event;
- return tid;
+ int id = d->delayedEventIdFreeList.next();
+ bool inMachineThread = (QThread::currentThread() == thread());
+ int timerId = inMachineThread ? startTimer(delay) : 0;
+ if (inMachineThread && !timerId) {
+ qWarning("QStateMachine::postDelayedEvent: failed to start timer with interval %d", delay);
+ d->delayedEventIdFreeList.release(id);
+ return -1;
+ }
+ QStateMachinePrivate::DelayedEvent delayedEvent(event, timerId);
+ d->delayedEvents.insert(id, delayedEvent);
+ if (timerId) {
+ d->timerIdToDelayedEventId.insert(timerId, id);
+ } else {
+ Q_ASSERT(!inMachineThread);
+ QMetaObject::invokeMethod(this, "_q_startDelayedEventTimer",
+ Qt::QueuedConnection,
+ Q_ARG(int, id),
+ Q_ARG(int, delay));
+ }
+ return id;
}
/*!
return false;
}
QMutexLocker locker(&d->delayedEventsMutex);
- QEvent *e = d->delayedEvents.take(id);
- if (!e)
+ QStateMachinePrivate::DelayedEvent e = d->delayedEvents.take(id);
+ if (!e.event)
return false;
- killTimer(id);
- delete e;
+ if (e.timerId) {
+ d->timerIdToDelayedEventId.remove(e.timerId);
+ bool inMachineThread = (QThread::currentThread() == thread());
+ if (inMachineThread) {
+ killTimer(e.timerId);
+ d->delayedEventIdFreeList.release(id);
+ } else {
+ QMetaObject::invokeMethod(this, "_q_killDelayedEventTimer",
+ Qt::QueuedConnection,
+ Q_ARG(int, id),
+ Q_ARG(int, e.timerId));
+ }
+ } else {
+ // Cancellation will be detected in pending _q_startDelayedEventTimer() call
+ }
+ delete e.event;
return true;
}
if (d->state != QStateMachinePrivate::Running) {
// This event has been cancelled already
QMutexLocker locker(&d->delayedEventsMutex);
- Q_ASSERT(!d->delayedEvents.contains(tid));
+ Q_ASSERT(!d->timerIdToDelayedEventId.contains(tid));
return true;
}
d->delayedEventsMutex.lock();
- QEvent *ee = d->delayedEvents.take(tid);
- if (ee != 0) {
+ int id = d->timerIdToDelayedEventId.take(tid);
+ QStateMachinePrivate::DelayedEvent ee = d->delayedEvents.take(id);
+ if (ee.event != 0) {
+ Q_ASSERT(ee.timerId == tid);
killTimer(tid);
+ d->delayedEventIdFreeList.release(id);
d->delayedEventsMutex.unlock();
- d->postExternalEvent(ee);
+ d->postExternalEvent(ee.event);
d->processEvents(QStateMachinePrivate::DirectProcessing);
return true;
} else {
#include <QtCore/qpair.h>
#include <QtCore/qset.h>
#include <QtCore/qvector.h>
+#include <private/qfreelist_p.h>
QT_BEGIN_NAMESPACE
#ifndef QT_NO_ANIMATION
void _q_animationFinished();
#endif
+ void _q_startDelayedEventTimer(int id, int delay);
+ void _q_killDelayedEventTimer(int id, int timerId);
QState *rootState() const;
#ifndef QT_NO_STATEMACHINE_EVENTFILTER
QHash<QObject*, QHash<QEvent::Type, int> > qobjectEvents;
#endif
- QHash<int, QEvent*> delayedEvents;
+ QFreeList<void> delayedEventIdFreeList;
+ struct DelayedEvent {
+ QEvent *event;
+ int timerId;
+ DelayedEvent(QEvent *e, int tid)
+ : event(e), timerId(tid) {}
+ DelayedEvent()
+ : event(0), timerId(0) {}
+ };
+ QHash<int, DelayedEvent> delayedEvents;
+ QHash<int, int> timerIdToDelayedEventId;
QMutex delayedEventsMutex;
typedef QEvent* (*f_cloneEvent)(QEvent*);
void postEvent();
void cancelDelayedEvent();
void postDelayedEventAndStop();
+ void postDelayedEventFromThread();
void stopAndPostEvent();
void stateFinished();
void parallelStates();
QVERIFY(machine.configuration().contains(s1));
}
+class DelayedEventPosterThread : public QThread
+{
+ Q_OBJECT
+public:
+ DelayedEventPosterThread(QStateMachine *machine, QObject *parent = 0)
+ : QThread(parent), firstEventWasCancelled(false),
+ m_machine(machine), m_count(0)
+ {
+ moveToThread(this);
+ QObject::connect(m_machine, SIGNAL(started()),
+ this, SLOT(postEvent()));
+ }
+
+ mutable bool firstEventWasCancelled;
+
+private Q_SLOTS:
+ void postEvent()
+ {
+ int id = m_machine->postDelayedEvent(new QEvent(QEvent::User), 1000);
+ firstEventWasCancelled = m_machine->cancelDelayedEvent(id);
+
+ m_machine->postDelayedEvent(new QEvent(QEvent::User), 1);
+
+ quit();
+ }
+private:
+ QStateMachine *m_machine;
+ int m_count;
+};
+
+void tst_QStateMachine::postDelayedEventFromThread()
+{
+ QStateMachine machine;
+ QState *s1 = new QState(&machine);
+ QFinalState *f = new QFinalState(&machine);
+ s1->addTransition(new EventTransition(QEvent::User, f));
+ machine.setInitialState(s1);
+
+ DelayedEventPosterThread poster(&machine);
+ poster.start();
+
+ QSignalSpy finishedSpy(&machine, SIGNAL(finished()));
+ QVERIFY(finishedSpy.isValid());
+ machine.start();
+ QTRY_COMPARE(finishedSpy.count(), 1);
+
+ QVERIFY(poster.firstEventWasCancelled);
+}
+
void tst_QStateMachine::stopAndPostEvent()
{
QStateMachine machine;