Fixed threadpool hang and added lots of smoketests.
* Fixed bug in threadpool logic when calling CleanupAnyWorkersSharingAllocator - this was deleting the worker's thread death notifier in the context of a worker thread (which is only allowed from the context of the main thread).
* Added lots more smoketests
* Added --overwrite option to fzip (previously unzipping would always overwrite)
--- a/commands/focus/focus.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/commands/focus/focus.cif Fri Sep 17 20:24:30 2010 +0100
@@ -44,3 +44,6 @@
Copyright (c) 2008-2010 Accenture. All rights reserved.
+==smoke-test
+
+focus $Quiet
--- a/commands/fzip/fzip.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/commands/fzip/fzip.cif Fri Sep 17 20:24:30 2010 +0100
@@ -20,9 +20,9 @@
Generates compressed archives of files or extracts files from a compressed archive. Both 'zip' and 'gzip' file formats are support ('zip' is used by default).
-==argument filename archive optional
+==argument filename zipfile optional
-The zip archive to create or extract.
+The zip archive to create or extract. If the zipfile argument is not specified when creating a zip, the first C<file> argument's name is used with a .zip or .gz extension appended.
==option bool v verbose
@@ -30,11 +30,11 @@
==option bool u unzip
-Extract files from the specified archive.
+Extract files from the specified zipfile.
==option filename d directory
-The directory to extract files into. Must be used in conjunction with '--unzip'.
+The directory to extract files into. Must be used in conjunction with C<--unzip>. If not specified, the current working directory is used.
==option bool r recurse
@@ -42,7 +42,7 @@
==option filename f file multiple
-A file or directory to archive. Only applicable when creating a new archive. If a directory is specified then it and any files contained immediately within that directory are archived. Use --recurse to archive all sub-directories and files within the directory.
+A file or directory to add to the zipfile. Only applicable when creating a new archive. If a directory is specified then it and any files contained immediately within that directory are archived. Use --recurse to archive all sub-directories and files within the directory.
==option enum t compression-type
@@ -54,7 +54,37 @@
GNU Zip format. Note, this format can only handle a single file.
+==option bool o overwrite
+
+By default fzip will exit with an error if a file it is creating already exists on disk. Use this flag to silently overwrite instead.
+
==copyright
Copyright (c) 2008-2010 Accenture. All rights reserved.
+==smoke-test
+
+export TESTDATA "This is some test data for fzip"
+echo -n "$TESTDATA" > test.txt
+rm test.txt.zip $Silent &| echo -n ""
+
+# Test zip
+fzip --file test.txt
+exists test.txt.zip || $Error
+rm test.txt
+
+# Test unzip
+fzip --unzip test.txt.zip
+exists test.txt || $Error
+cat -b test.txt | export -s RESULT
+var RESULT == "$TESTDATA" || $Error
+
+# Test that we don't overwrite files unless --overwrite is specified
+fzip --file test.txt $Silent &| var ? == "-11" || $Error
+fzip --file test.txt --overwrite
+
+fzip --unzip test.txt.zip $Silent &| var ? == "-11" || $Error
+fzip --unzip test.txt.zip --overwrite
+
+rm test.txt
+rm test.txt.zip
--- a/commands/fzip/fzip.cpp Wed Sep 15 00:45:50 2010 +0100
+++ b/commands/fzip/fzip.cpp Fri Sep 17 20:24:30 2010 +0100
@@ -15,6 +15,7 @@
#include "fzip.h"
_LIT(KGzExtension, ".gz");
+_LIT(KZipExtension, ".zip");
CCommandBase* CCmdZip::NewLC()
{
@@ -26,8 +27,7 @@
CCmdZip::~CCmdZip()
{
- if (iFileToZip.Count() > 0)
- iFileToZip.Close();
+ iFileToZip.Close();
}
CCmdZip::CCmdZip() : CCommandBase(CCommandBase::EManualComplete)
@@ -55,11 +55,11 @@
// command-line sanity checks
if (iFileToZip.Count() > 0)
{
- PrintWarning(_L("Ignoring \'-f\' file option."));
+ PrintWarning(_L("--file option is not relevant when unzipping."));
}
if (iRecurse)
{
- PrintWarning(_L("Ignoring \'-r\' recurse option."));
+ PrintWarning(_L("--recurse option is not relevant when unzipping."));
}
}
ExpandArchiveL();
@@ -71,21 +71,14 @@
// command-line sanity checks
if (iUnzipPath.Length() > 0)
{
- PrintWarning(_L("Ignoring '-d' directory option."));
+ PrintWarning(_L("--directory option is not relevant when zipping."));
}
}
if (iFileToZip.Count() == 0)
{
- PrintError(KErrArgument, _L("Use '-f' to specify source files."));
- User::Leave(KErrArgument);
+ LeaveIfErr(KErrArgument, _L("Specify some files to zip up using --file option."));
}
- TRAPD(err, CreateArchiveL());
- if (err != KErrNone)
- {
- PrintError(err, _L("Couldn't create archive"));
- Fs().Delete(iArchive); // ignore error
- User::Leave(err);
- }
+ TRAPL(CreateArchiveL(), _L("Couldn't create %S"), &iArchive);
}
if (iVerbose)
{
@@ -96,7 +89,7 @@
void CCmdZip::ArgumentsL(RCommandArgumentList& aArguments)
{
- _LIT(KArg1, "archive");
+ _LIT(KArg1, "zipfile");
aArguments.AppendFileNameL(iArchive, KArg1);
}
@@ -119,6 +112,9 @@
_LIT(KOptCompressionType, "compression-type");
aOptions.AppendEnumL((TInt&)iCompressionType, KOptCompressionType);
+
+ _LIT(KOptOverwrite, "overwrite");
+ aOptions.AppendBoolL(iOverwrite, KOptOverwrite);
}
@@ -132,6 +128,24 @@
//
void CCmdZip::CreateArchiveL()
{
+ if (iArchive.Length() == 0)
+ {
+ iArchive = iFileToZip[0];
+ iArchive.Append(iCompressionType == EGZip ? KGzExtension() : KZipExtension());
+ }
+
+ if (iArchive.Exists(FsL()))
+ {
+ if (iOverwrite)
+ {
+ FsL().Delete(iArchive);
+ }
+ else
+ {
+ LeaveIfErr(KErrAlreadyExists, _L("File %S already exists on disk. Use --overwrite or specify a different file"), &iArchive);
+ }
+ }
+
if (iCompressionType == EGZip)
{
CreateGzArchiveL();
@@ -158,23 +172,17 @@
LeaveIfErr(KErrArgument, _L("GNU Zip format can only handle a single file"));
}
- if (iArchive.Length() == 0)
- {
- iArchive = iFileToZip[0];
- iArchive.Append(KGzExtension);
- }
-
- RFile input;
if (iVerbose)
{
Printf(_L("Creating '%S'\r\n"), &iArchive);
}
// open the input file
- User::LeaveIfError(input.Open(Fs(), iFileToZip[0], EFileStream | EFileRead | EFileShareAny));
+ RFile input;
+ User::LeaveIfError(input.Open(FsL(), iFileToZip[0], EFileStream | EFileRead | EFileShareAny));
CleanupClosePushL(input);
- CEZFileToGZip* zip = CEZFileToGZip::NewLC(Fs(), iArchive, input);
+ CEZFileToGZip* zip = CEZFileToGZip::NewLC(FsL(), iArchive, input);
while (zip->DeflateL())
{
// do nothing
@@ -297,7 +305,15 @@
{
LeaveIfErr(err, _L("Couldn't create path '%S'"), &dest);
}
- User::LeaveIfError(newFile.Replace(Fs(), dest, EFileStream | EFileRead | EFileShareAny));
+ if (iOverwrite)
+ {
+ err = newFile.Replace(FsL(), dest, EFileStream | EFileRead | EFileWrite | EFileShareAny);
+ }
+ else
+ {
+ err = newFile.Create(FsL(), dest, EFileStream | EFileRead | EFileWrite | EFileShareAny);
+ }
+ LeaveIfErr(err, _L("Couldn't create file %S"), &dest);
CleanupClosePushL(newFile);
// inflate the compressed file
@@ -362,7 +378,15 @@
aZip.GetInputStreamL(&aMember, readStream);
CleanupStack::PushL(readStream);
- LeaveIfErr(newFile.Replace(Fs(), dest, EFileShareExclusive), _L("Couldn't create file %S"), &dest);
+ if (iOverwrite)
+ {
+ err = newFile.Replace(Fs(), dest, EFileShareExclusive);
+ }
+ else
+ {
+ err = newFile.Create(Fs(), dest, EFileShareExclusive);
+ }
+ LeaveIfErr(err, _L("Couldn't create file %S"), &dest);
CleanupClosePushL(newFile);
if (iVerbose)
{
--- a/commands/fzip/fzip.h Wed Sep 15 00:45:50 2010 +0100
+++ b/commands/fzip/fzip.h Fri Sep 17 20:24:30 2010 +0100
@@ -56,6 +56,7 @@
TFileName2 iArchive;
TFileName2 iUnzipPath;
RArray<TFileName2> iFileToZip;
+ TBool iOverwrite;
};
--- a/commands/grabscreen/grabscreen.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/commands/grabscreen/grabscreen.cif Fri Sep 17 20:24:30 2010 +0100
@@ -58,3 +58,6 @@
Copyright (c) 2008-2010 Accenture. All rights reserved.
+==smoke-test
+
+grabscreen > /dev/null
--- a/commands/iap/iap.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/commands/iap/iap.cif Fri Sep 17 20:24:30 2010 +0100
@@ -20,8 +20,10 @@
Add a dummy IAP for use with WinSockPrt (an ESock protocol module that replaces Symbian's TCP/IP stack with a shim over Microsoft's WinSock API).
-
==copyright
Copyright (c) 2008-2010 Accenture. All rights reserved.
+==smoke-test
+
+iap $Quiet
--- a/commands/listapps/listapps.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/commands/listapps/listapps.cif Fri Sep 17 20:24:30 2010 +0100
@@ -36,9 +36,10 @@
Display the details of the application with the specified window group identifier. If not specified, details of all currently running applications are displayed.
-
-
==copyright
Copyright (c) 2007-2010 Accenture. All rights reserved.
+==smoke-test
+
+listapps $Quiet
--- a/commands/localdrive/localdrive.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/commands/localdrive/localdrive.cif Fri Sep 17 20:24:30 2010 +0100
@@ -50,3 +50,6 @@
Copyright (c) 2010 Accenture. All rights reserved.
+==smoke-test
+
+localdrive $Silent # Localdrive can print errors for drives that are ejected, etc
--- a/commands/localdrive/localdrive.cpp Wed Sep 15 00:45:50 2010 +0100
+++ b/commands/localdrive/localdrive.cpp Fri Sep 17 20:24:30 2010 +0100
@@ -226,7 +226,12 @@
if (err == KErrNone)
{
TPckg<TLocalDriveCapsV2> capsBuf(iCaps);
- LeaveIfErr(iDrive.Caps(capsBuf), _L("Opened drive %d but couldn't read caps"), aDrive);
+ err = iDrive.Caps(capsBuf);
+ if (err)
+ {
+ iDrive.Close();
+ if (aLeaveOnConnectErr) LeaveIfErr(err, _L("Opened drive %d but couldn't read caps"), aDrive);
+ }
}
else if (aLeaveOnConnectErr)
{
--- a/commands/mrouter/mrouter.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/commands/mrouter/mrouter.cif Fri Sep 17 20:24:30 2010 +0100
@@ -20,3 +20,7 @@
Copyright (c) 2007-2010 Accenture. All rights reserved.
+==smoke-test
+
+# Do any platforms still support this command?
+mrouter
--- a/commands/screenmode/screenmode.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/commands/screenmode/screenmode.cif Fri Sep 17 20:24:30 2010 +0100
@@ -48,3 +48,6 @@
Copyright (c) 2009-2010 Accenture. All rights reserved.
+==smoke-test
+
+screenmode list $Quiet
--- a/commands/swi/swi.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/commands/swi/swi.cif Fri Sep 17 20:24:30 2010 +0100
@@ -56,3 +56,6 @@
Copyright (c) 2008-2010 Accenture. All rights reserved.
+==smoke-test
+
+swi list $Quiet
--- a/commands/usb/usb.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/commands/usb/usb.cif Fri Sep 17 20:24:30 2010 +0100
@@ -24,3 +24,6 @@
Copyright (c) 2010 Accenture. All rights reserved.
+==smoke-test
+
+usb $Quiet
--- a/commands/variant/variant.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/commands/variant/variant.cif Fri Sep 17 20:24:30 2010 +0100
@@ -50,3 +50,11 @@
Copyright (c) 2008-2010 Accenture. All rights reserved.
+==smoke-test
+
+variant $Quiet
+
+# One of these has to be true
+variant wins || variant target || $Error
+# But they can't both be
+variant wins && variant target && $Error
--- a/commands/wslog/wslog.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/commands/wslog/wslog.cif Fri Sep 17 20:24:30 2010 +0100
@@ -42,3 +42,6 @@
Copyright (c) 2009-2010 Accenture. All rights reserved.
+==smoke-test
+
+wslog status $Quiet
--- a/core/builtins/chunkinfo.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/core/builtins/chunkinfo.cif Fri Sep 17 20:24:30 2010 +0100
@@ -48,3 +48,9 @@
Copyright (c) 2006-2010 Accenture. All rights reserved.
+==smoke-test
+
+# This should fail with KErrNotFound, but shouldn't crash
+chunkinfo 1 $Silent &| var ? == "-1" || $Error
+
+chunkinfo $Quiet
--- a/core/builtins/ciftest.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/core/builtins/ciftest.cif Fri Sep 17 20:24:30 2010 +0100
@@ -92,3 +92,7 @@
==copyright
Copyright (c) 2010 Accenture. All rights reserved.
+
+==smoke-test
+
+# Ciftest itself doesn't have a smoketest!
--- a/core/builtins/ciftest.cpp Wed Sep 15 00:45:50 2010 +0100
+++ b/core/builtins/ciftest.cpp Fri Sep 17 20:24:30 2010 +0100
@@ -89,7 +89,9 @@
{
if (iVerbose)
{
- Printf(_L("%d tests run, %d passes %d failures. %d commands have no tests defined.\r\n"), iPasses + iFailures, iPasses, iFailures, iCifFiles.Count() - iPasses - iFailures);
+ Printf(_L("%d tests run, %d passes %d failures."), iPasses + iFailures, iPasses, iFailures);
+ if (iCifFiles.Count()) Printf(_L(" %d commands have no tests defined."), iCifFiles.Count() - iPasses - iFailures);
+ Printf(_L("\r\n"));
}
Complete(KErrNone);
}
--- a/core/builtins/clear.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/core/builtins/clear.cif Fri Sep 17 20:24:30 2010 +0100
@@ -28,3 +28,6 @@
Copyright (c) 2006-2010 Accenture. All rights reserved.
+==smoke-test
+
+cls --formfeed $Quiet
--- a/core/builtins/compare.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/core/builtins/compare.cif Fri Sep 17 20:24:30 2010 +0100
@@ -36,3 +36,15 @@
Copyright (c) 2006-2010 Accenture. All rights reserved.
+==smoke-test
+
+date --timestamp | export -s TIMESTAMP
+export Temp compare-test-$TIMESTAMP-
+echo "Stuff" > $Temp1
+echo "Stuff" > $Temp2
+echo "Stuff thats different" > $Temp3
+
+compare $Temp1 $Temp2 || $Error # Should be same
+compare $Temp1 $Temp3 && $Error # Should be different
+
+rm $Temp1 $Temp2 $Temp3
--- a/core/builtins/console.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/core/builtins/console.cif Fri Sep 17 20:24:30 2010 +0100
@@ -40,3 +40,6 @@
Copyright (c) 2006-2010 Accenture. All rights reserved.
+==smoke-test
+
+console $Quiet
--- a/core/builtins/date.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/core/builtins/date.cif Fri Sep 17 20:24:30 2010 +0100
@@ -85,3 +85,8 @@
Copyright (c) 2006-2010 Accenture. All rights reserved.
+==smoke-test
+
+date $Quiet
+date --raw $Quiet
+date --timestamp $Quiet
--- a/core/builtins/dialog.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/core/builtins/dialog.cif Fri Sep 17 20:24:30 2010 +0100
@@ -62,3 +62,9 @@
Copyright (c) 2006-2010 Accenture. All rights reserved.
+==smoke-test
+
+# Don't actually post a dialog, the tests need to be unobtrusive (and non-blocking)
+export DIALOG_IMPL null
+dialog "Some question which isn't important"
+var ? == 0 || $Error
--- a/core/builtins/driver.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/core/builtins/driver.cif Fri Sep 17 20:24:30 2010 +0100
@@ -48,3 +48,6 @@
Copyright (c) 2006-2010 Accenture. All rights reserved.
+==smoke-test
+
+driver list logical $Quiet
\ No newline at end of file
--- a/core/builtins/dump.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/core/builtins/dump.cif Fri Sep 17 20:24:30 2010 +0100
@@ -28,3 +28,7 @@
Copyright (c) 2006-2010 Accenture. All rights reserved.
+==smoke-test
+
+echo "123" | dump | export -s RESULT
+var RESULT == "00000000: 31 00 32 00 33 00 0D 00 0A 00 1.2.3.....^r^n" || $Error
--- a/core/builtins/echo.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/core/builtins/echo.cif Fri Sep 17 20:24:30 2010 +0100
@@ -102,3 +102,6 @@
Copyright (c) 2006-2010 Accenture. All rights reserved.
+==smoke-test
+
+# Tested by fshell-basic-test.script, this section is just so ciftest doesn't report it as a command without any tests
--- a/core/builtins/exists.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/core/builtins/exists.cif Fri Sep 17 20:24:30 2010 +0100
@@ -28,3 +28,6 @@
Copyright (c) 2006-2010 Accenture. All rights reserved.
+==smoke-test
+
+# Tested by fshell-basic-test.script, this section is just so ciftest doesn't report it as a command without any tests
--- a/core/builtins/exit.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/core/builtins/exit.cif Fri Sep 17 20:24:30 2010 +0100
@@ -20,8 +20,11 @@
Note, this causes fshell's command history to be persisted to a file.
-
==copyright
Copyright (c) 2006-2010 Accenture. All rights reserved.
+==smoke-test
+
+# This used to cause fshell problems
+fshell -e exit
--- a/core/builtins/match.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/core/builtins/match.cif Fri Sep 17 20:24:30 2010 +0100
@@ -43,3 +43,13 @@
==see-also
L<grep|grep>
+
+==smoke-test
+
+echo "Test line of some sort^r^nIsn't fshell great?^r^nSome other line" | export -s TESTDATA
+echo "$TESTDATA" | match *fshell* | export -s RESULT
+var RESULT == "Isn't fshell great?^r^n" || $Error
+
+# Test anchored search
+echo "$TESTDATA" | match Some* | export -s RESULT
+var RESULT == "Some other line^r^n" || $Error
--- a/core/builtins/ps.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/core/builtins/ps.cif Fri Sep 17 20:24:30 2010 +0100
@@ -104,3 +104,6 @@
Copyright (c) 2005-2010 Accenture. All rights reserved.
+==smoke-test
+
+ps $Quiet
--- a/core/builtins/repeat.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/core/builtins/repeat.cif Fri Sep 17 20:24:30 2010 +0100
@@ -40,3 +40,6 @@
Copyright (c) 2006-2010 Accenture. All rights reserved.
+==smoke-test
+
+# Tested by fshell-last-test.script, this section is just so ciftest doesn't report it as a command without any tests
\ No newline at end of file
--- a/core/builtins/rm.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/core/builtins/rm.cif Fri Sep 17 20:24:30 2010 +0100
@@ -36,3 +36,6 @@
Copyright (c) 2006-2010 Accenture. All rights reserved.
+==smoke-test
+
+# Tested by fshell-basic-test.script, this section is just so ciftest doesn't report it as a command without any tests
--- a/core/builtins/rom.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/core/builtins/rom.cif Fri Sep 17 20:24:30 2010 +0100
@@ -28,3 +28,6 @@
Copyright (c) 2006-2010 Accenture. All rights reserved.
+==smoke-test
+
+variant target && rom --verbose $Quiet
--- a/core/builtins/sort.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/core/builtins/sort.cif Fri Sep 17 20:24:30 2010 +0100
@@ -24,9 +24,11 @@
Reverse the sort order.
-
-
==copyright
Copyright (c) 2006-2010 Accenture. All rights reserved.
+==smoke-test
+
+echo "wonderful^r^nfshell^r^njust^r^nis" | sort | export -s RESULT
+var RESULT == "fshell^r^nis^r^njust^r^nwonderful^r^n" || $Error
--- a/core/builtins/source.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/core/builtins/source.cif Fri Sep 17 20:24:30 2010 +0100
@@ -56,3 +56,6 @@
Copyright (c) 2006-2010 Accenture. All rights reserved.
+==smoke-test
+
+# Tested by fshell-basic-test.script, this section is just so ciftest doesn't report it as a command without any tests
--- a/core/builtins/svrinfo.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/core/builtins/svrinfo.cif Fri Sep 17 20:24:30 2010 +0100
@@ -28,3 +28,6 @@
Copyright (c) 2006-2010 Accenture. All rights reserved.
+==smoke-test
+
+svrinfo $Quiet
--- a/core/builtins/touch.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/core/builtins/touch.cif Fri Sep 17 20:24:30 2010 +0100
@@ -24,9 +24,15 @@
The file to touch.
-
-
==copyright
Copyright (c) 2006-2010 Accenture. All rights reserved.
+==smoke-test
+
+date --timestamp | export -s TIMESTAMP
+export TEMPFILE temp$TIMESTAMP
+exists $TEMPFILE && $Error
+touch $TEMPFILE
+exists $TEMPFILE || $Error
+rm $TEMPFILE
--- a/core/builtins/var.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/core/builtins/var.cif Fri Sep 17 20:24:30 2010 +0100
@@ -76,3 +76,6 @@
Copyright (c) 2006-2010 Accenture. All rights reserved.
+==smoke-test
+
+# Tested by fshell-basic-test.script, this section is just so ciftest doesn't report it as a command without any tests
\ No newline at end of file
--- a/core/builtins/version.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/core/builtins/version.cif Fri Sep 17 20:24:30 2010 +0100
@@ -24,3 +24,6 @@
Copyright (c) 2008-2010 Accenture. All rights reserved.
+==smoke-test
+
+version $Quiet
--- a/core/builtins/which.cif Wed Sep 15 00:45:50 2010 +0100
+++ b/core/builtins/which.cif Fri Sep 17 20:24:30 2010 +0100
@@ -24,3 +24,7 @@
Copyright (c) 2006-2010 Accenture. All rights reserved.
+==smoke-test
+
+which which | export -s RESULT
+var RESULT == "which: built-in command 'which'^r^n" || $Error
--- a/core/src/worker_thread.cpp Wed Sep 15 00:45:50 2010 +0100
+++ b/core/src/worker_thread.cpp Fri Sep 17 20:24:30 2010 +0100
@@ -43,6 +43,49 @@
const TInt KCacheTime = 1000000; // 1 second
+// Thread death watchers run in the context of the thread pool main thread
+class CThreadDeathWatcher : public CActive
+ {
+public:
+ CThreadDeathWatcher(CWorkerThread* aWorker)
+ : CActive(CActive::EPriorityHigh), iWorker(aWorker)
+ {
+ }
+ ~CThreadDeathWatcher()
+ {
+ WT_LOG(_L("Deleting thread death watcher for worker %d"), TUint(iWorker->GetThreadId()));
+ Cancel();
+ }
+ void StartWatching()
+ {
+ CActiveScheduler::Add(this);
+ iWorker->iWorkerThread.Logon(iStatus);
+ SetActive();
+ }
+ CWorkerThread* WorkerThread() const
+ {
+ return iWorker;
+ }
+private:
+ void DoCancel()
+ {
+ iWorker->iWorkerThread.LogonCancel(iStatus);
+ }
+ void RunL()
+ {
+ iWorker->SignalClientThatThreadHasDied();
+ iWorker->iParentPool->WorkerDied(iWorker);
+ // WorkerDied may cause this object to be deleted, so don't add anything that accesses member data below this point!
+ }
+
+private:
+ CWorkerThread* iWorker;
+ };
+
+//
+
+
+
CThreadPool* CThreadPool::NewL()
{
CThreadPool* self = new(ELeave) CThreadPool();
@@ -65,17 +108,19 @@
iThreadPoolAllocator = &User::Allocator();
User::LeaveIfError(iLock.CreateLocal());
iThreads.ReserveL(2);
+ iThreadDeathWatchers.ReserveL(2);
// We don't create any workers by default
}
CThreadPool::~CThreadPool()
{
- WT_LOG(_L("Deleting thread pool. %d threads created during its lifetime"), iCountThreadsCreated);
+ WT_LOG(_L("Deleting thread pool. %d threads created during its lifetime, %d currently and %d watchers"), iCountThreadsCreated, iThreads.Count(), iThreadDeathWatchers.Count());
Cancel();
for (TInt i = 0; i < iThreads.Count(); i++)
{
iThreads[i]->Shutdown();
}
+ iThreadDeathWatchers.ResetAndDestroy();
iThreads.ResetAndDestroy();
iLock.Close();
delete iIdleTimer;
@@ -85,7 +130,9 @@
void CThreadPool::Lock()
{
+ WT_LOG(_L("Getting lock from thread %d"), TUint(RThread().Id()));
iLock.Wait();
+ WT_LOG(_L("Got lock from thread %d"), TUint(RThread().Id()));
}
void CThreadPool::SwitchToThreadPoolHeap()
@@ -112,6 +159,7 @@
void CThreadPool::Unlock()
{
RestoreHeap();
+ WT_LOG(_L("Releasing lock from thread %d"), TUint(RThread().Id()));
iLock.Signal();
}
@@ -154,12 +202,18 @@
if (foundThread == NULL)
{
SwitchToThreadPoolHeap();
+ iPendingThreadLogons.ReserveL(iPendingThreadLogons.Count() + 1); // So the Append below can't fail
+ iThreadDeathWatchers.ReserveL(iThreadDeathWatchers.Count() + 1);
+ iThreads.ReserveL(iThreads.Count() + 1);
foundThread = CWorkerThread::NewLC(this, requiredAllocator);
+ CThreadDeathWatcher* threadWatcher = new(ELeave) CThreadDeathWatcher(foundThread);
iCountThreadsCreated++;
- WT_LOG(_L("Creating new worker thread %d"), TUint(foundThread->GetThreadId()));
- iThreads.AppendL(foundThread);
- CleanupStack::Pop(foundThread);
- iPendingThreadLogons.AppendL(foundThread);
+ WT_LOG(_L("Created new worker thread %d"), TUint(foundThread->GetThreadId()));
+ iThreads.Append(foundThread);
+ iPendingThreadLogons.Append(threadWatcher);
+ iThreadDeathWatchers.Append(threadWatcher);
+ CleanupStack::Pop(foundThread);
+
SignalSelf(); // So the iPendingThreadLogons gets sorted out in context of main thread
RestoreHeap();
}
@@ -179,14 +233,19 @@
// This is now always called in the main thread
ASSERT(RThread().Id() == iMainThread.Id());
- // Find it and remove it - the next request will create a new worker if needed
- for (TInt i = 0; i < iThreads.Count(); i++)
+ for (TInt i = 0; i < iThreadDeathWatchers.Count(); i++)
{
- CWorkerThread* worker = iThreads[i];
- if (worker == aWorker)
+ if (iThreadDeathWatchers[i]->WorkerThread() == aWorker)
{
- delete worker;
+ // Clean up the watcher first
+ delete iThreadDeathWatchers[i];
+ iThreadDeathWatchers.Remove(i);
+
+ // The indexes in both arrays are guaranteed to be the same
+ ASSERT(iThreads[i] == aWorker);
+ delete aWorker;
iThreads.Remove(i);
+
break;
}
}
@@ -234,8 +293,7 @@
{
ASSERT(!worker->Busy());
worker->Shutdown();
- delete worker;
- iThreads.Remove(i);
+ // We don't delete the worker here, let CThreadDeathWatcher handle that
}
}
Unlock();
@@ -271,6 +329,8 @@
else
{
// Everything else (that isn't busy) gets cleaned up
+ delete iThreadDeathWatchers[i];
+ iThreadDeathWatchers.Remove(i);
worker->Shutdown();
delete worker;
iThreads.Remove(i);
@@ -302,7 +362,7 @@
{
for (TInt i = 0; i < iPendingThreadLogons.Count(); i++)
{
- iPendingThreadLogons[i]->RegisterThreadDeathWatcherOnCurrentThread();
+ iPendingThreadLogons[i]->StartWatching();
}
SwitchToThreadPoolHeap();
iPendingThreadLogons.Reset();
@@ -328,44 +388,11 @@
return 0;
}
-
////
-class CThreadDeathWatcher : public CActive
- {
-public:
- CThreadDeathWatcher(CWorkerThread* aWorker, RThread& aUnderlyingThread)
- : CActive(CActive::EPriorityHigh), iWorker(aWorker), iThread(aUnderlyingThread)
- {
- }
- ~CThreadDeathWatcher()
- {
- Cancel();
- }
- void StartWatching()
- {
- CActiveScheduler::Add(this);
- iThread.Logon(iStatus);
- SetActive();
- }
-private:
- void DoCancel()
- {
- iThread.LogonCancel(iStatus);
- }
- void RunL()
- {
- Deque(); // Our work is done. Might as well extract ourselves from whatever scheduler we're in while we can
- iWorker->ThreadDied();
- }
+const TInt KMaxHeapSize = 1024 * 1024;
-private:
- CWorkerThread* iWorker;
- RThread& iThread;
- };
-
-const TInt KMaxHeapSize = KMinHeapSize * 1024;
-
+// CWorkerThreadDispatchers run in the context of the worker thread they're owned by
class CWorkerThreadDispatcher : public CActive
{
public:
@@ -428,8 +455,6 @@
}
User::LeaveIfError(err);
- iThreadDeathWatcher = new(ELeave) CThreadDeathWatcher(this, iWorkerThread);
-
TRequestStatus stat;
iWorkerThread.Rendezvous(stat);
@@ -450,9 +475,8 @@
CWorkerThread::~CWorkerThread()
{
+ WT_LOG(_L("Deleting worker thread %d"), TUint(GetThreadId()));
ASSERT(iWorkerThread.Handle() == 0 || iWorkerThread.ExitType() != EExitPending);
- //Cancel();
- delete iThreadDeathWatcher;
iParentThread.Close();
iWorkerThread.Close();
}
@@ -601,7 +625,7 @@
return iSharedAllocator;
}
-void CWorkerThread::ThreadDied()
+void CWorkerThread::SignalClientThatThreadHasDied()
{
WT_LOG(_L("Task %d died with exittype %d reason %d"), iTaskId, iWorkerThread.ExitType(), iWorkerThread.ExitReason());
TInt err = iWorkerThread.ExitReason();
@@ -610,7 +634,6 @@
{
iParentThread.RequestComplete(iCompletionStatus, err);
}
- iParentPool->WorkerDied(this);
}
TInt CWorkerThread::ThreadFn(TAny* aSelf)
@@ -680,7 +703,3 @@
}
}
-void CWorkerThread::RegisterThreadDeathWatcherOnCurrentThread()
- {
- iThreadDeathWatcher->StartWatching();
- }
--- a/core/src/worker_thread.h Wed Sep 15 00:45:50 2010 +0100
+++ b/core/src/worker_thread.h Fri Sep 17 20:24:30 2010 +0100
@@ -31,6 +31,7 @@
};
class CWorkerThread;
+class CThreadDeathWatcher;
class CThreadPool : public CActive, public MTaskRunner
{
@@ -70,12 +71,10 @@
TInt iPendingCallbacks;
CPeriodic* iIdleTimer;
TInt iCountThreadsCreated; // This is for statistics gathering, not involved in the logic
- RArray<CWorkerThread*> iPendingThreadLogons; // Not owned
+ RArray<CThreadDeathWatcher*> iPendingThreadLogons; // Not owned
+ RPointerArray<CThreadDeathWatcher> iThreadDeathWatchers;
};
-
-class CThreadDeathWatcher;
-
class CWorkerThread : public CBase, public MThreadedTask
{
public:
@@ -89,7 +88,6 @@
TInt Setup(TInt aTaskId, const TDesC& aThreadName, MTaskRunner::TThreadFunctionL aThreadFunction, TAny* aThreadContext);
void Shutdown();
~CWorkerThread();
- void RegisterThreadDeathWatcherOnCurrentThread();
TBool Running() const { return iWorkerThread.Handle() && iWorkerThread.ExitType() == EExitPending; }
@@ -99,7 +97,7 @@
void AbortTask();
public: // For CThreadDeathWatcher to use
- void ThreadDied();
+ void SignalClientThatThreadHasDied();
private:
CWorkerThread(CThreadPool* aParentPool, RAllocator* aSharedAllocator);
@@ -126,6 +124,7 @@
TRequestStatus* iCompletionStatus;
friend class CWorkerThreadDispatcher;
+ friend class CThreadDeathWatcher;
};
#endif
--- a/core/tsrc/fshell-basic-test.script Wed Sep 15 00:45:50 2010 +0100
+++ b/core/tsrc/fshell-basic-test.script Fri Sep 17 20:24:30 2010 +0100
@@ -107,7 +107,9 @@
export FILE c:\fshell-basic-test.txt
rm $FILE 2>/dev/null &| echo -n "" # I don't like using "&| echo" syntax to indicate don't care if it fails. Probably rm -f should be quiet like unix version
+exists $FILE && $Error
echo -n "Testing file redirection" > c:\fshell-basic-test.txt
+exists $FILE || $Error
# The redirect stdin operation doesn't get used much
export -s FILECONTENTS < $FILE