summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/io/qdatastream.cpp59
-rw-r--r--src/corelib/io/qdatastream.h3
-rw-r--r--src/corelib/io/qprocess_unix.cpp30
-rw-r--r--src/corelib/io/qtextstream.cpp48
-rw-r--r--src/corelib/io/qtextstream.h3
-rw-r--r--src/corelib/kernel/qcoreapplication.cpp18
-rw-r--r--src/corelib/kernel/qcoreapplication.h1
-rw-r--r--src/corelib/kernel/qcoreapplication_p.h2
-rw-r--r--src/corelib/thread/qmutex.cpp107
-rw-r--r--src/corelib/thread/qmutex_p.h14
-rw-r--r--src/corelib/thread/qmutex_unix.cpp91
-rw-r--r--src/corelib/thread/qmutex_win.cpp10
-rw-r--r--src/corelib/tools/qelapsedtimer.h1
-rw-r--r--src/corelib/tools/qelapsedtimer_generic.cpp16
-rw-r--r--src/corelib/tools/qelapsedtimer_mac.cpp6
-rw-r--r--src/corelib/tools/qelapsedtimer_symbian.cpp22
-rw-r--r--src/corelib/tools/qelapsedtimer_unix.cpp11
-rw-r--r--src/corelib/tools/qelapsedtimer_win.cpp20
-rw-r--r--src/corelib/xml/qxmlstream.cpp76
-rw-r--r--src/corelib/xml/qxmlstream.h2
-rw-r--r--src/gui/painting/qpaintengine_raster.cpp37
-rw-r--r--src/opengl/gl2paintengineex/qglengineshadermanager.cpp294
-rw-r--r--src/opengl/gl2paintengineex/qglshadercache_meego_p.h457
-rw-r--r--src/opengl/gl2paintengineex/qglshadercache_p.h98
-rw-r--r--src/opengl/opengl.pro4
-rw-r--r--src/opengl/util/meego/main.cpp48
-rw-r--r--src/opengl/util/meego/shader-cache-introspector.pro7
-rw-r--r--tests/auto/qcompleter/tst_qcompleter.cpp27
-rw-r--r--tests/auto/qdatastream/tst_qdatastream.cpp51
-rw-r--r--tests/auto/qelapsedtimer/tst_qelapsedtimer.cpp7
-rw-r--r--tests/auto/qprogressbar/tst_qprogressbar.cpp7
-rw-r--r--tests/auto/qtextstream/tst_qtextstream.cpp37
-rw-r--r--tests/auto/qxmlstream/tst_qxmlstream.cpp82
-rw-r--r--tests/benchmarks/corelib/thread/qmutex/qmutex.pro2
-rw-r--r--tests/benchmarks/corelib/thread/qmutex/tst_qmutex.cpp326
-rw-r--r--tests/shared/filesystem.h3
36 files changed, 1678 insertions, 349 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)
diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp
index dd14e66..cdcd092 100644
--- a/src/gui/painting/qpaintengine_raster.cpp
+++ b/src/gui/painting/qpaintengine_raster.cpp
@@ -662,31 +662,23 @@ QRasterPaintEngineState::QRasterPaintEngineState()
QRasterPaintEngineState::QRasterPaintEngineState(QRasterPaintEngineState &s)
: QPainterState(s)
+ , stroker(s.stroker)
+ , lastBrush(s.lastBrush)
+ , brushData(s.brushData)
+ , lastPen(s.lastPen)
+ , penData(s.penData)
+ , fillFlags(s.fillFlags)
+ , strokeFlags(s.strokeFlags)
+ , pixmapFlags(s.pixmapFlags)
+ , intOpacity(s.intOpacity)
+ , txscale(s.txscale)
+ , flag_bits(s.flag_bits)
+ , clip(s.clip)
+ , dirty(s.dirty)
{
- stroker = s.stroker;
-
- lastBrush = s.lastBrush;
- brushData = s.brushData;
brushData.tempImage = 0;
-
- lastPen = s.lastPen;
- penData = s.penData;
penData.tempImage = 0;
-
- fillFlags = s.fillFlags;
- strokeFlags = s.strokeFlags;
- pixmapFlags = s.pixmapFlags;
-
- intOpacity = s.intOpacity;
-
- txscale = s.txscale;
-
- flag_bits = s.flag_bits;
-
- clip = s.clip;
flags.has_clip_ownership = false;
-
- dirty = s.dirty;
}
/*!
@@ -5165,7 +5157,8 @@ void QSpanData::setup(const QBrush &brush, int alpha, QPainter::CompositionMode
case Qt::SolidPattern: {
type = Solid;
QColor c = qbrush_color(brush);
- solid.color = PREMUL(ARGB_COMBINE_ALPHA(c.rgba(), alpha));
+ QRgb rgba = c.rgba();
+ solid.color = PREMUL(ARGB_COMBINE_ALPHA(rgba, alpha));
if ((solid.color & 0xff000000) == 0
&& compositionMode == QPainter::CompositionMode_SourceOver) {
type = None;
diff --git a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp
index 93ff3f4..0723c28 100644
--- a/src/opengl/gl2paintengineex/qglengineshadermanager.cpp
+++ b/src/opengl/gl2paintengineex/qglengineshadermanager.cpp
@@ -42,6 +42,7 @@
#include "qglengineshadermanager_p.h"
#include "qglengineshadersource_p.h"
#include "qpaintengineex_opengl2_p.h"
+#include "qglshadercache_p.h"
#if defined(QT_DEBUG)
#include <QMetaEnum>
@@ -170,64 +171,92 @@ QGLEngineSharedShaders::QGLEngineSharedShaders(const QGLContext* context)
QGLShader* fragShader;
QGLShader* vertexShader;
- QByteArray source;
+ QByteArray vertexSource;
+ QByteArray fragSource;
// Compile up the simple shader:
- source.clear();
- source.append(qShaderSnippets[MainVertexShader]);
- source.append(qShaderSnippets[PositionOnlyVertexShader]);
- vertexShader = new QGLShader(QGLShader::Vertex, context, 0);
- shaders.append(vertexShader);
- if (!vertexShader->compileSourceCode(source))
- qWarning("Vertex shader for simpleShaderProg (MainVertexShader & PositionOnlyVertexShader) failed to compile");
-
- source.clear();
- source.append(qShaderSnippets[MainFragmentShader]);
- source.append(qShaderSnippets[ShockingPinkSrcFragmentShader]);
- fragShader = new QGLShader(QGLShader::Fragment, context, 0);
- shaders.append(fragShader);
- if (!fragShader->compileSourceCode(source))
- qWarning("Fragment shader for simpleShaderProg (MainFragmentShader & ShockingPinkSrcFragmentShader) failed to compile");
+ vertexSource.append(qShaderSnippets[MainVertexShader]);
+ vertexSource.append(qShaderSnippets[PositionOnlyVertexShader]);
+
+ fragSource.append(qShaderSnippets[MainFragmentShader]);
+ fragSource.append(qShaderSnippets[ShockingPinkSrcFragmentShader]);
simpleShaderProg = new QGLShaderProgram(context, 0);
- simpleShaderProg->addShader(vertexShader);
- simpleShaderProg->addShader(fragShader);
- simpleShaderProg->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR);
- simpleShaderProg->bindAttributeLocation("pmvMatrix1", QT_PMV_MATRIX_1_ATTR);
- simpleShaderProg->bindAttributeLocation("pmvMatrix2", QT_PMV_MATRIX_2_ATTR);
- simpleShaderProg->bindAttributeLocation("pmvMatrix3", QT_PMV_MATRIX_3_ATTR);
+
+ CachedShader simpleShaderCache(fragSource, vertexSource);
+
+ bool inCache = simpleShaderCache.load(simpleShaderProg, context);
+
+ if (!inCache) {
+ vertexShader = new QGLShader(QGLShader::Vertex, context, 0);
+ shaders.append(vertexShader);
+ if (!vertexShader->compileSourceCode(vertexSource))
+ qWarning("Vertex shader for simpleShaderProg (MainVertexShader & PositionOnlyVertexShader) failed to compile");
+
+ fragShader = new QGLShader(QGLShader::Fragment, context, 0);
+ shaders.append(fragShader);
+ if (!fragShader->compileSourceCode(fragSource))
+ qWarning("Fragment shader for simpleShaderProg (MainFragmentShader & ShockingPinkSrcFragmentShader) failed to compile");
+
+ simpleShaderProg->addShader(vertexShader);
+ simpleShaderProg->addShader(fragShader);
+
+ simpleShaderProg->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR);
+ simpleShaderProg->bindAttributeLocation("pmvMatrix1", QT_PMV_MATRIX_1_ATTR);
+ simpleShaderProg->bindAttributeLocation("pmvMatrix2", QT_PMV_MATRIX_2_ATTR);
+ simpleShaderProg->bindAttributeLocation("pmvMatrix3", QT_PMV_MATRIX_3_ATTR);
+ }
+
simpleShaderProg->link();
- if (!simpleShaderProg->isLinked()) {
+
+ if (simpleShaderProg->isLinked()) {
+ if (!inCache)
+ simpleShaderCache.store(simpleShaderProg, context);
+ } else {
qCritical() << "Errors linking simple shader:"
<< simpleShaderProg->log();
}
// Compile the blit shader:
- source.clear();
- source.append(qShaderSnippets[MainWithTexCoordsVertexShader]);
- source.append(qShaderSnippets[UntransformedPositionVertexShader]);
- vertexShader = new QGLShader(QGLShader::Vertex, context, 0);
- shaders.append(vertexShader);
- if (!vertexShader->compileSourceCode(source))
- qWarning("Vertex shader for blitShaderProg (MainWithTexCoordsVertexShader & UntransformedPositionVertexShader) failed to compile");
-
- source.clear();
- source.append(qShaderSnippets[MainFragmentShader]);
- source.append(qShaderSnippets[ImageSrcFragmentShader]);
- fragShader = new QGLShader(QGLShader::Fragment, context, 0);
- shaders.append(fragShader);
- if (!fragShader->compileSourceCode(source))
- qWarning("Fragment shader for blitShaderProg (MainFragmentShader & ImageSrcFragmentShader) failed to compile");
+ vertexSource.clear();
+ vertexSource.append(qShaderSnippets[MainWithTexCoordsVertexShader]);
+ vertexSource.append(qShaderSnippets[UntransformedPositionVertexShader]);
+
+ fragSource.clear();
+ fragSource.append(qShaderSnippets[MainFragmentShader]);
+ fragSource.append(qShaderSnippets[ImageSrcFragmentShader]);
blitShaderProg = new QGLShaderProgram(context, 0);
- blitShaderProg->addShader(vertexShader);
- blitShaderProg->addShader(fragShader);
- blitShaderProg->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR);
- blitShaderProg->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR);
+
+ CachedShader blitShaderCache(fragSource, vertexSource);
+
+ inCache = blitShaderCache.load(blitShaderProg, context);
+
+ if (!inCache) {
+ vertexShader = new QGLShader(QGLShader::Vertex, context, 0);
+ shaders.append(vertexShader);
+ if (!vertexShader->compileSourceCode(vertexSource))
+ qWarning("Vertex shader for blitShaderProg (MainWithTexCoordsVertexShader & UntransformedPositionVertexShader) failed to compile");
+
+ fragShader = new QGLShader(QGLShader::Fragment, context, 0);
+ shaders.append(fragShader);
+ if (!fragShader->compileSourceCode(fragSource))
+ qWarning("Fragment shader for blitShaderProg (MainFragmentShader & ImageSrcFragmentShader) failed to compile");
+
+ blitShaderProg->addShader(vertexShader);
+ blitShaderProg->addShader(fragShader);
+
+ blitShaderProg->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR);
+ blitShaderProg->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR);
+ }
+
blitShaderProg->link();
- if (!blitShaderProg->isLinked()) {
+ if (blitShaderProg->isLinked()) {
+ if (!inCache)
+ blitShaderCache.store(blitShaderProg, context);
+ } else {
qCritical() << "Errors linking blit shader:"
- << simpleShaderProg->log();
+ << blitShaderProg->log();
}
#ifdef QT_GL_SHARED_SHADER_DEBUG
@@ -279,101 +308,110 @@ QGLEngineShaderProg *QGLEngineSharedShaders::findProgramInCache(const QGLEngineS
}
}
- QGLShader *vertexShader = 0;
- QGLShader *fragShader = 0;
- QGLEngineShaderProg *newProg = 0;
- bool success = false;
+ QScopedPointer<QGLEngineShaderProg> newProg;
do {
- QByteArray source;
+ QByteArray fragSource;
// Insert the custom stage before the srcPixel shader to work around an ATI driver bug
// where you cannot forward declare a function that takes a sampler as argument.
if (prog.srcPixelFragShader == CustomImageSrcFragmentShader)
- source.append(prog.customStageSource);
- source.append(qShaderSnippets[prog.mainFragShader]);
- source.append(qShaderSnippets[prog.srcPixelFragShader]);
+ fragSource.append(prog.customStageSource);
+ fragSource.append(qShaderSnippets[prog.mainFragShader]);
+ fragSource.append(qShaderSnippets[prog.srcPixelFragShader]);
if (prog.compositionFragShader)
- source.append(qShaderSnippets[prog.compositionFragShader]);
+ fragSource.append(qShaderSnippets[prog.compositionFragShader]);
if (prog.maskFragShader)
- source.append(qShaderSnippets[prog.maskFragShader]);
- fragShader = new QGLShader(QGLShader::Fragment, ctxGuard.context(), 0);
- shaders.append(fragShader);
- QByteArray description;
+ fragSource.append(qShaderSnippets[prog.maskFragShader]);
+
+ QByteArray vertexSource;
+ vertexSource.append(qShaderSnippets[prog.mainVertexShader]);
+ vertexSource.append(qShaderSnippets[prog.positionVertexShader]);
+
+ QScopedPointer<QGLShaderProgram> shaderProgram(new QGLShaderProgram(ctxGuard.context(), 0));
+
+ CachedShader shaderCache(fragSource, vertexSource);
+ bool inCache = shaderCache.load(shaderProgram.data(), ctxGuard.context());
+
+ if (!inCache) {
+
+ QScopedPointer<QGLShader> fragShader(new QGLShader(QGLShader::Fragment, ctxGuard.context(), 0));
+ QByteArray description;
#if defined(QT_DEBUG)
- // Name the shader for easier debugging
- description.append("Fragment shader: main=");
- description.append(snippetNameStr(prog.mainFragShader));
- description.append(", srcPixel=");
- description.append(snippetNameStr(prog.srcPixelFragShader));
- if (prog.compositionFragShader) {
- description.append(", composition=");
- description.append(snippetNameStr(prog.compositionFragShader));
- }
- if (prog.maskFragShader) {
- description.append(", mask=");
- description.append(snippetNameStr(prog.maskFragShader));
- }
- fragShader->setObjectName(QString::fromLatin1(description));
+ // Name the shader for easier debugging
+ description.append("Fragment shader: main=");
+ description.append(snippetNameStr(prog.mainFragShader));
+ description.append(", srcPixel=");
+ description.append(snippetNameStr(prog.srcPixelFragShader));
+ if (prog.compositionFragShader) {
+ description.append(", composition=");
+ description.append(snippetNameStr(prog.compositionFragShader));
+ }
+ if (prog.maskFragShader) {
+ description.append(", mask=");
+ description.append(snippetNameStr(prog.maskFragShader));
+ }
+ fragShader->setObjectName(QString::fromLatin1(description));
#endif
- if (!fragShader->compileSourceCode(source)) {
- qWarning() << "Warning:" << description << "failed to compile!";
- break;
- }
+ if (!fragShader->compileSourceCode(fragSource)) {
+ qWarning() << "Warning:" << description << "failed to compile!";
+ break;
+ }
- source.clear();
- source.append(qShaderSnippets[prog.mainVertexShader]);
- source.append(qShaderSnippets[prog.positionVertexShader]);
- vertexShader = new QGLShader(QGLShader::Vertex, ctxGuard.context(), 0);
- shaders.append(vertexShader);
+ QScopedPointer<QGLShader> vertexShader(new QGLShader(QGLShader::Vertex, ctxGuard.context(), 0));
#if defined(QT_DEBUG)
- // Name the shader for easier debugging
- description.clear();
- description.append("Vertex shader: main=");
- description.append(snippetNameStr(prog.mainVertexShader));
- description.append(", position=");
- description.append(snippetNameStr(prog.positionVertexShader));
- vertexShader->setObjectName(QString::fromLatin1(description));
+ // Name the shader for easier debugging
+ description.clear();
+ description.append("Vertex shader: main=");
+ description.append(snippetNameStr(prog.mainVertexShader));
+ description.append(", position=");
+ description.append(snippetNameStr(prog.positionVertexShader));
+ vertexShader->setObjectName(QString::fromLatin1(description));
#endif
- if (!vertexShader->compileSourceCode(source)) {
- qWarning() << "Warning:" << description << "failed to compile!";
- break;
- }
+ if (!vertexShader->compileSourceCode(vertexSource)) {
+ qWarning() << "Warning:" << description << "failed to compile!";
+ break;
+ }
- newProg = new QGLEngineShaderProg(prog);
-
- // If the shader program's not found in the cache, create it now.
- newProg->program = new QGLShaderProgram(ctxGuard.context(), 0);
- newProg->program->addShader(vertexShader);
- newProg->program->addShader(fragShader);
-
- // We have to bind the vertex attribute names before the program is linked:
- newProg->program->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR);
- if (newProg->useTextureCoords)
- newProg->program->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR);
- if (newProg->useOpacityAttribute)
- newProg->program->bindAttributeLocation("opacityArray", QT_OPACITY_ATTR);
- if (newProg->usePmvMatrixAttribute) {
- newProg->program->bindAttributeLocation("pmvMatrix1", QT_PMV_MATRIX_1_ATTR);
- newProg->program->bindAttributeLocation("pmvMatrix2", QT_PMV_MATRIX_2_ATTR);
- newProg->program->bindAttributeLocation("pmvMatrix3", QT_PMV_MATRIX_3_ATTR);
+ shaders.append(vertexShader.data());
+ shaders.append(fragShader.data());
+ shaderProgram->addShader(vertexShader.take());
+ shaderProgram->addShader(fragShader.take());
+
+ // We have to bind the vertex attribute names before the program is linked:
+ shaderProgram->bindAttributeLocation("vertexCoordsArray", QT_VERTEX_COORDS_ATTR);
+ if (prog.useTextureCoords)
+ shaderProgram->bindAttributeLocation("textureCoordArray", QT_TEXTURE_COORDS_ATTR);
+ if (prog.useOpacityAttribute)
+ shaderProgram->bindAttributeLocation("opacityArray", QT_OPACITY_ATTR);
+ if (prog.usePmvMatrixAttribute) {
+ shaderProgram->bindAttributeLocation("pmvMatrix1", QT_PMV_MATRIX_1_ATTR);
+ shaderProgram->bindAttributeLocation("pmvMatrix2", QT_PMV_MATRIX_2_ATTR);
+ shaderProgram->bindAttributeLocation("pmvMatrix3", QT_PMV_MATRIX_3_ATTR);
+ }
}
+ newProg.reset(new QGLEngineShaderProg(prog));
+ newProg->program = shaderProgram.take();
+
newProg->program->link();
- if (!newProg->program->isLinked()) {
+ if (newProg->program->isLinked()) {
+ if (!inCache)
+ shaderCache.store(newProg->program, ctxGuard.context());
+ } else {
QLatin1String none("none");
QLatin1String br("\n");
QString error;
- error = QLatin1String("Shader program failed to link,")
+ error = QLatin1String("Shader program failed to link,");
#if defined(QT_DEBUG)
- + br
- + QLatin1String(" Shaders Used:") + br
- + QLatin1String(" ") + vertexShader->objectName() + QLatin1String(": ") + br
- + QLatin1String(vertexShader->sourceCode()) + br
- + QLatin1String(" ") + fragShader->objectName() + QLatin1String(": ") + br
- + QLatin1String(fragShader->sourceCode()) + br
+ error += QLatin1String("\n Shaders Used:\n");
+ for (int i = 0; i < newProg->program->shaders().count(); ++i) {
+ QGLShader *shader = newProg->program->shaders().at(i);
+ error += QLatin1String(" ") + shader->objectName() + QLatin1String(": \n")
+ + QLatin1String(shader->sourceCode()) + br;
+ }
#endif
- + QLatin1String(" Error Log:\n")
- + QLatin1String(" ") + newProg->program->log();
+ error += QLatin1String(" Error Log:\n")
+ + QLatin1String(" ") + newProg->program->log();
qWarning() << error;
break;
}
@@ -395,26 +433,10 @@ QGLEngineShaderProg *QGLEngineSharedShaders::findProgramInCache(const QGLEngineS
}
}
- cachedPrograms.insert(0, newProg);
-
- success = true;
+ cachedPrograms.insert(0, newProg.data());
} while (false);
- // Clean up everything if we weren't successful
- if (!success) {
- if (newProg) {
- delete newProg; // Also deletes the QGLShaderProgram which in turn deletes the QGLShaders
- newProg = 0;
- }
- else {
- if (vertexShader)
- delete vertexShader;
- if (fragShader)
- delete fragShader;
- }
- }
-
- return newProg;
+ return newProg.take();
}
void QGLEngineSharedShaders::cleanupCustomStage(QGLCustomShaderStage* stage)
diff --git a/src/opengl/gl2paintengineex/qglshadercache_meego_p.h b/src/opengl/gl2paintengineex/qglshadercache_meego_p.h
new file mode 100644
index 0000000..5f51fc2
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qglshadercache_meego_p.h
@@ -0,0 +1,457 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// 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.
+//
+
+#ifndef QGLSHADERCACHE_MEEGO_P_H
+#define QGLSHADERCACHE_MEEGO_P_H
+
+#include <QtCore/qglobal.h>
+
+#if defined(QT_MEEGO_EXPERIMENTAL_SHADERCACHE) && defined(QT_OPENGL_ES_2)
+
+#include <QtCore/qcryptographichash.h>
+#include <QtCore/qsharedmemory.h>
+#include <QtCore/qsystemsemaphore.h>
+
+#ifndef QT_BOOTSTRAPPED
+# include <GLES2/gl2ext.h>
+#endif
+#if defined(QT_DEBUG) || defined(QT_MEEGO_EXPERIMENTAL_SHADERCACHE_TRACE)
+# include <syslog.h>
+#endif
+
+QT_BEGIN_HEADER
+
+/*
+ This cache stores internal Qt shader programs in shared memory.
+
+ This header file is ugly on purpose and can only be included once. It is only to be used
+ for the internal shader cache, not as a generic cache for anyone's shaders.
+
+ The cache stores either ShaderCacheMaxEntries shader programs or ShaderCacheDataSize kilobytes
+ of shader programs, whatever limit is reached first.
+
+ The layout of the cache is as outlined in the CachedShaders struct. After some
+ integers, an array of headers is reserved, then comes the space for the actual binaries.
+
+ Shader Programs are identified by the md5sum of their frag and vertex shader source code.
+
+ Shader Programs are never removed. The cache never shrinks or re-shuffles. This is done
+ on purpose to ensure minimum amount of locking, no alignment problems and very few write
+ operations.
+
+ Note: Locking the shader cache could be expensive, because the entire system might hang.
+ That's why the cache is immutable to minimize the time we need to keep it locked.
+
+ Why is it Meego specific?
+
+ First, the size is chosen so that it fits to generic meego usage. Second, on Meego, there's
+ always at least one Qt application active (the launcher), so the cache will never be destroyed.
+ Only when the last Qt app exits, the cache dies, which should only be when someone kills the
+ X11 server. And last but not least it was only tested with Meego's SGX driver.
+
+ There's a small tool in src/opengl/util/meego that dumps the contents of the cache.
+ */
+
+// anonymous namespace, prevent exporting of the private symbols
+namespace
+{
+
+struct CachedShaderHeader
+{
+ /* the index in the data[] member of CachedShaders */
+ int index;
+ /* the size of the binary shader */
+ GLsizei size;
+ /* the format of the binary shader */
+ GLenum format;
+ /* the md5sum of the frag+vertex shaders */
+ char md5Sum[16];
+};
+
+enum
+{
+ /* The maximum amount of shader programs the cache can hold */
+ ShaderCacheMaxEntries = 20
+};
+
+typedef CachedShaderHeader CachedShaderHeaders[ShaderCacheMaxEntries];
+
+enum
+{
+ // ShaderCacheDataSize is 20k minus the other data members of CachedShaders
+ ShaderCacheDataSize = 1024 * ShaderCacheMaxEntries - sizeof(CachedShaderHeaders) - 2 * sizeof(int)
+};
+
+struct CachedShaders
+{
+ /* How much space is still available in the cache */
+ inline int availableSize() const { return ShaderCacheDataSize - dataSize; }
+
+ /* The current amount of cached shaders */
+ int shaderCount;
+
+ /* The current amount (in bytes) of cached data */
+ int dataSize;
+
+ /* The headers describing the shaders */
+ CachedShaderHeaders headers;
+
+ /* The actual binary data of the shader programs */
+ char data[ShaderCacheDataSize];
+};
+
+//#define QT_DEBUG_SHADER_CACHE
+#ifdef QT_DEBUG_SHADER_CACHE
+static QDebug shaderCacheDebug()
+{
+ return QDebug(QtDebugMsg);
+}
+#else
+static inline QNoDebug shaderCacheDebug() { return QNoDebug(); }
+#endif
+
+class ShaderCacheSharedMemory
+{
+public:
+ ShaderCacheSharedMemory()
+ : shm(QLatin1String("qt_gles2_shadercache_" QT_VERSION_STR))
+ {
+ // we need a system semaphore here, since cache creation and initialization must be atomic
+ QSystemSemaphore attachSemaphore(QLatin1String("qt_gles2_shadercache_mutex_" QT_VERSION_STR), 1);
+
+ if (!attachSemaphore.acquire()) {
+ shaderCacheDebug() << "Unable to require shader cache semaphore:" << attachSemaphore.errorString();
+ return;
+ }
+
+ if (shm.attach()) {
+ // success!
+ shaderCacheDebug() << "Attached to shader cache";
+ } else {
+
+ // no cache exists - create and initialize it
+ if (shm.create(sizeof(CachedShaders))) {
+ shaderCacheDebug() << "Created new shader cache";
+ initializeCache();
+ } else {
+ shaderCacheDebug() << "Unable to create shader cache:" << shm.errorString();
+ }
+ }
+
+ attachSemaphore.release();
+ }
+
+ inline bool isAttached() const { return shm.isAttached(); }
+
+ inline bool lock() { return shm.lock(); }
+ inline bool unlock() { return shm.unlock(); }
+ inline void *data() { return shm.data(); }
+ inline QString errorString() { return shm.errorString(); }
+
+ ~ShaderCacheSharedMemory()
+ {
+ if (!shm.detach())
+ shaderCacheDebug() << "Unable to detach shader cache" << shm.errorString();
+ }
+
+private:
+ void initializeCache()
+ {
+ // no need to lock the shared memory since we're already protected by the
+ // attach system semaphore.
+
+ void *data = shm.data();
+ Q_ASSERT(data);
+
+ memset(data, 0, sizeof(CachedShaders));
+ }
+
+ QSharedMemory shm;
+};
+
+class ShaderCacheLocker
+{
+public:
+ inline ShaderCacheLocker(ShaderCacheSharedMemory *cache)
+ : shm(cache->lock() ? cache : (ShaderCacheSharedMemory *)0)
+ {
+ if (!shm)
+ shaderCacheDebug() << "Unable to lock shader cache" << cache->errorString();
+ }
+
+ inline bool isLocked() const { return shm; }
+
+ inline ~ShaderCacheLocker()
+ {
+ if (!shm)
+ return;
+ if (!shm->unlock())
+ shaderCacheDebug() << "Unable to unlock shader cache" << shm->errorString();
+ }
+
+private:
+ ShaderCacheSharedMemory *shm;
+};
+
+#ifdef QT_BOOTSTRAPPED
+} // end namespace
+#else
+
+static void traceCacheOverflow(const char *message)
+{
+#if defined(QT_DEBUG) || defined (QT_MEEGO_EXPERIMENTAL_SHADERCACHE_TRACE)
+ openlog(qPrintable(QCoreApplication::applicationName()), LOG_PID | LOG_ODELAY, LOG_USER);
+ syslog(LOG_DEBUG, message);
+ closelog();
+#endif
+ shaderCacheDebug() << message;
+}
+
+Q_GLOBAL_STATIC(ShaderCacheSharedMemory, shaderCacheSharedMemory)
+
+/*
+ Finds the index of the shader program identified by md5Sum in the cache.
+ Note: Does NOT lock the cache for reading, the cache must already be locked!
+
+ Returns -1 when no shader was found.
+ */
+static int qt_cache_index_unlocked(const QByteArray &md5Sum, CachedShaders *cache)
+{
+ for (int i = 0; i < cache->shaderCount; ++i) {
+ if (qstrncmp(md5Sum.constData(), cache->headers[i].md5Sum, 16) == 0) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/* Returns the index of the shader identified by md5Sum */
+static int qt_cache_index(const QByteArray &md5Sum)
+{
+ ShaderCacheSharedMemory *shm = shaderCacheSharedMemory();
+ if (!shm || !shm->isAttached())
+ return false;
+
+ Q_ASSERT(md5Sum.length() == 16);
+
+ ShaderCacheLocker locker(shm);
+ if (!locker.isLocked())
+ return false;
+
+ void *data = shm->data();
+ Q_ASSERT(data);
+
+ CachedShaders *cache = reinterpret_cast<CachedShaders *>(data);
+
+ return qt_cache_index_unlocked(md5Sum, cache);
+}
+
+/* Loads the cached shader at index \a shaderIndex into \a program
+ * Note: Since the cache is immutable, this operation doesn't lock the shared memory.
+ */
+static bool qt_cached_shader(QGLShaderProgram *program, const QGLContext *ctx, int shaderIndex)
+{
+ Q_ASSERT(shaderIndex >= 0 && shaderIndex <= ShaderCacheMaxEntries);
+ Q_ASSERT(program);
+
+ ShaderCacheSharedMemory *shm = shaderCacheSharedMemory();
+ if (!shm || !shm->isAttached())
+ return false;
+
+ void *data = shm->data();
+ Q_ASSERT(data);
+
+ CachedShaders *cache = reinterpret_cast<CachedShaders *>(data);
+
+ shaderCacheDebug() << "fetching cached shader at index" << shaderIndex
+ << "dataIndex" << cache->headers[shaderIndex].index
+ << "size" << cache->headers[shaderIndex].size
+ << "format" << cache->headers[shaderIndex].format;
+
+ // call program->programId first, since that resolves the glProgramBinaryOES symbol
+ GLuint programId = program->programId();
+ glProgramBinaryOES(programId, cache->headers[shaderIndex].format,
+ cache->data + cache->headers[shaderIndex].index,
+ cache->headers[shaderIndex].size);
+
+ return true;
+}
+
+/* Stores the shader program in the cache. Returns false if there's an error with the cache, or
+ if the cache is too small to hold the shader. */
+static bool qt_cache_shader(const QGLShaderProgram *shader, const QGLContext *ctx, const QByteArray &md5Sum)
+{
+ ShaderCacheSharedMemory *shm = shaderCacheSharedMemory();
+ if (!shm || !shm->isAttached())
+ return false;
+
+ void *data = shm->data();
+ Q_ASSERT(data);
+
+ CachedShaders *cache = reinterpret_cast<CachedShaders *>(data);
+
+ ShaderCacheLocker locker(shm);
+ if (!locker.isLocked())
+ return false;
+
+ int cacheIdx = cache->shaderCount;
+ if (cacheIdx >= ShaderCacheMaxEntries) {
+ traceCacheOverflow("Qt OpenGL shader cache index overflow!");
+ return false;
+ }
+
+ // now that we have the lock on the shared memory, make sure no one
+ // inserted the shader already while we were unlocked
+ if (qt_cache_index_unlocked(md5Sum, cache) != -1)
+ return true; // already cached
+
+ shaderCacheDebug() << "Caching shader at index" << cacheIdx;
+
+ GLint binaryLength = 0;
+ glGetProgramiv(shader->programId(), GL_PROGRAM_BINARY_LENGTH_OES, &binaryLength);
+
+ if (!binaryLength) {
+ shaderCacheDebug() << "Unable to determine binary shader size!";
+ return false;
+ }
+
+ if (binaryLength > cache->availableSize()) {
+ traceCacheOverflow("Qt OpenGL shader cache data overflow!");
+ return false;
+ }
+
+ GLsizei size = 0;
+ GLenum format = 0;
+ glGetProgramBinaryOES(shader->programId(), binaryLength, &size, &format,
+ cache->data + cache->dataSize);
+
+ if (!size) {
+ shaderCacheDebug() << "Unable to get binary shader!";
+ return false;
+ }
+
+ cache->headers[cacheIdx].index = cache->dataSize;
+ cache->dataSize += binaryLength;
+ ++cache->shaderCount;
+ cache->headers[cacheIdx].size = binaryLength;
+ cache->headers[cacheIdx].format = format;
+
+ memcpy(cache->headers[cacheIdx].md5Sum, md5Sum.constData(), 16);
+
+ shaderCacheDebug() << "cached shader size" << size
+ << "format" << format
+ << "binarySize" << binaryLength
+ << "cache index" << cacheIdx
+ << "data index" << cache->headers[cacheIdx].index;
+
+ return true;
+}
+
+} // namespace
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(OpenGL)
+
+class CachedShader
+{
+public:
+ CachedShader(const QByteArray &fragSource, const QByteArray &vertexSource)
+ : cacheIdx(-1)
+ {
+ QCryptographicHash md5Hash(QCryptographicHash::Md5);
+
+ md5Hash.addData(fragSource);
+ md5Hash.addData(vertexSource);
+
+ md5Sum = md5Hash.result();
+ }
+
+ bool isCached()
+ {
+ return cacheIndex() != -1;
+ }
+
+ int cacheIndex()
+ {
+ if (cacheIdx != -1)
+ return cacheIdx;
+ cacheIdx = qt_cache_index(md5Sum);
+ return cacheIdx;
+ }
+
+ bool load(QGLShaderProgram *program, const QGLContext *ctx)
+ {
+ if (cacheIndex() == -1)
+ return false;
+ return qt_cached_shader(program, ctx, cacheIdx);
+ }
+
+ bool store(QGLShaderProgram *program, const QGLContext *ctx)
+ {
+ return qt_cache_shader(program, ctx, md5Sum);
+ }
+
+private:
+ QByteArray md5Sum;
+ int cacheIdx;
+};
+
+
+QT_END_NAMESPACE
+
+#endif
+
+QT_END_HEADER
+
+#endif
+#endif
diff --git a/src/opengl/gl2paintengineex/qglshadercache_p.h b/src/opengl/gl2paintengineex/qglshadercache_p.h
new file mode 100644
index 0000000..29616ae
--- /dev/null
+++ b/src/opengl/gl2paintengineex/qglshadercache_p.h
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtOpenGL module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// 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.
+//
+
+#ifndef QGLSHADERCACHE_P_H
+#define QGLSHADERCACHE_P_H
+
+#include <QtCore/qglobal.h>
+
+#if defined(QT_MEEGO_EXPERIMENTAL_SHADERCACHE) && defined(QT_OPENGL_ES_2)
+# include "qglshadercache_meego_p.h"
+#else
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(OpenGL)
+
+class QGLShaderProgram;
+class QGLContext;
+
+class CachedShader
+{
+public:
+ inline CachedShader(const QByteArray &, const QByteArray &)
+ {}
+
+ inline bool isCached()
+ {
+ return false;
+ }
+
+ inline bool load(QGLShaderProgram *, const QGLContext *)
+ {
+ return false;
+ }
+
+ inline bool store(QGLShaderProgram *, const QGLContext *)
+ {
+ return false;
+ }
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
+#endif
diff --git a/src/opengl/opengl.pro b/src/opengl/opengl.pro
index 682e620..a089d55 100644
--- a/src/opengl/opengl.pro
+++ b/src/opengl/opengl.pro
@@ -60,7 +60,9 @@ SOURCES += qgl.cpp \
gl2paintengineex/qglcustomshaderstage_p.h \
gl2paintengineex/qtriangulatingstroker_p.h \
gl2paintengineex/qtriangulator_p.h \
- gl2paintengineex/qtextureglyphcache_gl_p.h
+ gl2paintengineex/qtextureglyphcache_gl_p.h \
+ gl2paintengineex/qglshadercache_p.h \
+ gl2paintengineex/qglshadercache_meego_p.h
SOURCES += qglshaderprogram.cpp \
qglpixmapfilter.cpp \
diff --git a/src/opengl/util/meego/main.cpp b/src/opengl/util/meego/main.cpp
new file mode 100644
index 0000000..0c9a915
--- /dev/null
+++ b/src/opengl/util/meego/main.cpp
@@ -0,0 +1,48 @@
+#include <QtCore/qdebug.h>
+
+#define QT_DEBUG_SHADER_CACHE
+#define QT_MEEGO_EXPERIMENTAL_SHADERCACHE
+#define QT_OPENGL_ES_2
+#define QT_BOOTSTRAPPED
+
+typedef int GLsizei;
+typedef unsigned int GLenum;
+
+#include "../../gl2paintengineex/qglshadercache_meego_p.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+int main()
+{
+ ShaderCacheSharedMemory shm;
+
+ if (!shm.isAttached()) {
+ fprintf(stderr, "Unable to attach to shared memory\n");
+ return EXIT_FAILURE;
+ }
+
+ ShaderCacheLocker locker(&shm);
+ if (!locker.isLocked()) {
+ fprintf(stderr, "Unable to lock shared memory\n");
+ return EXIT_FAILURE;
+ }
+
+ void *data = shm.data();
+ Q_ASSERT(data);
+
+ CachedShaders *cache = reinterpret_cast<CachedShaders *>(data);
+
+ for (int i = 0; i < cache->shaderCount; ++i) {
+ printf("Shader %d: %d bytes\n", i, cache->headers[i].size);
+ }
+
+ printf("\nSummary:\n\n"
+ " Amount of cached shaders: %d\n"
+ " Bytes used: %d\n"
+ " Bytes available: %d\n",
+ cache->shaderCount, cache->dataSize, cache->availableSize());
+
+ return EXIT_SUCCESS;
+}
+
diff --git a/src/opengl/util/meego/shader-cache-introspector.pro b/src/opengl/util/meego/shader-cache-introspector.pro
new file mode 100644
index 0000000..520e9a5
--- /dev/null
+++ b/src/opengl/util/meego/shader-cache-introspector.pro
@@ -0,0 +1,7 @@
+TEMPLATE = app
+
+SOURCES += main.cpp
+
+TARGET = shader-cache-introspector
+
+QT = core
diff --git a/tests/auto/qcompleter/tst_qcompleter.cpp b/tests/auto/qcompleter/tst_qcompleter.cpp
index 650c328..62e64be 100644
--- a/tests/auto/qcompleter/tst_qcompleter.cpp
+++ b/tests/auto/qcompleter/tst_qcompleter.cpp
@@ -49,6 +49,7 @@
#include <QPointer>
#include "../../shared/util.h"
+#include "../../shared/filesystem.h"
//TESTED_CLASS=
//TESTED_FILES=
@@ -1455,24 +1456,16 @@ void tst_QCompleter::task247560_keyboardNavigation()
void tst_QCompleter::QTBUG_14292_filesystem()
{
- QDir tmpDir = QDir::temp();
+ FileSystem fs;
+ QDir tmpDir = QDir::currentPath();
+
qsrand(QTime::currentTime().msec());
QString d = "tst_QCompleter_" + QString::number(qrand());
- QVERIFY(tmpDir.mkdir(d));
-
-#if 0
- struct Cleanup {
- QString dir;
- ~Cleanup() {
- qDebug() << dir <<
- QFile::remove(dir); }
- } cleanup;
- cleanup.dir = tmpDir.absolutePath()+"/" +d;
-#endif
+ QVERIFY(fs.createDirectory(tmpDir.filePath(d)));
QVERIFY(tmpDir.cd(d));
- QVERIFY(tmpDir.mkdir("hello"));
- QVERIFY(tmpDir.mkdir("holla"));
+ QVERIFY(fs.createDirectory(tmpDir.filePath("hello")));
+ QVERIFY(fs.createDirectory(tmpDir.filePath("holla")));
QLineEdit edit;
QCompleter comp;
@@ -1500,12 +1493,12 @@ void tst_QCompleter::QTBUG_14292_filesystem()
QCOMPARE(comp.popup()->model()->rowCount(), 1);
QTest::keyClick(&edit, 'r');
QTRY_VERIFY(!comp.popup()->isVisible());
- QVERIFY(tmpDir.mkdir("hero"));
+ QVERIFY(fs.createDirectory(tmpDir.filePath("hero")));
QTRY_VERIFY(comp.popup()->isVisible());
QCOMPARE(comp.popup()->model()->rowCount(), 1);
QTest::keyClick(comp.popup(), Qt::Key_Escape);
QTRY_VERIFY(!comp.popup()->isVisible());
- QVERIFY(tmpDir.mkdir("nothingThere"));
+ QVERIFY(fs.createDirectory(tmpDir.filePath("nothingThere")));
//there is no reason creating a file should open a popup, it did in Qt 4.7.0
QTest::qWait(60);
QVERIFY(!comp.popup()->isVisible());
@@ -1522,7 +1515,7 @@ void tst_QCompleter::QTBUG_14292_filesystem()
QTest::qWaitForWindowShown(&w);
QTRY_VERIFY(!edit.hasFocus() && !comp.popup()->hasFocus());
- QVERIFY(tmpDir.mkdir("hemo"));
+ QVERIFY(fs.createDirectory(tmpDir.filePath("hemo")));
//there is no reason creating a file should open a popup, it did in Qt 4.7.0
QTest::qWait(60);
QVERIFY(!comp.popup()->isVisible());
diff --git a/tests/auto/qdatastream/tst_qdatastream.cpp b/tests/auto/qdatastream/tst_qdatastream.cpp
index c03bc71..898fb84 100644
--- a/tests/auto/qdatastream/tst_qdatastream.cpp
+++ b/tests/auto/qdatastream/tst_qdatastream.cpp
@@ -168,6 +168,8 @@ private slots:
void stream_atEnd_data();
void stream_atEnd();
+ void stream_writeError();
+
void stream_QByteArray2();
void setVersion_data();
@@ -2345,6 +2347,55 @@ void tst_QDataStream::stream_atEnd()
}
}
+class FakeBuffer : public QBuffer
+{
+protected:
+ qint64 writeData(const char *c, qint64 i) { return m_lock ? 0 : QBuffer::writeData(c, i); }
+public:
+ FakeBuffer(bool locked = false) : m_lock(locked) {}
+ void setLocked(bool locked) { m_lock = locked; }
+private:
+ bool m_lock;
+};
+
+#define TEST_WRITE_ERROR(op) \
+ { \
+ FakeBuffer fb(false); \
+ QVERIFY(fb.open(QBuffer::ReadWrite)); \
+ QDataStream fs(&fb); \
+ fs.writeRawData("hello", 5); \
+ /* first write some initial content */ \
+ QCOMPARE(fs.status(), QDataStream::Ok); \
+ QCOMPARE(fb.data(), QByteArray("hello")); \
+ /* then test that writing can cause an error */ \
+ fb.setLocked(true); \
+ fs op; \
+ QCOMPARE(fs.status(), QDataStream::WriteFailed); \
+ QCOMPARE(fb.data(), QByteArray("hello")); \
+ /* finally test that writing after an error doesn't change the stream any more */ \
+ fb.setLocked(false); \
+ fs op; \
+ QCOMPARE(fs.status(), QDataStream::WriteFailed); \
+ QCOMPARE(fb.data(), QByteArray("hello")); \
+ }
+
+void tst_QDataStream::stream_writeError()
+{
+ TEST_WRITE_ERROR(<< true)
+ TEST_WRITE_ERROR(<< (qint8)1)
+ TEST_WRITE_ERROR(<< (quint8)1)
+ TEST_WRITE_ERROR(<< (qint16)1)
+ TEST_WRITE_ERROR(<< (quint16)1)
+ TEST_WRITE_ERROR(<< (qint32)1)
+ TEST_WRITE_ERROR(<< (quint32)1)
+ TEST_WRITE_ERROR(<< (qint64)1)
+ TEST_WRITE_ERROR(<< (quint64)1)
+ TEST_WRITE_ERROR(<< "hello")
+ TEST_WRITE_ERROR(<< (float)1.0)
+ TEST_WRITE_ERROR(<< (double)1.0)
+ TEST_WRITE_ERROR(.writeRawData("test", 4))
+}
+
void tst_QDataStream::stream_QByteArray2()
{
QByteArray ba;
diff --git a/tests/auto/qelapsedtimer/tst_qelapsedtimer.cpp b/tests/auto/qelapsedtimer/tst_qelapsedtimer.cpp
index 87df57d..bc61f52 100644
--- a/tests/auto/qelapsedtimer/tst_qelapsedtimer.cpp
+++ b/tests/auto/qelapsedtimer/tst_qelapsedtimer.cpp
@@ -122,11 +122,13 @@ void tst_QElapsedTimer::basics()
quint64 value1 = t1.msecsSinceReference();
qDebug() << value1 << t1;
+ qint64 nsecs = t1.nsecsElapsed();
qint64 elapsed = t1.restart();
QVERIFY(elapsed < minResolution);
+ QVERIFY(nsecs / 1000000 < minResolution);
quint64 value2 = t1.msecsSinceReference();
- qDebug() << value2 << t1 << elapsed;
+ qDebug() << value2 << t1 << elapsed << nsecs;
// in theory, elapsed == value2 - value1
// However, since QElapsedTimer keeps internally the full resolution,
@@ -150,7 +152,10 @@ void tst_QElapsedTimer::elapsed()
// don't check: t1.secsTo(t2)
// QVERIFY(t1 - t2 < 0);
+ QVERIFY(t1.nsecsElapsed() > 0);
QVERIFY(t1.elapsed() > 0);
+ // the number of elapsed nanoseconds and milliseconds should match
+ QVERIFY(t1.nsecsElapsed() - t1.elapsed() * 1000000 < 1000000);
QVERIFY(t1.hasExpired(minResolution));
QVERIFY(!t1.hasExpired(8*minResolution));
QVERIFY(!t2.hasExpired(minResolution));
diff --git a/tests/auto/qprogressbar/tst_qprogressbar.cpp b/tests/auto/qprogressbar/tst_qprogressbar.cpp
index 7d94e3c..e042fb8 100644
--- a/tests/auto/qprogressbar/tst_qprogressbar.cpp
+++ b/tests/auto/qprogressbar/tst_qprogressbar.cpp
@@ -179,10 +179,15 @@ void tst_QProgressBar::format()
bar.repainted = false;
bar.setFormat("%v of %m (%p%)");
qApp->processEvents();
+
#ifndef Q_WS_MAC
- // The Mac scroll bar is animated, which means we get paint events all the time.
+ // Animated scroll bars get paint events all the time
+#ifdef Q_OS_WIN
+ if (QSysInfo::WindowsVersion < QSysInfo::WV_VISTA)
+#endif
QVERIFY(!bar.repainted);
#endif
+
QCOMPARE(bar.text(), QString("1 of 10 (10%)"));
bar.setRange(5, 5);
bar.setValue(5);
diff --git a/tests/auto/qtextstream/tst_qtextstream.cpp b/tests/auto/qtextstream/tst_qtextstream.cpp
index 4c78ef0..005f686 100644
--- a/tests/auto/qtextstream/tst_qtextstream.cpp
+++ b/tests/auto/qtextstream/tst_qtextstream.cpp
@@ -228,6 +228,7 @@ private slots:
void status_real_read();
void status_integer_read();
void status_word_read();
+ void status_write_error();
// use case tests
void useCase1();
@@ -4176,6 +4177,42 @@ void tst_QTextStream::status_word_read()
QCOMPARE(s.status(), QTextStream::ReadPastEnd);
}
+class FakeBuffer : public QBuffer
+{
+protected:
+ qint64 writeData(const char *c, qint64 i) { return m_lock ? 0 : QBuffer::writeData(c, i); }
+public:
+ FakeBuffer(bool locked = false) : m_lock(locked) {}
+ void setLocked(bool locked) { m_lock = locked; }
+private:
+ bool m_lock;
+};
+
+void tst_QTextStream::status_write_error()
+{
+ FakeBuffer fb(false);
+ QVERIFY(fb.open(QBuffer::ReadWrite));
+ QTextStream fs(&fb);
+ fs.setCodec(QTextCodec::codecForName("latin1"));
+ /* first write some initial content */
+ fs << "hello";
+ fs.flush();
+ QCOMPARE(fs.status(), QTextStream::Ok);
+ QCOMPARE(fb.data(), QByteArray("hello"));
+ /* then test that writing can cause an error */
+ fb.setLocked(true);
+ fs << "error";
+ fs.flush();
+ QCOMPARE(fs.status(), QTextStream::WriteFailed);
+ QCOMPARE(fb.data(), QByteArray("hello"));
+ /* finally test that writing after an error doesn't change the stream any more */
+ fb.setLocked(false);
+ fs << "can't do that";
+ fs.flush();
+ QCOMPARE(fs.status(), QTextStream::WriteFailed);
+ QCOMPARE(fb.data(), QByteArray("hello"));
+}
+
void tst_QTextStream::task180679_alignAccountingStyle()
{
{
diff --git a/tests/auto/qxmlstream/tst_qxmlstream.cpp b/tests/auto/qxmlstream/tst_qxmlstream.cpp
index 19e4b90..9e4f3ec 100644
--- a/tests/auto/qxmlstream/tst_qxmlstream.cpp
+++ b/tests/auto/qxmlstream/tst_qxmlstream.cpp
@@ -574,6 +574,7 @@ private slots:
void checkCommentIndentation() const;
void checkCommentIndentation_data() const;
void qtbug9196_crash() const;
+ void hasError() const;
private:
static QByteArray readFile(const QString &filename);
@@ -1560,5 +1561,86 @@ void tst_QXmlStream::qtbug9196_crash() const
}
}
+class FakeBuffer : public QBuffer
+{
+protected:
+ qint64 writeData(const char *c, qint64 i)
+ {
+ qint64 ai = qMin(m_capacity, i);
+ m_capacity -= ai;
+ return ai ? QBuffer::writeData(c, ai) : 0;
+ }
+public:
+ void setCapacity(int capacity) { m_capacity = capacity; }
+private:
+ qint64 m_capacity;
+};
+
+void tst_QXmlStream::hasError() const
+{
+ {
+ FakeBuffer fb;
+ QVERIFY(fb.open(QBuffer::ReadWrite));
+ fb.setCapacity(1000);
+ QXmlStreamWriter writer(&fb);
+ writer.writeStartDocument();
+ writer.writeEndDocument();
+ QVERIFY(!writer.hasError());
+ QCOMPARE(fb.data(), QByteArray("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"));
+ }
+
+ {
+ // Failure caused by write(QString)
+ FakeBuffer fb;
+ QVERIFY(fb.open(QBuffer::ReadWrite));
+ fb.setCapacity(strlen("<?xml version=\""));
+ QXmlStreamWriter writer(&fb);
+ writer.writeStartDocument();
+ QVERIFY(writer.hasError());
+ QCOMPARE(fb.data(), QByteArray("<?xml version=\""));
+ }
+
+ {
+ // Failure caused by write(char *)
+ FakeBuffer fb;
+ QVERIFY(fb.open(QBuffer::ReadWrite));
+ fb.setCapacity(strlen("<?xml version=\"1.0"));
+ QXmlStreamWriter writer(&fb);
+ writer.writeStartDocument();
+ QVERIFY(writer.hasError());
+ QCOMPARE(fb.data(), QByteArray("<?xml version=\"1.0"));
+ }
+
+ {
+ // Failure caused by write(QStringRef)
+ FakeBuffer fb;
+ QVERIFY(fb.open(QBuffer::ReadWrite));
+ fb.setCapacity(strlen("<?xml version=\"1.0\" encoding=\"UTF-8\"?><test xmlns:"));
+ QXmlStreamWriter writer(&fb);
+ writer.writeStartDocument();
+ writer.writeStartElement("test");
+ writer.writeNamespace("http://foo.bar", "foo");
+ QVERIFY(writer.hasError());
+ QCOMPARE(fb.data(), QByteArray("<?xml version=\"1.0\" encoding=\"UTF-8\"?><test xmlns:"));
+ }
+
+ {
+ // Refusal to write after 1st failure
+ FakeBuffer fb;
+ QVERIFY(fb.open(QBuffer::ReadWrite));
+ fb.setCapacity(10);
+ QXmlStreamWriter writer(&fb);
+ writer.writeStartDocument();
+ QVERIFY(writer.hasError());
+ QCOMPARE(fb.data(), QByteArray("<?xml vers"));
+ fb.setCapacity(1000);
+ writer.writeStartElement("test"); // literal & qstring
+ writer.writeNamespace("http://foo.bar", "foo"); // literal & qstringref
+ QVERIFY(writer.hasError());
+ QCOMPARE(fb.data(), QByteArray("<?xml vers"));
+ }
+
+}
+
#include "tst_qxmlstream.moc"
// vim: et:ts=4:sw=4:sts=4
diff --git a/tests/benchmarks/corelib/thread/qmutex/qmutex.pro b/tests/benchmarks/corelib/thread/qmutex/qmutex.pro
index eda2f11..8fda5fa 100644
--- a/tests/benchmarks/corelib/thread/qmutex/qmutex.pro
+++ b/tests/benchmarks/corelib/thread/qmutex/qmutex.pro
@@ -1,6 +1,6 @@
load(qttest_p4)
TEMPLATE = app
TARGET = tst_bench_qmutex
-
+QT -= gui
SOURCES += tst_qmutex.cpp
diff --git a/tests/benchmarks/corelib/thread/qmutex/tst_qmutex.cpp b/tests/benchmarks/corelib/thread/qmutex/tst_qmutex.cpp
index fded508..b0c5702 100644
--- a/tests/benchmarks/corelib/thread/qmutex/tst_qmutex.cpp
+++ b/tests/benchmarks/corelib/thread/qmutex/tst_qmutex.cpp
@@ -39,34 +39,93 @@
**
****************************************************************************/
-#include <qtest.h>
-#include <QtCore>
+#include <QtCore/QtCore>
+#include <QtTest/QtTest>
#include <math.h>
-//TESTED_FILES=
+#ifdef Q_OS_UNIX
+# include <pthread.h>
+# include <errno.h>
+typedef pthread_mutex_t NativeMutexType;
+void NativeMutexInitialize(NativeMutexType *mutex)
+{
+ pthread_mutex_init(mutex, NULL);
+}
+void NativeMutexDestroy(NativeMutexType *mutex)
+{
+ pthread_mutex_destroy(mutex);
+}
+void NativeMutexLock(NativeMutexType *mutex)
+{
+ pthread_mutex_lock(mutex);
+}
+void NativeMutexUnlock(NativeMutexType *mutex)
+{
+ pthread_mutex_unlock(mutex);
+}
+#elif defined(Q_OS_WIN)
+# define _WIN32_WINNT 0x0400
+# include <windows.h>
+typedef CRITICAL_SECTION NativeMutexType;
+void NativeMutexInitialize(NativeMutexType *mutex)
+{
+ InitializeCriticalSection(mutex);
+}
+void NativeMutexDestroy(NativeMutexType *mutex)
+{
+ DeleteCriticalSection(mutex);
+}
+void NativeMutexLock(NativeMutexType *mutex)
+{
+ EnterCriticalSection(mutex);
+}
+void NativeMutexUnlock(NativeMutexType *mutex)
+{
+ LeaveCriticalSection(mutex);
+}
+#endif
+//TESTED_FILES=
class tst_QMutex : public QObject
{
Q_OBJECT
+ int threadCount;
+
public:
- tst_QMutex();
- virtual ~tst_QMutex();
+ // barriers for the contended tests
+ static QSemaphore semaphore1, semaphore2, semaphore3, semaphore4;
+
+ tst_QMutex()
+ {
+ // at least 2 threads, even on single cpu/core machines
+ threadCount = qMax(2, QThread::idealThreadCount());
+ qDebug("thread count: %d", threadCount);
+ }
private slots:
void noThread_data();
void noThread();
-};
-tst_QMutex::tst_QMutex()
-{
-}
+ void uncontendedNative();
+ void uncontendedQMutex();
+ void uncontendedQMutexLocker();
-tst_QMutex::~tst_QMutex()
-{
-}
+ void contendedNative_data();
+ void contendedQMutex_data() { contendedNative_data(); }
+ void contendedQMutexLocker_data() { contendedNative_data(); }
+
+ void contendedNative();
+ void contendedQMutex();
+ void contendedQMutexLocker();
+};
+
+QSemaphore tst_QMutex::semaphore1;
+QSemaphore tst_QMutex::semaphore2;
+QSemaphore tst_QMutex::semaphore3;
+QSemaphore tst_QMutex::semaphore4;
void tst_QMutex::noThread_data()
{
@@ -127,5 +186,248 @@ void tst_QMutex::noThread()
QCOMPARE(int(count), N);
}
+void tst_QMutex::uncontendedNative()
+{
+ NativeMutexType mutex;
+ NativeMutexInitialize(&mutex);
+ QBENCHMARK {
+ NativeMutexLock(&mutex);
+ NativeMutexUnlock(&mutex);
+ }
+ NativeMutexDestroy(&mutex);
+}
+
+void tst_QMutex::uncontendedQMutex()
+{
+ QMutex mutex;
+ QBENCHMARK {
+ mutex.lock();
+ mutex.unlock();
+ }
+}
+
+void tst_QMutex::uncontendedQMutexLocker()
+{
+ QMutex mutex;
+ QBENCHMARK {
+ QMutexLocker locker(&mutex);
+ }
+}
+
+void tst_QMutex::contendedNative_data()
+{
+ QTest::addColumn<int>("iterations");
+ QTest::addColumn<int>("msleepDuration");
+ QTest::addColumn<bool>("use2mutexes");
+
+ QTest::newRow("baseline") << 0 << -1 << false;
+
+ QTest::newRow("no msleep, 1 mutex") << 1000 << -1 << false;
+ QTest::newRow("no msleep, 2 mutexes") << 1000 << -1 << true;
+ QTest::newRow("msleep(0), 1 mutex") << 1000 << 0 << false;
+ QTest::newRow("msleep(0), 2 mutexes") << 1000 << 0 << true;
+ QTest::newRow("msleep(1), 1 mutex") << 10 << 1 << false;
+ QTest::newRow("msleep(1), 2 mutexes") << 10 << 1 << true;
+ QTest::newRow("msleep(2), 1 mutex") << 10 << 2 << false;
+ QTest::newRow("msleep(2), 2 mutexes") << 10 << 2 << true;
+ QTest::newRow("msleep(10), 1 mutex") << 10 << 10 << false;
+ QTest::newRow("msleep(10), 2 mutexes") << 10 << 10 << true;
+}
+
+class NativeMutexThread : public QThread
+{
+ NativeMutexType *mutex1, *mutex2;
+ int iterations, msleepDuration;
+ bool use2mutexes;
+public:
+ bool done;
+ NativeMutexThread(NativeMutexType *mutex1, NativeMutexType *mutex2, int iterations, int msleepDuration, bool use2mutexes)
+ : mutex1(mutex1), mutex2(mutex2), iterations(iterations), msleepDuration(msleepDuration), use2mutexes(use2mutexes), done(false)
+ { }
+ void run() {
+ forever {
+ tst_QMutex::semaphore1.release();
+ tst_QMutex::semaphore2.acquire();
+ if (done)
+ break;
+ for (int i = 0; i < iterations; ++i) {
+ NativeMutexLock(mutex1);
+ if (use2mutexes)
+ NativeMutexLock(mutex2);
+ if (msleepDuration >= 0)
+ msleep(msleepDuration);
+ if (use2mutexes)
+ NativeMutexUnlock(mutex2);
+ NativeMutexUnlock(mutex1);
+
+ QThread::yieldCurrentThread();
+ }
+ tst_QMutex::semaphore3.release();
+ tst_QMutex::semaphore4.acquire();
+ }
+ }
+};
+
+void tst_QMutex::contendedNative()
+{
+ QFETCH(int, iterations);
+ QFETCH(int, msleepDuration);
+ QFETCH(bool, use2mutexes);
+
+ NativeMutexType mutex1, mutex2;
+ NativeMutexInitialize(&mutex1);
+ NativeMutexInitialize(&mutex2);
+
+ QVector<NativeMutexThread *> threads(threadCount);
+ for (int i = 0; i < threads.count(); ++i) {
+ threads[i] = new NativeMutexThread(&mutex1, &mutex2, iterations, msleepDuration, use2mutexes);
+ threads[i]->start();
+ }
+
+ QBENCHMARK {
+ semaphore1.acquire(threadCount);
+ semaphore2.release(threadCount);
+ semaphore3.acquire(threadCount);
+ semaphore4.release(threadCount);
+ }
+
+ for (int i = 0; i < threads.count(); ++i)
+ threads[i]->done = true;
+ semaphore1.acquire(threadCount);
+ semaphore2.release(threadCount);
+ for (int i = 0; i < threads.count(); ++i)
+ threads[i]->wait();
+ qDeleteAll(threads);
+
+ NativeMutexDestroy(&mutex1);
+ NativeMutexDestroy(&mutex2);
+}
+
+class QMutexThread : public QThread
+{
+ QMutex *mutex1, *mutex2;
+ int iterations, msleepDuration;
+ bool use2mutexes;
+public:
+ bool done;
+ QMutexThread(QMutex *mutex1, QMutex *mutex2, int iterations, int msleepDuration, bool use2mutexes)
+ : mutex1(mutex1), mutex2(mutex2), iterations(iterations), msleepDuration(msleepDuration), use2mutexes(use2mutexes), done(false)
+ { }
+ void run() {
+ forever {
+ tst_QMutex::semaphore1.release();
+ tst_QMutex::semaphore2.acquire();
+ if (done)
+ break;
+ for (int i = 0; i < iterations; ++i) {
+ mutex1->lock();
+ if (use2mutexes)
+ mutex2->lock();
+ if (msleepDuration >= 0)
+ msleep(msleepDuration);
+ if (use2mutexes)
+ mutex2->unlock();
+ mutex1->unlock();
+
+ QThread::yieldCurrentThread();
+ }
+ tst_QMutex::semaphore3.release();
+ tst_QMutex::semaphore4.acquire();
+ }
+ }
+};
+
+void tst_QMutex::contendedQMutex()
+{
+ QFETCH(int, iterations);
+ QFETCH(int, msleepDuration);
+ QFETCH(bool, use2mutexes);
+
+ QMutex mutex1, mutex2;
+
+ QVector<QMutexThread *> threads(threadCount);
+ for (int i = 0; i < threads.count(); ++i) {
+ threads[i] = new QMutexThread(&mutex1, &mutex2, iterations, msleepDuration, use2mutexes);
+ threads[i]->start();
+ }
+
+ QBENCHMARK {
+ semaphore1.acquire(threadCount);
+ semaphore2.release(threadCount);
+ semaphore3.acquire(threadCount);
+ semaphore4.release(threadCount);
+ }
+
+ for (int i = 0; i < threads.count(); ++i)
+ threads[i]->done = true;
+ semaphore1.acquire(threadCount);
+ semaphore2.release(threadCount);
+ for (int i = 0; i < threads.count(); ++i)
+ threads[i]->wait();
+ qDeleteAll(threads);
+}
+
+class QMutexLockerThread : public QThread
+{
+ QMutex *mutex1, *mutex2;
+ int iterations, msleepDuration;
+ bool use2mutexes;
+public:
+ bool done;
+ QMutexLockerThread(QMutex *mutex1, QMutex *mutex2, int iterations, int msleepDuration, bool use2mutexes)
+ : mutex1(mutex1), mutex2(mutex2), iterations(iterations), msleepDuration(msleepDuration), use2mutexes(use2mutexes), done(false)
+ { }
+ void run() {
+ forever {
+ tst_QMutex::semaphore1.release();
+ tst_QMutex::semaphore2.acquire();
+ if (done)
+ break;
+ for (int i = 0; i < iterations; ++i) {
+ {
+ QMutexLocker locker1(mutex1);
+ QMutexLocker locker2(use2mutexes ? mutex2 : 0);
+ if (msleepDuration >= 0)
+ msleep(msleepDuration);
+ }
+
+ QThread::yieldCurrentThread();
+ }
+ tst_QMutex::semaphore3.release();
+ tst_QMutex::semaphore4.acquire();
+ }
+ }
+};
+
+void tst_QMutex::contendedQMutexLocker()
+{
+ QFETCH(int, iterations);
+ QFETCH(int, msleepDuration);
+ QFETCH(bool, use2mutexes);
+
+ QMutex mutex1, mutex2;
+
+ QVector<QMutexLockerThread *> threads(threadCount);
+ for (int i = 0; i < threads.count(); ++i) {
+ threads[i] = new QMutexLockerThread(&mutex1, &mutex2, iterations, msleepDuration, use2mutexes);
+ threads[i]->start();
+ }
+
+ QBENCHMARK {
+ semaphore1.acquire(threadCount);
+ semaphore2.release(threadCount);
+ semaphore3.acquire(threadCount);
+ semaphore4.release(threadCount);
+ }
+
+ for (int i = 0; i < threads.count(); ++i)
+ threads[i]->done = true;
+ semaphore1.acquire(threadCount);
+ semaphore2.release(threadCount);
+ for (int i = 0; i < threads.count(); ++i)
+ threads[i]->wait();
+ qDeleteAll(threads);
+}
+
QTEST_MAIN(tst_QMutex)
#include "tst_qmutex.moc"
diff --git a/tests/shared/filesystem.h b/tests/shared/filesystem.h
index 8274346..4775671 100644
--- a/tests/shared/filesystem.h
+++ b/tests/shared/filesystem.h
@@ -55,6 +55,9 @@
#define IO_REPARSE_TAG_MOUNT_POINT (0xA0000003L)
#endif
#define REPARSE_MOUNTPOINT_HEADER_SIZE 8
+#ifndef FSCTL_SET_REPARSE_POINT
+#define FSCTL_SET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 41, METHOD_BUFFERED, FILE_SPECIAL_ACCESS)
+#endif
#endif
struct FileSystem