core/builtins/undertaker.cpp
changeset 0 7f656887cf89
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/core/builtins/undertaker.cpp	Wed Jun 23 15:52:26 2010 +0100
@@ -0,0 +1,229 @@
+// undertaker.cpp
+// 
+// Copyright (c) 2005 - 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 <fshell/iocli.h>
+#include "undertaker.h"
+
+TInt CCmdUndertaker::Queue()
+	{
+	TInt err = iUndertaker.Logon(iStatus, iDeadThreadHandle);
+	if (err == KErrNone)
+		{
+		SetActive();
+		}
+	return err;
+	}
+
+void CCmdUndertaker::DoCancel()
+	{
+	iUndertaker.LogonCancel();
+	}
+
+void CCmdUndertaker::RunL()
+	{
+	TInt handle = iDeadThreadHandle;
+	Queue(); // Queue early, to try and avoid missing notifications
+	// We don't use the RunL if we're in paranoid mode - the undertaker notifications are serviced directly in DoWork() without an active scheduler
+	ProcessHandle(handle);
+	}
+	
+void CCmdUndertaker::ProcessHandle(TInt aDeadThreadHandle)
+	{
+	RThread deadThread;
+	deadThread.SetHandle(aDeadThreadHandle);
+	TFullName name(deadThread.FullName());
+	TExitType type = deadThread.ExitType();
+	if (type != EExitKill || deadThread.ExitReason() != 0 || iAll)
+		{
+		Write(_L("Thread "));
+		Write(name);
+		Printf(_L(" (tid=%d) "), (TUint)deadThread.Id());
+		}
+
+	if (type == EExitPanic)
+		{
+		TExitCategoryName cat = deadThread.ExitCategory();
+		Printf(_L("panicked with %S %d\r\n"), &cat, deadThread.ExitReason());
+		}
+	else if (type == EExitTerminate)
+		{
+		Printf(_L("terminated with reason %d\r\n"), deadThread.ExitReason());
+		}
+	else if (deadThread.ExitReason() != 0)
+		{
+		// We'll consider a kill with non-zero exit code as counting as abnormal
+		Printf(_L("killed with reason %d\r\n"), deadThread.ExitReason());
+		}
+	else if (iAll)
+		{
+		Printf(_L("exited cleanly\r\n"));
+		}
+		
+	if (!iLeakThreads)
+		{
+		deadThread.Close();
+		}
+	}
+
+CCommandBase* CCmdUndertaker::NewLC()
+	{
+	CCmdUndertaker* self = new(ELeave) CCmdUndertaker();
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	return self;
+	}
+
+CCmdUndertaker::~CCmdUndertaker()
+	{
+	Cancel();
+	if (iWorkerThread.Id() != RThread().Id()) iWorkerThread.Kill(KErrAbort);
+	iHandles.Close();
+	CloseProcessOwnedHandles();
+	}
+
+CCmdUndertaker::CCmdUndertaker()
+	: CMemoryAccessCommandBase(EManualComplete)
+	{
+	}
+
+void CCmdUndertaker::ConstructL()
+	{
+	BaseConstructL();
+	}
+
+const TDesC& CCmdUndertaker::Name() const
+	{
+	_LIT(KName, "undertaker");
+	return KName;
+	}
+
+void CCmdUndertaker::DoRunL()
+	{	
+	User::LeaveIfError(iUndertaker.Create());
+
+	if (iParanoid)
+		{
+		User::LeaveIfError(iMainThread.Open(RThread().Id()));
+		User::LeaveIfError(iLock.CreateLocal());
+		iHandles.ReserveL(10);
+		TFullName threadName(RThread().Name());
+		threadName.Append(_L("_worker"));
+		LeaveIfErr(iWorkerThread.Create(threadName, &WorkerThreadFn, 8192, NULL, this), _L("Couldn't create worker thread"));
+#ifdef FSHELL_MEMORY_ACCESS_SUPPORT
+		LoadMemoryAccessL();
+		LeaveIfErr(iMemAccess.SetThreadPriority(iWorkerThread, 31), _L("Couldn't set worker thread priority with memoryaccess"));
+#else
+		iWorkerThread.SetPriority(EPriorityMuchMore); // Best we can do
+#endif
+		iWorkerThread.Resume();
+		}
+	else
+		{
+		User::LeaveIfError(Queue());
+		}	
+		
+	if (Stdin().IsForeground() > 0)
+		{
+		Write(iAll ? _L("Waiting for any thread exit...\r\n") : _L("Waiting for panicked thread exit...\r\n"));
+		}
+		
+	if (iParanoid)
+		{
+		// We've spawned off our worker thread, which is the client of the RUndertaker. We now wait for it to signal us back again.
+		// It's easier to balance the requests if we don't go through the active scheduler (so long as nothing else signals this thread!)
+		for (;;)
+			{
+			User::WaitForRequest(iMainThreadStat);
+			if (iMainThreadStat.Int() != KErrNone) User::Leave(iMainThreadStat.Int());
+			iLock.Wait();
+			TInt handle = iHandles[0];
+			iHandles.Remove(0);
+			iLock.Signal();
+			ProcessHandle(handle);
+			}
+		}
+	}
+
+void CCmdUndertaker::OptionsL(RCommandOptionList& aOptions)
+	{
+	_LIT(KOptAll, "all");
+	_LIT(KOptNoClose, "noclose");
+	_LIT(KOptParanoid, "paranoid");
+	aOptions.AppendBoolL(iAll, KOptAll);
+	aOptions.AppendBoolL(iLeakThreads, KOptNoClose);
+	aOptions.AppendBoolL(iParanoid, KOptParanoid);
+	}
+
+TInt CCmdUndertaker::WorkerThreadFn(TAny* aSelf)
+	{
+	CCmdUndertaker* self = static_cast<CCmdUndertaker*>(aSelf);
+	return self->DoWork();
+	}
+	
+TInt CCmdUndertaker::DoWork()
+	{
+	TRequestStatus mainThreadDeadStat;
+	iMainThread.Logon(mainThreadDeadStat);
+	
+	TRequestStatus stat;
+	TInt deadThreadHandle;
+	TInt err = KErrNone;
+	for (;;)
+		{
+		err = iUndertaker.Logon(stat, deadThreadHandle);
+		TRequestStatus* s = &iMainThreadStat;
+		if (err)
+			{
+			iMainThread.RequestComplete(s, err);
+			break;
+			}
+		else
+			{
+			User::WaitForRequest(stat, mainThreadDeadStat);
+			if (mainThreadDeadStat.Int() != KRequestPending)
+				{
+				// We're dead
+				err = mainThreadDeadStat.Int();
+				// Have to clean up our process-owned handles here...
+				CloseProcessOwnedHandles();
+				break;
+				}
+			// Have to duplicate the thread handle so the main thread can see it. Can't seem to persuade RUndertaker to give us a process-owned handle in the first place
+			RThread origHandle;
+			origHandle.SetHandle(deadThreadHandle);
+			RThread newHandle(origHandle);
+			err = newHandle.Duplicate(RThread(), EOwnerProcess);
+			origHandle.Close();
+			if (!err)
+				{
+				iLock.Wait();
+				err = iHandles.Append(newHandle.Handle());
+				iLock.Signal();
+				}
+			iMainThread.RequestComplete(s, err);
+			}
+		}
+	return err;
+	}
+
+void CCmdUndertaker::CloseProcessOwnedHandles()
+	{
+	iMainThread.Close();
+	iWorkerThread.Close();
+	iUndertaker.Close();
+	iLock.Close();
+	}
+
+#ifdef EXE_BUILD
+EXE_BOILER_PLATE(CCmdUndertaker)
+#endif
+