--- a/build/common/bld.inf Mon Jul 26 17:19:00 2010 +0100
+++ b/build/common/bld.inf Wed Jul 28 16:41:19 2010 +0100
@@ -33,7 +33,9 @@
#include "..\..\plugins\consoles\rcons\group\bld.inf"
#include "..\..\plugins\consoles\vt100cons\group\bld.inf"
#include "..\..\plugins\consoles\win32cons\group\bld.inf"
+#ifdef FSHELL_TESTEXECUTE_SUPPORT
#include "..\..\plugins\consoles\tefcons\bld.inf"
+#endif
#include "..\..\plugins\networking\nullnif\group\bld.inf"
#include "..\..\plugins\networking\tcpcsy\group\bld.inf"
#include "..\..\plugins\networking\winsockprt\group\bld.inf"
--- a/build/common/common.mmh Mon Jul 26 17:19:00 2010 +0100
+++ b/build/common/common.mmh Wed Jul 28 16:41:19 2010 +0100
@@ -88,6 +88,9 @@
#ifndef FSHELL_NO_MD4_SUPPORT
#define FSHELL_MD4_SUPPORT
#endif
+#ifndef FSHELL_NO_SPCRE_SUPPORT
+#define FSHELL_SPCRE_SUPPORT
+#endif
//
// PlatSec.
--- a/build/s60/platform_generic.mmh Mon Jul 26 17:19:00 2010 +0100
+++ b/build/s60/platform_generic.mmh Wed Jul 28 16:41:19 2010 +0100
@@ -31,6 +31,6 @@
#define SYMBIAN_JAVA_NOT_INCLUDED
#endif
-#define FSHELL_SPCRE_SUPPORT
+#define FSHELL_TESTEXECUTE_SUPPORT
#endif // FSHELL_PLATFORM_GENERIC_MMH
--- a/commands/group/bld.inf Mon Jul 26 17:19:00 2010 +0100
+++ b/commands/group/bld.inf Wed Jul 28 16:41:19 2010 +0100
@@ -37,7 +37,6 @@
..\fed\group\fed.cif z:\resource\cif\fshell\fed.cif
..\snake\snake.cif z:\resource\cif\fshell\snake.cif
..\iniedit\iniedit.cif z:\resource\cif\fshell\iniedit.cif
-..\testexecute\testexecute.cif z:\resource\cif\fshell\testexecute.cif
PRJ_MMPFILES
..\cat\cat.mmp
@@ -61,7 +60,6 @@
..\fed\group\fed.mmp
..\snake\snake.mmp
..\iniedit\iniedit.mmp
-..\testexecute\testexecute.mmp
#ifdef FSHELL_CORE_SUPPORT_SWI
PRJ_EXPORTS
@@ -447,3 +445,10 @@
PRJ_MMPFILES
..\memspy\memspy.mmp
#endif
+
+#ifdef FSHELL_TESTEXECUTE_SUPPORT
+PRJ_EXPORTS
+..\testexecute\testexecute.mmp
+PRJ_MMPFILES
+..\testexecute\testexecute.cif z:\resource\cif\fshell\testexecute.cif
+#endif
--- a/commands/group/fshell_commands.iby Mon Jul 26 17:19:00 2010 +0100
+++ b/commands/group/fshell_commands.iby Wed Jul 28 16:41:19 2010 +0100
@@ -369,7 +369,9 @@
FSHELL_COMMAND_INFO_FILE(fshell,top.cif)
#endif
+#ifdef FSHELL_TESTEXECUTE_SUPPORT
FSHELL_EXECUTABLE_FILE(fshell_testexecute.exe)
FSHELL_COMMAND_INFO_FILE(fshell,testexecute.cif)
+#endif
#endif // __FSHELL_COMMANDS_IBY__
--- a/core/src/command_constructors.cpp Mon Jul 26 17:19:00 2010 +0100
+++ b/core/src/command_constructors.cpp Wed Jul 28 16:41:19 2010 +0100
@@ -66,32 +66,32 @@
// CThreadCommandConstructor.
//
-CThreadCommandConstructor* CThreadCommandConstructor::NewLC(TCommandConstructor aConstructor, TUint aFlags)
+CThreadCommandConstructor* CThreadCommandConstructor::NewLC(TCommandConstructor aConstructor, TUint aFlags, MTaskRunner* aTaskRunner)
{
CCommandBase* command = (*aConstructor)();
- CThreadCommandConstructor* self = CThreadCommandConstructor::NewLC(command->Name(), aConstructor, aFlags);
+ CThreadCommandConstructor* self = CThreadCommandConstructor::NewLC(command->Name(), aConstructor, aFlags, aTaskRunner);
CleanupStack::Pop(self);
CleanupStack::PopAndDestroy(command);
CleanupStack::PushL(self);
return self;
}
-CThreadCommandConstructor* CThreadCommandConstructor::NewLC(const TDesC& aCommandName, TCommandConstructor aConstructor, TUint aFlags)
+CThreadCommandConstructor* CThreadCommandConstructor::NewLC(const TDesC& aCommandName, TCommandConstructor aConstructor, TUint aFlags, MTaskRunner* aTaskRunner)
{
- CThreadCommandConstructor* self = new(ELeave) CThreadCommandConstructor(aFlags, aConstructor);
+ CThreadCommandConstructor* self = new(ELeave) CThreadCommandConstructor(aFlags, aConstructor, aTaskRunner);
CleanupStack::PushL(self);
self->BaseConstructL(aCommandName);
return self;
}
-CThreadCommandConstructor::CThreadCommandConstructor(TUint aFlags, TCommandConstructor aConstructor)
- : CCommandConstructorBase(ETypeThread), iFlags(aFlags), iConstructor(aConstructor)
+CThreadCommandConstructor::CThreadCommandConstructor(TUint aFlags, TCommandConstructor aConstructor, MTaskRunner* aTaskRunner)
+ : CCommandConstructorBase(ETypeThread), iFlags(aFlags), iConstructor(aConstructor), iTaskRunner(aTaskRunner)
{
}
MCommand* CThreadCommandConstructor::ConstructCommandL()
{
- return CThreadCommand::NewL(CommandName(), iConstructor, iFlags);
+ return CThreadCommand::NewL(CommandName(), iConstructor, iFlags, iTaskRunner);
}
void CThreadCommandConstructor::AppendDescriptionL(RLtkBuf16& aBuf) const
--- a/core/src/command_constructors.h Mon Jul 26 17:19:00 2010 +0100
+++ b/core/src/command_constructors.h Wed Jul 28 16:41:19 2010 +0100
@@ -18,6 +18,8 @@
#include <fshell/ioutils.h>
class MCommand;
+class MTaskRunner;
+
namespace LtkUtils { class RLtkBuf16; }
using LtkUtils::RLtkBuf16;
@@ -61,16 +63,17 @@
class CThreadCommandConstructor : public CCommandConstructorBase
{
public:
- static CThreadCommandConstructor* NewLC(TCommandConstructor aConstructor, TUint aFlags);
- static CThreadCommandConstructor* NewLC(const TDesC& aCommandName, TCommandConstructor aConstructor, TUint aFlags);
+ static CThreadCommandConstructor* NewLC(TCommandConstructor aConstructor, TUint aFlags, MTaskRunner* aTaskRunner);
+ static CThreadCommandConstructor* NewLC(const TDesC& aCommandName, TCommandConstructor aConstructor, TUint aFlags, MTaskRunner* aTaskRunner);
private:
- CThreadCommandConstructor(TUint aFlags, TCommandConstructor aConstructor);
+ CThreadCommandConstructor(TUint aFlags, TCommandConstructor aConstructor, MTaskRunner* aTaskRunner);
private: // From CCommandConstructorBase.
virtual MCommand* ConstructCommandL();
virtual void AppendDescriptionL(RLtkBuf16& aBuf) const;
private:
TUint iFlags;
TCommandConstructor iConstructor;
+ MTaskRunner* iTaskRunner;
};
class CExeCommandConstructor : public CCommandConstructorBase
--- a/core/src/command_factory.cpp Mon Jul 26 17:19:00 2010 +0100
+++ b/core/src/command_factory.cpp Wed Jul 28 16:41:19 2010 +0100
@@ -24,7 +24,7 @@
#include "xmodem.h"
#include "ymodem.h"
#include "version.h"
-
+#include "worker_thread.h"
//
// Constants.
@@ -84,6 +84,7 @@
Cancel();
iCommands.ResetAndDestroy();
iLock.Close();
+ delete iThreadPool;
}
TInt CompareCommandNames(const CCommandConstructorBase& aCommand1, const CCommandConstructorBase& aCommand2)
@@ -250,6 +251,7 @@
{
User::LeaveIfError(iLock.CreateLocal());
User::LeaveIfError(iFs.DriveList(iDriveList));
+ iThreadPool = CThreadPool::NewL();
AddThreadCommandL(CCmdExit::NewLC); // Note, this command should never execute as 'exit' has handled explicitly by CParser. It exists so that 'exit' appears in fshell's help list and also to support 'exit --help'.
AddThreadCommandL(CCmdHelp::NewLC, CThreadCommand::ESharedHeap);
@@ -301,7 +303,7 @@
AddThreadCommandL(CCmdStart::NewLC);
AddThreadCommandL(CCmdCompare::NewLC);
AddThreadCommandL(CCmdTime::NewLC);
- AddThreadCommandL(CCmdRepeat::NewLC);
+ AddThreadCommandL(CCmdRepeat::NewLC); // TODO: Should this have EUpdateEnvironment? It seems weird that source and foreach do but repeat doesn't. -TomS
AddThreadCommandL(CCmdDebug::NewLC);
AddThreadCommandL(CCmdReadMem::NewLC);
AddThreadCommandL(CCmdE32Header::NewLC);
@@ -417,14 +419,14 @@
void CCommandFactory::AddThreadCommandL(TCommandConstructor aConstructor, TUint aFlags)
{
- CCommandConstructorBase* constructor = CThreadCommandConstructor::NewLC(aConstructor, aFlags);
+ CCommandConstructorBase* constructor = CThreadCommandConstructor::NewLC(aConstructor, aFlags, iThreadPool);
AddCommandL(constructor);
CleanupStack::Pop(constructor);
}
void CCommandFactory::AddThreadCommandL(const TDesC& aCommandName, TCommandConstructor aConstructor, TUint aAttributes, TUint aFlags)
{
- CCommandConstructorBase* constructor = CThreadCommandConstructor::NewLC(aCommandName, aConstructor, aFlags);
+ CCommandConstructorBase* constructor = CThreadCommandConstructor::NewLC(aCommandName, aConstructor, aFlags, iThreadPool);
constructor->SetAttributes(aAttributes);
AddCommandL(constructor);
CleanupStack::Pop(constructor);
@@ -432,7 +434,7 @@
void CCommandFactory::AddAliasCommandL(const TDesC& aAliasName, TCommandConstructor aConstructor, const TDesC* aAdditionalArguments, const TDesC* aReplacementArguments, TUint aAttributes, TUint aFlags)
{
- CCommandConstructorBase* aliasedCommand = CThreadCommandConstructor::NewLC(aConstructor, aFlags);
+ CCommandConstructorBase* aliasedCommand = CThreadCommandConstructor::NewLC(aConstructor, aFlags, iThreadPool);
CCommandConstructorBase* constructor = CAliasCommandConstructor::NewLC(aAliasName, aliasedCommand, aAdditionalArguments, aReplacementArguments);
CleanupStack::Pop(2, aliasedCommand); // Now owned by "constructor".
CleanupStack::PushL(constructor);
@@ -472,10 +474,6 @@
}
}
- //_LIT(KDllExtension, ".dll");
- //TUidType dllUids(KNullUid, KFshellDllUid, KNullUid);
- //AppendExternalCommandsL(dllUids, KDllExtension);
-
_LIT(KExeExtension, ".exe");
TUidType exeUids(KNullUid, KFshellExeUid, KNullUid);
AppendExternalCommandsL(exeUids, KExeExtension);
--- a/core/src/command_factory.h Mon Jul 26 17:19:00 2010 +0100
+++ b/core/src/command_factory.h Wed Jul 28 16:41:19 2010 +0100
@@ -15,7 +15,9 @@
#define __COMMAND_FACTORY_H__
#include <e32base.h>
-#include "command_constructors.h"
+#include "command_wrappers.h"
+class CCommandConstructorBase;
+class CThreadPool;
#include "error.h"
class RFs;
@@ -51,6 +53,7 @@
virtual void RunL();
virtual void DoCancel();
virtual TInt RunError(TInt aError);
+
private:
RFs& iFs;
mutable RMutex iLock;
@@ -58,6 +61,7 @@
TDriveList iDriveList;
TBool iFileSystemScanned;
TBool iFailedToScanFileSystem;
+ CThreadPool* iThreadPool;
};
--- a/core/src/command_wrappers.cpp Mon Jul 26 17:19:00 2010 +0100
+++ b/core/src/command_wrappers.cpp Wed Jul 28 16:41:19 2010 +0100
@@ -11,20 +11,14 @@
//
#include "command_wrappers.h"
-
-
-//
-// Constants.
-//
-
-const TInt KMaxHeapSize = KMinHeapSize * 1024;
-
+#include "worker_thread.h"
//
// CCommandWrapperBase.
//
CCommandWrapperBase::CCommandWrapperBase()
+ : CActive(CActive::EPriorityStandard)
{
}
@@ -96,14 +90,23 @@
delete this;
}
+void CCommandWrapperBase::RunL()
+ {
+ // Optionally for use by subclasses
+ }
+
+void CCommandWrapperBase::DoCancel()
+ {
+ // Optionally for use by subclasses
+ }
//
// CThreadCommand.
//
-CThreadCommand* CThreadCommand::NewL(const TDesC& aName, TCommandConstructor aCommandConstructor, TUint aFlags)
+CThreadCommand* CThreadCommand::NewL(const TDesC& aName, TCommandConstructor aCommandConstructor, TUint aFlags, MTaskRunner* aTaskRunner)
{
- CThreadCommand* self = new(ELeave) CThreadCommand(aCommandConstructor, aFlags);
+ CThreadCommand* self = new(ELeave) CThreadCommand(aCommandConstructor, aFlags, aTaskRunner);
CleanupStack::PushL(self);
self->ConstructL(aName);
CleanupStack::Pop(self);
@@ -112,14 +115,15 @@
CThreadCommand::~CThreadCommand()
{
- delete iWatcher;
- delete iArgs;
+ Cancel();
+ delete iCommandLine;
iThread.Close();
}
-CThreadCommand::CThreadCommand(TCommandConstructor aCommandConstructor, TUint aFlags)
- : iFlags(aFlags), iCommandConstructor(aCommandConstructor)
+CThreadCommand::CThreadCommand(TCommandConstructor aCommandConstructor, TUint aFlags, MTaskRunner* aTaskRunner)
+ : iFlags(aFlags), iCommandConstructor(aCommandConstructor), iTaskRunner(aTaskRunner)
{
+ CActiveScheduler::Add(this);
iThread.SetHandle(0); // By default RThread refers to the current thread. This results in fshell's thread exiting if this object gets killed before it has managed to open a real thread handle.
if (iFlags & EUpdateEnvironment) iFlags |= ESharedHeap; // Update environment implies a shared heap, ever since we did away with the explict SwitchAllocator
}
@@ -127,66 +131,31 @@
void CThreadCommand::ConstructL(const TDesC& aName)
{
BaseConstructL(aName);
- iWatcher = CThreadWatcher::NewL();
}
-void CommandThreadStartL(CThreadCommand::TArgs& aArgs)
+void CThreadCommand::DoCommandThreadStartL(TAny* aSelf)
{
- if (aArgs.iFlags & CThreadCommand::ESharedHeap)
- {
- // If we're sharing the main fshell heap, we have to play by the rules and not crash
- User::SetCritical(User::EProcessCritical);
- }
-
- CActiveScheduler* scheduler = new(ELeave) CActiveScheduler;
- CleanupStack::PushL(scheduler);
- CActiveScheduler::Install(scheduler);
-
- HBufC* commandLine = aArgs.iCommandLine.AllocLC();
+ CThreadCommand* self = static_cast<CThreadCommand*>(aSelf);
IoUtils::CEnvironment* env;
- if (aArgs.iFlags & CThreadCommand::EUpdateEnvironment)
+ if (self->iFlags & CThreadCommand::EUpdateEnvironment)
{
- env = aArgs.iEnv.CreateSharedEnvironmentL();
+ env = self->iSuppliedEnv->CreateSharedEnvironmentL();
}
else
{
// A straight-forward copy
- env = IoUtils::CEnvironment::NewL(aArgs.iEnv);
+ env = IoUtils::CEnvironment::NewL(*self->iSuppliedEnv);
}
CleanupStack::PushL(env);
- CCommandBase* command = (*aArgs.iCommandConstructor)();
- RThread parentThread;
- User::LeaveIfError(parentThread.Open(aArgs.iParentThreadId));
- parentThread.RequestComplete(aArgs.iParentStatus, KErrNone);
- parentThread.Close();
-
- command->RunCommandL(commandLine, env);
- CleanupStack::PopAndDestroy(4, scheduler); // env, command, commandline, scheduler
+ CCommandBase* command = (*self->iCommandConstructor)();
+ //RDebug::Print(_L("5. DoCommandThreadStartL rendezvousing for %S %S"), &self->CmndName(), self->iCommandLine);
+ RThread::Rendezvous(KErrNone);
+ command->RunCommandL(self->iCommandLine, env);
+ CleanupStack::PopAndDestroy(2, env); // command, env
}
-TInt CommandThreadStart(TAny* aPtr)
- {
- CThreadCommand::TArgs args = *(CThreadCommand::TArgs*)aPtr;
- TBool sharedHeap = (args.iFlags & CThreadCommand::ESharedHeap);
- if (!sharedHeap)
- {
- __UHEAP_MARK;
- }
- TInt err = KErrNoMemory;
- CTrapCleanup* cleanup = CTrapCleanup::New();
- if (cleanup)
- {
- TRAP(err, CommandThreadStartL(args));
- delete cleanup;
- }
- if (!sharedHeap)
- {
- __UHEAP_MARKEND;
- }
- return err;
- }
void SetHandleOwnersL(TThreadId aThreadId, RIoReadHandle& aStdin, RIoWriteHandle& aStdout, RIoWriteHandle& aStderr)
{
@@ -199,64 +168,36 @@
{
ASSERT(iObserver == NULL);
- TRequestStatus status(KRequestPending);
- iArgs = new TArgs(iFlags, aEnv, iCommandConstructor, aCommandLine, status);
- if (iArgs == NULL)
+ MThreadedTask* thread = NULL;
+ TRAPD(err, thread = iTaskRunner->NewTaskInSeparateThreadL(CmndName(), iFlags & ESharedHeap, &DoCommandThreadStartL, this));
+ if (err) return err;
+
+ TRAP(err, SetHandleOwnersL(thread->GetThreadId(), CmndStdin(), CmndStdout(), CmndStderr()));
+
+ if (!err)
{
- return KErrNoMemory;
+ iCommandLine = aCommandLine.Alloc();
+ if (!iCommandLine) err = KErrNoMemory;
}
- TInt i = 0;
- TName threadName;
- TInt err = KErrNone;
- do
+ if (!err)
{
- const TDesC& name = CmndName();
- threadName.Format(_L("%S_%02d"), &name, i++);
- if (iFlags & ESharedHeap)
- {
- err = iThread.Create(threadName, CommandThreadStart, KDefaultStackSize, NULL, iArgs);
- }
- else
- {
- err = iThread.Create(threadName, CommandThreadStart, KDefaultStackSize, KMinHeapSize, KMaxHeapSize, iArgs);
- }
- }
- while (err == KErrAlreadyExists);
-
- if (err)
- {
- return err;
+ err = iThread.Open(thread->GetThreadId());
}
- err = iWatcher->Logon(*this, iThread, aObserver);
- if (err)
+ if (!err)
{
- iThread.Kill(0);
- iThread.Close();
- return err;
+ iSuppliedEnv = &aEnv;
+ iObserver = &aObserver;
+ thread->ExecuteTask(iStatus);
+ SetActive();
+ }
+ else
+ {
+ thread->AbortTask();
}
- TThreadId threadId = iThread.Id();
- TRAP(err, SetHandleOwnersL(threadId, CmndStdin(), CmndStdout(), CmndStderr()));
- if (err)
- {
- iThread.Kill(0);
- iThread.Close();
- return err;
- }
-
- iThread.Resume();
- User::WaitForRequest(status, iWatcher->iStatus);
- if (status == KRequestPending)
- {
- iThread.Close();
- return iWatcher->iStatus.Int();
- }
-
- iWatcher->SetActive();
- iObserver = &aObserver;
- return KErrNone;
+ return err;
}
void CThreadCommand::CmndForeground()
@@ -300,71 +241,14 @@
return iThread.ExitCategory();
}
-
-//
-// CThreadCommand::TArgs.
-//
-
-CThreadCommand::TArgs::TArgs(TUint aFlags, IoUtils::CEnvironment& aEnv, TCommandConstructor aCommandConstructor, const TDesC& aCommandLine, TRequestStatus& aParentStatus)
- : iFlags(aFlags), iEnv(aEnv), iCommandConstructor(aCommandConstructor), iCommandLine(aCommandLine), iParentStatus(&aParentStatus), iParentThreadId(RThread().Id())
+void CThreadCommand::RunL()
{
- }
-
-
-//
-// CThreadCommand::CThreadWatcher.
-//
-
-CThreadCommand::CThreadWatcher* CThreadCommand::CThreadWatcher::NewL()
- {
- return new(ELeave) CThreadWatcher();
- }
-
-CThreadCommand::CThreadWatcher::~CThreadWatcher()
- {
- Cancel();
- }
-
-CThreadCommand::CThreadWatcher::CThreadWatcher()
- : CActive(CActive::EPriorityStandard)
- {
- CActiveScheduler::Add(this);
+ iObserver->HandleCommandComplete(*this, iStatus.Int());
}
-TInt CThreadCommand::CThreadWatcher::Logon(CThreadCommand& aCommand, RThread& aThread, MCommandObserver& aObserver)
+void CThreadCommand::DoCancel()
{
- TInt ret = KErrNone;
- aThread.Logon(iStatus);
- if (iStatus != KRequestPending)
- {
- User::WaitForRequest(iStatus);
- ret = iStatus.Int();
- }
- else
- {
- iCommand = &aCommand;
- iThread = &aThread;
- iObserver = &aObserver;
- }
- return ret;
- }
-
-void CThreadCommand::CThreadWatcher::SetActive()
- {
- CActive::SetActive();
- }
-
-void CThreadCommand::CThreadWatcher::RunL()
- {
- iObserver->HandleCommandComplete(*iCommand, iStatus.Int());
- }
-
-void CThreadCommand::CThreadWatcher::DoCancel()
- {
- if (iThread)
- {
- iThread->LogonCancel(iStatus);
- }
+ CmndKill(); // This is a bit drastic, but effective...
}
--- a/core/src/command_wrappers.h Mon Jul 26 17:19:00 2010 +0100
+++ b/core/src/command_wrappers.h Wed Jul 28 16:41:19 2010 +0100
@@ -55,8 +55,7 @@
virtual void HandleCommandComplete(MCommand& aCommand, TInt aError) = 0;
};
-
-class CCommandWrapperBase : public CBase, public MCommand
+class CCommandWrapperBase : public CActive, public MCommand
{
protected:
CCommandWrapperBase();
@@ -74,6 +73,10 @@
virtual void CmndDisown();
private: // From MCommand.
virtual void CmndRelease();
+protected: // From CActive
+ void RunL();
+ void DoCancel();
+
private:
HBufC* iName; ///< This is used by concrete classes as they see fit.
RIoReadHandle iStdin;
@@ -91,11 +94,14 @@
ESharedHeap = 0x00000002, // Any command that accesses gShell must have this set
};
public:
- static CThreadCommand* NewL(const TDesC& aName, TCommandConstructor aCommandConstructor, TUint aFlags);
+ static CThreadCommand* NewL(const TDesC& aName, TCommandConstructor aCommandConstructor, TUint aFlags, MTaskRunner* aTaskRunner);
~CThreadCommand();
private:
- CThreadCommand(TCommandConstructor aCommandConstructor, TUint aFlags);
+ CThreadCommand(TCommandConstructor aCommandConstructor, TUint aFlags, MTaskRunner* aTaskRunner);
void ConstructL(const TDesC& aName);
+ void RunL();
+ void DoCancel();
+ static void DoCommandThreadStartL(TAny* aSelf);
private: // From MCommand.
virtual TInt CmndRun(const TDesC& aCommandLine, IoUtils::CEnvironment& aEnv, MCommandObserver& aObserver, RIoSession& aIoSession);
virtual void CmndForeground();
@@ -105,43 +111,14 @@
virtual TInt CmndResume();
virtual TExitType CmndExitType() const;
virtual TExitCategoryName CmndExitCategory() const;
-public:
- class TArgs
- {
- public:
- TArgs(TUint aFlags, CEnvironment& aEnv, TCommandConstructor aCommandConstructor, const TDesC& aCommandLine, TRequestStatus& aParentStatus);
- public:
- TUint iFlags;
- CEnvironment& iEnv;
- TCommandConstructor iCommandConstructor;
- const TPtrC iCommandLine;
- TRequestStatus* iParentStatus;
- TThreadId iParentThreadId;
- };
- class CThreadWatcher : public CActive
- {
- public:
- static CThreadWatcher* NewL();
- ~CThreadWatcher();
- TInt Logon(CThreadCommand& aCommand, RThread& aThread, MCommandObserver& aObserver);
- void SetActive();
- private:
- CThreadWatcher();
- private: // From CActive.
- virtual void RunL();
- virtual void DoCancel();
- private:
- CThreadCommand* iCommand;
- RThread* iThread;
- MCommandObserver* iObserver;
- };
private:
TUint iFlags;
TCommandConstructor iCommandConstructor;
MCommandObserver* iObserver;
- TArgs* iArgs;
RThread iThread;
- CThreadWatcher* iWatcher;
+ MTaskRunner* iTaskRunner;
+ CEnvironment* iSuppliedEnv;
+ HBufC* iCommandLine;
};
--- a/core/src/fshell.mmp Mon Jul 26 17:19:00 2010 +0100
+++ b/core/src/fshell.mmp Wed Jul 28 16:41:19 2010 +0100
@@ -65,6 +65,7 @@
source lexer.cpp
source file_reader.cpp
source script_command.cpp
+source worker_thread.cpp
sourcepath ..\builtins
source hello.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/worker_thread.cpp Wed Jul 28 16:41:19 2010 +0100
@@ -0,0 +1,686 @@
+// worker_thread.cpp
+//
+// Copyright (c) 2010 Accenture. All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Accenture - Initial contribution
+//
+
+/*
+
+Some notes on how CThreadPool works:
+ * There are 2 types of CWorkerThread - ones that share a specific heap, and those that have their own heap.
+ * Tasks that need a shared heap can only be run using a CWorkerThread that is bound to that heap (one will be created if necessary)
+ * Tasks that don't need a shared heap can be run on any CWorkerThread that has its own heap.
+ * Excess CWorkerThreads are deleted after they've been idle for KCacheTime.
+ * CThreadPool uses an RFastLock on all operations that could be called from multiple threads.
+ * All member data of CThreadPool (including CWorkerThreads and their members) is kept on iThreadPoolAllocator, which was the heap
+ that the CThreadPool was created on. User::SwitchAllocator() is used where necessary to manipulate CThreadPool data from other threads
+ while the Lock is held
+ * When a task is queued, a non-busy CWorkerThread is found (or created).
+ * When a task is completed in a worker thread, it calls iParentThread.RequestComplete(iCompletionStatus, err). It then calls
+ CThreadPool::WorkerFinished() to set the CWorkerThread to be non-busy again.
+ * When a CWorkerThread is created it arranges (via SignalSelf()) to add a CThreadDeathWatcher to the main thread.
+ It follows that notifications about tasks failed because of thread death are always serviced by the main thread, which must therefore
+ remain responsive. (In the non-dead case, the completion notification is sent directly from the worker thread).
+
+*/
+
+#include "worker_thread.h"
+#include <e32debug.h>
+
+#define WT_LOG(args...)
+
+// These defines are for debugging only
+//#define WT_LOG(args...) RDebug::Print(args)
+// These probably don't even work any more - use with extreme caution!
+//#define NO_REUSE
+//#define IMMEDIATE_CLEANUP
+
+const TInt KCacheTime = 1000000; // 1 second
+
+CThreadPool* CThreadPool::NewL()
+ {
+ CThreadPool* self = new(ELeave) CThreadPool();
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ CleanupStack::Pop(self);
+ return self;
+ }
+
+CThreadPool::CThreadPool()
+ : CActive(CActive::EPriorityLow) // Cleanup doesn't need to be a priority
+ {
+ CActiveScheduler::Add(this);
+ }
+
+void CThreadPool::ConstructL()
+ {
+ User::LeaveIfError(iMainThread.Open(RThread().Id()));
+ iIdleTimer = CPeriodic::NewL(CActive::EPriorityLow-1); // Interactions around iPendingThreadLogons mean it's probably a good idea to have this priority below that of CThreadPool itself
+ iThreadPoolAllocator = &User::Allocator();
+ User::LeaveIfError(iLock.CreateLocal());
+ iThreads.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);
+ Cancel();
+ for (TInt i = 0; i < iThreads.Count(); i++)
+ {
+ iThreads[i]->Shutdown();
+ }
+ iThreads.ResetAndDestroy();
+ iLock.Close();
+ delete iIdleTimer;
+ iMainThread.Close();
+ iPendingThreadLogons.Close();
+ }
+
+void CThreadPool::Lock()
+ {
+ iLock.Wait();
+ }
+
+void CThreadPool::SwitchToThreadPoolHeap()
+ {
+ //ASSERT(iLock.IsHeld());
+ RAllocator* allocator = &User::Allocator();
+ if (allocator != iThreadPoolAllocator)
+ {
+ WT_LOG(_L("Thread %d switching from 0x%x to thread pool allocator 0x%x"), TUint(RThread().Id()), allocator, iThreadPoolAllocator);
+ iTempThreadAllocator = User::SwitchAllocator(iThreadPoolAllocator);
+ }
+ }
+
+void CThreadPool::RestoreHeap()
+ {
+ if (iTempThreadAllocator)
+ {
+ WT_LOG(_L("Thread %d restoring heap 0x%x"), TUint(RThread().Id()), iTempThreadAllocator);
+ User::SwitchAllocator(iTempThreadAllocator);
+ iTempThreadAllocator = NULL;
+ }
+ }
+
+void CThreadPool::Unlock()
+ {
+ RestoreHeap();
+ iLock.Signal();
+ }
+
+void CThreadPool::LockLC()
+ {
+ Lock();
+ CleanupStack::PushL(TCleanupItem(&DoUnlock, &iLock));
+ }
+
+#define _LOFF(p,T,f) ((T*)(((TUint8*)(p))-_FOFF(T,f))) // Why isn't this defined user-side?
+
+void CThreadPool::DoUnlock(TAny* aLock)
+ {
+ CThreadPool* self = _LOFF(aLock, CThreadPool, iLock);
+ self->Unlock();
+ }
+
+MThreadedTask* CThreadPool::NewTaskInSeparateThreadL(const TDesC& aThreadName, TBool aSharedHeap, MTaskRunner::TThreadFunctionL aThreadFunction, TAny* aThreadContext)
+ {
+ WT_LOG(_L("1. NewTaskInSeparateThreadL task %d (%S)"), iTaskCounter, &aThreadName);
+
+ LockLC();
+ // Hunt for a non-busy thread
+ CWorkerThread* foundThread = NULL;
+ RAllocator* requiredAllocator = aSharedHeap ? &User::Allocator() : NULL;
+#ifndef NO_REUSE // This code normally does run, except during debugging when NO_REUSE is defined
+ for (TInt i = 0; i < iThreads.Count(); i++)
+ {
+ CWorkerThread* thread = iThreads[i];
+ if (!thread->Busy() && thread->SharedAllocator() == requiredAllocator)
+ {
+ // If the worker thread is sharing an allocator, it must be sharing the *same* one as this current thread
+ ASSERT(thread->Running());
+ foundThread = thread;
+ break;
+ }
+ }
+#endif
+
+ if (foundThread == NULL)
+ {
+ SwitchToThreadPoolHeap();
+ foundThread = CWorkerThread::NewLC(this, requiredAllocator);
+ iCountThreadsCreated++;
+ WT_LOG(_L("Creating new worker thread %d"), TUint(foundThread->GetThreadId()));
+ iThreads.AppendL(foundThread);
+ CleanupStack::Pop(foundThread);
+ iPendingThreadLogons.AppendL(foundThread);
+ SignalSelf(); // So the iPendingThreadLogons gets sorted out in context of main thread
+ RestoreHeap();
+ }
+
+ WT_LOG(_L("Using worker thread %d for task %d (%S)"), TUint(foundThread->GetThreadId()), iTaskCounter, &aThreadName);
+
+ User::LeaveIfError(foundThread->Setup(iTaskCounter, aThreadName, aThreadFunction, aThreadContext));
+ foundThread->SetBusy(ETrue);
+ iTaskCounter++;
+ CleanupStack::PopAndDestroy(&iLock);
+ return foundThread;
+ }
+
+void CThreadPool::WorkerDied(CWorkerThread* aWorker)
+ {
+ Lock();
+ // 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++)
+ {
+ CWorkerThread* worker = iThreads[i];
+ if (worker == aWorker)
+ {
+ delete worker;
+ iThreads.Remove(i);
+ break;
+ }
+ }
+ Unlock();
+ }
+
+void CThreadPool::WorkerFinished(CWorkerThread* aWorker)
+ {
+ Lock();
+ aWorker->SetBusy(EFalse);
+#if defined(NO_REUSE)
+ // Nothing
+#else
+ // This is the normal case - queue ourself to run (on the main thread) so we can trigger the idle timer from our RunL
+ // Can't do that directly as timers are all thread-local
+ SignalSelf();
+#endif
+ Unlock();
+ }
+
+
+void CThreadPool::SignalSelf()
+ {
+ // TODO: Optimise if we actually are the main thread?
+
+ // Must be holding lock
+ iPendingCallbacks++;
+ TRequestStatus* stat = &iStatus;
+ if (iPendingCallbacks == 1) // Can't use IsActive() as we might not be in the same thread
+ {
+ iStatus = KRequestPending;
+ SetActive();
+ }
+ iMainThread.RequestComplete(stat, KErrNone); // Fortunately RThread::RequestComplete doesn't set the status to KRequestPending before completing it (unlike User::RequestComplete)
+ }
+
+void CThreadPool::CleanupAnyWorkersSharingAllocator(RAllocator* aAllocator)
+ {
+ Lock();
+ SwitchToThreadPoolHeap();
+ for (TInt i = iThreads.Count() - 1; i >= 0; i--)
+ {
+ CWorkerThread* worker = iThreads[i];
+ if (worker->SharedAllocator() == aAllocator)
+ {
+ ASSERT(!worker->Busy());
+ worker->Shutdown();
+ delete worker;
+ iThreads.Remove(i);
+ }
+ }
+ Unlock();
+ }
+
+void CThreadPool::PerformHouseKeeping()
+ {
+ // Time to do some housekeeping. Algorithm is:
+ // * Keep a single spare non-busy shared worker around, but only for the main heap (ie a CWorkerThread whose SharedAllocator() equals iThreadPoolAllocator)
+ // * Bin everything else that isn't busy
+
+ WT_LOG(_L("Idle timer expired, cleaning up spare workers"));
+ Lock();
+ ASSERT(&User::Allocator() == iThreadPoolAllocator); // We're running in the thread pool's main thread so no need to switch allocators
+
+#ifdef IMMEDIATE_CLEANUP
+ // Everything gets nuked if this is defined
+ TBool foundSpareWorker = ETrue;
+#else
+ TBool foundSpareWorker = EFalse;
+#endif
+ for (TInt i = iThreads.Count() - 1; i >= 0; i--)
+ {
+ CWorkerThread* worker = iThreads[i];
+ if (!worker->Busy())
+ {
+ RAllocator* allocator = worker->SharedAllocator();
+ if (allocator == iThreadPoolAllocator && !foundSpareWorker)
+ {
+ // This one can stay
+ foundSpareWorker = ETrue;
+ }
+ else
+ {
+ // Everything else (that isn't busy) gets cleaned up
+ worker->Shutdown();
+ delete worker;
+ iThreads.Remove(i);
+ }
+ }
+ }
+ Unlock();
+ }
+
+void CThreadPool::DoCancel()
+ {
+ // There's nothing we can cancel as we complete ourself
+ }
+
+void CThreadPool::RunL()
+ {
+ Lock();
+ iPendingCallbacks--;
+ while (iPendingCallbacks)
+ {
+ // Consume any further signals currently pending - we're somewhat abusing the active scheduler by completing the same repeatedly without it running in between so we have to consume the abuse here to balance things
+ User::WaitForRequest(iStatus);
+ iPendingCallbacks--;
+ }
+
+ // We kinda overload our RunL, using it for both registering logons and triggering the idle cleanup. Ah well.
+
+ if (iPendingThreadLogons.Count())
+ {
+ for (TInt i = 0; i < iPendingThreadLogons.Count(); i++)
+ {
+ iPendingThreadLogons[i]->RegisterThreadDeathWatcherOnCurrentThread();
+ }
+ SwitchToThreadPoolHeap();
+ iPendingThreadLogons.Reset();
+ }
+
+#ifdef IMMEDIATE_CLEANUP
+ Unlock();
+ // Delete the thread straight away
+ PerformHouseKeeping();
+#else
+ // Normally go through the timer
+ iIdleTimer->Cancel(); // Reset it if it's already counting
+ iIdleTimer->Start(KCacheTime, KCacheTime, TCallBack(&TimerCallback, this));
+ Unlock();
+#endif
+ }
+
+TInt CThreadPool::TimerCallback(TAny* aSelf)
+ {
+ CThreadPool* self = static_cast<CThreadPool*>(aSelf);
+ self->iIdleTimer->Cancel(); // Stop the thing being periodic
+ self->PerformHouseKeeping();
+ 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();
+ }
+
+private:
+ CWorkerThread* iWorker;
+ RThread& iThread;
+ };
+
+const TInt KMaxHeapSize = KMinHeapSize * 1024;
+
+class CWorkerThreadDispatcher : public CActive
+ {
+public:
+ CWorkerThreadDispatcher(CWorkerThread* aThread)
+ : CActive(CActive::EPriorityStandard), iThread(aThread)
+ {
+ CActiveScheduler::Add(this);
+ iStatus = KRequestPending;
+ SetActive();
+ }
+
+ void RunL()
+ {
+ if (iStatus.Int() == KErrNone)
+ {
+ iStatus = KRequestPending;
+ SetActive();
+ iThread->ThreadRun();
+ }
+ else
+ {
+ // Time to die
+ iThread->iAsWait.AsyncStop();
+ }
+ }
+
+ void DoCancel() {} // Can never happen because we can only ever be destroyed as a result of the AsyncStop in RunL from which we can never be active
+
+private:
+ CWorkerThread* iThread;
+ };
+
+CWorkerThread* CWorkerThread::NewLC(CThreadPool* aParentPool, RAllocator* aSharedAllocator)
+ {
+ CWorkerThread* self = new(ELeave) CWorkerThread(aParentPool, aSharedAllocator);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ return self;
+ }
+
+CWorkerThread::CWorkerThread(CThreadPool* aParentPool, RAllocator* aSharedAllocator)
+ : iParentPool(aParentPool), iSharedAllocator(aSharedAllocator)
+ {
+ iWorkerThread.SetHandle(0);
+ }
+
+void CWorkerThread::ConstructL()
+ {
+ TInt err = KErrNone;
+ TName name;
+ name.Format(_L("WorkerThread_%x"), this);
+ if (iSharedAllocator)
+ {
+ err = iWorkerThread.Create(name, &ThreadFn, KDefaultStackSize, iSharedAllocator, this);
+ }
+ else
+ {
+ // Create a new heap with default heap size
+ err = iWorkerThread.Create(name, &ThreadFn, KDefaultStackSize, KMinHeapSize, KMaxHeapSize, this);
+ }
+ User::LeaveIfError(err);
+
+ iThreadDeathWatcher = new(ELeave) CThreadDeathWatcher(this, iWorkerThread);
+
+ TRequestStatus stat;
+ iWorkerThread.Rendezvous(stat);
+
+ ASSERT(stat == KRequestPending);
+ if (stat == KRequestPending)
+ {
+ iWorkerThread.Resume();
+ }
+ else
+ {
+ iWorkerThread.Kill(stat.Int());
+ }
+
+ User::WaitForRequest(stat);
+ User::LeaveIfError(stat.Int());
+ // Thread is now ready to do stuff
+ }
+
+CWorkerThread::~CWorkerThread()
+ {
+ ASSERT(iWorkerThread.Handle() == 0 || iWorkerThread.ExitType() != EExitPending);
+ //Cancel();
+ delete iThreadDeathWatcher;
+ iParentThread.Close();
+ iWorkerThread.Close();
+ }
+
+TInt CWorkerThread::Setup(TInt aTaskId, const TDesC& aThreadName, MTaskRunner::TThreadFunctionL aThreadFunction, TAny* aThreadContext)
+ {
+ ASSERT(!Busy());
+
+ // Things to do in preparation for running a command:
+ // 1. Check what thread we're running in now and set ourselves and our thread death watcher on its scheduler (may not be the thread that created us originally)
+ // 2. Store the required context for the worker thread to access
+
+ TInt err = iParentThread.Open(RThread().Id());
+ if (!err)
+ {
+ iTaskId = aTaskId;
+ iName = &aThreadName;
+ iFn = aThreadFunction;
+ iContext = aThreadContext;
+ }
+ return err;
+ }
+
+TInt CWorkerThread::ExecuteTask(TRequestStatus& aCompletionStatus)
+ {
+ WT_LOG(_L("2. + Go task %d (%S)"), iTaskId, iName);
+
+ ASSERT(iCompletionStatus == NULL);
+ aCompletionStatus = KRequestPending;
+ iCompletionStatus = &aCompletionStatus;
+
+ // Setup rendezvous and completion requestStatuses before signalling iDispatchStatus
+ TRequestStatus rendezvousStat;
+ iWorkerThread.Rendezvous(rendezvousStat);
+
+ // Signal the worker thread to do its thing
+ TRequestStatus* dispatchStat = iDispatchStatus;
+ iWorkerThread.RequestComplete(dispatchStat, KErrNone);
+
+ User::WaitForRequest(rendezvousStat);
+
+ TInt err = rendezvousStat.Int();
+ if (iWorkerThread.ExitType() != EExitPending && rendezvousStat.Int() >= 0)
+ {
+ err = KErrDied;
+ }
+
+ WT_LOG(_L("6. - Go task %d (%S) err=%d"), iTaskId, iName, err);
+
+ if (err != KErrNone)
+ {
+ // We don't signal completion if there was an error prior to the rendezvous, we just return it here
+ iCompletionStatus = NULL;
+ }
+ return err;
+ }
+
+TThreadId CWorkerThread::GetThreadId() const
+ {
+ return iWorkerThread.Id();
+ }
+
+void CWorkerThread::AbortTask()
+ {
+ // Undo setup
+ iName = NULL;
+ iFn = NULL;
+ iContext = NULL;
+ iParentThread.Close();
+ iParentPool->WorkerFinished(this);
+ }
+
+void CWorkerThread::ThreadRun()
+ {
+ // Runs in the worker thread
+
+ if (!UsingSharedAllocator())
+ {
+ __UHEAP_MARK;
+ }
+
+ TInt i = 0;
+ TInt err = KErrNone;
+ do
+ {
+ TName threadName;
+ threadName.Format(_L("%S_%02d"), iName, i++);
+ err = User::RenameThread(threadName);
+ WT_LOG(_L("3. Running thread for task %d (%S)"), iTaskId, &threadName);
+ }
+ while (err == KErrAlreadyExists);
+
+ // Execute the actual function
+ WT_LOG(_L("4. + ThreadRun for task %d (%S)"), iTaskId, iName);
+ TRAP(err, (*iFn)(iContext));
+ WT_LOG(_L("7. - ThreadRun for task %d (%S) signalling thread %d with err=%d"), iTaskId, iName, TUint(iParentThread.Id()), err);
+
+ if (!UsingSharedAllocator())
+ {
+ // Do this before saying we've actually finished the task, otherwise we risk deadlocking on the thread pool lock when IMMEDIATE_CLEANUP is defined
+ // because CleanupAnyWorkersSharingAllocator takes the lock, but the lock can already be held by that point around the Shutdown that IMMEDIATE_CLEANUP does.
+ iParentPool->CleanupAnyWorkersSharingAllocator(&User::Allocator()); // Otherwise the heap check will fail
+ }
+
+ // And signal back the result
+ iParentPool->WorkerFinished(this);
+ iParentThread.RequestComplete(iCompletionStatus, err);
+
+ // Finally put our name back to what it was
+#ifdef _DEBUG
+ _LIT(KDebugName, "WorkerThread_%x (was %S)");
+ TName threadName = RThread().Name();
+ TPtrC oldName = threadName.Left(threadName.MaxLength() - KDebugName().Length());
+ TName newName;
+ newName.Format(KDebugName, this, &oldName);
+ User::RenameThread(newName);
+#else
+ TName threadName;
+ threadName.Format(_L("WorkerThread_%x"), this);
+ User::RenameThread(threadName);
+#endif
+
+ if (!UsingSharedAllocator())
+ {
+ __UHEAP_MARKEND;
+ }
+ }
+
+TBool CWorkerThread::Busy() const
+ {
+ return iBusy;
+ }
+
+void CWorkerThread::SetBusy(TBool aBusy)
+ {
+ iBusy = aBusy;
+ }
+
+TBool CWorkerThread::UsingSharedAllocator() const
+ {
+ return iSharedAllocator != NULL;
+ }
+
+RAllocator* CWorkerThread::SharedAllocator() const
+ {
+ return iSharedAllocator;
+ }
+
+void CWorkerThread::ThreadDied()
+ {
+ WT_LOG(_L("Task %d died with exittype %d reason %d"), iTaskId, iWorkerThread.ExitType(), iWorkerThread.ExitReason());
+ TInt err = iWorkerThread.ExitReason();
+ if (err >= 0) err = KErrDied;
+ if (iCompletionStatus)
+ {
+ iParentThread.RequestComplete(iCompletionStatus, err);
+ }
+ iParentPool->WorkerDied(this);
+ }
+
+TInt CWorkerThread::ThreadFn(TAny* aSelf)
+ {
+ CWorkerThread* self = static_cast<CWorkerThread*>(aSelf);
+ if (self->UsingSharedAllocator())
+ {
+ // If we're sharing the main fshell heap, we have to play by the rules and not crash
+ User::SetCritical(User::EProcessCritical);
+
+ // We also need to temporarily switch to the thread pool's allocator, because our CTrapCleanup, CActiveScheduler etc conceptually belong with the worker thread object and not with the heap we're sharing (which could be different... damn repeat command again)
+ //self->iParentPool->Lock();
+ //self->iParentPool->SwitchToThreadPoolHeap();
+ }
+ else
+ {
+ __UHEAP_MARK;
+ }
+ TInt err = KErrNoMemory;
+ CTrapCleanup* cleanup = CTrapCleanup::New();
+ //WT_LOG(_L("Worker thread %d creating trapcleanup 0x%x"), TUint(RThread().Id()), cleanup);
+ if (cleanup)
+ {
+ TRAP(err, self->ThreadFnL());
+ //WT_LOG(_L("Worker thread %d deleting trapcleanup 0x%x"), TUint(RThread().Id()), cleanup);
+ delete cleanup;
+ }
+ if (!self->UsingSharedAllocator())
+ {
+ __UHEAP_MARKEND;
+ }
+ return err;
+ }
+
+void CWorkerThread::ThreadFnL()
+ {
+ CActiveScheduler* scheduler = new(ELeave) CActiveScheduler;
+ CleanupStack::PushL(scheduler);
+ CActiveScheduler::Install(scheduler);
+
+ CWorkerThreadDispatcher* dispatcher = new(ELeave) CWorkerThreadDispatcher(this);
+ CleanupStack::PushL(dispatcher);
+ iDispatchStatus = &dispatcher->iStatus;
+ RThread::Rendezvous(KErrNone);
+ iAsWait.Start();
+ CleanupStack::PopAndDestroy(2, scheduler); // dispatcher, scheduler
+ }
+
+void CWorkerThread::Shutdown()
+ {
+ // Can be called from (potentially) any thread
+ WT_LOG(_L("Shutting down worker thread %d whose exittype is %d"), TUint(GetThreadId()), iWorkerThread.ExitType());
+
+ if (Busy())
+ {
+ WT_LOG(_L("Thread is still busy - killing it"));
+ iWorkerThread.Kill(KErrAbort);
+ // The thread death watcher should take care of everything else, eventually
+ }
+ else if (iWorkerThread.ExitType() == EExitPending)
+ {
+ TRequestStatus stat;
+ iWorkerThread.Logon(stat);
+ iWorkerThread.RequestComplete(iDispatchStatus, KErrCancel);
+ User::WaitForRequest(stat);
+ WT_LOG(_L("Shut down worker %d exit=%d"), TUint(GetThreadId()), iWorkerThread.ExitType());
+ }
+ }
+
+void CWorkerThread::RegisterThreadDeathWatcherOnCurrentThread()
+ {
+ iThreadDeathWatcher->StartWatching();
+ }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/core/src/worker_thread.h Wed Jul 28 16:41:19 2010 +0100
@@ -0,0 +1,131 @@
+// worker_thread.h
+//
+// Copyright (c) 2010 Accenture. All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Accenture - Initial contribution
+//
+
+#ifndef WORKER_THREAD_H
+#define WORKER_THREAD_H
+
+#include <e32base.h>
+
+class MThreadedTask
+ {
+public:
+ virtual TThreadId GetThreadId() const=0;
+ virtual TInt ExecuteTask(TRequestStatus& aCompletionStatus)=0;
+ virtual void AbortTask()=0; // If you decide you don't want to call ExecuteTask
+ };
+
+class MTaskRunner
+ {
+public:
+ typedef void (*TThreadFunctionL)(TAny*);
+ virtual MThreadedTask* NewTaskInSeparateThreadL(const TDesC& aThreadName, TBool aSharedHeap, TThreadFunctionL aThreadFunction, TAny* aThreadContext)=0;
+ };
+
+class CWorkerThread;
+
+class CThreadPool : public CActive, public MTaskRunner
+ {
+public:
+ static CThreadPool* NewL();
+ ~CThreadPool();
+ void WorkerDied(CWorkerThread* aWorker);
+ void WorkerFinished(CWorkerThread* aWorker);
+ void CleanupAnyWorkersSharingAllocator(RAllocator* aAllocator);
+
+public: // From MTaskRunner
+ virtual MThreadedTask* NewTaskInSeparateThreadL(const TDesC& aThreadName, TBool aSharedHeap, TThreadFunctionL aThreadFunction, TAny* aThreadContext);
+
+private:
+ CThreadPool();
+ void ConstructL();
+ static TInt TimerCallback(TAny* aSelf);
+ void PerformHouseKeeping();
+ void RunL();
+ void DoCancel();
+ void SignalSelf();
+
+ void Lock();
+ void LockLC();
+ void SwitchToThreadPoolHeap();
+ void RestoreHeap();
+ void Unlock();
+ static void DoUnlock(TAny* aLock);
+
+private:
+ RFastLock iLock; // Thread pool is shared between eg fshell main thread and any source threads, so needs locking around WorkerFinished and NewTaskInSeparateThreadL etc
+ RAllocator* iThreadPoolAllocator; // The thread pool can be modified from a thread that isn't using the main heap, so we need to track that and use User::SwitchAllocator as needed
+ RAllocator* iTempThreadAllocator; // Only used while lock is held
+ RPointerArray<CWorkerThread> iThreads;
+ TInt iTaskCounter;
+ RThread iMainThread;
+ TInt iPendingCallbacks;
+ CPeriodic* iIdleTimer;
+ TInt iCountThreadsCreated; // This is for statistics gathering, not involved in the logic
+ RArray<CWorkerThread*> iPendingThreadLogons; // Not owned
+ };
+
+
+class CThreadDeathWatcher;
+
+class CWorkerThread : public CBase, public MThreadedTask
+ {
+public:
+ static CWorkerThread* NewLC(CThreadPool* aParentPool, RAllocator* aSharedAllocator);
+ // For safety these 2 need to be called with the thread pool lock held
+ TBool Busy() const;
+ void SetBusy(TBool aBusy);
+
+ TBool UsingSharedAllocator() const;
+ RAllocator* SharedAllocator() const;
+ 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; }
+
+public: // From MThreadedTask
+ TThreadId GetThreadId() const;
+ TInt ExecuteTask(TRequestStatus& aCompletionStatus);
+ void AbortTask();
+
+public: // For CThreadDeathWatcher to use
+ void ThreadDied();
+
+private:
+ CWorkerThread(CThreadPool* aParentPool, RAllocator* aSharedAllocator);
+ void ConstructL();
+ void ThreadRun();
+ static TInt ThreadFn(TAny* aSelf);
+ void ThreadFnL();
+
+private:
+ CThreadPool* iParentPool;
+ RThread iWorkerThread;
+ RAllocator* iSharedAllocator;
+ TRequestStatus* iDispatchStatus;
+ CActiveSchedulerWait iAsWait; // Use a CActiveSchedulerWait to protect ourselves from whatever aThreadFunction may try and do
+ CThreadDeathWatcher* iThreadDeathWatcher;
+ TBool iBusy;
+
+ // Things specific to the task currently being executed
+ RThread iParentThread; // Worker needs this to signal completion to us
+ const TDesC* iName;
+ MTaskRunner::TThreadFunctionL iFn;
+ TAny* iContext;
+ TInt iTaskId;
+ TRequestStatus* iCompletionStatus;
+
+ friend class CWorkerThreadDispatcher;
+ };
+
+#endif
--- a/documentation/common_mmh.pod Mon Jul 26 17:19:00 2010 +0100
+++ b/documentation/common_mmh.pod Wed Jul 28 16:41:19 2010 +0100
@@ -202,7 +202,7 @@
Indicates that the baseline is recent enough to be aware of the flexible memory model. It doesn't necessarily mean that the flexible model is actually the one being used. It generally guards kernel APIs that weren't introduced until the FMM was (such as Kern::ChunkUserBase).
-=item FSHELL_SPCRE_SUPPORT
+=item FSHELL_[NO_]SPCRE_SUPPORT
The regular expression libraries spcre.dll and libpcre.dll are available.
@@ -214,6 +214,10 @@
The MemSpy Engine APIs are available.
+=item FSHELL_TESTEXECUTE_SUPPORT
+
+The TestExecute APIs are available.
+
=back
A couple of further macros follow - these are specifically for platforms that provide an custom implementation of the relevant command rather than using fshell's built-in version.
--- a/documentation/fshell_builddocs.mk Mon Jul 26 17:19:00 2010 +0100
+++ b/documentation/fshell_builddocs.mk Wed Jul 28 16:41:19 2010 +0100
@@ -9,7 +9,7 @@
# Initial Contributors:
# Accenture - Initial contribution
#
-MAKMAKE :
+BLD :
..\tools\fsh-builddocs -i $(EPOCROOT)epoc32/include pod-list.txt
RELEASABLES :
@@ -18,4 +18,4 @@
CLEAN :
..\tools\fsh-builddocs -i $(EPOCROOT)epoc32/include pod-list.txt -c
-BLD FREEZE LIB CLEANLIB RESOURCE SAVESPACE FINAL :
+MAKMAKE FREEZE LIB CLEANLIB RESOURCE SAVESPACE FINAL :
--- a/documentation/getting_started.pod Mon Jul 26 17:19:00 2010 +0100
+++ b/documentation/getting_started.pod Wed Jul 28 16:41:19 2010 +0100
@@ -14,6 +14,8 @@
__END__
+=head1 Getting started with FShell
+
=head1 Retreiving the FShell Source Code
I<Todo - add instructions for accessing the Mercurial repository.>
@@ -32,7 +34,7 @@
=back
-Each of these platforms have a separate directory with F<\fshell\build>. In some platforms there may be further separate sub-directories for specific variants of the platform. You need to identify a suitable directory (that contains a file named F<bld.inf>) from which to build from. Open a command prompt and C<cd> to this directory.
+Each of these platforms have a separate directory with F<\fshell\build>. In some platforms there may be further separate sub-directories for specific variants of the platform. You need to identify a suitable directory (that contains a file named F<bld.inf>) from which to build from. For example to build for the Symbian^3 release you need to build from F<\fshell\build\sf\3>. Open a command prompt and C<cd> to this directory.
You also need to identify the type of binaries you want to build. Common ones are:
@@ -48,11 +50,11 @@
=item * C<gcce>
-Used for running fshell on either physical or simulated ARM hardware. Requires an installation of the GCC-E. I<Todo - does the SF provide builds of GCC-E?>
+Used for running fshell on either physical or simulated ARM hardware. Requires an installation of the GCC crosscompiler (for arm-none-symbianelf) - see here for more information: http://developer.symbian.org/wiki/index.php/The_GCCE_toolchain_initiative.
=back
-Lastly, you need to identify the build variant you plan to use, either debug (C<udeb>) or release (C<urel>). Commonly, C<udeb> is used on the emulator whereas C<urel> is used on target hardward. For the purposes of this introduction we will assume that you will be building C<winscw udeb> and C<armv5 urel>.
+Lastly, you need to identify the build variant you plan to use, either debug (C<udeb>) or release (C<urel>). Commonly, C<udeb> is used on the emulator whereas C<urel> is used on target hardware. For the purposes of this introduction we will assume that you will be building C<winscw udeb> and C<armv5 urel>.
=head2 SBSv1 (aka abld)
@@ -62,7 +64,25 @@
=head2 SBSv2 (aka Raptor)
- M:\fshell\build\sf\2>sbs -c armv5_urel -c winscw_udeb
+ M:\fshell\build\sf\2>sbs -c armv5_urel
+ M:\fshell\build\sf\2>sbs -c winscw_udeb
+
+I<Note:> There are currently some concurrency issues in the build process that mean we don't recommend building multiple platforms in a single invocation of sbs. Ie do the above rather than C<sbs -c armv5_urel -c winscw_udeb>. In some cases you may even need to add the option C<-j 1> to sbs to have it run completely single-threaded. We'll fix this as soon as we can!
+
+=head2 Notes on building for GCC
+
+Fshell has only been tested to build with GCC-E 4.4.1, and the legacy 3.4.2.
+
+I<Note:> The SF website appears to be out of date with regards to how to build with GCC-E on Raptor - at the time of writing the way to build is as follows:
+
+ S:\fshell\build\sf\3>set SBS_GCCE432BIN=Nothing
+ S:\fshell\build\sf\3>set SBS_GCCE433BIN=Nothing
+ S:\fshell\build\sf\3>set SBS_GCCE441BIN=c:/apps/gcce/bin
+ S:\fshell\build\sf\3>sbs -c arm.v5.urel.gcce4_4_1
+
+You may also need to update the compiler support libraries - I had to unzip rt_3_1_bin.zip from \sf\os\kernelhwsrv\kernel\eka\compsupp from a different baseline. (Unzip over \epoc32\release\armv5). The working libraries I used had datestamp of 28/1/2009, the non-working ones were 1/1/2009.
+
+There's one final gotcha if you want to build with GCC-E - at the time of writing the makefiles produced by SBS for PIPS executables is incorrect, therefore you need to add C<#define FSHELL_NO_PIPS_SUPPORT> to \fshell\build\sf\platform_shared.mmh. Hopefully this is a temporary measure.
=head1 Installation
--- a/libraries/iosrv/bwins/iocliu.def Mon Jul 26 17:19:00 2010 +0100
+++ b/libraries/iosrv/bwins/iocliu.def Wed Jul 28 16:41:19 2010 +0100
@@ -332,11 +332,11 @@
?Reset@CTextBuffer@IoUtils@@UAEXXZ @ 331 NONAME ; void IoUtils::CTextBuffer::Reset(void)
?Reset@CTextFormatter@IoUtils@@UAEXXZ @ 332 NONAME ; void IoUtils::CTextFormatter::Reset(void)
?Run@RChildProcess@IoUtils@@QAEXAAVTRequestStatus@@@Z @ 333 NONAME ; void IoUtils::RChildProcess::Run(class TRequestStatus &)
- ?RunCommand@CCommandBase@IoUtils@@QAEHAAVRIoSession@@AAVRIoReadHandle@@AAVRIoWriteHandle@@2PBVTDesC16@@PAVCEnvironment@2@@Z @ 334 NONAME ; int IoUtils::CCommandBase::RunCommand(class RIoSession &, class RIoReadHandle &, class RIoWriteHandle &, class RIoWriteHandle &, class TDesC16 const *, class IoUtils::CEnvironment *)
- ?RunCommand@CCommandBase@IoUtils@@QAEHPBVTDesC16@@PAVCEnvironment@2@@Z @ 335 NONAME ; int IoUtils::CCommandBase::RunCommand(class TDesC16 const *, class IoUtils::CEnvironment *)
- ?RunCommand@CCommandBase@IoUtils@@QAEHXZ @ 336 NONAME ; int IoUtils::CCommandBase::RunCommand(void)
- ?RunCommand@CCommandBase@IoUtils@@QAEXAAVRIoSession@@AAVRIoReadHandle@@AAVRIoWriteHandle@@2PBVTDesC16@@PAVCEnvironment@2@PAVMCommandBaseObserver@2@@Z @ 337 NONAME ; void IoUtils::CCommandBase::RunCommand(class RIoSession &, class RIoReadHandle &, class RIoWriteHandle &, class RIoWriteHandle &, class TDesC16 const *, class IoUtils::CEnvironment *, class IoUtils::MCommandBaseObserver *)
- ?RunCommandL@CCommandBase@IoUtils@@QAEXAAVRIoSession@@AAVRIoReadHandle@@AAVRIoWriteHandle@@2PBVTDesC16@@PAVCEnvironment@2@@Z @ 338 NONAME ; void IoUtils::CCommandBase::RunCommandL(class RIoSession &, class RIoReadHandle &, class RIoWriteHandle &, class RIoWriteHandle &, class TDesC16 const *, class IoUtils::CEnvironment *)
+ ?RunCommand@CCommandBase@IoUtils@@QAEHAAVRIoSession@@AAVRIoReadHandle@@AAVRIoWriteHandle@@2PBVTDesC16@@PAVCEnvironment@2@@Z @ 334 NONAME ABSENT ; int IoUtils::CCommandBase::RunCommand(class RIoSession &, class RIoReadHandle &, class RIoWriteHandle &, class RIoWriteHandle &, class TDesC16 const *, class IoUtils::CEnvironment *)
+ ?RunCommand@CCommandBase@IoUtils@@QAEHPBVTDesC16@@PAVCEnvironment@2@@Z @ 335 NONAME ABSENT ; int IoUtils::CCommandBase::RunCommand(class TDesC16 const *, class IoUtils::CEnvironment *)
+ ?RunCommand@CCommandBase@IoUtils@@QAEHXZ @ 336 NONAME ABSENT ; int IoUtils::CCommandBase::RunCommand(void)
+ ?RunCommand@CCommandBase@IoUtils@@QAEXAAVRIoSession@@AAVRIoReadHandle@@AAVRIoWriteHandle@@2PBVTDesC16@@PAVCEnvironment@2@PAVMCommandBaseObserver@2@@Z @ 337 NONAME ABSENT ; void IoUtils::CCommandBase::RunCommand(class RIoSession &, class RIoReadHandle &, class RIoWriteHandle &, class RIoWriteHandle &, class TDesC16 const *, class IoUtils::CEnvironment *, class IoUtils::MCommandBaseObserver *)
+ ?RunCommandL@CCommandBase@IoUtils@@QAEXAAVRIoSession@@AAVRIoReadHandle@@AAVRIoWriteHandle@@2PBVTDesC16@@PAVCEnvironment@2@@Z @ 338 NONAME ABSENT ; void IoUtils::CCommandBase::RunCommandL(class RIoSession &, class RIoReadHandle &, class RIoWriteHandle &, class RIoWriteHandle &, class TDesC16 const *, class IoUtils::CEnvironment *)
?RunCommandL@CCommandBase@IoUtils@@QAEXPBVTDesC16@@PAVCEnvironment@2@@Z @ 339 NONAME ; void IoUtils::CCommandBase::RunCommandL(class TDesC16 const *, class IoUtils::CEnvironment *)
?RunCommandL@CCommandBase@IoUtils@@QAEXXZ @ 340 NONAME ; void IoUtils::CCommandBase::RunCommandL(void)
?RunError@CCommandBase@IoUtils@@MAEHH@Z @ 341 NONAME ; int IoUtils::CCommandBase::RunError(int)
--- a/libraries/iosrv/client/command_base.cpp Mon Jul 26 17:19:00 2010 +0100
+++ b/libraries/iosrv/client/command_base.cpp Wed Jul 28 16:41:19 2010 +0100
@@ -1274,23 +1274,11 @@
// CCommandBase.
//
-EXPORT_C TInt CCommandBase::RunCommand()
- {
- TRAPD(err, RunCommandL());
- return err;
- }
-
EXPORT_C void CCommandBase::RunCommandL()
{
RunCommandL(NULL, NULL);
}
-EXPORT_C TInt CCommandBase::RunCommand(const TDesC* aCommandLine, CEnvironment* aEnv)
- {
- TRAPD(err, RunCommandL(aCommandLine, aEnv));
- return err;
- }
-
NONSHARABLE_CLASS(TExitController) : public MCommandBaseObserver
{
public:
@@ -1516,27 +1504,7 @@
}
}
-EXPORT_C TInt CCommandBase::RunCommand(RIoSession& aIoSession, RIoReadHandle& aStdin, RIoWriteHandle& aStdout, RIoWriteHandle& aStderr, const TDesC* aCommandLine, CEnvironment* aEnv)
- {
- TExitController exitController;
- RunCommand(aIoSession, aStdin, aStdout, aStderr, aCommandLine, aEnv, &exitController);
- if (exitController.IsComplete())
- {
- return exitController.CompletionError();
- }
- else
- {
- exitController.WaitForCompletion();
- return exitController.CompletionError();
- }
- }
-
-EXPORT_C void CCommandBase::RunCommandL(RIoSession& aIoSession, RIoReadHandle& aStdin, RIoWriteHandle& aStdout, RIoWriteHandle& aStderr, const TDesC* aCommandLine, CEnvironment* aEnv)
- {
- User::LeaveIfError(RunCommand(aIoSession, aStdin, aStdout, aStderr, aCommandLine, aEnv));
- }
-
-EXPORT_C void CCommandBase::RunCommand(RIoSession& aIoSession, RIoReadHandle& aStdin, RIoWriteHandle& aStdout, RIoWriteHandle& aStderr, const TDesC* aCommandLine, CEnvironment* aEnv, MCommandBaseObserver* aObserver)
+void CCommandBase::RunCommand(RIoSession& aIoSession, RIoReadHandle& aStdin, RIoWriteHandle& aStdout, RIoWriteHandle& aStderr, const TDesC* aCommandLine, CEnvironment* aEnv, MCommandBaseObserver* aObserver)
{
TRAPD(err, RunCommandL(aIoSession, aStdin, aStdout, aStderr, aCommandLine, aEnv, aObserver));
if (err)
--- a/libraries/iosrv/eabi/iocliu.def Mon Jul 26 17:19:00 2010 +0100
+++ b/libraries/iosrv/eabi/iocliu.def Wed Jul 28 16:41:19 2010 +0100
@@ -187,14 +187,14 @@
_ZN7IoUtils12CCommandBase10Extension_EjRPvS1_ @ 186 NONAME
_ZN7IoUtils12CCommandBase10HaveStaticEv @ 187 NONAME
_ZN7IoUtils12CCommandBase10PrintErrorEi11TRefByValueIK7TDesC16Ez @ 188 NONAME
- _ZN7IoUtils12CCommandBase10RunCommandEPK7TDesC16PNS_12CEnvironmentE @ 189 NONAME
- _ZN7IoUtils12CCommandBase10RunCommandER10RIoSessionR13RIoReadHandleR14RIoWriteHandleS6_PK7TDesC16PNS_12CEnvironmentE @ 190 NONAME
- _ZN7IoUtils12CCommandBase10RunCommandER10RIoSessionR13RIoReadHandleR14RIoWriteHandleS6_PK7TDesC16PNS_12CEnvironmentEPNS_20MCommandBaseObserverE @ 191 NONAME
- _ZN7IoUtils12CCommandBase10RunCommandEv @ 192 NONAME
+ _ZN7IoUtils12CCommandBase10RunCommandEPK7TDesC16PNS_12CEnvironmentE @ 189 NONAME ABSENT
+ _ZN7IoUtils12CCommandBase10RunCommandER10RIoSessionR13RIoReadHandleR14RIoWriteHandleS6_PK7TDesC16PNS_12CEnvironmentE @ 190 NONAME ABSENT
+ _ZN7IoUtils12CCommandBase10RunCommandER10RIoSessionR13RIoReadHandleR14RIoWriteHandleS6_PK7TDesC16PNS_12CEnvironmentEPNS_20MCommandBaseObserverE @ 191 NONAME ABSENT
+ _ZN7IoUtils12CCommandBase10RunCommandEv @ 192 NONAME ABSENT
_ZN7IoUtils12CCommandBase11DisplayHelpEv @ 193 NONAME
_ZN7IoUtils12CCommandBase11HandleLeaveEi @ 194 NONAME
_ZN7IoUtils12CCommandBase11RunCommandLEPK7TDesC16PNS_12CEnvironmentE @ 195 NONAME
- _ZN7IoUtils12CCommandBase11RunCommandLER10RIoSessionR13RIoReadHandleR14RIoWriteHandleS6_PK7TDesC16PNS_12CEnvironmentE @ 196 NONAME
+ _ZN7IoUtils12CCommandBase11RunCommandLER10RIoSessionR13RIoReadHandleR14RIoWriteHandleS6_PK7TDesC16PNS_12CEnvironmentE @ 196 NONAME ABSENT
_ZN7IoUtils12CCommandBase11RunCommandLEv @ 197 NONAME
_ZN7IoUtils12CCommandBase11StdinChangeEj @ 198 NONAME
_ZN7IoUtils12CCommandBase12GetHelpTextLEv @ 199 NONAME
--- a/libraries/iosrv/inc/ioutils.h Mon Jul 26 17:19:00 2010 +0100
+++ b/libraries/iosrv/inc/ioutils.h Wed Jul 28 16:41:19 2010 +0100
@@ -666,13 +666,9 @@
};
public:
IMPORT_C ~CCommandBase();
- IMPORT_C TInt RunCommand();
IMPORT_C void RunCommandL();
- IMPORT_C TInt RunCommand(const TDesC* aCommandLine, CEnvironment* aEnv);
IMPORT_C void RunCommandL(const TDesC* aCommandLine, CEnvironment* aEnv);
- IMPORT_C TInt RunCommand(RIoSession& aIoSession, RIoReadHandle& aStdin, RIoWriteHandle& aStdout, RIoWriteHandle& aStderr, const TDesC* aCommandLine, CEnvironment* aEnv);
- IMPORT_C void RunCommandL(RIoSession& aIoSession, RIoReadHandle& aStdin, RIoWriteHandle& aStdout, RIoWriteHandle& aStderr, const TDesC* aCommandLine, CEnvironment* aEnv);
- IMPORT_C void RunCommand(RIoSession& aIoSession, RIoReadHandle& aStdin, RIoWriteHandle& aStdout, RIoWriteHandle& aStderr, const TDesC* aCommandLine, CEnvironment* aEnv, MCommandBaseObserver* aObserver);
+ void RunCommand(RIoSession& aIoSession, RIoReadHandle& aStdin, RIoWriteHandle& aStdout, RIoWriteHandle& aStderr, const TDesC* aCommandLine, CEnvironment* aEnv, MCommandBaseObserver* aObserver);
IMPORT_C TUint Flags() const;
IMPORT_C void SetFlags(TUint aFlags);
IMPORT_C const RFs& Fs() const;
--- a/libraries/memoryaccess/MemoryAccess.mmp Mon Jul 26 17:19:00 2010 +0100
+++ b/libraries/memoryaccess/MemoryAccess.mmp Wed Jul 28 16:41:19 2010 +0100
@@ -36,7 +36,9 @@
source hwbreak.cia
#endif
-#ifdef EABI
+#if defined(EABI) && !defined(GCCE)
+macro SUPPORT_BREAKPOINT_STUFF
+// This doesn't build with GCC-E
source TrkNextInstructionAfterPC.cpp
#endif
--- a/libraries/memoryaccess/fdebuggerkernel.cpp Mon Jul 26 17:19:00 2010 +0100
+++ b/libraries/memoryaccess/fdebuggerkernel.cpp Wed Jul 28 16:41:19 2010 +0100
@@ -1496,7 +1496,7 @@
TInt DDebuggerEventHandler::MoveBreakpointToNextInstructionForThread(DThread* aThread, SBreakpoint* aBreakpoint)
{
-#ifdef __EABI__
+#ifdef SUPPORT_BREAKPOINT_STUFF
ASSERT_BREAKPOINT_LOCKED();
TUint32 notUsed = 0;
TBool aModeChange = EFalse;
--- a/libraries/spcre/bwins/spcreu.def Mon Jul 26 17:19:00 2010 +0100
+++ b/libraries/spcre/bwins/spcreu.def Wed Jul 28 16:41:19 2010 +0100
@@ -4,32 +4,32 @@
?GlobalReplaceL@CRegEx@@QBEHABVTDesC8@@AAVTDes8@@@Z @ 3 NONAME ; int CRegEx::GlobalReplaceL(class TDesC8 const &, class TDes8 &) const
?FindAndConsumeL@CRegEx@@QBEHAAVTDes8@@@Z @ 4 NONAME ; int CRegEx::FindAndConsumeL(class TDes8 &) const
?ParseTUint8Hex@TRegExArg@@SAHABVTDesC8@@PAX@Z @ 5 NONAME ; int TRegExArg::ParseTUint8Hex(class TDesC8 const &, void *)
- ??0TRegExArg@@QAE@PAVTChar@@P6AHABVTDesC8@@PAX@Z@Z @ 6 NONAME ; TRegExArg::TRegExArg(class TChar *, int (*)(class TDesC8 const &, void *))
- ??0TRegExArg@@QAE@PAJ@Z @ 7 NONAME ; TRegExArg::TRegExArg(long *)
- ??0TRegExArg@@QAE@PAJP6AHABVTDesC8@@PAX@Z@Z @ 8 NONAME ; TRegExArg::TRegExArg(long *, int (*)(class TDesC8 const &, void *))
+ ??0TRegExArg@@QAE@PAVTChar@@P6AHABVTDesC8@@PAX@Z@Z @ 6 NONAME ABSENT ; TRegExArg::TRegExArg(class TChar *, int (*)(class TDesC8 const &, void *))
+ ??0TRegExArg@@QAE@PAJ@Z @ 7 NONAME ABSENT ; TRegExArg::TRegExArg(long *)
+ ??0TRegExArg@@QAE@PAJP6AHABVTDesC8@@PAX@Z@Z @ 8 NONAME ABSENT ; TRegExArg::TRegExArg(long *, int (*)(class TDesC8 const &, void *))
??0TRegExOptions@@QAE@XZ @ 9 NONAME ; TRegExOptions::TRegExOptions(void)
?PartialMatchL@CRegEx@@QBEHABVTDesC8@@ABVTRegExArg@@11@Z @ 10 NONAME ; int CRegEx::PartialMatchL(class TDesC8 const &, class TRegExArg const &, class TRegExArg const &, class TRegExArg const &) const
?DoMatchL@CRegEx@@QBEHABVTDesC8@@W4TAnchor@1@AAHABV?$RPointerArray@$$CBVTRegExArg@@@@@Z @ 11 NONAME ; int CRegEx::DoMatchL(class TDesC8 const &, enum CRegEx::TAnchor, int &, class RPointerArray<class TRegExArg const > const &) const
?ParseTReal32@TRegExArg@@CAHABVTDesC8@@PAX@Z @ 12 NONAME ; int TRegExArg::ParseTReal32(class TDesC8 const &, void *)
- ??0TRegExArg@@QAE@PAVTPtrC8@@P6AHABVTDesC8@@PAX@Z@Z @ 13 NONAME ; TRegExArg::TRegExArg(class TPtrC8 *, int (*)(class TDesC8 const &, void *))
- ??0TRegExArg@@QAE@PAE@Z @ 14 NONAME ; TRegExArg::TRegExArg(unsigned char *)
+ ??0TRegExArg@@QAE@PAVTPtrC8@@P6AHABVTDesC8@@PAX@Z@Z @ 13 NONAME ABSENT ; TRegExArg::TRegExArg(class TPtrC8 *, int (*)(class TDesC8 const &, void *))
+ ??0TRegExArg@@QAE@PAE@Z @ 14 NONAME ABSENT ; TRegExArg::TRegExArg(unsigned char *)
?ParseTInt16@TRegExArg@@CAHABVTDesC8@@PAX@Z @ 15 NONAME ; int TRegExArg::ParseTInt16(class TDesC8 const &, void *)
?FindAndConsumeL@CRegEx@@QBEHAAVTDes8@@ABVTRegExArg@@111@Z @ 16 NONAME ; int CRegEx::FindAndConsumeL(class TDes8 &, class TRegExArg const &, class TRegExArg const &, class TRegExArg const &, class TRegExArg const &) const
?FullMatchL@CRegEx@@QBEHABVTDesC8@@ABVTRegExArg@@@Z @ 17 NONAME ; int CRegEx::FullMatchL(class TDesC8 const &, class TRegExArg const &) const
?FindAndConsumeL@CRegEx@@QBEHAAVTDes8@@ABVTRegExArg@@11@Z @ 18 NONAME ; int CRegEx::FindAndConsumeL(class TDes8 &, class TRegExArg const &, class TRegExArg const &, class TRegExArg const &) const
- ??0TRegExArg@@QAE@PAMP6AHABVTDesC8@@PAX@Z@Z @ 19 NONAME ; TRegExArg::TRegExArg(float *, int (*)(class TDesC8 const &, void *))
- ??0TRegExArg@@QAE@PACP6AHABVTDesC8@@PAX@Z@Z @ 20 NONAME ; TRegExArg::TRegExArg(signed char *, int (*)(class TDesC8 const &, void *))
- ??0TRegExArg@@QAE@PAVTDes8@@P6AHABVTDesC8@@PAX@Z@Z @ 21 NONAME ; TRegExArg::TRegExArg(class TDes8 *, int (*)(class TDesC8 const &, void *))
+ ??0TRegExArg@@QAE@PAMP6AHABVTDesC8@@PAX@Z@Z @ 19 NONAME ABSENT ; TRegExArg::TRegExArg(float *, int (*)(class TDesC8 const &, void *))
+ ??0TRegExArg@@QAE@PACP6AHABVTDesC8@@PAX@Z@Z @ 20 NONAME ABSENT ; TRegExArg::TRegExArg(signed char *, int (*)(class TDesC8 const &, void *))
+ ??0TRegExArg@@QAE@PAVTDes8@@P6AHABVTDesC8@@PAX@Z@Z @ 21 NONAME ABSENT ; TRegExArg::TRegExArg(class TDes8 *, int (*)(class TDesC8 const &, void *))
?FullMatchL@CRegEx@@QBEHABVTDesC8@@ABVTRegExArg@@111@Z @ 22 NONAME ; int CRegEx::FullMatchL(class TDesC8 const &, class TRegExArg const &, class TRegExArg const &, class TRegExArg const &, class TRegExArg const &) const
?ExtractL@CRegEx@@QBEHABVTDesC8@@0AAVTDes8@@@Z @ 23 NONAME ; int CRegEx::ExtractL(class TDesC8 const &, class TDesC8 const &, class TDes8 &) const
- ??0TRegExArg@@QAE@PAVTChar@@@Z @ 24 NONAME ; TRegExArg::TRegExArg(class TChar *)
+ ??0TRegExArg@@QAE@PAVTChar@@@Z @ 24 NONAME ABSENT ; TRegExArg::TRegExArg(class TChar *)
?ParseTInt@TRegExArg@@CAHABVTDesC8@@PAX@Z @ 25 NONAME ; int TRegExArg::ParseTInt(class TDesC8 const &, void *)
?ReplaceL@CRegEx@@QBEHABVTDesC8@@AAVTDes8@@@Z @ 26 NONAME ; int CRegEx::ReplaceL(class TDesC8 const &, class TDes8 &) const
?ParseTInt32@TRegExArg@@CAHABVTDesC8@@PAX@Z @ 27 NONAME ; int TRegExArg::ParseTInt32(class TDesC8 const &, void *)
- ??0TRegExArg@@QAE@PAG@Z @ 28 NONAME ; TRegExArg::TRegExArg(unsigned short *)
+ ??0TRegExArg@@QAE@PAG@Z @ 28 NONAME ABSENT ; TRegExArg::TRegExArg(unsigned short *)
?ParseTInt64Hex@TRegExArg@@SAHABVTDesC8@@PAX@Z @ 29 NONAME ; int TRegExArg::ParseTInt64Hex(class TDesC8 const &, void *)
?FullMatchL@CRegEx@@QBEHABVTDesC8@@@Z @ 30 NONAME ; int CRegEx::FullMatchL(class TDesC8 const &) const
- ??0TRegExArg@@QAE@PAK@Z @ 31 NONAME ; TRegExArg::TRegExArg(unsigned long *)
+ ??0TRegExArg@@QAE@PAK@Z @ 31 NONAME ABSENT ; TRegExArg::TRegExArg(unsigned long *)
?PartialMatchL@CRegEx@@QBEHABVTDesC8@@ABVTRegExArg@@111@Z @ 32 NONAME ; int CRegEx::PartialMatchL(class TDesC8 const &, class TRegExArg const &, class TRegExArg const &, class TRegExArg const &, class TRegExArg const &) const
??0TRegExArg@@QAE@PAIP6AHABVTDesC8@@PAX@Z@Z @ 33 NONAME ; TRegExArg::TRegExArg(unsigned int *, int (*)(class TDesC8 const &, void *))
??0TRegExArg@@QAE@PANP6AHABVTDesC8@@PAX@Z@Z @ 34 NONAME ; TRegExArg::TRegExArg(double *, int (*)(class TDesC8 const &, void *))
@@ -39,15 +39,15 @@
?FindAndConsumeL@CRegEx@@QBEHAAVTDes8@@ABVTRegExArg@@1@Z @ 38 NONAME ; int CRegEx::FindAndConsumeL(class TDes8 &, class TRegExArg const &, class TRegExArg const &) const
?DoMatchL@CRegEx@@QBEHABVTDesC8@@W4TAnchor@1@AAHABVTRegExArg@@333@Z @ 39 NONAME ; int CRegEx::DoMatchL(class TDesC8 const &, enum CRegEx::TAnchor, int &, class TRegExArg const &, class TRegExArg const &, class TRegExArg const &, class TRegExArg const &) const
?ConsumeL@CRegEx@@QBEHAAVTDes8@@ABVTRegExArg@@1@Z @ 40 NONAME ; int CRegEx::ConsumeL(class TDes8 &, class TRegExArg const &, class TRegExArg const &) const
- ??0TRegExArg@@QAE@PAFP6AHABVTDesC8@@PAX@Z@Z @ 41 NONAME ; TRegExArg::TRegExArg(short *, int (*)(class TDesC8 const &, void *))
- ??0TRegExArg@@QAE@PAN@Z @ 42 NONAME ; TRegExArg::TRegExArg(double *)
+ ??0TRegExArg@@QAE@PAFP6AHABVTDesC8@@PAX@Z@Z @ 41 NONAME ABSENT ; TRegExArg::TRegExArg(short *, int (*)(class TDesC8 const &, void *))
+ ??0TRegExArg@@QAE@PAN@Z @ 42 NONAME ABSENT ; TRegExArg::TRegExArg(double *)
?FindAndConsumeL@CRegEx@@QBEHAAVTDes8@@ABVTRegExArg@@@Z @ 43 NONAME ; int CRegEx::FindAndConsumeL(class TDes8 &, class TRegExArg const &) const
- ??0TRegExArg@@QAE@PAGP6AHABVTDesC8@@PAX@Z@Z @ 44 NONAME ; TRegExArg::TRegExArg(unsigned short *, int (*)(class TDesC8 const &, void *))
+ ??0TRegExArg@@QAE@PAGP6AHABVTDesC8@@PAX@Z@Z @ 44 NONAME ABSENT ; TRegExArg::TRegExArg(unsigned short *, int (*)(class TDesC8 const &, void *))
?DoMatchL@CRegEx@@QBEHABVTDesC8@@W4TAnchor@1@AAHABVTRegExArg@@@Z @ 45 NONAME ; int CRegEx::DoMatchL(class TDesC8 const &, enum CRegEx::TAnchor, int &, class TRegExArg const &) const
?ParseTInt8@TRegExArg@@CAHABVTDesC8@@PAX@Z @ 46 NONAME ; int TRegExArg::ParseTInt8(class TDesC8 const &, void *)
?ParseTInt64Radix@TRegExArg@@CAHABVTDesC8@@PAXW4TRadix@@@Z @ 47 NONAME ; int TRegExArg::ParseTInt64Radix(class TDesC8 const &, void *, enum TRadix)
?ParseTInt64@TRegExArg@@CAHABVTDesC8@@PAX@Z @ 48 NONAME ; int TRegExArg::ParseTInt64(class TDesC8 const &, void *)
- ??0TRegExArg@@QAE@PAM@Z @ 49 NONAME ; TRegExArg::TRegExArg(float *)
+ ??0TRegExArg@@QAE@PAM@Z @ 49 NONAME ABSENT ; TRegExArg::TRegExArg(float *)
?ParseTUint@TRegExArg@@CAHABVTDesC8@@PAX@Z @ 50 NONAME ; int TRegExArg::ParseTUint(class TDesC8 const &, void *)
?DoMatchL@CRegEx@@QBEHABVTDesC8@@W4TAnchor@1@AAH@Z @ 51 NONAME ; int CRegEx::DoMatchL(class TDesC8 const &, enum CRegEx::TAnchor, int &) const
??1CRegEx@@UAE@XZ @ 52 NONAME ; CRegEx::~CRegEx(void)
@@ -68,10 +68,10 @@
?ParseTUint8Octal@TRegExArg@@SAHABVTDesC8@@PAX@Z @ 67 NONAME ; int TRegExArg::ParseTUint8Octal(class TDesC8 const &, void *)
?NewLC@CRegEx@@SAPAV1@ABVTDesC8@@@Z @ 68 NONAME ; class CRegEx * CRegEx::NewLC(class TDesC8 const &)
?ParseTDes8@TRegExArg@@CAHABVTDesC8@@PAX@Z @ 69 NONAME ; int TRegExArg::ParseTDes8(class TDesC8 const &, void *)
- ??0TRegExArg@@QAE@PAI@Z @ 70 NONAME ; TRegExArg::TRegExArg(unsigned int *)
+ ??0TRegExArg@@QAE@PAI@Z @ 70 NONAME ABSENT ; TRegExArg::TRegExArg(unsigned int *)
?PartialMatchL@CRegEx@@QBEHABVTDesC8@@@Z @ 71 NONAME ; int CRegEx::PartialMatchL(class TDesC8 const &) const
?ParseTUint8@TRegExArg@@CAHABVTDesC8@@PAX@Z @ 72 NONAME ; int TRegExArg::ParseTUint8(class TDesC8 const &, void *)
- ??0TRegExArg@@QAE@PAKP6AHABVTDesC8@@PAX@Z@Z @ 73 NONAME ; TRegExArg::TRegExArg(unsigned long *, int (*)(class TDesC8 const &, void *))
+ ??0TRegExArg@@QAE@PAKP6AHABVTDesC8@@PAX@Z@Z @ 73 NONAME ABSENT ; TRegExArg::TRegExArg(unsigned long *, int (*)(class TDesC8 const &, void *))
?ParseTInt64Octal@TRegExArg@@SAHABVTDesC8@@PAX@Z @ 74 NONAME ; int TRegExArg::ParseTInt64Octal(class TDesC8 const &, void *)
?ConsumeL@CRegEx@@QBEHAAVTDes8@@ABVTRegExArg@@@Z @ 75 NONAME ; int CRegEx::ConsumeL(class TDes8 &, class TRegExArg const &) const
?ParseTUint8Radix@TRegExArg@@CAHABVTDesC8@@PAXW4TRadix@@@Z @ 76 NONAME ; int TRegExArg::ParseTUint8Radix(class TDesC8 const &, void *, enum TRadix)
@@ -79,23 +79,23 @@
??0TRegExArg@@QAE@PAH@Z @ 78 NONAME ; TRegExArg::TRegExArg(int *)
?ConsumeL@CRegEx@@QBEHAAVTDes8@@@Z @ 79 NONAME ; int CRegEx::ConsumeL(class TDes8 &) const
?ParseTUint16Hex@TRegExArg@@SAHABVTDesC8@@PAX@Z @ 80 NONAME ; int TRegExArg::ParseTUint16Hex(class TDesC8 const &, void *)
- ??0TRegExArg@@QAE@PA_J@Z @ 81 NONAME ; TRegExArg::TRegExArg(long long *)
+ ??0TRegExArg@@QAE@PA_J@Z @ 81 NONAME ABSENT ; TRegExArg::TRegExArg(long long *)
?NewL@CRegEx@@SAPAV1@ABVTDesC8@@@Z @ 82 NONAME ; class CRegEx * CRegEx::NewL(class TDesC8 const &)
?FullMatchL@CRegEx@@QBEHABVTDesC8@@ABVTRegExArg@@11@Z @ 83 NONAME ; int CRegEx::FullMatchL(class TDesC8 const &, class TRegExArg const &, class TRegExArg const &, class TRegExArg const &) const
?FullMatchL@CRegEx@@QBEHABVTDesC8@@ABVTRegExArg@@1@Z @ 84 NONAME ; int CRegEx::FullMatchL(class TDesC8 const &, class TRegExArg const &, class TRegExArg const &) const
?QuoteMetaL@CRegEx@@SAPAVHBufC8@@ABVTDesC8@@@Z @ 85 NONAME ; class HBufC8 * CRegEx::QuoteMetaL(class TDesC8 const &)
- ??0TRegExArg@@QAE@PAVTDes8@@@Z @ 86 NONAME ; TRegExArg::TRegExArg(class TDes8 *)
+ ??0TRegExArg@@QAE@PAVTDes8@@@Z @ 86 NONAME ABSENT ; TRegExArg::TRegExArg(class TDes8 *)
?ConsumeL@CRegEx@@QBEHAAVTDes8@@ABVTRegExArg@@11@Z @ 87 NONAME ; int CRegEx::ConsumeL(class TDes8 &, class TRegExArg const &, class TRegExArg const &, class TRegExArg const &) const
?NewLC@CRegEx@@SAPAV1@ABVTDesC8@@ABVTRegExOptions@@@Z @ 88 NONAME ; class CRegEx * CRegEx::NewLC(class TDesC8 const &, class TRegExOptions const &)
- ??0TRegExArg@@QAE@PAC@Z @ 89 NONAME ; TRegExArg::TRegExArg(signed char *)
- ??0TRegExArg@@QAE@PAVTPtrC8@@@Z @ 90 NONAME ; TRegExArg::TRegExArg(class TPtrC8 *)
- ??0TRegExArg@@QAE@PAF@Z @ 91 NONAME ; TRegExArg::TRegExArg(short *)
- ??0TRegExArg@@QAE@PAEP6AHABVTDesC8@@PAX@Z@Z @ 92 NONAME ; TRegExArg::TRegExArg(unsigned char *, int (*)(class TDesC8 const &, void *))
+ ??0TRegExArg@@QAE@PAC@Z @ 89 NONAME ABSENT ; TRegExArg::TRegExArg(signed char *)
+ ??0TRegExArg@@QAE@PAVTPtrC8@@@Z @ 90 NONAME ABSENT ; TRegExArg::TRegExArg(class TPtrC8 *)
+ ??0TRegExArg@@QAE@PAF@Z @ 91 NONAME ABSENT ; TRegExArg::TRegExArg(short *)
+ ??0TRegExArg@@QAE@PAEP6AHABVTDesC8@@PAX@Z@Z @ 92 NONAME ABSENT ; TRegExArg::TRegExArg(unsigned char *, int (*)(class TDesC8 const &, void *))
?ParseTPtrC8@TRegExArg@@CAHABVTDesC8@@PAX@Z @ 93 NONAME ; int TRegExArg::ParseTPtrC8(class TDesC8 const &, void *)
?DoMatchL@CRegEx@@QBEHABVTDesC8@@W4TAnchor@1@AAHABVTRegExArg@@33@Z @ 94 NONAME ; int CRegEx::DoMatchL(class TDesC8 const &, enum CRegEx::TAnchor, int &, class TRegExArg const &, class TRegExArg const &, class TRegExArg const &) const
??0TRegExOptions@@QAE@I@Z @ 95 NONAME ; TRegExOptions::TRegExOptions(unsigned int)
?ParseTUint32Radix@TRegExArg@@CAHABVTDesC8@@PAXW4TRadix@@@Z @ 96 NONAME ; int TRegExArg::ParseTUint32Radix(class TDesC8 const &, void *, enum TRadix)
- ??0TRegExArg@@QAE@PA_JP6AHABVTDesC8@@PAX@Z@Z @ 97 NONAME ; TRegExArg::TRegExArg(long long *, int (*)(class TDesC8 const &, void *))
- ??0TRegExArg@@QAE@PAHP6AHABVTDesC8@@PAX@Z@Z @ 98 NONAME ; TRegExArg::TRegExArg(int *, int (*)(class TDesC8 const &, void *))
+ ??0TRegExArg@@QAE@PA_JP6AHABVTDesC8@@PAX@Z@Z @ 97 NONAME ABSENT ; TRegExArg::TRegExArg(long long *, int (*)(class TDesC8 const &, void *))
+ ??0TRegExArg@@QAE@PAHP6AHABVTDesC8@@PAX@Z@Z @ 98 NONAME ABSENT ; TRegExArg::TRegExArg(int *, int (*)(class TDesC8 const &, void *))
?NewL@CRegEx@@SAPAV1@ABVTDesC16@@ABVTRegExOptions@@@Z @ 99 NONAME ; class CRegEx * CRegEx::NewL(class TDesC16 const &, class TRegExOptions const &)
--- a/libraries/spcre/inc/tregexarg.h Mon Jul 26 17:19:00 2010 +0100
+++ b/libraries/spcre/inc/tregexarg.h Wed Jul 28 16:41:19 2010 +0100
@@ -51,8 +51,8 @@
// Type-specific parsers
#define PCRE_MAKE_PARSER(type,name) \
- IMPORT_C TRegExArg(type* aType) : iArg(aType), iParser(name) { } \
- IMPORT_C TRegExArg(type* aType, Parser aParser) : iArg(aType), iParser(aParser) { }
+ inline TRegExArg(type* aType) : iArg(aType), iParser(name) { } \
+ inline TRegExArg(type* aType, Parser aParser) : iArg(aType), iParser(aParser) { }
PCRE_MAKE_PARSER(TChar, ParseTChar)
PCRE_MAKE_PARSER(TInt8, ParseTInt8)
--- a/libraries/spcre/libpcre/group/libpcre.mmp Mon Jul 26 17:19:00 2010 +0100
+++ b/libraries/spcre/libpcre/group/libpcre.mmp Wed Jul 28 16:41:19 2010 +0100
@@ -62,3 +62,9 @@
library euser.lib
library estlib.lib
+
+// Remove some of the many warnings that armcc doesn't like
+// Warning: #546-D: transfer of control bypasses initialization of:
+// Warning: #1134-D: literal treated as "long long"
+// Warning: C2874W: othercase may be used before being set
+option ARMCC --diag-supress 1134,2874,546
--- a/tools/fshell_copyright.pl Mon Jul 26 17:19:00 2010 +0100
+++ b/tools/fshell_copyright.pl Wed Jul 28 16:41:19 2010 +0100
@@ -48,7 +48,7 @@
my @newlines = ();
my $commentstart = "//";
- if ($name =~ /\.(pl|pm|script|pod|ini|idf|esk|mk|cif)/i || !($name =~ /\./))
+ if ($name =~ /\.(pl|pm|script|pod|ini|idf|esk|mk|cif|flm)/i || !($name =~ /\./))
{
# Need to use # instead of //
# Second part of conditional is for perl scripts that don't specify any ending