pidgin/quail/quail-redux

0760912235cb
Parents 8f1353660059
Children 0ddfc69bdc67
Experiments with how to improve the event system
--- a/.hgignore Wed Sep 18 14:04:25 2013 +0100
+++ b/.hgignore Thu Sep 19 16:09:24 2013 +0100
@@ -1,6 +1,7 @@
# use glob syntax.
syntax: glob
+*.o
obj
moc
win32-bin
--- a/Quail-redux.pro Wed Sep 18 14:04:25 2013 +0100
+++ b/Quail-redux.pro Thu Sep 19 16:09:24 2013 +0100
@@ -56,10 +56,15 @@
PKGCONFIG += purple glib-2.0 gmodule-2.0
}
-win32-g++ {
+win32-g++* {
message("Using win32")
- HEADERS += src/QuailWinGlibEventLoop.h
- SOURCES += src/QuailWinGlibEventLoop.cpp
+# HEADERS += src/quailtimerinfolist.h \
+# src/qsystemlibrary.h \
+# src/QuailWinGlibEventLoop.h
+
+# SOURCES += src/quailtimerinfolist.cpp \
+# src/qsystemlibrary.h \
+# src/QuailWinGlibEventLoop.cpp
#RC_FILE = resource.rc
@@ -74,7 +79,7 @@
LIBS += -L"$(QTDIR)/lib"
}
-HEADERS = \
+HEADERS += \
src/QuailAccountBox.h \
src/QuailAccountEditor.h \
src/QuailAccountsWindow.h \
@@ -103,8 +108,7 @@
src/QuailStatusSelector.h \
src/QuailBlistItem.h
-
-SOURCES = \
+SOURCES += \
src/QuailAccountBox.cpp \
src/QuailAccountEditor.cpp \
src/QuailAccountsWindow.cpp \
@@ -130,9 +134,8 @@
src/main.cpp \
src/QuailConvDisplay.cpp \
src/QuailStatusSelector.cpp \
- src/QuailBlistItem.cpp
-
-
+ src/QuailBlistItem.cpp \
+ src/qsystemlibrary.cpp
RESOURCES += \
quail.qrc
--- a/src/QuailEventLoop.cpp Wed Sep 18 14:04:25 2013 +0100
+++ b/src/QuailEventLoop.cpp Thu Sep 19 16:09:24 2013 +0100
@@ -20,8 +20,10 @@
* MA 02111-1307 USA
*/
#include "QuailEventLoop.h"
+#include <QApplication>
#include <QDebug>
#include <QMap>
+#include <QThread>
typedef struct
{
@@ -39,21 +41,38 @@
static guint nextSourceId = 0;
static QMap<guint, QQuailSourceInfo*> m_sources;
+//static QThread *quailThread = new QThread();
QQuailTimer::QQuailTimer(guint sourceId, GSourceFunc func, gpointer data)
- : QTimer(), sourceId(sourceId), func(func), userData(data)
+ : sourceId(sourceId), func(func), userData(data), t(0)
{
- connect(this, SIGNAL(timeout()),
- this, SLOT(update()));
+ qDebug() << "QQuailTimer::QQuailTimer.1";
}
void
QQuailTimer::update()
{
+ qDebug() << "QQuailTimer::update()";
if (!func(userData))
qQuailSourceRemove(sourceId);
}
+void
+QQuailTimer::start(int msec)
+{
+ qDebug() << "QQuailTimer::start.1";
+// if (mainWin == 0)
+// return;
+
+ if (t == 0)
+ {
+ t = mainWin->getNewTimer();
+ connect(t, SIGNAL(timeout()),
+ this, SLOT(update()));
+ }
+ t->start(msec);
+}
+
QQuailInputNotifier::QQuailInputNotifier(int fd,
PurpleInputCondition cond,
PurpleInputFunction func,
@@ -115,6 +134,7 @@
info->handle = nextSourceId++;
info->timer = new QQuailTimer(info->handle, func, data);
+ //info->timer->moveToThread(quailThread);
info->timer->start(interval);
m_sources.insert(info->handle, info);
@@ -205,3 +225,4 @@
{
return &eventloop_ops;
}
+
--- a/src/QuailEventLoop.h Wed Sep 18 14:04:25 2013 +0100
+++ b/src/QuailEventLoop.h Thu Sep 19 16:09:24 2013 +0100
@@ -28,14 +28,18 @@
#include <QSocketNotifier>
/* http://harmattan-dev.nokia.com/docs/library/html/qt4/qabstracteventdispatcher.html */
//#include <QAbstractEventDispatcher>
+#include "QuailMainWindow.h"
-class QQuailTimer : public QTimer
+static QQuailMainWindow *mainWin = 0;
+
+class QQuailTimer : public QObject
{
Q_OBJECT
public:
QQuailTimer(guint sourceId, GSourceFunc func, gpointer data);
+ void start(int interval);
private slots:
void update();
@@ -43,6 +47,7 @@
guint sourceId;
GSourceFunc func;
gpointer userData;
+ QTimer *t;
};
class QQuailInputNotifier : public QObject
@@ -64,6 +69,8 @@
QSocketNotifier *readNotifier, *writeNotifier;
};
+
+
/**
* Returns the Qt event loop UI operations structure.
*
--- a/src/QuailMainWindow.cpp Wed Sep 18 14:04:25 2013 +0100
+++ b/src/QuailMainWindow.cpp Thu Sep 19 16:09:24 2013 +0100
@@ -57,8 +57,6 @@
#include "QuailPrefsDialog.h"
#include "QuailRequest.h"
-static QQuailMainWindow *mainWin = NULL;
-
/**************************************************************************
* Core stuff
**************************************************************************/
@@ -339,8 +337,7 @@
qDebug() << "QQuailMainWindow::initCore().4";
purple_plugins_add_search_path(path);
qDebug() << "QQuailMainWindow::initCore().5";
-
- purple_plugins_probe(NULL);
+ purple_plugins_probe(NULL);
qDebug() << "QQuailMainWindow::initCore().6";
purple_prefs_load();
qDebug() << "QQuailMainWindow::initCore().7";
--- a/src/QuailMainWindow.h Wed Sep 18 14:04:25 2013 +0100
+++ b/src/QuailMainWindow.h Thu Sep 19 16:09:24 2013 +0100
@@ -24,6 +24,7 @@
#include <QMainWindow>
#include <QTranslator>
+#include <QTimer>
#include <libpurple/conversation.h>
@@ -61,6 +62,9 @@
QStackedWidget *getWidgetStack() const;
QQuailConnectionMeters *getMeters() const;
+ QTimer *getNewTimer()
+ { return new QTimer(this); }
+
public slots:
void showBlistWindow();
void showAccountsWindow();
--- a/src/QuailWinGlibEventLoop.cpp Wed Sep 18 14:04:25 2013 +0100
+++ b/src/QuailWinGlibEventLoop.cpp Thu Sep 19 16:09:24 2013 +0100
@@ -2,6 +2,18 @@
#include <QCoreApplication>
#include <glib.h>
+#include <QDateTime>
+#include <QDebug>
+#include <QThread>
+#include <QMutex>
+#include <QMutexLocker>
+#include "qsystemlibrary.h"
+#include <QWinEventNotifier>
+#include <QElapsedTimer>
+#include "quailtimerinfolist.h"
+
+#include <sys/time.h>
+#include <windows.h>
struct GPollFDWithQSocketNotifier
{
@@ -200,19 +212,21 @@
static gboolean postEventSourcePrepare(GSource *s, gint *timeout)
{
- QThreadData *data = QThreadData::current();
- if (!data)
- return false;
+// QThreadData *data = QThreadData::current();
+// if (!data)
+// return false;
- gint dummy;
- if (!timeout)
- timeout = &dummy;
- const bool canWait = data->canWaitLocked();
- *timeout = canWait ? -1 : 0;
+// gint dummy;
+// if (!timeout)
+// timeout = &dummy;
+// const bool canWait = data->canWaitLocked();
+// *timeout = canWait ? -1 : 0;
GPostEventSource *source = reinterpret_cast<GPostEventSource *>(s);
- return (!canWait
- || (source->serialNumber.load() != source->lastSerialNumber));
+// return (!canWait
+// || (source->serialNumber.load() != source->lastSerialNumber));
+ return source->serialNumber.load() != source->lastSerialNumber;
+
}
static gboolean postEventSourceCheck(GSource *source)
@@ -479,7 +493,9 @@
static bool triedResolve = false;
if (!triedResolve) {
#ifndef QT_NO_THREAD
- QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve));
+ //QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve));
+ QMutex locker;
+ triedResolve = locker.tryLock();
if (triedResolve)
return;
#endif
@@ -500,17 +516,21 @@
}
}
-QuailEventDispatcherWinGlib::QuailEventDispatcherWinGlib(GMainContext *context = 0, QObject parent = 0)
- : mainContext(context), threadId(GetCurrentThreadId()), interruptFlag(false), internalHwnd(0), getMessageHook(0),
- serialNumber(0), lastSerialNumber(0), sendPostedEventsWindowsTimerId(0), wakeUps(0)
+QuailEventDispatcherWinGlib::QuailEventDispatcherWinGlib(QObject *parent)
+ : QAbstractEventDispatcher(parent), mainContext(0),
+ threadId(GetCurrentThreadId()), interruptFlag(false),
+ internalHwnd(0), getMessageHook(0),
+ serialNumber(0), lastSerialNumber(0),
+ sendPostedEventsWindowsTimerId(0), wakeUps(0)
{
+ qDebug() << "QuailEventDispatcherWinGlib";
resolveTimerAPI();
#if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 32
if (qEnvironmentVariableIsEmpty("QT_NO_THREADED_GLIB")) {
static QBasicMutex mutex;
QMutexLocker locker(&mutex);
- if (!g_thread_supported())
- g_thread_init(NULL);
+// if (!g_thread_supported())
+// g_thread_init(NULL);
}
#endif
@@ -580,7 +600,7 @@
void QuailEventDispatcherWinGlib::activateEventNotifier(QWinEventNotifier * wen)
{
QEvent event(QEvent::WinEventAct);
- QCoreApplication::sendEvent(wen, &event);
+ QCoreApplication::sendEvent((QObject*)wen, &event);
}
// This function is called by a workerthread
@@ -620,7 +640,8 @@
#endif
QuailEventDispatcherWinGlib *d = 0;
if (q != 0)
- d = q->d_func();
+ d = q;
+ //d = q->d_func();
if (message == WM_QT_SOCKETNOTIFIER) {
// socket notifier message
@@ -685,7 +706,8 @@
Q_ASSERT(q != 0);
if (q) {
MSG *msg = (MSG *) lp;
- QuailEventDispatcherWinGlib *d = q->d_func();
+ //QuailEventDispatcherWinGlib *d = q->d_func();
+ QuailEventDispatcherWinGlib *d = q;
const int localSerialNumber = d->serialNumber.load();
if (HIWORD(GetQueueStatus(QS_TIMER | QS_INPUT | QS_RAWINPUT)) == 0) {
// no more input or timer events in the message queue, we can allow posted events to be sent normally now
@@ -768,9 +790,11 @@
void QuailEventDispatcherWinGlib::registerTimer(WinTimerInfo *t)
{
+ qDebug() << "QuailEventDispatcherWinGlib::registerTimer";
Q_ASSERT(internalHwnd);
- Q_Q(QuailEventDispatcherWinGlib);
+ //Q_Q(QuailEventDispatcherWinGlib);
+ QuailEventDispatcherWinGlib *q = this;
int ok = 0;
uint interval = t->interval;
@@ -790,7 +814,7 @@
ok = SetTimer(internalHwnd, t->timerId, interval, 0);
}
- t->timeout = qt_msectime() + interval;
+ t->timeout = QDateTime::currentMSecsSinceEpoch() + interval;
if (ok == 0)
qErrnoWarning("QuailEventDispatcherWinGlib::registerTimer: Failed to create a timer");
@@ -799,10 +823,12 @@
void QuailEventDispatcherWinGlib::unregisterTimer(WinTimerInfo *t)
{
if (t->interval == 0) {
- QCoreApplicationPrivate::removePostedTimerEvent(t->dispatcher, t->timerId);
+ //QCoreApplicationPrivate::removePostedTimerEvent(t->dispatcher, t->timerId);
+ QCoreApplication::removePostedEvents(t->dispatcher, t->timerId);
} else if (t->fastTimerId != 0) {
qtimeKillEvent(t->fastTimerId);
- QCoreApplicationPrivate::removePostedTimerEvent(t->dispatcher, t->timerId);
+ //QCoreApplicationPrivate::removePostedTimerEvent(t->dispatcher, t->timerId);
+ QCoreApplication::removePostedEvents(t->dispatcher, t->timerId);
} else if (internalHwnd) {
KillTimer(internalHwnd, t->timerId);
}
@@ -839,12 +865,13 @@
sn_event |= FD_OOB;
// BoundsChecker may emit a warning for WSAAsyncSelect when sn_event == 0
// This is a BoundsChecker bug and not a Qt bug
- WSAAsyncSelect(socket, internalHwnd, sn_event ? int(WM_QT_SOCKETNOTIFIER) : 0, sn_event);
+ //WSAAsyncSelect(socket, internalHwnd, sn_event ? int(WM_QT_SOCKETNOTIFIER) : 0, sn_event);
}
void QuailEventDispatcherWinGlib::createInternalHwnd()
{
- Q_D(QuailEventDispatcherWinGlib);
+ //QuailEventDispatcherWinGlib *d = this;
+ QuailEventDispatcherWinGlib *d = this;
Q_ASSERT(!d->internalHwnd);
if (d->internalHwnd)
@@ -876,7 +903,9 @@
bool QuailEventDispatcherWinGlib::processEvents(QEventLoop::ProcessEventsFlags flags)
{
- Q_D(QuailEventDispatcherWinGlib);
+ qDebug() << "QuailEventDispatcherWinGlib::processEvents";
+ //QuailEventDispatcherWinGlib *d = this;
+ QuailEventDispatcherWinGlib *d = this;
if (!d->internalHwnd)
createInternalHwnd();
@@ -1025,6 +1054,7 @@
void QuailEventDispatcherWinGlib::registerSocketNotifier(QSocketNotifier *notifier)
{
+ qDebug() << "QuailEventDispatcherWinGlib::registerSocketNotifier";
Q_ASSERT(notifier);
int sockfd = notifier->socket();
int type = notifier->type();
@@ -1038,7 +1068,8 @@
}
#endif
- Q_D(QuailEventDispatcherWinGlib);
+ //QuailEventDispatcherWinGlib *d = this;
+ QuailEventDispatcherWinGlib *d = this;
QSNDict *sn_vec[3] = { &d->sn_read, &d->sn_write, &d->sn_except };
QSNDict *dict = sn_vec[type];
@@ -1063,6 +1094,7 @@
void QuailEventDispatcherWinGlib::unregisterSocketNotifier(QSocketNotifier *notifier)
{
+ qDebug() << "QuailEventDispatcherWinGlib::unregisterSocketNotifier";
Q_ASSERT(notifier);
int sockfd = notifier->socket();
int type = notifier->type();
@@ -1076,7 +1108,7 @@
}
#endif
- Q_D(QuailEventDispatcherWinGlib);
+ QuailEventDispatcherWinGlib *d = this;
QSNDict *sn_vec[3] = { &d->sn_read, &d->sn_write, &d->sn_except };
QSNDict *dict = sn_vec[type];
QSockNot *sn = dict->value(sockfd);
@@ -1092,6 +1124,7 @@
void QuailEventDispatcherWinGlib::registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *object)
{
+ qDebug() << "QuailEventDispatcherWinGlib::registerTimer.GLIB'ed";
if (timerId < 1 || interval < 0 || !object) {
qWarning("QuailEventDispatcherWinGlib::registerTimer: invalid arguments");
return;
@@ -1100,7 +1133,7 @@
return;
}
- Q_D(QuailEventDispatcherWinGlib);
+ QuailEventDispatcherWinGlib *d = this;
WinTimerInfo *t = new WinTimerInfo;
t->dispatcher = this;
@@ -1120,6 +1153,7 @@
bool QuailEventDispatcherWinGlib::unregisterTimer(int timerId)
{
+ qDebug() << "QuailEventDispatcherWinGlib::unregisterTimer";
if (timerId < 1) {
qWarning("QuailEventDispatcherWinGlib::unregisterTimer: invalid argument");
return false;
@@ -1130,7 +1164,7 @@
return false;
}
- Q_D(QuailEventDispatcherWinGlib);
+ QuailEventDispatcherWinGlib *d = this;
if (d->timerVec.isEmpty() || timerId <= 0)
return false;
@@ -1146,6 +1180,7 @@
bool QuailEventDispatcherWinGlib::unregisterTimers(QObject *object)
{
+ qDebug() << "QuailEventDispatcherWinGlib::unregisterTimer";
if (!object) {
qWarning("QuailEventDispatcherWinGlib::unregisterTimers: invalid argument");
return false;
@@ -1156,7 +1191,7 @@
return false;
}
- Q_D(QuailEventDispatcherWinGlib);
+ QuailEventDispatcherWinGlib *d = this;
if (d->timerVec.isEmpty())
return false;
register WinTimerInfo *t;
@@ -1175,12 +1210,13 @@
QList<QuailEventDispatcherWinGlib::TimerInfo>
QuailEventDispatcherWinGlib::registeredTimers(QObject *object) const
{
+ qDebug() << "QuailEventDispatcherWinGlib::registeredTimers";
if (!object) {
qWarning("QuailEventDispatcherWinGlib:registeredTimers: invalid argument");
return QList<TimerInfo>();
}
- Q_D(const QuailEventDispatcherWinGlib);
+ const QuailEventDispatcherWinGlib *d = this;
QList<TimerInfo> list;
for (int i = 0; i < d->timerVec.size(); ++i) {
const WinTimerInfo *t = d->timerVec.at(i);
@@ -1192,6 +1228,7 @@
bool QuailEventDispatcherWinGlib::registerEventNotifier(QWinEventNotifier *notifier)
{
+ qDebug() << "QuailEventDispatcherWinGlib::registerEventNotifier";
if (!notifier) {
qWarning("QWinEventNotifier: Internal error");
return false;
@@ -1200,7 +1237,7 @@
return false;
}
- Q_D(QuailEventDispatcherWinGlib);
+ QuailEventDispatcherWinGlib *d = this;
if (d->winEventNotifierList.contains(notifier))
return true;
@@ -1215,6 +1252,7 @@
void QuailEventDispatcherWinGlib::unregisterEventNotifier(QWinEventNotifier *notifier)
{
+ qDebug() << "QuailEventDispatcherWinGlib::unregisterEventNotifier";
if (!notifier) {
qWarning("QWinEventNotifier: Internal error");
return;
@@ -1223,7 +1261,7 @@
return;
}
- Q_D(QuailEventDispatcherWinGlib);
+ QuailEventDispatcherWinGlib *d = this;
int i = d->winEventNotifierList.indexOf(notifier);
if (i != -1)
@@ -1232,7 +1270,7 @@
void QuailEventDispatcherWinGlib::activateEventNotifiers()
{
- Q_D(QuailEventDispatcherWinGlib);
+ QuailEventDispatcherWinGlib *d = this;
//### this could break if events are removed/added in the activation
for (int i=0; i<d->winEventNotifierList.count(); i++) {
#if !defined(Q_OS_WINCE)
@@ -1254,12 +1292,12 @@
}
#endif
- Q_D(QuailEventDispatcherWinGlib);
+ QuailEventDispatcherWinGlib *d = this;
if (d->timerVec.isEmpty())
return -1;
- quint64 currentTime = qt_msectime();
+ quint64 currentTime = QDateTime::currentMSecsSinceEpoch ();
register WinTimerInfo *t;
for (int i=0; i<d->timerVec.size(); i++) {
@@ -1283,7 +1321,7 @@
void QuailEventDispatcherWinGlib::wakeUp()
{
- Q_D(QuailEventDispatcherWinGlib);
+ QuailEventDispatcherWinGlib *d = this;
d->serialNumber.ref();
if (d->internalHwnd && d->wakeUps.testAndSetAcquire(0, 1)) {
// post a WM_QT_SENDPOSTEDEVENTS to this thread if there isn't one already pending
@@ -1293,7 +1331,7 @@
void QuailEventDispatcherWinGlib::interrupt()
{
- Q_D(QuailEventDispatcherWinGlib);
+ QuailEventDispatcherWinGlib *d = this;
d->interruptFlag = true;
wakeUp();
}
@@ -1306,7 +1344,7 @@
void QuailEventDispatcherWinGlib::closingDown()
{
- Q_D(QuailEventDispatcherWinGlib);
+ QuailEventDispatcherWinGlib *d = this;
// clean up any socketnotifiers
while (!d->sn_read.isEmpty())
@@ -1331,7 +1369,8 @@
bool QuailEventDispatcherWinGlib::event(QEvent *e)
{
- Q_D(QuailEventDispatcherWinGlib);
+ qDebug() << "QuailEventDispatcherWinGlib::event";
+ QuailEventDispatcherWinGlib *d = this;
if (e->type() == QEvent::ZeroTimerEvent) {
QZeroTimerEvent *zte = static_cast<QZeroTimerEvent*>(e);
WinTimerInfo *t = d->timerDict.value(zte->timerId());
@@ -1361,6 +1400,62 @@
void QuailEventDispatcherWinGlib::sendPostedEvents()
{
- Q_D(QuailEventDispatcherWinGlib);
- QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
+ //QuailEventDispatcherWinGlib *d = this;
+ //QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
+ QCoreApplication::sendPostedEvents();
+}
+
+QThreadData::QThreadData(int initialRefCount)
+ : _ref(initialRefCount), thread(0), threadId(0),
+ quitNow(false), loopLevel(0), eventDispatcher(0), canWait(true), isAdopted(false)
+{
+ // fprintf(stderr, "QThreadData %p created\n", this);
}
+
+QThreadData::~QThreadData()
+{
+ Q_ASSERT(_ref.load() == 0);
+
+ // In the odd case that Qt is running on a secondary thread, the main
+ // thread instance will have been dereffed asunder because of the deref in
+ // QThreadData::current() and the deref in the pthread_destroy. To avoid
+ // crashing during QCoreApplicationData's global static cleanup we need to
+ // safeguard the main thread here.. This fix is a bit crude, but it solves
+ // the problem...
+// if (this->thread == QCoreApplication::thread()) {
+// //QCoreApplication::thread() = 0;
+// QThreadData::clearCurrentThreadData();
+// }
+
+ QThread *t = thread;
+ thread = 0;
+ delete t;
+
+ for (int i = 0; i < postEventList.size(); ++i) {
+ const QPostEvent &pe = postEventList.at(i);
+ if (pe.event) {
+ //--pe.receiver->d_func()->postedEvents;
+ pe.event->posted = false;
+ delete pe.event;
+ }
+ }
+
+ // fprintf(stderr, "QThreadData %p destroyed\n", this);
+}
+
+void QThreadData::ref()
+{
+#ifndef QT_NO_THREAD
+ (void) _ref.ref();
+ Q_ASSERT(_ref.load() != 0);
+#endif
+}
+
+void QThreadData::deref()
+{
+#ifndef QT_NO_THREAD
+ if (!_ref.deref())
+ delete this;
+#endif
+}
+
--- a/src/QuailWinGlibEventLoop.h Wed Sep 18 14:04:25 2013 +0100
+++ b/src/QuailWinGlibEventLoop.h Thu Sep 19 16:09:24 2013 +0100
@@ -2,15 +2,18 @@
#define QUAILWINGLIBEVENTLOOP_H
#include "QtCore/qt_windows.h"
-#include "QtCore/qcoreevent.h"
+//#include "QtCore/qcoreevent.h"
#include <QAbstractEventDispatcher>
+#include <QCoreApplication>
#include <QHash>
+#include <QMutex>
+#include <QThread>
+#include <QSocketNotifier>
+#include <QStack>
+#include <QVector>
typedef struct _GMainContext GMainContext;
-class QWinEventNotifier;
-class QuailEventDispatcherWinGlibPrivate;
-
// forward declaration
LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp);
int qt_msectime();
@@ -49,13 +52,169 @@
typedef QList<WinTimerInfo*> WinTimerVec; // vector of TimerInfo structs
typedef QHash<int, WinTimerInfo*> WinTimerDict; // fast dict of timers
+class QPostEvent
+{
+public:
+ QObject *receiver;
+ QEvent *event;
+ int priority;
+ inline QPostEvent()
+ : receiver(0), event(0), priority(0)
+ { }
+ inline QPostEvent(QObject *r, QEvent *e, int p)
+ : receiver(r), event(e), priority(p)
+ { }
+};
+
+inline bool operator<(const QPostEvent &first, const QPostEvent &second)
+{
+ return first.priority > second.priority;
+}
+
+// This class holds the list of posted events.
+// The list has to be kept sorted by priority
+class QPostEventList : public QVector<QPostEvent>
+{
+public:
+ // recursion == recursion count for sendPostedEvents()
+ int recursion;
+
+ // sendOffset == the current event to start sending
+ int startOffset;
+ // insertionOffset == set by sendPostedEvents to tell postEvent() where to start insertions
+ int insertionOffset;
+
+ QMutex mutex;
+
+ inline QPostEventList()
+ : QVector<QPostEvent>(), recursion(0), startOffset(0), insertionOffset(0)
+ { }
+
+ void addEvent(const QPostEvent &ev) {
+ int priority = ev.priority;
+ if (isEmpty() ||
+ last().priority >= priority ||
+ insertionOffset >= size()) {
+ // optimization: we can simply append if the last event in
+ // the queue has higher or equal priority
+ append(ev);
+ } else {
+ // insert event in descending priority order, using upper
+ // bound for a given priority (to ensure proper ordering
+ // of events with the same priority)
+ QPostEventList::iterator at = std::upper_bound(begin() + insertionOffset, end(), ev);
+ insert(at, ev);
+ }
+ }
+private:
+ //hides because they do not keep that list sorted. addEvent must be used
+ using QVector<QPostEvent>::append;
+ using QVector<QPostEvent>::insert;
+};
+
+class QThreadPrivate : public QObject
+{
+ Q_OBJECT
+
+public:
+ QThreadPrivate(QThreadData *d = 0);
+ //~QThreadPrivate();
+
+ void setPriority(QThread::Priority prio);
+
+ mutable QMutex mutex;
+ QAtomicInt quitLockRef;
+
+ bool running;
+ bool finished;
+ bool isInFinish; //when in QThreadPrivate::finish
+
+ bool exited;
+ int returnCode;
+
+ uint stackSize;
+ QThread::Priority priority;
+
+ static QThread *threadForId(int id);
+
+#ifdef Q_OS_UNIX
+ pthread_t thread_id;
+ QWaitCondition thread_done;
+
+ static void *start(void *arg);
+ static void finish(void *);
+
+#endif // Q_OS_UNIX
+
+#ifdef Q_OS_WIN
+ static unsigned int __stdcall start(void *);
+ static void finish(void *, bool lockAnyway=true);
+
+ Qt::HANDLE handle;
+ unsigned int id;
+ int waiters;
+ bool terminationEnabled, terminatePending;
+# endif
+ QThreadData *data;
+
+ static void createEventDispatcher(QThreadData *data);
+
+ void ref()
+ {
+ quitLockRef.ref();
+ }
+
+ void deref()
+ {
+ if (!quitLockRef.deref() && running) {
+ QCoreApplication::instance()->postEvent(this, new QEvent(QEvent::Quit));
+ }
+ }
+};
+
+class QThreadData
+{
+ QAtomicInt _ref;
+
+public:
+ QThreadData(int initialRefCount = 1);
+ ~QThreadData();
+
+ static QThreadData *current();
+ static void clearCurrentThreadData();
+// static QThreadData *get2(QThread *thread)
+// { Q_ASSERT_X(thread != 0, "QThread", "internal error"); return thread->d_func()->data; }
+
+ void ref();
+ void deref();
+ inline bool hasEventDispatcher() const
+ { return eventDispatcher.load() != 0; }
+
+ bool canWaitLocked()
+ {
+ QMutexLocker locker(&postEventList.mutex);
+ return canWait;
+ }
+
+ QThread *thread;
+ Qt::HANDLE threadId;
+ bool quitNow;
+ int loopLevel;
+ QAtomicPointer<QAbstractEventDispatcher> eventDispatcher;
+ QStack<QEventLoop *> eventLoops;
+ QPostEventList postEventList;
+ bool canWait;
+ QVector<void *> tls;
+ bool isAdopted;
+};
+
class QuailEventDispatcherWinGlib : public QAbstractEventDispatcher
{
Q_OBJECT
void createInternalHwnd();
public:
- explicit QuailEventDispatcherWinGlib(GMainContext *context = 0, QObject *parent = 0);
+ QuailEventDispatcherWinGlib(QObject *parent = 0);
~QuailEventDispatcherWinGlib();
bool QT_ENSURE_STACK_ALIGNED_FOR_SSE processEvents(QEventLoop::ProcessEventsFlags flags);
--- a/src/main.cpp Wed Sep 18 14:04:25 2013 +0100
+++ b/src/main.cpp Thu Sep 19 16:09:24 2013 +0100
@@ -23,14 +23,13 @@
#if defined(Q_OS_CYGWIN)
#include "QuailWinGlibEventLoop.h"
#endif
-
+#include <QApplication>
#include "QuailMainWindow.h"
-#include <QApplication>
-
int main(int argc, char *argv[])
{
#if defined(Q_OS_CYGWIN)
+ qDebug() << "MAIN.1";
QuailEventDispatcherWinGlib quailEventLoop;
#endif
QApplication a(argc, argv);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/qsystemlibrary.cpp Thu Sep 19 16:09:24 2013 +0100
@@ -0,0 +1,145 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsystemlibrary.h"
+#include <QtCore/qvarlengtharray.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qfileinfo.h>
+
+#include <QtCore/qstring.h>
+#include <qt_windows.h>
+
+/*!
+
+ \internal
+ \class QSystemLibrary
+ \inmodule QtCore
+
+ The purpose of this class is to load only libraries that are located in
+ well-known and trusted locations on the filesystem. It does not suffer from
+ the security problem that QLibrary has, therefore it will never search in
+ the current directory.
+
+ The search order is the same as the order in DLL Safe search mode Windows,
+ except that we don't search:
+ * The current directory
+ * The 16-bit system directory. (normally \c{c:\windows\system})
+ * The Windows directory. (normally \c{c:\windows})
+
+ This means that the effective search order is:
+ 1. Application path.
+ 2. System libraries path.
+ 3. Trying all paths inside the PATH environment variable.
+
+ Note, when onlySystemDirectory is true it will skip 1) and 3).
+
+ DLL Safe search mode is documented in the "Dynamic-Link Library Search
+ Order" document on MSDN.
+
+ Since library loading code is sometimes shared between Windows and WinCE,
+ this class can also be used on WinCE. However, its implementation just
+ calls the LoadLibrary() function. This is ok since it is documented as not
+ loading from the current directory on WinCE. This behaviour is documented
+ in the documentation for LoadLibrary for Windows CE at MSDN.
+ (http://msdn.microsoft.com/en-us/library/ms886736.aspx)
+*/
+
+QT_BEGIN_NAMESPACE
+
+#if defined(Q_OS_WINCE)
+HINSTANCE QSystemLibrary::load(const wchar_t *libraryName, bool onlySystemDirectory /* = true */)
+{
+ return ::LoadLibrary(libraryName);
+}
+#else
+
+#if !defined(QT_BOOTSTRAPPED)
+extern QString qAppFileName();
+#endif
+
+static QString qSystemDirectory()
+{
+ QVarLengthArray<wchar_t, MAX_PATH> fullPath;
+
+ UINT retLen = ::GetSystemDirectory(fullPath.data(), MAX_PATH);
+ if (retLen > MAX_PATH) {
+ fullPath.resize(retLen);
+ retLen = ::GetSystemDirectory(fullPath.data(), retLen);
+ }
+ // in some rare cases retLen might be 0
+ return QString::fromWCharArray(fullPath.constData(), int(retLen));
+}
+
+HINSTANCE QSystemLibrary::load(const wchar_t *libraryName, bool onlySystemDirectory /* = true */)
+{
+ QStringList searchOrder;
+
+#if !defined(QT_BOOTSTRAPPED)
+ if (!onlySystemDirectory)
+ searchOrder << QFileInfo(qAppFileName()).path();
+#endif
+ searchOrder << qSystemDirectory();
+
+ if (!onlySystemDirectory) {
+ const QString PATH(QLatin1String(qgetenv("PATH").constData()));
+ searchOrder << PATH.split(QLatin1Char(';'), QString::SkipEmptyParts);
+ }
+ QString fileName = QString::fromWCharArray(libraryName);
+ fileName.append(QLatin1String(".dll"));
+
+ // Start looking in the order specified
+ for (int i = 0; i < searchOrder.count(); ++i) {
+ QString fullPathAttempt = searchOrder.at(i);
+ if (!fullPathAttempt.endsWith(QLatin1Char('\\'))) {
+ fullPathAttempt.append(QLatin1Char('\\'));
+ }
+ fullPathAttempt.append(fileName);
+ HINSTANCE inst = ::LoadLibrary((const wchar_t *)fullPathAttempt.utf16());
+ if (inst != 0)
+ return inst;
+ }
+ return 0;
+
+}
+
+#endif //Q_OS_WINCE
+
+QT_END_NAMESPACE
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/qsystemlibrary.h Thu Sep 19 16:09:24 2013 +0100
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSYSTEMLIBRARY_P_H
+#define QSYSTEMLIBRARY_P_H
+
+#include <QtCore/qglobal.h>
+#ifdef Q_OS_WIN
+# include <QtCore/qstring.h>
+# include <qt_windows.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSystemLibrary
+{
+public:
+ explicit QSystemLibrary(const QString &libraryName)
+ {
+ m_libraryName = libraryName;
+ m_handle = 0;
+ m_didLoad = false;
+ }
+
+ explicit QSystemLibrary(const wchar_t *libraryName)
+ {
+ m_libraryName = QString::fromWCharArray(libraryName);
+ m_handle = 0;
+ m_didLoad = false;
+ }
+
+ bool load(bool onlySystemDirectory = true)
+ {
+ m_handle = load((const wchar_t *)m_libraryName.utf16(), onlySystemDirectory);
+ m_didLoad = true;
+ return (m_handle != 0);
+ }
+
+ bool isLoaded()
+ {
+ return (m_handle != 0);
+ }
+
+ QFunctionPointer resolve(const char *symbol)
+ {
+ if (!m_didLoad)
+ load();
+ if (!m_handle)
+ return 0;
+#ifdef Q_OS_WINCE
+ return QFunctionPointer(GetProcAddress(m_handle, (const wchar_t*)QString::fromLatin1(symbol).utf16()));
+#else
+ return QFunctionPointer(GetProcAddress(m_handle, symbol));
+#endif
+ }
+
+ static QFunctionPointer resolve(const QString &libraryName, const char *symbol)
+ {
+ return QSystemLibrary(libraryName).resolve(symbol);
+ }
+
+ static HINSTANCE load(const wchar_t *lpFileName, bool onlySystemDirectory = true);
+private:
+ HINSTANCE m_handle;
+ QString m_libraryName;
+ bool m_didLoad;
+};
+
+QT_END_NAMESPACE
+
+#endif //Q_OS_WIN
+
+#endif //QSYSTEMLIBRARY_P_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/quailglibthread.cpp Thu Sep 19 16:09:24 2013 +0100
@@ -0,0 +1,65 @@
+#include "quailglibthread.h"
+
+QuailThread::QuailThread(QObject *parent) :
+ QThread(parent)
+{
+
+}
+
+QuailThread::~QuailThread()
+{
+ stop();
+}
+
+bool QuailThread::start()
+{
+ QMutexLocker locker(&m);
+ QThread::start();
+ w.wait(&m);
+ return success;
+}
+
+void QuailThread::stop()
+{
+ QMutexLocker locker(&m);
+ if(mainLoop)
+ {
+ // thread-safe ?
+ g_main_loop_quit(mainLoop);
+ w.wait(&m);
+ }
+
+ wait();
+}
+
+GMainContext *QuailThread::mainContext()
+{
+ QMutexLocker locker(&m);
+ return mainContext;
+}
+
+void QuailThread::run()
+{
+ // this will be unlocked as soon as the mainloop runs
+ m.lock();
+
+ success = true;
+
+ mainContext = g_main_context_new();
+ mainLoop = g_main_loop_new(mainContext, FALSE);
+
+ // deferred call to loop_started()
+ GSource *timer = g_timeout_source_new(0);
+ g_source_attach(timer, mainContext);
+ g_source_set_callback(timer, cb_loop_started, d, NULL);
+
+ // kick off the event loop
+ g_main_loop_run(mainLoop);
+
+ QMutexLocker locker(&m);
+ g_main_loop_unref(mainLoop);
+ mainLoop = 0;
+ g_main_context_unref(mainContext);
+ mainContext = 0;
+ w.wakeOne();
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/quailglibthread.h Thu Sep 19 16:09:24 2013 +0100
@@ -0,0 +1,46 @@
+#ifndef QUAILGLIBTHREAD_H
+#define QUAILGLIBTHREAD_H
+
+#include <QThread>
+#include <QMutex>
+#include <QWaitCondition>
+#include <glib/gmain.h>
+
+
+class QuailGlibThread : public QThread
+{
+ Q_OBJECT
+
+public:
+ QuailThread(QObject *parent = 0);
+ ~QuailThread();
+
+ bool start();
+ void stop();
+ void run();
+
+ GMainContext *mainContext();
+
+protected:
+ bool success;
+ GMainContext *mainContext;
+ GMainLoop *mainLoop;
+ QMutex m;
+ QWaitCondition w;
+
+ static gboolean cb_loop_started(gpointer data)
+ {
+ return ((Private *)data)->loop_started();
+ }
+
+ gboolean loop_started()
+ {
+ w.wakeOne();
+ m.unlock();
+ return FALSE;
+ }
+
+};
+
+
+#endif // QUAILGLIBTHREAD_H
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/quailtimerinfolist.cpp Thu Sep 19 16:09:24 2013 +0100
@@ -0,0 +1,649 @@
+#include "quailtimerinfolist.h"
+
+#include <qelapsedtimer.h>
+#include <qcoreapplication.h>
+
+//#include "private/qcore_unix_p.h"
+//#include "private/qtimerinfo_unix_p.h"
+//#include "private/qobject_p.h"
+//#include "private/qabstracteventdispatcher_p.h"
+#include <QtCore>
+#include <QTimerEvent>
+#include <QObject>
+#include <QAbstractEventDispatcher>
+
+#include <QtCore/QElapsedTimer>
+
+#ifdef QTIMERINFO_DEBUG
+# include <QDebug>
+# include <QThread>
+#endif
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+static inline void qt_clock_gettime(int, struct timespec *ts)
+{
+ // support clock_gettime with gettimeofday
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ ts->tv_sec = tv.tv_sec;
+ ts->tv_nsec = tv.tv_usec * 1000;
+}
+
+static inline void do_gettime(qint64 *sec, qint64 *frac)
+{
+ timespec ts;
+ qt_clock_gettime(0, &ts);
+ *sec = ts.tv_sec;
+ *frac = ts.tv_nsec;
+}
+
+// used in qcore_unix.cpp and qeventdispatcher_unix.cpp
+struct timespec qt_gettime() Q_DECL_NOTHROW
+{
+ qint64 sec, frac;
+ do_gettime(&sec, &frac);
+
+ timespec tv;
+ tv.tv_sec = sec;
+ tv.tv_nsec = frac;
+
+ return tv;
+}
+
+bool qt_disable_lowpriority_timers=false;
+
+/*
+ * Internal functions for manipulating timer data structures. The
+ * timerBitVec array is used for keeping track of timer identifiers.
+ */
+
+QTimerInfoList::QTimerInfoList()
+{
+#if (_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(Q_OS_MAC) && !defined(Q_OS_NACL)
+ if (!QElapsedTimer::isMonotonic()) {
+ // not using monotonic timers, initialize the timeChanged() machinery
+ previousTime = qt_gettime();
+
+ tms unused;
+ previousTicks = times(&unused);
+
+ ticksPerSecond = sysconf(_SC_CLK_TCK);
+ msPerTick = 1000/ticksPerSecond;
+ } else {
+ // detected monotonic timers
+ previousTime.tv_sec = previousTime.tv_nsec = 0;
+ previousTicks = 0;
+ ticksPerSecond = 0;
+ msPerTick = 0;
+ }
+#endif
+
+ firstTimerInfo = 0;
+}
+
+timespec QTimerInfoList::updateCurrentTime()
+{
+ return (currentTime = qt_gettime());
+}
+
+#if ((_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(Q_OS_MAC) && !defined(Q_OS_INTEGRITY)) || defined(QT_BOOTSTRAPPED)
+
+timespec qAbsTimespec(const timespec &t)
+{
+ timespec tmp = t;
+ if (tmp.tv_sec < 0) {
+ tmp.tv_sec = -tmp.tv_sec - 1;
+ tmp.tv_nsec -= 1000000000;
+ }
+ if (tmp.tv_sec == 0 && tmp.tv_nsec < 0) {
+ tmp.tv_nsec = -tmp.tv_nsec;
+ }
+ return normalizedTimespec(tmp);
+}
+
+/*
+ Returns true if the real time clock has changed by more than 10%
+ relative to the processor time since the last time this function was
+ called. This presumably means that the system time has been changed.
+
+ If /a delta is nonzero, delta is set to our best guess at how much the system clock was changed.
+*/
+bool QTimerInfoList::timeChanged(timespec *delta)
+{
+#ifdef Q_OS_NACL
+ Q_UNUSED(delta)
+ return false; // Calling "times" crashes.
+#endif
+ struct tms unused;
+ clock_t currentTicks = times(&unused);
+
+ clock_t elapsedTicks = currentTicks - previousTicks;
+ timespec elapsedTime = currentTime - previousTime;
+
+ timespec elapsedTimeTicks;
+ elapsedTimeTicks.tv_sec = elapsedTicks / ticksPerSecond;
+ elapsedTimeTicks.tv_nsec = (((elapsedTicks * 1000) / ticksPerSecond) % 1000) * 1000 * 1000;
+
+ timespec dummy;
+ if (!delta)
+ delta = &dummy;
+ *delta = elapsedTime - elapsedTimeTicks;
+
+ previousTicks = currentTicks;
+ previousTime = currentTime;
+
+ // If tick drift is more than 10% off compared to realtime, we assume that the clock has
+ // been set. Of course, we have to allow for the tick granularity as well.
+ timespec tickGranularity;
+ tickGranularity.tv_sec = 0;
+ tickGranularity.tv_nsec = msPerTick * 1000 * 1000;
+ return elapsedTimeTicks < ((qAbsTimespec(*delta) - tickGranularity) * 10);
+}
+
+/*
+ repair broken timer
+*/
+void QTimerInfoList::timerRepair(const timespec &diff)
+{
+ // repair all timers
+ for (int i = 0; i < size(); ++i) {
+ QTimerInfo *t = at(i);
+ t->timeout = t->timeout + diff;
+ }
+}
+
+void QTimerInfoList::repairTimersIfNeeded()
+{
+ if (QElapsedTimer::isMonotonic())
+ return;
+ timespec delta;
+ if (timeChanged(&delta))
+ timerRepair(delta);
+}
+
+#else // !(_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(QT_BOOTSTRAPPED)
+
+void QTimerInfoList::repairTimersIfNeeded()
+{
+}
+
+#endif
+
+/*
+ insert timer info into list
+*/
+void QTimerInfoList::timerInsert(QTimerInfo *ti)
+{
+ int index = size();
+ while (index--) {
+ const QTimerInfo * const t = at(index);
+ if (!(ti->timeout < t->timeout))
+ break;
+ }
+ insert(index+1, ti);
+}
+
+inline timespec &operator+=(timespec &t1, int ms)
+{
+ t1.tv_sec += ms / 1000;
+ t1.tv_nsec += ms % 1000 * 1000 * 1000;
+ return normalizedTimespec(t1);
+}
+
+inline timespec operator+(const timespec &t1, int ms)
+{
+ timespec t2 = t1;
+ return t2 += ms;
+}
+
+static timespec roundToMillisecond(timespec val)
+{
+ // always round up
+ // worst case scenario is that the first trigger of a 1-ms timer is 0.999 ms late
+
+ int ns = val.tv_nsec % (1000 * 1000);
+ val.tv_nsec += 1000 * 1000 - ns;
+ return normalizedTimespec(val);
+}
+
+#ifdef QTIMERINFO_DEBUG
+QDebug operator<<(QDebug s, timeval tv)
+{
+ s.nospace() << tv.tv_sec << "." << qSetFieldWidth(6) << qSetPadChar(QChar(48)) << tv.tv_usec << reset;
+ return s.space();
+}
+QDebug operator<<(QDebug s, Qt::TimerType t)
+{
+ s << (t == Qt::PreciseTimer ? "P" :
+ t == Qt::CoarseTimer ? "C" : "VC");
+ return s;
+}
+#endif
+
+static void calculateCoarseTimerTimeout(QTimerInfo *t, timespec currentTime)
+{
+ // The coarse timer works like this:
+ // - interval under 40 ms: round to even
+ // - between 40 and 99 ms: round to multiple of 4
+ // - otherwise: try to wake up at a multiple of 25 ms, with a maximum error of 5%
+ //
+ // We try to wake up at the following second-fraction, in order of preference:
+ // 0 ms
+ // 500 ms
+ // 250 ms or 750 ms
+ // 200, 400, 600, 800 ms
+ // other multiples of 100
+ // other multiples of 50
+ // other multiples of 25
+ //
+ // The objective is to make most timers wake up at the same time, thereby reducing CPU wakeups.
+
+ uint interval = uint(t->interval);
+ uint msec = uint(t->timeout.tv_nsec) / 1000 / 1000;
+ Q_ASSERT(interval >= 20);
+
+ // Calculate how much we can round and still keep within 5% error
+ uint absMaxRounding = interval / 20;
+
+ if (interval < 100 && interval != 25 && interval != 50 && interval != 75) {
+ // special mode for timers of less than 100 ms
+ if (interval < 50) {
+ // round to even
+ // round towards multiples of 50 ms
+ bool roundUp = (msec % 50) >= 25;
+ msec >>= 1;
+ msec |= uint(roundUp);
+ msec <<= 1;
+ } else {
+ // round to multiple of 4
+ // round towards multiples of 100 ms
+ bool roundUp = (msec % 100) >= 50;
+ msec >>= 2;
+ msec |= uint(roundUp);
+ msec <<= 2;
+ }
+ } else {
+ uint min = qMax<int>(0, msec - absMaxRounding);
+ uint max = qMin(1000u, msec + absMaxRounding);
+
+ // find the boundary that we want, according to the rules above
+ // extra rules:
+ // 1) whatever the interval, we'll take any round-to-the-second timeout
+ if (min == 0) {
+ msec = 0;
+ goto recalculate;
+ } else if (max == 1000) {
+ msec = 1000;
+ goto recalculate;
+ }
+
+ uint wantedBoundaryMultiple;
+
+ // 2) if the interval is a multiple of 500 ms and > 5000 ms, we'll always round
+ // towards a round-to-the-second
+ // 3) if the interval is a multiple of 500 ms, we'll round towards the nearest
+ // multiple of 500 ms
+ if ((interval % 500) == 0) {
+ if (interval >= 5000) {
+ msec = msec >= 500 ? max : min;
+ goto recalculate;
+ } else {
+ wantedBoundaryMultiple = 500;
+ }
+ } else if ((interval % 50) == 0) {
+ // 4) same for multiples of 250, 200, 100, 50
+ uint mult50 = interval / 50;
+ if ((mult50 % 4) == 0) {
+ // multiple of 200
+ wantedBoundaryMultiple = 200;
+ } else if ((mult50 % 2) == 0) {
+ // multiple of 100
+ wantedBoundaryMultiple = 100;
+ } else if ((mult50 % 5) == 0) {
+ // multiple of 250
+ wantedBoundaryMultiple = 250;
+ } else {
+ // multiple of 50
+ wantedBoundaryMultiple = 50;
+ }
+ } else {
+ wantedBoundaryMultiple = 25;
+ }
+
+ uint base = msec / wantedBoundaryMultiple * wantedBoundaryMultiple;
+ uint middlepoint = base + wantedBoundaryMultiple / 2;
+ if (msec < middlepoint)
+ msec = qMax(base, min);
+ else
+ msec = qMin(base + wantedBoundaryMultiple, max);
+ }
+
+recalculate:
+ if (msec == 1000u) {
+ ++t->timeout.tv_sec;
+ t->timeout.tv_nsec = 0;
+ } else {
+ t->timeout.tv_nsec = msec * 1000 * 1000;
+ }
+
+ if (t->timeout < currentTime)
+ t->timeout += interval;
+}
+
+static void calculateNextTimeout(QTimerInfo *t, timespec currentTime)
+{
+ switch (t->timerType) {
+ case Qt::PreciseTimer:
+ case Qt::CoarseTimer:
+ t->timeout += t->interval;
+ if (t->timeout < currentTime) {
+ t->timeout = currentTime;
+ t->timeout += t->interval;
+ }
+#ifdef QTIMERINFO_DEBUG
+ t->expected += t->interval;
+ if (t->expected < currentTime) {
+ t->expected = currentTime;
+ t->expected += t->interval;
+ }
+#endif
+ if (t->timerType == Qt::CoarseTimer)
+ calculateCoarseTimerTimeout(t, currentTime);
+ return;
+
+ case Qt::VeryCoarseTimer:
+ // we don't need to take care of the microsecond component of t->interval
+ t->timeout.tv_sec += t->interval;
+ if (t->timeout.tv_sec <= currentTime.tv_sec)
+ t->timeout.tv_sec = currentTime.tv_sec + t->interval;
+#ifdef QTIMERINFO_DEBUG
+ t->expected.tv_sec += t->interval;
+ if (t->expected.tv_sec <= currentTime.tv_sec)
+ t->expected.tv_sec = currentTime.tv_sec + t->interval;
+#endif
+ return;
+ }
+
+#ifdef QTIMERINFO_DEBUG
+ if (t->timerType != Qt::PreciseTimer)
+ qDebug() << "timer" << t->timerType << hex << t->id << dec << "interval" << t->interval
+ << "originally expected at" << t->expected << "will fire at" << t->timeout
+ << "or" << (t->timeout - t->expected) << "s late";
+#endif
+}
+
+/*
+ Returns the time to wait for the next timer, or null if no timers
+ are waiting.
+*/
+bool QTimerInfoList::timerWait(timespec &tm)
+{
+ timespec currentTime = updateCurrentTime();
+ repairTimersIfNeeded();
+
+ // Find first waiting timer not already active
+ QTimerInfo *t = 0;
+ for (QTimerInfoList::const_iterator it = constBegin(); it != constEnd(); ++it) {
+ if (!(*it)->activateRef) {
+ t = *it;
+ break;
+ }
+ }
+
+ if (!t)
+ return false;
+
+ if (currentTime < t->timeout) {
+ // time to wait
+ tm = roundToMillisecond(t->timeout - currentTime);
+ } else {
+ // no time to wait
+ tm.tv_sec = 0;
+ tm.tv_nsec = 0;
+ }
+
+ return true;
+}
+
+/*
+ Returns the timer's remaining time in milliseconds with the given timerId, or
+ null if there is nothing left. If the timer id is not found in the list, the
+ returned value will be -1. If the timer is overdue, the returned value will be 0.
+*/
+int QTimerInfoList::timerRemainingTime(int timerId)
+{
+ timespec currentTime = updateCurrentTime();
+ repairTimersIfNeeded();
+ timespec tm = {0, 0};
+
+ for (int i = 0; i < count(); ++i) {
+ QTimerInfo *t = at(i);
+ if (t->id == timerId) {
+ if (currentTime < t->timeout) {
+ // time to wait
+ tm = roundToMillisecond(t->timeout - currentTime);
+ return tm.tv_sec*1000 + tm.tv_nsec/1000/1000;
+ } else {
+ return 0;
+ }
+ }
+ }
+
+#ifndef QT_NO_DEBUG
+ qWarning("QTimerInfoList::timerRemainingTime: timer id %i not found", timerId);
+#endif
+
+ return -1;
+}
+
+void QTimerInfoList::registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *object)
+{
+ QTimerInfo *t = new QTimerInfo;
+ t->id = timerId;
+ t->interval = interval;
+ t->timerType = timerType;
+ t->obj = object;
+ t->activateRef = 0;
+
+ timespec expected = updateCurrentTime() + interval;
+
+ switch (timerType) {
+ case Qt::PreciseTimer:
+ // high precision timer is based on millisecond precision
+ // so no adjustment is necessary
+ t->timeout = expected;
+ break;
+
+ case Qt::CoarseTimer:
+ // this timer has up to 5% coarseness
+ // so our boundaries are 20 ms and 20 s
+ // below 20 ms, 5% inaccuracy is below 1 ms, so we convert to high precision
+ // above 20 s, 5% inaccuracy is above 1 s, so we convert to VeryCoarseTimer
+ if (interval >= 20000) {
+ t->timerType = Qt::VeryCoarseTimer;
+ // fall through
+ } else {
+ t->timeout = expected;
+ if (interval <= 20) {
+ t->timerType = Qt::PreciseTimer;
+ // no adjustment is necessary
+ } else if (interval <= 20000) {
+ calculateCoarseTimerTimeout(t, currentTime);
+ }
+ break;
+ }
+ // fall through
+ case Qt::VeryCoarseTimer:
+ // the very coarse timer is based on full second precision,
+ // so we keep the interval in seconds (round to closest second)
+ t->interval /= 500;
+ t->interval += 1;
+ t->interval >>= 1;
+ t->timeout.tv_sec = currentTime.tv_sec + t->interval;
+ t->timeout.tv_nsec = 0;
+
+ // if we're past the half-second mark, increase the timeout again
+ if (currentTime.tv_nsec > 500*1000*1000)
+ ++t->timeout.tv_sec;
+ }
+
+ timerInsert(t);
+
+#ifdef QTIMERINFO_DEBUG
+ t->expected = expected;
+ t->cumulativeError = 0;
+ t->count = 0;
+ if (t->timerType != Qt::PreciseTimer)
+ qDebug() << "timer" << t->timerType << hex <<t->id << dec << "interval" << t->interval << "expected at"
+ << t->expected << "will fire first at" << t->timeout;
+#endif
+}
+
+bool QTimerInfoList::unregisterTimer(int timerId)
+{
+ // set timer inactive
+ for (int i = 0; i < count(); ++i) {
+ QTimerInfo *t = at(i);
+ if (t->id == timerId) {
+ // found it
+ removeAt(i);
+ if (t == firstTimerInfo)
+ firstTimerInfo = 0;
+ if (t->activateRef)
+ *(t->activateRef) = 0;
+ delete t;
+ return true;
+ }
+ }
+ // id not found
+ return false;
+}
+
+bool QTimerInfoList::unregisterTimers(QObject *object)
+{
+ if (isEmpty())
+ return false;
+ for (int i = 0; i < count(); ++i) {
+ QTimerInfo *t = at(i);
+ if (t->obj == object) {
+ // object found
+ removeAt(i);
+ if (t == firstTimerInfo)
+ firstTimerInfo = 0;
+ if (t->activateRef)
+ *(t->activateRef) = 0;
+ delete t;
+ // move back one so that we don't skip the new current item
+ --i;
+ }
+ }
+ return true;
+}
+
+QList<QAbstractEventDispatcher::TimerInfo> QTimerInfoList::registeredTimers(QObject *object) const
+{
+ QList<QAbstractEventDispatcher::TimerInfo> list;
+ for (int i = 0; i < count(); ++i) {
+ const QTimerInfo * const t = at(i);
+ if (t->obj == object) {
+ list << QAbstractEventDispatcher::TimerInfo(t->id,
+ (t->timerType == Qt::VeryCoarseTimer
+ ? t->interval * 1000
+ : t->interval),
+ t->timerType);
+ }
+ }
+ return list;
+}
+
+/*
+ Activate pending timers, returning how many where activated.
+*/
+int QTimerInfoList::activateTimers()
+{
+ if (qt_disable_lowpriority_timers || isEmpty())
+ return 0; // nothing to do
+
+ int n_act = 0, maxCount = 0;
+ firstTimerInfo = 0;
+
+ timespec currentTime = updateCurrentTime();
+ // qDebug() << "Thread" << QThread::currentThreadId() << "woken up at" << currentTime;
+ repairTimersIfNeeded();
+
+
+ // Find out how many timer have expired
+ for (QTimerInfoList::const_iterator it = constBegin(); it != constEnd(); ++it) {
+ if (currentTime < (*it)->timeout)
+ break;
+ maxCount++;
+ }
+
+ //fire the timers.
+ while (maxCount--) {
+ if (isEmpty())
+ break;
+
+ QTimerInfo *currentTimerInfo = first();
+ if (currentTime < currentTimerInfo->timeout)
+ break; // no timer has expired
+
+ if (!firstTimerInfo) {
+ firstTimerInfo = currentTimerInfo;
+ } else if (firstTimerInfo == currentTimerInfo) {
+ // avoid sending the same timer multiple times
+ break;
+ } else if (currentTimerInfo->interval < firstTimerInfo->interval
+ || currentTimerInfo->interval == firstTimerInfo->interval) {
+ firstTimerInfo = currentTimerInfo;
+ }
+
+ // remove from list
+ removeFirst();
+
+#ifdef QTIMERINFO_DEBUG
+ float diff;
+ if (currentTime < currentTimerInfo->expected) {
+ // early
+ timeval early = currentTimerInfo->expected - currentTime;
+ diff = -(early.tv_sec + early.tv_usec / 1000000.0);
+ } else {
+ timeval late = currentTime - currentTimerInfo->expected;
+ diff = late.tv_sec + late.tv_usec / 1000000.0;
+ }
+ currentTimerInfo->cumulativeError += diff;
+ ++currentTimerInfo->count;
+ if (currentTimerInfo->timerType != Qt::PreciseTimer)
+ qDebug() << "timer" << currentTimerInfo->timerType << hex << currentTimerInfo->id << dec << "interval"
+ << currentTimerInfo->interval << "firing at" << currentTime
+ << "(orig" << currentTimerInfo->expected << "scheduled at" << currentTimerInfo->timeout
+ << ") off by" << diff << "activation" << currentTimerInfo->count
+ << "avg error" << (currentTimerInfo->cumulativeError / currentTimerInfo->count);
+#endif
+
+ // determine next timeout time
+ calculateNextTimeout(currentTimerInfo, currentTime);
+
+ // reinsert timer
+ timerInsert(currentTimerInfo);
+ if (currentTimerInfo->interval > 0)
+ n_act++;
+
+ if (!currentTimerInfo->activateRef) {
+ // send event, but don't allow it to recurse
+ currentTimerInfo->activateRef = &currentTimerInfo;
+
+ QTimerEvent e(currentTimerInfo->id);
+ QCoreApplication::sendEvent(currentTimerInfo->obj, &e);
+
+ if (currentTimerInfo)
+ currentTimerInfo->activateRef = 0;
+ }
+ }
+
+ firstTimerInfo = 0;
+ // qDebug() << "Thread" << QThread::currentThreadId() << "activated" << n_act << "timers";
+ return n_act;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/quailtimerinfolist.h Thu Sep 19 16:09:24 2013 +0100
@@ -0,0 +1,120 @@
+#ifndef QUAILTIMERINFOLIST_H
+#define QUAILTIMERINFOLIST_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+// #define QTIMERINFO_DEBUG
+
+#include "qabstracteventdispatcher.h"
+
+#include <sys/time.h> // struct timeval
+
+// Internal operator functions for timespecs
+inline timespec &normalizedTimespec(timespec &t)
+{
+ while (t.tv_nsec >= 1000000000) {
+ ++t.tv_sec;
+ t.tv_nsec -= 1000000000;
+ }
+ while (t.tv_nsec < 0) {
+ --t.tv_sec;
+ t.tv_nsec += 1000000000;
+ }
+ return t;
+}
+inline bool operator<(const timespec &t1, const timespec &t2)
+{ return t1.tv_sec < t2.tv_sec || (t1.tv_sec == t2.tv_sec && t1.tv_nsec < t2.tv_nsec); }
+inline bool operator==(const timespec &t1, const timespec &t2)
+{ return t1.tv_sec == t2.tv_sec && t1.tv_nsec == t2.tv_nsec; }
+inline timespec &operator+=(timespec &t1, const timespec &t2)
+{
+ t1.tv_sec += t2.tv_sec;
+ t1.tv_nsec += t2.tv_nsec;
+ return normalizedTimespec(t1);
+}
+inline timespec operator+(const timespec &t1, const timespec &t2)
+{
+ timespec tmp;
+ tmp.tv_sec = t1.tv_sec + t2.tv_sec;
+ tmp.tv_nsec = t1.tv_nsec + t2.tv_nsec;
+ return normalizedTimespec(tmp);
+}
+inline timespec operator-(const timespec &t1, const timespec &t2)
+{
+ timespec tmp;
+ tmp.tv_sec = t1.tv_sec - (t2.tv_sec - 1);
+ tmp.tv_nsec = t1.tv_nsec - (t2.tv_nsec + 1000000000);
+ return normalizedTimespec(tmp);
+}
+inline timespec operator*(const timespec &t1, int mul)
+{
+ timespec tmp;
+ tmp.tv_sec = t1.tv_sec * mul;
+ tmp.tv_nsec = t1.tv_nsec * mul;
+ return normalizedTimespec(tmp);
+}
+
+
+// internal timer info
+struct QTimerInfo {
+ int id; // - timer identifier
+ int interval; // - timer interval in milliseconds
+ Qt::TimerType timerType; // - timer type
+ timespec timeout; // - when to actually fire
+ QObject *obj; // - object to receive event
+ QTimerInfo **activateRef; // - ref from activateTimers
+
+#ifdef QTIMERINFO_DEBUG
+ timeval expected; // when timer is expected to fire
+ float cumulativeError;
+ uint count;
+#endif
+};
+
+class QTimerInfoList : public QList<QTimerInfo*>
+{
+#if ((_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(Q_OS_MAC)) || defined(QT_BOOTSTRAPPED)
+ timespec previousTime;
+ clock_t previousTicks;
+ int ticksPerSecond;
+ int msPerTick;
+
+ bool timeChanged(timespec *delta);
+ void timerRepair(const timespec &);
+#endif
+
+ // state variables used by activateTimers()
+ QTimerInfo *firstTimerInfo;
+
+public:
+ QTimerInfoList();
+
+ timespec currentTime;
+ timespec updateCurrentTime();
+
+ // must call updateCurrentTime() first!
+ void repairTimersIfNeeded();
+
+ bool timerWait(timespec &);
+ void timerInsert(QTimerInfo *);
+
+ int timerRemainingTime(int timerId);
+
+ void registerTimer(int timerId, int interval, Qt::TimerType timerType, QObject *object);
+ bool unregisterTimer(int timerId);
+ bool unregisterTimers(QObject *object);
+ QList<QAbstractEventDispatcher::TimerInfo> registeredTimers(QObject *object) const;
+
+ int activateTimers();
+};
+
+#endif // QUAILTIMERINFOLIST_H