libraries/clogger/src/SensibleServer.cpp
changeset 0 7f656887cf89
--- /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);
+		}
+	}