First cut of thread pools in fshell. Not fully working yet.
Removed 4 overloads of CCommandBase::RunCommand[L] that are no longer used at all, and changed one more to not be exported as it's only used internally to iocli.dll.
Added a thread pool (CThreadPool) to fshell so that thread commands no longer have to spawn a new thread every time they run. As part of this, CCommandWrapperBase now derives from CActive so that CThreadCommand can use its CActive-ness. CThreadCommand rejigged quite a bit to use CThreadPool (via MTaskRunner interface).
// command_wrappers.cpp
//
// Copyright (c) 2006 - 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
//
#include "command_wrappers.h"
#include "worker_thread.h"
//
// CCommandWrapperBase.
//
CCommandWrapperBase::CCommandWrapperBase()
: CActive(CActive::EPriorityStandard)
{
}
CCommandWrapperBase::~CCommandWrapperBase()
{
iStdin.Close();
iStdout.Close();
iStderr.Close();
delete iName;
}
void CCommandWrapperBase::BaseConstructL(const TDesC& aName)
{
iName = aName.AllocL();
}
RIoReadHandle& CCommandWrapperBase::CmndStdin()
{
return iStdin;
}
RIoWriteHandle& CCommandWrapperBase::CmndStdout()
{
return iStdout;
}
RIoWriteHandle& CCommandWrapperBase::CmndStderr()
{
return iStderr;
}
const TDesC& CCommandWrapperBase::CmndName() const
{
return *iName;
}
TInt CCommandWrapperBase::CmndReattachStdin(RIoEndPoint& aStdinEndPoint)
{
TInt isForeground = iStdin.IsForeground();
if (isForeground < 0)
{
return isForeground;
}
return aStdinEndPoint.Attach(iStdin, isForeground ? RIoEndPoint::EForeground : RIoEndPoint::EBackground);
}
TInt CCommandWrapperBase::CmndReattachStdout(RIoEndPoint& aStdoutEndPoint)
{
return aStdoutEndPoint.Attach(iStdout);
}
TInt CCommandWrapperBase::CmndReattachStderr(RIoEndPoint& aStderrEndPoint)
{
return aStderrEndPoint.Attach(iStderr);
}
TBool CCommandWrapperBase::CmndIsDisownable() const
{
return EFalse;
}
void CCommandWrapperBase::CmndDisown()
{
ASSERT(EFalse);
}
void CCommandWrapperBase::CmndRelease()
{
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, MTaskRunner* aTaskRunner)
{
CThreadCommand* self = new(ELeave) CThreadCommand(aCommandConstructor, aFlags, aTaskRunner);
CleanupStack::PushL(self);
self->ConstructL(aName);
CleanupStack::Pop(self);
return self;
}
CThreadCommand::~CThreadCommand()
{
Cancel();
delete iCommandLine;
iThread.Close();
}
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
}
void CThreadCommand::ConstructL(const TDesC& aName)
{
BaseConstructL(aName);
}
void CThreadCommand::DoCommandThreadStartL(TAny* aSelf)
{
CThreadCommand* self = static_cast<CThreadCommand*>(aSelf);
IoUtils::CEnvironment* env;
if (self->iFlags & CThreadCommand::EUpdateEnvironment)
{
env = self->iSuppliedEnv->CreateSharedEnvironmentL();
}
else
{
// A straight-forward copy
env = IoUtils::CEnvironment::NewL(*self->iSuppliedEnv);
}
CleanupStack::PushL(env);
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
}
void SetHandleOwnersL(TThreadId aThreadId, RIoReadHandle& aStdin, RIoWriteHandle& aStdout, RIoWriteHandle& aStderr)
{
User::LeaveIfError(aStdin.SetOwner(aThreadId));
User::LeaveIfError(aStdout.SetOwner(aThreadId));
User::LeaveIfError(aStderr.SetOwner(aThreadId));
};
TInt CThreadCommand::CmndRun(const TDesC& aCommandLine, IoUtils::CEnvironment& aEnv, MCommandObserver& aObserver, RIoSession&)
{
ASSERT(iObserver == 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)
{
iCommandLine = aCommandLine.Alloc();
if (!iCommandLine) err = KErrNoMemory;
}
if (!err)
{
err = iThread.Open(thread->GetThreadId());
}
if (!err)
{
iSuppliedEnv = &aEnv;
iObserver = &aObserver;
thread->ExecuteTask(iStatus);
SetActive();
}
else
{
thread->AbortTask();
}
return err;
}
void CThreadCommand::CmndForeground()
{
iThread.SetPriority(EPriorityAbsoluteForeground);
}
void CThreadCommand::CmndBackground()
{
iThread.SetPriority(EPriorityAbsoluteBackground);
}
void CThreadCommand::CmndKill()
{
if (iThread.Handle())
{
iThread.Kill(KErrAbort);
}
}
TInt CThreadCommand::CmndSuspend()
{
iThread.Suspend();
return KErrNone;
}
TInt CThreadCommand::CmndResume()
{
iThread.Resume();
return KErrNone;
}
TExitType CThreadCommand::CmndExitType() const
{
return iThread.ExitType();
}
TExitCategoryName CThreadCommand::CmndExitCategory() const
{
return iThread.ExitCategory();
}
void CThreadCommand::RunL()
{
iObserver->HandleCommandComplete(*this, iStatus.Int());
}
void CThreadCommand::DoCancel()
{
CmndKill(); // This is a bit drastic, but effective...
}
//
// CProcessCommand.
//
CProcessCommand* CProcessCommand::NewL(const TDesC& aExeName)
{
CProcessCommand* self = new(ELeave) CProcessCommand();
CleanupStack::PushL(self);
self->ConstructL(aExeName);
CleanupStack::Pop(self);
return self;
}
CProcessCommand* CProcessCommand::NewL(const TDesC& aExeName, RProcess& aProcess)
{
CProcessCommand* self = new(ELeave) CProcessCommand();
CleanupStack::PushL(self);
self->ConstructL(aExeName, &aProcess);
CleanupStack::Pop(self);
return self;
}
CProcessCommand::~CProcessCommand()
{
delete iWatcher;
iProcess.Close();
}
CProcessCommand::CProcessCommand()
{
iProcess.SetHandle(KNullHandle); // By default RProcess refers to the current process (KCurrentProcessHandle). This results in fshell's process exiting if this object gets killed before it has managed to open a real process handle.
}
void CProcessCommand::ConstructL(const TDesC& aExeName, RProcess* aProcess)
{
BaseConstructL(aExeName);
iWatcher = CProcessWatcher::NewL();
if (aProcess)
{
// Don't take ownership of the aProcess handle until after everything that can leave has been run
iProcess.SetHandle(aProcess->Handle());
}
}
void CProcessCommand::CreateProcessL(const TDesC& aCommandLine, IoUtils::CEnvironment&)
{
if (iProcess.Handle() == KNullHandle)
{
// Don't create new proc if we were passed one in initially
User::LeaveIfError(iProcess.Create(CmndName(), aCommandLine));
}
}
void CProcessCommand::CmndRunL(const TDesC& aCommandLine, IoUtils::CEnvironment& aEnv, MCommandObserver& aObserver)
{
CreateProcessL(aCommandLine, aEnv);
HBufC8* envBuf = aEnv.ExternalizeLC();
User::LeaveIfError(iProcess.SetParameter(IoUtils::KEnvironmentProcessSlot, *envBuf));
CleanupStack::PopAndDestroy(envBuf);
TFullName fullName(iProcess.Name());
_LIT(KThreadName,"::Main");
fullName.Append(KThreadName);
RThread thread;
User::LeaveIfError(thread.Open(fullName));
TThreadId threadId(thread.Id());
thread.Close();
User::LeaveIfError(CmndStdin().SetOwner(threadId));
User::LeaveIfError(CmndStdout().SetOwner(threadId));
User::LeaveIfError(CmndStderr().SetOwner(threadId));
TInt err = iWatcher->Logon(*this, iProcess, aObserver);
if (err)
{
iProcess.Kill(0);
}
User::LeaveIfError(err);
iObserver = &aObserver;
iProcess.Resume();
}
TInt CProcessCommand::CmndRun(const TDesC& aCommandLine, IoUtils::CEnvironment& aEnv, MCommandObserver& aObserver, RIoSession&)
{
ASSERT(iObserver == NULL);
TRAPD(err, CmndRunL(aCommandLine, aEnv, aObserver));
return err;
}
void CProcessCommand::CmndForeground()
{
iProcess.SetPriority(EPriorityForeground);
}
void CProcessCommand::CmndBackground()
{
iProcess.SetPriority(EPriorityBackground);
}
void CProcessCommand::CmndKill()
{
if (iProcess.Handle())
{
iProcess.Kill(KErrAbort);
}
}
TInt CProcessCommand::CmndSuspend()
{
#ifdef EKA2
// Can't currently support suspend in EKA2 - KERN-EXEC 46 will result.
return KErrNotSupported;
#else
TName processName(iProcess.Name());
processName.Append(_L("::*"));
TFullName threadName;
TFindThread threadFinder(processName);
while (threadFinder.Next(threadName) == KErrNone)
{
RThread thread;
if (thread.Open(threadName) == KErrNone)
{
thread.Suspend();
thread.Close();
}
}
return KErrNone;
#endif
}
TInt CProcessCommand::CmndResume()
{
#ifdef EKA2
// Can't currently support resume in EKA2 - KERN-EXEC 46 will result.
return KErrNotSupported;
#else
TName processName(iProcess.Name());
processName.Append(_L("::*"));
TFullName threadName;
TFindThread threadFinder(processName);
while (threadFinder.Next(threadName) == KErrNone)
{
RThread thread;
if (thread.Open(threadName) == KErrNone)
{
thread.Resume();
thread.Close();
}
}
return KErrNone;
#endif
}
TExitType CProcessCommand::CmndExitType() const
{
return iProcess.ExitType();
}
TExitCategoryName CProcessCommand::CmndExitCategory() const
{
return iProcess.ExitCategory();
}
TBool CProcessCommand::CmndIsDisownable() const
{
return ETrue;
}
void CProcessCommand::CmndDisown()
{
delete iWatcher;
iWatcher = NULL;
iProcess.Close();
}
//
// CProcessCommand::CProcessWatcher.
//
CProcessCommand::CProcessWatcher* CProcessCommand::CProcessWatcher::NewL()
{
return new(ELeave) CProcessWatcher();
}
CProcessCommand::CProcessWatcher::~CProcessWatcher()
{
Cancel();
}
TInt CProcessCommand::CProcessWatcher::Logon(CProcessCommand& aCommand, RProcess& aProcess, MCommandObserver& aObserver)
{
TInt ret = KErrNone;
aProcess.Logon(iStatus);
if (iStatus != KRequestPending)
{
User::WaitForRequest(iStatus);
ret = iStatus.Int();
}
else
{
iCommand = &aCommand;
iProcess = &aProcess;
iObserver = &aObserver;
SetActive();
}
return ret;
}
CProcessCommand::CProcessWatcher::CProcessWatcher()
: CActive(CActive::EPriorityStandard)
{
CActiveScheduler::Add(this);
}
void CProcessCommand::CProcessWatcher::RunL()
{
iObserver->HandleCommandComplete(*iCommand, iStatus.Int());
}
void CProcessCommand::CProcessWatcher::DoCancel()
{
if (iProcess)
{
iProcess->LogonCancel(iStatus);
}
}
//
// CPipsCommand.
//
CPipsCommand* CPipsCommand::NewL(const TDesC& aExeName)
{
CPipsCommand* self = new (ELeave) CPipsCommand();
CleanupStack::PushL(self);
self->ConstructL(aExeName);
CleanupStack::Pop(self);
return self;
}
CPipsCommand::CPipsCommand()
{
}
CPipsCommand::~CPipsCommand()
{
}
TInt CPipsCommand::CmndRun(const TDesC& aCommandLine, IoUtils::CEnvironment& aEnv, MCommandObserver& aObserver, RIoSession& aIoSession)
{
TInt err = CProcessCommand::CmndRun(aCommandLine, aEnv, aObserver, aIoSession);
if ((err == KErrNone) && iUsingPipsRun)
{
TRequestStatus status;
iProcess.Rendezvous(status);
User::WaitForRequest(status);
err = status.Int();
if (err > 0)
{
iPipsRunChildProcessId = err;
err = KErrNone;
}
}
return err;
}
void CPipsCommand::CmndKill()
{
CProcessCommand::CmndKill();
if (iUsingPipsRun)
{
RProcess pipsRunChildProcess;
if (pipsRunChildProcess.Open(iPipsRunChildProcessId) == KErrNone)
{
pipsRunChildProcess.Kill(KErrAbort);
pipsRunChildProcess.Close();
}
}
}
void CPipsCommand::CreateProcessL(const TDesC& aCommandLine, IoUtils::CEnvironment&)
{
_LIT(KPipsRunExe, "pipsrun");
TInt err = iProcess.Create(KPipsRunExe, KNullDesC);
if (err == KErrNotFound)
{
// Looks like pipsrun.exe isn't present - just load the PIPS exe directly.
User::LeaveIfError(iProcess.Create(CmndName(), aCommandLine));
}
else
{
User::LeaveIfError(err);
User::LeaveIfError(iProcess.SetParameter(IoUtils::KPipsCommandNameProcessSlot, CmndName()));
User::LeaveIfError(iProcess.SetParameter(IoUtils::KPipsCommandLineProcessSlot, aCommandLine));
iUsingPipsRun = ETrue;
}
}
//
// CAliasCommand.
//
CAliasCommand* CAliasCommand::NewL(MCommand& aAliasedCommand, const TDesC* aAdditionalArguments, const TDesC* aReplacementArguments)
{
CAliasCommand* self = new(ELeave) CAliasCommand(aAliasedCommand);
CleanupStack::PushL(self);
self->ConstructL(aAdditionalArguments, aReplacementArguments);
CleanupStack::Pop(self);
return self;
}
CAliasCommand::~CAliasCommand()
{
delete iAdditionalArguments;
delete iReplacementArguments;
iAliasedCommand.CmndRelease();
}
CAliasCommand::CAliasCommand(MCommand& aAliasedCommand)
: iAliasedCommand(aAliasedCommand)
{
}
void CAliasCommand::ConstructL(const TDesC* aAdditionalArguments, const TDesC* aReplacementArguments)
{
BaseConstructL(iAliasedCommand.CmndName());
if (aAdditionalArguments)
{
iAdditionalArguments = aAdditionalArguments->AllocL();
}
if (aReplacementArguments)
{
iReplacementArguments = aReplacementArguments->AllocL();
}
}
RIoReadHandle& CAliasCommand::CmndStdin()
{
return iAliasedCommand.CmndStdin();
}
RIoWriteHandle& CAliasCommand::CmndStdout()
{
return iAliasedCommand.CmndStdout();
}
RIoWriteHandle& CAliasCommand::CmndStderr()
{
return iAliasedCommand.CmndStderr();
}
TInt CAliasCommand::CmndRun(const TDesC& aCommandLine, IoUtils::CEnvironment& aEnv, MCommandObserver& aObserver, RIoSession& aIoSession)
{
if (iAdditionalArguments && !iReplacementArguments)
{
iAdditionalArguments = iAdditionalArguments->ReAlloc(iAdditionalArguments->Length() + aCommandLine.Length() + 1);
if (iAdditionalArguments == NULL)
{
return KErrNoMemory;
}
_LIT(KSpace, " ");
iAdditionalArguments->Des().Append(KSpace);
iAdditionalArguments->Des().Append(aCommandLine);
}
iCommandObserver = &aObserver;
const TDesC* args = &aCommandLine;
if (iReplacementArguments)
{
args = iReplacementArguments;
}
else if (iAdditionalArguments)
{
args = iAdditionalArguments;
}
return iAliasedCommand.CmndRun(*args, aEnv, *this, aIoSession);
}
void CAliasCommand::CmndForeground()
{
iAliasedCommand.CmndForeground();
}
void CAliasCommand::CmndBackground()
{
iAliasedCommand.CmndBackground();
}
void CAliasCommand::CmndKill()
{
iAliasedCommand.CmndKill();
}
TInt CAliasCommand::CmndSuspend()
{
return iAliasedCommand.CmndSuspend();
}
TInt CAliasCommand::CmndResume()
{
return iAliasedCommand.CmndResume();
}
TExitType CAliasCommand::CmndExitType() const
{
return iAliasedCommand.CmndExitType();
}
TExitCategoryName CAliasCommand::CmndExitCategory() const
{
return iAliasedCommand.CmndExitCategory();
}
void CAliasCommand::HandleCommandComplete(MCommand&, TInt aError)
{
iCommandObserver->HandleCommandComplete(*this, aError);
}