diff -r 000000000000 -r 7f656887cf89 libraries/clogger/src/SensibleServer.cpp --- /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(const_cast(CSessionBase::Server()));} + +inline CFilteringScheduler* CSensibleServer::Scheduler() + {return iScheduler;} + +#ifdef RUN_SERVER_WITH_EIKONENV + +#include +#include + +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(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(&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(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 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 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 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); + } + }