libraries/clogger/src/SensibleClient.cpp
changeset 0 7f656887cf89
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libraries/clogger/src/SensibleClient.cpp	Wed Jun 23 15:52:26 2010 +0100
@@ -0,0 +1,494 @@
+// SensibleClient.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 "SensibleClient.h"
+#include "SensibleClient_internals.h"
+#include "cliserv.h"
+#include "SensibleCompat.h"
+
+_LIT(KClientPanic, "SensibleClient");
+#define DebugPanic() User::Panic(KClientPanic, - __LINE__)
+#define AssertPanic(x) User::Panic(KClientPanic, x)
+enum TPanic
+	{
+	EReadOffEndOfCallback, // A call to TCallbackParser::Next when there's nothing more in the callback buffer
+	EBadType, // A call to TCallbackParser::NextX where X doesn't match the type of the next thing in the buffer
+	};
+
+#ifdef EKA2
+
+static TInt StartServer()
+//
+// Start the server process. Simultaneous launching
+// of two such processes should be detected when the second one attempts to
+// create the server object, failing with KErrAlreadyExists.
+//
+	{
+	const TUidType serverUid(KNullUid,KNullUid,KServerUid3);
+	RProcess server;
+	TInt r=server.Create(KMyServerImg,KNullDesC,serverUid);
+	if (r!=KErrNone)
+		return r;
+	TRequestStatus stat;
+	server.Rendezvous(stat);
+	if (stat!=KRequestPending)
+		server.Kill(0);		// abort startup
+	else
+		server.Resume();	// logon OK - start the server
+	User::WaitForRequest(stat);		// wait for start or death
+	// we are expected to return a negative error code from this function so if the server died 
+	// with a positive (or zero) code, map it to KErrServerTerminated
+	r = (server.ExitType() != EExitPending && stat.Int() >= 0) ? KErrServerTerminated : stat.Int();
+	server.Close();
+	return r;
+	}
+
+
+
+#else
+
+#include <e32math.h>
+
+inline TServerStart::TServerStart(TRequestStatus& aStatus)
+	:iId(RThread().Id()),iStatus(&aStatus)
+	{aStatus=KRequestPending;}
+inline TPtrC TServerStart::AsCommand() const
+	{return TPtrC(reinterpret_cast<const TText*>(this),sizeof(TServerStart)/sizeof(TText));}
+
+static TInt StartServer()
+//
+// Start the server process/thread which lives in an EPOCEXE object
+//
+	{
+	TRequestStatus started;
+	TServerStart start(started);
+
+	const TUidType serverUid(KNullUid,KNullUid,KServerUid3);
+
+#ifdef __WINS__
+	//
+	// In WINS the server is a DLL, the exported entrypoint returns a TInt
+	// which represents the real entry-point for the server thread
+	//
+	RLibrary lib;
+	TInt r=lib.Load(KMyServerImg,serverUid);
+	if (r!=KErrNone)
+		return r;
+	TLibraryFunction ordinal1=lib.Lookup(1);
+	TThreadFunction serverFunc=reinterpret_cast<TThreadFunction>(ordinal1());
+	//
+	// To deal with the unique thread (+semaphore!) naming in EPOC, and that we may
+	// be trying to restart a server that has just exited we attempt to create a
+	// unique thread name for the server.
+	// This uses Math::Random() to generate a 32-bit random number for the name
+	//
+	TName name(KMyServerName);
+	name.AppendNum(Math::Random(),EHex);
+	RThread server;
+	r=server.Create(name,serverFunc,
+					KMyServerStackSize,
+					&start,&lib,NULL,
+					KMyServerInitHeapSize,KMyServerMaxHeapSize,EOwnerProcess);
+	lib.Close();	// if successful, server thread has handle to library now
+#else
+	//
+	// EPOC is easy, we just create a new server process. Simultaneous launching
+	// of two such processes should be detected when the second one attempts to
+	// create the server object, failing with KErrAlreadyExists.
+	//
+	RProcess server;
+	TInt r=server.Create(KMyServerImg,start.AsCommand(),serverUid);
+#endif
+	if (r!=KErrNone)
+		return r;
+	TRequestStatus died;
+	server.Logon(died);
+	if (died!=KRequestPending)
+		{
+		// logon failed - server is not yet running, so cannot have terminated
+		User::WaitForRequest(died);	// eat signal
+		server.Kill(0);				// abort startup
+		server.Close();
+		return died.Int();
+		}
+	//
+	// logon OK - start the server
+	server.Resume();
+	User::WaitForRequest(started,died);		// wait for start or death
+	if (started==KRequestPending)
+		{
+		// server has died, never made it to the startup signal
+		server.Close();
+		return died.Int();
+		}
+	//
+	// server started (at last). Cancel and consume the death-notification
+	// before reporting success
+	server.LogonCancel(died);
+	server.Close();
+	User::WaitForRequest(died);		// eat the signal (from the cancel)
+	return KErrNone;
+	}
+
+TInt E32Dll(TDllReason)
+	{
+	return 0;
+	}
+
+
+#endif
+
+RSensibleSessionBody::RSensibleSessionBody()
+	: iDispatcher(NULL)
+	{}
+
+TInt RSensibleSessionBody::Connect(TInt aMessageSlots, TBool aStartCallbackDispatcher)
+//
+// Connect to the server, attempting to start it if necessary
+//
+	{
+	TInt r = KErrNone;
+	TInt retry=2;
+	for (;;)
+		{
+		r=DoCreateSession(KMyServerName, TVersion(0,0,0), aMessageSlots);
+		if (r!=KErrNotFound && r!=KErrServerTerminated)
+			break;
+		if (--retry==0)
+			break;
+		r=StartServer();
+		if (r!=KErrNone && r!=KErrAlreadyExists)
+			break;
+		}
+	if (r == KErrNone && aStartCallbackDispatcher)
+		{
+		if (StartCallbackDispatcher() != KErrNone)
+			{
+			Close();
+			return KErrNoMemory;
+			}
+		}
+	return r;
+	}
+
+TInt RSensibleSessionBody::StartCallbackDispatcher()
+	{
+	if (!iDispatcher)
+		{
+		iDispatcher = new CServerCallbackDispatcher(*this);
+		}
+	else if (!iDispatcher->IsActive())
+		{
+		iDispatcher->Register();
+		}
+	return iDispatcher == NULL ? KErrNoMemory : KErrNone;
+	}
+
+void RSensibleSessionBody::StopCallbackDispatcher()
+	{
+	if (iDispatcher) iDispatcher->Cancel();
+	}
+
+void RSensibleSessionBody::DispatchCallbackL(TServerCallback& /*aCallback*/, TCallbackParser& /*aParser*/)
+	{
+	// Implemented by subclass
+	}
+
+void RSensibleSessionBody::ServerDiedL()
+	{
+	// Implemented by subclass
+	}
+
+void RSensibleSessionBody::Close()
+	{
+	delete iDispatcher;
+	iDispatcher = NULL;
+	}
+
+// Not happy this actually works or is worth it
+/*
+void CCleanupAndComplete::RunL()
+	{
+	if (iClientStatus)
+		{
+		// We use this class with a null pointer to allow non-asynchronous (ie blind) APIs to still have the ability to cleanup resource when they are completed
+		User::RequestComplete(iClientStatus, iStatus.Int());
+		}
+	delete this;
+	}
+
+void CCleanupAndComplete::DoCancel()
+	{
+	//TODO what? Probably delete this in some way
+	//delete this;
+	}
+
+CCleanupAndComplete::CCleanupAndComplete(TRequestStatus* aClientStatus)
+	: CActive(EPriorityStandard), iClientStatus(aClientStatus)
+	{
+	CActiveScheduler::Add(this);
+	if (iClientStatus)
+		{
+		*iClientStatus = KRequestPending;
+		}
+	}
+	
+CCleanupAndComplete* CCleanupAndComplete::NewLC(TRequestStatus* aClientStatus)
+	{
+	CCleanupAndComplete* self = new(ELeave) CCleanupAndComplete(aClientStatus);
+	CleanupStack::PushL(self);
+	return self;
+	}
+		
+TInt CCleanupAndComplete::Create(TRequestStatus*& aStatus, const TDesC8& aDes, TInt& aIpcArg)
+	{
+	CCleanupAndComplete* self = new CCleanupAndComplete(aStatus);
+	TInt err = KErrNone;
+	if (!self) err = KErrNoMemory;
+	HBufC8* buf = NULL;
+	if (!err)
+		{
+		buf = aDes.Alloc();
+		}
+	if (!buf) err = KErrNoMemory;
+	if (!err)
+		{
+		err = self->iWithoutDestructor.Append(buf);
+		}
+	if (err) 
+		{
+		delete buf;
+		delete self;
+		return KErrNoMemory;
+		}
+	aIpcArg = (TInt)buf;
+	aStatus = &self->iStatus;
+	self->SetActive();
+	return KErrNone;
+	}
+
+TInt CCleanupAndComplete::Create(TRequestStatus*& aStatus, const TDesC16& aDes, TInt& aIpcArg)
+	{
+	CCleanupAndComplete* self = new CCleanupAndComplete(aStatus);
+	TInt err = KErrNone;
+	if (!self) err = KErrNoMemory;
+	HBufC16* buf = NULL;
+	if (!err)
+		{
+		buf = aDes.Alloc();
+		}
+	if (!buf) err = KErrNoMemory;
+	if (!err)
+		{
+		err = self->iWithoutDestructor.Append(buf);
+		}
+	if (err) 
+		{
+		delete buf;
+		delete self;
+		return KErrNoMemory;
+		}
+	aIpcArg = (TInt)buf;
+	aStatus = &self->iStatus;
+	self->SetActive();
+	return KErrNone;
+	}
+
+void CCleanupAndComplete::AddL(CBase* aWithDestructor)
+	{
+	User::LeaveIfError(iWithDestructor.Append(aWithDestructor));
+	}
+
+void CCleanupAndComplete::AddL(TAny* aWithoutDestructor)
+	{
+	User::LeaveIfError(iWithoutDestructor.Append(aWithoutDestructor));
+	}
+
+CCleanupAndComplete::~CCleanupAndComplete()
+	{
+	Cancel();
+	iWithDestructor.ResetAndDestroy();
+	for (TInt i = 0; i < iWithoutDestructor.Count(); i++)
+		{
+		delete iWithoutDestructor[i];
+		}
+	}
+
+void CCleanupAndComplete::SetActive()
+	{
+	CActive::SetActive();
+	}
+
+*/
+
+CServerCallbackDispatcher::CServerCallbackDispatcher(RSensibleSessionBody& aSession)
+	: CActive(EPriorityStandard), iSession(aSession), iContextPtr(NULL, 0)
+	{
+	CActiveScheduler::Add(this);
+	Register();
+	}
+	
+void CServerCallbackDispatcher::Register()
+	{
+	iState = EWaitingForCallback;
+	IPC(args, &iNextCallback, 0,0,0);
+	iSession.DoSendReceive(ERegisterCallbackNotifier, args, iStatus);
+	SetActive();
+	}
+
+void CServerCallbackDispatcher::RunL()
+	{
+	DISOWN(iCachedCallbackResult8);
+	DISOWN(iCachedCallbackResult16);
+
+	if (iStatus == KErrServerTerminated)
+		{
+		iSession.ServerDiedL();
+		return;
+		}
+	else if (iStatus != KErrNone)
+		{
+		//TODO Do something...
+		__DEBUGGER();
+		return;
+		}
+
+	TServerCallback cb = iNextCallback();
+	TCallbackParser p(cb);
+
+	if (iState == EWaitingForCallback && cb.iContextLength != 0)
+		{
+		iState = EWaitingForContext;
+		DISOWN(iContext);
+		iContext = HBufC8::NewL(cb.iContextLength);
+		iContextPtr = iContext->Des();
+		IPC(args, &iContextPtr, 0,0,0);
+		iSession.DoSendReceive(EGetCallbackContext, args, iStatus);
+		SetActive();
+		return;
+		}
+
+	if (iState == EWaitingForContext)
+		{
+		p.SetContext(*iContext);
+		}
+
+	Register();
+	iSession.DispatchCallbackL(cb, p);
+	}
+
+void CServerCallbackDispatcher::DoCancel()
+	{
+	IPC(args, 0,0,0,0);
+	iSession.DoSendReceive(ECancelCallbackNotifier, args);
+	}
+
+CServerCallbackDispatcher::~CServerCallbackDispatcher()
+	{
+	Cancel();
+	DISOWN(iCachedCallbackResult8);
+	DISOWN(iCachedCallbackResult16);
+	DISOWN(iContext);
+	}
+
+
+TCallbackParser::TCallbackParser(const TServerCallback& aCallback)
+	: iCallback(aCallback), iContext(NULL), iNextPtr(aCallback.iData.Ptr()), iInContext(EFalse)
+	{}
+
+void TCallbackParser::SetContext(TDesC8& aContext)
+	{
+	iContext = &aContext;
+	}
+
+#define DOGET(T, type, name) T name; TAny* res = Next(sizeof(T), #type); Mem::Copy(&name, res, sizeof(T));
+#define GET(T, type) DOGET(T, type, __result); return __result;
+
+TInt TCallbackParser::NextInt()
+	{
+	GET(TInt, i);
+	}
+
+TUint TCallbackParser::NextUint()
+	{
+	GET(TUint, u);
+	}
+
+TPoint TCallbackParser::NextPoint()
+	{
+	GET(TPoint, P);
+	}
+
+TSize TCallbackParser::NextSize()
+	{
+	GET(TSize, S);
+	}
+
+TRgb TCallbackParser::NextRgb()
+	{
+	GET(TRgb, G);
+	}
+
+TRect TCallbackParser::NextRect()
+	{
+	GET(TRect, R);
+	}
+
+TPtrC TCallbackParser::NextDesC()
+	{
+	DOGET(TInt, i, len);
+	const TUint16* ptr = reinterpret_cast<const TUint16*>(Next(len*2, "D"));
+	if (TUint(ptr)&1)
+		{
+		// Need to allow for padding
+		ptr = (TUint16*)(((TUint8*)ptr)+1);
+		iNextPtr++; // And also move iNextPtr to the right place for the next read
+		}
+	return TPtrC(ptr, len);
+	}
+
+TPtrC8 TCallbackParser::NextDesC8()
+	{
+	DOGET(TInt, i, len);
+	const TUint8* ptr = reinterpret_cast<const TUint8*>(Next(len, "8"));
+	return TPtrC8(ptr, len);
+	}
+
+void* TCallbackParser::Next(TInt aSize, char* aType)
+	{
+	const TUint8* endPtr = iInContext ? iContext->Ptr() + iContext->Size() : iCallback.iData.Ptr() + iCallback.iData.Size();
+
+	if (iNextPtr + aSize + 1 > endPtr)
+		{
+		// No room for arg and type
+		if (!iInContext && iContext)
+			{
+			// try moving to context
+			iNextPtr = (TUint8*)iContext->Ptr();
+			iInContext = ETrue;
+			}
+		else
+			{
+			// Either there's no context, or we're already in it and reading off the end
+			AssertPanic(EReadOffEndOfCallback);
+			}
+		}
+
+	TUint8 nextType = *iNextPtr; //iInContext ? iCallback.iContextTypes[iIdx] : iCallback.iDataTypes[iIdx];
+	iNextPtr++;
+	void* result = (void*)iNextPtr;
+	
+	__ASSERT_ALWAYS(nextType == *aType, AssertPanic(EBadType)); // Types must match
+
+	iNextPtr = iNextPtr + aSize;
+	return result;
+	}