diff options
29 files changed, 1521 insertions, 292 deletions
diff --git a/bin/createpackage.pl b/bin/createpackage.pl index df91876..b7457e1 100755 --- a/bin/createpackage.pl +++ b/bin/createpackage.pl @@ -58,7 +58,7 @@ use File::Spec; use File::Path; # use CWD abs_bath, which is exported only on request use Cwd 'abs_path'; - +use File::Copy; sub Usage() { print <<ENDUSAGESTRING; @@ -84,6 +84,8 @@ Where supported options are as follows: [-g|gcce-is-armv5] = Convert gcce platform to armv5. [-d|dont-patch] = Skip automatic patching of capabilities and pkg file if default certificate is used. Instead non-self-signable capabilities just cause warnings. + [-t|tmp-dir <path>] = Specifies temporary directory to be used for package creation. + Defaults to 'createpackage_tmp' under same directory as templatepkg. Where parameters are as follows: templatepkg = Name of .pkg file template target = Either debug or release @@ -130,6 +132,7 @@ my $signed_sis_name = ""; my $onlyUnsigned = ""; my $convertGcce = ""; my $dontPatchCaps = ""; +my $tempPackageDir = ""; unless (GetOptions('i|install' => \$install, 'p|preprocess' => \$preprocessonly, @@ -139,7 +142,8 @@ unless (GetOptions('i|install' => \$install, 's|stub' => \$stub, 'n|sisname=s' => \$signed_sis_name, 'g|gcce-is-armv5' => \$convertGcce, - 'd|dont-patch' => \$dontPatchCaps,)) { + 'd|dont-patch' => \$dontPatchCaps, + 't|tmp-dir=s' => \$tempPackageDir,)) { Usage(); } @@ -190,18 +194,22 @@ $key = $ARGV[3] or $key = ""; my $passphrase; $passphrase = $ARGV[4] or $passphrase = ""; +if ($tempPackageDir eq "") { + my ($templateVolume, $templatePath, $templateFileName) = File::Spec->splitpath($templatepkg); + $tempPackageDir = File::Spec->catpath($templateVolume, $templatePath."createpackage_tmp", ""); +} + +mkpath($tempPackageDir); + # Generate output pkg basename (i.e. file name without extension) my $pkgoutputbasename = $templatepkg; -my $preservePkgOutput = ""; $pkgoutputbasename =~ s/_template/_$targetplatform/g; $pkgoutputbasename =~ s/_installer\.pkg/_installer___temp\.pkg/g; -if ($pkgoutputbasename eq $templatepkg) { - $preservePkgOutput = "1"; -} $pkgoutputbasename =~ s/\.pkg//g; # Store output file names to variables -my $pkgoutput = $pkgoutputbasename.".pkg"; +my ($dummy1, $dummy2, $pkgoutput) = File::Spec->splitpath($pkgoutputbasename.".pkg"); +$pkgoutput = $tempPackageDir."/".$pkgoutput; my $sisoutputbasename; if ($signed_sis_name eq "") { $sisoutputbasename = $pkgoutputbasename; @@ -300,9 +308,7 @@ unlink $unsigned_sis_name; if (!$onlyUnsigned) { unlink $signed_sis_name; } -if (!$preservePkgOutput) { - unlink $pkgoutput; -} +unlink $pkgoutput; # Preprocess PKG @@ -334,6 +340,11 @@ print OUTPUT $_; close OUTPUT; if ($preprocessonly) { + # Copy preprocessed file from tmp dir to pkg file dir + my ($templateVolume, $templatePath, $templateFileName) = File::Spec->splitpath($templatepkg); + my ($dummy1, $dummy2, $copyFileName) = File::Spec->splitpath($pkgoutput); + my $copyTarget = File::Spec->catpath($templateVolume, $templatePath, $copyFileName); + copy($pkgoutput, $copyTarget) or die "Preprocessed pkg file '$pkgoutput' cannot be copied."; exit; } @@ -354,7 +365,7 @@ if($stub) { system ("$patch_capabilities -c $pkgoutput") and print ("Warning: Package check for self-signing viability failed. Installing the package on a device will most likely fail!\n\n"); } else { print("Auto-patching self-signed package.\n"); - system ("$patch_capabilities $pkgoutput") and die ("ERROR: Automatic patching failed"); + system ("$patch_capabilities -t $tempPackageDir $pkgoutput") and die ("ERROR: Automatic patching failed"); } } @@ -377,9 +388,6 @@ if($stub) { print ("\nUnsigned package creation failed!\n"); } - if (!$preservePkgOutput) { - unlink $pkgoutput; - } print ("\n"); exit; } @@ -405,10 +413,7 @@ if($stub) { print ("\tAdditionally signed the SIS with certificate: $row->[0]!\n"); } - # remove temporary pkg and unsigned sis - if (!$preservePkgOutput) { - unlink $pkgoutput; - } + # remove temporary unsigned sis if (!$preserveUnsigned) { unlink $unsigned_sis_name; } diff --git a/bin/patch_capabilities.pl b/bin/patch_capabilities.pl index 91ab4b8..40b6a17 100755 --- a/bin/patch_capabilities.pl +++ b/bin/patch_capabilities.pl @@ -53,6 +53,7 @@ use File::Copy; use File::Spec; +use File::Path; sub Usage() { print("This script can be used to set capabilities of all binaries\n"); @@ -63,11 +64,13 @@ sub Usage() { print(" symbian-sbsv2 platform, 'target-platform' is REQUIRED. ***\n\n"); print(" *** NOTE2: When patching gcce binaries built with symbian-sbsv2 toolchain,\n"); print(" armv5 must be specified as platform.\n"); - print("\nUsage: patch_capabilities.pl [-c] pkg_filename [target-platform [capability list]]\n"); + print("\nUsage: patch_capabilities.pl [-c|-t tmp_path] pkg_filename [target-platform [capability list]]\n"); print("\nE.g. patch_capabilities.pl myapp_template.pkg release-armv5 \"All -TCB\"\n"); print("\nThe parameter -c can be used to just check if package is compatible with self-signing\n"); print("without actually doing any patching.\n"); print("Explicit capability list cannot be used with -c parameter.\n"); + print("\nThe parameter -t can be used to specify a dir under which the temporary files are created.\n"); + print("Defaults to 'patch_capabilities_tmp' under the path to pkg file.\n"); exit(); } @@ -101,6 +104,7 @@ if (@ARGV) my $pkgFileName = shift(@ARGV); my $justCheck = ""; my $msgPrefix = "Patching:"; + my $tempPatchPath = ""; if ($pkgFileName eq "-c") { $pkgFileName = shift(@ARGV); @@ -108,6 +112,18 @@ if (@ARGV) $msgPrefix = "Warning:"; } + if ($pkgFileName eq "-t") { + $tempPatchPath = shift(@ARGV); + $pkgFileName = shift(@ARGV); + } + + my ($pkgVolume, $pkgPath, $pkgPlainFileName) = File::Spec->splitpath($pkgFileName); + if ($tempPatchPath eq "") { + $tempPatchPath = File::Spec->catpath($pkgVolume, $pkgPath."patch_capabilities_tmp", ""); + } + + mkpath($tempPatchPath); + # These variables will only be set for template .pkg files. my $target; my $platform; @@ -165,8 +181,9 @@ if (@ARGV) # Start with no binaries listed. my @binaries = (); + my $binariesDelimeter = "///"; - my $tempPkgFileName = $pkgFileName."_@@TEMP@@"; + my $tempPkgFileName = $tempPatchPath."/__TEMP__".$pkgPlainFileName; if (!$justCheck) { unlink($tempPkgFileName); @@ -216,19 +233,23 @@ if (@ARGV) $sourcePath =~ s/\$\(TARGET\)/$target/gm; } + my ($dummy1, $dummy2, $binaryBaseName) = File::Spec->splitpath($sourcePath); + if ($justCheck) { - push (@binaries, $sourcePath); + push (@binaries, $binaryBaseName.$binariesDelimeter.$sourcePath); } else { - # Change the source file name (but only if not already patched) + # Copy original files over to patching dir + # Patching dir will be flat to make it cleanable with QMAKE_CLEAN, so path + # will be collapsed into the file name to avoid name collisions in the rare + # case where custom pkg rules are used to install files with same names from + # different directories (probably using platform checks to choose only one of them.) my $patchedSourcePath = $sourcePath; - if ($patchedSourcePath !~ m/_patched_caps/) - { - $newLine =~ s/(^.*)(\.dll|\.exe)(.*)(\.dll|\.exe)/$1_patched_caps$2$3$4/i; - $patchedSourcePath =~ s/(^.*)(\.dll|\.exe)/$1_patched_caps$2/i; + $patchedSourcePath =~ s/[\/\\:]/_/g; + $patchedSourcePath = "$tempPatchPath/$patchedSourcePath"; + $newLine =~ s/^.*(\.dll|\.exe)(.*)(\.dll|\.exe)/\"$patchedSourcePath$2$3/i; - copy($sourcePath, $patchedSourcePath) or die "$sourcePath cannot be copied for patching."; - } - push (@binaries, $patchedSourcePath); + copy($sourcePath, $patchedSourcePath) or die "$sourcePath cannot be copied for patching."; + push (@binaries, $binaryBaseName.$binariesDelimeter.$patchedSourcePath); } } } @@ -250,10 +271,13 @@ if (@ARGV) my $baseCommandToExecute = "${epocToolsDir}elftran -vid 0x0 -capability \"%s\" "; # Actually set the capabilities of the listed binaries. - foreach my $binaryPath(@binaries) + foreach my $binariesItem(@binaries) { + $binariesItem =~ m|^(.*)$binariesDelimeter(.*)$|; + my $binaryBaseName = $1; + my $binaryPath = $2; + # Create the command line for setting the capabilities. - my ($binaryVolume, $binaryDirs, $binaryBaseName) = File::Spec->splitpath($binaryPath); my $commandToExecute = $baseCommandToExecute; my $executeNeeded = ""; if (@capabilitiesSpecified) diff --git a/mkspecs/features/symbian/sis_targets.prf b/mkspecs/features/symbian/sis_targets.prf index d6d6df0..d0fe881 100644 --- a/mkspecs/features/symbian/sis_targets.prf +++ b/mkspecs/features/symbian/sis_targets.prf @@ -26,6 +26,9 @@ equals(GENERATE_SIS_TARGETS, true) { qtPrepareTool(QMAKE_CREATEPACKAGE, createpackage) + CREATEPACKAGE_DIR = $$OBJECTS_DIR/createpackage_tmp + QMAKE_CLEAN += $$CREATEPACKAGE_DIR/* + symbian-abld|symbian-sbsv2 { symbian-sbsv2 { CONVERT_GCCE_PARAM = -g @@ -48,7 +51,7 @@ equals(GENERATE_SIS_TARGETS, true) { sis_target.depends += $${baseTarget}_template.pkg ok_sis_target.target = ok_sis - ok_sis_target.commands = $$QMAKE_CREATEPACKAGE $$CONVERT_GCCE_PARAM $(QT_SIS_OPTIONS) $${baseTarget}_template.pkg \ + ok_sis_target.commands = $$QMAKE_CREATEPACKAGE $$CONVERT_GCCE_PARAM -t $$CREATEPACKAGE_DIR $(QT_SIS_OPTIONS) $${baseTarget}_template.pkg \ $(QT_SIS_TARGET) $(QT_SIS_CERTIFICATE) $(QT_SIS_KEY) $(QT_SIS_PASSPHRASE) unsigned_sis_target.target = unsigned_sis @@ -65,7 +68,7 @@ equals(GENERATE_SIS_TARGETS, true) { unsigned_sis_target.depends += $${baseTarget}_template.pkg ok_unsigned_sis_target.target = ok_unsigned_sis - ok_unsigned_sis_target.commands = $$QMAKE_CREATEPACKAGE $$CONVERT_GCCE_PARAM $(QT_SIS_OPTIONS) -o $${baseTarget}_template.pkg $(QT_SIS_TARGET) + ok_unsigned_sis_target.commands = $$QMAKE_CREATEPACKAGE $$CONVERT_GCCE_PARAM -t $$CREATEPACKAGE_DIR $(QT_SIS_OPTIONS) -o $${baseTarget}_template.pkg $(QT_SIS_TARGET) target_sis_target.target = $${baseTarget}.sis target_sis_target.commands = $(MAKE) -f $(MAKEFILE) sis @@ -77,7 +80,7 @@ equals(GENERATE_SIS_TARGETS, true) { installer_sis_target.depends = $${baseTarget}_installer.pkg sis ok_installer_sis_target.target = ok_installer_sis - ok_installer_sis_target.commands = $$QMAKE_CREATEPACKAGE $(QT_SIS_OPTIONS) $${baseTarget}_installer.pkg - \ + ok_installer_sis_target.commands = $$QMAKE_CREATEPACKAGE -t $$CREATEPACKAGE_DIR $(QT_SIS_OPTIONS) $${baseTarget}_installer.pkg - \ $(QT_SIS_CERTIFICATE) $(QT_SIS_KEY) $(QT_SIS_PASSPHRASE) ok_installer_sis_target.depends = $${baseTarget}_installer.pkg @@ -86,7 +89,7 @@ equals(GENERATE_SIS_TARGETS, true) { unsigned_installer_sis_target.depends = $${baseTarget}_installer.pkg unsigned_sis ok_unsigned_installer_sis_target.target = ok_unsigned_installer_sis - ok_unsigned_installer_sis_target.commands = $$QMAKE_CREATEPACKAGE $(QT_SIS_OPTIONS) -o $${baseTarget}_installer.pkg + ok_unsigned_installer_sis_target.commands = $$QMAKE_CREATEPACKAGE -t $$CREATEPACKAGE_DIR $(QT_SIS_OPTIONS) -o $${baseTarget}_installer.pkg ok_unsigned_installer_sis_target.depends = $${baseTarget}_installer.pkg fail_sis_nocache_target.target = fail_sis_nocache @@ -106,7 +109,7 @@ equals(GENERATE_SIS_TARGETS, true) { stub_sis_target.depends += $${baseTarget}_stub.pkg ok_stub_sis_target.target = ok_stub_sis - ok_stub_sis_target.commands = $$QMAKE_CREATEPACKAGE -s $(QT_SIS_OPTIONS) $${baseTarget}_stub.pkg \ + ok_stub_sis_target.commands = $$QMAKE_CREATEPACKAGE -t $$CREATEPACKAGE_DIR -s $(QT_SIS_OPTIONS) $${baseTarget}_stub.pkg \ $(QT_SIS_TARGET) $(QT_SIS_CERTIFICATE) $(QT_SIS_KEY) $(QT_SIS_PASSPHRASE) QMAKE_EXTRA_TARGETS += sis_target \ @@ -142,6 +145,7 @@ equals(GENERATE_SIS_TARGETS, true) { && echo QT_SIS_TARGET ?= $(QT_SIS_TARGET) >> $$make_cache_name QMAKE_EXTRA_TARGETS += store_build_target + QMAKE_CLEAN += $$make_cache_name } } else { sis_destdir = $$DESTDIR @@ -149,12 +153,12 @@ equals(GENERATE_SIS_TARGETS, true) { !equals(TARGET, "$$baseTarget"):sis_destdir = $$sis_destdir/$$dirname(TARGET) sis_target.target = sis - sis_target.commands = $$QMAKE_CREATEPACKAGE $(QT_SIS_OPTIONS) $${baseTarget}_template.pkg \ + sis_target.commands = $$QMAKE_CREATEPACKAGE -t $$CREATEPACKAGE_DIR $(QT_SIS_OPTIONS) $${baseTarget}_template.pkg \ - $(QT_SIS_CERTIFICATE) $(QT_SIS_KEY) $(QT_SIS_PASSPHRASE) sis_target.depends = first $${baseTarget}_template.pkg unsigned_sis_target.target = unsigned_sis - unsigned_sis_target.commands = $$QMAKE_CREATEPACKAGE $(QT_SIS_OPTIONS) -o $${baseTarget}_template.pkg + unsigned_sis_target.commands = $$QMAKE_CREATEPACKAGE -t $$CREATEPACKAGE_DIR $(QT_SIS_OPTIONS) -o $${baseTarget}_template.pkg unsigned_sis_target.depends = first $${baseTarget}_template.pkg target_sis_target.target = $${sis_destdir}/$${baseTarget}.sis @@ -165,12 +169,12 @@ equals(GENERATE_SIS_TARGETS, true) { installer_sis_target.depends = $${baseTarget}_installer.pkg sis ok_installer_sis_target.target = ok_installer_sis - ok_installer_sis_target.commands = $$QMAKE_CREATEPACKAGE $(QT_SIS_OPTIONS) $${baseTarget}_installer.pkg - \ + ok_installer_sis_target.commands = $$QMAKE_CREATEPACKAGE -t $$CREATEPACKAGE_DIR $(QT_SIS_OPTIONS) $${baseTarget}_installer.pkg - \ $(QT_SIS_CERTIFICATE) $(QT_SIS_KEY) $(QT_SIS_PASSPHRASE) ok_installer_sis_target.depends = $${baseTarget}_installer.pkg unsigned_installer_sis_target.target = unsigned_installer_sis - unsigned_installer_sis_target.commands = $$QMAKE_CREATEPACKAGE $(QT_SIS_OPTIONS) -o $${baseTarget}_installer.pkg + unsigned_installer_sis_target.commands = $$QMAKE_CREATEPACKAGE -t $$CREATEPACKAGE_DIR $(QT_SIS_OPTIONS) -o $${baseTarget}_installer.pkg unsigned_installer_sis_target.depends = $${baseTarget}_installer.pkg unsigned_sis !isEmpty(sis_destdir):!equals(sis_destdir, "."):!equals(sis_destdir, "./") { diff --git a/qmake/generators/symbian/initprojectdeploy_symbian.cpp b/qmake/generators/symbian/initprojectdeploy_symbian.cpp index f9fae9d..8d04a42 100644 --- a/qmake/generators/symbian/initprojectdeploy_symbian.cpp +++ b/qmake/generators/symbian/initprojectdeploy_symbian.cpp @@ -169,9 +169,7 @@ void initProjectDeploySymbian(QMakeProject* project, QStringList& generatedDirs, QStringList& generatedFiles) { - QString targetPath = project->values("deploy.path").join(" "); - if (targetPath.isEmpty()) - targetPath = testPath; + QString targetPath = testPath; if (targetPath.endsWith("/") || targetPath.endsWith("\\")) targetPath = targetPath.mid(0, targetPath.size() - 1); @@ -225,7 +223,10 @@ void initProjectDeploySymbian(QMakeProject* project, } else { if (0 == platform.compare(QLatin1String(EMULATOR_DEPLOYMENT_PLATFORM))) { if (devicePathHasDriveLetter) { - devicePath = qt_epocRoot() + "epoc32/winscw/" + devicePath.remove(1, 1); + if (devicePath.startsWith("!")) + devicePath = qt_epocRoot() + "epoc32/winscw/c" + devicePath.remove(0, 2); + else + devicePath = qt_epocRoot() + "epoc32/winscw/" + devicePath.remove(1, 1); } else { devicePath = qt_epocRoot() + "epoc32/winscw/c" + devicePath; } diff --git a/qmake/generators/symbian/symmake_sbsv2.cpp b/qmake/generators/symbian/symmake_sbsv2.cpp index 0fdef86..767645a 100644 --- a/qmake/generators/symbian/symmake_sbsv2.cpp +++ b/qmake/generators/symbian/symmake_sbsv2.cpp @@ -84,6 +84,12 @@ static void fixFlmCmd(QString *cmdLine, const QMap<QString, QString> &commandsTo // separator, so replace it with "&&" command concatenator. cmdLine->replace("\n\t", "&&"); + // Strip output suppression, as sbsv2 can't handle it in FLMs. Cannot be done by simply + // adding "@" to commandsToReplace, as it'd get handled last due to alphabetical ordering, + // potentially masking other commands that need replacing. + if (cmdLine->contains("@")) + cmdLine->replace(QRegExp(cmdFind.arg("@")), cmdReplace.arg("")); + // Iterate command replacements in reverse alphabetical order of keys so // that keys which are starts of other longer keys are iterated after longer keys. QMapIterator<QString, QString> cmdIter(commandsToReplace); diff --git a/src/corelib/io/qfilesystemwatcher_symbian.cpp b/src/corelib/io/qfilesystemwatcher_symbian.cpp index 29ec77a..8e8dfe5 100644 --- a/src/corelib/io/qfilesystemwatcher_symbian.cpp +++ b/src/corelib/io/qfilesystemwatcher_symbian.cpp @@ -95,7 +95,10 @@ void QNotifyChangeEvent::RunL() SetActive(); if (!failureCount) { - QT_TRYCATCH_LEAVING(engine->emitPathChanged(this)); + int err; + QT_TRYCATCH_ERROR(err, engine->emitPathChanged(this)); + if (err != KErrNone) + qWarning("QNotifyChangeEvent::RunL() - emitPathChanged threw exception (Converted error code: %d)", err); } } } diff --git a/src/corelib/io/qprocess.cpp b/src/corelib/io/qprocess.cpp index db41a55..a45225f 100644 --- a/src/corelib/io/qprocess.cpp +++ b/src/corelib/io/qprocess.cpp @@ -2127,6 +2127,9 @@ void QProcess::terminate() On Symbian, this function requires platform security capability \c PowerMgmt. If absent, the process will panic with KERN-EXEC 46. + \note Killing running processes from other processes will typically + cause a panic in Symbian due to platform security. + \sa {Symbian Platform Security Requirements} \sa terminate() */ diff --git a/src/corelib/io/qprocess_symbian.cpp b/src/corelib/io/qprocess_symbian.cpp index d22d1ed..8a74c7b 100644 --- a/src/corelib/io/qprocess_symbian.cpp +++ b/src/corelib/io/qprocess_symbian.cpp @@ -961,16 +961,15 @@ bool QProcessPrivate::waitForFinished(int msecs) User::WaitForRequest(logonStatus, timerStatus); QPROCESS_DEBUG_PRINT("QProcessPrivate::waitForFinished() - Wait completed"); - if (timerStatus == KErrNone) + if (logonStatus != KRequestPending) { + timer.Cancel(); + User::WaitForRequest(timerStatus); + } else { timeoutOccurred = true; - - timer.Cancel(); + symbianProcess->LogonCancel(logonStatus); + User::WaitForRequest(logonStatus); + } timer.Close(); - - symbianProcess->LogonCancel(logonStatus); - - // Eat cancel request completion so that it won't mess up main thread scheduling later - User::WaitForRequest(logonStatus, timerStatus); } } else { QPROCESS_DEBUG_PRINT("QProcessPrivate::waitForFinished(), qt_rprocess_running returned false"); diff --git a/src/corelib/kernel/qeventdispatcher_symbian.cpp b/src/corelib/kernel/qeventdispatcher_symbian.cpp index 53796be..4c01bde 100644 --- a/src/corelib/kernel/qeventdispatcher_symbian.cpp +++ b/src/corelib/kernel/qeventdispatcher_symbian.cpp @@ -43,6 +43,7 @@ #include <private/qthread_p.h> #include <qcoreapplication.h> #include <private/qcoreapplication_p.h> +#include <qsemaphore.h> #include <unistd.h> #include <errno.h> @@ -654,34 +655,54 @@ class QIdleDetectorThread { public: QIdleDetectorThread() - : m_state(STATE_RUN), m_stop(false) + : m_state(STATE_RUN), m_stop(false), m_running(false) { - qt_symbian_throwIfError(m_lock.CreateLocal(0)); + start(); + } + + ~QIdleDetectorThread() + { + stop(); + } + + void start() + { + QMutexLocker lock(&m_mutex); + if (m_running) + return; + m_stop = false; + m_state = STATE_RUN; TInt err = m_idleDetectorThread.Create(KNullDesC(), &idleDetectorThreadFunc, 1024, &User::Allocator(), this); if (err != KErrNone) - m_lock.Close(); - qt_symbian_throwIfError(err); + return; // Fail silently on error. Next kick will try again. Exception might stop the event being processed m_idleDetectorThread.SetPriority(EPriorityAbsoluteBackgroundNormal); m_idleDetectorThread.Resume(); + m_running = true; + // get a callback from QCoreApplication destruction to stop this thread + qAddPostRoutine(StopIdleDetectorThread); } - ~QIdleDetectorThread() + void stop() { + QMutexLocker lock(&m_mutex); + if (!m_running) + return; // close down the idle thread because if corelib is loaded temporarily, this would leak threads into the host process m_stop = true; - m_lock.Signal(); + m_kick.release(); m_idleDetectorThread.SetPriority(EPriorityNormal); TRequestStatus s; m_idleDetectorThread.Logon(s); User::WaitForRequest(s); m_idleDetectorThread.Close(); - m_lock.Close(); + m_running = false; } void kick() { + start(); m_state = STATE_KICKED; - m_lock.Signal(); + m_kick.release(); } bool hasRun() @@ -700,20 +721,29 @@ private: void IdleLoop() { while (!m_stop) { - m_lock.Wait(); + m_kick.acquire(); m_state = STATE_RUN; } } + static void StopIdleDetectorThread(); + private: enum IdleStates {STATE_KICKED, STATE_RUN} m_state; bool m_stop; + bool m_running; RThread m_idleDetectorThread; - RSemaphore m_lock; + QSemaphore m_kick; + QMutex m_mutex; }; Q_GLOBAL_STATIC(QIdleDetectorThread, idleDetectorThread); +void QIdleDetectorThread::StopIdleDetectorThread() +{ + idleDetectorThread()->stop(); +} + const int maxBusyTime = 2000; // maximum time we allow idle detector to be blocked before worrying, in milliseconds const int baseDelay = 1000; // minimum delay time used when backing off to allow idling, in microseconds #endif diff --git a/src/corelib/thread/qmutex_p.h b/src/corelib/thread/qmutex_p.h index 1a31c87..70860b1 100644 --- a/src/corelib/thread/qmutex_p.h +++ b/src/corelib/thread/qmutex_p.h @@ -62,6 +62,10 @@ # include <mach/semaphore.h> #endif +#if defined(Q_OS_SYMBIAN) +# include <e32std.h> +#endif + QT_BEGIN_NAMESPACE class QMutexPrivate : public QMutexData { @@ -81,12 +85,14 @@ public: #if defined(Q_OS_MAC) semaphore_t mach_semaphore; -#elif defined(Q_OS_UNIX) && !defined(Q_OS_LINUX) +#elif defined(Q_OS_UNIX) && !defined(Q_OS_LINUX) && !defined(Q_OS_SYMBIAN) volatile bool wakeup; pthread_mutex_t mutex; pthread_cond_t cond; #elif defined(Q_OS_WIN32) || defined(Q_OS_WINCE) HANDLE event; +#elif defined(Q_OS_SYMBIAN) + RSemaphore lock; #endif }; diff --git a/src/corelib/thread/qmutex_symbian.cpp b/src/corelib/thread/qmutex_symbian.cpp new file mode 100644 index 0000000..288c576 --- /dev/null +++ b/src/corelib/thread/qmutex_symbian.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore 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$ +** +****************************************************************************/ + +#include "qplatformdefs.h" +#include "qmutex.h" + +#ifndef QT_NO_THREAD +#include "qatomic.h" +#include "qelapsedtimer.h" +#include "qthread.h" +#include "qmutex_p.h" + +QT_BEGIN_NAMESPACE + + +QMutexPrivate::QMutexPrivate(QMutex::RecursionMode mode) + : QMutexData(mode), maximumSpinTime(MaximumSpinTimeThreshold), averageWaitTime(0), owner(0), count(0) +{ + int r = lock.CreateLocal(0); + if (r != KErrNone) + qWarning("QMutex: failed to create lock, error %d", r); + qt_symbian_throwIfError(r); +} + +QMutexPrivate::~QMutexPrivate() +{ + lock.Close(); +} + +bool QMutexPrivate::wait(int timeout) +{ + if (contenders.fetchAndAddAcquire(1) == 0) { + // lock acquired without waiting + return true; + } + int r = KErrTimedOut; + if (timeout < 0) { + lock.Wait(); + r = KErrNone; + } else { + // Symbian lock waits are specified in microseconds. + // The wait is therefore chunked. + // KErrNone indicates success, KErrGeneral and KErrArgument are real fails, anything else is a timeout + do { + int waitTime = qMin(KMaxTInt / 1000, timeout); + timeout -= waitTime; + // Symbian undocumented feature - 0us means no timeout! Use a minimum of 1 + r = lock.Wait(qMax(1, waitTime * 1000)); + } while (r != KErrNone && r != KErrGeneral && r != KErrArgument && timeout > 0); + } + bool returnValue = (r == KErrNone); + contenders.deref(); + return returnValue; +} + +void QMutexPrivate::wakeUp() +{ + lock.Signal(); +} + +QT_END_NAMESPACE + +#endif // QT_NO_THREAD diff --git a/src/corelib/thread/qthread_symbian.cpp b/src/corelib/thread/qthread_symbian.cpp new file mode 100644 index 0000000..1474b36 --- /dev/null +++ b/src/corelib/thread/qthread_symbian.cpp @@ -0,0 +1,599 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore 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$ +** +****************************************************************************/ + +#include "qthread.h" +#include "qplatformdefs.h" +#include <private/qcoreapplication_p.h> +#include <private/qeventdispatcher_symbian_p.h> +#include "qthreadstorage.h" +#include "qthread_p.h" +#include <private/qsystemerror_p.h> + +#include <sched.h> +#include <hal.h> +#include <hal_data.h> + +// You only find these enumerations on Symbian^3 onwards, so we need to provide our own +// to remain compatible with older releases. They won't be called by pre-Sym^3 SDKs. + +// HALData::ENumCpus +#define QT_HALData_ENumCpus 119 + +QT_BEGIN_NAMESPACE + +#ifndef QT_NO_THREAD + +enum { ThreadPriorityResetFlag = 0x80000000 }; + +// Utility functions for getting, setting and clearing thread specific data. +static QThreadData *get_thread_data() +{ + return reinterpret_cast<QThreadData *>(Dll::Tls()); +} + +static void set_thread_data(QThreadData *data) +{ + qt_symbian_throwIfError(Dll::SetTls(data)); +} + +static void clear_thread_data() +{ + Dll::FreeTls(); +} + + +static void init_symbian_thread_handle(RThread &thread) +{ + thread = RThread(); + TThreadId threadId = thread.Id(); + qt_symbian_throwIfError(thread.Open(threadId, EOwnerProcess)); +} + +QThreadData *QThreadData::current() +{ + QThreadData *data = get_thread_data(); + if (!data) { + void *a; + if (QInternal::activateCallbacks(QInternal::AdoptCurrentThread, &a)) { + QThread *adopted = static_cast<QThread*>(a); + Q_ASSERT(adopted); + data = QThreadData::get2(adopted); + set_thread_data(data); + adopted->d_func()->running = true; + adopted->d_func()->finished = false; + static_cast<QAdoptedThread *>(adopted)->init(); + } else { + data = new QThreadData; + QT_TRY { + set_thread_data(data); + data->thread = new QAdoptedThread(data); + } QT_CATCH(...) { + clear_thread_data(); + data->deref(); + data = 0; + QT_RETHROW; + } + data->deref(); + } + if (!QCoreApplicationPrivate::theMainThread) + QCoreApplicationPrivate::theMainThread = data->thread; + } + return data; +} + + +class QCAdoptedThreadMonitor : public CActive +{ +public: + QCAdoptedThreadMonitor(QThread *thread) + : CActive(EPriorityStandard), data(QThreadData::get2(thread)) + { + CActiveScheduler::Add(this); + data->symbian_thread_handle.Logon(iStatus); + SetActive(); + } + ~QCAdoptedThreadMonitor() + { + Cancel(); + } + void DoCancel() + { + data->symbian_thread_handle.LogonCancel(iStatus); + } + void RunL(); +private: + QThreadData* data; +}; + +class QCAddAdoptedThread : public CActive +{ +public: + QCAddAdoptedThread() + : CActive(EPriorityStandard) + { + CActiveScheduler::Add(this); + } + void ConstructL() + { + User::LeaveIfError(monitorThread.Open(RThread().Id())); + start(); + } + ~QCAddAdoptedThread() + { + Cancel(); + monitorThread.Close(); + } + void DoCancel() + { + User::RequestComplete(stat, KErrCancel); + } + void start() + { + iStatus = KRequestPending; + SetActive(); + stat = &iStatus; + } + void RunL() + { + if (iStatus.Int() != KErrNone) + return; + + QMutexLocker adoptedThreadMonitorMutexlock(&adoptedThreadMonitorMutex); + for (int i=threadsToAdd.size()-1; i>=0; i--) { + // Create an active object to monitor the thread + new (ELeave) QCAdoptedThreadMonitor(threadsToAdd[i]); + count++; + threadsToAdd.pop_back(); + } + start(); + } + static void add(QThread *thread) + { + QMutexLocker adoptedThreadMonitorMutexlock(&adoptedThreadMonitorMutex); + if (!adoptedThreadAdder) { + RThread monitorThread; + qt_symbian_throwIfError(monitorThread.Create(KNullDesC(), &monitorThreadFunc, 1024, &User::Allocator(), 0)); + TRequestStatus started; + monitorThread.Rendezvous(started); + monitorThread.Resume(); + User::WaitForRequest(started); + monitorThread.Close(); + } + if (RThread().Id() == adoptedThreadAdder->monitorThread.Id()) + return; + adoptedThreadAdder->threadsToAdd.push_back(thread); + if (adoptedThreadAdder->stat) { + adoptedThreadAdder->monitorThread.RequestComplete(adoptedThreadAdder->stat, KErrNone); + } + } + static void monitorThreadFuncL() + { + CActiveScheduler* scheduler = new (ELeave) CActiveScheduler(); + CleanupStack::PushL(scheduler); + CActiveScheduler::Install(scheduler); + + adoptedThreadAdder = new(ELeave) QCAddAdoptedThread(); + CleanupStack::PushL(adoptedThreadAdder); + adoptedThreadAdder->ConstructL(); + QCAddAdoptedThread *adder = adoptedThreadAdder; + + RThread::Rendezvous(KErrNone); + CActiveScheduler::Start(); + + CleanupStack::PopAndDestroy(adder); + CleanupStack::PopAndDestroy(scheduler); + } + static int monitorThreadFunc(void *) + { + _LIT(KMonitorThreadName, "adoptedMonitorThread"); + RThread::RenameMe(KMonitorThreadName()); + CTrapCleanup* cleanup = CTrapCleanup::New(); + TRAPD(ret, monitorThreadFuncL()); + delete cleanup; + return ret; + } + static void threadDied() + { + QMutexLocker adoptedThreadMonitorMutexlock(&adoptedThreadMonitorMutex); + if (adoptedThreadAdder) { + adoptedThreadAdder->count--; + if (adoptedThreadAdder->count <= 0 && adoptedThreadAdder->threadsToAdd.size() == 0) { + CActiveScheduler::Stop(); + adoptedThreadAdder = 0; + } + } + } + +private: + QVector<QThread*> threadsToAdd; + RThread monitorThread; + static QMutex adoptedThreadMonitorMutex; + static QCAddAdoptedThread *adoptedThreadAdder; + int count; + TRequestStatus *stat; +}; + +QMutex QCAddAdoptedThread::adoptedThreadMonitorMutex; +QCAddAdoptedThread* QCAddAdoptedThread::adoptedThreadAdder = 0; + +void QCAdoptedThreadMonitor::RunL() +{ + data->deref(); + QCAddAdoptedThread::threadDied(); + delete this; +} + +void QAdoptedThread::init() +{ + Q_D(QThread); + d->thread_id = RThread().Id(); // type operator to TUint + init_symbian_thread_handle(d->data->symbian_thread_handle); + QCAddAdoptedThread::add(this); +} + +/* + QThreadPrivate +*/ + +#if defined(Q_C_CALLBACKS) +extern "C" { +#endif + +typedef void*(*QtThreadCallback)(void*); + +#if defined(Q_C_CALLBACKS) +} +#endif + +#endif // QT_NO_THREAD + +void QThreadPrivate::createEventDispatcher(QThreadData *data) +{ + data->eventDispatcher = new QEventDispatcherSymbian; + data->eventDispatcher->startingUp(); +} + +#ifndef QT_NO_THREAD + +void *QThreadPrivate::start(void *arg) +{ + QThread *thr = reinterpret_cast<QThread *>(arg); + QThreadData *data = QThreadData::get2(thr); + + // do we need to reset the thread priority? + if (int(thr->d_func()->priority) & ThreadPriorityResetFlag) { + thr->setPriority(QThread::Priority(thr->d_func()->priority & ~ThreadPriorityResetFlag)); + } + + // On symbian, threads other than the main thread are non critical by default + // This means a worker thread can crash without crashing the application - to + // use this feature, we would need to use RThread::Logon in the main thread + // to catch abnormal thread exit and emit the finished signal. + // For the sake of cross platform consistency, we set the thread as process critical + // - advanced users who want the symbian behaviour can change the critical + // attribute of the thread again once the app gains control in run() + User::SetCritical(User::EProcessCritical); + + set_thread_data(data); + + { + QMutexLocker locker(&thr->d_func()->mutex); + data->quitNow = thr->d_func()->exited; + } + + // ### TODO: allow the user to create a custom event dispatcher + createEventDispatcher(data); + + emit thr->started(); + thr->run(); + + QThreadPrivate::finish(arg); + + return 0; +} + +void QThreadPrivate::finish(void *arg, bool lockAnyway, bool closeNativeHandle) +{ + QThread *thr = reinterpret_cast<QThread *>(arg); + QThreadPrivate *d = thr->d_func(); + + QMutexLocker locker(lockAnyway ? &d->mutex : 0); + + d->isInFinish = true; + d->priority = QThread::InheritPriority; + bool terminated = d->terminated; + void *data = &d->data->tls; + locker.unlock(); + if (terminated) + emit thr->terminated(); + emit thr->finished(); + QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); + QThreadStorageData::finish((void **)data); + locker.relock(); + d->terminated = false; + + QAbstractEventDispatcher *eventDispatcher = d->data->eventDispatcher; + if (eventDispatcher) { + d->data->eventDispatcher = 0; + locker.unlock(); + eventDispatcher->closingDown(); + delete eventDispatcher; + locker.relock(); + } + + d->thread_id = 0; + if (closeNativeHandle) + d->data->symbian_thread_handle.Close(); + d->running = false; + d->finished = true; + + d->isInFinish = false; + d->thread_done.wakeAll(); +} + + + + +/************************************************************************** + ** QThread + *************************************************************************/ + +Qt::HANDLE QThread::currentThreadId() +{ + return (Qt::HANDLE) (TUint) RThread().Id(); +} + +int QThread::idealThreadCount() +{ + int cores = 1; + + if (QSysInfo::symbianVersion() >= QSysInfo::SV_SF_3) { + TInt inumcpus; + TInt err; + err = HAL::Get((HALData::TAttribute)QT_HALData_ENumCpus, inumcpus); + if (err == KErrNone) { + cores = qMax(inumcpus, 1); + } + } + + return cores; +} + +void QThread::yieldCurrentThread() +{ + sched_yield(); +} + +/* \internal + helper function to do thread sleeps +*/ +static void thread_sleep(unsigned long remaining, unsigned long scale) +{ + // maximum Symbian wait is 2^31 microseconds + unsigned long maxWait = KMaxTInt / scale; + do { + unsigned long waitTime = qMin(maxWait, remaining); + remaining -= waitTime; + User::AfterHighRes(waitTime * scale); + } while (remaining); +} + +void QThread::sleep(unsigned long secs) +{ + thread_sleep(secs, 1000000ul); +} + +void QThread::msleep(unsigned long msecs) +{ + thread_sleep(msecs, 1000ul); +} + +void QThread::usleep(unsigned long usecs) +{ + thread_sleep(usecs, 1ul); +} + +TThreadPriority calculateSymbianPriority(QThread::Priority priority) + { + // Both Qt & Symbian use limited enums; this matches the mapping previously done through conversion to Posix granularity + TThreadPriority symPriority; + switch (priority) + { + case QThread::IdlePriority: + symPriority = EPriorityMuchLess; + break; + case QThread::LowestPriority: + case QThread::LowPriority: + symPriority = EPriorityLess; + break; + case QThread::NormalPriority: + symPriority = EPriorityNormal; + break; + case QThread::HighPriority: + symPriority = EPriorityMore; + break; + case QThread::HighestPriority: + case QThread::TimeCriticalPriority: + symPriority = EPriorityMuchMore; + break; + case QThread::InheritPriority: + default: + symPriority = RThread().Priority(); + break; + } + return symPriority; + } + +void QThread::start(Priority priority) +{ + Q_D(QThread); + QMutexLocker locker(&d->mutex); + + if (d->isInFinish) + d->thread_done.wait(locker.mutex()); + + if (d->running) + return; + + d->running = true; + d->finished = false; + d->terminated = false; + d->returnCode = 0; + d->exited = false; + + d->priority = priority; + + if (d->stackSize == 0) + // The default stack size on Symbian is very small, making even basic + // operations like file I/O fail, so we increase it by default. + d->stackSize = 0x14000; // Maximum stack size on Symbian. + + int code = d->data->symbian_thread_handle.Create(KNullDesC, (TThreadFunction) QThreadPrivate::start, d->stackSize, NULL, this); + if (code == KErrNone) { + d->thread_id = d->data->symbian_thread_handle.Id(); + TThreadPriority symPriority = calculateSymbianPriority(priority); + d->data->symbian_thread_handle.SetPriority(symPriority); + d->data->symbian_thread_handle.Resume(); + } else { + qWarning("QThread::start: Thread creation error: %s", qPrintable(QSystemError(code, QSystemError::NativeError).toString())); + + d->running = false; + d->finished = false; + d->thread_id = 0; + d->data->symbian_thread_handle.Close(); + } +} + +void QThread::terminate() +{ + Q_D(QThread); + QMutexLocker locker(&d->mutex); + + if (!d->thread_id) + return; + + if (!d->running) + return; + if (!d->terminationEnabled) { + d->terminatePending = true; + return; + } + + d->terminated = true; + // "false, false" meaning: + // 1. lockAnyway = false. Don't lock the mutex because it's already locked + // (see above). + // 2. closeNativeSymbianHandle = false. We don't want to close the thread handle, + // because we need it here to terminate the thread. + QThreadPrivate::finish(this, false, false); + d->data->symbian_thread_handle.Terminate(KErrNone); + d->data->symbian_thread_handle.Close(); +} + +bool QThread::wait(unsigned long time) +{ + Q_D(QThread); + QMutexLocker locker(&d->mutex); + + if (d->thread_id == (TUint) RThread().Id()) { + qWarning("QThread::wait: Thread tried to wait on itself"); + return false; + } + + if (d->finished || !d->running) + return true; + + while (d->running) { + // Check if thread still exists. Needed because kernel will kill it without notification + // before global statics are deleted at application exit. + if (d->data->symbian_thread_handle.Handle() + && d->data->symbian_thread_handle.ExitType() != EExitPending) { + // Cannot call finish here as wait is typically called from another thread. + // It won't be necessary anyway, as we should never get here under normal operations; + // all QThreads are EProcessCritical and therefore cannot normally exit + // undetected (i.e. panic) as long as all thread control is via QThread. + return true; + } + if (!d->thread_done.wait(locker.mutex(), time)) + return false; + } + return true; +} + +void QThread::setTerminationEnabled(bool enabled) +{ + QThread *thr = currentThread(); + Q_ASSERT_X(thr != 0, "QThread::setTerminationEnabled()", + "Current thread was not started with QThread."); + QThreadPrivate *d = thr->d_func(); + QMutexLocker locker(&d->mutex); + d->terminationEnabled = enabled; + if (enabled && d->terminatePending) { + d->terminated = true; + // "false" meaning: + // - lockAnyway = false. Don't lock the mutex because it's already locked + // (see above). + QThreadPrivate::finish(thr, false); + locker.unlock(); // don't leave the mutex locked! + User::Exit(0); // may be some other cleanup required? what if AS or cleanup stack? + } +} + +void QThread::setPriority(Priority priority) +{ + Q_D(QThread); + QMutexLocker locker(&d->mutex); + if (!d->running) { + qWarning("QThread::setPriority: Cannot set priority, thread is not running"); + return; + } + + d->priority = priority; + + // copied from start() with a few modifications: + TThreadPriority symPriority = calculateSymbianPriority(priority); + d->data->symbian_thread_handle.SetPriority(symPriority); +} + +#endif // QT_NO_THREAD + +QT_END_NAMESPACE + diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp index 811a193..835378a 100644 --- a/src/corelib/thread/qthread_unix.cpp +++ b/src/corelib/thread/qthread_unix.cpp @@ -48,16 +48,7 @@ # include "../kernel/qeventdispatcher_glib_p.h" #endif -#ifdef Q_OS_SYMBIAN -#include <private/qeventdispatcher_symbian_p.h> -#else #include <private/qeventdispatcher_unix_p.h> -#endif - -#ifdef Q_OS_SYMBIAN -#include <hal.h> -#include <hal_data.h> -#endif #include "qthreadstorage.h" @@ -68,12 +59,6 @@ #include <sched.h> #include <errno.h> -// You only find these enumerations on Symbian^3 onwards, so we need to provide our own -// to remain compatible with older releases. They won't be called by pre-Sym^3 SDKs. - -// HALData::ENumCpus -#define QT_HALData_ENumCpus 119 - #ifdef Q_OS_BSD4 #include <sys/sysctl.h> #endif @@ -174,57 +159,23 @@ Q_DESTRUCTOR_FUNCTION(destroy_current_thread_data_key) // Utility functions for getting, setting and clearing thread specific data. -// In Symbian, TLS access is significantly faster than pthread_getspecific. -// However Symbian does not have the thread destruction cleanup functionality -// that pthread has, so pthread_setspecific is also used. static QThreadData *get_thread_data() { -#ifdef HAVE_TLS - return currentThreadData; -#elif defined Q_OS_SYMBIAN - return reinterpret_cast<QThreadData *>(Dll::Tls()); -#else pthread_once(¤t_thread_data_once, create_current_thread_data_key); return reinterpret_cast<QThreadData *>(pthread_getspecific(current_thread_data_key)); -#endif } static void set_thread_data(QThreadData *data) { -#ifdef HAVE_TLS - currentThreadData = data; -#elif defined Q_OS_SYMBIAN - qt_symbian_throwIfError(Dll::SetTls(data)); -#endif pthread_once(¤t_thread_data_once, create_current_thread_data_key); pthread_setspecific(current_thread_data_key, data); } static void clear_thread_data() { -#ifdef HAVE_TLS - currentThreadData = 0; -#elif defined Q_OS_SYMBIAN - Dll::FreeTls(); -#endif pthread_setspecific(current_thread_data_key, 0); } - -#ifdef Q_OS_SYMBIAN -static void init_symbian_thread_handle(RThread &thread) -{ - thread = RThread(); - TThreadId threadId = thread.Id(); - thread.Open(threadId); - - // Make thread handle accessible process wide - RThread originalCloser = thread; - thread.Duplicate(thread, EOwnerProcess); - originalCloser.Close(); -} -#endif - QThreadData *QThreadData::current() { QThreadData *data = get_thread_data(); @@ -262,9 +213,6 @@ void QAdoptedThread::init() { Q_D(QThread); d->thread_id = pthread_self(); -#ifdef Q_OS_SYMBIAN - init_symbian_thread_handle(d->data->symbian_thread_handle); -#endif } /* @@ -292,11 +240,7 @@ void QThreadPrivate::createEventDispatcher(QThreadData *data) data->eventDispatcher = new QEventDispatcherGlib; else #endif -#ifdef Q_OS_SYMBIAN - data->eventDispatcher = new QEventDispatcherSymbian; -#else - data->eventDispatcher = new QEventDispatcherUNIX; -#endif + data->eventDispatcher = new QEventDispatcherUNIX; data->eventDispatcher->startingUp(); } @@ -304,11 +248,8 @@ void QThreadPrivate::createEventDispatcher(QThreadData *data) void *QThreadPrivate::start(void *arg) { - // Symbian Open C supports neither thread cancellation nor cleanup_push. -#ifndef Q_OS_SYMBIAN pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); pthread_cleanup_push(QThreadPrivate::finish, arg); -#endif QThread *thr = reinterpret_cast<QThread *>(arg); QThreadData *data = QThreadData::get2(thr); @@ -318,23 +259,6 @@ void *QThreadPrivate::start(void *arg) thr->setPriority(QThread::Priority(thr->d_func()->priority & ~ThreadPriorityResetFlag)); } -#ifdef Q_OS_SYMBIAN - // Because Symbian Open C does not provide a way to convert between - // RThread and pthread_t, we must delay initialization of the RThread - // handle when creating a thread, until we are running in the new thread. - // Here, we pick up the current thread and assign that to the handle. - init_symbian_thread_handle(data->symbian_thread_handle); - - // On symbian, threads other than the main thread are non critical by default - // This means a worker thread can crash without crashing the application - to - // use this feature, we would need to use RThread::Logon in the main thread - // to catch abnormal thread exit and emit the finished signal. - // For the sake of cross platform consistency, we set the thread as process critical - // - advanced users who want the symbian behaviour can change the critical - // attribute of the thread again once the app gains control in run() - User::SetCritical(User::EProcessCritical); -#endif - set_thread_data(data); data->ref(); @@ -347,35 +271,21 @@ void *QThreadPrivate::start(void *arg) createEventDispatcher(data); emit thr->started(); -#ifndef Q_OS_SYMBIAN pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); pthread_testcancel(); -#endif thr->run(); -#ifdef Q_OS_SYMBIAN - QThreadPrivate::finish(arg); -#else pthread_cleanup_pop(1); -#endif return 0; } -#ifdef Q_OS_SYMBIAN -void QThreadPrivate::finish(void *arg, bool lockAnyway, bool closeNativeHandle) -#else void QThreadPrivate::finish(void *arg) -#endif { QThread *thr = reinterpret_cast<QThread *>(arg); QThreadPrivate *d = thr->d_func(); -#ifdef Q_OS_SYMBIAN - QMutexLocker locker(lockAnyway ? &d->mutex : 0); -#else QMutexLocker locker(&d->mutex); -#endif d->isInFinish = true; d->priority = QThread::InheritPriority; @@ -400,10 +310,6 @@ void QThreadPrivate::finish(void *arg) } d->thread_id = 0; -#ifdef Q_OS_SYMBIAN - if (closeNativeHandle) - d->data->symbian_thread_handle.Close(); -#endif d->running = false; d->finished = true; @@ -461,21 +367,6 @@ int QThread::idealThreadCount() #elif defined(Q_OS_INTEGRITY) // as of aug 2008 Integrity only supports one single core CPU cores = 1; -#elif defined(Q_OS_SYMBIAN) - if (QSysInfo::symbianVersion() >= QSysInfo::SV_SF_3) { - TInt inumcpus; - TInt err; - err = HAL::Get((HALData::TAttribute)QT_HALData_ENumCpus, inumcpus); - if (err != KErrNone) { - cores = 1; - } else if ( inumcpus <= 0 ) { - cores = 1; - } else { - cores = inumcpus; - } - } else { - cores = 1; - } #elif defined(Q_OS_VXWORKS) // VxWorks # if defined(QT_VXWORKS_HAS_CPUSET) @@ -616,8 +507,7 @@ void QThread::start(Priority priority) d->priority = priority; -#if defined(QT_HAS_THREAD_PRIORITY_SCHEDULING) && !defined(Q_OS_SYMBIAN) -// ### Need to implement thread sheduling and priorities for symbian os. Implementation removed for now +#if defined(QT_HAS_THREAD_PRIORITY_SCHEDULING) switch (priority) { case InheritPriority: { @@ -659,12 +549,6 @@ void QThread::start(Priority priority) } #endif // QT_HAS_THREAD_PRIORITY_SCHEDULING -#ifdef Q_OS_SYMBIAN - if (d->stackSize == 0) - // The default stack size on Symbian is very small, making even basic - // operations like file I/O fail, so we increase it by default. - d->stackSize = 0x14000; // Maximum stack size on Symbian. -#endif if (d->stackSize > 0) { #if defined(_POSIX_THREAD_ATTR_STACKSIZE) && (_POSIX_THREAD_ATTR_STACKSIZE-0 > 0) @@ -690,9 +574,7 @@ void QThread::start(Priority priority) if (code == EPERM) { // caller does not have permission to set the scheduling // parameters/policy -#ifndef Q_OS_SYMBIAN pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED); -#endif code = pthread_create(&d->thread_id, &attr, QThreadPrivate::start, this); } @@ -705,9 +587,6 @@ void QThread::start(Priority priority) d->running = false; d->finished = false; d->thread_id = 0; -#ifdef Q_OS_SYMBIAN - d->data->symbian_thread_handle.Close(); -#endif } } @@ -719,7 +598,6 @@ void QThread::terminate() if (!d->thread_id) return; -#ifndef Q_OS_SYMBIAN int code = pthread_cancel(d->thread_id); if (code) { qWarning("QThread::start: Thread termination error: %s", @@ -727,26 +605,6 @@ void QThread::terminate() } else { d->terminated = true; } -#else - if (!d->running) - return; - if (!d->terminationEnabled) { - d->terminatePending = true; - return; - } - - d->terminated = true; - // "false, false" meaning: - // 1. lockAnyway = false. Don't lock the mutex because it's already locked - // (see above). - // 2. closeNativeSymbianHandle = false. We don't want to close the thread handle, - // because we need it here to terminate the thread. - QThreadPrivate::finish(this, false, false); - d->data->symbian_thread_handle.Terminate(KErrNone); - d->data->symbian_thread_handle.Close(); -#endif - - } bool QThread::wait(unsigned long time) @@ -763,18 +621,6 @@ bool QThread::wait(unsigned long time) return true; while (d->running) { -#ifdef Q_OS_SYMBIAN - // Check if thread still exists. Needed because kernel will kill it without notification - // before global statics are deleted at application exit. - if (d->data->symbian_thread_handle.Handle() - && d->data->symbian_thread_handle.ExitType() != EExitPending) { - // Cannot call finish here as wait is typically called from another thread. - // It won't be necessary anyway, as we should never get here under normal operations; - // all QThreads are EProcessCritical and therefore cannot normally exit - // undetected (i.e. panic) as long as all thread control is via QThread. - return true; - } -#endif if (!d->thread_done.wait(locker.mutex(), time)) return false; } @@ -786,25 +632,11 @@ void QThread::setTerminationEnabled(bool enabled) QThread *thr = currentThread(); Q_ASSERT_X(thr != 0, "QThread::setTerminationEnabled()", "Current thread was not started with QThread."); -#ifndef Q_OS_SYMBIAN + Q_UNUSED(thr) pthread_setcancelstate(enabled ? PTHREAD_CANCEL_ENABLE : PTHREAD_CANCEL_DISABLE, NULL); if (enabled) pthread_testcancel(); -#else - QThreadPrivate *d = thr->d_func(); - QMutexLocker locker(&d->mutex); - d->terminationEnabled = enabled; - if (enabled && d->terminatePending) { - d->terminated = true; - // "false" meaning: - // - lockAnyway = false. Don't lock the mutex because it's already locked - // (see above). - QThreadPrivate::finish(thr, false); - locker.unlock(); // don't leave the mutex locked! - pthread_exit(NULL); - } -#endif } void QThread::setPriority(Priority priority) diff --git a/src/corelib/thread/qwaitcondition_symbian.cpp b/src/corelib/thread/qwaitcondition_symbian.cpp new file mode 100644 index 0000000..9967382 --- /dev/null +++ b/src/corelib/thread/qwaitcondition_symbian.cpp @@ -0,0 +1,196 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore 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$ +** +****************************************************************************/ + +#include "qplatformdefs.h" +#include "qwaitcondition.h" +#include "qmutex.h" +#include "qreadwritelock.h" +#include "qatomic.h" +#include "qstring.h" +#include "qelapsedtimer.h" + +#include "qmutex_p.h" +#include "qreadwritelock_p.h" + +#ifndef QT_NO_THREAD + +QT_BEGIN_NAMESPACE + +static void report_error(int err, const char *where, const char *what) +{ + if (err != KErrNone) + qWarning("%s: %s failure: %d", where, what, err); +} + +class QWaitConditionPrivate { +public: + RMutex mutex; + RCondVar cond; + int waiters; + int wakeups; + + QWaitConditionPrivate() + : waiters(0), wakeups(0) + { + qt_symbian_throwIfError(mutex.CreateLocal()); + int err = cond.CreateLocal(); + if (err != KErrNone) { + mutex.Close(); + qt_symbian_throwIfError(err); + } + } + + ~QWaitConditionPrivate() + { + cond.Close(); + mutex.Close(); + } + + bool wait(unsigned long time) + { + TInt err = KErrNone; + if (time == ULONG_MAX) { + // untimed wait, loop because RCondVar::Wait may return before the condition is triggered + do { + err = cond.Wait(mutex); + } while (err == KErrNone && wakeups == 0); + } else { + unsigned long maxWait = KMaxTInt / 1000; + QElapsedTimer waitTimer; + do { + waitTimer.start(); + unsigned long waitTime = qMin(maxWait, time); + // wait at least 1ms, as 0 means no wait + err = cond.TimedWait(mutex, qMax(1ul, waitTime) * 1000); + // RCondVar::TimedWait may return before the condition is triggered, update the timeout with actual wait time + time -= qMin((unsigned long)waitTimer.elapsed(), waitTime); + } while ((err == KErrNone && wakeups == 0) || (err == KErrTimedOut && time > 0)); + } + + Q_ASSERT_X(waiters > 0, "QWaitCondition::wait", "internal error (waiters)"); + --waiters; + if (err == KErrNone) { + Q_ASSERT_X(wakeups > 0, "QWaitCondition::wait", "internal error (wakeups)"); + --wakeups; + } + + mutex.Signal(); + + if (err && err != KErrTimedOut) + report_error(err, "QWaitCondition::wait()", "cv wait"); + return err == KErrNone; + } +}; + +QWaitCondition::QWaitCondition() +{ + d = new QWaitConditionPrivate; +} + +QWaitCondition::~QWaitCondition() +{ + delete d; +} + +void QWaitCondition::wakeOne() +{ + d->mutex.Wait(); + d->wakeups = qMin(d->wakeups + 1, d->waiters); + d->cond.Signal(); + d->mutex.Signal(); +} + +void QWaitCondition::wakeAll() +{ + d->mutex.Wait(); + d->wakeups = d->waiters; + d->cond.Broadcast(); + d->mutex.Signal(); +} + +bool QWaitCondition::wait(QMutex *mutex, unsigned long time) +{ + if (! mutex) + return false; + if (mutex->d->recursive) { + qWarning("QWaitCondition: cannot wait on recursive mutexes"); + return false; + } + + d->mutex.Wait(); + ++d->waiters; + mutex->unlock(); + + bool returnValue = d->wait(time); + + mutex->lock(); + + return returnValue; +} + +bool QWaitCondition::wait(QReadWriteLock *readWriteLock, unsigned long time) +{ + if (!readWriteLock || readWriteLock->d->accessCount == 0) + return false; + if (readWriteLock->d->accessCount < -1) { + qWarning("QWaitCondition: cannot wait on QReadWriteLocks with recursive lockForWrite()"); + return false; + } + + d->mutex.Wait(); + ++d->waiters; + + int previousAccessCount = readWriteLock->d->accessCount; + readWriteLock->unlock(); + + bool returnValue = d->wait(time); + + if (previousAccessCount < 0) + readWriteLock->lockForWrite(); + else + readWriteLock->lockForRead(); + + return returnValue; +} + +QT_END_NAMESPACE + +#endif // QT_NO_THREAD diff --git a/src/corelib/thread/qwaitcondition_unix.cpp b/src/corelib/thread/qwaitcondition_unix.cpp index f06a158..d0f23b5 100644 --- a/src/corelib/thread/qwaitcondition_unix.cpp +++ b/src/corelib/thread/qwaitcondition_unix.cpp @@ -61,8 +61,6 @@ static void report_error(int code, const char *where, const char *what) qWarning("%s: %s failure: %s", where, what, qPrintable(qt_error_string(code))); } - - class QWaitConditionPrivate { public: pthread_mutex_t mutex; diff --git a/src/corelib/thread/thread.pri b/src/corelib/thread/thread.pri index 90583bb..592ab16 100644 --- a/src/corelib/thread/thread.pri +++ b/src/corelib/thread/thread.pri @@ -4,11 +4,11 @@ HEADERS += thread/qmutex.h \ thread/qreadwritelock.h \ thread/qsemaphore.h \ - thread/qthread.h \ - thread/qthreadstorage.h \ - thread/qwaitcondition.h \ - thread/qatomic.h - + thread/qthread.h \ + thread/qthreadstorage.h \ + thread/qwaitcondition.h \ + thread/qatomic.h + # private headers HEADERS += thread/qmutex_p.h \ thread/qmutexpool_p.h \ @@ -19,14 +19,18 @@ HEADERS += thread/qmutex_p.h \ SOURCES += thread/qatomic.cpp \ thread/qmutex.cpp \ thread/qreadwritelock.cpp \ - thread/qmutexpool.cpp \ - thread/qsemaphore.cpp \ - thread/qthread.cpp \ - thread/qthreadstorage.cpp + thread/qmutexpool.cpp \ + thread/qsemaphore.cpp \ + thread/qthread.cpp \ + thread/qthreadstorage.cpp -unix:SOURCES += thread/qmutex_unix.cpp \ - thread/qthread_unix.cpp \ - thread/qwaitcondition_unix.cpp +unix:!symbian:SOURCES += thread/qmutex_unix.cpp \ + thread/qthread_unix.cpp \ + thread/qwaitcondition_unix.cpp + +symbian:SOURCES += thread/qmutex_symbian.cpp \ + thread/qthread_symbian.cpp \ + thread/qwaitcondition_symbian.cpp win32:SOURCES += thread/qmutex_win.cpp \ thread/qthread_win.cpp \ diff --git a/src/gui/kernel/qkeymapper_s60.cpp b/src/gui/kernel/qkeymapper_s60.cpp index bcf32a5..08cfae0 100644 --- a/src/gui/kernel/qkeymapper_s60.cpp +++ b/src/gui/kernel/qkeymapper_s60.cpp @@ -69,8 +69,17 @@ void QKeyMapperPrivate::clearMappings() QString QKeyMapperPrivate::translateKeyEvent(int keySym, Qt::KeyboardModifiers /* modifiers */) { - if (keySym >= Qt::Key_Escape) - return QString(); + if (keySym >= Qt::Key_Escape) { + switch (keySym) { + case Qt::Key_Tab: + return QString(QChar('\t')); + case Qt::Key_Return: // fall through + case Qt::Key_Enter: + return QString(QChar('\r')); + default: + return QString(); + } + } // Symbian doesn't actually use modifiers, but gives us the character code directly. diff --git a/src/gui/widgets/qcombobox.cpp b/src/gui/widgets/qcombobox.cpp index 7b4ef50..5f00afa 100644 --- a/src/gui/widgets/qcombobox.cpp +++ b/src/gui/widgets/qcombobox.cpp @@ -2363,7 +2363,12 @@ void QComboBox::showPopup() initStyleOption(&opt); QRect listRect(style->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxListBoxPopup, this)); +#ifndef Q_WS_S60 QRect screen = d->popupGeometry(QApplication::desktop()->screenNumber(this)); +#else + QRect screen = qt_TRect2QRect(static_cast<CEikAppUi*>(S60->appUi())->ClientRect()); +#endif + QPoint below = mapToGlobal(listRect.bottomLeft()); int belowHeight = screen.bottom() - below.y(); QPoint above = mapToGlobal(listRect.topLeft()); @@ -2486,18 +2491,10 @@ void QComboBox::showPopup() listRect.setWidth(listRect.height()); //by default popup is centered on screen in landscape listRect.moveCenter(screen.center()); - if (staConTopRect.IsEmpty()) { - TRect cbaRect = TRect(); - AknLayoutUtils::LayoutMetricsRect(AknLayoutUtils::EControlPane, cbaRect); - AknLayoutUtils::TAknCbaLocation cbaLocation = AknLayoutUtils::CbaLocation(); - switch (cbaLocation) { - case AknLayoutUtils::EAknCbaLocationRight: - listRect.setRight(screen.right()); - break; - case AknLayoutUtils::EAknCbaLocationLeft: - listRect.setLeft(screen.left()); - break; - } + if (staConTopRect.IsEmpty() && AknLayoutUtils::CbaLocation() != AknLayoutUtils::EAknCbaLocationBottom) { + // landscape without stacon, menu should be at the right + (opt.direction == Qt::LeftToRight) ? listRect.setRight(screen.right()) : + listRect.setLeft(screen.left()); } } #endif @@ -2716,7 +2713,7 @@ void QComboBox::changeEvent(QEvent *e) initStyleOption(&opt); if (style()->styleHint(QStyle::SH_ComboBox_Popup, &opt, this)) { - const QRect screen = d->popupGeometry(QApplication::desktop()->screenNumber(this)); + QRect screen = qt_TRect2QRect(static_cast<CEikAppUi*>(S60->appUi())->ClientRect()); QRect listRect(style()->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxListBoxPopup, this)); @@ -2731,13 +2728,14 @@ void QComboBox::changeEvent(QEvent *e) listRect.setWidth(listRect.height()); //by default popup is centered on screen in landscape listRect.moveCenter(screen.center()); - if (staConTopRect.IsEmpty()) { + if (staConTopRect.IsEmpty() && AknLayoutUtils::CbaLocation() != AknLayoutUtils::EAknCbaLocationBottom) { // landscape without stacon, menu should be at the right (opt.direction == Qt::LeftToRight) ? listRect.setRight(screen.right()) : listRect.setLeft(screen.left()); } - d->container->setGeometry(listRect); } + + d->container->setGeometry(listRect); } } #endif @@ -2770,6 +2768,10 @@ void QComboBox::changeEvent(QEvent *e) void QComboBox::resizeEvent(QResizeEvent *) { Q_D(QComboBox); +#ifdef Q_WS_S60 + if (d->viewContainer() && d->viewContainer()->isVisible()) + showPopup(); +#endif d->updateLineEditGeometry(); } diff --git a/src/plugins/s60/3_2/3_2.pro b/src/plugins/s60/3_2/3_2.pro index 0524866..b104c05 100644 --- a/src/plugins/s60/3_2/3_2.pro +++ b/src/plugins/s60/3_2/3_2.pro @@ -16,6 +16,7 @@ contains(S60_VERSION, 3.1) { LIBS += -lDirectoryLocalizer } LIBS += -lefsrv + LIBS += -lnumberconversion INCLUDEPATH += $$APP_LAYER_SYSTEMINCLUDE } diff --git a/src/plugins/s60/5_0/5_0.pro b/src/plugins/s60/5_0/5_0.pro index 00aea1b..b037215 100644 --- a/src/plugins/s60/5_0/5_0.pro +++ b/src/plugins/s60/5_0/5_0.pro @@ -16,6 +16,7 @@ contains(S60_VERSION, 3.1) { LIBS += -lDirectoryLocalizer } LIBS += -lefsrv + LIBS += -lnumberconversion INCLUDEPATH += $$APP_LAYER_SYSTEMINCLUDE } diff --git a/src/plugins/s60/src/qlocale_3_2.cpp b/src/plugins/s60/src/qlocale_3_2.cpp index 8c0edd2..ecbf46c 100644 --- a/src/plugins/s60/src/qlocale_3_2.cpp +++ b/src/plugins/s60/src/qlocale_3_2.cpp @@ -42,6 +42,7 @@ #include <exception> #include <e32std.h> #include <e32base.h> +#include <numberconversion.h> EXPORT_C TPtrC defaultGetLongDateFormatSpec(TExtendedLocale& locale) { @@ -61,4 +62,5 @@ EXPORT_C TPtrC defaultGetTimeFormatSpec(TExtendedLocale& locale) EXPORT_C void defaultFormatL(TTime& time, TDes& des, const TDesC& format, const TLocale& locale) { time.FormatL(des, format, locale); + NumberConversion::ConvertDigits(des, locale.DigitType()); } diff --git a/tests/auto/qftp/tst_qftp.cpp b/tests/auto/qftp/tst_qftp.cpp index 1b4b503..64d49d8 100644 --- a/tests/auto/qftp/tst_qftp.cpp +++ b/tests/auto/qftp/tst_qftp.cpp @@ -262,6 +262,10 @@ void tst_QFtp::init() void tst_QFtp::cleanup() { + if (ftp) { + delete ftp; + ftp = 0; + } QFETCH_GLOBAL(bool, setProxy); if (setProxy) { QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy); @@ -289,6 +293,7 @@ void tst_QFtp::connectToHost() QTestEventLoop::instance().enterLoop( 61 ); delete ftp; + ftp = 0; if ( QTestEventLoop::instance().timeout() ) QFAIL( "Network operation timed out" ); @@ -337,6 +342,7 @@ void tst_QFtp::connectToUnresponsiveHost() QVERIFY( it.value().success == 0 ); delete ftp; + ftp = 0; } void tst_QFtp::login_data() @@ -369,6 +375,7 @@ void tst_QFtp::login() QTestEventLoop::instance().enterLoop( 30 ); delete ftp; + ftp = 0; if ( QTestEventLoop::instance().timeout() ) QFAIL( "Network operation timed out" ); @@ -415,6 +422,7 @@ void tst_QFtp::close() QTestEventLoop::instance().enterLoop( 30 ); delete ftp; + ftp = 0; if ( QTestEventLoop::instance().timeout() ) QFAIL( "Network operation timed out" ); @@ -482,6 +490,7 @@ void tst_QFtp::list() QTestEventLoop::instance().enterLoop( 30 ); delete ftp; + ftp = 0; if ( QTestEventLoop::instance().timeout() ) QFAIL( "Network operation timed out" ); @@ -542,6 +551,7 @@ void tst_QFtp::cd() QTestEventLoop::instance().enterLoop( 30 ); delete ftp; + ftp = 0; if ( QTestEventLoop::instance().timeout() ) { QFAIL( "Network operation timed out" ); } @@ -617,6 +627,7 @@ void tst_QFtp::get() QTestEventLoop::instance().enterLoop( 50 ); delete ftp; + ftp = 0; if ( QTestEventLoop::instance().timeout() ) QFAIL( "Network operation timed out" ); @@ -743,6 +754,7 @@ void tst_QFtp::put() break; } delete ftp; + ftp = 0; if ( QTestEventLoop::instance().timeout() ) QFAIL( "Network operation timed out" ); @@ -775,6 +787,7 @@ void tst_QFtp::put() break; } delete ftp; + ftp = 0; if ( QTestEventLoop::instance().timeout() ) QFAIL( "Network operation timed out" ); @@ -792,6 +805,7 @@ void tst_QFtp::put() QTestEventLoop::instance().enterLoop( timestep ); delete ftp; + ftp = 0; if ( QTestEventLoop::instance().timeout() ) QFAIL( "Network operation timed out" ); @@ -860,6 +874,7 @@ void tst_QFtp::mkdir() QTestEventLoop::instance().enterLoop( 30 ); delete ftp; + ftp = 0; if ( QTestEventLoop::instance().timeout() ) QFAIL( "Network operation timed out" ); @@ -884,6 +899,7 @@ void tst_QFtp::mkdir() QTestEventLoop::instance().enterLoop( 30 ); delete ftp; + ftp = 0; if ( QTestEventLoop::instance().timeout() ) QFAIL( "Network operation timed out" ); @@ -903,6 +919,7 @@ void tst_QFtp::mkdir() QTestEventLoop::instance().enterLoop( 30 ); delete ftp; + ftp = 0; if ( QTestEventLoop::instance().timeout() ) QFAIL( "Network operation timed out" ); @@ -942,6 +959,7 @@ void tst_QFtp::mkdir2() QVERIFY(commandFinishedSpy.at(3).at(1).toBool()); delete ftp; + ftp = 0; } void tst_QFtp::mkdir2Slot(int id, bool) @@ -1019,6 +1037,7 @@ void tst_QFtp::renameInit( const QString &host, const QString &user, const QStri QTestEventLoop::instance().enterLoop( 50 ); delete ftp; + ftp = 0; if ( QTestEventLoop::instance().timeout() ) QFAIL( "Network operation timed out" ); @@ -1043,6 +1062,7 @@ void tst_QFtp::renameCleanup( const QString &host, const QString &user, const QS QTestEventLoop::instance().enterLoop( 30 ); delete ftp; + ftp = 0; if ( QTestEventLoop::instance().timeout() ) QFAIL( "Network operation timed out" ); @@ -1087,6 +1107,7 @@ void tst_QFtp::rename() QTestEventLoop::instance().enterLoop( 30 ); delete ftp; + ftp = 0; if ( QTestEventLoop::instance().timeout() ) QFAIL( "Network operation timed out" ); @@ -1273,6 +1294,7 @@ void tst_QFtp::commandSequence() QTestEventLoop::instance().enterLoop( 30 ); delete ftp; + ftp = 0; if ( QTestEventLoop::instance().timeout() ) QFAIL( "Network operation timed out" ); @@ -1330,6 +1352,7 @@ void tst_QFtp::abort() break; } delete ftp; + ftp = 0; if ( QTestEventLoop::instance().timeout() ) QFAIL( "Network operation timed out" ); @@ -1367,6 +1390,7 @@ void tst_QFtp::abort() QTestEventLoop::instance().enterLoop( 30 ); delete ftp; + ftp = 0; if ( QTestEventLoop::instance().timeout() ) QFAIL( "Network operation timed out" ); @@ -1425,6 +1449,7 @@ void tst_QFtp::bytesAvailable() ftp->readAll(); QVERIFY( ftp->bytesAvailable() == 0 ); delete ftp; + ftp = 0; } void tst_QFtp::activeMode() @@ -1497,6 +1522,7 @@ void tst_QFtp::proxy() QTestEventLoop::instance().enterLoop( 50 ); delete ftp; + ftp = 0; if ( QTestEventLoop::instance().timeout() ) { QFAIL( "Network operation timed out" ); } @@ -1531,6 +1557,8 @@ void tst_QFtp::binaryAscii() addCommand(QFtp::Close, ftp->close()); QTestEventLoop::instance().enterLoop( 30 ); + delete ftp; + ftp = 0; if ( QTestEventLoop::instance().timeout() ) QFAIL( "Network operation timed out" ); @@ -1551,6 +1579,8 @@ void tst_QFtp::binaryAscii() addCommand(QFtp::Close, ftp->close()); QTestEventLoop::instance().enterLoop( 30 ); + delete ftp; + ftp = 0; if ( QTestEventLoop::instance().timeout() ) QFAIL( "Network operation timed out" ); @@ -1573,6 +1603,7 @@ void tst_QFtp::binaryAscii() QTestEventLoop::instance().enterLoop( 30 ); delete ftp; + ftp = 0; if ( QTestEventLoop::instance().timeout() ) QFAIL( "Network operation timed out" ); @@ -1920,6 +1951,7 @@ bool tst_QFtp::fileExists( const QString &host, quint16 port, const QString &use inFileDirExistsFunction = TRUE; QTestEventLoop::instance().enterLoop( 30 ); delete ftp; + ftp = 0; if ( QTestEventLoop::instance().timeout() ) { // ### make this test work qWarning("tst_QFtp::fileExists: Network operation timed out"); @@ -1970,6 +2002,7 @@ bool tst_QFtp::dirExists( const QString &host, quint16 port, const QString &user inFileDirExistsFunction = TRUE; QTestEventLoop::instance().enterLoop( 30 ); delete ftp; + ftp = 0; if ( QTestEventLoop::instance().timeout() ) { // ### make this test work // QFAIL( "Network operation timed out" ); @@ -1996,8 +2029,11 @@ void tst_QFtp::doneSignal() ftp.close(); done_success = 0; - while ( ftp.hasPendingCommands() ) - QCoreApplication::instance()->processEvents(); + connect(&ftp, SIGNAL(done(bool)), &(QTestEventLoop::instance()), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(61); + if (QTestEventLoop::instance().timeout()) + QFAIL("Network operation timed out"); + QTest::qWait(200); QCOMPARE(spy.count(), 1); diff --git a/tests/auto/qmutex/tst_qmutex.cpp b/tests/auto/qmutex/tst_qmutex.cpp index ea983cb..a8c4b37 100644 --- a/tests/auto/qmutex/tst_qmutex.cpp +++ b/tests/auto/qmutex/tst_qmutex.cpp @@ -129,27 +129,57 @@ void tst_QMutex::tryLock() testsTurn.release(); threadsTurn.acquire(); + QVERIFY(!normalMutex.tryLock(0)); + testsTurn.release(); + + threadsTurn.acquire(); + timer.start(); + QVERIFY(normalMutex.tryLock(0)); + QVERIFY(timer.elapsed() < 1000); + QVERIFY(lockCount.testAndSetRelaxed(0, 1)); + QVERIFY(!normalMutex.tryLock(0)); + QVERIFY(lockCount.testAndSetRelaxed(1, 0)); + normalMutex.unlock(); + testsTurn.release(); + + threadsTurn.acquire(); } }; Thread thread; thread.start(); + // thread can't acquire lock + testsTurn.acquire(); + normalMutex.lock(); + QVERIFY(lockCount.testAndSetRelaxed(0, 1)); + threadsTurn.release(); + + // thread can acquire lock + testsTurn.acquire(); + QVERIFY(lockCount.testAndSetRelaxed(1, 0)); + normalMutex.unlock(); + threadsTurn.release(); + + // thread can't acquire lock, timeout = 1000 testsTurn.acquire(); normalMutex.lock(); QVERIFY(lockCount.testAndSetRelaxed(0, 1)); threadsTurn.release(); + // thread can acquire lock, timeout = 1000 testsTurn.acquire(); QVERIFY(lockCount.testAndSetRelaxed(1, 0)); normalMutex.unlock(); threadsTurn.release(); + // thread can't acquire lock, timeout = 0 testsTurn.acquire(); normalMutex.lock(); QVERIFY(lockCount.testAndSetRelaxed(0, 1)); threadsTurn.release(); + // thread can acquire lock, timeout = 0 testsTurn.acquire(); QVERIFY(lockCount.testAndSetRelaxed(1, 0)); normalMutex.unlock(); @@ -190,6 +220,7 @@ void tst_QMutex::tryLock() timer.start(); QVERIFY(!recursiveMutex.tryLock(1000)); QVERIFY(timer.elapsed() >= 1000); + QVERIFY(!recursiveMutex.tryLock(0)); testsTurn.release(); threadsTurn.acquire(); @@ -206,12 +237,47 @@ void tst_QMutex::tryLock() testsTurn.release(); threadsTurn.acquire(); + QVERIFY(!recursiveMutex.tryLock(0)); + QVERIFY(!recursiveMutex.tryLock(0)); + testsTurn.release(); + + threadsTurn.acquire(); + timer.start(); + QVERIFY(recursiveMutex.tryLock(0)); + QVERIFY(timer.elapsed() < 1000); + QVERIFY(lockCount.testAndSetRelaxed(0, 1)); + QVERIFY(recursiveMutex.tryLock(0)); + QVERIFY(lockCount.testAndSetRelaxed(1, 2)); + QVERIFY(lockCount.testAndSetRelaxed(2, 1)); + recursiveMutex.unlock(); + QVERIFY(lockCount.testAndSetRelaxed(1, 0)); + recursiveMutex.unlock(); + testsTurn.release(); + + threadsTurn.acquire(); } }; Thread thread; thread.start(); + // thread can't acquire lock + testsTurn.acquire(); + recursiveMutex.lock(); + QVERIFY(lockCount.testAndSetRelaxed(0, 1)); + recursiveMutex.lock(); + QVERIFY(lockCount.testAndSetRelaxed(1, 2)); + threadsTurn.release(); + + // thread can acquire lock + testsTurn.acquire(); + QVERIFY(lockCount.testAndSetRelaxed(2, 1)); + recursiveMutex.unlock(); + QVERIFY(lockCount.testAndSetRelaxed(1, 0)); + recursiveMutex.unlock(); + threadsTurn.release(); + + // thread can't acquire lock, timeout = 1000 testsTurn.acquire(); recursiveMutex.lock(); QVERIFY(lockCount.testAndSetRelaxed(0, 1)); @@ -219,6 +285,7 @@ void tst_QMutex::tryLock() QVERIFY(lockCount.testAndSetRelaxed(1, 2)); threadsTurn.release(); + // thread can acquire lock, timeout = 1000 testsTurn.acquire(); QVERIFY(lockCount.testAndSetRelaxed(2, 1)); recursiveMutex.unlock(); @@ -226,6 +293,7 @@ void tst_QMutex::tryLock() recursiveMutex.unlock(); threadsTurn.release(); + // thread can't acquire lock, timeout = 0 testsTurn.acquire(); recursiveMutex.lock(); QVERIFY(lockCount.testAndSetRelaxed(0, 1)); @@ -233,6 +301,7 @@ void tst_QMutex::tryLock() QVERIFY(lockCount.testAndSetRelaxed(1, 2)); threadsTurn.release(); + // thread can acquire lock, timeout = 0 testsTurn.acquire(); QVERIFY(lockCount.testAndSetRelaxed(2, 1)); recursiveMutex.unlock(); diff --git a/tests/auto/qsemaphore/tst_qsemaphore.cpp b/tests/auto/qsemaphore/tst_qsemaphore.cpp index ba2175a..9fc6de1 100644 --- a/tests/auto/qsemaphore/tst_qsemaphore.cpp +++ b/tests/auto/qsemaphore/tst_qsemaphore.cpp @@ -244,6 +244,8 @@ void tst_QSemaphore::tryAcquireWithTimeout() QSemaphore semaphore; QTime time; +#define QVERIFYGE(a,b) {int e = a; if (a<b) qDebug() << #a << "=" << e << " !>= " << #b << "=" << b; QVERIFY(e>=b);} +#define QVERIFYLE(a,b) {int e = a; if (b<a) qDebug() << #a << "=" << e << " !<= " << #b << "=" << b; QVERIFY(e<=b);} QCOMPARE(semaphore.available(), 0); @@ -251,69 +253,69 @@ void tst_QSemaphore::tryAcquireWithTimeout() QCOMPARE(semaphore.available(), 1); time.start(); QVERIFY(!semaphore.tryAcquire(2, timeout)); - QVERIFY(time.elapsed() >= timeout); + QVERIFYGE(time.elapsed(), timeout); QCOMPARE(semaphore.available(), 1); semaphore.release(); QCOMPARE(semaphore.available(), 2); time.start(); QVERIFY(!semaphore.tryAcquire(3, timeout)); - QVERIFY(time.elapsed() >= timeout); + QVERIFYGE(time.elapsed(), timeout); QCOMPARE(semaphore.available(), 2); semaphore.release(10); QCOMPARE(semaphore.available(), 12); time.start(); QVERIFY(!semaphore.tryAcquire(100, timeout)); - QVERIFY(time.elapsed() >= timeout); + QVERIFYGE(time.elapsed(), timeout); QCOMPARE(semaphore.available(), 12); semaphore.release(10); QCOMPARE(semaphore.available(), 22); time.start(); QVERIFY(!semaphore.tryAcquire(100, timeout)); - QVERIFY(time.elapsed() >= timeout); + QVERIFYGE(time.elapsed(), timeout); QCOMPARE(semaphore.available(), 22); time.start(); QVERIFY(semaphore.tryAcquire(1, timeout)); - QVERIFY(time.elapsed() <= timeout); + QVERIFYLE(time.elapsed(), timeout); QCOMPARE(semaphore.available(), 21); time.start(); QVERIFY(semaphore.tryAcquire(1, timeout)); - QVERIFY(time.elapsed() <= timeout); + QVERIFYLE(time.elapsed(), timeout); QCOMPARE(semaphore.available(), 20); time.start(); QVERIFY(semaphore.tryAcquire(10, timeout)); - QVERIFY(time.elapsed() <= timeout); + QVERIFYLE(time.elapsed(), timeout); QCOMPARE(semaphore.available(), 10); time.start(); QVERIFY(semaphore.tryAcquire(10, timeout)); - QVERIFY(time.elapsed() <= timeout); + QVERIFYLE(time.elapsed(), timeout); QCOMPARE(semaphore.available(), 0); // should not be able to acquire more time.start(); QVERIFY(!semaphore.tryAcquire(1, timeout)); - QVERIFY(time.elapsed() >= timeout); + QVERIFYGE(time.elapsed(), timeout); QCOMPARE(semaphore.available(), 0); time.start(); QVERIFY(!semaphore.tryAcquire(1, timeout)); - QVERIFY(time.elapsed() >= timeout); + QVERIFYGE(time.elapsed(), timeout); QCOMPARE(semaphore.available(), 0); time.start(); QVERIFY(!semaphore.tryAcquire(10, timeout)); - QVERIFY(time.elapsed() >= timeout); + QVERIFYGE(time.elapsed(), timeout); QCOMPARE(semaphore.available(), 0); time.start(); QVERIFY(!semaphore.tryAcquire(10, timeout)); - QVERIFY(time.elapsed() >= timeout); + QVERIFYGE(time.elapsed(), timeout); QCOMPARE(semaphore.available(), 0); } diff --git a/tests/auto/qthread/tst_qthread.cpp b/tests/auto/qthread/tst_qthread.cpp index c69052e..7386d9d 100644 --- a/tests/auto/qthread/tst_qthread.cpp +++ b/tests/auto/qthread/tst_qthread.cpp @@ -104,6 +104,7 @@ private slots: void adoptedThreadExec(); void adoptedThreadFinished(); void adoptMultipleThreads(); + void adoptMultipleThreadsOverlap(); void QTBUG13810_exitAndStart(); void QTBUG15378_exitAndExec(); @@ -663,7 +664,9 @@ void tst_QThread::usleep() typedef void (*FunctionPointer)(void *); void noop(void*) { } -#ifdef Q_OS_UNIX +#ifdef Q_OS_SYMBIAN +typedef RThread ThreadHandle; +#elif defined Q_OS_UNIX typedef pthread_t ThreadHandle; #elif defined Q_OS_WIN typedef HANDLE ThreadHandle; @@ -694,6 +697,7 @@ public: protected: static void *runUnix(void *data); static unsigned WIN_FIX_STDCALL runWin(void *data); + static int runSymbian(void *data); FunctionPointer functionPointer; void *data; @@ -703,7 +707,10 @@ void NativeThreadWrapper::start(FunctionPointer functionPointer, void *data) { this->functionPointer = functionPointer; this->data = data; -#ifdef Q_OS_UNIX +#ifdef Q_OS_SYMBIAN + qt_symbian_throwIfError(nativeThreadHandle.Create(KNullDesC(), NativeThreadWrapper::runSymbian, 1024, &User::Allocator(), this)); + nativeThreadHandle.Resume(); +#elif defined Q_OS_UNIX const int state = pthread_create(&nativeThreadHandle, 0, NativeThreadWrapper::runUnix, this); Q_UNUSED(state); #elif defined(Q_OS_WINCE) @@ -723,7 +730,12 @@ void NativeThreadWrapper::startAndWait(FunctionPointer functionPointer, void *da void NativeThreadWrapper::join() { -#ifdef Q_OS_UNIX +#ifdef Q_OS_SYMBIAN + TRequestStatus stat; + nativeThreadHandle.Logon(stat); + User::WaitForRequest(stat); + nativeThreadHandle.Close(); +#elif defined Q_OS_UNIX pthread_join(nativeThreadHandle, 0); #elif defined Q_OS_WIN WaitForSingleObject(nativeThreadHandle, INFINITE); @@ -763,6 +775,12 @@ unsigned WIN_FIX_STDCALL NativeThreadWrapper::runWin(void *data) return 0; } +int NativeThreadWrapper::runSymbian(void *data) +{ + runUnix(data); + return 0; +} + void NativeThreadWrapper::stop() { QMutexLocker lock(&mutex); @@ -924,6 +942,7 @@ void tst_QThread::adoptMultipleThreads() for (int i = 0; i < numThreads; ++i) { nativeThreads.at(i)->stop(); nativeThreads.at(i)->join(); + delete nativeThreads.at(i); } QTestEventLoop::instance().enterLoop(5); @@ -931,6 +950,50 @@ void tst_QThread::adoptMultipleThreads() QCOMPARE(int(recorder.activationCount), numThreads); } +void tst_QThread::adoptMultipleThreadsOverlap() +{ +#if defined(Q_OS_WIN) + // Windows CE is not capable of handling that many threads. On the emulator it is dead with 26 threads already. +# if defined(Q_OS_WINCE) + const int numThreads = 20; +# else + // need to test lots of threads, so that we exceed MAXIMUM_WAIT_OBJECTS in qt_adopted_thread_watcher() + const int numThreads = 200; +# endif +#elif defined(Q_OS_SYMBIAN) + // stress the monitoring thread's add function + const int numThreads = 100; +#else + const int numThreads = 5; +#endif + QVector<NativeThreadWrapper*> nativeThreads; + + SignalRecorder recorder; + + for (int i = 0; i < numThreads; ++i) { + nativeThreads.append(new NativeThreadWrapper()); + nativeThreads.at(i)->setWaitForStop(); + nativeThreads.at(i)->mutex.lock(); + nativeThreads.at(i)->start(); + } + for (int i = 0; i < numThreads; ++i) { + nativeThreads.at(i)->startCondition.wait(&nativeThreads.at(i)->mutex); + QObject::connect(nativeThreads.at(i)->qthread, SIGNAL(finished()), &recorder, SLOT(slot())); + nativeThreads.at(i)->mutex.unlock(); + } + + QObject::connect(nativeThreads.at(numThreads - 1)->qthread, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + + for (int i = 0; i < numThreads; ++i) { + nativeThreads.at(i)->stop(); + nativeThreads.at(i)->join(); + delete nativeThreads.at(i); + } + + QTestEventLoop::instance().enterLoop(5); + QVERIFY(!QTestEventLoop::instance().timeout()); + QCOMPARE(int(recorder.activationCount), numThreads); +} void tst_QThread::stressTest() { #if defined(Q_OS_WINCE) diff --git a/tests/auto/qthreadstorage/tst_qthreadstorage.cpp b/tests/auto/qthreadstorage/tst_qthreadstorage.cpp index a62bfee..2391c81 100644 --- a/tests/auto/qthreadstorage/tst_qthreadstorage.cpp +++ b/tests/auto/qthreadstorage/tst_qthreadstorage.cpp @@ -385,19 +385,18 @@ void tst_QThreadStorage::QTBUG14579_leakInDestructor() QCOMPARE(int(SPointer::count), c); } - -class QTBUG14579_reset; -Q_GLOBAL_STATIC(QThreadStorage<QTBUG14579_reset *>, QTBUG14579_resetTls) - class QTBUG14579_reset { public: SPointer member; - ~QTBUG14579_reset() { - //Quite stupid, but WTF::ThreadSpecific<T>::destroy does it. - QTBUG14579_resetTls()->setLocalData(this); - } + ~QTBUG14579_reset(); }; +Q_GLOBAL_STATIC(QThreadStorage<QTBUG14579_reset *>, QTBUG14579_resetTls) + +QTBUG14579_reset::~QTBUG14579_reset() { + //Quite stupid, but WTF::ThreadSpecific<T>::destroy does it. + QTBUG14579_resetTls()->setLocalData(this); +} void tst_QThreadStorage::QTBUG14579_resetInDestructor() { diff --git a/tests/benchmarks/corelib/thread/qmutex/tst_qmutex.cpp b/tests/benchmarks/corelib/thread/qmutex/tst_qmutex.cpp index aa4c15a..8b38661 100644 --- a/tests/benchmarks/corelib/thread/qmutex/tst_qmutex.cpp +++ b/tests/benchmarks/corelib/thread/qmutex/tst_qmutex.cpp @@ -44,7 +44,26 @@ #include <math.h> -#ifdef Q_OS_UNIX +#ifdef Q_OS_SYMBIAN +# include <e32std.h> +typedef RMutex NativeMutexType; +void NativeMutexInitialize(NativeMutexType *mutex) +{ + mutex->CreateLocal(); +} +void NativeMutexDestroy(NativeMutexType *mutex) +{ + mutex->Close(); +} +void NativeMutexLock(NativeMutexType *mutex) +{ + mutex->Wait(); +} +void NativeMutexUnlock(NativeMutexType *mutex) +{ + mutex->Signal(); +} +#elif defined(Q_OS_UNIX) # include <pthread.h> # include <errno.h> typedef pthread_mutex_t NativeMutexType; diff --git a/tests/benchmarks/corelib/thread/qwaitcondition/qwaitcondition.pro b/tests/benchmarks/corelib/thread/qwaitcondition/qwaitcondition.pro new file mode 100644 index 0000000..bc7bd58 --- /dev/null +++ b/tests/benchmarks/corelib/thread/qwaitcondition/qwaitcondition.pro @@ -0,0 +1,5 @@ +load(qttest_p4) +TEMPLATE = app +TARGET = tst_bench_qwaitcondition +QT -= gui +SOURCES += tst_qwaitcondition.cpp diff --git a/tests/benchmarks/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp b/tests/benchmarks/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp new file mode 100644 index 0000000..1bfc637 --- /dev/null +++ b/tests/benchmarks/corelib/thread/qwaitcondition/tst_qwaitcondition.cpp @@ -0,0 +1,210 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (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 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$ +** +****************************************************************************/ + +#include <QtCore/QtCore> +#include <QtTest/QtTest> + +#include <math.h> + + +class tst_QWaitCondition : public QObject +{ + Q_OBJECT + +public: + tst_QWaitCondition() + { + } + +private slots: + void oscillate_data(); + void oscillate(); + + void thrash_data(); + void thrash(); + +public: + static QWaitCondition local, remote; + enum Turn {LocalTurn, RemoteTurn}; + static Turn turn; +}; + +QWaitCondition tst_QWaitCondition::local; +QWaitCondition tst_QWaitCondition::remote; +tst_QWaitCondition::Turn tst_QWaitCondition::turn = tst_QWaitCondition::LocalTurn; + +class OscillateThread : public QThread +{ +public: + bool m_done; + bool m_useMutex; + unsigned long m_timeout; + bool m_wakeOne; + int count; + + OscillateThread(bool useMutex, unsigned long timeout, bool wakeOne) + : m_done(false), m_useMutex(useMutex), m_timeout(timeout), m_wakeOne(wakeOne) + {} + void run() + { + QMutex mtx; + QReadWriteLock rwl; + count = 0; + + forever { + if (m_done) + break; + if (m_useMutex) { + mtx.lock(); + while (tst_QWaitCondition::turn == tst_QWaitCondition::LocalTurn) + tst_QWaitCondition::remote.wait(&mtx, m_timeout); + mtx.unlock(); + } else { + rwl.lockForWrite(); + while (tst_QWaitCondition::turn == tst_QWaitCondition::LocalTurn) + tst_QWaitCondition::remote.wait(&rwl, m_timeout); + rwl.unlock(); + } + tst_QWaitCondition::turn = tst_QWaitCondition::LocalTurn; + if (m_wakeOne) + tst_QWaitCondition::local.wakeOne(); + else + tst_QWaitCondition::local.wakeAll(); + count++; + } + } +}; + +void tst_QWaitCondition::oscillate_data() +{ + QTest::addColumn<bool>("useMutex"); + QTest::addColumn<unsigned long>("timeout"); + QTest::addColumn<bool>("wakeOne"); + + QTest::newRow("mutex, timeout, one") << true << 1000ul << true; + QTest::newRow("readWriteLock, timeout, one") << false << 1000ul << true; + QTest::newRow("mutex, timeout, all") << true << 1000ul << false; + QTest::newRow("readWriteLock, timeout, all") << false << 1000ul << false; + QTest::newRow("mutex, forever, one") << true << ULONG_MAX << true; + QTest::newRow("readWriteLock, forever, one") << false << ULONG_MAX << true; + QTest::newRow("mutex, forever, all") << true << ULONG_MAX << false; + QTest::newRow("readWriteLock, forever, all") << false << ULONG_MAX << false; +} + +void tst_QWaitCondition::oscillate() +{ + QMutex mtx; + QReadWriteLock rwl; + + QFETCH(bool, useMutex); + QFETCH(unsigned long, timeout); + QFETCH(bool, wakeOne); + + turn = LocalTurn; + OscillateThread thrd(useMutex, timeout, wakeOne); + thrd.start(); + + QBENCHMARK { + if (useMutex) + mtx.lock(); + else + rwl.lockForWrite(); + turn = RemoteTurn; + if (wakeOne) + remote.wakeOne(); + else + remote.wakeAll(); + if (useMutex) { + while (turn == RemoteTurn) + local.wait(&mtx, timeout); + mtx.unlock(); + } else { + while (turn == RemoteTurn) + local.wait(&rwl, timeout); + rwl.unlock(); + } + } + + thrd.m_done = true; + remote.wakeAll(); + thrd.wait(); + + QCOMPARE(0, 0); +} + +void tst_QWaitCondition::thrash_data() +{ + oscillate_data(); +} + +void tst_QWaitCondition::thrash() +{ + QMutex mtx; + mtx.lock(); + + QFETCH(bool, useMutex); + QFETCH(unsigned long, timeout); + QFETCH(bool, wakeOne); + + turn = LocalTurn; + OscillateThread thrd(useMutex, timeout, wakeOne); + thrd.start(); + local.wait(&mtx, 1000ul); + mtx.unlock(); + + QBENCHMARK { + turn = RemoteTurn; + if (wakeOne) + remote.wakeOne(); + else + remote.wakeAll(); + } + + thrd.m_done = true; + turn = RemoteTurn; + remote.wakeAll(); + thrd.wait(); + + QCOMPARE(0, 0); +} + +QTEST_MAIN(tst_QWaitCondition) +#include "tst_qwaitcondition.moc" |