--- /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;
+ }