--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/libraries/clogger/src/SensibleServer.cpp Wed Jun 23 15:52:26 2010 +0100
@@ -0,0 +1,913 @@
+// SensibleServer.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 "SensibleServer.h"
+#include "SensibleServer_server_specific.h"
+
+#define DebugPanic() User::Panic(KDebugPanic, __LINE__)
+#define ClientPanic(aMsg) PanicClient(aMsg, - __LINE__)
+
+inline CShutdown::CShutdown()
+ :CTimer(-1)
+ {CActiveScheduler::Add(this);}
+inline void CShutdown::ConstructL()
+ {CTimer::ConstructL();}
+inline void CShutdown::Start(TInt aDelay)
+ {
+ if (aDelay)
+ {
+ After(aDelay);
+ }
+ }
+
+CSensibleServer::CSensibleServer()
+ :CServerBase(0,ESharableSessions)
+ {}
+
+CSensibleSession::CSensibleSession()
+ : iCallbackQ(_FOFF(CCallbackContext, iLink))
+ {}
+
+inline CSensibleServer& CSensibleSession::Server()
+ {return *static_cast<CSensibleServer*>(const_cast<CServerBase*>(CSessionBase::Server()));}
+
+inline CFilteringScheduler* CSensibleServer::Scheduler()
+ {return iScheduler;}
+
+#ifdef RUN_SERVER_WITH_EIKONENV
+
+#include <eikenv.h>
+#include <eikappui.h>
+
+class CSensibleAppUi : public CEikAppUi
+ {
+public:
+ void ConstructL();
+ ~CSensibleAppUi();
+
+private:
+ CSensibleServer* iServer;
+ };
+
+void CSensibleAppUi::ConstructL()
+ {
+ BaseConstructL(ENoAppResourceFile /*|ENoScreenFurniture*/);
+
+ //
+ // create the server
+ iServer = new(ELeave) CServer_Class_Name();
+ iServer->ConstructL();
+ }
+
+CSensibleAppUi::~CSensibleAppUi()
+ {
+ delete iServer;
+ }
+
+// Have to derive from CONE scheduler otherwise ccoeenv complains mightily
+#define SCHEDULER_SUPER CCoeScheduler
+#define SCHEDULER_CONSTRUCTOR CCoeScheduler(CCoeEnv::Static())
+inline void ExitScheduler() { CBaActiveScheduler::Exit(); }
+
+#else
+
+#define SCHEDULER_SUPER CActiveScheduler
+#define SCHEDULER_CONSTRUCTOR CActiveScheduler()
+inline void ExitScheduler() { CActiveScheduler::Stop(); }
+
+#endif
+
+class CFilteringScheduler : public SCHEDULER_SUPER
+ {
+public:
+ CFilteringScheduler();
+ void WaitForAnyRequest();
+ //void OnlyRunThisObject(CActive* aActive); // This doesn't work atm!
+ void RunEverythingExcept(CActive* aActive, CActive* aActive2);
+ void StopFiltering();
+
+private:
+ struct SObj {
+ CActive* iObject;
+ TInt iCachedStatus;
+ };
+ static const TInt KNumFilteredObjects = 2;
+ SObj iFilteredObjects[KNumFilteredObjects];
+ };
+
+inline TServerStart::TServerStart()
+ {}
+
+void TServerStart::SignalL()
+//
+// Signal the owning thread that the server has started successfully
+// This may itself fail
+//
+ {
+#ifdef EKA2
+ RProcess::Rendezvous(KErrNone);
+#else
+ RThread starter;
+ User::LeaveIfError(starter.Open(iId));
+ starter.RequestComplete(iStatus,KErrNone);
+ starter.Close();
+#endif
+ }
+
+
+///////////////////////
+
+#ifndef __HIDE_IPC_V1__
+void CSensibleSession::CreateL(const CServer& aServer)
+//
+// 2nd phase construct for sessions - called by the CServer framework
+//
+ {
+ CSharableSession::CreateL(aServer); // does not leave
+ CreateL();
+ }
+#endif
+
+void CSensibleSession::CreateL()
+ {
+ Server().AddSession();
+ }
+
+CSensibleSession::~CSensibleSession()
+ {
+ Server().DropSession();
+ }
+
+static void JustPanic(TAny*)
+ {
+ User::Panic(_L("Crash!"), 0);
+ }
+
+extern void CleanupPanicPushL()
+ {
+ CleanupStack::PushL(TCleanupItem(&JustPanic, 0));
+ }
+
+void CSensibleSession::ServiceL(const RMessage& aMessage)
+//
+// Handle a client request.
+// Leaving is handled by CSensibleServer::RunError() which reports the error code
+// to the client
+//
+ {
+ switch (aMessage.Function())
+ {
+ case ERegisterCallbackNotifier:
+ __ASSERT_ALWAYS(!iCallbackPending, ClientPanic(aMessage)); // Can't call Register is there's something already registered
+ iCallbackNotifier = aMessage;
+ iCallbackPending = ETrue;
+ CompleteNextCallback();
+ break;
+ case EGetCallbackContext:
+ {
+ __ASSERT_ALWAYS(!iCallbackQ.IsEmpty(), ClientPanic(aMessage));
+ CCallbackContext* c = iCallbackQ.First();
+ __ASSERT_ALWAYS(c->Flag(EActive), ClientPanic(aMessage));
+ __ASSERT_ALWAYS(c->CallbackHasContext(), ClientPanic(aMessage));
+ aMessage.WriteL(SLOT(aMessage, 0), *c->Context());
+ aMessage.Complete(KErrNone);
+ if (!c->CallbackRequiresResult())
+ {
+ iCallbackQ.Remove(*c);
+ delete c;
+ }
+ break;
+ }
+ case EWriteCallbackResultAndReregister:
+ {
+ __ASSERT_ALWAYS(!iCallbackPending, ClientPanic(aMessage)); // Can't call Register is there's something already registered
+ __ASSERT_ALWAYS(!iCallbackQ.IsEmpty(), ClientPanic(aMessage));
+ CCallbackContext* c = iCallbackQ.First();
+ __ASSERT_ALWAYS(c->Flag(EActive), ClientPanic(aMessage));
+ __ASSERT_ALWAYS(c->CallbackRequiresResult(), ClientPanic(aMessage));
+
+ // Get the reregistering out of the way
+ iCallbackNotifier = aMessage;
+ iCallbackPending = ETrue;
+
+ if (aMessage.Int2() < 0)
+ {
+ // Leave code
+ c->SetFlags(EResultIsLeaveCode);
+ c->Result().integer = aMessage.Int2();
+ }
+ else if (c->Flag(EResultHBufC16))
+ {
+ HBufC16* result = HBufC16::New(aMessage.Int2());
+ if (!result)
+ {
+ c->SetFlags(EResultIsLeaveCode);
+ c->Result().integer = KErrNoMemory;
+ }
+ else
+ {
+ *c->Result().l = result;
+ TPtr16 ptr(result->Des());
+ aMessage.ReadL(SLOT(aMessage, 1), ptr);
+ }
+ }
+ else if (c->Flag(EResultHBufC8))
+ {
+ HBufC8* result = HBufC8::New(aMessage.Int2());
+ if (!result)
+ {
+ c->SetFlags(EResultIsLeaveCode);
+ c->Result().integer = KErrNoMemory;
+ }
+ else
+ {
+ *c->Result().s = result;
+ TPtr8 ptr(result->Des());
+ aMessage.ReadL(SLOT(aMessage, 1), ptr);
+ }
+ }
+ else
+ {
+ // It's a TPkg
+ aMessage.ReadL(SLOT(aMessage, 1), *c->Result().pkg);
+ }
+ CActiveScheduler::Stop();
+ break;
+ }
+ case ECancelCallbackNotifier:
+ {
+ if (iCallbackPending)
+ {
+ iCallbackNotifier.Complete(KErrCancel);
+ iCallbackPending = EFalse;
+ }
+ aMessage.Complete(KErrNone);
+ break;
+ }
+ case EDummy:
+ aMessage.Complete(KErrNone);
+ break;
+
+ default:
+ if (!DoServiceL(aMessage))
+ {
+ PanicClient(aMessage, EPanicIllegalFunction);
+ }
+ break;
+ }
+ }
+
+TBool CSensibleSession::DoServiceL(const RMessage& aMessage)
+ {
+ // Subclasses override this!
+ aMessage.Complete(KErrNone);
+ return ETrue;
+ }
+
+void CShutdown::RunL()
+//
+// Initiate server exit when the timer expires
+//
+ {
+ ExitScheduler();
+ }
+
+void CSensibleServer::ConstructL()
+//
+// 2nd phase construction - ensure the timer and server objects are running
+//
+ {
+ StartL(KMyServerName);
+ iShutdown.ConstructL();
+ // ensure that the server still exits even if the 1st client fails to connect
+ iShutdown.Start(TransientServerShutdownTime());
+
+ // Now set up our special scheduler. This is tricky because of good old eikonenv doing stuff differently
+ // Basically without eikonenv, RunServer owns the old scheduler so we can't delete it
+ // However eikonenv will delete the new one as part of its shutdown!
+
+ iScheduler = new(ELeave) CFilteringScheduler();
+ iOldScheduler = CActiveScheduler::Replace(iScheduler);
+
+#ifdef RUN_SERVER_WITH_EIKONENV
+ DISOWN(iOldScheduler);
+#endif
+ }
+
+CSensibleServer::~CSensibleServer()
+ {
+#ifndef RUN_SERVER_WITH_EIKONENV
+ DISOWN(iScheduler); // To mimic what CCoeEnv does
+#endif
+ }
+
+#ifdef __HIDE_IPC_V1__
+CSessionBase* CSensibleServer::NewSessionL(const TVersion& /*aVersion*/, const RMessage& /*aMessage*/) const
+#else
+CSessionBase* CSensibleServer::NewSessionL(const TVersion& /*aVersion*/) const
+#endif
+//
+// Cretae a new client session. This should really check the version number.
+//
+ {
+ return new(ELeave) CSensibleSession();
+ }
+
+void CSensibleServer::AddSession()
+//
+// A new session is being created
+// Cancel the shutdown timer if it was running
+//
+ {
+ ++iSessionCount;
+ iShutdown.Cancel();
+ }
+
+void CSensibleServer::DropSession()
+//
+// A session is being destroyed
+// Start the shutdown timer if it is the last session.
+//
+ {
+ if (--iSessionCount==0 && CActiveScheduler::Current()) // Check we have a scheduler, because if the server is being shut down there won't be one (and there'll be no point starting a shutdown timer
+ iShutdown.Start(TransientServerShutdownTime());
+ }
+
+
+TInt CSensibleServer::RunError(TInt aError)
+//
+// Handle an error from CSensibleSession::ServiceL()
+// A bad descriptor error implies a badly programmed client, so panic it;
+// otherwise report the error to the client
+//
+ {
+ if (aError==KErrBadDescriptor)
+ PanicClient(Message(),EPanicBadDescriptor);
+ else
+ Message().Complete(aError);
+ //
+ // The leave will result in an early return from CServer::RunL(), skipping
+ // the call to request another message. So do that now in order to keep the
+ // server running.
+ ReStart();
+ return KErrNone; // handled the error fully
+ }
+
+/*void CSensibleServer::BlockAllAOsExceptServerRequests()
+ {
+ Scheduler()->OnlyRunThisObject(this);
+ }*/
+
+void CSensibleServer::BlockRequestsFrom(CActive* aActive1, CActive* aActive2)
+ {
+ Scheduler()->RunEverythingExcept(aActive1, aActive2);
+ }
+
+void CSensibleServer::StopBlocking()
+ {
+ Scheduler()->StopFiltering();
+ }
+
+TInt CSensibleServer::TransientServerShutdownTime() const
+ {
+ return 2000000; // Default to 2 seconds
+ }
+
+void PanicClient(const RMessage& aMessage, TInt aPanic)
+//
+// RMessage::Panic() also completes the message. This is:
+// (a) important for efficient cleanup within the kernel
+// (b) a problem if the message is completed a second time
+//
+ {
+ __DEBUGGER();
+ aMessage.Panic(KDebugPanic, aPanic);
+ }
+
+static void RunServerL(TServerStart& aStart)
+//
+// Perform all server initialisation, in particular creation of the
+// scheduler and server and then run the scheduler
+//
+ {
+
+#ifndef RUN_SERVER_WITH_EIKONENV
+ // create and install the active scheduler we need
+ CActiveScheduler* s=new(ELeave) CActiveScheduler;
+ CleanupStack::PushL(s);
+ CActiveScheduler::Install(s);
+#endif
+
+ //
+ // naming the server thread after the server helps to debug panics
+#ifdef __SECURE_API__
+ User::LeaveIfError(User::RenameThread(KMyServerName));
+#else
+ User::LeaveIfError(RThread().Rename(KMyServerName));
+#endif
+
+#ifdef RUN_SERVER_WITH_EIKONENV
+ // In this case, the server creation/destruction is pushed into CSensibleAppUi
+
+ // Give ourselves a eikonenv
+ CEikonEnv* env = new CEikonEnv;
+ CEikAppUi* appui = NULL;
+ TInt err = KErrNone;
+ if (env != NULL)
+ {
+ TRAP(err,
+ env->ConstructL(EFalse);
+ appui = new (ELeave)CSensibleAppUi();
+ appui->ConstructL();
+ env->SetAppUi(appui);
+ );
+ }
+ if (err == KErrNone)
+ {
+ //
+ // Initialisation complete, now signal the client
+ aStart.SignalL();
+ env->ExecuteD();
+ }
+ else
+ {
+ if (env != NULL)
+ {
+ env->DestroyEnvironment();
+ }
+ User::Leave(err); // This will tell the client that something's gone wrong
+ }
+#else
+
+ //
+ // create the server
+ CSensibleServer* server = new(ELeave) CServer_Class_Name();
+ CleanupStack::PushL(server);
+ server->ConstructL();
+ //
+ // Initialisation complete, now signal the client
+ aStart.SignalL();
+ //
+ // Ready to run
+ CActiveScheduler::Start();
+
+ CleanupStack::PopAndDestroy(2, s); // server, scheduler
+
+#endif
+ }
+
+static TInt RunServer(TServerStart& aStart)
+//
+// Main entry-point for the server thread
+//
+ {
+ __UHEAP_MARK;
+ //
+ CTrapCleanup* cleanup=CTrapCleanup::New();
+ TInt r=KErrNoMemory;
+ if (cleanup)
+ {
+//#ifdef _DEBUG
+// TRAP(r, CleanupPanicPushL(); RunServerL(aStart); CleanupStack::Pop());
+//#else
+ TRAP(r,RunServerL(aStart));
+//#endif
+ delete cleanup;
+ }
+ //
+ __UHEAP_MARKEND;
+ return r;
+ }
+
+
+#ifndef EKA2
+
+// The server binary is an "EPOCEXE" target type
+// Thus the server parameter passing and startup code for WINS and EPOC are
+// significantly different.
+
+#ifdef __WINS__
+
+// In WINS, the EPOCEXE target is a DLL with an entry point called WinsMain,
+// taking no parameters and returning TInt. This is not really valid as a thread
+// function which takes a TAny* parameter which we need.
+//
+// So the DLL entry-point WinsMain() is used to return a TInt representing the
+// real thread function within the DLL. This is good as long as
+// sizeof(TInt)>=sizeof(TThreadFunction).
+//
+
+static TInt ThreadFunction(TAny* aParms)
+//
+// WINS thread entry-point function.
+// The TServerStart objects is passed as the thread parameter
+//
+ {
+ return RunServer(*static_cast<TServerStart*>(aParms));
+ }
+
+IMPORT_C TInt WinsMain();
+EXPORT_C TInt WinsMain()
+//
+// WINS DLL entry-point. Just return the real thread function
+// cast to TInt
+//
+ {
+ return reinterpret_cast<TInt>(&ThreadFunction);
+ }
+
+TInt E32Dll(TDllReason)
+ {
+ return KErrNone;
+ }
+
+#else
+
+//
+// In EPOC, the EPOCEXE target is a process, and the server startup
+// parameters are encoded in the command line
+//
+
+TInt TServerStart::GetCommand()
+ {
+ RProcess p;
+ if (p.CommandLineLength()!=sizeof(TServerStart)/sizeof(TText))
+ return KErrGeneral;
+ TPtr ptr(reinterpret_cast<TText*>(this),0,sizeof(TServerStart)/sizeof(TText));
+ p.CommandLine(ptr);
+ return KErrNone;
+ }
+
+TInt E32Main()
+//
+// Server process entry-point
+// Recover the startup parameters and run the server
+//
+ {
+ TServerStart start;
+ TInt r=start.GetCommand();
+ if (r==KErrNone)
+ r=RunServer(start);
+ return r;
+ }
+
+#endif
+
+#else
+
+TInt E32Main()
+//
+// Server process entry-point
+//
+ {
+ TServerStart start;
+ TInt r = RunServer(start);
+ return r;
+ }
+
+#endif
+
+//// CCallbackContext ////
+
+CCallbackContext::CCallbackContext(TCallbackCode aCode)
+ : iCallback(aCode)
+ {
+ iCallback.iCode = aCode;
+ }
+
+CCallbackContext::~CCallbackContext()
+ {
+ __ASSERT_DEBUG(!Flag(EActive), DebugPanic());
+ delete iContext;
+ }
+
+void CCallbackContext::SetFlags(TInt aFlags)
+ {
+ iFlags |= aFlags;
+ }
+
+TBool CCallbackContext::Flag(TInt aFlags) const
+ {
+ return iFlags & aFlags;
+ }
+
+void CCallbackContext::ClearFlags(TInt aFlags)
+ {
+ iFlags = iFlags & ~aFlags;
+ }
+
+TBool CCallbackContext::CallbackRequiresResult() const
+ {
+ //TODO
+ return EFalse;
+ }
+
+TBool CCallbackContext::CallbackHasContext() const
+ {
+ return (iContext != NULL);
+ }
+
+void CCallbackContext::SetResult(TDes8& aPkg)
+ {
+ iResult.pkg = &aPkg;
+ ClearFlags(EResultHBufC8 | EResultHBufC16);
+ }
+
+void CCallbackContext::SetResult(HBufC8*& aResult)
+ {
+ iResult.s = &aResult;
+ SetFlags(EResultHBufC8);
+ ClearFlags(EResultHBufC16);
+ }
+
+void CCallbackContext::SetResult(HBufC16*& aResult)
+ {
+ iResult.l = &aResult;
+ SetFlags(EResultHBufC16);
+ ClearFlags(EResultHBufC8);
+ }
+
+HBufC8* CCallbackContext::Context()
+ {
+ return iContext;
+ }
+
+CCallbackContext::TResult& CCallbackContext::Result()
+ {
+ return iResult;
+ }
+
+TServerCallback& CCallbackContext::Callback()
+ {
+ return iCallback;
+ }
+
+TCallbackWriter CCallbackContext::Writer()
+ {
+ TCallbackWriter res(iCallback, &iContext);
+ return res;
+ }
+
+//// CFilteringScheduler ////
+
+CFilteringScheduler::CFilteringScheduler()
+: SCHEDULER_CONSTRUCTOR
+ {}
+
+
+/*
+void CFilteringScheduler::OnlyRunThisObject(CActive* aActive)
+ {
+ __ASSERT_ALWAYS(!iObject, DebugPanic());
+ iObject = &aActive->iStatus;
+ iOnlyRunThisObject = ETrue;
+ User::Panic(_L("OnlyRunThisObject doesn't work yet!"), 0);
+ }
+*/
+
+void CFilteringScheduler::RunEverythingExcept(CActive* aActive, CActive* aActive2)
+ {
+ __ASSERT_ALWAYS(!iFilteredObjects[0].iObject, DebugPanic());
+ iFilteredObjects[0].iObject = aActive;
+ iFilteredObjects[1].iObject = aActive2;
+
+ iFilteredObjects[0].iCachedStatus = KRequestPending;
+ iFilteredObjects[1].iCachedStatus = KRequestPending;
+ }
+
+void CFilteringScheduler::StopFiltering()
+ {
+ for (TInt i = 0; i < KNumFilteredObjects; i++)
+ {
+ SObj& obj = iFilteredObjects[i];
+ if (obj.iObject && obj.iCachedStatus != KRequestPending)
+ {
+ TRequestStatus* stat = &obj.iObject->iStatus;
+ User::RequestComplete(stat, obj.iCachedStatus); // Since we consumed the signal from the previous complete, we need to re-signal by calling RequestComplete rather than just updating the object status ourselves
+ }
+ obj.iObject = NULL;
+ }
+ }
+
+void CFilteringScheduler::WaitForAnyRequest()
+ {
+
+ if (!iFilteredObjects[0].iObject)
+ {
+ SCHEDULER_SUPER::WaitForAnyRequest();
+ return;
+ }
+
+ for (;;)
+ {
+ SCHEDULER_SUPER::WaitForAnyRequest();
+ TBool found = EFalse;
+ for (TInt i = 0; i < KNumFilteredObjects; i++)
+ {
+ SObj& obj = iFilteredObjects[i];
+ TBool isReadyToRun = obj.iObject && obj.iObject->IsActive() && obj.iObject->iStatus != KRequestPending;
+ if (isReadyToRun)
+ {
+ // Our target object has completed, so mark it back as pending, and consume the signal
+ ASSERT(obj.iCachedStatus == KRequestPending); // If this is already set something has gone quite wrong with our logic
+ obj.iCachedStatus = obj.iObject->iStatus.Int();
+ *((TInt*)&obj.iObject->iStatus) = KRequestPending; // Cast this to a TInt* to prevent TRequestStatus::operator= changing the flags
+ found = ETrue;
+ break;
+ }
+ }
+ if (!found) break; // It wasn't one of our objects that completed so no need to go round the loop again
+ }
+ }
+
+//// TCallbackWriter ////
+
+TCallbackWriter::TCallbackWriter(TServerCallback& aCallback, HBufC8** aContext)
+: iCallback(aCallback), iContext(aContext), iBuf((TUint8*)aCallback.iData.Ptr(), aCallback.iData.MaxLength()), iInContext(EFalse)
+ {
+ if (iContext) *iContext = NULL;
+ }
+
+void TCallbackWriter::AddL(const TDesC8& aData, char* aType)
+ {
+ __ASSERT_DEBUG(aData.Length(), DebugPanic());
+ __ASSERT_DEBUG(aType, DebugPanic());
+ TInt bytesRemaining = iBuf.MaxSize() - iBuf.Size();
+
+ TInt typeSize = 1;
+ if (*aType == 'D')
+ {
+ // TDesC16s need to be 2-byte aligned, so make sure the data after the type byte will be
+ if (!(iBuf.Length() & 1)) typeSize = 2;
+ }
+
+ if (aData.Size() + typeSize > bytesRemaining)
+ {
+ // No room for arg and type
+ if (!iContext) User::Leave(KErrNoMemory);
+ if (!iInContext)
+ {
+ // so construct context
+ if (*aType == 'D') typeSize = 2;
+ *iContext = HBufC8::NewL(aData.Length() + typeSize);
+ iInContext = ETrue;
+ }
+ else
+ {
+ // realloc
+ HBufC8* newContext = (*iContext)->ReAlloc(Max(iBuf.MaxSize() * 2, iBuf.MaxSize() + aData.Size() + typeSize));
+ if (!newContext)
+ {
+ delete *iContext;
+ *iContext = NULL;
+ User::Leave(KErrNoMemory);
+ }
+ *iContext = newContext;
+ }
+ iBuf.Set((*iContext)->Des());
+ }
+ iBuf.Append(*aType);
+ if (typeSize == 2) iBuf.Append('-'); // Padding
+ iBuf.Append(aData);
+ if (iInContext)
+ {
+ iCallback.iContextLength = iBuf.Length();
+ }
+ else
+ {
+ iCallback.iData.SetLength(iBuf.Length()); // Because a TPtr pointing to a buf doesn't behave like one pointing to an HBufC, sigh...
+ }
+
+ }
+
+#define ADD(T, arg, type) { TPckg<T> x(arg); AddL(x, #type); }
+
+void TCallbackWriter::AddL(TInt aInt) { ADD(TInt, aInt, i); }
+void TCallbackWriter::AddL(TUint aInt) { ADD(TUint, aInt, u); }
+void TCallbackWriter::AddL(TPoint aPoint) { ADD(TPoint, aPoint, P); }
+void TCallbackWriter::AddL(TSize aSize) { ADD(TSize, aSize, S); }
+void TCallbackWriter::AddL(TRgb aRgb) { ADD(TRgb, aRgb, G); }
+void TCallbackWriter::AddL(TRect aRect) { ADD(TRect, aRect, R); }
+
+void TCallbackWriter::AddL(const TDesC16& aDesc)
+ {
+ ADD(TInt, aDesc.Length(), i);
+ TPtrC8 x((TUint8*)aDesc.Ptr(), aDesc.Size());
+ AddL(x, "D");
+ }
+
+void TCallbackWriter::AddL(const TDesC8& aDesc)
+ {
+ ADD(TInt, aDesc.Length(), i);
+ AddL(aDesc, "8");
+ }
+
+//// CSensibleSession ////
+
+void CSensibleSession::QueueCallbackL(CCallbackContext* aContext)
+ {
+ iCallbackQ.AddLast(*aContext);
+ if (aContext->CallbackRequiresResult())
+ {
+ if (aContext->Flag(EBlockServer))
+ {
+ //Server().Scheduler()->OnlyRunThisObject(&Server()); //TODO fix this at some point!
+ }
+ __ASSERT_ALWAYS(!iWaitingForCallbackResult, DebugPanic()); // This means someone queued a callback that required a result without specifying EBlockServer, and in the meantime someone else has queued another callback requiring a result. This isn't supported! If there is the remotest chance this could happen, then all must specify EBlockServer.
+ iWaitingForCallbackResult = ETrue;
+ CompleteNextCallback();
+ CActiveScheduler::Start();
+ // When we reach here, the client stuff has finished and aContext has our result in
+ // We call CompleteNextCallback again here since to reach this point the server must have received a EWriteCallbackResultAndReregister, which means it's ready for another callback
+ CompleteNextCallback();
+ if (aContext->Flag(EBlockServer))
+ {
+ Server().Scheduler()->StopFiltering();
+ }
+ iWaitingForCallbackResult = EFalse;
+ if (aContext->Flag(EResultIsLeaveCode))
+ {
+ iCallbackQ.Remove(*aContext);
+ TInt err = aContext->Result().integer;
+ delete aContext;
+ User::Leave(err);
+ }
+ // Nothing else needed
+ }
+ else
+ {
+ CompleteNextCallback();
+ }
+ }
+
+TBool CSensibleSession::DispatchCallback(TServerCallback& aCallback)
+ {
+ if (!iCallbackPending)
+ {
+ // Client not ready to be notified
+ return EFalse;
+ }
+
+ TPckg<TServerCallback> pkg(aCallback);
+ TRAPD(err, iCallbackNotifier.WriteL(SLOT(iCallbackNotifier, 0), pkg));
+ if (err)
+ {
+ PanicClient(iCallbackNotifier, EPanicBadDescriptor);
+ iCallbackPending = EFalse;
+ return EFalse;
+ }
+
+ iCallbackNotifier.Complete(KErrNone);
+ iCallbackPending = EFalse;
+ return ETrue;
+ }
+
+void CSensibleSession::CompleteNextCallback()
+ {
+ if (!iCallbackPending)
+ {
+ // Client not ready to be notified
+ return;
+ }
+ else if (iCallbackQ.IsEmpty())
+ {
+ // Nothing to complete yet
+ return;
+ }
+
+ CCallbackContext* c = iCallbackQ.First();
+ TPckg<TServerCallback> pkg(c->Callback());
+ #ifdef __HIDE_IPC_V1__
+ TRAPD(err, iCallbackNotifier.WriteL(0, pkg));
+ #else
+ TRAPD(err, iCallbackNotifier.WriteL(iCallbackNotifier.Ptr0(), pkg));
+ #endif
+ if (err)
+ {
+ PanicClient(iCallbackNotifier, EPanicBadDescriptor);
+ iCallbackPending = EFalse;
+ return;
+ }
+
+ iCallbackNotifier.Complete(KErrNone);
+ iCallbackPending = EFalse;
+
+ if (!c->CallbackRequiresResult() && !c->CallbackHasContext())
+ {
+ iCallbackQ.Remove(*c);
+ delete c;
+ }
+ else
+ {
+ c->SetFlags(EActive);
+ }
+ }