libraries/clogger/src/SensibleServer.cpp
changeset 0 7f656887cf89
equal deleted inserted replaced
-1:000000000000 0:7f656887cf89
       
     1 // SensibleServer.cpp
       
     2 // 
       
     3 // Copyright (c) 2006 - 2010 Accenture. All rights reserved.
       
     4 // This component and the accompanying materials are made available
       
     5 // under the terms of the "Eclipse Public License v1.0"
       
     6 // which accompanies this distribution, and is available
       
     7 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 // 
       
     9 // Initial Contributors:
       
    10 // Accenture - Initial contribution
       
    11 //
       
    12 
       
    13 #include "SensibleServer.h"
       
    14 #include "SensibleServer_server_specific.h"
       
    15 
       
    16 #define DebugPanic() User::Panic(KDebugPanic, __LINE__)
       
    17 #define ClientPanic(aMsg) PanicClient(aMsg, - __LINE__)
       
    18 
       
    19 inline CShutdown::CShutdown()
       
    20 	:CTimer(-1)
       
    21 	{CActiveScheduler::Add(this);}
       
    22 inline void CShutdown::ConstructL()
       
    23 	{CTimer::ConstructL();}
       
    24 inline void CShutdown::Start(TInt aDelay)
       
    25 	{
       
    26 	if (aDelay)
       
    27 		{
       
    28 		After(aDelay);
       
    29 		}
       
    30 	}
       
    31 
       
    32 CSensibleServer::CSensibleServer()
       
    33 	:CServerBase(0,ESharableSessions)
       
    34 	{}
       
    35 
       
    36 CSensibleSession::CSensibleSession()
       
    37 	: iCallbackQ(_FOFF(CCallbackContext, iLink))
       
    38 	{}
       
    39 
       
    40 inline CSensibleServer& CSensibleSession::Server()
       
    41 	{return *static_cast<CSensibleServer*>(const_cast<CServerBase*>(CSessionBase::Server()));}
       
    42 
       
    43 inline CFilteringScheduler* CSensibleServer::Scheduler()
       
    44 	{return iScheduler;}
       
    45 
       
    46 #ifdef RUN_SERVER_WITH_EIKONENV
       
    47 
       
    48 #include <eikenv.h>
       
    49 #include <eikappui.h>
       
    50 
       
    51 class CSensibleAppUi : public CEikAppUi
       
    52 	{
       
    53 public:
       
    54 	void ConstructL();
       
    55 	~CSensibleAppUi();
       
    56 
       
    57 private:
       
    58 	CSensibleServer* iServer;
       
    59 	};
       
    60 
       
    61 void CSensibleAppUi::ConstructL()
       
    62 	{
       
    63 	BaseConstructL(ENoAppResourceFile /*|ENoScreenFurniture*/);
       
    64 
       
    65 	//
       
    66 	// create the server
       
    67 	iServer = new(ELeave) CServer_Class_Name();
       
    68 	iServer->ConstructL();
       
    69 	}
       
    70 
       
    71 CSensibleAppUi::~CSensibleAppUi()
       
    72 	{
       
    73 	delete iServer;
       
    74 	}
       
    75 
       
    76 // Have to derive from CONE scheduler otherwise ccoeenv complains mightily
       
    77 #define SCHEDULER_SUPER CCoeScheduler
       
    78 #define SCHEDULER_CONSTRUCTOR CCoeScheduler(CCoeEnv::Static())
       
    79 inline void ExitScheduler() { CBaActiveScheduler::Exit(); }
       
    80 
       
    81 #else
       
    82 
       
    83 #define SCHEDULER_SUPER CActiveScheduler
       
    84 #define SCHEDULER_CONSTRUCTOR CActiveScheduler()
       
    85 inline void ExitScheduler() { CActiveScheduler::Stop(); }
       
    86 
       
    87 #endif
       
    88 
       
    89 class CFilteringScheduler : public SCHEDULER_SUPER
       
    90 	{
       
    91 public:
       
    92 	CFilteringScheduler();
       
    93 	void WaitForAnyRequest();
       
    94 	//void OnlyRunThisObject(CActive* aActive); // This doesn't work atm!
       
    95 	void RunEverythingExcept(CActive* aActive, CActive* aActive2);
       
    96 	void StopFiltering();
       
    97 	
       
    98 private:
       
    99 	struct SObj {
       
   100 		CActive* iObject;
       
   101 		TInt iCachedStatus;
       
   102 		};
       
   103 	static const TInt KNumFilteredObjects = 2;
       
   104 	SObj iFilteredObjects[KNumFilteredObjects];
       
   105 	};
       
   106 
       
   107 inline TServerStart::TServerStart()
       
   108 	{}
       
   109 
       
   110 void TServerStart::SignalL()
       
   111 //
       
   112 // Signal the owning thread that the server has started successfully
       
   113 // This may itself fail
       
   114 //
       
   115 	{
       
   116 #ifdef EKA2
       
   117 	RProcess::Rendezvous(KErrNone);
       
   118 #else
       
   119 	RThread starter;
       
   120 	User::LeaveIfError(starter.Open(iId));
       
   121 	starter.RequestComplete(iStatus,KErrNone);
       
   122 	starter.Close();
       
   123 #endif
       
   124 	}
       
   125 
       
   126 
       
   127 ///////////////////////
       
   128 
       
   129 #ifndef __HIDE_IPC_V1__
       
   130 void CSensibleSession::CreateL(const CServer& aServer)
       
   131 //
       
   132 // 2nd phase construct for sessions - called by the CServer framework
       
   133 //
       
   134 	{
       
   135 	CSharableSession::CreateL(aServer);	// does not leave
       
   136 	CreateL();
       
   137 	}
       
   138 #endif
       
   139 
       
   140 void CSensibleSession::CreateL()
       
   141 	{
       
   142 	Server().AddSession();
       
   143 	}
       
   144 
       
   145 CSensibleSession::~CSensibleSession()
       
   146 	{
       
   147 	Server().DropSession();
       
   148 	}
       
   149 
       
   150 static void JustPanic(TAny*)
       
   151 	{
       
   152 	User::Panic(_L("Crash!"), 0);
       
   153 	}
       
   154 
       
   155 extern void CleanupPanicPushL()
       
   156 	{
       
   157 	CleanupStack::PushL(TCleanupItem(&JustPanic, 0));
       
   158 	}
       
   159 
       
   160 void CSensibleSession::ServiceL(const RMessage& aMessage)
       
   161 //
       
   162 // Handle a client request.
       
   163 // Leaving is handled by CSensibleServer::RunError() which reports the error code
       
   164 // to the client
       
   165 //
       
   166 	{
       
   167 	switch (aMessage.Function())
       
   168 		{
       
   169 	case ERegisterCallbackNotifier:
       
   170 		__ASSERT_ALWAYS(!iCallbackPending, ClientPanic(aMessage)); // Can't call Register is there's something already registered
       
   171 		iCallbackNotifier = aMessage;
       
   172 		iCallbackPending = ETrue;
       
   173 		CompleteNextCallback();
       
   174 		break;
       
   175 	case EGetCallbackContext:
       
   176 		{
       
   177 		__ASSERT_ALWAYS(!iCallbackQ.IsEmpty(), ClientPanic(aMessage));
       
   178 		CCallbackContext* c = iCallbackQ.First();
       
   179 		__ASSERT_ALWAYS(c->Flag(EActive), ClientPanic(aMessage));
       
   180 		__ASSERT_ALWAYS(c->CallbackHasContext(), ClientPanic(aMessage));
       
   181 		aMessage.WriteL(SLOT(aMessage, 0), *c->Context());
       
   182 		aMessage.Complete(KErrNone);
       
   183 		if (!c->CallbackRequiresResult())
       
   184 			{
       
   185 			iCallbackQ.Remove(*c);
       
   186 			delete c;
       
   187 			}
       
   188 		break;
       
   189 		}
       
   190 	case EWriteCallbackResultAndReregister:
       
   191 		{
       
   192 		__ASSERT_ALWAYS(!iCallbackPending, ClientPanic(aMessage)); // Can't call Register is there's something already registered
       
   193 		__ASSERT_ALWAYS(!iCallbackQ.IsEmpty(), ClientPanic(aMessage));
       
   194 		CCallbackContext* c = iCallbackQ.First();
       
   195 		__ASSERT_ALWAYS(c->Flag(EActive), ClientPanic(aMessage));
       
   196 		__ASSERT_ALWAYS(c->CallbackRequiresResult(), ClientPanic(aMessage));
       
   197 		
       
   198 		// Get the reregistering out of the way
       
   199 		iCallbackNotifier = aMessage;
       
   200 		iCallbackPending = ETrue;
       
   201 		
       
   202 		if (aMessage.Int2() < 0)
       
   203 			{
       
   204 			// Leave code
       
   205 			c->SetFlags(EResultIsLeaveCode);
       
   206 			c->Result().integer = aMessage.Int2();
       
   207 			}
       
   208 		else if (c->Flag(EResultHBufC16))
       
   209 			{
       
   210 			HBufC16* result = HBufC16::New(aMessage.Int2());
       
   211 			if (!result) 
       
   212 				{
       
   213 				c->SetFlags(EResultIsLeaveCode);
       
   214 				c->Result().integer = KErrNoMemory;
       
   215 				}
       
   216 			else
       
   217 				{
       
   218 				*c->Result().l = result;
       
   219 				TPtr16 ptr(result->Des());
       
   220 				aMessage.ReadL(SLOT(aMessage, 1), ptr);
       
   221 				}
       
   222 			}
       
   223 		else if (c->Flag(EResultHBufC8))
       
   224 			{
       
   225 			HBufC8* result = HBufC8::New(aMessage.Int2());
       
   226 			if (!result) 
       
   227 				{
       
   228 				c->SetFlags(EResultIsLeaveCode);
       
   229 				c->Result().integer = KErrNoMemory;
       
   230 				}
       
   231 			else
       
   232 				{
       
   233 				*c->Result().s = result;
       
   234 				TPtr8 ptr(result->Des());
       
   235 				aMessage.ReadL(SLOT(aMessage, 1), ptr);
       
   236 				}
       
   237 			}
       
   238 		else
       
   239 			{
       
   240 			// It's a TPkg
       
   241 			aMessage.ReadL(SLOT(aMessage, 1), *c->Result().pkg);
       
   242 			}
       
   243 		CActiveScheduler::Stop();
       
   244 		break;
       
   245 		}
       
   246 	case ECancelCallbackNotifier:
       
   247 		{
       
   248 		if (iCallbackPending)
       
   249 			{
       
   250 			iCallbackNotifier.Complete(KErrCancel);
       
   251 			iCallbackPending = EFalse;
       
   252 			}
       
   253 		aMessage.Complete(KErrNone);
       
   254 		break;
       
   255 		}
       
   256 	case EDummy:
       
   257 		aMessage.Complete(KErrNone);
       
   258 		break;
       
   259 
       
   260 	default:
       
   261 		if (!DoServiceL(aMessage))
       
   262 			{
       
   263 			PanicClient(aMessage, EPanicIllegalFunction);
       
   264 			}
       
   265 		break;
       
   266 		}
       
   267 	}
       
   268 
       
   269 TBool CSensibleSession::DoServiceL(const RMessage& aMessage)
       
   270 	{
       
   271 	// Subclasses override this!
       
   272 	aMessage.Complete(KErrNone);
       
   273 	return ETrue;
       
   274 	}
       
   275 
       
   276 void CShutdown::RunL()
       
   277 //
       
   278 // Initiate server exit when the timer expires
       
   279 //
       
   280 	{
       
   281 	ExitScheduler();
       
   282 	}
       
   283 
       
   284 void CSensibleServer::ConstructL()
       
   285 //
       
   286 // 2nd phase construction - ensure the timer and server objects are running
       
   287 //
       
   288 	{
       
   289 	StartL(KMyServerName);
       
   290 	iShutdown.ConstructL();
       
   291 	// ensure that the server still exits even if the 1st client fails to connect
       
   292 	iShutdown.Start(TransientServerShutdownTime());
       
   293 
       
   294 	// Now set up our special scheduler. This is tricky because of good old eikonenv doing stuff differently
       
   295 	// Basically without eikonenv, RunServer owns the old scheduler so we can't delete it
       
   296 	// However eikonenv will delete the new one as part of its shutdown!
       
   297 
       
   298 	iScheduler = new(ELeave) CFilteringScheduler();
       
   299 	iOldScheduler = CActiveScheduler::Replace(iScheduler);
       
   300 	
       
   301 #ifdef RUN_SERVER_WITH_EIKONENV
       
   302 	DISOWN(iOldScheduler);
       
   303 #endif
       
   304 	}
       
   305 
       
   306 CSensibleServer::~CSensibleServer()
       
   307 	{
       
   308 #ifndef RUN_SERVER_WITH_EIKONENV
       
   309 	DISOWN(iScheduler); // To mimic what CCoeEnv does
       
   310 #endif
       
   311 	}
       
   312 
       
   313 #ifdef __HIDE_IPC_V1__
       
   314 CSessionBase* CSensibleServer::NewSessionL(const TVersion& /*aVersion*/, const RMessage& /*aMessage*/) const
       
   315 #else
       
   316 CSessionBase* CSensibleServer::NewSessionL(const TVersion& /*aVersion*/) const
       
   317 #endif
       
   318 //
       
   319 // Cretae a new client session. This should really check the version number.
       
   320 //
       
   321 	{
       
   322 	return new(ELeave) CSensibleSession();
       
   323 	}
       
   324 
       
   325 void CSensibleServer::AddSession()
       
   326 //
       
   327 // A new session is being created
       
   328 // Cancel the shutdown timer if it was running
       
   329 //
       
   330 	{
       
   331 	++iSessionCount;
       
   332 	iShutdown.Cancel();
       
   333 	}
       
   334 
       
   335 void CSensibleServer::DropSession()
       
   336 //
       
   337 // A session is being destroyed
       
   338 // Start the shutdown timer if it is the last session.
       
   339 //
       
   340 	{
       
   341 	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
       
   342 		iShutdown.Start(TransientServerShutdownTime());
       
   343 	}
       
   344 
       
   345 
       
   346 TInt CSensibleServer::RunError(TInt aError)
       
   347 //
       
   348 // Handle an error from CSensibleSession::ServiceL()
       
   349 // A bad descriptor error implies a badly programmed client, so panic it;
       
   350 // otherwise report the error to the client
       
   351 //
       
   352 	{
       
   353 	if (aError==KErrBadDescriptor)
       
   354 		PanicClient(Message(),EPanicBadDescriptor);
       
   355 	else
       
   356 		Message().Complete(aError);
       
   357 	//
       
   358 	// The leave will result in an early return from CServer::RunL(), skipping
       
   359 	// the call to request another message. So do that now in order to keep the
       
   360 	// server running.
       
   361 	ReStart();
       
   362 	return KErrNone;	// handled the error fully
       
   363 	}
       
   364 
       
   365 /*void CSensibleServer::BlockAllAOsExceptServerRequests()
       
   366 	{
       
   367 	Scheduler()->OnlyRunThisObject(this);
       
   368 	}*/
       
   369 
       
   370 void CSensibleServer::BlockRequestsFrom(CActive* aActive1, CActive* aActive2)
       
   371 	{
       
   372 	Scheduler()->RunEverythingExcept(aActive1, aActive2);
       
   373 	}
       
   374 
       
   375 void CSensibleServer::StopBlocking()
       
   376 	{
       
   377 	Scheduler()->StopFiltering();
       
   378 	}
       
   379 
       
   380 TInt CSensibleServer::TransientServerShutdownTime() const
       
   381 	{
       
   382 	return 2000000; // Default to 2 seconds
       
   383 	}
       
   384 
       
   385 void PanicClient(const RMessage& aMessage, TInt aPanic)
       
   386 //
       
   387 // RMessage::Panic() also completes the message. This is:
       
   388 // (a) important for efficient cleanup within the kernel
       
   389 // (b) a problem if the message is completed a second time
       
   390 //
       
   391 	{
       
   392 	__DEBUGGER();
       
   393 	aMessage.Panic(KDebugPanic, aPanic);
       
   394 	}
       
   395 
       
   396 static void RunServerL(TServerStart& aStart)
       
   397 //
       
   398 // Perform all server initialisation, in particular creation of the
       
   399 // scheduler and server and then run the scheduler
       
   400 //
       
   401 	{
       
   402 
       
   403 #ifndef RUN_SERVER_WITH_EIKONENV
       
   404 	// create and install the active scheduler we need
       
   405 	CActiveScheduler* s=new(ELeave) CActiveScheduler;
       
   406 	CleanupStack::PushL(s);
       
   407 	CActiveScheduler::Install(s);
       
   408 #endif
       
   409 
       
   410 	//
       
   411 	// naming the server thread after the server helps to debug panics
       
   412 #ifdef __SECURE_API__
       
   413 	User::LeaveIfError(User::RenameThread(KMyServerName));
       
   414 #else
       
   415 	User::LeaveIfError(RThread().Rename(KMyServerName));
       
   416 #endif
       
   417 
       
   418 #ifdef RUN_SERVER_WITH_EIKONENV
       
   419 	// In this case, the server creation/destruction is pushed into CSensibleAppUi
       
   420 
       
   421 	// Give ourselves a eikonenv
       
   422 	CEikonEnv* env = new CEikonEnv;
       
   423 	CEikAppUi* appui = NULL;
       
   424 	TInt err = KErrNone;
       
   425 	if (env != NULL)
       
   426 		{
       
   427 		TRAP(err,
       
   428 			env->ConstructL(EFalse);
       
   429 			appui = new (ELeave)CSensibleAppUi();
       
   430 			appui->ConstructL();
       
   431 			env->SetAppUi(appui);
       
   432 			);
       
   433 		}
       
   434 	if (err == KErrNone)
       
   435 		{
       
   436 		//
       
   437 		// Initialisation complete, now signal the client
       
   438 		aStart.SignalL();
       
   439 		env->ExecuteD();
       
   440 		}
       
   441 	else
       
   442 		{
       
   443 		if (env != NULL)
       
   444 			{
       
   445 			env->DestroyEnvironment();
       
   446 			}
       
   447 		User::Leave(err); // This will tell the client that something's gone wrong
       
   448 		}
       
   449 #else	
       
   450 
       
   451 	//
       
   452 	// create the server
       
   453 	CSensibleServer* server = new(ELeave) CServer_Class_Name();
       
   454 	CleanupStack::PushL(server);
       
   455 	server->ConstructL();
       
   456 	//
       
   457 	// Initialisation complete, now signal the client
       
   458 	aStart.SignalL();
       
   459 	//
       
   460 	// Ready to run
       
   461 	CActiveScheduler::Start();
       
   462 
       
   463 	CleanupStack::PopAndDestroy(2, s); // server, scheduler
       
   464 
       
   465 #endif
       
   466 	}
       
   467 
       
   468 static TInt RunServer(TServerStart& aStart)
       
   469 //
       
   470 // Main entry-point for the server thread
       
   471 //
       
   472 	{
       
   473 	__UHEAP_MARK;
       
   474 	//
       
   475 	CTrapCleanup* cleanup=CTrapCleanup::New();
       
   476 	TInt r=KErrNoMemory;
       
   477 	if (cleanup)
       
   478 		{
       
   479 //#ifdef _DEBUG
       
   480 //		TRAP(r, CleanupPanicPushL(); RunServerL(aStart); CleanupStack::Pop());
       
   481 //#else
       
   482 		TRAP(r,RunServerL(aStart));
       
   483 //#endif
       
   484 		delete cleanup;
       
   485 		}
       
   486 	//
       
   487 	__UHEAP_MARKEND;
       
   488 	return r;
       
   489 	}
       
   490 
       
   491 
       
   492 #ifndef EKA2
       
   493 
       
   494 // The server binary is an "EPOCEXE" target type
       
   495 // Thus the server parameter passing and startup code for WINS and EPOC are
       
   496 // significantly different.
       
   497 
       
   498 #ifdef __WINS__
       
   499 
       
   500 // In WINS, the EPOCEXE target is a DLL with an entry point called WinsMain,
       
   501 // taking no parameters and returning TInt. This is not really valid as a thread
       
   502 // function which takes a TAny* parameter which we need.
       
   503 //
       
   504 // So the DLL entry-point WinsMain() is used to return a TInt representing the
       
   505 // real thread function within the DLL. This is good as long as
       
   506 // sizeof(TInt)>=sizeof(TThreadFunction).
       
   507 //
       
   508 
       
   509 static TInt ThreadFunction(TAny* aParms)
       
   510 //
       
   511 // WINS thread entry-point function.
       
   512 // The TServerStart objects is passed as the thread parameter
       
   513 //
       
   514 	{
       
   515 	return RunServer(*static_cast<TServerStart*>(aParms));
       
   516 	}
       
   517 
       
   518 IMPORT_C TInt WinsMain();
       
   519 EXPORT_C TInt WinsMain()
       
   520 //
       
   521 // WINS DLL entry-point. Just return the real thread function 
       
   522 // cast to TInt
       
   523 //
       
   524 	{
       
   525 	return reinterpret_cast<TInt>(&ThreadFunction);
       
   526 	}
       
   527 
       
   528 TInt E32Dll(TDllReason)
       
   529 	{
       
   530 	return KErrNone;
       
   531 	}
       
   532 
       
   533 #else
       
   534 
       
   535 //
       
   536 // In EPOC, the EPOCEXE target is a process, and the server startup
       
   537 // parameters are encoded in the command line
       
   538 //
       
   539 
       
   540 TInt TServerStart::GetCommand()
       
   541 	{
       
   542 	RProcess p;
       
   543 	if (p.CommandLineLength()!=sizeof(TServerStart)/sizeof(TText))
       
   544 		return KErrGeneral;
       
   545 	TPtr ptr(reinterpret_cast<TText*>(this),0,sizeof(TServerStart)/sizeof(TText));
       
   546 	p.CommandLine(ptr);
       
   547 	return KErrNone;
       
   548 	}
       
   549 
       
   550 TInt E32Main()
       
   551 //
       
   552 // Server process entry-point
       
   553 // Recover the startup parameters and run the server
       
   554 //
       
   555 	{
       
   556 	TServerStart start;
       
   557 	TInt r=start.GetCommand();
       
   558 	if (r==KErrNone)
       
   559 		r=RunServer(start);
       
   560 	return r;
       
   561 	}
       
   562 
       
   563 #endif
       
   564 
       
   565 #else
       
   566 
       
   567 TInt E32Main()
       
   568 //
       
   569 // Server process entry-point
       
   570 //
       
   571 	{
       
   572 	TServerStart start;
       
   573 	TInt r = RunServer(start);
       
   574 	return r;
       
   575 	}
       
   576 
       
   577 #endif
       
   578 
       
   579 //// CCallbackContext ////
       
   580 
       
   581 CCallbackContext::CCallbackContext(TCallbackCode aCode)
       
   582 	: iCallback(aCode)
       
   583 	{
       
   584 	iCallback.iCode = aCode;
       
   585 	}
       
   586 
       
   587 CCallbackContext::~CCallbackContext()
       
   588 	{
       
   589 	__ASSERT_DEBUG(!Flag(EActive), DebugPanic());
       
   590 	delete iContext;
       
   591 	}
       
   592 
       
   593 void CCallbackContext::SetFlags(TInt aFlags)
       
   594 	{
       
   595 	iFlags |= aFlags;
       
   596 	}
       
   597 
       
   598 TBool CCallbackContext::Flag(TInt aFlags) const
       
   599 	{
       
   600 	return iFlags & aFlags;
       
   601 	}
       
   602 
       
   603 void CCallbackContext::ClearFlags(TInt aFlags)
       
   604 	{
       
   605 	iFlags = iFlags & ~aFlags;
       
   606 	}
       
   607 
       
   608 TBool CCallbackContext::CallbackRequiresResult() const
       
   609 	{
       
   610 	//TODO
       
   611 	return EFalse;
       
   612 	}
       
   613 
       
   614 TBool CCallbackContext::CallbackHasContext() const
       
   615 	{
       
   616 	return (iContext != NULL);
       
   617 	}
       
   618 
       
   619 void CCallbackContext::SetResult(TDes8& aPkg)
       
   620 	{
       
   621 	iResult.pkg = &aPkg;
       
   622 	ClearFlags(EResultHBufC8 | EResultHBufC16);
       
   623 	}
       
   624 
       
   625 void CCallbackContext::SetResult(HBufC8*& aResult)
       
   626 	{
       
   627 	iResult.s = &aResult;
       
   628 	SetFlags(EResultHBufC8);
       
   629 	ClearFlags(EResultHBufC16);
       
   630 	}
       
   631 
       
   632 void CCallbackContext::SetResult(HBufC16*& aResult)
       
   633 	{
       
   634 	iResult.l = &aResult;
       
   635 	SetFlags(EResultHBufC16);
       
   636 	ClearFlags(EResultHBufC8);
       
   637 	}
       
   638 
       
   639 HBufC8* CCallbackContext::Context()
       
   640 	{
       
   641 	return iContext;
       
   642 	}
       
   643 
       
   644 CCallbackContext::TResult& CCallbackContext::Result()
       
   645 	{
       
   646 	return iResult;
       
   647 	}
       
   648 
       
   649 TServerCallback& CCallbackContext::Callback()
       
   650 	{
       
   651 	return iCallback;
       
   652 	}
       
   653 
       
   654 TCallbackWriter CCallbackContext::Writer()
       
   655 	{
       
   656 	TCallbackWriter res(iCallback, &iContext);
       
   657 	return res;
       
   658 	}
       
   659 
       
   660 //// CFilteringScheduler ////
       
   661 
       
   662 CFilteringScheduler::CFilteringScheduler()
       
   663 : SCHEDULER_CONSTRUCTOR
       
   664 	{}
       
   665 
       
   666 
       
   667 /*
       
   668 void CFilteringScheduler::OnlyRunThisObject(CActive* aActive)
       
   669 	{
       
   670 	__ASSERT_ALWAYS(!iObject, DebugPanic());
       
   671 	iObject = &aActive->iStatus;
       
   672 	iOnlyRunThisObject = ETrue;
       
   673 	User::Panic(_L("OnlyRunThisObject doesn't work yet!"), 0);
       
   674 	}
       
   675 */
       
   676 
       
   677 void CFilteringScheduler::RunEverythingExcept(CActive* aActive, CActive* aActive2)
       
   678 	{
       
   679 	__ASSERT_ALWAYS(!iFilteredObjects[0].iObject, DebugPanic());
       
   680 	iFilteredObjects[0].iObject = aActive;
       
   681 	iFilteredObjects[1].iObject = aActive2;
       
   682 
       
   683 	iFilteredObjects[0].iCachedStatus = KRequestPending;
       
   684 	iFilteredObjects[1].iCachedStatus = KRequestPending;
       
   685 	}
       
   686 
       
   687 void CFilteringScheduler::StopFiltering()
       
   688 	{
       
   689 	for (TInt i = 0; i < KNumFilteredObjects; i++)
       
   690 		{
       
   691 		SObj& obj = iFilteredObjects[i];
       
   692 		if (obj.iObject && obj.iCachedStatus != KRequestPending)
       
   693 			{
       
   694 			TRequestStatus* stat = &obj.iObject->iStatus;
       
   695 			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
       
   696 			}
       
   697 		obj.iObject = NULL;
       
   698 		}
       
   699 	}
       
   700 
       
   701 void CFilteringScheduler::WaitForAnyRequest()
       
   702 	{
       
   703 
       
   704 	if (!iFilteredObjects[0].iObject)
       
   705 		{
       
   706 		SCHEDULER_SUPER::WaitForAnyRequest();		
       
   707 		return;
       
   708 		}
       
   709 
       
   710 	for (;;)
       
   711 		{
       
   712 		SCHEDULER_SUPER::WaitForAnyRequest();
       
   713 		TBool found = EFalse;
       
   714 		for (TInt i = 0; i < KNumFilteredObjects; i++)
       
   715 			{
       
   716 			SObj& obj = iFilteredObjects[i];
       
   717 			TBool isReadyToRun = obj.iObject && obj.iObject->IsActive() && obj.iObject->iStatus != KRequestPending;
       
   718 			if (isReadyToRun)
       
   719 				{
       
   720 				// Our target object has completed, so mark it back as pending, and consume the signal
       
   721 				ASSERT(obj.iCachedStatus == KRequestPending); // If this is already set something has gone quite wrong with our logic
       
   722 				obj.iCachedStatus = obj.iObject->iStatus.Int();
       
   723 				*((TInt*)&obj.iObject->iStatus) = KRequestPending; // Cast this to a TInt* to prevent TRequestStatus::operator= changing the flags
       
   724 				found = ETrue;
       
   725 				break;
       
   726 				}
       
   727 			}
       
   728 		if (!found) break; // It wasn't one of our objects that completed so no need to go round the loop again
       
   729 		}
       
   730 	}
       
   731 
       
   732 //// TCallbackWriter ////
       
   733 
       
   734 TCallbackWriter::TCallbackWriter(TServerCallback& aCallback, HBufC8** aContext)
       
   735 : iCallback(aCallback), iContext(aContext), iBuf((TUint8*)aCallback.iData.Ptr(), aCallback.iData.MaxLength()), iInContext(EFalse)
       
   736 	{
       
   737 	if (iContext) *iContext = NULL;
       
   738 	}
       
   739 
       
   740 void TCallbackWriter::AddL(const TDesC8& aData, char* aType)
       
   741 	{
       
   742 	__ASSERT_DEBUG(aData.Length(), DebugPanic());
       
   743 	__ASSERT_DEBUG(aType, DebugPanic());
       
   744 	TInt bytesRemaining = iBuf.MaxSize() - iBuf.Size();
       
   745 
       
   746 	TInt typeSize = 1;
       
   747 	if (*aType == 'D')
       
   748 		{
       
   749 		// TDesC16s need to be 2-byte aligned, so make sure the data after the type byte will be
       
   750 		if (!(iBuf.Length() & 1)) typeSize = 2;
       
   751 		}
       
   752 
       
   753 	if (aData.Size() + typeSize > bytesRemaining)
       
   754 		{
       
   755 		// No room for arg and type
       
   756  		if (!iContext) User::Leave(KErrNoMemory);
       
   757 		if (!iInContext)
       
   758 			{
       
   759 			// so construct context
       
   760 			if (*aType == 'D') typeSize = 2;
       
   761 			*iContext = HBufC8::NewL(aData.Length() + typeSize);
       
   762 			iInContext = ETrue;
       
   763 			}
       
   764 		else
       
   765 			{
       
   766 			// realloc
       
   767 			HBufC8* newContext = (*iContext)->ReAlloc(Max(iBuf.MaxSize() * 2, iBuf.MaxSize() + aData.Size() + typeSize));
       
   768 			if (!newContext)
       
   769 				{
       
   770 				delete *iContext;
       
   771 				*iContext = NULL;
       
   772 				User::Leave(KErrNoMemory);
       
   773 				}
       
   774 			*iContext = newContext;
       
   775 			}
       
   776 		iBuf.Set((*iContext)->Des());
       
   777 		}
       
   778 	iBuf.Append(*aType);
       
   779 	if (typeSize == 2) iBuf.Append('-'); // Padding
       
   780 	iBuf.Append(aData);
       
   781 	if (iInContext) 
       
   782 		{
       
   783 		iCallback.iContextLength = iBuf.Length();
       
   784 		}
       
   785 	else
       
   786 		{
       
   787 		iCallback.iData.SetLength(iBuf.Length()); // Because a TPtr pointing to a buf doesn't behave like one pointing to an HBufC, sigh...
       
   788 		}
       
   789 
       
   790 	}
       
   791 
       
   792 #define ADD(T, arg, type) { TPckg<T> x(arg); AddL(x, #type); }
       
   793 
       
   794 void TCallbackWriter::AddL(TInt aInt)		{ ADD(TInt, aInt, i); }
       
   795 void TCallbackWriter::AddL(TUint aInt)		{ ADD(TUint, aInt, u); }
       
   796 void TCallbackWriter::AddL(TPoint aPoint)	{ ADD(TPoint, aPoint, P); }
       
   797 void TCallbackWriter::AddL(TSize aSize)		{ ADD(TSize, aSize, S); }
       
   798 void TCallbackWriter::AddL(TRgb aRgb)		{ ADD(TRgb, aRgb, G); }
       
   799 void TCallbackWriter::AddL(TRect aRect)		{ ADD(TRect, aRect, R); }
       
   800 
       
   801 void TCallbackWriter::AddL(const TDesC16& aDesc)
       
   802 	{
       
   803 	ADD(TInt, aDesc.Length(), i);
       
   804 	TPtrC8 x((TUint8*)aDesc.Ptr(), aDesc.Size());
       
   805 	AddL(x, "D");
       
   806 	}
       
   807 
       
   808 void TCallbackWriter::AddL(const TDesC8& aDesc)
       
   809 	{
       
   810 	ADD(TInt, aDesc.Length(), i);
       
   811 	AddL(aDesc, "8");
       
   812 	}
       
   813 
       
   814 //// CSensibleSession ////
       
   815 
       
   816 void CSensibleSession::QueueCallbackL(CCallbackContext* aContext)
       
   817 	{
       
   818 	iCallbackQ.AddLast(*aContext);
       
   819 	if (aContext->CallbackRequiresResult())
       
   820 		{
       
   821 		if (aContext->Flag(EBlockServer))
       
   822 			{
       
   823 			//Server().Scheduler()->OnlyRunThisObject(&Server()); //TODO fix this at some point!
       
   824 			}
       
   825 		__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.
       
   826 		iWaitingForCallbackResult = ETrue;
       
   827 		CompleteNextCallback();
       
   828 		CActiveScheduler::Start();
       
   829 		// When we reach here, the client stuff has finished and aContext has our result in
       
   830 		// 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
       
   831 		CompleteNextCallback();
       
   832 		if (aContext->Flag(EBlockServer))
       
   833 			{
       
   834 			Server().Scheduler()->StopFiltering();
       
   835 			}
       
   836 		iWaitingForCallbackResult = EFalse;
       
   837 		if (aContext->Flag(EResultIsLeaveCode))
       
   838 			{
       
   839 			iCallbackQ.Remove(*aContext);
       
   840 			TInt err = aContext->Result().integer;
       
   841 			delete aContext;
       
   842 			User::Leave(err);
       
   843 			}
       
   844 		// Nothing else needed
       
   845 		}
       
   846 	else
       
   847 		{
       
   848 		CompleteNextCallback();
       
   849 		}
       
   850 	}
       
   851 
       
   852 TBool CSensibleSession::DispatchCallback(TServerCallback& aCallback)
       
   853 	{
       
   854 	if (!iCallbackPending)
       
   855 		{
       
   856 		// Client not ready to be notified
       
   857 		return EFalse;
       
   858 		}
       
   859 
       
   860 	TPckg<TServerCallback> pkg(aCallback);
       
   861 	TRAPD(err, iCallbackNotifier.WriteL(SLOT(iCallbackNotifier, 0), pkg));
       
   862 	if (err)
       
   863 		{
       
   864 		PanicClient(iCallbackNotifier, EPanicBadDescriptor);
       
   865 		iCallbackPending = EFalse;
       
   866 		return EFalse;
       
   867 		}
       
   868 	
       
   869 	iCallbackNotifier.Complete(KErrNone);
       
   870 	iCallbackPending = EFalse;
       
   871 	return ETrue;
       
   872 	}
       
   873 
       
   874 void CSensibleSession::CompleteNextCallback()
       
   875 	{
       
   876 	if (!iCallbackPending)
       
   877 		{
       
   878 		// Client not ready to be notified
       
   879 		return;
       
   880 		}
       
   881 	else if (iCallbackQ.IsEmpty())
       
   882 		{
       
   883 		// Nothing to complete yet
       
   884 		return;
       
   885 		}
       
   886 	
       
   887 	CCallbackContext* c = iCallbackQ.First();
       
   888 	TPckg<TServerCallback> pkg(c->Callback());
       
   889 	#ifdef __HIDE_IPC_V1__
       
   890 	TRAPD(err, iCallbackNotifier.WriteL(0, pkg));
       
   891 	#else
       
   892 	TRAPD(err, iCallbackNotifier.WriteL(iCallbackNotifier.Ptr0(), pkg));
       
   893 	#endif
       
   894 	if (err)
       
   895 		{
       
   896 		PanicClient(iCallbackNotifier, EPanicBadDescriptor);
       
   897 		iCallbackPending = EFalse;
       
   898 		return;
       
   899 		}
       
   900 	
       
   901 	iCallbackNotifier.Complete(KErrNone);
       
   902 	iCallbackPending = EFalse;
       
   903 	
       
   904 	if (!c->CallbackRequiresResult() && !c->CallbackHasContext())
       
   905 		{
       
   906 		iCallbackQ.Remove(*c);
       
   907 		delete c;
       
   908 		}
       
   909 	else
       
   910 		{
       
   911 		c->SetFlags(EActive);
       
   912 		}
       
   913 	}