diff options
Diffstat (limited to 'src/corelib')
-rw-r--r-- | src/corelib/io/qdatastream.cpp | 59 | ||||
-rw-r--r-- | src/corelib/io/qdatastream.h | 3 | ||||
-rw-r--r-- | src/corelib/io/qprocess_unix.cpp | 30 | ||||
-rw-r--r-- | src/corelib/io/qtextstream.cpp | 48 | ||||
-rw-r--r-- | src/corelib/io/qtextstream.h | 3 | ||||
-rw-r--r-- | src/corelib/kernel/qcoreapplication.cpp | 18 | ||||
-rw-r--r-- | src/corelib/kernel/qcoreapplication.h | 1 | ||||
-rw-r--r-- | src/corelib/kernel/qcoreapplication_p.h | 2 | ||||
-rw-r--r-- | src/corelib/thread/qmutex.cpp | 107 | ||||
-rw-r--r-- | src/corelib/thread/qmutex_p.h | 14 | ||||
-rw-r--r-- | src/corelib/thread/qmutex_unix.cpp | 91 | ||||
-rw-r--r-- | src/corelib/thread/qmutex_win.cpp | 10 | ||||
-rw-r--r-- | src/corelib/tools/qelapsedtimer.h | 1 | ||||
-rw-r--r-- | src/corelib/tools/qelapsedtimer_generic.cpp | 16 | ||||
-rw-r--r-- | src/corelib/tools/qelapsedtimer_mac.cpp | 6 | ||||
-rw-r--r-- | src/corelib/tools/qelapsedtimer_symbian.cpp | 22 | ||||
-rw-r--r-- | src/corelib/tools/qelapsedtimer_unix.cpp | 11 | ||||
-rw-r--r-- | src/corelib/tools/qelapsedtimer_win.cpp | 20 | ||||
-rw-r--r-- | src/corelib/xml/qxmlstream.cpp | 76 | ||||
-rw-r--r-- | src/corelib/xml/qxmlstream.h | 2 |
20 files changed, 382 insertions, 158 deletions
diff --git a/src/corelib/io/qdatastream.cpp b/src/corelib/io/qdatastream.cpp index 58f0190..73ce490 100644 --- a/src/corelib/io/qdatastream.cpp +++ b/src/corelib/io/qdatastream.cpp @@ -223,6 +223,7 @@ QT_BEGIN_NAMESPACE \value ReadPastEnd The data stream has read past the end of the data in the underlying device. \value ReadCorruptData The data stream has read corrupt data. + \value WriteFailed The data stream cannot write to the underlying device. */ /***************************************************************************** @@ -243,6 +244,11 @@ QT_BEGIN_NAMESPACE } #endif +#define CHECK_STREAM_WRITE_PRECOND(retVal) \ + CHECK_STREAM_PRECOND(retVal) \ + if (q_status != Ok) \ + return retVal; + enum { DefaultStreamVersion = QDataStream::Qt_4_6 }; @@ -495,6 +501,9 @@ void QDataStream::resetStatus() /*! Sets the status of the data stream to the \a status given. + Subsequent calls to setStatus() are ignored until resetStatus() + is called. + \sa Status status() resetStatus() */ void QDataStream::setStatus(Status status) @@ -992,8 +1001,9 @@ int QDataStream::readRawData(char *s, int len) QDataStream &QDataStream::operator<<(qint8 i) { - CHECK_STREAM_PRECOND(*this) - dev->putChar(i); + CHECK_STREAM_WRITE_PRECOND(*this) + if (!dev->putChar(i)) + q_status = WriteFailed; return *this; } @@ -1015,11 +1025,12 @@ QDataStream &QDataStream::operator<<(qint8 i) QDataStream &QDataStream::operator<<(qint16 i) { - CHECK_STREAM_PRECOND(*this) + CHECK_STREAM_WRITE_PRECOND(*this) if (!noswap) { i = qbswap(i); } - dev->write((char *)&i, sizeof(qint16)); + if (dev->write((char *)&i, sizeof(qint16)) != sizeof(qint16)) + q_status = WriteFailed; return *this; } @@ -1032,11 +1043,12 @@ QDataStream &QDataStream::operator<<(qint16 i) QDataStream &QDataStream::operator<<(qint32 i) { - CHECK_STREAM_PRECOND(*this) + CHECK_STREAM_WRITE_PRECOND(*this) if (!noswap) { i = qbswap(i); } - dev->write((char *)&i, sizeof(qint32)); + if (dev->write((char *)&i, sizeof(qint32)) != sizeof(qint32)) + q_status = WriteFailed; return *this; } @@ -1057,7 +1069,7 @@ QDataStream &QDataStream::operator<<(qint32 i) QDataStream &QDataStream::operator<<(qint64 i) { - CHECK_STREAM_PRECOND(*this) + CHECK_STREAM_WRITE_PRECOND(*this) if (version() < 6) { quint32 i1 = i & 0xffffffff; quint32 i2 = i >> 32; @@ -1066,7 +1078,8 @@ QDataStream &QDataStream::operator<<(qint64 i) if (!noswap) { i = qbswap(i); } - dev->write((char *)&i, sizeof(qint64)); + if (dev->write((char *)&i, sizeof(qint64)) != sizeof(qint64)) + q_status = WriteFailed; } return *this; } @@ -1086,8 +1099,9 @@ QDataStream &QDataStream::operator<<(qint64 i) QDataStream &QDataStream::operator<<(bool i) { - CHECK_STREAM_PRECOND(*this) - dev->putChar(qint8(i)); + CHECK_STREAM_WRITE_PRECOND(*this) + if (!dev->putChar(qint8(i))) + q_status = WriteFailed; return *this; } @@ -1108,7 +1122,7 @@ QDataStream &QDataStream::operator<<(float f) return *this; } - CHECK_STREAM_PRECOND(*this) + CHECK_STREAM_WRITE_PRECOND(*this) float g = f; // fixes float-on-stack problem if (!noswap) { union { @@ -1119,7 +1133,8 @@ QDataStream &QDataStream::operator<<(float f) x.val2 = qbswap(x.val2); g = x.val1; } - dev->write((char *)&g, sizeof(float)); + if (dev->write((char *)&g, sizeof(float)) != sizeof(float)) + q_status = WriteFailed; return *this; } @@ -1141,10 +1156,11 @@ QDataStream &QDataStream::operator<<(double f) return *this; } - CHECK_STREAM_PRECOND(*this) + CHECK_STREAM_WRITE_PRECOND(*this) #ifndef Q_DOUBLE_FORMAT if (noswap) { - dev->write((char *)&f, sizeof(double)); + if (dev->write((char *)&f, sizeof(double)) != sizeof(double)) + q_status = WriteFailed; } else { union { double val1; @@ -1152,7 +1168,8 @@ QDataStream &QDataStream::operator<<(double f) } x; x.val1 = f; x.val2 = qbswap(x.val2); - dev->write((char *)&x.val2, sizeof(double)); + if (dev->write((char *)&x.val2, sizeof(double)) != sizeof(double)) + q_status = WriteFailed; } #else union { @@ -1181,7 +1198,8 @@ QDataStream &QDataStream::operator<<(double f) b[Q_DF(1)] = *p++; b[Q_DF(0)] = *p; } - dev->write(b, 8); + if (dev->write(b, 8) != 8) + q_status = WriteFailed; #endif return *this; } @@ -1221,7 +1239,7 @@ QDataStream &QDataStream::operator<<(const char *s) QDataStream &QDataStream::writeBytes(const char *s, uint len) { - CHECK_STREAM_PRECOND(*this) + CHECK_STREAM_WRITE_PRECOND(*this) *this << (quint32)len; // write length specifier if (len) writeRawData(s, len); @@ -1239,8 +1257,11 @@ QDataStream &QDataStream::writeBytes(const char *s, uint len) int QDataStream::writeRawData(const char *s, int len) { - CHECK_STREAM_PRECOND(-1) - return dev->write(s, len); + CHECK_STREAM_WRITE_PRECOND(-1) + int ret = dev->write(s, len); + if (ret != len) + q_status = WriteFailed; + return ret; } /*! diff --git a/src/corelib/io/qdatastream.h b/src/corelib/io/qdatastream.h index 774c4bc..05248ac 100644 --- a/src/corelib/io/qdatastream.h +++ b/src/corelib/io/qdatastream.h @@ -101,7 +101,8 @@ public: enum Status { Ok, ReadPastEnd, - ReadCorruptData + ReadCorruptData, + WriteFailed }; enum FloatingPointPrecision { diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp index 216c382..e52d132 100644 --- a/src/corelib/io/qprocess_unix.cpp +++ b/src/corelib/io/qprocess_unix.cpp @@ -169,17 +169,27 @@ private: Q_GLOBAL_STATIC(QMutex, processManagerGlobalMutex) -static QProcessManager *processManager() { +static QProcessManager *processManagerInstance = 0; + +static QProcessManager *processManager() +{ // The constructor of QProcessManager should be called only once // so we cannot use Q_GLOBAL_STATIC directly for QProcessManager QMutex *mutex = processManagerGlobalMutex(); QMutexLocker locker(mutex); - static QProcessManager processManager; - return &processManager; + + if (!processManagerInstance) + QProcessPrivate::initializeProcessManager(); + + Q_ASSERT(processManagerInstance); + return processManagerInstance; } QProcessManager::QProcessManager() { + // can only be called from main thread + Q_ASSERT(!qApp || qApp->thread() == QThread::currentThread()); + #if defined (QPROCESS_DEBUG) qDebug() << "QProcessManager::QProcessManager()"; #endif @@ -198,6 +208,8 @@ QProcessManager::QProcessManager() ::sigaction(SIGCHLD, &action, &oldAction); if (oldAction.sa_handler != qt_sa_sigchld_handler) qt_sa_old_sigchld_handler = oldAction.sa_handler; + + processManagerInstance = this; } QProcessManager::~QProcessManager() @@ -226,6 +238,8 @@ QProcessManager::~QProcessManager() if (oldAction.sa_handler != qt_sa_sigchld_handler) { ::sigaction(SIGCHLD, &oldAction, 0); } + + processManagerInstance = 0; } void QProcessManager::run() @@ -1289,7 +1303,15 @@ bool QProcessPrivate::startDetached(const QString &program, const QStringList &a void QProcessPrivate::initializeProcessManager() { - (void) processManager(); + if (qApp && qApp->thread() != QThread::currentThread()) { + // The process manager must be initialized in the main thread + // Note: The call below will re-enter this function, but in the right thread, + // so the else statement below will be executed. + QMetaObject::invokeMethod(qApp, "_q_initializeProcessManager", Qt::BlockingQueuedConnection); + } else { + static QProcessManager processManager; + Q_UNUSED(processManager); + } } QT_END_NAMESPACE diff --git a/src/corelib/io/qtextstream.cpp b/src/corelib/io/qtextstream.cpp index 6091ec0..3bdbf32 100644 --- a/src/corelib/io/qtextstream.cpp +++ b/src/corelib/io/qtextstream.cpp @@ -224,6 +224,7 @@ static const int QTEXTSTREAM_BUFFERSIZE = 16384; \value ReadPastEnd The text stream has read past the end of the data in the underlying device. \value ReadCorruptData The text stream has read corrupt data. + \value WriteFailed The text stream cannot write to the underlying device. \sa status() */ @@ -396,19 +397,19 @@ public: npsInvalidPrefix }; - inline bool write(const QString &data); inline bool getChar(QChar *ch); inline void ungetChar(const QChar &ch); NumberParsingStatus getNumber(qulonglong *l); bool getReal(double *f); - bool putNumber(qulonglong number, bool negative); - inline bool putString(const QString &ch, bool number = false); + inline void write(const QString &data); + inline void putString(const QString &ch, bool number = false); + void putNumber(qulonglong number, bool negative); // buffers bool fillReadBuffer(qint64 maxBytes = -1); void resetReadBuffer(); - bool flushWriteBuffer(); + void flushWriteBuffer(); QString writeBuffer; QString readBuffer; int readBufferOffset; @@ -642,14 +643,20 @@ void QTextStreamPrivate::resetReadBuffer() /*! \internal */ -bool QTextStreamPrivate::flushWriteBuffer() +void QTextStreamPrivate::flushWriteBuffer() { // no buffer next to the QString itself; this function should only // be called internally, for devices. if (string || !device) - return false; + return; + + // Stream went bye-bye already. Appending further data may succeed again, + // but would create a corrupted stream anyway. + if (status != QTextStream::Ok) + return; + if (writeBuffer.isEmpty()) - return true; + return; #if defined (Q_OS_WIN) // handle text translation and bypass the Text flag in the device. @@ -681,8 +688,10 @@ bool QTextStreamPrivate::flushWriteBuffer() qDebug("QTextStreamPrivate::flushWriteBuffer(), device->write(\"%s\") == %d", qt_prettyDebug(data.constData(), qMin(data.size(),32), data.size()).constData(), int(bytesWritten)); #endif - if (bytesWritten <= 0) - return false; + if (bytesWritten <= 0) { + status = QTextStream::WriteFailed; + return; + } #if defined (Q_OS_WIN) // replace the text flag @@ -693,7 +702,7 @@ bool QTextStreamPrivate::flushWriteBuffer() // flush the file #ifndef QT_NO_QOBJECT QFile *file = qobject_cast<QFile *>(device); - bool flushed = file && file->flush(); + bool flushed = !file || file->flush(); #else bool flushed = true; #endif @@ -702,7 +711,8 @@ bool QTextStreamPrivate::flushWriteBuffer() qDebug("QTextStreamPrivate::flushWriteBuffer() wrote %d bytes", int(bytesWritten)); #endif - return flushed && bytesWritten == qint64(data.size()); + if (!flushed || bytesWritten != qint64(data.size())) + status = QTextStream::WriteFailed; } QString QTextStreamPrivate::read(int maxlen) @@ -908,7 +918,7 @@ inline void QTextStreamPrivate::restoreToSavedConverterState() /*! \internal */ -inline bool QTextStreamPrivate::write(const QString &data) +inline void QTextStreamPrivate::write(const QString &data) { if (string) { // ### What about seek()?? @@ -916,9 +926,8 @@ inline bool QTextStreamPrivate::write(const QString &data) } else { writeBuffer += data; if (writeBuffer.size() > QTEXTSTREAM_BUFFERSIZE) - return flushWriteBuffer(); + flushWriteBuffer(); } - return true; } /*! \internal @@ -959,7 +968,7 @@ inline void QTextStreamPrivate::ungetChar(const QChar &ch) /*! \internal */ -inline bool QTextStreamPrivate::putString(const QString &s, bool number) +inline void QTextStreamPrivate::putString(const QString &s, bool number) { QString tmp = s; @@ -993,7 +1002,7 @@ inline bool QTextStreamPrivate::putString(const QString &s, bool number) qt_prettyDebug(a.constData(), a.size(), qMax(16, a.size())).constData(), qt_prettyDebug(b.constData(), b.size(), qMax(16, b.size())).constData()); #endif - return write(tmp); + write(tmp); } /*! @@ -1593,6 +1602,9 @@ void QTextStream::resetStatus() Sets the status of the text stream to the \a status given. + Subsequent calls to setStatus() are ignored until resetStatus() + is called. + \sa Status status() resetStatus() */ void QTextStream::setStatus(Status status) @@ -2267,7 +2279,7 @@ QTextStream &QTextStream::operator>>(char *c) /*! \internal */ -bool QTextStreamPrivate::putNumber(qulonglong number, bool negative) +void QTextStreamPrivate::putNumber(qulonglong number, bool negative) { QString result; @@ -2307,7 +2319,7 @@ bool QTextStreamPrivate::putNumber(qulonglong number, bool negative) result.prepend(QLatin1Char('0')); } } - return putString(result, true); + putString(result, true); } /*! diff --git a/src/corelib/io/qtextstream.h b/src/corelib/io/qtextstream.h index d82da59..c635691 100644 --- a/src/corelib/io/qtextstream.h +++ b/src/corelib/io/qtextstream.h @@ -89,7 +89,8 @@ public: enum Status { Ok, ReadPastEnd, - ReadCorruptData + ReadCorruptData, + WriteFailed }; enum NumberFlag { ShowBase = 0x1, diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index 0f95ee0..799433b 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -336,6 +336,16 @@ void QCoreApplicationPrivate::createEventDispatcher() #endif } +void QCoreApplicationPrivate::_q_initializeProcessManager() +{ +#ifndef QT_NO_PROCESS +# ifdef Q_OS_UNIX + QProcessPrivate::initializeProcessManager(); +# endif +#endif +} + + QThread *QCoreApplicationPrivate::theMainThread = 0; QThread *QCoreApplicationPrivate::mainThread() { @@ -600,12 +610,6 @@ void QCoreApplication::init() } #endif -#if defined(Q_OS_UNIX) && !(defined(QT_NO_PROCESS)) - // Make sure the process manager thread object is created in the main - // thread. - QProcessPrivate::initializeProcessManager(); -#endif - #ifdef QT_EVAL extern void qt_core_eval_init(uint); qt_core_eval_init(d->application_type); @@ -2666,3 +2670,5 @@ int QCoreApplication::loopLevel() */ QT_END_NAMESPACE + +#include "moc_qcoreapplication.cpp" diff --git a/src/corelib/kernel/qcoreapplication.h b/src/corelib/kernel/qcoreapplication.h index f1c7c26..17e784e 100644 --- a/src/corelib/kernel/qcoreapplication.h +++ b/src/corelib/kernel/qcoreapplication.h @@ -205,6 +205,7 @@ protected: QCoreApplication(QCoreApplicationPrivate &p); private: + Q_PRIVATE_SLOT(d_func(), void _q_initializeProcessManager()) static bool sendSpontaneousEvent(QObject *receiver, QEvent *event); bool notifyInternal(QObject *receiver, QEvent *event); diff --git a/src/corelib/kernel/qcoreapplication_p.h b/src/corelib/kernel/qcoreapplication_p.h index 2355c37..aafa821 100644 --- a/src/corelib/kernel/qcoreapplication_p.h +++ b/src/corelib/kernel/qcoreapplication_p.h @@ -82,6 +82,8 @@ public: bool sendThroughObjectEventFilters(QObject *, QEvent *); bool notify_helper(QObject *, QEvent *); + void _q_initializeProcessManager(); + virtual QString appName() const; virtual void createEventDispatcher(); static void removePostedEvent(QEvent *); diff --git a/src/corelib/thread/qmutex.cpp b/src/corelib/thread/qmutex.cpp index b85a22d..1009f7b 100644 --- a/src/corelib/thread/qmutex.cpp +++ b/src/corelib/thread/qmutex.cpp @@ -45,6 +45,7 @@ #ifndef QT_NO_THREAD #include "qatomic.h" +#include "qelapsedtimer.h" #include "qthread.h" #include "qmutex_p.h" @@ -157,15 +158,12 @@ void QMutex::lock() return; } - bool isLocked = d->contenders.fetchAndAddAcquire(1) == 0; + bool isLocked = d->contenders.testAndSetAcquire(0, 1); if (!isLocked) { // didn't get the lock, wait for it isLocked = d->wait(); Q_ASSERT_X(isLocked, "QMutex::lock", "Internal error, infinite wait has timed out."); - - // don't need to wait for the lock anymore - d->contenders.deref(); } d->owner = self; @@ -174,8 +172,7 @@ void QMutex::lock() return; } - - bool isLocked = d->contenders == 0 && d->contenders.testAndSetAcquire(0, 1); + bool isLocked = d->contenders.testAndSetAcquire(0, 1); if (!isLocked) { lockInternal(); } @@ -211,7 +208,7 @@ bool QMutex::tryLock() return true; } - bool isLocked = d->contenders == 0 && d->contenders.testAndSetAcquire(0, 1); + bool isLocked = d->contenders.testAndSetAcquire(0, 1); if (!isLocked) { // some other thread has the mutex locked, or we tried to // recursively lock an non-recursive mutex @@ -224,13 +221,7 @@ bool QMutex::tryLock() return isLocked; } - bool isLocked = d->contenders == 0 && d->contenders.testAndSetAcquire(0, 1); - if (!isLocked) { - // some other thread has the mutex locked, or we tried to - // recursively lock an non-recursive mutex - return isLocked; - } - return isLocked; + return d->contenders.testAndSetAcquire(0, 1); } /*! \overload @@ -269,13 +260,10 @@ bool QMutex::tryLock(int timeout) return true; } - bool isLocked = d->contenders.fetchAndAddAcquire(1) == 0; + bool isLocked = d->contenders.testAndSetAcquire(0, 1); if (!isLocked) { // didn't get the lock, wait for it isLocked = d->wait(timeout); - - // don't need to wait for the lock anymore - d->contenders.deref(); if (!isLocked) return false; } @@ -286,17 +274,9 @@ bool QMutex::tryLock(int timeout) return true; } - bool isLocked = d->contenders.fetchAndAddAcquire(1) == 0; - if (!isLocked) { - // didn't get the lock, wait for it - isLocked = d->wait(timeout); - - // don't need to wait for the lock anymore - d->contenders.deref(); - if (!isLocked) - return false; - } - return true; + return (d->contenders.testAndSetAcquire(0, 1) + // didn't get the lock, wait for it + || d->wait(timeout)); } @@ -310,7 +290,6 @@ bool QMutex::tryLock(int timeout) void QMutex::unlock() { QMutexPrivate *d = static_cast<QMutexPrivate *>(this->d); - if (d->recursive) { if (!--d->count) { d->owner = 0; @@ -451,37 +430,57 @@ void QMutex::unlock() void QMutex::lockInternal() { QMutexPrivate *d = static_cast<QMutexPrivate *>(this->d); - int spinCount = 0; - int lastSpinCount = d->lastSpinCount; - enum { AdditionalSpins = 20, SpinCountPenalizationDivisor = 4 }; - const int maximumSpinCount = lastSpinCount + AdditionalSpins; + if (QThread::idealThreadCount() == 1) { + // don't spin on single cpu machines + bool isLocked = d->wait(); + Q_ASSERT_X(isLocked, "QMutex::lock", + "Internal error, infinite wait has timed out."); + Q_UNUSED(isLocked); + return; + } + QElapsedTimer elapsedTimer; + elapsedTimer.start(); do { - if (spinCount++ > maximumSpinCount) { - // puts("spinning useless, sleeping"); - bool isLocked = d->contenders.fetchAndAddAcquire(1) == 0; - if (!isLocked) { - - // didn't get the lock, wait for it - isLocked = d->wait(); - Q_ASSERT_X(isLocked, "QMutex::lock", - "Internal error, infinite wait has timed out."); - - // don't need to wait for the lock anymore - d->contenders.deref(); + qint64 spinTime = elapsedTimer.nsecsElapsed(); + if (spinTime > d->maximumSpinTime) { + // didn't get the lock, wait for it, since we're not going to gain anything by spinning more + elapsedTimer.start(); + bool isLocked = d->wait(); + Q_ASSERT_X(isLocked, "QMutex::lock", + "Internal error, infinite wait has timed out."); + Q_UNUSED(isLocked); + + qint64 maximumSpinTime = d->maximumSpinTime; + qint64 averageWaitTime = d->averageWaitTime; + qint64 actualWaitTime = elapsedTimer.nsecsElapsed(); + if (actualWaitTime < (QMutexPrivate::MaximumSpinTimeThreshold * 3 / 2)) { + // measure the wait times + averageWaitTime = d->averageWaitTime = qMin((averageWaitTime + actualWaitTime) / 2, qint64(QMutexPrivate::MaximumSpinTimeThreshold)); } - // decrease the lastSpinCount since we didn't actually get the lock by spinning - spinCount = -d->lastSpinCount / SpinCountPenalizationDivisor; - break; + + // adjust the spin count when spinning does not benefit contention performance + if ((spinTime + actualWaitTime) - qint64(QMutexPrivate::MaximumSpinTimeThreshold) >= qint64(QMutexPrivate::MaximumSpinTimeThreshold)) { + // long waits, stop spinning + d->maximumSpinTime = 0; + } else { + // allow spinning if wait times decrease, but never spin more than the average wait time (otherwise we may perform worse) + d->maximumSpinTime = qBound(qint64(averageWaitTime * 3 / 2), maximumSpinTime / 2, qint64(QMutexPrivate::MaximumSpinTimeThreshold)); + } + return; } + // be a good citizen... yielding lets something else run if there is something to run, but may also relieve memory pressure if not + QThread::yieldCurrentThread(); } while (d->contenders != 0 || !d->contenders.testAndSetAcquire(0, 1)); - // adjust the last spin lock count - lastSpinCount = d->lastSpinCount; - d->lastSpinCount = spinCount >= 0 - ? qMax(lastSpinCount, spinCount) - : lastSpinCount + spinCount; + // spinning is working, do not change the spin time (unless we are using much less time than allowed to spin) + qint64 maximumSpinTime = d->maximumSpinTime; + qint64 spinTime = elapsedTimer.nsecsElapsed(); + if (spinTime < maximumSpinTime / 2) { + // we are using much less time than we need, adjust the limit + d->maximumSpinTime = qBound(qint64(d->averageWaitTime * 3 / 2), maximumSpinTime / 2, qint64(QMutexPrivate::MaximumSpinTimeThreshold)); + } } /*! diff --git a/src/corelib/thread/qmutex_p.h b/src/corelib/thread/qmutex_p.h index 6126423..9d40bea 100644 --- a/src/corelib/thread/qmutex_p.h +++ b/src/corelib/thread/qmutex_p.h @@ -58,6 +58,10 @@ #include <QtCore/qnamespace.h> #include <QtCore/qmutex.h> +#if defined(Q_OS_MAC) +# include <mach/semaphore.h> +#endif + QT_BEGIN_NAMESPACE class QMutexPrivate : public QMutexData { @@ -65,15 +69,19 @@ public: QMutexPrivate(QMutex::RecursionMode mode); ~QMutexPrivate(); - ulong self(); bool wait(int timeout = -1); void wakeUp(); - volatile int lastSpinCount; + // 1ms = 1000000ns + enum { MaximumSpinTimeThreshold = 1000000 }; + volatile qint64 maximumSpinTime; + volatile qint64 averageWaitTime; Qt::HANDLE owner; uint count; -#if defined(Q_OS_UNIX) +#if defined(Q_OS_MAC) + semaphore_t mach_semaphore; +#elif defined(Q_OS_UNIX) && !defined(Q_OS_LINUX) volatile bool wakeup; pthread_mutex_t mutex; pthread_cond_t cond; diff --git a/src/corelib/thread/qmutex_unix.cpp b/src/corelib/thread/qmutex_unix.cpp index 7e7ef22..0e09ea0 100644 --- a/src/corelib/thread/qmutex_unix.cpp +++ b/src/corelib/thread/qmutex_unix.cpp @@ -53,30 +53,116 @@ #undef wakeup #endif +#if defined(Q_OS_MAC) +# include <mach/mach.h> +# include <mach/task.h> +#elif defined(Q_OS_LINUX) +# include <linux/futex.h> +# include <sys/syscall.h> +# include <unistd.h> +#endif + QT_BEGIN_NAMESPACE +#if !defined(Q_OS_MAC) && !defined(Q_OS_LINUX) static void report_error(int code, const char *where, const char *what) { if (code != 0) qWarning("%s: %s failure: %s", where, what, qPrintable(qt_error_string(code))); } +#endif QMutexPrivate::QMutexPrivate(QMutex::RecursionMode mode) - : QMutexData(mode), lastSpinCount(0), owner(0), count(0), wakeup(false) + : QMutexData(mode), maximumSpinTime(MaximumSpinTimeThreshold), averageWaitTime(0), owner(0), count(0) { +#if defined(Q_OS_MAC) + kern_return_t r = semaphore_create(mach_task_self(), &mach_semaphore, SYNC_POLICY_FIFO, 0); + if (r != KERN_SUCCESS) + qWarning("QMutex: failed to create semaphore, error %d", r); +#elif !defined(Q_OS_LINUX) + wakeup = false; report_error(pthread_mutex_init(&mutex, NULL), "QMutex", "mutex init"); report_error(pthread_cond_init(&cond, NULL), "QMutex", "cv init"); +#endif } QMutexPrivate::~QMutexPrivate() { +#if defined(Q_OS_MAC) + kern_return_t r = semaphore_destroy(mach_task_self(), mach_semaphore); + if (r != KERN_SUCCESS) + qWarning("QMutex: failed to destroy semaphore, error %d", r); +#elif !defined(Q_OS_LINUX) report_error(pthread_cond_destroy(&cond), "QMutex", "cv destroy"); report_error(pthread_mutex_destroy(&mutex), "QMutex", "mutex destroy"); +#endif +} + +#if defined(Q_OS_MAC) + +bool QMutexPrivate::wait(int timeout) +{ + if (contenders.fetchAndAddAcquire(1) == 0) { + // lock acquired without waiting + return true; + } + bool returnValue; + if (timeout < 0) { + returnValue = semaphore_wait(mach_semaphore) == KERN_SUCCESS; + } else { + mach_timespec_t ts; + ts.tv_nsec = ((timeout % 1000) * 1000) * 1000; + ts.tv_sec = (timeout / 1000); + kern_return_t r = semaphore_timedwait(mach_semaphore, ts); + returnValue = r == KERN_SUCCESS; + } + contenders.deref(); + return returnValue; +} + +void QMutexPrivate::wakeUp() +{ + semaphore_signal(mach_semaphore); +} + +#elif defined(Q_OS_LINUX) + +static inline int _q_futex(volatile int *addr, int op, int val, const struct timespec *timeout, int *addr2, int val2) +{ + return syscall(SYS_futex, addr, op, val, timeout, addr2, val2); } bool QMutexPrivate::wait(int timeout) { + while (contenders.fetchAndStoreAcquire(2) > 0) { + struct timespec ts, *pts = 0; + if (timeout >= 0) { + ts.tv_nsec = ((timeout % 1000) * 1000) * 1000; + ts.tv_sec = (timeout / 1000); + pts = &ts; + } + int r = _q_futex(&contenders._q_value, FUTEX_WAIT, 2, pts, 0, 0); + if (r != 0 && errno == ETIMEDOUT) + return false; + } + return true; +} + +void QMutexPrivate::wakeUp() +{ + (void) contenders.fetchAndStoreRelease(0); + (void) _q_futex(&contenders._q_value, FUTEX_WAKE, 1, 0, 0, 0); +} + +#else // !Q_OS_MAC && !Q_OS_LINUX + +bool QMutexPrivate::wait(int timeout) +{ + if (contenders.fetchAndAddAcquire(1) == 0) { + // lock acquired without waiting + return true; + } report_error(pthread_mutex_lock(&mutex), "QMutex::lock", "mutex lock"); int errorCode = 0; while (!wakeup) { @@ -101,6 +187,7 @@ bool QMutexPrivate::wait(int timeout) } wakeup = false; report_error(pthread_mutex_unlock(&mutex), "QMutex::lock", "mutex unlock"); + contenders.deref(); return errorCode == 0; } @@ -112,6 +199,8 @@ void QMutexPrivate::wakeUp() report_error(pthread_mutex_unlock(&mutex), "QMutex::unlock", "mutex unlock"); } +#endif // !Q_OS_MAC && !Q_OS_LINUX + QT_END_NAMESPACE #endif // QT_NO_THREAD diff --git a/src/corelib/thread/qmutex_win.cpp b/src/corelib/thread/qmutex_win.cpp index a810000..a759caa 100644 --- a/src/corelib/thread/qmutex_win.cpp +++ b/src/corelib/thread/qmutex_win.cpp @@ -48,7 +48,7 @@ QT_BEGIN_NAMESPACE QMutexPrivate::QMutexPrivate(QMutex::RecursionMode mode) - : QMutexData(mode), lastSpinCount(0), owner(0), count(0) + : QMutexData(mode), maximumSpinTime(MaximumSpinTimeThreshold), averageWaitTime(0), owner(0), count(0) { event = CreateEvent(0, FALSE, FALSE, 0); if (!event) @@ -60,7 +60,13 @@ QMutexPrivate::~QMutexPrivate() bool QMutexPrivate::wait(int timeout) { - return WaitForSingleObject(event, timeout < 0 ? INFINITE : timeout) == WAIT_OBJECT_0; + if (contenders.fetchAndAddAcquire(1) == 0) { + // lock acquired without waiting + return true; + } + bool returnValue = (WaitForSingleObject(event, timeout < 0 ? INFINITE : timeout) == WAIT_OBJECT_0); + contenders.deref(); + return returnValue; } void QMutexPrivate::wakeUp() diff --git a/src/corelib/tools/qelapsedtimer.h b/src/corelib/tools/qelapsedtimer.h index b996f6a..4118389 100644 --- a/src/corelib/tools/qelapsedtimer.h +++ b/src/corelib/tools/qelapsedtimer.h @@ -68,6 +68,7 @@ public: void invalidate(); bool isValid() const; + qint64 nsecsElapsed() const; qint64 elapsed() const; bool hasExpired(qint64 timeout) const; diff --git a/src/corelib/tools/qelapsedtimer_generic.cpp b/src/corelib/tools/qelapsedtimer_generic.cpp index 85986e6..740f496 100644 --- a/src/corelib/tools/qelapsedtimer_generic.cpp +++ b/src/corelib/tools/qelapsedtimer_generic.cpp @@ -103,6 +103,22 @@ qint64 QElapsedTimer::restart() return t1 - old; } +/*! \since 4.8 + + Returns the number of nanoseconds since this QElapsedTimer was last + started. Calling this function in a QElapsedTimer that was invalidated + will result in undefined results. + + On platforms that do not provide nanosecond resolution, the value returned + will be the best estimate available. + + \sa start(), restart(), hasExpired(), invalidate() +*/ +qint64 QElapsedTimer::nsecsElapsed() const +{ + return elapsed() * 1000000; +} + /*! Returns the number of milliseconds since this QElapsedTimer was last started. Calling this function in a QElapsedTimer that was invalidated diff --git a/src/corelib/tools/qelapsedtimer_mac.cpp b/src/corelib/tools/qelapsedtimer_mac.cpp index 8fceb49..e51f8b3 100644 --- a/src/corelib/tools/qelapsedtimer_mac.cpp +++ b/src/corelib/tools/qelapsedtimer_mac.cpp @@ -97,6 +97,12 @@ qint64 QElapsedTimer::restart() return absoluteToMSecs(t1 - old); } +qint64 QElapsedTimer::nsecsElapsed() const +{ + uint64_t cpu_time = mach_absolute_time(); + return absoluteToNSecs(cpu_time - t1); +} + qint64 QElapsedTimer::elapsed() const { uint64_t cpu_time = mach_absolute_time(); diff --git a/src/corelib/tools/qelapsedtimer_symbian.cpp b/src/corelib/tools/qelapsedtimer_symbian.cpp index 038b102..7cd5f1e 100644 --- a/src/corelib/tools/qelapsedtimer_symbian.cpp +++ b/src/corelib/tools/qelapsedtimer_symbian.cpp @@ -64,11 +64,6 @@ static quint64 getMicrosecondFromTick() return nanokernel_tick_period * (val | (quint64(highdword) << 32)); } -static quint64 getMillisecondFromTick() -{ - return getMicrosecondFromTick() / 1000; -} - timeval qt_gettime() { timeval tv; @@ -91,36 +86,41 @@ bool QElapsedTimer::isMonotonic() void QElapsedTimer::start() { - t1 = getMillisecondFromTick(); + t1 = getMicrosecondFromTick(); t2 = 0; } qint64 QElapsedTimer::restart() { qint64 oldt1 = t1; - t1 = getMillisecondFromTick(); + t1 = getMicrosecondFromTick(); t2 = 0; return t1 - oldt1; } +qint64 QElapsedTimer::nsecsElapsed() const +{ + return (getMicrosecondFromTick() - t1) * 1000; +} + qint64 QElapsedTimer::elapsed() const { - return getMillisecondFromTick() - t1; + return (getMicrosecondFromTick() - t1) / 1000; } qint64 QElapsedTimer::msecsSinceReference() const { - return t1; + return t1 / 1000; } qint64 QElapsedTimer::msecsTo(const QElapsedTimer &other) const { - return other.t1 - t1; + return (other.t1 - t1) / 1000; } qint64 QElapsedTimer::secsTo(const QElapsedTimer &other) const { - return msecsTo(other) / 1000; + return msecsTo(other) / 1000000; } bool operator<(const QElapsedTimer &v1, const QElapsedTimer &v2) diff --git a/src/corelib/tools/qelapsedtimer_unix.cpp b/src/corelib/tools/qelapsedtimer_unix.cpp index 633fa00..7407e1b 100644 --- a/src/corelib/tools/qelapsedtimer_unix.cpp +++ b/src/corelib/tools/qelapsedtimer_unix.cpp @@ -167,6 +167,17 @@ qint64 QElapsedTimer::restart() return elapsedAndRestart(t1, t2, &t1, &t2); } +qint64 QElapsedTimer::nsecsElapsed() const +{ + qint64 sec, frac; + do_gettime(&sec, &frac); + sec = sec - t1; + frac = frac - t2; + if (!monotonicClockAvailable) + frac *= 1000; + return sec * Q_INT64_C(1000000000) + frac; +} + qint64 QElapsedTimer::elapsed() const { qint64 sec, frac; diff --git a/src/corelib/tools/qelapsedtimer_win.cpp b/src/corelib/tools/qelapsedtimer_win.cpp index c77acaa..3f4acc1 100644 --- a/src/corelib/tools/qelapsedtimer_win.cpp +++ b/src/corelib/tools/qelapsedtimer_win.cpp @@ -79,14 +79,14 @@ static void resolveLibs() done = true; } -static inline qint64 ticksToMilliseconds(qint64 ticks) +static inline qint64 ticksToNanoseconds(qint64 ticks) { if (counterFrequency > 0) { // QueryPerformanceCounter uses an arbitrary frequency - return ticks * 1000 / counterFrequency; + return ticks * 1000000000 / counterFrequency; } else { // GetTickCount(64) return milliseconds - return ticks; + return ticks * 1000000; } } @@ -144,24 +144,30 @@ qint64 QElapsedTimer::restart() qint64 oldt1 = t1; t1 = getTickCount(); t2 = 0; - return ticksToMilliseconds(t1 - oldt1); + return ticksToNanoseconds(t1 - oldt1) / 1000000; +} + +qint64 QElapsedTimer::nsecsElapsed() const +{ + qint64 elapsed = getTickCount() - t1; + return ticksToNanoseconds(elapsed); } qint64 QElapsedTimer::elapsed() const { qint64 elapsed = getTickCount() - t1; - return ticksToMilliseconds(elapsed); + return ticksToNanoseconds(elapsed) / 1000000; } qint64 QElapsedTimer::msecsSinceReference() const { - return ticksToMilliseconds(t1); + return ticksToNanoseconds(t1) / 1000000; } qint64 QElapsedTimer::msecsTo(const QElapsedTimer &other) const { qint64 difference = other.t1 - t1; - return ticksToMilliseconds(difference); + return ticksToNanoseconds(difference) / 1000000; } qint64 QElapsedTimer::secsTo(const QElapsedTimer &other) const diff --git a/src/corelib/xml/qxmlstream.cpp b/src/corelib/xml/qxmlstream.cpp index 91c3a19..64a9857 100644 --- a/src/corelib/xml/qxmlstream.cpp +++ b/src/corelib/xml/qxmlstream.cpp @@ -2941,6 +2941,9 @@ QStringRef QXmlStreamReader::documentEncoding() const By default, QXmlStreamWriter encodes XML in UTF-8. Different encodings can be enforced using setCodec(). + If an error occurs while writing to the underlying device, hasError() + starts returning true and subsequent writes are ignored. + The \l{QXmlStream Bookmarks Example} illustrates how to use a stream writer to write an XML bookmark file (XBEL) that was previously read in by a QXmlStreamReader. @@ -2965,7 +2968,8 @@ public: void write(const QStringRef &); void write(const QString &); void writeEscaped(const QString &, bool escapeWhitespace = false); - void write(const char *s); + void write(const char *s, int len); + template <int N> void write(const char (&s)[N]) { write(s, N - 1); } bool finishStartElement(bool contents = true); void writeStartElement(const QString &namespaceUri, const QString &name); QIODevice *device; @@ -2975,6 +2979,7 @@ public: uint inEmptyElement :1; uint lastWasStartElement :1; uint wroteSomething :1; + uint hasError :1; uint autoFormatting :1; QByteArray autoFormattingIndent; NamespaceDeclaration emptyNamespace; @@ -3007,6 +3012,7 @@ QXmlStreamWriterPrivate::QXmlStreamWriterPrivate(QXmlStreamWriter *q) #endif inStartElement = inEmptyElement = false; wroteSomething = false; + hasError = false; lastWasStartElement = false; lastNamespaceDeclaration = 1; autoFormatting = false; @@ -3016,11 +3022,15 @@ QXmlStreamWriterPrivate::QXmlStreamWriterPrivate(QXmlStreamWriter *q) void QXmlStreamWriterPrivate::write(const QStringRef &s) { if (device) { + if (hasError) + return; #ifdef QT_NO_TEXTCODEC - device->write(s.toString().toLatin1(), s.size()); + QByteArray bytes = s.toLatin1(); #else - device->write(encoder->fromUnicode(s.constData(), s.size())); + QByteArray bytes = encoder->fromUnicode(s.constData(), s.size()); #endif + if (device->write(bytes) != bytes.size()) + hasError = true; } else if (stringDevice) s.appendTo(stringDevice); @@ -3031,11 +3041,15 @@ void QXmlStreamWriterPrivate::write(const QStringRef &s) void QXmlStreamWriterPrivate::write(const QString &s) { if (device) { + if (hasError) + return; #ifdef QT_NO_TEXTCODEC - device->write(s.toLatin1(), s.size()); + QByteArray bytes = s.toLatin1(); #else - device->write(encoder->fromUnicode(s)); + QByteArray bytes = encoder->fromUnicode(s); #endif + if (device->write(bytes) != bytes.size()) + hasError = true; } else if (stringDevice) stringDevice->append(s); @@ -3070,31 +3084,19 @@ void QXmlStreamWriterPrivate::writeEscaped(const QString &s, bool escapeWhitespa escaped += QChar(c); } } - if (device) { -#ifdef QT_NO_TEXTCODEC - device->write(escaped.toLatin1(), escaped.size()); -#else - device->write(encoder->fromUnicode(escaped)); -#endif - } - else if (stringDevice) - stringDevice->append(escaped); - else - qWarning("QXmlStreamWriter: No device"); + write(escaped); } - -void QXmlStreamWriterPrivate::write(const char *s) +// ASCII only! +void QXmlStreamWriterPrivate::write(const char *s, int len) { if (device) { -#ifndef QT_NO_TEXTCODEC - if (codec->mibEnum() != 106) - device->write(encoder->fromUnicode(QLatin1String(s))); - else -#endif - device->write(s, strlen(s)); + if (hasError) + return; + if (device->write(s, len) != len) + hasError = true; } else if (stringDevice) { - stringDevice->append(QLatin1String(s)); + stringDevice->append(QString::fromLatin1(s, len)); } else qWarning("QXmlStreamWriter: No device"); } @@ -3172,7 +3174,7 @@ void QXmlStreamWriterPrivate::indent(int level) { write("\n"); for (int i = level; i > 0; --i) - write(autoFormattingIndent.constData()); + write(autoFormattingIndent.constData(), autoFormattingIndent.length()); } @@ -3373,6 +3375,17 @@ int QXmlStreamWriter::autoFormattingIndent() const return d->autoFormattingIndent.count(' ') - d->autoFormattingIndent.count('\t'); } +/*! + Returns \c true if the stream failed to write to the underlying device. + + The error status is never reset. Writes happening after the error + occurred are ignored, even if the error condition is cleared. + */ +bool QXmlStreamWriter::hasError() const +{ + Q_D(const QXmlStreamWriter); + return d->hasError; +} /*! \overload @@ -3759,7 +3772,7 @@ void QXmlStreamWriter::writeStartDocument(const QString &version) #ifdef QT_NO_TEXTCODEC d->write("iso-8859-1"); #else - d->write(d->codec->name().constData()); + d->write(d->codec->name().constData(), d->codec->name().length()); #endif } d->write("\"?>"); @@ -3782,12 +3795,13 @@ void QXmlStreamWriter::writeStartDocument(const QString &version, bool standalon #ifdef QT_NO_TEXTCODEC d->write("iso-8859-1"); #else - d->write(d->codec->name().constData()); + d->write(d->codec->name().constData(), d->codec->name().length()); #endif } - d->write("\" standalone=\""); - d->write(standalone ? "yes" : "no"); - d->write("\"?>"); + if (standalone) + d->write("\" standalone=\"yes\"?>"); + else + d->write("\" standalone=\"no\"?>"); } diff --git a/src/corelib/xml/qxmlstream.h b/src/corelib/xml/qxmlstream.h index d7143bd..244d3d4 100644 --- a/src/corelib/xml/qxmlstream.h +++ b/src/corelib/xml/qxmlstream.h @@ -474,6 +474,8 @@ public: void writeCurrentToken(const QXmlStreamReader &reader); #endif + bool hasError() const; + private: Q_DISABLE_COPY(QXmlStreamWriter) Q_DECLARE_PRIVATE(QXmlStreamWriter) |