|
1 // Copyright (c) 2005-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 "cs_thread.h" |
|
22 #include "C32LOG.H" |
|
23 #include "CS_STD.H" |
|
24 #include "cs_roles.h" |
|
25 #include "cs_msgs.h" |
|
26 #include "cs_glob.h" |
|
27 #include <elements/nm_address_internal.h> |
|
28 |
|
29 #ifdef SYMBIAN_ENABLE_SPLIT_HEADERS |
|
30 #include <cflog.h> |
|
31 #endif |
|
32 |
|
33 #include <elements/cftransportmsg.h> |
|
34 |
|
35 using namespace CommsFW; |
|
36 |
|
37 #ifndef KLogSubSysSerComms |
|
38 __CFLOG_STMT(_LIT8(KLogSubSysSerComms, "SerComms");) |
|
39 #endif |
|
40 |
|
41 // |
|
42 // CC32WorkerThread class definitions |
|
43 // |
|
44 |
|
45 CC32WorkerThread* CC32WorkerThread::NewL(TCFModuleInfo* aModuleInfo) |
|
46 { |
|
47 CleanupStack::PushL(TCleanupItem(DeleteHBufC8, aModuleInfo->iIniData)); |
|
48 CC32WorkerThread* self = new (ELeave) CC32WorkerThread; |
|
49 CleanupStack::PushL(self); |
|
50 self->ConstructL(aModuleInfo); |
|
51 CleanupStack::Pop(self); |
|
52 CleanupStack::PopAndDestroy(); // aModuleInfo->iIniData |
|
53 return self; |
|
54 } |
|
55 |
|
56 CC32WorkerThread::CC32WorkerThread() |
|
57 #ifdef _DEBUG |
|
58 : iFailType(RAllocator::ENone) |
|
59 #endif |
|
60 { |
|
61 } |
|
62 |
|
63 /** |
|
64 The worker thread secondary construction will create the relevant Player/Dealer |
|
65 instances needed as well as the channel handler to the Root Server. If and only if it |
|
66 is the main thread it will also create the PitBoss. |
|
67 @note If it has a Dealer and is not the main thread it is a WorkerDealer. |
|
68 */ |
|
69 void CC32WorkerThread::ConstructL(TCFModuleInfo* aModuleInfo) |
|
70 { |
|
71 TBool isDealer; |
|
72 TBool isPlayer; |
|
73 __CFLOG_OPEN; |
|
74 C32LOG1(KC32Bootup,_L8("CC32WorkerThread::ConstructL Determining roles")); |
|
75 DetermineRoleL(*aModuleInfo->iIniData, isDealer, isPlayer); |
|
76 |
|
77 iWorkerRegister = CC32WorkerRegister::NewL(WorkerId(), NULL); |
|
78 iTransport = CCommsTransport::NewL(*iWorkerRegister, NULL, NULL); |
|
79 iTransport->RegisterLegacyInterface(this); |
|
80 |
|
81 // initglobals allocates on heap and stores pointer in TLS, so |
|
82 // requires special cleanup if we leave beyond here - handled in RunC32Thread |
|
83 C32GlobalUtil::InitC32GlobalsL(this); |
|
84 |
|
85 #ifdef _DEBUG |
|
86 if(aModuleInfo->iIniData!=NULL) |
|
87 { |
|
88 // Support for simulated heap allocation failures: place "AllocFail= type rate" in .CMI file |
|
89 // where: |
|
90 // type == ERandom, ETrueRandom, EDeterministic, EFailNext |
|
91 // rate == rate/chance of failure |
|
92 // See RAllocator documentation for details. Best to set this for the heap owner only, since the |
|
93 // last one processed will determine function for all heap users & it could get confusing. |
|
94 _LIT8(KAllocFailLabel, "AllocFail"); |
|
95 TPtrC8 allocFail; |
|
96 if(GetVarFromIniData(*(aModuleInfo->iIniData), KNullDesC8, KAllocFailLabel, allocFail)) |
|
97 { |
|
98 TLex8 lex(allocFail); |
|
99 TPtrC8 failTypeTok = lex.NextToken(); |
|
100 const struct |
|
101 { |
|
102 const TText8* iLabel; |
|
103 RAllocator::TAllocFail iType; |
|
104 } failModes[] = |
|
105 { |
|
106 { _S8("ERandom"), RAllocator::ERandom }, |
|
107 { _S8("ETrueRandom"), RAllocator::ETrueRandom }, |
|
108 { _S8("EDeterministic"), RAllocator::EDeterministic }, |
|
109 { _S8("EFailNext"), RAllocator::EFailNext } |
|
110 }; |
|
111 TInt i; |
|
112 for(i = sizeof(failModes) / sizeof(failModes[0]) - 1; i >= 0; --i) |
|
113 { |
|
114 if(TPtrC8(failModes[i].iLabel).CompareF(failTypeTok) == 0) |
|
115 { |
|
116 break; |
|
117 } |
|
118 } |
|
119 TInt rate = 0; |
|
120 lex.SkipSpace(); |
|
121 if(i < 0 || lex.Val(rate) != KErrNone) |
|
122 { |
|
123 // Already in Debug mode so just log and panic |
|
124 C32LOG1(KC32Bootup, _L("Panic - Corrupt Ini data - AllocFail param")); |
|
125 Fault(EBadIniData); |
|
126 } |
|
127 iFailType = failModes[i].iType; |
|
128 iFailRate = rate; |
|
129 } |
|
130 |
|
131 /* |
|
132 For debug builds, ensure that the array inside the cleanup stack will never |
|
133 need to allocate any memory. This aids checking for leaked cells across a |
|
134 sequence of calls that is heap-balanced. |
|
135 */ |
|
136 { |
|
137 const TInt KStretchExtent = 10; |
|
138 TRAP_IGNORE( |
|
139 for(TInt i = 0; i < KStretchExtent; i++) |
|
140 { |
|
141 CleanupStack::PushL((TAny*)1); |
|
142 } |
|
143 CleanupStack::Pop(KStretchExtent); |
|
144 ) |
|
145 } |
|
146 |
|
147 } |
|
148 #endif // _DEBUG |
|
149 |
|
150 if(isDealer) |
|
151 { |
|
152 if(IsMainThread()) |
|
153 { |
|
154 C32LOG(KC32Bootup, _L8("I am the Main Thread. Creating Dealer.")); |
|
155 iDealer = CC32Dealer::NewL(this,*(aModuleInfo->iIniData)); |
|
156 if(iDealer->ThreadManager()) |
|
157 { |
|
158 if(iDealer->ThreadManager()->DefaultThread() == KMainThreadId) |
|
159 { |
|
160 /* |
|
161 The default thread which loads unlisted CSYs is the dealer/main thread (workerId=0), |
|
162 this isn't normal in a multi-threaded configuration, but can happen due to |
|
163 1. BAD configuration (would fault in debug builds to signal a corrupt configuration, but not in release) |
|
164 2. CMI files with no C32SerComms group field in it. |
|
165 3. CSYList=* field present in IniData section of dealer/main thread (workerId=0) [valid, but discouraged in multi-threaded conf] |
|
166 */ |
|
167 C32LOG(KC32Warning,_L8("Dealer is default thread for loading unlisted CSYs! A BAD configuration perhaps ?")); |
|
168 isPlayer=ETrue; |
|
169 } |
|
170 } |
|
171 } |
|
172 } |
|
173 else |
|
174 { |
|
175 SetDealerShutdownComplete(ETrue); |
|
176 } |
|
177 |
|
178 if(isPlayer) |
|
179 { |
|
180 C32LOG1(KC32Bootup, _L8("I am a Player, creating instance.")); |
|
181 iPlayer = CC32Player::NewL(this); |
|
182 } |
|
183 else |
|
184 { |
|
185 SetPlayerShutdownComplete(ETrue); |
|
186 } |
|
187 |
|
188 |
|
189 // Start listening for binds, etc, from the RS |
|
190 C32LOG1(KC32Bootup, _L8("CC32WorkerThread::ConstructL Init RS ChannelHandler")); |
|
191 iChannelHandler = CCommChannelHandler::NewL(aModuleInfo->iRxQueues, aModuleInfo->iTxQueues, this); |
|
192 C32LOG1(KC32Bootup, _L8("CC32WorkerThread::ConstructL Done")); |
|
193 } |
|
194 |
|
195 CC32WorkerThread::~CC32WorkerThread() |
|
196 { |
|
197 C32LOG2(KC32Shutdown, _L8("CC32WorkerThread(%08x)::~CC32WorkerThread()"), this); |
|
198 delete iTransport; |
|
199 delete iChannelHandler; |
|
200 delete iPlayer; |
|
201 if(IsMainThread()) |
|
202 { |
|
203 delete iDealer; |
|
204 } |
|
205 delete iWorkerRegister; |
|
206 C32LOG2(KC32Shutdown, _L8("CC32WorkerThread(%08x)::~CC32WorkerThread() complete"), this); |
|
207 __CFLOG_CLOSE; |
|
208 } |
|
209 |
|
210 /** Determine from inidata whether this worker is Dealer, Player or both. */ |
|
211 void CC32WorkerThread::DetermineRoleL(const TDesC8& aIniData, TBool &aIsDealer, TBool &aIsPlayer) |
|
212 { |
|
213 aIsDealer = EFalse; |
|
214 aIsPlayer = EFalse; |
|
215 // BC when No IniData present, this CPM is both Dealer and Player |
|
216 if(&aIniData==NULL) |
|
217 { |
|
218 aIsDealer = ETrue; |
|
219 aIsPlayer = ETrue; |
|
220 } |
|
221 else |
|
222 { |
|
223 TPtrC8 roleValue; |
|
224 // Role missing - Dealer |
|
225 if (!GetVarFromIniData(aIniData, KNullDesC8, KRoleLabel, roleValue)) |
|
226 { |
|
227 aIsDealer = ETrue; |
|
228 } |
|
229 else |
|
230 { |
|
231 // Role present - player |
|
232 if (roleValue.CompareF(KPlayerRole)==0) |
|
233 { |
|
234 aIsPlayer = ETrue; |
|
235 } |
|
236 // Role present - dealer |
|
237 else if (roleValue.CompareF(KDealerRole)==0) |
|
238 { |
|
239 aIsDealer = ETrue; |
|
240 } |
|
241 // Role present - invalid arg |
|
242 else |
|
243 { |
|
244 C32LOG2(KC32Warning, _L8("Invalid Role in [IniData] section: %S"),&roleValue); |
|
245 __ASSERT_DEBUG(0,Fault(EBadIniData)); |
|
246 } |
|
247 } |
|
248 |
|
249 // WorkerId missing |
|
250 TPtrC8 workerIdValue; |
|
251 if(!GetVarFromIniData(aIniData, KNullDesC8, KWorkerIdLabel, workerIdValue)) |
|
252 { |
|
253 C32LOG1(KC32Warning, _L8("Corrupt [IniData]: WorkerId missing.")); |
|
254 __ASSERT_DEBUG(0,Fault(EBadIniData)); |
|
255 // leave if ini data corrupt rather than panic unless debug |
|
256 User::Leave(KErrCorrupt); |
|
257 } |
|
258 |
|
259 User::LeaveIfError(ConvertVal(workerIdValue, iWorkerId)); |
|
260 // check for boundary values for WorkerId |
|
261 if(iWorkerId > TC32WorkerThreadPublicInfo::EMaxWorkerThreadId) |
|
262 { |
|
263 C32LOG2(KC32Warning, _L8("Corrupt [IniData]: Invalid WorkerId: %d"),iWorkerId); |
|
264 __ASSERT_DEBUG(0,Fault(EBadIniData)); |
|
265 // leave if ini data corrup rather than panic if not debug |
|
266 User::Leave(KErrCorrupt); |
|
267 } |
|
268 |
|
269 if(iWorkerId == TC32WorkerThreadPublicInfo::EMainThread && aIsDealer == EFalse) |
|
270 { |
|
271 C32LOG1(KC32Warning, _L8("Worker Id zero with no dealer role. overriding")); |
|
272 aIsDealer = ETrue; |
|
273 } |
|
274 |
|
275 // CSYList present - Player |
|
276 TPtrC8 csyListValue; |
|
277 if(GetVarFromIniData(aIniData, KNullDesC8, KCSYListLabel, csyListValue)) |
|
278 { |
|
279 aIsPlayer = ETrue; |
|
280 } |
|
281 } // End of else block [if(aIniData==NULL)] |
|
282 } |
|
283 |
|
284 |
|
285 |
|
286 /** |
|
287 Check that our end sub-module address is correctly named: the sub-module name must be numeric and match our worker id. |
|
288 */ |
|
289 TInt CC32WorkerThread::DecodePeerId(const TCFSubModuleAddress* aSubModule1, const TCFSubModuleAddress* aSubModule2, TWorkerId& aPeerId) |
|
290 { |
|
291 TInt err = KErrNone; |
|
292 if(ConvertVal(aSubModule1->SubModule(), aPeerId) != KErrNone || aPeerId != WorkerId()) |
|
293 { |
|
294 err = KErrCorrupt; |
|
295 } |
|
296 else |
|
297 { |
|
298 if(ConvertVal(aSubModule2->SubModule(), aPeerId) != KErrNone || |
|
299 aPeerId > TC32WorkerThreadPublicInfo::EMaxWorkerThreadId) |
|
300 { |
|
301 err = KErrCorrupt; |
|
302 } |
|
303 } |
|
304 return err; |
|
305 } |
|
306 |
|
307 |
|
308 /** |
|
309 Deals with binding requests from the Root Server. Note that although the Root Server allows |
|
310 multiple identical bindings C32 does not allow this and will panic if the binding already exists. |
|
311 Bindings are expressed in C32 by CCommsTransport. Since all sub-module |
|
312 names are unique across all C32 instances (they are the individual owning worker ID converted to text) |
|
313 they can be used here. I.e. the remote end sub-module is converted back to int and used to insert the |
|
314 CCommsTransport into an array in the position corresponding to the remote end sub-module name/number. |
|
315 As for responding to the bind request there are two cases: |
|
316 -# This worker is a "dumb" Player: Send bind response immediately. |
|
317 -# This worker is the Main thread: Send introduction messages to remote end and |
|
318 postpone bind response until introduction response messages have arrived back. |
|
319 @see CCommsTransport |
|
320 */ |
|
321 void CC32WorkerThread::CFBindMessageReceived(const TCFBindMsg& aMsg) |
|
322 { |
|
323 __CFLOG_SMADDR2(( KLogSubSysSerComms, KLogSubSysSerComms, _L8("W%d: CFBindMessageReceived %S <=> %S"), |
|
324 WorkerId(), |
|
325 &aMsg.SubModule1()->Printable(__FullModName1), |
|
326 &aMsg.SubModule2()->Printable(__FullModName2) )); |
|
327 TWorkerId bindId; |
|
328 TInt err = DecodePeerId(aMsg.SubModule1(), aMsg.SubModule2(), bindId); |
|
329 ASSERT(bindId <= TC32WorkerThreadPublicInfo::EMaxWorkerThreadId); |
|
330 if(err == KErrNone) |
|
331 { |
|
332 if(iTransport->PeerReachable(bindId)) |
|
333 { |
|
334 C32LOG2(KC32Bootup, _L("%d Already exists. Error in configuration of cmi files"),bindId); |
|
335 //Must panic b/c the new module will be left in a half bound state and will tend to get out of hand |
|
336 __ASSERT_DEBUG(0, User::Panic(KCFTransportPanic, ECFTransInvalidWorkerId)); |
|
337 } |
|
338 else |
|
339 { |
|
340 err = iTransport->EstablishTransportToPeer(bindId, aMsg.ChannelInput(), aMsg.ChannelOutput()); |
|
341 // Main thread introduces itself; workers wait passively for this |
|
342 if(err == KErrNone) |
|
343 { |
|
344 iTransport->SetLastRequestIdConcerningPeer(bindId, aMsg.Identifier()); |
|
345 if(IsMainThread()) |
|
346 { |
|
347 C32LOG1(KC32Bootup, _L("Sending Introduction Message to Worker")); |
|
348 DealerByRef().SendIntroductionToWorker(bindId); |
|
349 } |
|
350 } |
|
351 } |
|
352 } |
|
353 /* Main dealer only completes when it has received the introduction response messages from peers, |
|
354 dumb players respond immediately. */ |
|
355 if(err!=KErrNone || !IsMainThread()) |
|
356 { |
|
357 TCFBindCompleteMsg respMsg(aMsg.Identifier(), err); |
|
358 VERIFY_RESULT(iChannelHandler->Send(respMsg), KErrNone); |
|
359 } |
|
360 } |
|
361 |
|
362 void CC32WorkerThread::SetDealerShutdownComplete(TBool aComplete) |
|
363 { |
|
364 // Backsliding not permitted; cannot step back to !complete |
|
365 if(aComplete) |
|
366 { |
|
367 iDealerShutdownComplete = aComplete; |
|
368 MaybeCompleteUnbindings(); |
|
369 } |
|
370 } |
|
371 |
|
372 void CC32WorkerThread::SetPlayerShutdownComplete(TBool aComplete) |
|
373 { |
|
374 // Backsliding not permitted; cannot step back to !complete |
|
375 if(aComplete) |
|
376 { |
|
377 iPlayerShutdownComplete = aComplete; |
|
378 MaybeCompleteUnbindings(); |
|
379 } |
|
380 } |
|
381 |
|
382 /** |
|
383 The unbind requests are only responded to once the channel is presumed to be idle: |
|
384 - for Dealers this means no sessions remain with subsessions on that Player |
|
385 - for Players this means no subsessions remain for that Dealer |
|
386 So the peer handler is marked as pending unbind but the unbind response is not sent back |
|
387 until both Dealer and Player (if any) confirm idleness, which has to be checked whenever |
|
388 closing sessions or subsessions. |
|
389 Perhaps this could/should switch to using ref counts? |
|
390 Once unbind is pending the Dealer refrains from creating any new subsessions which use it. |
|
391 @see CC32WorkerThread::MaybeCompleteUnbinding |
|
392 */ |
|
393 void CC32WorkerThread::CFUnbindMessageReceived(const TCFUnbindMsg& aMsg) |
|
394 { |
|
395 // Mark the peer handler as unbinding |
|
396 __CFLOG_SMADDR2(( KLogSubSysSerComms, KLogSubSysSerComms, _L8("W%d: CFUnbindMessageReceived %S <=> %S"), |
|
397 WorkerId(), |
|
398 &aMsg.SubModule1()->Printable(__FullModName1), |
|
399 &aMsg.SubModule2()->Printable(__FullModName2) )); |
|
400 |
|
401 TWorkerId unbindId; |
|
402 TInt err = DecodePeerId(aMsg.SubModule1(), aMsg.SubModule2(), unbindId); |
|
403 if(err == KErrNone && !iTransport->PeerReachable(unbindId)) |
|
404 { |
|
405 __ASSERT_DEBUG(0, User::Panic(KCFTransportPanic, ECFTransAbsentWorker)); // shouldn't happen, but could possibly by race with thread panicked in shutdown? |
|
406 err = KErrNotFound; |
|
407 } |
|
408 if(err == KErrNone) |
|
409 { |
|
410 iTransport->SetLastRequestIdConcerningPeer(unbindId, aMsg.Identifier()); |
|
411 iTransport->SetDropTransportPending(unbindId, ETrue); |
|
412 MaybeCompleteUnbinding(unbindId); |
|
413 } |
|
414 else |
|
415 { |
|
416 TCFUnbindCompleteMsg respMsg(aMsg.Identifier(), err); |
|
417 VERIFY_RESULT(iChannelHandler->Send(respMsg), KErrNone); |
|
418 } |
|
419 } |
|
420 |
|
421 /** |
|
422 Can be called at any time, will poll all peer handlers for unbind "readiness" and make sure |
|
423 they complete the unbind if possible. |
|
424 @see CC32WorkerThread::MaybeCompleteUnbinding |
|
425 */ |
|
426 void CC32WorkerThread::MaybeCompleteUnbindings() |
|
427 { |
|
428 if(iProlongBindingLife == 0) |
|
429 { |
|
430 for(TWorkerId player = TC32WorkerThreadPublicInfo::EMainThread; player <= TC32WorkerThreadPublicInfo::EMaxWorkerThreadId; ++player) |
|
431 { |
|
432 MaybeCompleteUnbinding(player); |
|
433 } |
|
434 } |
|
435 } |
|
436 |
|
437 /** |
|
438 If the peer handler have been marked for unbind check whether local Dealer/Player is ready to |
|
439 unbind from this particular worker. If so, deletes handler and send unbind response to Root Server. |
|
440 @see CC32Dealer::CanUnbindFromWorker |
|
441 @see CC32Player::CanUnbindFromWorker |
|
442 @see TCFUnbindCompleteMsg |
|
443 */ |
|
444 void CC32WorkerThread::MaybeCompleteUnbinding(TWorkerId aWorker) |
|
445 { |
|
446 if(iTransport->PeerReachable(aWorker) && iTransport->IsDropTransportPending(aWorker)) |
|
447 { |
|
448 if(!Dealer() || Dealer()->CanUnbindFromWorker(aWorker)) |
|
449 { |
|
450 if(!Player() || Player()->CanUnbindFromWorker(aWorker)) |
|
451 { |
|
452 C32LOG2(KC32Bootup, _L("CC32WorkerThread::MaybeCompleteUnbinding(%d) - dropping transport & unbinding"), aWorker); |
|
453 TCFUnbindCompleteMsg respMsg(iTransport->LastRequestIdConcerningPeer(aWorker), KErrNone); |
|
454 iTransport->DropTransportToPeer(aWorker); |
|
455 VERIFY_RESULT(iChannelHandler->Send(respMsg), KErrNone); |
|
456 } |
|
457 } |
|
458 } |
|
459 } |
|
460 |
|
461 /** |
|
462 A shutdown message can be of several types: |
|
463 -# EImmediate: We shutdown immediately even if leaking resources, although doing a best effort to cleanup. However, SymbianOS doesnt allow the server to gracefully terminate sessions so certain things cant be cleaned up. |
|
464 -# EOptional: Ignored. |
|
465 -# EGraceful: The module only unloads once no resources remain, which means for the Dealer no sessions and for the Player no subsessions. The shutdown request arrives after the unbind requests. |
|
466 @see CommsFW::TCFShutdownMsg |
|
467 @see CommsFW::TCFShutdownType |
|
468 @see CC32Dealer::ProcessShutdownRequest |
|
469 @see CC32Player::ProcessShutdownRequest |
|
470 @see CC32WorkerThread::SetShuttingDown |
|
471 @see CC32WorkerThread::MaybeTriggerThreadShutdownCallback |
|
472 */ |
|
473 void CC32WorkerThread::CFShutdownMessageReceived(const CommsFW::TCFShutdownMsg& aMsg) |
|
474 { |
|
475 const CommsFW::TCFShutdownType type = aMsg.Type(); |
|
476 C32LOG2(KC32Shutdown, _L("CFShutdownMessageReceived(%d)"), type); |
|
477 |
|
478 if(EOptional == type) |
|
479 { |
|
480 C32LOG1(KC32Shutdown, _L("Type is EOptional: ignoring")); |
|
481 return; |
|
482 } |
|
483 else if(EImmediate == type) |
|
484 { |
|
485 C32LOG1(KC32Shutdown, _L("Type is EImmediate")); |
|
486 DealerByRef().SetImmediateShutdownPresent(); |
|
487 } |
|
488 |
|
489 SetShuttingDown(); |
|
490 |
|
491 if(Dealer()) |
|
492 { |
|
493 Dealer()->ProcessShutdownRequest(type); |
|
494 } |
|
495 if(Player()) |
|
496 { |
|
497 Player()->ProcessShutdownRequest(type); |
|
498 } |
|
499 |
|
500 MaybeTriggerThreadShutdownCallback(); |
|
501 } |
|
502 |
|
503 void CC32WorkerThread::PostMessage(TWorkerId aWorkerId, TCFMessage& aMessage) |
|
504 { |
|
505 TCFLegacyMessagePacker::PackForPostage(aWorkerId, aMessage); |
|
506 iTransport->PostMessage(aMessage); |
|
507 } |
|
508 |
|
509 /** |
|
510 Deal with incoming messages from other workers. |
|
511 */ |
|
512 void CC32WorkerThread::DispatchL(const TCFMessage& aMessage, TWorkerId aSenderId) |
|
513 { |
|
514 switch(aMessage.Code()) |
|
515 { |
|
516 case TC32WorkerMsg::EMainIntroduction: |
|
517 { |
|
518 C32LOG2(KC32CommsTrspMsg, _L("DispatchL(%d). EMainIntroduction"), aSenderId); |
|
519 ASSERT(!IsMainThread()); |
|
520 const TC32WorkerMainIntroductionMsg& msg = reinterpret_cast<const TC32WorkerMainIntroductionMsg&>(aMessage); |
|
521 iDealer = msg.Dealer(); |
|
522 iWorkerRegister->SetGlobalThreadRegister(&iDealer->WorkerDataGlobals()); |
|
523 TC32WorkerThreadPublicInfo info; |
|
524 info.Init(WorkerId(), aSenderId == TC32WorkerThreadPublicInfo::EMainThread); |
|
525 info.iWorker = this; |
|
526 TC32WorkerMainIntroductionRespMsg respMsg(info); |
|
527 #ifdef _DEBUG |
|
528 respMsg.SetFailType(iFailType); |
|
529 respMsg.SetFailRate(iFailRate); |
|
530 #endif |
|
531 PostMessage(aSenderId, respMsg); |
|
532 break; |
|
533 } |
|
534 case TC32WorkerMsg::EMainIntroductionResp: |
|
535 { |
|
536 C32LOG2(KC32CommsTrspMsg, _L("DispatchL(%d). EMainIntroductionResp"), aSenderId); |
|
537 ASSERT(IsMainThread()); |
|
538 const TC32WorkerMainIntroductionRespMsg& msg = reinterpret_cast<const TC32WorkerMainIntroductionRespMsg&>(aMessage); |
|
539 DealerByRef().ProcessWorkerIntroductionResponse(msg); |
|
540 |
|
541 /* In contrast with the players, who send their bind response immediately to the RootServer, |
|
542 the Main dealer has deferred sending the bind response until it has initialised the protocols |
|
543 signalled in the introduction response message. This ultimately ensures that the configurator |
|
544 only increments the sequence level when all protocols are actually initialised. */ |
|
545 TCFBindCompleteMsg bindresp(iTransport->LastRequestIdConcerningPeer(aSenderId), KErrNone); |
|
546 VERIFY_RESULT(iChannelHandler->Send(bindresp), KErrNone); |
|
547 iTransport->SetLastRequestIdConcerningPeer(aSenderId, TCFCommsMessageId::KNullMessageId); |
|
548 // delete cpmLoader |
|
549 DealerByRef().DeleteCPMLoader(aSenderId); |
|
550 break; |
|
551 } |
|
552 |
|
553 case TC32PlayerMsg::EForwardRequest: |
|
554 { |
|
555 ASSERT(Player()); |
|
556 TC32PlayerForwardRequestMsg fwdReqMsg(reinterpret_cast<const TC32PlayerForwardRequestMsg&>(aMessage)); |
|
557 C32LOG2(KC32CommsTrspMsg, _L8("DispatchL(%d). EForwardRequest "), aSenderId); |
|
558 Player()->ProcessMessageL(fwdReqMsg.Message(), fwdReqMsg.SubSession()); |
|
559 break; |
|
560 } |
|
561 case TC32PlayerMsg::ESessionClose: |
|
562 { |
|
563 C32LOG2(KC32CommsTrspMsg, _L("DispatchL(%d). ESessionClose"), aSenderId); |
|
564 CC32Player* player = Player(); |
|
565 ASSERT(player); |
|
566 const TC32PlayerSessionCloseMsg& sessionCloseMsg = reinterpret_cast<const TC32PlayerSessionCloseMsg&>(aMessage); |
|
567 /* Only do something if the message is within the deadline and we're sure |
|
568 the session pointer is safe to use */ |
|
569 TTime time; |
|
570 time.HomeTime(); |
|
571 if(time.Int64()<=sessionCloseMsg.SessionCloseDeadline()) |
|
572 { |
|
573 IncProlongBindingLife(); |
|
574 player->CloseSession(sessionCloseMsg.Session()); |
|
575 TC32PlayerSessionCloseRespMsg sessionCloseRespMsg(WorkerId(), sessionCloseMsg); |
|
576 PostMessage(aSenderId, sessionCloseRespMsg); |
|
577 DecProlongBindingLife(); |
|
578 if(ShuttingDown()) |
|
579 { |
|
580 player->MaybeSetPlayerShutdownComplete(EFalse); |
|
581 MaybeTriggerThreadShutdownCallback(); |
|
582 } |
|
583 } |
|
584 break; |
|
585 } |
|
586 case TC32PlayerMsg::ESessionCloseResp: |
|
587 { |
|
588 C32LOG2(KC32CommsTrspMsg, _L("DispatchL(%d). ESessionCloseResp"), aSenderId); |
|
589 ASSERT(IsMainThread()); |
|
590 const TC32PlayerSessionCloseRespMsg& msg = reinterpret_cast<const TC32PlayerSessionCloseRespMsg&>(aMessage); |
|
591 ASSERT(msg.Session()); |
|
592 |
|
593 /* Only do something if the message is within the deadline and we're sure |
|
594 the session pointer is safe to use */ |
|
595 TTime time; |
|
596 time.HomeTime(); |
|
597 if(time.Int64()<=msg.SessionCloseDeadline()) |
|
598 { |
|
599 CCommSession* session = msg.Session(); |
|
600 session->SessionCloseResp(msg.WorkerId()); |
|
601 if(session->IsDisconnectListEmpty()) |
|
602 { |
|
603 // Deleting the session may allow the binding to be removed, which will |
|
604 // cause the channel handler to be deleted |
|
605 Dealer()->DeleteSession(session); |
|
606 } |
|
607 } |
|
608 break; |
|
609 } |
|
610 case TC32PlayerMsg::ELoadCommModuleMsg: |
|
611 { |
|
612 C32LOG2(KC32CommsTrspMsg, _L("DispatchL(%d). ELoadCommModuleMsg"), aSenderId); |
|
613 ASSERT(Player()); |
|
614 TC32PlayerLoadCommModuleMsg msg(reinterpret_cast<const TC32PlayerLoadCommModuleMsg&>(aMessage)); |
|
615 // make function call to player here |
|
616 Player()->LoadCommModule(msg.Message()); |
|
617 break; |
|
618 } |
|
619 case TC32PlayerMsg::ELoadCommModuleSuccessResp: |
|
620 { |
|
621 C32LOG2(KC32CommsTrspMsg, _L("DispatchL(%d). ELoadCommModuleResp"), aSenderId); |
|
622 ASSERT(IsMainThread()); |
|
623 TC32PlayerLoadCommModuleSuccessResp msg(reinterpret_cast<const TC32PlayerLoadCommModuleSuccessResp&>(aMessage)); |
|
624 // update CC32ThreadManager with CSerial*, portprefix and csyfilename for the csy loaded successfully |
|
625 // portprefix is retrieved using CSerial* (by switching heap if necessary) and csyfilename is obtained |
|
626 // from message, after dealer after updating ThreadManager complete message with KErrNone, to emilinate |
|
627 // possible race condition of player completing message and subsequent port open rerquest reaching |
|
628 // ThreadManager before dealer gets a chance to update ThreadManager |
|
629 Dealer()->ProcessLoadCommModuleSuccessResponse(msg.Message(), msg.SerialPtr()); |
|
630 break; |
|
631 } |
|
632 case TC32PlayerMsg::ELoadCommModuleFailureResp: |
|
633 { |
|
634 C32LOG2(KC32CommsTrspMsg, _L("DispatchL(%d). ELoadCommModuleFailureResp"), aSenderId); |
|
635 ASSERT(IsMainThread()); |
|
636 const TC32PlayerLoadCommModuleFailureResp msg(reinterpret_cast<const TC32PlayerLoadCommModuleFailureResp&>(aMessage)); |
|
637 // remove CSY from session CSY container and de-allocate memory for this csy in ThreadManager as load failed |
|
638 Dealer()->ProcessLoadCommModuleFailureResponse(msg.Message(), msg.FailureReason()); |
|
639 break; |
|
640 } |
|
641 case TC32PlayerMsg::EUnLoadCommModuleMsg: |
|
642 { |
|
643 C32LOG2(KC32Detail, _L("DispatchL(%d). EUnLoadCommModuleMsg"), aSenderId); |
|
644 ASSERT(Player()); |
|
645 TC32PlayerUnLoadCommModuleMsg msg(reinterpret_cast<const TC32PlayerUnLoadCommModuleMsg&>(aMessage)); |
|
646 Player()->ProcessUnLoadCommModuleMsg(msg.SerialPtr()); |
|
647 break; |
|
648 } |
|
649 case TC32PlayerMsg::EGetPortInfoMsg: |
|
650 { |
|
651 C32LOG2(KC32CommsTrspMsg, _L("DispatchL(%d). EGetPortInfoMsg"), aSenderId); |
|
652 ASSERT(Player()); |
|
653 TC32PlayerGetPortInfoMsg msg(reinterpret_cast<const TC32PlayerGetPortInfoMsg&>(aMessage)); |
|
654 // make function call to player here |
|
655 Player()->PortInfo(msg.Message(), msg.SerialPtr()); |
|
656 break; |
|
657 } |
|
658 default: |
|
659 { |
|
660 C32LOG1(KC32Warning, _L("Unknown Transport message received. Do Nothing")); |
|
661 break; |
|
662 } |
|
663 |
|
664 } |
|
665 } |
|
666 |
|
667 /** |
|
668 The ProcessMessageL function which takes care of incoming messages doesnt TRAP anything, |
|
669 instead we count on dealing with failures here in the RunError. This simplifies the |
|
670 ProcessMessageL function and avoids the cost of another TRAP |
|
671 @see CC32Player::ProcessMessageL |
|
672 */ |
|
673 void CC32WorkerThread::OnDispatchLeave(const TCFMessage& aMessage, TWorkerId /*aSenderId*/, TInt aError) |
|
674 { |
|
675 C32LOG3(KC32Warning,_L8("CC32WorkerThread::OnDispatchLeave- Dispatch Left!! for message %x with error %d"),&aMessage,aError); |
|
676 switch(aMessage.Code()) |
|
677 { |
|
678 case TC32PlayerMsg::EForwardRequest: |
|
679 { |
|
680 TC32PlayerForwardRequestMsg fwdReqMsg(reinterpret_cast<const TC32PlayerForwardRequestMsg&>(aMessage)); |
|
681 if(aError != KErrNone && aError != KErrBadDescriptor) |
|
682 { |
|
683 fwdReqMsg.Message().Complete(aError); |
|
684 C32LOG1(KC32Warning,_L8("CC32WorkerThread::OnDispatchLeave, EForwardRequest - completing message.")); |
|
685 } |
|
686 break; |
|
687 } |
|
688 case TC32PlayerMsg::ELoadCommModuleMsg: |
|
689 { |
|
690 TC32PlayerLoadCommModuleMsg loadMsg(reinterpret_cast<const TC32PlayerLoadCommModuleMsg&>(aMessage)); |
|
691 if(aError != KErrNone && aError != KErrBadDescriptor) |
|
692 { |
|
693 loadMsg.Message().Complete(aError); |
|
694 C32LOG1(KC32Warning,_L8("CC32WorkerThread::OnDispatchLeave, ELoadCommModuleMsg - completing message.")); |
|
695 } |
|
696 break; |
|
697 } |
|
698 case TC32PlayerMsg::ELoadCommModuleSuccessResp: |
|
699 { |
|
700 TC32PlayerLoadCommModuleSuccessResp loadSuccessMsg(reinterpret_cast<const TC32PlayerLoadCommModuleSuccessResp&>(aMessage)); |
|
701 if(aError != KErrNone && aError != KErrBadDescriptor) |
|
702 { |
|
703 loadSuccessMsg.Message().Complete(aError); |
|
704 C32LOG1(KC32Warning,_L8("CC32WorkerThread::OnDispatchLeave, ELoadCommModuleSuccessResp - completing message.")); |
|
705 } |
|
706 break; |
|
707 } |
|
708 case TC32PlayerMsg::ELoadCommModuleFailureResp: |
|
709 { |
|
710 TC32PlayerLoadCommModuleFailureResp loadFailMsg(reinterpret_cast<const TC32PlayerLoadCommModuleFailureResp&>(aMessage)); |
|
711 if(aError != KErrNone && aError != KErrBadDescriptor) |
|
712 { |
|
713 loadFailMsg.Message().Complete(aError); |
|
714 C32LOG1(KC32Warning,_L8("CC32WorkerThread::OnDispatchLeave, ELoadCommModuleFailureResp - completing message.")); |
|
715 } |
|
716 break; |
|
717 } |
|
718 case TC32PlayerMsg::EGetPortInfoMsg: |
|
719 { |
|
720 TC32PlayerGetPortInfoMsg portInfoMsg(reinterpret_cast<const TC32PlayerGetPortInfoMsg&>(aMessage)); |
|
721 if(aError != KErrNone && aError != KErrBadDescriptor) |
|
722 { |
|
723 portInfoMsg.Message().Complete(aError); |
|
724 C32LOG1(KC32Warning,_L8("CC32WorkerThread::OnDispatchLeave, EGetPortInfoMsg - completing message.")); |
|
725 } |
|
726 break; |
|
727 } |
|
728 } |
|
729 } |
|
730 |
|
731 |
|
732 TBool CC32WorkerThread::ShuttingDown() const |
|
733 { |
|
734 return iWorkerShuttingDown; |
|
735 } |
|
736 |
|
737 void CC32WorkerThread::SetShuttingDown() |
|
738 { |
|
739 iWorkerShuttingDown = ETrue; |
|
740 } |
|
741 |
|
742 void CC32WorkerThread::SessionShutdownComplete() |
|
743 { |
|
744 if(IsMainThread()) |
|
745 { |
|
746 DealerByRef().SessionShutdownComplete(); |
|
747 } |
|
748 else |
|
749 { |
|
750 TriggerThreadShutdownCallback(); |
|
751 } |
|
752 } |
|
753 |
|
754 void CC32WorkerThread::TriggerThreadShutdownCallback() |
|
755 { |
|
756 C32LOG1(KC32Shutdown, _L("CC32WorkerThread::TriggerThreadShutdownCallback()") ); |
|
757 CC32DataInTls()->iShutdownWatchDog->Shutdown(); |
|
758 } |
|
759 |
|
760 void CC32WorkerThread::MaybeTriggerThreadShutdownCallback() |
|
761 { |
|
762 if(!ShuttingDown()) |
|
763 { |
|
764 return; |
|
765 } |
|
766 TBool doTrigger = DealerByRef().TestImmediateShutdownPresent(); |
|
767 C32LOG4(KC32Shutdown, _L("MaybeTriggerThreadShutdownCallback() - immediate=%d, dealerComplete=%d, playerComplete=%d"), |
|
768 doTrigger, DealerShutdownComplete(), PlayerShutdownComplete()); |
|
769 if(!doTrigger && DealerShutdownComplete()) |
|
770 { |
|
771 if(!PlayerShutdownComplete() && Player()) |
|
772 { |
|
773 Player()->MaybeSetPlayerShutdownComplete(EFalse); |
|
774 } |
|
775 if(PlayerShutdownComplete()) |
|
776 { |
|
777 // Check if all bindings have gone |
|
778 doTrigger = ETrue; |
|
779 for(TWorkerId player = TC32WorkerThreadPublicInfo::EMainThread; player <= TC32WorkerThreadPublicInfo::EMaxWorkerThreadId; ++player) |
|
780 { |
|
781 if(player != WorkerId() && iTransport->PeerReachable(player)) |
|
782 { |
|
783 C32LOG2(KC32Bootup, _L("MaybeTriggerThreadShutdownCallback() - peer %d binding remains"), player); |
|
784 doTrigger = EFalse; |
|
785 break; |
|
786 } |
|
787 } |
|
788 } |
|
789 } |
|
790 if(doTrigger) |
|
791 { |
|
792 if(WorkerId() == TC32WorkerThreadPublicInfo::EMainThread) |
|
793 { |
|
794 DealerByRef().ShutdownIfReady(); |
|
795 } |
|
796 else |
|
797 { |
|
798 TriggerThreadShutdownCallback(); |
|
799 } |
|
800 } |
|
801 } |
|
802 |
|
803 |
|
804 |
|
805 LOCAL_C void LoadDeviceDrivers(void) |
|
806 /** |
|
807 * On the target, load all the EUART LDDs and PDDs that can be found |
|
808 */ |
|
809 { |
|
810 #if defined (__EPOC32__) |
|
811 //Load Pdd's |
|
812 _LIT(CommPddName, "EUART"); |
|
813 _LIT(CommPddName0, "EUART0"); |
|
814 _LIT(CommPddName1, "EUART1"); |
|
815 _LIT(CommPddName2, "EUART2"); |
|
816 _LIT(CommPddName3, "EUART3"); |
|
817 _LIT(CommPddName4, "EUART4"); |
|
818 _LIT(CommPddName5, "EUART5"); |
|
819 _LIT(CommPddName6, "EUART6"); |
|
820 _LIT(CommPddName7, "EUART7"); |
|
821 _LIT(CommPddName8, "EUART8"); |
|
822 _LIT(CommPddName9, "EUART9"); |
|
823 User::LoadPhysicalDevice(CommPddName); |
|
824 User::LoadPhysicalDevice(CommPddName0); |
|
825 User::LoadPhysicalDevice(CommPddName1); |
|
826 User::LoadPhysicalDevice(CommPddName2); |
|
827 User::LoadPhysicalDevice(CommPddName3); |
|
828 User::LoadPhysicalDevice(CommPddName4); |
|
829 User::LoadPhysicalDevice(CommPddName5); |
|
830 User::LoadPhysicalDevice(CommPddName6); |
|
831 User::LoadPhysicalDevice(CommPddName7); |
|
832 User::LoadPhysicalDevice(CommPddName8); |
|
833 User::LoadPhysicalDevice(CommPddName9); |
|
834 |
|
835 // Load logical device driver |
|
836 _LIT(CommLddName, "ECOMM.LDD"); |
|
837 User::LoadLogicalDevice(CommLddName); |
|
838 #endif // #ifdef __EPOC32__ |
|
839 } |
|
840 |
|
841 |
|
842 void CC32WorkerThread::DeleteHBufC8(TAny* aHBufC) |
|
843 { |
|
844 delete static_cast<HBufC8*>(aHBufC); |
|
845 } |
|
846 |
|
847 |
|
848 /** |
|
849 The C32 thread. This is where control will resume when the RootServer starts a C32 instance. |
|
850 This function creates the worker thread object and starts the active scheduler. |
|
851 */ |
|
852 EXPORT_C TInt CC32WorkerThread::ThreadEntryPoint(TAny* aArg) |
|
853 { |
|
854 return RunC32Thread(static_cast<TCFModuleInfo*>(aArg)); |
|
855 } |
|
856 |
|
857 EXPORT_C TInt CC32WorkerThread::RunC32Thread(TCFModuleInfo* aArg) |
|
858 /** |
|
859 * The comm server thread. |
|
860 */ |
|
861 { |
|
862 CTrapCleanup* tc=CTrapCleanup::New(); |
|
863 if (!tc) |
|
864 { |
|
865 return KErrNoMemory; |
|
866 } |
|
867 |
|
868 LoadDeviceDrivers(); |
|
869 |
|
870 CCommScheduler* cs = CCommScheduler::New(); |
|
871 |
|
872 TCFModuleInfo* moduleInfo = aArg; |
|
873 |
|
874 TRAPD(res, CC32WorkerThread::NewL(moduleInfo)); // takes ownership of aArg and eventually deletes |
|
875 |
|
876 if(res != KErrNone) |
|
877 { |
|
878 // memory added to the TLS won't have been deleted since it isn't referred to be the cleanupstack |
|
879 // due to it being deemed to be "owned" by the thread via the TLS. So we delete it here |
|
880 CTLSRedirector* tls = CRedirectorInTls(); |
|
881 if (tls != NULL) |
|
882 { |
|
883 // The CC32Data destructor does not own any data that it points to, so does not delete the data, |
|
884 // So we have to delete watchdog directly |
|
885 |
|
886 CC32Data* globals = CC32DataInTls(); |
|
887 if (globals != NULL) |
|
888 { |
|
889 delete globals->iShutdownWatchDog; |
|
890 } |
|
891 delete tls; |
|
892 Dll::SetTls(NULL); // so that logger sees it is gone |
|
893 } |
|
894 |
|
895 delete cs; |
|
896 delete tc; |
|
897 |
|
898 return res; |
|
899 } |
|
900 |
|
901 RThread::Rendezvous(KErrNone); |
|
902 |
|
903 CActiveScheduler::Start(); |
|
904 |
|
905 delete cs; |
|
906 delete tc; |
|
907 |
|
908 return KErrNone; |
|
909 } |
|
910 |
|
911 |
|
912 // |
|
913 // CCommChannelHandler class definitions |
|
914 // |
|
915 CCommChannelHandler::CCommChannelHandler(CC32WorkerThread* aWorkerThread) |
|
916 : CCFModuleChannelHandler(CActive::EPriorityStandard), |
|
917 iWorkerThread(aWorkerThread) |
|
918 { |
|
919 } |
|
920 |
|
921 CCommChannelHandler* CCommChannelHandler::NewL(RCFChannel::TMsgQueues aRxQueues, |
|
922 RCFChannel::TMsgQueues aTxQueues, |
|
923 CC32WorkerThread* aWorkerThread) |
|
924 { |
|
925 CCommChannelHandler* pHandler = new (ELeave) CCommChannelHandler(aWorkerThread); |
|
926 CleanupStack::PushL(pHandler); |
|
927 pHandler->ConstructL(aRxQueues, aTxQueues); |
|
928 CleanupStack::Pop(pHandler); |
|
929 return pHandler; |
|
930 } |
|
931 |
|
932 TInt CCommChannelHandler::Send(const CommsFW::TCFMessage &aMessage) |
|
933 { |
|
934 return inherited::Send(aMessage); |
|
935 } |
|
936 |
|
937 |
|
938 /** |
|
939 This will be called by the handler when a shutdown message have been received from |
|
940 the Root Server. Delegating the call to the Worker Thread. |
|
941 */ |
|
942 void CCommChannelHandler::CFMessageShutdown(const TCFShutdownMsg& aMessage) |
|
943 { |
|
944 iWorkerThread->CFShutdownMessageReceived(aMessage); |
|
945 } |
|
946 |
|
947 /** |
|
948 This will be called by the handler when a Discovery message have been received from |
|
949 the Root Server. It will send a response, telling the Root Server that it has a single |
|
950 sub-module/binding point which is named after the WorkerID. |
|
951 As each Worker has a unique ID each worker thus also has a unique sub-module name to bind to, |
|
952 which potentially allows for simpler handling code and easy to interpret logs. |
|
953 */ |
|
954 void CCommChannelHandler::CFMessageDiscover(const TCFDiscoverMsg& aMessage) |
|
955 { |
|
956 ASSERT(aMessage.Size() > 0); |
|
957 TBuf8<4> subname; |
|
958 _LIT8(subconst, "%d"); |
|
959 subname.Format(subconst, iWorkerThread->WorkerId()); |
|
960 aMessage.SubModuleNames()->Copy(subname); |
|
961 TCFDiscoverRespMsg respMsg(aMessage.Identifier(), 1, EFalse); |
|
962 VERIFY_RESULT(Send(respMsg), KErrNone); |
|
963 } |
|
964 |
|
965 /** |
|
966 This will be called by the handler when a Bind message have been received from |
|
967 the Root Server. Delegating the call to the Worker Thread. |
|
968 */ |
|
969 void CCommChannelHandler::CFMessageBind(const TCFBindMsg& aMessage) |
|
970 { |
|
971 iWorkerThread->CFBindMessageReceived(aMessage); |
|
972 } |
|
973 |
|
974 /** |
|
975 This will be called by the handler when a Unbind message have has received from |
|
976 the Root Server. Delegating the call to the Worker Thread. |
|
977 */ |
|
978 void CCommChannelHandler::CFMessageUnbind(const TCFUnbindMsg& aMessage) |
|
979 { |
|
980 iWorkerThread->CFUnbindMessageReceived(aMessage); |
|
981 } |
|
982 |
|
983 |