summaryrefslogtreecommitdiffstats
path: root/tests/auto/qsharedmemory
diff options
context:
space:
mode:
authorLars Knoll <lars.knoll@nokia.com>2009-03-23 09:34:13 (GMT)
committerSimon Hausmann <simon.hausmann@nokia.com>2009-03-23 09:34:13 (GMT)
commit67ad0519fd165acee4a4d2a94fa502e9e4847bd0 (patch)
tree1dbf50b3dff8d5ca7e9344733968c72704eb15ff /tests/auto/qsharedmemory
downloadQt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.zip
Qt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.tar.gz
Qt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.tar.bz2
Long live Qt!
Diffstat (limited to 'tests/auto/qsharedmemory')
-rw-r--r--tests/auto/qsharedmemory/.gitignore3
-rw-r--r--tests/auto/qsharedmemory/lackey/lackey.pro18
-rw-r--r--tests/auto/qsharedmemory/lackey/main.cpp368
-rw-r--r--tests/auto/qsharedmemory/lackey/scripts/consumer.js41
-rw-r--r--tests/auto/qsharedmemory/lackey/scripts/producer.js36
-rw-r--r--tests/auto/qsharedmemory/lackey/scripts/readonly_segfault.js4
-rw-r--r--tests/auto/qsharedmemory/lackey/scripts/systemlock_read.js11
-rw-r--r--tests/auto/qsharedmemory/lackey/scripts/systemlock_readwrite.js11
-rw-r--r--tests/auto/qsharedmemory/lackey/scripts/systemsemaphore_acquire.js18
-rw-r--r--tests/auto/qsharedmemory/lackey/scripts/systemsemaphore_acquirerelease.js11
-rw-r--r--tests/auto/qsharedmemory/lackey/scripts/systemsemaphore_release.js11
-rw-r--r--tests/auto/qsharedmemory/qsharedmemory.pro4
-rw-r--r--tests/auto/qsharedmemory/qsystemlock/qsystemlock.pro16
-rw-r--r--tests/auto/qsharedmemory/qsystemlock/tst_qsystemlock.cpp222
-rw-r--r--tests/auto/qsharedmemory/src/qsystemlock.cpp246
-rw-r--r--tests/auto/qsharedmemory/src/qsystemlock.h135
-rw-r--r--tests/auto/qsharedmemory/src/qsystemlock_p.h106
-rw-r--r--tests/auto/qsharedmemory/src/qsystemlock_unix.cpp209
-rw-r--r--tests/auto/qsharedmemory/src/qsystemlock_win.cpp190
-rw-r--r--tests/auto/qsharedmemory/src/src.pri10
-rw-r--r--tests/auto/qsharedmemory/test/test.pro29
-rw-r--r--tests/auto/qsharedmemory/tst_qsharedmemory.cpp744
22 files changed, 2443 insertions, 0 deletions
diff --git a/tests/auto/qsharedmemory/.gitignore b/tests/auto/qsharedmemory/.gitignore
new file mode 100644
index 0000000..03ddcf2
--- /dev/null
+++ b/tests/auto/qsharedmemory/.gitignore
@@ -0,0 +1,3 @@
+tst_qsharedmemory
+lackey/lackey.exe
+qsystemlock/tst_qsystemlock.exe
diff --git a/tests/auto/qsharedmemory/lackey/lackey.pro b/tests/auto/qsharedmemory/lackey/lackey.pro
new file mode 100644
index 0000000..9d2fcad
--- /dev/null
+++ b/tests/auto/qsharedmemory/lackey/lackey.pro
@@ -0,0 +1,18 @@
+include(../src/src.pri)
+
+QT = core script
+
+CONFIG += qtestlib
+
+DESTDIR = ./
+
+win32: CONFIG += console
+mac:CONFIG -= app_bundle
+
+DEFINES += QSHAREDMEMORY_DEBUG
+DEFINES += QSYSTEMSEMAPHORE_DEBUG
+
+SOURCES += main.cpp
+TARGET = lackey
+
+
diff --git a/tests/auto/qsharedmemory/lackey/main.cpp b/tests/auto/qsharedmemory/lackey/main.cpp
new file mode 100644
index 0000000..5b3cffc
--- /dev/null
+++ b/tests/auto/qsharedmemory/lackey/main.cpp
@@ -0,0 +1,368 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the test suite 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+
+#include <qscriptengine.h>
+
+#include <QtCore/QFile>
+#include <QtCore/QTextStream>
+#include <QTest>
+
+#include <qstringlist.h>
+#include <stdlib.h>
+#include <qsharedmemory.h>
+#include <qsystemsemaphore.h>
+#include <qsystemlock.h>
+
+class ScriptSystemSemaphore : public QObject
+{
+ Q_OBJECT
+
+public:
+ ScriptSystemSemaphore(QObject *parent = 0) : QObject(parent), ss(QString())
+ {
+ }
+
+public slots:
+ bool acquire()
+ {
+ return ss.acquire();
+ };
+
+ bool release(int n = 1)
+ {
+ return ss.release(n);
+ };
+
+ void setKey(const QString &key, int n = 0)
+ {
+ ss.setKey(key, n);
+ };
+
+ QString key() const
+ {
+ return ss.key();
+ }
+
+private:
+ QSystemSemaphore ss;
+};
+
+class ScriptSystemLock : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString key WRITE setKey READ key)
+
+public:
+ ScriptSystemLock(QObject *parent = 0) : QObject(parent), sl(QString())
+ {
+ }
+
+public slots:
+
+ bool lockReadOnly()
+ {
+ return sl.lock(QSystemLock::ReadOnly);
+ }
+
+ bool lock()
+ {
+ return sl.lock();
+ };
+
+ bool unlock()
+ {
+ return sl.unlock();
+ };
+
+ void setKey(const QString &key)
+ {
+ sl.setKey(key);
+ };
+
+ QString key() const
+ {
+ return sl.key();
+ }
+
+private:
+ QSystemLock sl;
+};
+
+class ScriptSharedMemory : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(bool attached READ isAttached)
+ Q_PROPERTY(QString key WRITE setKey READ key)
+
+public:
+ enum SharedMemoryError
+ {
+ NoError = 0,
+ PermissionDenied = 1,
+ InvalidSize = 2,
+ KeyError = 3,
+ AlreadyExists = 4,
+ NotFound = 5,
+ LockError = 6,
+ OutOfResources = 7,
+ UnknownError = 8
+ };
+
+ ScriptSharedMemory(QObject *parent = 0) : QObject(parent)
+ {
+ }
+
+public slots:
+ void sleep(int x) const
+ {
+ QTest::qSleep(x);
+ }
+
+ bool create(int size)
+ {
+ return sm.create(size);
+ };
+
+ bool createReadOnly(int size)
+ {
+ return sm.create(size, QSharedMemory::ReadOnly);
+ };
+
+ int size() const
+ {
+ return sm.size();
+ };
+
+ bool attach()
+ {
+ return sm.attach();
+ };
+
+ bool attachReadOnly()
+ {
+ return sm.attach(QSharedMemory::ReadOnly);
+ };
+
+ bool isAttached() const
+ {
+ return sm.isAttached();
+ };
+
+ bool detach()
+ {
+ return sm.detach();
+ };
+
+ int error() const
+ {
+ return (int)sm.error();
+ };
+
+ QString errorString() const
+ {
+ return sm.errorString();
+ };
+
+ void set(int i, QChar value)
+ {
+ ((char*)sm.data())[i] = value.toLatin1();
+ }
+
+ QString get(int i)
+ {
+ return QChar::fromLatin1(((char*)sm.data())[i]);
+ }
+
+ char *data() const
+ {
+ return (char*)sm.data();
+ };
+
+ void setKey(const QString &key)
+ {
+ sm.setKey(key);
+ };
+
+ QString key() const
+ {
+ return sm.key();
+ }
+
+ bool lock()
+ {
+ return sm.lock();
+ }
+
+ bool unlock()
+ {
+ return sm.unlock();
+ }
+
+private:
+ QSharedMemory sm;
+};
+
+Q_SCRIPT_DECLARE_QMETAOBJECT(ScriptSharedMemory, QObject*);
+Q_SCRIPT_DECLARE_QMETAOBJECT(ScriptSystemLock, QObject*);
+Q_SCRIPT_DECLARE_QMETAOBJECT(ScriptSystemSemaphore, QObject*);
+
+static void interactive(QScriptEngine &eng)
+{
+#ifdef Q_OS_WINCE
+ fprintf(stderr, "Interactive mode not supported on Windows CE\n");
+ return;
+#endif
+ QTextStream qin(stdin, QFile::ReadOnly);
+
+ const char *qscript_prompt = "qs> ";
+ const char *dot_prompt = ".... ";
+ const char *prompt = qscript_prompt;
+
+ QString code;
+
+ forever {
+ QString line;
+
+ printf("%s", prompt);
+ fflush(stdout);
+
+ line = qin.readLine();
+ if (line.isNull())
+ break;
+
+ code += line;
+ code += QLatin1Char('\n');
+
+ if (line.trimmed().isEmpty()) {
+ continue;
+
+ } else if (! eng.canEvaluate(code)) {
+ prompt = dot_prompt;
+
+ } else {
+ QScriptValue result = eng.evaluate(code);
+ code.clear();
+ prompt = qscript_prompt;
+ if (!result.isUndefined())
+ fprintf(stderr, "%s\n", qPrintable(result.toString()));
+ }
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication app(argc, argv);
+
+ QScriptEngine eng;
+ QScriptValue globalObject = eng.globalObject();
+
+ QScriptValue sm = qScriptValueFromQMetaObject<ScriptSharedMemory>(&eng);
+ eng.globalObject().setProperty("ScriptSharedMemory", sm);
+
+ QScriptValue sl = qScriptValueFromQMetaObject<ScriptSystemLock>(&eng);
+ eng.globalObject().setProperty("ScriptSystemLock", sl);
+
+ QScriptValue ss = qScriptValueFromQMetaObject<ScriptSystemSemaphore>(&eng);
+ eng.globalObject().setProperty("ScriptSystemSemaphore", ss);
+
+
+ if (! *++argv) {
+ interactive(eng);
+ return EXIT_SUCCESS;
+ }
+
+ QStringList arguments = app.arguments();
+ arguments.takeFirst();
+
+ while (!arguments.isEmpty()) {
+ QString fn = arguments.takeFirst();
+
+ if (fn == QLatin1String("-i")) {
+ interactive(eng);
+ break;
+ }
+
+ QString contents;
+
+ if (fn == QLatin1String("-")) {
+ QTextStream stream(stdin, QFile::ReadOnly);
+ contents = stream.readAll();
+ } else {
+ QFile file(fn);
+ if (!file.exists()) {
+ fprintf(stderr, "%s doesn't exists\n", qPrintable(fn));
+ return EXIT_FAILURE;
+ }
+ if (file.open(QFile::ReadOnly)) {
+ QTextStream stream(&file);
+ contents = stream.readAll();
+ file.close();
+ }
+ }
+
+ if (contents.isEmpty())
+ continue;
+
+ if (contents[0] == '#') {
+ contents.prepend("//");
+ QScriptValue args = eng.newArray();
+ args.setProperty("0", QScriptValue(&eng, fn));
+ int i = 1;
+ while (!arguments.isEmpty())
+ args.setProperty(i++, QScriptValue(&eng, arguments.takeFirst()));
+ eng.currentContext()->activationObject().setProperty("args", args);
+ }
+ QScriptValue r = eng.evaluate(contents);
+ if (eng.hasUncaughtException()) {
+ int line = eng.uncaughtExceptionLineNumber();
+ fprintf(stderr, "%d: %s\n\t%s\n\n", line, qPrintable(fn), qPrintable(r.toString()));
+ return EXIT_FAILURE;
+ }
+ if (r.isNumber())
+ return r.toInt32();
+ }
+
+ return EXIT_SUCCESS;
+}
+
+#include "main.moc"
diff --git a/tests/auto/qsharedmemory/lackey/scripts/consumer.js b/tests/auto/qsharedmemory/lackey/scripts/consumer.js
new file mode 100644
index 0000000..4d12dca
--- /dev/null
+++ b/tests/auto/qsharedmemory/lackey/scripts/consumer.js
@@ -0,0 +1,41 @@
+function QVERIFY(x, debugInfo) {
+ if (!(x)) {
+ print(debugInfo);
+ throw(debugInfo);
+ }
+}
+
+var consumer = new ScriptSharedMemory;
+consumer.setKey("market");
+
+//print("consumer starting");
+var tries = 0;;
+while(!consumer.attach()) {
+ if (tries == 5000) {
+ var message = "consumer exiting, waiting too long";
+ print(message);
+ throw(message);
+ }
+ ++tries;
+ consumer.sleep(1);
+}
+//print("consumer attached");
+
+
+var i = 0;
+while(true) {
+ QVERIFY(consumer.lock(), "lock");
+ if (consumer.get(0) == 'Q') {
+ consumer.set(0, ++i);
+ //print ("consumer sets" + i);
+ }
+ if (consumer.get(0) == 'E') {
+ QVERIFY(consumer.unlock(), "unlock");
+ break;
+ }
+ QVERIFY(consumer.unlock(), "unlock");
+ consumer.sleep(10);
+}
+
+//print("consumer detaching");
+QVERIFY(consumer.detach());
diff --git a/tests/auto/qsharedmemory/lackey/scripts/producer.js b/tests/auto/qsharedmemory/lackey/scripts/producer.js
new file mode 100644
index 0000000..88b2b80
--- /dev/null
+++ b/tests/auto/qsharedmemory/lackey/scripts/producer.js
@@ -0,0 +1,36 @@
+function QVERIFY(x, debugInfo) {
+ if (!(x)) {
+ print(debugInfo);
+ throw(debugInfo);
+ }
+}
+
+var producer = new ScriptSharedMemory;
+producer.setKey("market");
+
+var size = 1024;
+if (!producer.create(size)) {
+ QVERIFY(producer.error() == 4, "create");
+ QVERIFY(producer.attach());
+}
+//print ("producer created and attached");
+
+var i = 0;
+while(i < 5) {
+ QVERIFY(producer.lock(), "lock");
+ if (producer.get(0) == 'Q') {
+ QVERIFY(producer.unlock(), "unlock");
+ producer.sleep(1);
+ continue;
+ }
+ //print("producer: " + i);
+ ++i;
+ producer.set(0, 'Q');
+ QVERIFY(producer.unlock(), "unlock");
+ producer.sleep(1);
+}
+QVERIFY(producer.lock());
+producer.set(0, 'E');
+QVERIFY(producer.unlock());
+
+//print ("producer done");
diff --git a/tests/auto/qsharedmemory/lackey/scripts/readonly_segfault.js b/tests/auto/qsharedmemory/lackey/scripts/readonly_segfault.js
new file mode 100644
index 0000000..3eaf789
--- /dev/null
+++ b/tests/auto/qsharedmemory/lackey/scripts/readonly_segfault.js
@@ -0,0 +1,4 @@
+var sm = new ScriptSharedMemory;
+sm.setKey("readonly_segfault");
+sm.createReadOnly(1024);
+var data = sm.set(0, "a");
diff --git a/tests/auto/qsharedmemory/lackey/scripts/systemlock_read.js b/tests/auto/qsharedmemory/lackey/scripts/systemlock_read.js
new file mode 100644
index 0000000..1048bc7
--- /dev/null
+++ b/tests/auto/qsharedmemory/lackey/scripts/systemlock_read.js
@@ -0,0 +1,11 @@
+function QVERIFY(x, debugInfo) {
+ if (!(x)) {
+ print(debugInfo);
+ throw(debugInfo);
+ }
+}
+
+var lock = new ScriptSystemLock;
+lock.setKey("market");
+QVERIFY(lock.lockReadOnly());
+QVERIFY(lock.unlock()); \ No newline at end of file
diff --git a/tests/auto/qsharedmemory/lackey/scripts/systemlock_readwrite.js b/tests/auto/qsharedmemory/lackey/scripts/systemlock_readwrite.js
new file mode 100644
index 0000000..fc6367f
--- /dev/null
+++ b/tests/auto/qsharedmemory/lackey/scripts/systemlock_readwrite.js
@@ -0,0 +1,11 @@
+function QVERIFY(x, debugInfo) {
+ if (!(x)) {
+ print(debugInfo);
+ throw(debugInfo);
+ }
+}
+
+var lock = new ScriptSystemLock;
+lock.setKey("market");
+QVERIFY(lock.lock());
+QVERIFY(lock.unlock());
diff --git a/tests/auto/qsharedmemory/lackey/scripts/systemsemaphore_acquire.js b/tests/auto/qsharedmemory/lackey/scripts/systemsemaphore_acquire.js
new file mode 100644
index 0000000..5cff429
--- /dev/null
+++ b/tests/auto/qsharedmemory/lackey/scripts/systemsemaphore_acquire.js
@@ -0,0 +1,18 @@
+#/bin/qscript
+function QVERIFY(x, debugInfo) {
+ if (!(x)) {
+ print(debugInfo);
+ throw(debugInfo);
+ }
+}
+
+
+var sem = new ScriptSystemSemaphore;
+sem.setKey("store");
+
+var count = Number(args[1]);
+if (isNaN(count))
+ count = 1;
+for (var i = 0; i < count; ++i)
+ QVERIFY(sem.acquire());
+print("done aquiring");
diff --git a/tests/auto/qsharedmemory/lackey/scripts/systemsemaphore_acquirerelease.js b/tests/auto/qsharedmemory/lackey/scripts/systemsemaphore_acquirerelease.js
new file mode 100644
index 0000000..cedde3f
--- /dev/null
+++ b/tests/auto/qsharedmemory/lackey/scripts/systemsemaphore_acquirerelease.js
@@ -0,0 +1,11 @@
+function QVERIFY(x, debugInfo) {
+ if (!(x)) {
+ print(debugInfo);
+ throw(debugInfo);
+ }
+}
+
+var lock = new ScriptSystemSemaphore;
+lock.setKey("store");
+QVERIFY(lock.acquire());
+QVERIFY(lock.release());
diff --git a/tests/auto/qsharedmemory/lackey/scripts/systemsemaphore_release.js b/tests/auto/qsharedmemory/lackey/scripts/systemsemaphore_release.js
new file mode 100644
index 0000000..c805e0f
--- /dev/null
+++ b/tests/auto/qsharedmemory/lackey/scripts/systemsemaphore_release.js
@@ -0,0 +1,11 @@
+function QVERIFY(x, debugInfo) {
+ if (!(x)) {
+ print(debugInfo);
+ throw(debugInfo);
+ }
+}
+
+var sem = new ScriptSystemSemaphore;
+sem.setKey("store");
+QVERIFY(sem.release());
+print ("done releasing");
diff --git a/tests/auto/qsharedmemory/qsharedmemory.pro b/tests/auto/qsharedmemory/qsharedmemory.pro
new file mode 100644
index 0000000..0aad554
--- /dev/null
+++ b/tests/auto/qsharedmemory/qsharedmemory.pro
@@ -0,0 +1,4 @@
+TEMPLATE = subdirs
+SUBDIRS = lackey test qsystemlock
+
+
diff --git a/tests/auto/qsharedmemory/qsystemlock/qsystemlock.pro b/tests/auto/qsharedmemory/qsystemlock/qsystemlock.pro
new file mode 100644
index 0000000..042ab3f
--- /dev/null
+++ b/tests/auto/qsharedmemory/qsystemlock/qsystemlock.pro
@@ -0,0 +1,16 @@
+CONFIG += qttest_p4
+#QT = core
+
+include(../src/src.pri)
+win32: CONFIG += console
+mac:CONFIG -= app_bundle
+
+DESTDIR = ./
+
+DEFINES += QSHAREDMEMORY_DEBUG
+DEFINES += QSYSTEMSEMAPHORE_DEBUG
+
+SOURCES += tst_qsystemlock.cpp
+TARGET = tst_qsystemlock
+
+
diff --git a/tests/auto/qsharedmemory/qsystemlock/tst_qsystemlock.cpp b/tests/auto/qsharedmemory/qsystemlock/tst_qsystemlock.cpp
new file mode 100644
index 0000000..c72b586
--- /dev/null
+++ b/tests/auto/qsharedmemory/qsystemlock/tst_qsystemlock.cpp
@@ -0,0 +1,222 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the test suite 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+#include <qsystemlock.h>
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+#define EXISTING_SHARE "existing"
+
+class tst_QSystemLock : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QSystemLock();
+ virtual ~tst_QSystemLock();
+
+public Q_SLOTS:
+ void init();
+ void cleanup();
+
+private slots:
+ void key_data();
+ void key();
+
+ void basicLock();
+ void complexLock();
+ void lockModes();
+ void sucessive();
+ void processes_data();
+ void processes();
+
+private:
+ QSystemLock *existingLock;
+
+};
+
+tst_QSystemLock::tst_QSystemLock()
+{
+}
+
+tst_QSystemLock::~tst_QSystemLock()
+{
+}
+
+void tst_QSystemLock::init()
+{
+ existingLock = new QSystemLock(EXISTING_SHARE);
+}
+
+void tst_QSystemLock::cleanup()
+{
+ delete existingLock;
+}
+
+void tst_QSystemLock::key_data()
+{
+ QTest::addColumn<QString>("constructorKey");
+ QTest::addColumn<QString>("setKey");
+
+ QTest::newRow("null, null") << QString() << QString();
+ QTest::newRow("null, one") << QString() << QString("one");
+ QTest::newRow("one, two") << QString("one") << QString("two");
+}
+
+/*!
+ Basic key testing
+ */
+void tst_QSystemLock::key()
+{
+ QFETCH(QString, constructorKey);
+ QFETCH(QString, setKey);
+
+ QSystemLock sl(constructorKey);
+ QCOMPARE(sl.key(), constructorKey);
+ sl.setKey(setKey);
+ QCOMPARE(sl.key(), setKey);
+}
+
+void tst_QSystemLock::basicLock()
+{
+ QSystemLock lock("foo");
+ QVERIFY(lock.lock());
+ QVERIFY(lock.unlock());
+}
+
+void tst_QSystemLock::complexLock()
+{
+ QSystemLock lock("foo");
+ QVERIFY(lock.lock(QSystemLock::ReadOnly));
+ QVERIFY(lock.unlock());
+
+ QVERIFY(lock.lock(QSystemLock::ReadWrite));
+ QVERIFY(lock.unlock());
+
+ QVERIFY(lock.lock(QSystemLock::ReadOnly));
+ QVERIFY(lock.lock(QSystemLock::ReadOnly));
+ QVERIFY(lock.unlock());
+ QVERIFY(lock.unlock());
+}
+
+void tst_QSystemLock::lockModes()
+{
+ QSystemLock reader1("library");
+ QSystemLock reader2("library");
+
+ QSystemLock librarian("library");
+ QVERIFY(reader1.lock(QSystemLock::ReadOnly));
+ QVERIFY(reader2.lock(QSystemLock::ReadOnly));
+ QVERIFY(reader1.unlock());
+ QVERIFY(reader2.unlock());
+ QVERIFY(librarian.lock(QSystemLock::ReadWrite));
+ QVERIFY(librarian.unlock());
+}
+
+void tst_QSystemLock::sucessive()
+{
+ QSystemLock lock("library");
+ QVERIFY(lock.lock(QSystemLock::ReadOnly));
+ QVERIFY(lock.lock(QSystemLock::ReadOnly));
+ QVERIFY(lock.lock(QSystemLock::ReadOnly));
+ QVERIFY(lock.lock(QSystemLock::ReadOnly));
+ QVERIFY(lock.lock(QSystemLock::ReadOnly));
+ QVERIFY(lock.unlock());
+ QVERIFY(lock.unlock());
+ QVERIFY(lock.unlock());
+ QVERIFY(lock.unlock());
+ QVERIFY(lock.unlock());
+ QVERIFY(!lock.unlock());
+}
+
+void tst_QSystemLock::processes_data()
+{
+ QTest::addColumn<int>("readOnly");
+ QTest::addColumn<int>("readWrite");
+ for (int i = 0; i < 5; ++i) {
+ QTest::newRow("1/0 process") << 1 << 0;
+ QTest::newRow("0/1 process") << 0 << 1;
+ QTest::newRow("0/4 process") << 0 << 4;
+ QTest::newRow("1/1 process") << 1 << 1;
+ QTest::newRow("10/1 process") << 10 << 1;
+ QTest::newRow("1/10 process") << 1 << 10;
+ QTest::newRow("10/10 processes") << 10 << 10;
+ }
+}
+
+/*!
+ Create external processes
+ */
+void tst_QSystemLock::processes()
+{
+ QFETCH(int, readOnly);
+ QFETCH(int, readWrite);
+
+ QStringList scripts;
+ for (int i = 0; i < readOnly; ++i)
+ scripts.append("../lackey/scripts/systemlock_read.js");
+ for (int i = 0; i < readWrite; ++i)
+ scripts.append("../lackey/scripts/systemlock_readwrite.js");
+
+ QList<QProcess*> consumers;
+ for (int i = 0; i < scripts.count(); ++i) {
+ QStringList arguments = QStringList() << scripts.at(i);
+ QProcess *p = new QProcess;
+ p->setProcessChannelMode(QProcess::ForwardedChannels);
+ consumers.append(p);
+ p->start("../lackey/lackey", arguments);
+ }
+
+ while (!consumers.isEmpty()) {
+ consumers.first()->waitForFinished(3000);
+ consumers.first()->kill();
+ QCOMPARE(consumers.first()->exitStatus(), QProcess::NormalExit);
+ QCOMPARE(consumers.first()->exitCode(), 0);
+ delete consumers.takeFirst();
+ }
+}
+
+QTEST_MAIN(tst_QSystemLock)
+#include "tst_qsystemlock.moc"
+
diff --git a/tests/auto/qsharedmemory/src/qsystemlock.cpp b/tests/auto/qsharedmemory/src/qsystemlock.cpp
new file mode 100644
index 0000000..5546ebe
--- /dev/null
+++ b/tests/auto/qsharedmemory/src/qsystemlock.cpp
@@ -0,0 +1,246 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the test suite 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "qsystemlock.h"
+#include "qsystemlock_p.h"
+
+#include <qdebug.h>
+
+/*! \class QSystemLocker
+
+ \brief The QSystemLocker class is a convenience class that simplifies
+ locking and unlocking system locks.
+
+ The purpose of QSystemLocker is to simplify QSystemLock locking and
+ unlocking. Locking and unlocking a QSystemLock in complex functions and
+ statements or in exception handling code is error-prone and difficult to
+ debug. QSystemLocker can be used in such situations to ensure that the
+ state of the locks is always well-defined.
+
+ QSystemLocker should be created within a function where a QSystemLock needs
+ to be locked. The system lock is locked when QSystemLocker is created. If
+ locked, the system lock will be unlocked when the QSystemLocker is
+ destroyed. QSystemLocker can be unlocked with unlock() and relocked with
+ relock().
+
+ \sa QSystemLock
+ */
+
+/*! \fn QSystemLocker::QSystemLocker()
+
+ Constructs a QSystemLocker and locks \a lock. The \a lock will be
+ unlocked when the QSystemLocker is destroyed. If lock is zero,
+ QSystemLocker does nothing.
+
+ \sa QSystemLock::lock()
+ */
+
+/*! \fn QSystemLocker::~QSystemLocker()
+
+ Destroys the QSystemLocker and unlocks it if it was
+ locked in the constructor.
+
+ \sa QSystemLock::unlock()
+ */
+
+/*! \fn QSystemLocker::systemLock()
+
+ Returns a pointer to the lock that was locked in the constructor.
+ */
+
+/*! \fn QSystemLocker::relock()
+
+ Relocks an unlocked locker.
+
+ \sa unlock()
+ */
+
+/*! \fn QSystemLocker::unlock()
+
+ Unlocks this locker. You can use relock() to lock it again.
+ It does not need to be locked when destroyed.
+
+ \sa relock()
+ */
+
+/*! \class QSystemLock
+
+ \brief The QSystemLock class provides a system wide lock
+ that can be used between threads or processes.
+
+ The purpose of a QSystemLocker is to protect an object that can be
+ accessed by multiple threads or processes such as shared memory or a file.
+
+ For example, say there is a method which prints a message to a log file:
+
+ void log(const QString &logText)
+ {
+ QSystemLock systemLock(QLatin1String("logfile"));
+ systemLock.lock();
+ QFile file(QDir::temp() + QLatin1String("/log"));
+ if (file.open(QIODevice::Append)) {
+ QTextStream out(&file);
+ out << logText;
+ }
+ systemLock.unlock();
+ }
+
+ If this is called from two seperate processes the resulting log file is
+ guaranteed to contain both lines.
+
+ When you call lock(), other threads or processes that try to call lock()
+ with the same key will block until the thread or process that got the lock
+ calls unlock().
+
+ A non-blocking alternative to lock() is tryLock().
+ */
+
+/*!
+ Constructs a new system lock with \a key. The lock is created in an
+ unlocked state.
+
+ \sa lock(), key().
+ */
+QSystemLock::QSystemLock(const QString &key)
+{
+ d = new QSystemLockPrivate;
+ setKey(key);
+}
+
+/*!
+ Destroys a system lock.
+
+ warning: This will not unlock the system lock if it has been locked.
+*/
+QSystemLock::~QSystemLock()
+{
+ d->cleanHandle();
+ delete d;
+}
+
+/*!
+ Sets a new key to this system lock.
+
+ \sa key()
+ */
+void QSystemLock::setKey(const QString &key)
+{
+ if (key == d->key)
+ return;
+ d->cleanHandle();
+ d->lockCount = 0;
+ d->key = key;
+ // cache the file name so it doesn't have to be generated all the time.
+ d->fileName = d->makeKeyFileName();
+ d->error = QSystemLock::NoError;
+ d->errorString = QString();
+ d->handle();
+}
+
+/*!
+ Returns the key assigned to this system lock
+
+ \sa setKey()
+ */
+QString QSystemLock::key() const
+{
+ return d->key;
+}
+
+/*!
+ Locks the system lock. Lock \a mode can either be ReadOnly or ReadWrite.
+ If a mode is ReadOnly, attempts by other processes to obtain
+ ReadOnly locks will succeed, and ReadWrite attempts will block until
+ all of the ReadOnly locks are unlocked. If locked as ReadWrite, all
+ other attempts to lock will block until the lock is unlocked. A given
+ QSystemLock can be locked multiple times without blocking, and will
+ only be unlocked after a corresponding number of unlock()
+ calls are made. Returns true on success; otherwise returns false.
+
+ \sa unlock(), tryLock()
+ */
+bool QSystemLock::lock(LockMode mode)
+{
+ if (d->lockCount > 0 && mode == ReadOnly && d->lockedMode == ReadWrite) {
+ qWarning() << "QSystemLock::lock readwrite lock on top of readonly lock.";
+ return false;
+ }
+ return d->modifySemaphore(QSystemLockPrivate::Lock, mode);
+}
+
+/*!
+ Unlocks the system lock.
+ Returns true on success; otherwise returns false.
+
+ \sa lock()
+ */
+bool QSystemLock::unlock()
+{
+ if (d->lockCount == 0) {
+ qWarning() << "QSystemLock::unlock: unlock with no lock.";
+ return false;
+ }
+ return d->modifySemaphore(QSystemLockPrivate::Unlock, d->lockedMode);
+}
+
+/*!
+ Returns the type of error that occurred last or NoError.
+
+ \sa errorString()
+ */
+QSystemLock::SystemLockError QSystemLock::error() const
+{
+ return d->error;
+}
+
+/*!
+ Returns the human-readable message appropriate to the current error
+ reported by error(). If no suitable string is available, an empty
+ string is returned.
+
+ \sa error()
+ */
+QString QSystemLock::errorString() const
+{
+ return d->errorString;
+}
+
diff --git a/tests/auto/qsharedmemory/src/qsystemlock.h b/tests/auto/qsharedmemory/src/qsystemlock.h
new file mode 100644
index 0000000..99c2682
--- /dev/null
+++ b/tests/auto/qsharedmemory/src/qsystemlock.h
@@ -0,0 +1,135 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the test suite 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#ifndef QSYSTEMLOCK_H
+#define QSYSTEMLOCK_H
+
+#include <QtCore/qstring.h>
+
+QT_BEGIN_HEADER
+
+#ifndef QT_NO_SYSTEMLOCK
+
+QT_FORWARD_DECLARE_CLASS(QSystemLockPrivate)
+
+class QSystemLock
+{
+
+public:
+ enum SystemLockError
+ {
+ NoError,
+ UnknownError
+ };
+
+ QSystemLock(const QString &key);
+ ~QSystemLock();
+
+ void setKey(const QString &key);
+ QString key() const;
+
+ enum LockMode
+ {
+ ReadOnly,
+ ReadWrite
+ };
+
+ bool lock(LockMode mode = ReadWrite);
+ bool unlock();
+
+ SystemLockError error() const;
+ QString errorString() const;
+
+private:
+ Q_DISABLE_COPY(QSystemLock)
+
+ QSystemLockPrivate *d;
+};
+
+class QSystemLocker
+{
+
+public:
+ inline QSystemLocker(QSystemLock *systemLock,
+ QSystemLock::LockMode mode = QSystemLock::ReadWrite) : q_lock(systemLock)
+ {
+ autoUnLocked = relock(mode);
+ }
+
+ inline ~QSystemLocker()
+ {
+ if (autoUnLocked)
+ unlock();
+ }
+
+ inline QSystemLock *systemLock() const
+ {
+ return q_lock;
+ }
+
+ inline bool relock(QSystemLock::LockMode mode = QSystemLock::ReadWrite)
+ {
+ return (q_lock && q_lock->lock(mode));
+ }
+
+ inline bool unlock()
+ {
+ if (q_lock && q_lock->unlock()) {
+ autoUnLocked = false;
+ return true;
+ }
+ return false;
+ }
+
+private:
+ Q_DISABLE_COPY(QSystemLocker)
+
+ bool autoUnLocked;
+ QSystemLock *q_lock;
+};
+
+#endif // QT_NO_SYSTEMLOCK
+
+QT_END_HEADER
+
+#endif // QSYSTEMLOCK_H
+
diff --git a/tests/auto/qsharedmemory/src/qsystemlock_p.h b/tests/auto/qsharedmemory/src/qsystemlock_p.h
new file mode 100644
index 0000000..674c7b2
--- /dev/null
+++ b/tests/auto/qsharedmemory/src/qsystemlock_p.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the test suite 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#ifndef QSYSTEMLOCK_P_H
+#define QSYSTEMLOCK_P_H
+
+#ifndef QT_NO_SYSTEMLOCK
+
+#include "qsystemlock.h"
+#include "private/qsharedmemory_p.h"
+
+#define MAX_LOCKS 64
+
+class QSystemLockPrivate
+{
+
+public:
+ QSystemLockPrivate();
+
+ QString makeKeyFileName()
+ {
+ return QSharedMemoryPrivate::makePlatformSafeKey(key, QLatin1String("qipc_systemlock_"));
+ }
+
+ void setErrorString(const QString &function);
+
+#ifdef Q_OS_WIN
+ HANDLE handle();
+ bool lock(HANDLE, int count);
+ bool unlock(HANDLE, int count);
+#else
+ key_t handle();
+#endif
+ void cleanHandle();
+
+ enum Operation {
+ Lock,
+ Unlock
+ };
+ bool modifySemaphore(Operation op, QSystemLock::LockMode mode = QSystemLock::ReadOnly);
+
+ QString key;
+ QString fileName;
+#ifdef Q_OS_WIN
+ HANDLE semaphore;
+ HANDLE semaphoreLock;
+#else
+ int semaphore;
+#endif
+ int lockCount;
+ QSystemLock::LockMode lockedMode;
+
+ QSystemLock::SystemLockError error;
+ QString errorString;
+
+private:
+#ifndef Q_OS_WIN
+ key_t unix_key;
+ bool createdFile;
+ bool createdSemaphore;
+#endif
+};
+
+#endif // QT_NO_SYSTEMLOCK
+
+#endif // QSYSTEMLOCK_P_H
+
diff --git a/tests/auto/qsharedmemory/src/qsystemlock_unix.cpp b/tests/auto/qsharedmemory/src/qsystemlock_unix.cpp
new file mode 100644
index 0000000..5c46ce5
--- /dev/null
+++ b/tests/auto/qsharedmemory/src/qsystemlock_unix.cpp
@@ -0,0 +1,209 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the test suite 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "qsystemlock.h"
+#include "qsystemlock_p.h"
+
+#include <qdebug.h>
+#include <qfile.h>
+
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/shm.h>
+
+#include <sys/sem.h>
+// We have to define this as on some sem.h will have it
+union qt_semun {
+ int val; /* value for SETVAL */
+ struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
+ unsigned short *array; /* array for GETALL, SETALL */
+};
+
+#define tr(x) QT_TRANSLATE_NOOP(QLatin1String("QSystemLock"), (x))
+
+QSystemLockPrivate::QSystemLockPrivate() :
+ semaphore(-1), lockCount(0),
+ error(QSystemLock::NoError), unix_key(-1), createdFile(false), createdSemaphore(false)
+{
+}
+
+void QSystemLockPrivate::setErrorString(const QString &function)
+{
+ switch (errno) {
+ case EIDRM:
+ errorString = function + QLatin1String(": ") + tr("The semaphore set was removed");
+ error = QSystemLock::UnknownError;
+ break;
+ default:
+ errorString = function + QLatin1String(": ") + tr("unknown error");
+ error = QSystemLock::UnknownError;
+ qWarning() << errorString << "key" << key << "errno" << errno << ERANGE << ENOMEM << EINVAL << EINTR << EFBIG << EFAULT << EAGAIN << EACCES << E2BIG;
+ }
+}
+
+/*!
+ \internal
+
+ Setup unix_key
+ */
+key_t QSystemLockPrivate::handle()
+{
+ if (key.isEmpty())
+ return -1;
+
+ // ftok requires that an actual file exists somewhere
+ // If we have already made at some point in the past,
+ // double check that it is still there.
+ if (-1 != unix_key) {
+ int aNewunix_key = ftok(QFile::encodeName(fileName).constData(), 'Q');
+ if (aNewunix_key != unix_key) {
+ cleanHandle();
+ } else {
+ return unix_key;
+ }
+ }
+
+ // Create the file needed for ftok
+ int built = QSharedMemoryPrivate::createUnixKeyFile(fileName);
+ if (-1 == built)
+ return -1;
+ createdFile = (1 == built);
+
+ // Get the unix key for the created file
+ unix_key = ftok(QFile::encodeName(fileName).constData(), 'Q');
+ if (-1 == unix_key) {
+ setErrorString(QLatin1String("QSystemLock::handle ftok"));
+ return -1;
+ }
+
+ // Get semaphore
+ semaphore = semget(unix_key, 1, 0666 | IPC_CREAT | IPC_EXCL);
+ if (-1 == semaphore) {
+ if (errno == EEXIST)
+ semaphore = semget(unix_key, 1, 0666 | IPC_CREAT);
+ if (-1 == semaphore) {
+ setErrorString(QLatin1String("QSystemLock::handle semget"));
+ cleanHandle();
+ return -1;
+ }
+ } else {
+ // Created semaphore, initialize value.
+ createdSemaphore = true;
+ qt_semun init_op;
+ init_op.val = MAX_LOCKS;
+ if (-1 == semctl(semaphore, 0, SETVAL, init_op)) {
+ setErrorString(QLatin1String("QSystemLock::handle semctl"));
+ cleanHandle();
+ return -1;
+ }
+ }
+
+ return unix_key;
+}
+
+/*!
+ \internal
+
+ Cleanup the unix_key
+ */
+void QSystemLockPrivate::cleanHandle()
+{
+ unix_key = -1;
+
+ // remove the file if we made it
+ if (createdFile) {
+ if (!QFile::remove(fileName))
+ setErrorString(QLatin1String("QSystemLock::cleanHandle QFile::remove"));
+ createdFile = false;
+ }
+
+ if (createdSemaphore) {
+ if (-1 != semaphore) {
+ if (-1 == semctl(semaphore, 0, IPC_RMID)) {
+ setErrorString(QLatin1String("QSystemLock::cleanHandle semctl"));
+ }
+ semaphore = -1;
+ }
+ createdSemaphore = false;
+ }
+}
+
+/*!
+ \internal
+
+ modifySemaphore generates operation.sem_op and handles recursive behavior.
+ */
+bool QSystemLockPrivate::modifySemaphore(QSystemLockPrivate::Operation op,
+ QSystemLock::LockMode mode)
+{
+ if (-1 == handle())
+ return false;
+
+ if ((lockCount == 0 && op == Lock) || (lockCount > 0 && op == Unlock)) {
+ if (op == Unlock) {
+ --lockCount;
+ Q_ASSERT(lockCount >= 0);
+ if (lockCount > 0)
+ return true;
+ }
+
+ struct sembuf operation;
+ operation.sem_num = 0;
+ operation.sem_op = (mode == QSystemLock::ReadWrite) ? MAX_LOCKS : 1;
+ if (op == Lock)
+ operation.sem_op *= -1;
+ operation.sem_flg = SEM_UNDO;
+
+ if (-1 == semop(semaphore, &operation, 1)) {
+ setErrorString(QLatin1String("QSystemLock::modify"));
+ return false;
+ }
+ lockedMode = mode;
+ }
+ if (op == Lock)
+ lockCount++;
+
+ return true;
+}
+
diff --git a/tests/auto/qsharedmemory/src/qsystemlock_win.cpp b/tests/auto/qsharedmemory/src/qsystemlock_win.cpp
new file mode 100644
index 0000000..62f1b1b
--- /dev/null
+++ b/tests/auto/qsharedmemory/src/qsystemlock_win.cpp
@@ -0,0 +1,190 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the test suite 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "qsystemlock.h"
+#include "qsystemlock_p.h"
+#include <qdebug.h>
+#include <QtCore>
+QSystemLockPrivate::QSystemLockPrivate() :
+ semaphore(0), semaphoreLock(0),
+ lockCount(0), error(QSystemLock::NoError)
+{
+}
+
+void QSystemLockPrivate::setErrorString(const QString &function)
+{
+ BOOL windowsError = GetLastError();
+ if (windowsError == 0)
+ return;
+ errorString = function + QLatin1String(": ")
+ + QLatin1String("Unknown error");
+ error = QSystemLock::UnknownError;
+ qWarning() << errorString << "key" << key << (int)windowsError << semaphore << semaphoreLock;
+}
+
+/*!
+ \internal
+
+ Setup the semaphore
+ */
+HANDLE QSystemLockPrivate::handle()
+{
+ // don't allow making handles on empty keys
+ if (key.isEmpty())
+ return 0;
+
+ // Create it if it doesn't already exists.
+ if (semaphore == 0) {
+ QString safeName = makeKeyFileName();
+ QT_WA({
+ semaphore = CreateSemaphoreW(0, MAX_LOCKS, MAX_LOCKS, (TCHAR*)safeName.utf16());
+ }, {
+ semaphore = CreateSemaphoreA(0, MAX_LOCKS, MAX_LOCKS, safeName.toLocal8Bit().constData());
+ });
+
+ if (semaphore == 0) {
+ setErrorString(QLatin1String("QSystemLockPrivate::handle"));
+ return 0;
+ }
+ }
+
+ if (semaphoreLock == 0) {
+ QString safeLockName = QSharedMemoryPrivate::makePlatformSafeKey(key + QLatin1String("lock"), QLatin1String("qipc_systemlock_"));
+ QT_WA({
+ semaphoreLock = CreateSemaphoreW(0,
+ 1, 1, (TCHAR*)safeLockName.utf16());
+ }, {
+ semaphoreLock = CreateSemaphoreA(0,
+ 1, 1, safeLockName.toLocal8Bit().constData());
+ });
+ if (semaphoreLock == 0) {
+ setErrorString(QLatin1String("QSystemLockPrivate::handle"));
+ return 0;
+ }
+ }
+ return semaphore;
+}
+
+/*!
+ \internal
+
+ Cleanup the semaphore
+ */
+void QSystemLockPrivate::cleanHandle()
+{
+ if (semaphore && !CloseHandle(semaphore))
+ setErrorString(QLatin1String("QSystemLockPrivate::cleanHandle:"));
+ if (semaphoreLock && !CloseHandle(semaphoreLock))
+ setErrorString(QLatin1String("QSystemLockPrivate::cleanHandle:"));
+ semaphore = 0;
+ semaphoreLock = 0;
+}
+
+bool QSystemLockPrivate::lock(HANDLE handle, int count)
+{
+ if (count == 1) {
+ WaitForSingleObject(handle, INFINITE);
+ return true;
+ }
+
+ int i = count;
+ while (i > 0) {
+ if (WAIT_OBJECT_0 == WaitForSingleObject(handle, 0)) {
+ --i;
+ } else {
+ // undo what we have done, sleep and then try again later
+ ReleaseSemaphore(handle, (count - i), 0);
+ i = count;
+ ReleaseSemaphore(semaphoreLock, 1, 0);
+ Sleep(1);
+ WaitForSingleObject(semaphoreLock, INFINITE);
+ }
+ }
+ return true;
+}
+
+bool QSystemLockPrivate::unlock(HANDLE handle, int count)
+{
+ if (0 == ReleaseSemaphore(handle, count, 0)) {
+ setErrorString(QLatin1String("QSystemLockPrivate::unlock"));
+ return false;
+ }
+ return true;
+}
+
+/*!
+ \internal
+
+ modifySemaphore handles recursive behavior and modifies the semaphore.
+ */
+bool QSystemLockPrivate::modifySemaphore(QSystemLockPrivate::Operation op,
+ QSystemLock::LockMode mode)
+{
+ if (0 == handle())
+ return false;
+
+ if ((lockCount == 0 && op == Lock) || (lockCount > 0 && op == Unlock)) {
+ if (op == Unlock) {
+ --lockCount;
+ Q_ASSERT(lockCount >= 0);
+ if (lockCount > 0)
+ return true;
+ }
+
+ int count = (mode == QSystemLock::ReadWrite) ? MAX_LOCKS : 1;
+ if (op == Lock) {
+ lock(semaphoreLock, 1);
+ lock(semaphore, count);
+ if (count != MAX_LOCKS) unlock(semaphoreLock, 1);
+ lockedMode = mode;
+ } else {
+ if (count == MAX_LOCKS) unlock(semaphoreLock, 1);
+ unlock(semaphore, count);
+ }
+
+ }
+ if (op == Lock)
+ lockCount++;
+
+ return true;
+}
+
diff --git a/tests/auto/qsharedmemory/src/src.pri b/tests/auto/qsharedmemory/src/src.pri
new file mode 100644
index 0000000..5bc9de6
--- /dev/null
+++ b/tests/auto/qsharedmemory/src/src.pri
@@ -0,0 +1,10 @@
+INCLUDEPATH += $$PWD
+DEPENDPATH += $$PWD
+
+SOURCES += $$PWD/qsystemlock.cpp
+
+HEADERS += $$PWD/qsystemlock.h \
+ $$PWD/qsystemlock_p.h
+
+unix:SOURCES += $$PWD/qsystemlock_unix.cpp
+win32:SOURCES += $$PWD/qsystemlock_win.cpp
diff --git a/tests/auto/qsharedmemory/test/test.pro b/tests/auto/qsharedmemory/test/test.pro
new file mode 100644
index 0000000..e294a75
--- /dev/null
+++ b/tests/auto/qsharedmemory/test/test.pro
@@ -0,0 +1,29 @@
+load(qttest_p4)
+
+include(../src/src.pri)
+QT -= gui
+
+DEFINES += QSHAREDMEMORY_DEBUG
+DEFINES += QSYSTEMSEMAPHORE_DEBUG
+
+SOURCES += ../tst_qsharedmemory.cpp
+TARGET = ../tst_qsharedmemory
+
+!wince*:win32 {
+ CONFIG(debug, debug|release) {
+ TARGET = ../../debug/tst_qsharedmemory
+} else {
+ TARGET = ../../release/tst_qsharedmemory
+ }
+}
+
+wince*: {
+QT += gui script
+addFiles.sources = ../lackey/lackey.exe ../lackey/scripts
+addFiles.path = lackey
+DEPLOYMENT += addFiles
+DEFINES += SRCDIR=\\\"\\\"
+} else {
+DEFINES += SRCDIR=\\\"$$PWD/../\\\"
+}
+
diff --git a/tests/auto/qsharedmemory/tst_qsharedmemory.cpp b/tests/auto/qsharedmemory/tst_qsharedmemory.cpp
new file mode 100644
index 0000000..f39b376
--- /dev/null
+++ b/tests/auto/qsharedmemory/tst_qsharedmemory.cpp
@@ -0,0 +1,744 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Qt Software Information (qt-info@nokia.com)
+**
+** This file is part of the test suite 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 either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** 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.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtTest/QtTest>
+#include <qsharedmemory.h>
+
+//TESTED_CLASS=
+//TESTED_FILES=
+
+#define EXISTING_SHARE "existing"
+#define EXISTING_SIZE 1024
+
+Q_DECLARE_METATYPE(QSharedMemory::SharedMemoryError);
+Q_DECLARE_METATYPE(QSharedMemory::AccessMode);
+
+class tst_QSharedMemory : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QSharedMemory();
+ virtual ~tst_QSharedMemory();
+
+public Q_SLOTS:
+ void init();
+ void cleanup();
+
+private slots:
+ // basics
+ void constructor();
+ void key_data();
+ void key();
+ void create_data();
+ void create();
+ void attach_data();
+ void attach();
+ void lock();
+
+ // custom edge cases
+ void removeWhileAttached();
+ void emptyMemory();
+ void readOnly();
+
+ // basics all together
+ void simpleProducerConsumer_data();
+ void simpleProducerConsumer();
+ void simpleDoubleProducerConsumer();
+
+ // with threads
+ void simpleThreadedProducerConsumer_data();
+ void simpleThreadedProducerConsumer();
+
+ // with processes
+ void simpleProcessProducerConsumer_data();
+ void simpleProcessProducerConsumer();
+
+ // extreme cases
+ void useTooMuchMemory();
+ void attachTooMuch();
+
+protected:
+ int remove(const QString &key);
+
+ QString rememberKey(const QString &key)
+ {
+ if (key == EXISTING_SHARE)
+ return key;
+ if (!keys.contains(key)) {
+ keys.append(key);
+ remove(key);
+ }
+ return key;
+ }
+
+ QStringList keys;
+ QList<QSharedMemory*> jail;
+ QSharedMemory *existingSharedMemory;
+};
+
+tst_QSharedMemory::tst_QSharedMemory() : existingSharedMemory(0)
+{
+}
+
+tst_QSharedMemory::~tst_QSharedMemory()
+{
+}
+
+void tst_QSharedMemory::init()
+{
+ existingSharedMemory = new QSharedMemory(EXISTING_SHARE);
+ if (!existingSharedMemory->create(EXISTING_SIZE)) {
+ QVERIFY(existingSharedMemory->error() == QSharedMemory::AlreadyExists);
+ }
+}
+
+void tst_QSharedMemory::cleanup()
+{
+ delete existingSharedMemory;
+ qDeleteAll(jail.begin(), jail.end());
+ jail.clear();
+
+ keys.append(EXISTING_SHARE);
+ for (int i = 0; i < keys.count(); ++i) {
+ QSharedMemory sm(keys.at(i));
+ if (!sm.create(1024)) {
+ //if(sm.error() != QSharedMemory::KeyError)
+ // qWarning() << "test cleanup: remove failed:" << keys.at(i) << sm.error() << sm.errorString();
+ sm.attach();
+ sm.detach();
+ remove(keys.at(i));
+ }
+ }
+}
+
+#ifndef Q_OS_WIN
+#include "private/qsharedmemory_p.h"
+#include <sys/types.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#endif
+
+int tst_QSharedMemory::remove(const QString &key)
+{
+#ifndef Q_OS_WIN
+ // On unix the shared memory might exists from a previously failed test
+ // or segfault, remove it it does
+ if (key.isEmpty())
+ return -1;
+
+ // ftok requires that an actual file exists somewhere
+ QString fileName = QSharedMemoryPrivate::makePlatformSafeKey(key);
+ if (!QFile::exists(fileName)) {
+ //qDebug() << "exits failed";
+ return -2;
+ }
+
+ int unix_key = ftok(fileName.toLatin1().constData(), 'Q');
+ if (-1 == unix_key) {
+ qDebug() << "ftok failed";
+ return -3;
+ }
+
+ int id = shmget(unix_key, 0, 0660);
+ if (-1 == id) {
+ qDebug() << "shmget failed";
+ return -4;
+ }
+
+ struct shmid_ds shmid_ds;
+ if (-1 == shmctl(id, IPC_RMID, &shmid_ds)) {
+ qDebug() << "shmctl failed";
+ return -5;
+ }
+ return QFile::remove(fileName);
+#else
+ Q_UNUSED(key);
+ return 0;
+#endif
+}
+
+/*!
+ Tests the default values
+ */
+void tst_QSharedMemory::constructor()
+{
+ QSharedMemory sm;
+ QCOMPARE(sm.key(), QString());
+ QVERIFY(!sm.isAttached());
+ QVERIFY(sm.data() == 0);
+ QCOMPARE(sm.size(), 0);
+ QCOMPARE(sm.error(), QSharedMemory::NoError);
+ QVERIFY(sm.errorString() == QString());
+}
+
+void tst_QSharedMemory::key_data()
+{
+ QTest::addColumn<QString>("constructorKey");
+ QTest::addColumn<QString>("setKey");
+
+ QTest::newRow("null, null") << QString() << QString();
+ QTest::newRow("null, one") << QString() << QString("one");
+ QTest::newRow("one, two") << QString("one") << QString("two");
+ QTest::newRow("invalid") << QString("o/e") << QString("t/o");
+}
+
+/*!
+ Basic key testing
+ */
+void tst_QSharedMemory::key()
+{
+ QFETCH(QString, constructorKey);
+ QFETCH(QString, setKey);
+
+ QSharedMemory sm(constructorKey);
+ QCOMPARE(sm.key(), constructorKey);
+ sm.setKey(setKey);
+ QCOMPARE(sm.key(), setKey);
+ QCOMPARE(sm.isAttached(), false);
+
+ QCOMPARE(sm.error(), QSharedMemory::NoError);
+ QVERIFY(sm.errorString() == QString());
+ QVERIFY(sm.data() == 0);
+ QCOMPARE(sm.size(), 0);
+
+ QCOMPARE(sm.detach(), false);
+}
+
+void tst_QSharedMemory::create_data()
+{
+ QTest::addColumn<QString>("key");
+ QTest::addColumn<int>("size");
+ QTest::addColumn<bool>("canCreate");
+ QTest::addColumn<QSharedMemory::SharedMemoryError>("error");
+
+ QTest::newRow("null key") << QString() << 1024
+ << false << QSharedMemory::LockError;
+ QTest::newRow("-1 size") << QString("negsize") << -1
+ << false << QSharedMemory::InvalidSize;
+ QTest::newRow("nor size") << QString("norsize") << 1024
+ << true << QSharedMemory::NoError;
+ QTest::newRow("already exists") << QString(EXISTING_SHARE) << EXISTING_SIZE
+ << false << QSharedMemory::AlreadyExists;
+}
+
+/*!
+ Basic create testing
+ */
+void tst_QSharedMemory::create()
+{
+ QFETCH(QString, key);
+ QFETCH(int, size);
+ QFETCH(bool, canCreate);
+ QFETCH(QSharedMemory::SharedMemoryError, error);
+
+ QSharedMemory sm(rememberKey(key));
+ QCOMPARE(sm.create(size), canCreate);
+ if(sm.error() != error)
+ qDebug() << sm.errorString();
+ QCOMPARE(sm.key(), key);
+ if (canCreate) {
+ QVERIFY(sm.errorString() == QString());
+ QVERIFY(sm.data() != 0);
+ QVERIFY(sm.size() != 0);
+ } else {
+ QVERIFY(sm.data() == 0);
+ QVERIFY(sm.errorString() != QString());
+ }
+}
+
+void tst_QSharedMemory::attach_data()
+{
+ QTest::addColumn<QString>("key");
+ QTest::addColumn<bool>("exists");
+ QTest::addColumn<QSharedMemory::SharedMemoryError>("error");
+
+ QTest::newRow("null key") << QString() << false << QSharedMemory::LockError;
+ QTest::newRow("doesn't exists") << QString("doesntexists") << false << QSharedMemory::NotFound;
+ QTest::newRow("already exists") << QString(EXISTING_SHARE) << true << QSharedMemory::NoError;
+}
+
+/*!
+ Basic attach/detach testing
+ */
+void tst_QSharedMemory::attach()
+{
+ QFETCH(QString, key);
+ QFETCH(bool, exists);
+ QFETCH(QSharedMemory::SharedMemoryError, error);
+#ifdef Q_OS_HPUX
+ if (QLatin1String(QTest::currentDataTag()) == QLatin1String("already exists")) {
+ QSKIP("HPUX doesn't allow for multiple attaches per process", SkipSingle);
+ }
+#endif
+ QSharedMemory sm(key);
+ QCOMPARE(sm.attach(), exists);
+ QCOMPARE(sm.isAttached(), exists);
+ QCOMPARE(sm.error(), error);
+ QCOMPARE(sm.key(), key);
+ if (exists) {
+ QVERIFY(sm.data() != 0);
+ QVERIFY(sm.size() != 0);
+ QCOMPARE(sm.errorString(), QString());
+ QVERIFY(sm.detach());
+ // Make sure detach doesn't screw up something and we can't re-attach.
+ QVERIFY(sm.attach());
+ QVERIFY(sm.detach());
+ QCOMPARE(sm.size(), 0);
+ QVERIFY(sm.data() == 0);
+ } else {
+ QVERIFY(sm.data() == 0);
+ QVERIFY(sm.size() == 0);
+ QVERIFY(sm.errorString() != QString());
+ QVERIFY(!sm.detach());
+ }
+}
+
+void tst_QSharedMemory::lock()
+{
+ QSharedMemory shm;
+ QVERIFY(!shm.lock());
+ QCOMPARE(shm.error(), QSharedMemory::LockError);
+
+ shm.setKey(QLatin1String("qsharedmemory"));
+
+ QVERIFY(!shm.lock());
+ QCOMPARE(shm.error(), QSharedMemory::LockError);
+
+ QVERIFY(shm.create(100));
+ QVERIFY(shm.lock());
+ QTest::ignoreMessage(QtWarningMsg, "QSharedMemory::lock: already locked");
+ QVERIFY(shm.lock());
+ // don't lock forever
+}
+
+/*!
+ Other shared memory are allowed to be attached after we remove,
+ but new shared memory are not allowed to attach after a remove.
+ */
+void tst_QSharedMemory::removeWhileAttached()
+{
+#ifdef Q_OS_HPUX
+ QSKIP("HPUX doesn't allow for multiple attaches per process", SkipAll);
+#endif
+ rememberKey("one");
+
+ // attach 1
+ QSharedMemory *smOne = new QSharedMemory(QLatin1String("one"));
+ QVERIFY(smOne->create(1024));
+ QVERIFY(smOne->isAttached());
+
+ // attach 2
+ QSharedMemory *smTwo = new QSharedMemory(QLatin1String("one"));
+ QVERIFY(smTwo->attach());
+ QVERIFY(smTwo->isAttached());
+
+ // detach 1 and remove, remove one first to catch another error.
+ delete smOne;
+ delete smTwo;
+
+ // three shouldn't be able to attach
+ QSharedMemory smThree(QLatin1String("one"));
+ QVERIFY(!smThree.attach());
+ QCOMPARE(smThree.error(), QSharedMemory::NotFound);
+}
+
+/*!
+ The memory should be set to 0 after created.
+ */
+void tst_QSharedMemory::emptyMemory()
+{
+ QSharedMemory sm(rememberKey(QLatin1String("voidland")));
+ int size = 1024;
+ QVERIFY(sm.create(size, QSharedMemory::ReadOnly));
+ char *get = (char*)sm.data();
+ char null = 0;
+ for (int i = 0; i < size; ++i)
+ QCOMPARE(get[i], null);
+}
+
+/*!
+ Verify that attach with ReadOnly is actually read only
+ by writing to data and causing a segfault.
+*/
+void tst_QSharedMemory::readOnly()
+{
+#ifdef Q_OS_WIN
+ QSKIP("This test opens a crash dialog on Windows", SkipSingle);
+#endif
+ QString program = "./lackey/lackey";
+ QStringList arguments;
+ rememberKey("readonly_segfault");
+ arguments << SRCDIR "lackey/scripts/readonly_segfault.js";
+
+ // ### on windows disable the popup somehow
+ QProcess p;
+ p.start(program, arguments);
+ p.setProcessChannelMode(QProcess::ForwardedChannels);
+ p.waitForFinished();
+ QCOMPARE(p.error(), QProcess::Crashed);
+}
+
+/*!
+ Keep making shared memory until the kernel stops us.
+ */
+void tst_QSharedMemory::useTooMuchMemory()
+{
+#ifdef Q_OS_LINUX
+ bool success = true;
+ int count = 0;
+ while (success) {
+ QString key = QString("maxmemorytest_%1").arg(count++);
+ QSharedMemory *sm = new QSharedMemory(rememberKey(key));
+ QVERIFY(sm);
+ jail.append(sm);
+ int size = 32768 * 1024;
+ success = sm->create(size);
+ if (!success && sm->error() == QSharedMemory::AlreadyExists) {
+ // left over from a crash, clean it up
+ sm->attach();
+ sm->detach();
+ success = sm->create(size);
+ }
+
+ if (!success) {
+ QVERIFY(!sm->isAttached());
+ QCOMPARE(sm->key(), key);
+ QCOMPARE(sm->size(), 0);
+ QVERIFY(sm->data() == 0);
+ if (sm->error() != QSharedMemory::OutOfResources)
+ qDebug() << sm->error() << sm->errorString();
+ // ### Linux wont return OutOfResources if there are not enough semaphores to use.
+ QVERIFY(sm->error() == QSharedMemory::OutOfResources
+ || sm->error() == QSharedMemory::LockError);
+ QVERIFY(sm->errorString() != QString());
+ QVERIFY(!sm->attach());
+ QVERIFY(!sm->detach());
+ } else {
+ QVERIFY(sm->isAttached());
+ }
+ }
+#endif
+}
+
+/*!
+ Create one shared memory (government) and see how many other shared memories (wars) we can
+ attach before the system runs out of resources.
+ */
+void tst_QSharedMemory::attachTooMuch()
+{
+ QSKIP("disabled", SkipAll);
+#ifdef Q_OS_HPUX
+ QSKIP("HPUX doesn't allow for multiple attaches per process", SkipAll);
+#endif
+#ifdef Q_OS_WINCE
+ QSKIP("This nearly kills the system itself, so skip for Qt/WinCE", SkipAll);
+#endif
+ QSharedMemory government(rememberKey("government"));
+ QVERIFY(government.create(1024));
+ while (true) {
+ QSharedMemory *war = new QSharedMemory(government.key());
+ QVERIFY(war);
+ jail.append(war);
+ if (!war->attach()) {
+ QVERIFY(!war->isAttached());
+ QCOMPARE(war->key(), government.key());
+ QCOMPARE(war->size(), 0);
+ QVERIFY(war->data() == 0);
+ QCOMPARE(war->error(), QSharedMemory::OutOfResources);
+ QVERIFY(war->errorString() != QString());
+ QVERIFY(!war->detach());
+ break;
+ } else {
+ QVERIFY(war->isAttached());
+ }
+ }
+}
+
+void tst_QSharedMemory::simpleProducerConsumer_data()
+{
+ QTest::addColumn<QSharedMemory::AccessMode>("mode");
+
+ QTest::newRow("readonly") << QSharedMemory::ReadOnly;
+ QTest::newRow("readwrite") << QSharedMemory::ReadWrite;
+}
+
+/*!
+ The basic consumer producer that rounds out the basic testing.
+ If this fails then any muli-threading/process might fail (but be
+ harder to debug)
+
+ This doesn't require nor test any locking system.
+ */
+void tst_QSharedMemory::simpleProducerConsumer()
+{
+#ifdef Q_OS_HPUX
+ QSKIP("HPUX doesn't allow for multiple attaches per process", SkipAll);
+#endif
+ QFETCH(QSharedMemory::AccessMode, mode);
+
+ rememberKey(QLatin1String("market"));
+ QSharedMemory producer(QLatin1String("market"));
+ QSharedMemory consumer(QLatin1String("market"));
+ int size = 512;
+ QVERIFY(producer.create(size));
+ QVERIFY(consumer.attach(mode));
+
+ char *put = (char*)producer.data();
+ char *get = (char*)consumer.data();
+ // On Windows CE you always have ReadWrite access. Thus
+ // ViewMapOfFile returns the same pointer
+#ifndef Q_OS_WINCE
+ QVERIFY(put != get);
+#endif
+ for (int i = 0; i < size; ++i) {
+ put[i] = 'Q';
+ QCOMPARE(get[i], 'Q');
+ }
+ QVERIFY(consumer.detach());
+}
+
+void tst_QSharedMemory::simpleDoubleProducerConsumer()
+{
+#ifdef Q_OS_HPUX
+ QSKIP("HPUX doesn't allow for multiple attaches per process", SkipAll);
+#endif
+ rememberKey(QLatin1String("market"));
+ QSharedMemory producer(QLatin1String("market"));
+ int size = 512;
+ QVERIFY(producer.create(size));
+ QVERIFY(producer.detach());
+ QVERIFY(producer.create(size));
+
+ {
+ QSharedMemory consumer(QLatin1String("market"));
+ QVERIFY(consumer.attach());
+ }
+}
+
+class Consumer : public QThread
+{
+
+public:
+ void run()
+ {
+ QSharedMemory consumer(QLatin1String("market"));
+ while (!consumer.attach()) {
+ if (consumer.error() != QSharedMemory::NotFound)
+ qDebug() << "consumer: failed to connect" << consumer.error() << consumer.errorString();
+ QVERIFY(consumer.error() == QSharedMemory::NotFound || consumer.error() == QSharedMemory::KeyError);
+ QTest::qWait(1);
+ }
+
+ char *memory = (char*)consumer.data();
+
+ int i = 0;
+ while (true) {
+ if(!consumer.lock())
+ break;
+ if (memory[0] == 'Q')
+ memory[0] = ++i;
+ if (memory[0] == 'E') {
+ memory[1]++;
+ QVERIFY(consumer.unlock());
+ break;
+ }
+ QVERIFY(consumer.unlock());
+ QTest::qWait(1);
+ }
+
+ QVERIFY(consumer.detach());
+ }
+};
+
+class Producer : public QThread
+{
+
+public:
+ void run()
+ {
+ QSharedMemory producer(QLatin1String("market"));
+ int size = 1024;
+ if (!producer.create(size)) {
+ // left over from a crash...
+ if (producer.error() == QSharedMemory::AlreadyExists) {
+ producer.attach();
+ producer.detach();
+ QVERIFY(producer.create(size));
+ }
+ }
+ QVERIFY(producer.isAttached());
+ char *memory = (char*)producer.data();
+ memory[1] = '0';
+ QTime timer;
+ timer.start();
+ int i = 0;
+ while (i < 5 && timer.elapsed() < 5000) {
+ QVERIFY(producer.lock());
+ if (memory[0] == 'Q') {
+ QVERIFY(producer.unlock());
+ QTest::qWait(1);
+ continue;
+ }
+ ++i;
+ memory[0] = 'Q';
+ QVERIFY(producer.unlock());
+ QTest::qWait(1);
+ }
+
+ // tell everyone to quit
+ QVERIFY(producer.lock());
+ memory[0] = 'E';
+ QVERIFY(producer.unlock());
+ }
+private:
+
+};
+
+void tst_QSharedMemory::simpleThreadedProducerConsumer_data()
+{
+ QTest::addColumn<bool>("producerIsThread");
+ QTest::addColumn<int>("threads");
+ for (int i = 0; i < 5; ++i) {
+ QTest::newRow("1 consumer, producer is thread") << true << 1;
+ QTest::newRow("1 consumer, producer is this") << false << 1;
+ QTest::newRow("5 consumers, producer is thread") << true << 5;
+ QTest::newRow("5 consumers, producer is this") << false << 5;
+ }
+}
+
+/*!
+ The basic producer/consumer, but this time using threads.
+ */
+void tst_QSharedMemory::simpleThreadedProducerConsumer()
+{
+ QFETCH(bool, producerIsThread);
+ QFETCH(int, threads);
+ rememberKey(QLatin1String("market"));
+
+#if defined Q_OS_HPUX && defined __ia64
+ QSKIP("This test locks up on gravlaks.troll.no", SkipSingle);
+#endif
+
+ Producer p;
+ if (producerIsThread)
+ p.start();
+
+ QList<Consumer*> consumers;
+ for (int i = 0; i < threads; ++i) {
+ consumers.append(new Consumer());
+ consumers.last()->start();
+ }
+
+ if (!producerIsThread)
+ p.run();
+
+ p.wait(5000);
+ while (!consumers.isEmpty()) {
+ QVERIFY(consumers.first()->wait(5000));
+ delete consumers.takeFirst();
+ }
+}
+
+void tst_QSharedMemory::simpleProcessProducerConsumer_data()
+{
+ QTest::addColumn<int>("processes");
+ int tries = 10;
+#ifdef Q_OS_WIN
+ tries = 5;
+#endif
+ for (int i = 0; i < tries; ++i) {
+ QTest::newRow("1 process") << 1;
+ QTest::newRow("5 processes") << 5;
+ }
+}
+
+/*!
+ Create external processes that produce and consume.
+ */
+void tst_QSharedMemory::simpleProcessProducerConsumer()
+{
+ QFETCH(int, processes);
+
+ rememberKey("market");
+
+#ifndef Q_OS_WINCE
+ QStringList arguments = QStringList() << SRCDIR "lackey/scripts/producer.js";
+#else
+ QStringList arguments = QStringList() << QFileInfo(SRCDIR "lackey/scripts/producer.js").absoluteFilePath();
+#endif
+ QProcess producer;
+ producer.setProcessChannelMode(QProcess::ForwardedChannels);
+ producer.start("./lackey/lackey", arguments);
+ producer.waitForStarted();
+
+ QList<QProcess*> consumers;
+ for (int i = 0; i < processes; ++i) {
+#ifndef Q_OS_WINCE
+ QStringList arguments = QStringList() << SRCDIR "lackey/scripts/consumer.js";
+#else
+ QStringList arguments = QStringList() << QFileInfo(SRCDIR "lackey/scripts/consumer.js").absoluteFilePath();
+#endif
+ QProcess *p = new QProcess;
+ p->setProcessChannelMode(QProcess::ForwardedChannels);
+ consumers.append(p);
+ p->start("./lackey/lackey", arguments);
+ }
+
+ producer.waitForFinished(5000);
+
+ bool consumerFailed = false;
+
+ while (!consumers.isEmpty()) {
+ consumers.first()->waitForFinished(1000);
+ if (consumers.first()->exitStatus() != QProcess::NormalExit ||
+ consumers.first()->exitCode() != 0) {
+ consumerFailed = true;
+ }
+ delete consumers.takeFirst();
+ }
+ QCOMPARE(consumerFailed, false);
+}
+
+QTEST_MAIN(tst_QSharedMemory)
+#include "tst_qsharedmemory.moc"
+