|
1 // Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). |
|
2 // All rights reserved. |
|
3 // This component and the accompanying materials are made available |
|
4 // under the terms of "Eclipse Public License v1.0" |
|
5 // which accompanies this distribution, and is available |
|
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // |
|
15 |
|
16 /** |
|
17 @file |
|
18 @internalComponent |
|
19 */ |
|
20 |
|
21 #include <e32base.h> |
|
22 #include "sd_log.h" |
|
23 #include "sd_roles.h" |
|
24 |
|
25 |
|
26 #ifdef _DEBUG |
|
27 // Panic category for "absolutely impossible!" vanilla ASSERT()-type panics from this module |
|
28 // (if it could happen through user error then you should give it an explicit, documented, category + code) |
|
29 _LIT(KSpecAssert_ElemSvrDenDealrC, "ElemSvrDenDealrC"); |
|
30 #endif |
|
31 |
|
32 using namespace Den; |
|
33 using namespace CommsFW; |
|
34 |
|
35 // |
|
36 // CCommonDealer |
|
37 // |
|
38 |
|
39 /** |
|
40 This function will first do the ConstructL and if that succeeds it will |
|
41 start the server using the server name discovered during ConstructL. So when this |
|
42 function returns the new dealer object, its server is in effect running. |
|
43 */ |
|
44 EXPORT_C CCommonDealer* CCommonDealer::NewL(CCommonServer* aServer) |
|
45 { |
|
46 CCommonDealer* self = new(ELeave) CCommonDealer(aServer); |
|
47 CleanupStack::PushL(self); |
|
48 self->ConstructL(); |
|
49 CleanupStack::Pop(self); |
|
50 return self; |
|
51 } |
|
52 |
|
53 EXPORT_C void CCommonDealer::ConstructL() |
|
54 { |
|
55 iServer->StartL(iServer->ServerName()); |
|
56 } |
|
57 |
|
58 EXPORT_C CCommonDealer::CCommonDealer(CCommonServer* aServer) |
|
59 : iServer(aServer) |
|
60 { |
|
61 } |
|
62 |
|
63 EXPORT_C CCommonDealer::~CCommonDealer() |
|
64 { |
|
65 COMMONLOG((WorkerId(), KECommonServerTag, _L8("CCommonDealer::~CCommonDealer()."))); |
|
66 iParkedRequests.Close(); |
|
67 delete iServer; |
|
68 } |
|
69 |
|
70 EXPORT_C const TDesC& CCommonDealer::ServerName() const |
|
71 { |
|
72 return iServer->ServerName(); |
|
73 } |
|
74 |
|
75 /** |
|
76 When deleting a session it will be in response to a disconnect from the client, so we |
|
77 signal that to the session. If this thread is in the process of shutting down we need to |
|
78 check whether it was just waiting for this session (if this was the last one) and if this is |
|
79 the case we can initiate the asynchronous process of shutting down the worker thread. |
|
80 */ |
|
81 EXPORT_C void CCommonDealer::DeleteSession(CWorkerSession* aSession) |
|
82 { |
|
83 // If we ever see evidence of shutdowns nested inside active calls (ie "this" deleted prematurely then consider |
|
84 // switching handling to use a CAsyncOneShot) |
|
85 aSession->CompleteDisconnect(); |
|
86 if(iServer->WorkerThread().ShuttingDown() && CanShutdown()) |
|
87 { |
|
88 iServer->WorkerThread().SetDealerShutdownComplete(ETrue); |
|
89 iServer->WorkerThread().MaybeTriggerThreadShutdownCallback(); |
|
90 } |
|
91 } |
|
92 |
|
93 /** |
|
94 Iterate through all sessions and make sure they deploy the SubSessionProcessor on each owned sub-session. |
|
95 It is not known here what the SubSessionProcessor actually does as it is implemented by the caller. |
|
96 */ |
|
97 void CCommonDealer::ProcessSubSessions(TWorkerId aPeerId, CWorkerSession::TSubSessionProcessor aSubSessionProcessor, TAny* aArg) |
|
98 { |
|
99 iServer->SessionIterator().SetToFirst(); |
|
100 CWorkerSession* sess; |
|
101 while((sess = static_cast<CWorkerSession*>(iServer->SessionIterator()++)) != NULL) |
|
102 { |
|
103 sess->SubSessions().Lock(); |
|
104 |
|
105 static_cast<CWorkerSession*>(sess)->ProcessSubSessions(aPeerId, aSubSessionProcessor, aArg); |
|
106 |
|
107 sess->SubSessions().Unlock(); |
|
108 } |
|
109 } |
|
110 |
|
111 /** |
|
112 Forget all subsessions that belonged to a Player in the dead peer. The post-mortem cleanup mechanism will |
|
113 separately complete any client requests against these. |
|
114 */ |
|
115 EXPORT_C void CCommonDealer::CleanupDeadWorker(TWorkerId aPeerId) |
|
116 { |
|
117 iServer->SessionIterator().SetToFirst(); |
|
118 CSession2* sess; |
|
119 while((sess = iServer->SessionIterator()++) != NULL) |
|
120 { |
|
121 static_cast<CWorkerSession*>(sess)->CleanupDeadWorker(aPeerId); |
|
122 } |
|
123 } |
|
124 |
|
125 TInt CCommonDealer::SubsessionCountInPlayer(TWorkerId aPeerId) |
|
126 { |
|
127 TInt numSubSessions = 0; |
|
128 ProcessSubSessions(aPeerId, CWorkerSession::CountSubSessions, &numSubSessions); |
|
129 return numSubSessions; |
|
130 } |
|
131 |
|
132 /** |
|
133 Check if we can unbind from a worker. This is only possible if the local Dealer doesn't |
|
134 have any outstanding sub-sessions terminating in the peer and it doesn't have any sessions |
|
135 with outstanding closes against the peer. Otherwise the channel is still needed. |
|
136 */ |
|
137 TBool CCommonDealer::CanUnbindFromWorker(TWorkerId aWorker) |
|
138 { |
|
139 //TBDAA ASSERT_HOME_THREAD; |
|
140 if (!iServer->WorkerThread().PitBoss().Player(aWorker)) |
|
141 { |
|
142 return ETrue; |
|
143 } |
|
144 if(SubsessionCountInPlayer(aWorker) == 0) |
|
145 { |
|
146 // Check for any sessions which have outstanding session closes against the Worker |
|
147 iServer->SessionIterator().SetToFirst(); |
|
148 CSession2* sess; |
|
149 while((sess = iServer->SessionIterator()++) != NULL) |
|
150 { |
|
151 if(static_cast<CWorkerSession*>(sess)->IsPlayerInDisconnectList(aWorker)) |
|
152 { |
|
153 return EFalse; |
|
154 } |
|
155 } |
|
156 return ETrue; |
|
157 } |
|
158 return EFalse; |
|
159 } |
|
160 |
|
161 /** Add to the queue of indeterminate requests parked for re-evaluation once configuration has completed |
|
162 */ |
|
163 EXPORT_C TInt CCommonDealer::ParkRequest(CWorkerSession* aSession, const RMessage2& aMessage, TParkReason aReason) const |
|
164 { |
|
165 COMMONLOG((WorkerId(), KECommonBootingTag, _L8("CCommonDealer::ParkRequest Session(%08x) Message(%08x) - search back through the logs for the message handle to see info about the message when it was decoded in ServiceL"), aSession, aMessage.Handle())); |
|
166 TInt err = iParkedRequests.Append(TParkedRequest(aSession, aMessage, aReason)); |
|
167 |
|
168 #if defined _DEBUG || defined SYMBIAN_TRACE_ENABLE |
|
169 if( ( err == KErrNone ) && ( iParkedRequests.Count() == 1 ) ) |
|
170 { |
|
171 // Log hint to anyone who runs into a CPM deadlock due to a dependency on load order. |
|
172 // We log at the first ParkRequest to avoid unnecessary log messages as the risk only |
|
173 // exists if a module depends on a module with a parked request. |
|
174 // We log in C32Start as well as ESock and RDebug so that users are directed from the |
|
175 // CStart32 log to the ESock documentation in case they fail to realise the implications |
|
176 // of a park request. |
|
177 _LIT8(KParkRequestPotentialDeadlockWarning, "ESockSvr CCommonDealer::ParkRequest(reason=%d): If the log hangs before the \"ESockSvr startup modules loaded!\" log entry then there might be a deadlock involving a client calling too early during bootup before a component is ready. See Esock How To."); |
|
178 #ifdef _DEBUG |
|
179 RDebug::Printf(reinterpret_cast<const char*>(TPtrC8(KParkRequestPotentialDeadlockWarning).Ptr()), aReason); |
|
180 #endif |
|
181 COMMONLOG((WorkerId(), KECommonBootingTag, KParkRequestPotentialDeadlockWarning)); |
|
182 } |
|
183 #endif |
|
184 |
|
185 return err; |
|
186 } |
|
187 |
|
188 CCommonDealer::TParkedRequest::TParkedRequest(CWorkerSession* aSession, const RMessage2& aMessage, TParkReason aReason) |
|
189 : iSession(aSession), |
|
190 iMessage(aMessage), |
|
191 iReason(aReason) |
|
192 { |
|
193 } |
|
194 |
|
195 /** Complete all requests parked for the given reason |
|
196 */ |
|
197 void CCommonDealer::ReleaseParkedRequests(TParkReason aReason) |
|
198 { |
|
199 COMMONLOG((WorkerId(),KECommonBootingTag, _L8("CCommonDealer::ReleaseParkedRequests(aReason = %d) - %d total parked messages"), aReason, iParkedRequests.Count())); |
|
200 TInt i = 0; |
|
201 while(i < iParkedRequests.Count()) // traverse in order of arrival to minimise odd effects for clients (they shouldn't have such dependencies really, but could) |
|
202 { |
|
203 const TParkedRequest& request = iParkedRequests[i]; |
|
204 if(request.iReason == aReason) |
|
205 { |
|
206 // Check that the session still exists |
|
207 iServer->SessionIterator().SetToFirst(); |
|
208 CWorkerSession* ss; |
|
209 while((ss = static_cast<CWorkerSession*>(iServer->SessionIterator()++)) != NULL && ss != request.iSession) |
|
210 { |
|
211 } |
|
212 |
|
213 if(ss) |
|
214 { |
|
215 COMMONLOG((WorkerId(),KECommonBootingTag, _L8("CCommonDealer::ReleaseParkedRequests() - parked message %08x for sess %08x"), request.iMessage.Handle(), ss)); |
|
216 TRAPD(err, ss->ServiceL(request.iMessage)); |
|
217 if(err != KErrNone && !request.iMessage.IsNull()) |
|
218 { |
|
219 request.iMessage.Complete(err); |
|
220 } |
|
221 } |
|
222 iParkedRequests.Remove(i); |
|
223 } |
|
224 else |
|
225 { |
|
226 ++i; |
|
227 } |
|
228 } |
|
229 } |
|
230 |
|
231 #if defined(__ELEMENTS_MESSAGE_INTERCEPT_ACTIVE) |
|
232 |
|
233 /** Complete a selection of requests parked by debug requests |
|
234 */ |
|
235 void CCommonDealer::ReleaseDebugParkedRequests(CWorkerSession* aSess, TInt aSubSessHandle) |
|
236 { |
|
237 COMMONLOG((WorkerId(),KECommonBootingTag, _L8("CCommonDealer::ReleaseDebugParkedRequests(aSess=%08x, aSubSessHandle=%08x) - %d total parked messages"), aSess, aSubSessHandle, iParkedRequests.Count())); |
|
238 TInt i = 0; |
|
239 while(i < iParkedRequests.Count()) // traverse in order of arrival to minimise odd effects for clients (they shouldn't have such dependencies really, but could) |
|
240 { |
|
241 const TParkedRequest& request = iParkedRequests[i]; |
|
242 if(request.iReason == EDebugParking && |
|
243 (!aSess || aSess == request.iSession) && |
|
244 (!aSubSessHandle || aSubSessHandle == request.iMessage.Int3())) |
|
245 { |
|
246 // Check that the session still exists |
|
247 iServer->SessionIterator().SetToFirst(); |
|
248 CWorkerSession* ss; |
|
249 while((ss = static_cast<CWorkerSession*>(iServer->SessionIterator()++)) != NULL && ss != request.iSession) |
|
250 { |
|
251 } |
|
252 if(ss) |
|
253 { |
|
254 COMMONLOG((WorkerId(),KECommonBootingTag, _L8("CCommonDealer::ReleaseDebugParkedRequests() - parked message %08x for sess %08x"), request.iMessage.Handle(), ss)); |
|
255 TRAPD(err, ss->ServiceL(request.iMessage)); |
|
256 if(err != KErrNone && !request.iMessage.IsNull() && !ss->Disconnecting()) |
|
257 { |
|
258 request.iMessage.Complete(err); |
|
259 } |
|
260 } |
|
261 iParkedRequests.Remove(i); |
|
262 } |
|
263 else |
|
264 { |
|
265 ++i; |
|
266 } |
|
267 } |
|
268 } |
|
269 |
|
270 #endif // __ELEMENTS_MESSAGE_INTERCEPT_ACTIVE |
|
271 |
|
272 |
|
273 /** Once server configuration is complete any requests parked because they required the full environment can be |
|
274 re-evaluated |
|
275 */ |
|
276 EXPORT_C void CCommonDealer::ProcessConfigurationComplete(TConfigurationCompletionType aType) |
|
277 { |
|
278 // The enums are numerically equivalent (at time of writing), so have to hope compiler does the obvious here |
|
279 CCommonDealer::TParkReason reason(EIndeterminateDuringBoot); |
|
280 switch(aType) |
|
281 { |
|
282 case EModuleInitialisation: |
|
283 reason = EIndeterminateDuringBoot; |
|
284 break; |
|
285 case ETierMapping: |
|
286 reason = EAwaitingTierToWorkerMapping; |
|
287 break; |
|
288 default: |
|
289 __ASSERT_DEBUG(FALSE, User::Panic(KSpecAssert_ElemSvrDenDealrC, 1)); |
|
290 } |
|
291 ReleaseParkedRequests(reason); |
|
292 } |
|
293 |
|
294 void CCommonDealer::ProcessShutdownRequest(TCFShutdownType aType) |
|
295 { |
|
296 TBool shutdownImmediately = CanShutdown(); |
|
297 |
|
298 // If have to do now and there are still sessions then we exit anyway but suppress the heap check and log a rude message. We |
|
299 // used to delete the sessions but that isn't safe |
|
300 if(CommsFW::EImmediate==aType) |
|
301 { |
|
302 shutdownImmediately = ETrue; |
|
303 |
|
304 #ifdef SYMBIAN_TRACE_ENABLE |
|
305 iServer->SessionIterator().SetToFirst(); |
|
306 if(iServer->SessionIterator()++ != NULL) |
|
307 { |
|
308 TInt cnt = 0; |
|
309 iServer->SessionIterator().SetToFirst(); |
|
310 CSession2* ss; |
|
311 while((ss = iServer->SessionIterator()++) != NULL) |
|
312 { |
|
313 COMMONLOG((WorkerId(), KECommonServerTag, _L8("<==Session(%08x): remaining"), ss)); |
|
314 ++cnt; |
|
315 } |
|
316 COMMONLOG((WorkerId(), KECommonServerTag, _L8("NB! Immediate shutdown commanded but #%d client sessions remaining (bad test code?)"), cnt)); |
|
317 } |
|
318 #endif |
|
319 } |
|
320 |
|
321 if(WorkerThread().IsMainThread()) |
|
322 { |
|
323 if(CommsFW::EImmediate != aType) |
|
324 { |
|
325 WorkerThread().PitBoss().StartShutdown(); |
|
326 } |
|
327 // Even the PitBoss yields to the immediate shutdown |
|
328 if(shutdownImmediately) |
|
329 { |
|
330 WorkerThread().PitBoss().SessionShutdownComplete(); |
|
331 } |
|
332 } |
|
333 WorkerThread().SetDealerShutdownComplete(shutdownImmediately); |
|
334 } |
|
335 |
|
336 |
|
337 |
|
338 // |
|
339 // CCommonWorkerDealer |
|
340 // |
|
341 |
|
342 EXPORT_C CCommonWorkerDealer* CCommonWorkerDealer::NewL(CCommonServer* aServer) |
|
343 { |
|
344 CCommonWorkerDealer* self = new(ELeave) CCommonWorkerDealer(aServer); |
|
345 CleanupStack::PushL(self); |
|
346 self->ConstructL(); |
|
347 CleanupStack::Pop(self); |
|
348 return self; |
|
349 } |
|
350 |
|
351 EXPORT_C CCommonWorkerDealer::CCommonWorkerDealer(CCommonServer* aServer) |
|
352 : CCommonDealer(aServer) |
|
353 { |
|
354 } |
|
355 |
|
356 /** |
|
357 The name of the server will be postfixed with thread ID and Worker ID. If this worker is revived in |
|
358 case of a crash, it will have a different thread ID and not cause a name clash with the dead server. |
|
359 It is also useful for debugging/logging purposes. |
|
360 */ |
|
361 EXPORT_C void CCommonWorkerDealer::ConstructL() |
|
362 { |
|
363 iEligibleClients.ConstructL(); // Slightly unusual construction pattern, but does it reallly need to be CBase.. |
|
364 |
|
365 // Replace server name |
|
366 _LIT(KServerNameFormat, "-%d-%x"); |
|
367 TUint threadId = RThread().Id(); |
|
368 iServer->ServerName().AppendFormat(KServerNameFormat, WorkerId(), threadId); |
|
369 |
|
370 CCommonDealer::ConstructL(); //Start the server etc |
|
371 } |
|
372 |
|
373 EXPORT_C CCommonWorkerDealer::~CCommonWorkerDealer() |
|
374 { |
|
375 //CloseSessionSustainer(); |
|
376 } |
|
377 |
|
378 /** |
|
379 This method is called directly from the PitBoss thread, never from the local worker thread. |
|
380 */ |
|
381 EXPORT_C void CCommonWorkerDealer::AddEligiblePidL(TProcessId aId) |
|
382 { |
|
383 iEligibleClients.AddL(aId); |
|
384 } |
|
385 |
|
386 /** |
|
387 This method is called directly from the PitBoss thread, never from the local worker thread. |
|
388 */ |
|
389 EXPORT_C void CCommonWorkerDealer::RemoveEligiblePid(TProcessId aId) |
|
390 { |
|
391 iEligibleClients.Remove(aId); |
|
392 } |
|
393 |
|
394 /** |
|
395 The WorkerDealer is using this when a client does a Connect, to check whether the process has |
|
396 been allowed access by the PitBoss. |
|
397 */ |
|
398 EXPORT_C TBool CCommonWorkerDealer::IsEligible(TProcessId aId) |
|
399 { |
|
400 return iEligibleClients.IsEligible(aId); |
|
401 } |
|
402 |
|
403 /* |
|
404 void CCommonWorkerDealer::CloseSessionSustainer() |
|
405 { |
|
406 //iSessionSustainer.Close(); |
|
407 } |
|
408 */ |
|
409 |
|
410 // |
|
411 // EligibleClients |
|
412 // |
|
413 |
|
414 CCommonWorkerDealer::XEligibleClients::XEligibleClients() |
|
415 { |
|
416 // Initialize the list of eligible client PIDs |
|
417 for(TInt i=0; i<KMaxEligibleList; i++) |
|
418 { |
|
419 iEligibleClients[i] = KNullProcessId; |
|
420 } |
|
421 } |
|
422 |
|
423 CCommonWorkerDealer::XEligibleClients::~XEligibleClients() |
|
424 { |
|
425 iLock.Close(); // Lock created/opened in the ConstructL |
|
426 } |
|
427 |
|
428 void CCommonWorkerDealer::XEligibleClients::ConstructL() |
|
429 { |
|
430 User::LeaveIfError(iLock.CreateLocal()); |
|
431 } |
|
432 |
|
433 /** |
|
434 Adds ProcessID to list of eligible clients that can connect to dealer. |
|
435 @note This function MUST ONLY be called by the main dealer! |
|
436 @param aId The TProcessId to add to the list. |
|
437 @leave KErrServerBusy If table is full. |
|
438 */ |
|
439 void CCommonWorkerDealer::XEligibleClients::AddL(TProcessId aId) |
|
440 { |
|
441 TBool inserted=EFalse; |
|
442 |
|
443 iLock.Wait(); // Entering locked zone, no leaving or returning inside allowed. |
|
444 { |
|
445 for(TInt i=0; i<KMaxEligibleList && !inserted; i++) |
|
446 { |
|
447 if(KNullProcessId == iEligibleClients[i]) |
|
448 { |
|
449 iEligibleClients[i]=aId; |
|
450 inserted=ETrue; |
|
451 } |
|
452 } |
|
453 } |
|
454 iLock.Signal(); |
|
455 |
|
456 if(!inserted) |
|
457 { |
|
458 User::Leave(KErrServerBusy); |
|
459 } |
|
460 } |
|
461 |
|
462 /** |
|
463 Removes ProcessID from the list of eligible clients that can connect to dealer. |
|
464 @note This function MUST ONLY be called by the main dealer! |
|
465 @param aId The TProcessId to remove from the list. |
|
466 */ |
|
467 void CCommonWorkerDealer::XEligibleClients::Remove(TProcessId aId) |
|
468 { |
|
469 TBool removed=EFalse; |
|
470 |
|
471 iLock.Wait(); // Entering locked zone, no leaving or returning inside allowed. |
|
472 { |
|
473 for(TInt i=0; i<KMaxEligibleList && !removed; i++) |
|
474 { |
|
475 if(iEligibleClients[i]==aId) |
|
476 { |
|
477 iEligibleClients[i] = KNullProcessId; |
|
478 removed=ETrue; |
|
479 } |
|
480 } |
|
481 } |
|
482 iLock.Signal(); |
|
483 |
|
484 __ASSERT_DEBUG(removed, User::Panic(KSpecAssert_ElemSvrDenDealrC, 2)); // Wanted to remove a nonexisting PID, something is VERY WRONG in the state of Denmark. |
|
485 } |
|
486 |
|
487 /** |
|
488 Check whether a client is eligible to connect to this dealer. |
|
489 @param aId The TProcessId to look for in the list of eligible clients. |
|
490 */ |
|
491 TBool CCommonWorkerDealer::XEligibleClients::IsEligible(TProcessId aId) |
|
492 { |
|
493 TBool eligible=EFalse; |
|
494 |
|
495 iLock.Wait(); // Entering locked zone, no leaving or returning inside allowed. |
|
496 { |
|
497 for(TInt i=0; i<KMaxEligibleList && !eligible; i++) |
|
498 { |
|
499 if(iEligibleClients[i]==aId) |
|
500 { |
|
501 eligible=ETrue; |
|
502 } |
|
503 } |
|
504 } |
|
505 iLock.Signal(); |
|
506 |
|
507 return eligible; |
|
508 } |
|
509 |
|
510 |