|
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 } |