|
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 "sd_log.h" |
|
22 #include "sd_roles.h" |
|
23 #include "sd_msgs.h" |
|
24 #include "sd_objectbroker.h" |
|
25 #include <rsshared.h> |
|
26 #include <elements/cfmacro.h> |
|
27 |
|
28 |
|
29 #ifdef _DEBUG |
|
30 // Panic category for "absolutely impossible!" vanilla ASSERT()-type panics from this module |
|
31 // (if it could happen through user error then you should give it an explicit, documented, category + code) |
|
32 _LIT(KSpecAssert_ElemSvrDenPitBsC, "ElemSvrDenPitBsC"); |
|
33 #endif |
|
34 |
|
35 using namespace Den; |
|
36 using namespace CommsFW; |
|
37 |
|
38 // |
|
39 // CCommonPitBoss |
|
40 // |
|
41 |
|
42 |
|
43 EXPORT_C void CCommonPitBoss::ConstructL() |
|
44 { |
|
45 User::LeaveIfError(iImmediateShutdownLock.CreateLocal()); |
|
46 iWorkerDataGlobals = CWorkerDataGlobals::NewL(); |
|
47 iConfLevelMonitor = CConfigurationLevelMonitor::NewL(this); |
|
48 TWorkerThreadRegister* mainProperties = WorkerDataGlobals().GetWorkerGlobals(TWorkerThreadPublicInfo::EMainThread); |
|
49 mainProperties->iHeap = &User::Heap(); |
|
50 mainProperties->iDealer = OwnerThread()->Dealer(); |
|
51 mainProperties->iPlayer = OwnerThread()->Player(); |
|
52 } |
|
53 |
|
54 EXPORT_C CCommonDealer* CCommonPitBoss::GetDealer(TWorkerId aId) |
|
55 { |
|
56 return WorkerDataGlobals().GetWorkerGlobals(aId)->iDealer; |
|
57 } |
|
58 |
|
59 EXPORT_C CCommonPlayer* CCommonPitBoss::GetPlayer(const TWorkerIntroductionMsg& aMsg) |
|
60 { |
|
61 const TWorkerThreadPublicInfo& msgInfo = aMsg.WorkerInfo(); |
|
62 TWorkerThreadRegister& workerReg = *WorkerDataGlobals().GetWorkerGlobals(msgInfo.iWorkerId); |
|
63 return workerReg.iPlayer; |
|
64 } |
|
65 |
|
66 EXPORT_C TBool CCommonPitBoss::ModuleConfigurationComplete() const |
|
67 { |
|
68 return iConfLevelMonitor == NULL; |
|
69 } |
|
70 |
|
71 EXPORT_C TBool CCommonPitBoss::IsShuttingDown() const |
|
72 { |
|
73 return iShuttingDown; |
|
74 } |
|
75 |
|
76 void CCommonPitBoss::StartShutdown() |
|
77 { |
|
78 iShuttingDown = ETrue; |
|
79 } |
|
80 |
|
81 EXPORT_C void CCommonPitBoss::SessionShutdownComplete() |
|
82 { |
|
83 iSessionShutdownComplete = ETrue; |
|
84 ShutdownIfReady(); |
|
85 } |
|
86 |
|
87 void CCommonPitBoss::ShutdownIfReady() |
|
88 { |
|
89 __ASSERT_DEBUG(iOwnerThread->WorkerId() == TWorkerThreadPublicInfo::EMainThread, User::Panic(KSpecAssert_ElemSvrDenPitBsC, 1)); |
|
90 COMMONLOG((iOwnerThread->WorkerId(), KECommonServerTag, _L8("CPitBoss::ShutdownIfReady() - iShuttingDown == %d, iSessionShutdownComplete = %d, iPeerShutdownComplete = %d"), |
|
91 iShuttingDown, iSessionShutdownComplete, iPeerShutdownComplete)); |
|
92 if(iShuttingDown && iSessionShutdownComplete && iPeerShutdownComplete) |
|
93 { |
|
94 iOwnerThread->TriggerThreadShutdownCallback(); |
|
95 } |
|
96 } |
|
97 |
|
98 /** |
|
99 Immediate shutdowns expose a number of additional race risks; in the face of one happening to |
|
100 any thread at all we stop attempting certain kinds of cleanup. This should be acceptable |
|
101 behaviour since at it will be the whole of ESock shutting down. |
|
102 To avoid races between the testing and setting of this state, testing for it gains a lock |
|
103 which must be explicitly released. |
|
104 @see CPitBoss::ReleaseImmediateShutdownPresent() |
|
105 */ |
|
106 TBool CCommonPitBoss::TestAndLockImmediateShutdownPresent() const |
|
107 { |
|
108 iImmediateShutdownLock.Wait(); |
|
109 return iImmediateShutdownMark; |
|
110 } |
|
111 |
|
112 void CCommonPitBoss::ReleaseImmediateShutdownPresent() const |
|
113 { |
|
114 iImmediateShutdownLock.Signal(); |
|
115 } |
|
116 |
|
117 void CCommonPitBoss::SetImmediateShutdownPresent() |
|
118 { |
|
119 iImmediateShutdownLock.Wait(); |
|
120 iImmediateShutdownMark = ETrue; |
|
121 iImmediateShutdownLock.Signal(); |
|
122 } |
|
123 |
|
124 /** |
|
125 Tests whether immediate shutdown is signaled. This function does not |
|
126 lock and is used where this knowledge is desired but no action is taken that might |
|
127 race with other threads. |
|
128 @see CPitBoss::TestAndLockImmediateShutdownPresent() |
|
129 @see CPitBoss::ReleaseImmediateShutdownPresent() |
|
130 */ |
|
131 EXPORT_C TBool CCommonPitBoss::TestImmediateShutdownPresent() const |
|
132 { |
|
133 return iImmediateShutdownMark; |
|
134 } |
|
135 |
|
136 EXPORT_C CCommonPitBoss::~CCommonPitBoss() |
|
137 { |
|
138 delete iConfLevelMonitor; |
|
139 |
|
140 /* The reason for this bit of code is that when we shut down there's a race between the Pitboss |
|
141 and an optimal Dealer where the Pitboss doesn't discover the optimal dealer is going down (PB |
|
142 goes down first) and thus doesn't close the resources it have on it before the Pitboss is destroyed. |
|
143 */ |
|
144 for(TWorkerId id = TWorkerThreadPublicInfo::EFirstPlayerThread; id <= TWorkerThreadPublicInfo::EMaxWorkerThreadId; ++id) |
|
145 { |
|
146 if(WorkerExists(id)) |
|
147 { |
|
148 FreeWorkerReferences(id); |
|
149 } |
|
150 } |
|
151 delete iWorkerDataGlobals; |
|
152 iImmediateShutdownLock.Close(); |
|
153 //#ifdef _DEBUG |
|
154 iForsakenHeapList.Close(); |
|
155 //#endif |
|
156 } |
|
157 |
|
158 EXPORT_C CCommonPitBoss::CCommonPitBoss(CCommonWorkerThread* aOwnerThread) |
|
159 : iOwnerThread(aOwnerThread), |
|
160 iNextUniqueId(1) |
|
161 { |
|
162 __ASSERT_DEBUG(iOwnerThread->WorkerId() == TWorkerThreadPublicInfo::EMainThread, User::Panic(KSpecAssert_ElemSvrDenPitBsC, 2)); |
|
163 __ASSERT_DEBUG(iOwnerThread->Player() == NULL, User::Panic(KSpecAssert_ElemSvrDenPitBsC, 3)); |
|
164 } |
|
165 |
|
166 EXPORT_C void CCommonPitBoss::PostMessage(CommsFW::TWorkerId aWorkerId, CommsFW::TCFMessage& aMessage) |
|
167 { |
|
168 iOwnerThread->PostMessage(aWorkerId, aMessage); |
|
169 } |
|
170 |
|
171 /** |
|
172 Check whether a worker Id is legal and a worker with that Id is installed. |
|
173 */ |
|
174 EXPORT_C TBool CCommonPitBoss::WorkerExists(TWorkerId aId) const |
|
175 { |
|
176 return WorkerDataGlobals().WorkerPresent(aId); |
|
177 } |
|
178 |
|
179 |
|
180 /** |
|
181 Given the "local" worker thread's id (normally but not necessarily the current worker |
|
182 thread and that of another "foreign" worker thread, if they have different heaps then |
|
183 switch heaps and return the previous one, otherwise return NULL. |
|
184 */ |
|
185 EXPORT_C RAllocator* CCommonPitBoss::MaybeSwitchHeap(TWorkerId aForeignWorkerId) |
|
186 { |
|
187 const TWorkerThreadPublicInfo& foreignInfo = *WorkerDataGlobals().GetWorkerGlobals(aForeignWorkerId); |
|
188 __ASSERT_DEBUG(foreignInfo.iHeap, User::Panic(KSpecAssert_ElemSvrDenPitBsC, 4)); |
|
189 RHeap* heap = &User::Heap(); |
|
190 if(heap != foreignInfo.iHeap) |
|
191 { |
|
192 COMMONLOG((iOwnerThread->WorkerId(), KECommonSessDetailTag, _L8("CPitBoss::MaybeSwitchHeap - Switching heap to %08x."),foreignInfo.iHeap)); |
|
193 return User::SwitchAllocator(foreignInfo.iHeap); |
|
194 } |
|
195 else |
|
196 { |
|
197 COMMONLOG((iOwnerThread->WorkerId(), KECommonSessDetailTag, _L8("CPitBoss::MaybeSwitchHeap - No heap switch happened - Heap %08x."),heap)); |
|
198 } |
|
199 |
|
200 return NULL; |
|
201 } |
|
202 |
|
203 /** |
|
204 Called by any Player/thread to add a sub-session to a session. It will switch the local heap to that |
|
205 of the peer before performing operations on the session pointer. It is essential that the session lock is used |
|
206 around this call. |
|
207 @see CSubSessionIx::Lock |
|
208 @see CSubSessionIx::UnLock |
|
209 */ |
|
210 EXPORT_C TInt CCommonPitBoss::AddSubSession(CWorkerSubSession* aSubSession, CWorkerSession* aSession, TInt& aHandle) |
|
211 { |
|
212 RAllocator* prevAllocator = MaybeSwitchHeap(aSession->WorkerId()); |
|
213 TInt err = aSession->SubSessions().Add(static_cast<CWorkerSubSession*>(aSubSession), aHandle); |
|
214 COMMONLOG((iOwnerThread->WorkerId(),KECommonSessDetailTag, _L8("CPitBoss::AddSubSession(%08x, %08x, => %08x)"), aSubSession, aSession, aHandle)); |
|
215 |
|
216 if(prevAllocator) |
|
217 { |
|
218 User::SwitchAllocator(prevAllocator); |
|
219 } |
|
220 return err; |
|
221 } |
|
222 |
|
223 /** |
|
224 Called by any Player/thread to remove a sub-session from a session. It will switch the local heap to that |
|
225 of the peer before performing operations on the session pointer. It is essential that the session lock is used |
|
226 around this call. |
|
227 @see CSubSessionIx::Lock |
|
228 @see CSubSessionIx::UnLock |
|
229 */ |
|
230 EXPORT_C void CCommonPitBoss::RemoveSubSession(TInt aHandle, CWorkerSession* aSession) |
|
231 { |
|
232 RAllocator* prevAllocator = MaybeSwitchHeap(aSession->WorkerId()); |
|
233 COMMONLOG((iOwnerThread->WorkerId(),KECommonSessDetailTag, _L8("CPitBoss::RemoveSubSession(%08x, %08x)"), aHandle, aSession)); |
|
234 VERIFY(aSession->SubSessions().Remove(aHandle) != NULL); |
|
235 if(prevAllocator) |
|
236 { |
|
237 User::SwitchAllocator(prevAllocator); |
|
238 } |
|
239 } |
|
240 |
|
241 CCommonDealer* CCommonPitBoss::Dealer(TWorkerId aWorkerId) const |
|
242 { |
|
243 return WorkerDataGlobals().GetWorkerGlobals(aWorkerId)->iDealer; |
|
244 } |
|
245 |
|
246 CCommonPlayer* CCommonPitBoss::Player(TWorkerId aWorkerId) const |
|
247 { |
|
248 return WorkerDataGlobals().GetWorkerGlobals(aWorkerId)->iPlayer; |
|
249 } |
|
250 |
|
251 EXPORT_C const RThread& CCommonPitBoss::RThreadRef(TWorkerId aWorkerId) const |
|
252 { |
|
253 __ASSERT_DEBUG(aWorkerId <= TWorkerThreadPublicInfo::EMaxWorkerThreadId, User::Panic(KSpecAssert_ElemSvrDenPitBsC, 5)); |
|
254 __ASSERT_DEBUG(WorkerExists(aWorkerId), User::Panic(KSpecAssert_ElemSvrDenPitBsC, 6)); // can't do this to an absent thread |
|
255 return WorkerDataGlobals().GetWorkerGlobals(aWorkerId)->iThread; |
|
256 } |
|
257 |
|
258 void CCommonPitBoss::AddPendingIntroductionResponse() |
|
259 { |
|
260 ++iPendingIntroResponses; |
|
261 } |
|
262 |
|
263 void CCommonPitBoss::RemovePendingIntroductionResponse() |
|
264 { |
|
265 --iPendingIntroResponses; |
|
266 } |
|
267 |
|
268 EXPORT_C void CCommonPitBoss::BindMessageReceived(const CommsFW ::TCFModuleName& aPeerName, TWorkerId aPeerId) |
|
269 { |
|
270 WorkerDataGlobals().GetWorkerGlobals(aPeerId)->iModuleName = aPeerName; |
|
271 AddPendingIntroductionResponse(); |
|
272 } |
|
273 |
|
274 /** |
|
275 Used during binding when the PitBoss receives a introduction response message from a worker. |
|
276 The PitBoss will set-up housekeeping datastructures for the worker and add the supported |
|
277 protocols to its list of protocol pairings. |
|
278 @see TWorkerMsg::EMainIntroductionResp |
|
279 */ |
|
280 EXPORT_C void CCommonPitBoss::ProcessWorkerIntroductionL(const TWorkerIntroductionMsg& aMsg) |
|
281 { |
|
282 // Note the arrival of the response - if we leave below then whatever the cause we shouldn't diagnose the peer |
|
283 // as having failed to respond at all |
|
284 RemovePendingIntroductionResponse(); |
|
285 |
|
286 const TWorkerThreadPublicInfo& msgInfo = aMsg.WorkerInfo(); |
|
287 __ASSERT_DEBUG(msgInfo.iWorkerId > TWorkerThreadPublicInfo::EMainThread && msgInfo.iWorkerId <= TWorkerThreadPublicInfo::EMaxWorkerThreadId, User::Panic(KSpecAssert_ElemSvrDenPitBsC, 7)); |
|
288 __ASSERT_DEBUG(!WorkerDataGlobals().WorkerPresent(msgInfo.iWorkerId), User::Panic(KSpecAssert_ElemSvrDenPitBsC, 8)); |
|
289 TWorkerThreadRegister& workerReg = *WorkerDataGlobals().GetWorkerGlobals(msgInfo.iWorkerId); |
|
290 // Passing the thread id rather than opening a reference looks like a race possibility. However |
|
291 // the worker should not have executed any protocol code yet so shouldn't have died. And even if |
|
292 // it has the RootServer hasn't had a chance to unbind us, so the id must still be valid. |
|
293 // Copy the base class details from the worker's response and flesh out frequently looked-up details |
|
294 static_cast<TWorkerThreadPublicInfo&>(workerReg) = msgInfo; |
|
295 workerReg.PeerCleanupPending(msgInfo.iWorkerId); // until the dies we regard it as pending cleanup on itself, so that the exit of peers doesn't provoke premature cleanup |
|
296 workerReg.iPeerDeathNotifier = new(ELeave) CPeerDeathNotifier(msgInfo.iWorkerId, workerReg.iThread, *this); |
|
297 workerReg.iDealer = workerReg.iWorker->Dealer(); |
|
298 workerReg.iPlayer = workerReg.iWorker->Player(); |
|
299 |
|
300 //Domain specific stuff here |
|
301 DoProcessWorkerIntroductionL(aMsg); |
|
302 |
|
303 #ifdef _DEBUG |
|
304 // We only switch on the configured simulated allocation failures once the bindings are complete, because |
|
305 // it's too hard to recover from them earlier. This is a regrettable but hopefully harmless limitation in |
|
306 // practice, ie if we're OOM during boot then recovery strategies aren't obvious. |
|
307 workerReg.iHasGlobalAllocFails = aMsg.FailType() != RAllocator::ENone; |
|
308 if(workerReg.iHasGlobalAllocFails) |
|
309 { |
|
310 workerReg.iHeap->__DbgSetAllocFail(aMsg.FailType(), aMsg.FailRate()); |
|
311 } |
|
312 #endif |
|
313 } |
|
314 |
|
315 /** |
|
316 The PitBoss monitors the Comms Configurator sequence level and when the core components |
|
317 have been configured (this includes ESock) this method is called to delete any data structures |
|
318 used only during startup of ESock. |
|
319 @see CConfigurationLevelMonitor |
|
320 @see CPitBoss::iPendingIntroResponses |
|
321 */ |
|
322 EXPORT_C void CCommonPitBoss::OnCPMsConfigured() |
|
323 { |
|
324 // Replaced ASSERT with Log entry |
|
325 // shouldn't get here without all bindings complete; if we do then it means a module |
|
326 // load must have timed out & so the configurator moved on. So the module in question |
|
327 // probably isn't bound correctly, etc. |
|
328 |
|
329 if (iPendingIntroResponses != 0) |
|
330 { |
|
331 COMMONLOG((iOwnerThread->WorkerId(), KECommonBootingTag, _L8("CPitBoss::OnCPMsConfigured: %d module(s) not bound correctly"),iPendingIntroResponses)); |
|
332 } |
|
333 |
|
334 //#ifdef _DEBUG |
|
335 // if(iPendingIntroResponses == 0 && iOwnerThread->AllocFailType() != RAllocator::ENone) |
|
336 // { |
|
337 // WorkerDataGlobals().GetWorkerGlobals(TWorkerThreadPublicInfo::EMainThread)->iHeap->__DbgSetAllocFail(iOwnerThread->AllocFailType(), iOwnerThread->AllocFailRate()); |
|
338 // } |
|
339 //#endif |
|
340 |
|
341 delete iConfLevelMonitor; |
|
342 iConfLevelMonitor = NULL; |
|
343 |
|
344 DoOnCPMsConfigured(); |
|
345 BroadcastConfigurationComplete(EModuleInitialisation); |
|
346 } |
|
347 |
|
348 /** |
|
349 If a worker dies the PitBoss will call this method. It will clean up the housekeeping datastructures |
|
350 related to the worker and it will spawn the RedShirt thread which will try to delete the workers own |
|
351 data structures. It is a best effort attempt that doesn't guarantee to free up all the dead workers memory |
|
352 and the RedShirt could be PANICed by the kernel, which is why a short lived seperate thread is doing it. |
|
353 */ |
|
354 EXPORT_C void CCommonPitBoss::OnPeerDeath(TWorkerId aWorkerId) |
|
355 { |
|
356 COMMONLOG((iOwnerThread->WorkerId(), KECommonServerTag, _L8("CPitBoss::OnPeerDeath() worker %d died"), aWorkerId)); |
|
357 |
|
358 TWorkerThreadRegister& worker = *WorkerDataGlobals().GetWorkerGlobals(aWorkerId); |
|
359 delete worker.iPeerDeathNotifier; |
|
360 worker.iPeerDeathNotifier = NULL; |
|
361 |
|
362 // If worker ran a Player all its protocol pairings are now dead. We can't know whether the thread actually |
|
363 // did run a Player as (presuming it exited cleanly) it cleaned up, but it's adequately cheap & a rare case |
|
364 DoOnPeerDeath(aWorkerId); |
|
365 |
|
366 worker.PeerCleanupCompleted(aWorkerId); // worker is no longer waiting for itself to exit |
|
367 |
|
368 // Check how the worker died - if it appears to be involuntary then we consider its heap to be |
|
369 // forsaken and don't check it for leaks later |
|
370 if(worker.iThread.ExitType() != EExitKill || worker.iThread.ExitReason() != KErrNone) |
|
371 { |
|
372 iForsakenHeapList.Add(worker.iHeap); |
|
373 } |
|
374 |
|
375 // Main dealer needs to cleanup |
|
376 Dealer(TWorkerThreadPublicInfo::EMainThread)->CleanupDeadWorker(aWorkerId); |
|
377 |
|
378 CCommonWorkerThread* deadWorker = WorkerDataGlobals().WorkerThread(aWorkerId); |
|
379 if(deadWorker) // always clean-up now |
|
380 { |
|
381 // Send in the Red Shirt thread to complete any blocked operations on any remaining |
|
382 // subsession with KErrAbort. If it dies in vain then we care as briefly as |
|
383 // Captain Kirk ever did about one of his anonymous security guards. |
|
384 RThread redShirt; |
|
385 if(DoCreateRedShirt(redShirt, aWorkerId, *deadWorker) == KErrNone) |
|
386 { |
|
387 TRequestStatus rendezStatus; |
|
388 redShirt.Rendezvous(rendezStatus); |
|
389 COMMONLOG((iOwnerThread->WorkerId(), KECommonServerTag, _L8("CPitBoss::CompleteWorkerThreadCleanup() - releasing RedShirt to complete Player's blocked requests"))); |
|
390 redShirt.Resume(); |
|
391 User::WaitForRequest(rendezStatus); |
|
392 COMMONLOG((iOwnerThread->WorkerId(), KECommonServerTag, _L8("CPitBoss::CompleteWorkerThreadCleanup() - RedShirt completed, status %d"), rendezStatus.Int())); |
|
393 redShirt.Close(); |
|
394 } |
|
395 else |
|
396 { |
|
397 COMMONLOG((iOwnerThread->WorkerId(), KECommonServerTag, _L8("CPitBoss::CompleteWorkerThreadCleanup() - failed creating RedShirt to clean Player"))); |
|
398 } |
|
399 } |
|
400 |
|
401 // Tell all peer threads to clean up |
|
402 for(CommsFW::TWorkerId peerId = TWorkerThreadPublicInfo::EFirstPlayerThread; peerId <= TWorkerThreadPublicInfo::EMaxWorkerThreadId; ++peerId) |
|
403 { |
|
404 if(peerId != aWorkerId && WorkerExists(peerId)) |
|
405 { |
|
406 // If the cleanup of any peer was waiting for confirmation from the one which just died then regard |
|
407 // it as complete now |
|
408 HandleWorkerCleanupCompletionByPeer( peerId, aWorkerId); //Deliberately mirrored sense |
|
409 // May already have closed transport to peer |
|
410 if(iOwnerThread->PeerReachable(peerId)) |
|
411 { |
|
412 COMMONLOG((iOwnerThread->WorkerId(), KECommonServerTag, _L8("CPitBoss::OnPeerDeath() instructing peer %d to cleanup"), peerId)); |
|
413 worker.PeerCleanupPending(peerId); |
|
414 TWorkerCleanupDeadPeerMsg msg(aWorkerId); |
|
415 iOwnerThread->PostMessage(peerId, msg); |
|
416 } |
|
417 else |
|
418 COMMONLOG((iOwnerThread->WorkerId(), KECommonServerTag, _L8("CPitBoss::OnPeerDeath() peer %d no longer bound to main thread"), peerId)); |
|
419 } |
|
420 } |
|
421 |
|
422 // Initial clean-up of worker is by PitBoss now complete; final clean-up may |
|
423 // not occur until any cleanup requests have been completed |
|
424 COMMONLOG((iOwnerThread->WorkerId(), KECommonServerTag, _L8("Destroying peer #%d handler"),aWorkerId)); |
|
425 iOwnerThread->DropTransportToPeer(aWorkerId); |
|
426 HandleWorkerCleanupCompletionByPeer(aWorkerId, TWorkerThreadPublicInfo::EMainThread); |
|
427 } |
|
428 |
|
429 EXPORT_C TInt CCommonPitBoss::DoCreateRedShirt(RThread& aRedShirt, TWorkerId aWorkerId, CCommonWorkerThread& aDeadWorker) |
|
430 { |
|
431 //Default implementation - uses CCommonWorkerThread::PostMortemCleanupThreadEntry |
|
432 return aRedShirt.Create(KNullDesC, CCommonWorkerThread::PostMortemCleanupThreadEntry, 8192, static_cast<RHeap*>(WorkerDataGlobals().GetWorkerGlobals(aWorkerId)->iHeap), &aDeadWorker); |
|
433 } |
|
434 |
|
435 EXPORT_C TBool CCommonPitBoss::ResolvePlayerRoleToId(const TPlayerRole& aRoleId, CommsFW::TWorkerId& aWorkerId) const |
|
436 { |
|
437 for(TWorkerId id = TWorkerThreadPublicInfo::EFirstPlayerThread; id <= TWorkerThreadPublicInfo::EMaxWorkerThreadId; ++id) |
|
438 { |
|
439 if (WorkerExists(id) && Player(id)->PlayerRole().Role() == aRoleId.Role()) |
|
440 { |
|
441 aWorkerId = id; |
|
442 return ETrue; |
|
443 } |
|
444 } |
|
445 #ifdef _DEBUG |
|
446 aWorkerId = TWorkerThreadPublicInfo::ENullWorkerId; // help find buggy code which ignores returns |
|
447 #endif |
|
448 return EFalse; |
|
449 } |
|
450 |
|
451 |
|
452 EXPORT_C TBool CCommonPitBoss::ResolveWorkerNameToId(const TDesC8& aWorkerName, TWorkerId& aWorkerId) const |
|
453 { |
|
454 for(TWorkerId id = TWorkerThreadPublicInfo::EFirstPlayerThread; id <= TWorkerThreadPublicInfo::EMaxWorkerThreadId; ++id) |
|
455 { |
|
456 if(WorkerExists(id) && WorkerDataGlobals().GetWorkerGlobals(id)->iModuleName.CompareF(aWorkerName) == 0) |
|
457 { |
|
458 aWorkerId = id; |
|
459 return ETrue; |
|
460 } |
|
461 } |
|
462 #ifdef _DEBUG |
|
463 aWorkerId = TWorkerThreadPublicInfo::ENullWorkerId; // help find buggy code which ignores returns |
|
464 #endif |
|
465 return EFalse; |
|
466 } |
|
467 |
|
468 EXPORT_C void CCommonPitBoss::HandleWorkerCleanupCompletionByPeer(TWorkerId aWorkerId, TWorkerId aPeerId) |
|
469 { |
|
470 COMMONLOG((iOwnerThread->WorkerId(), KECommonServerTag, _L8("CPitBoss::HandleWorkerCleanupCompletionByPeer() cleanup of worker %d by peer %d completed"), aWorkerId, aPeerId)); |
|
471 TWorkerThreadRegister& properties(*WorkerDataGlobals().GetWorkerGlobals(aWorkerId)); |
|
472 if(properties.IsValid()) // Might already have processed the worker's own shutdown |
|
473 { |
|
474 properties.PeerCleanupCompleted(aPeerId); |
|
475 if(properties.AllPeerCleanupsCompleted()) |
|
476 { |
|
477 CompleteWorkerThreadCleanup(aWorkerId); |
|
478 } |
|
479 } |
|
480 } |
|
481 |
|
482 void CCommonPitBoss::CompleteWorkerThreadCleanup(TWorkerId aWorkerId) |
|
483 { |
|
484 __ASSERT_DEBUG(aWorkerId != TWorkerThreadPublicInfo::EMainThread, User::Panic(KSpecAssert_ElemSvrDenPitBsC, 9)); |
|
485 COMMONLOG((iOwnerThread->WorkerId(), KECommonServerTag, _L8("CPitBoss::CompleteWorkerThreadCleanup() - final cleanup phase for worker %d"), aWorkerId)); |
|
486 |
|
487 // Free resources |
|
488 FreeWorkerReferences(aWorkerId); |
|
489 |
|
490 // Test whether all cleanups done |
|
491 TWorkerId peerId; |
|
492 for(peerId = TWorkerThreadPublicInfo::EFirstPlayerThread; peerId <= TWorkerThreadPublicInfo::EMaxWorkerThreadId; ++peerId) |
|
493 { |
|
494 if(WorkerExists(peerId)) |
|
495 { |
|
496 break; |
|
497 } |
|
498 } |
|
499 if(peerId > TWorkerThreadPublicInfo::EMaxWorkerThreadId) |
|
500 { |
|
501 iPeerShutdownComplete = ETrue; |
|
502 ShutdownIfReady(); |
|
503 } |
|
504 } |
|
505 |
|
506 void CCommonPitBoss::FreeWorkerReferences(TWorkerId aWorkerId) |
|
507 { |
|
508 DoFreeWorkerReferences(aWorkerId); |
|
509 |
|
510 TWorkerThreadRegister& properties(*WorkerDataGlobals().GetWorkerGlobals(aWorkerId)); |
|
511 delete properties.iPeerDeathNotifier; |
|
512 // properties.iHeap->Close(); |
|
513 properties.Clear(); |
|
514 } |
|
515 |
|
516 #ifdef _DEBUG |
|
517 // If a heap has been configured from boot to have a failure mode then we don't override this here as |
|
518 // the lifetime failure testing is more important than the specific test case doing a SetFailNext |
|
519 EXPORT_C void CCommonPitBoss::SetFailNextForAllHeaps(TInt aFailNext) |
|
520 { |
|
521 for(TWorkerId workerId = TWorkerThreadPublicInfo::EMainThread; workerId <= TWorkerThreadPublicInfo::EMaxWorkerThreadId; ++workerId) |
|
522 { |
|
523 if(WorkerExists(workerId)) |
|
524 { |
|
525 if(WorkerDataGlobals().GetWorkerGlobals(workerId)->iHasGlobalAllocFails) |
|
526 { |
|
527 if(aFailNext == KResetAllocFails) |
|
528 { |
|
529 // Reset lifetime failure mode. |
|
530 WorkerDataGlobals().GetWorkerGlobals(workerId)->iHeap->__DbgSetAllocFail(RAllocator::EReset, 0); |
|
531 } |
|
532 } |
|
533 else |
|
534 { |
|
535 WorkerDataGlobals().GetWorkerGlobals(workerId)->iHeap->__DbgSetAllocFail((aFailNext < 0)? RAllocator::EReset: RAllocator::EFailNext, aFailNext); |
|
536 } |
|
537 } |
|
538 } |
|
539 } |
|
540 |
|
541 EXPORT_C TBool CCommonPitBoss::TestFailNextForAllHeaps() const |
|
542 { |
|
543 class RPeekHeap : public RHeap |
|
544 { |
|
545 public: |
|
546 RAllocator::TAllocFail FailNextMode() const |
|
547 { |
|
548 return iFailType; |
|
549 } |
|
550 }; |
|
551 |
|
552 for(TWorkerId workerId = TWorkerThreadPublicInfo::EMainThread; workerId <= TWorkerThreadPublicInfo::EMaxWorkerThreadId; ++workerId) |
|
553 { |
|
554 if(WorkerExists(workerId) && !WorkerDataGlobals().GetWorkerGlobals(workerId)->iHasGlobalAllocFails) |
|
555 { |
|
556 RPeekHeap* heap = static_cast<RPeekHeap*>(WorkerDataGlobals().GetWorkerGlobals(workerId)->iHeap); |
|
557 if(heap->FailNextMode() != RAllocator::EFailNext) |
|
558 { |
|
559 return EFalse; // at least one heap isn't in fail-next mode |
|
560 } |
|
561 } |
|
562 } |
|
563 return ETrue; |
|
564 } |
|
565 #else |
|
566 EXPORT_C void CCommonPitBoss::SetFailNextForAllHeaps(TInt /*aFailNext*/) |
|
567 {} |
|
568 EXPORT_C TBool CCommonPitBoss::TestFailNextForAllHeaps() const |
|
569 {return EFalse;} |
|
570 #endif |
|
571 |
|
572 /** Called synchronously by worker threads just prior to their exit to signal that their exit completed normally and |
|
573 that external cleanup is not required |
|
574 */ |
|
575 EXPORT_C void CCommonPitBoss::PeerWorkerExiting(TWorkerId aWorker) |
|
576 { |
|
577 // Remove the pointers to the major control objects which a cleanup thread would use in the event of abnormal exit |
|
578 TWorkerThreadRegister& properties(*WorkerDataGlobals().GetWorkerGlobals(aWorker)); |
|
579 properties.iWorker = NULL; |
|
580 properties.iDealer = NULL; |
|
581 properties.iPlayer = NULL; |
|
582 } |
|
583 |
|
584 |
|
585 |
|
586 EXPORT_C void CCommonPitBoss::BroadcastConfigurationComplete(TConfigurationCompletionType aType) |
|
587 { |
|
588 TWorkerConfigurationComplete msg(aType); |
|
589 for(CommsFW::TWorkerId peerId = TWorkerThreadPublicInfo::EFirstPlayerThread; peerId <= TWorkerThreadPublicInfo::EMaxWorkerThreadId; ++peerId) |
|
590 { |
|
591 if(Dealer(peerId)) |
|
592 { |
|
593 COMMONLOG((iOwnerThread->WorkerId(), KECommonServerTag, _L8("CPitBoss::OnCPMsConfigured() - notifying peer %d"), peerId)); |
|
594 if(OwnerThread()->PeerReachable(peerId)) |
|
595 { |
|
596 // Need to make temporary copy of the message as TCFLegacyMessagePacker modifies |
|
597 // it in place and will assert in debug builds if it detects its own special |
|
598 // override code indicating that a message is being packed twice if we broadcast |
|
599 // to more than one peer. |
|
600 TWorkerConfigurationComplete tempMsg(msg); |
|
601 |
|
602 OwnerThread()->PostMessage(peerId, tempMsg); |
|
603 } |
|
604 } |
|
605 } |
|
606 // Process parked requests for the main Dealer |
|
607 Dealer(TWorkerThreadPublicInfo::EMainThread)->ProcessConfigurationComplete(aType); |
|
608 } |
|
609 |
|
610 /** |
|
611 */ |
|
612 EXPORT_C TBool CCommonPitBoss::FindOptimalDealer(TWorkerId aWorkerId, CCommonWorkerDealer*& aDealer) |
|
613 { |
|
614 if(WorkerExists(aWorkerId)) |
|
615 { |
|
616 CCommonDealer* hostThreadDealer = Dealer(*WorkerDataGlobals().GetWorkerGlobals(aWorkerId)); |
|
617 if (hostThreadDealer) |
|
618 { |
|
619 aDealer = hostThreadDealer->WorkerThread().WorkerDealer(); |
|
620 if (aDealer) |
|
621 { |
|
622 return ETrue; |
|
623 } |
|
624 } |
|
625 } |
|
626 return EFalse; |
|
627 } |
|
628 |
|
629 // |
|
630 // CPitBoss::CPeerDeathNotifier |
|
631 // |
|
632 |
|
633 CPeerDeathNotifier::CPeerDeathNotifier(TWorkerId aWorkerId, RThread aThread, MPeerDeathObserver& aObserver) |
|
634 : CActive(EPriorityLow), |
|
635 iWorkerId(aWorkerId), |
|
636 iThread(aThread), |
|
637 iObserver(aObserver) |
|
638 { |
|
639 iThread.Logon(iStatus); |
|
640 CActiveScheduler::Add(this); |
|
641 SetActive(); |
|
642 } |
|
643 |
|
644 void CPeerDeathNotifier::DoCancel() |
|
645 { |
|
646 iThread.LogonCancel(iStatus); |
|
647 } |
|
648 |
|
649 void CPeerDeathNotifier::RunL() |
|
650 { |
|
651 iObserver.OnPeerDeath(iWorkerId); |
|
652 } |
|
653 |
|
654 |
|
655 // |
|
656 // CConfigurationLevelMonitor |
|
657 // |
|
658 |
|
659 CConfigurationLevelMonitor* CConfigurationLevelMonitor::NewL(MConfiguratorObserver* aObserver) |
|
660 { |
|
661 CConfigurationLevelMonitor* self = new(ELeave) CConfigurationLevelMonitor(aObserver); |
|
662 TInt err = self->iConfLevelProperty.Attach(KUidSystemCategory, aObserver->PropertyKey().iUid, EOwnerThread); |
|
663 if(err != KErrNone) |
|
664 { |
|
665 delete self; |
|
666 User::Leave(err); |
|
667 } |
|
668 CActiveScheduler::Add(self); |
|
669 self->Subscribe(); |
|
670 return self; |
|
671 } |
|
672 |
|
673 CConfigurationLevelMonitor::CConfigurationLevelMonitor(MConfiguratorObserver* aObserver) |
|
674 : CActive(EPriorityLow), |
|
675 iObserver(aObserver) |
|
676 { |
|
677 } |
|
678 |
|
679 CConfigurationLevelMonitor::~CConfigurationLevelMonitor() |
|
680 { |
|
681 Cancel(); |
|
682 iConfLevelProperty.Close(); |
|
683 } |
|
684 |
|
685 void CConfigurationLevelMonitor::Subscribe() |
|
686 { |
|
687 iConfLevelProperty.Subscribe(iStatus); |
|
688 SetActive(); |
|
689 } |
|
690 |
|
691 /** |
|
692 Monitors the Comms Configurator configuration level. Will notify the listener when |
|
693 core components have been configured. |
|
694 @see CPitBoss::OnCPMsConfigured |
|
695 */ |
|
696 void CConfigurationLevelMonitor::RunL() |
|
697 { |
|
698 Subscribe(); |
|
699 TInt level; |
|
700 if(iConfLevelProperty.Get(level) == KErrNone) |
|
701 { |
|
702 if(level >= RootServer::EConfigurationComplete && !iCoreComponentsConfigured) |
|
703 { |
|
704 iCoreComponentsConfigured = ETrue; |
|
705 iObserver->OnCPMsConfigured(); |
|
706 } |
|
707 if(level >= RootServer::EConfigurationComplete) |
|
708 { |
|
709 Cancel(); |
|
710 } |
|
711 } |
|
712 } |
|
713 |
|
714 void CConfigurationLevelMonitor::DoCancel() |
|
715 { |
|
716 iConfLevelProperty.Cancel(); |
|
717 } |
|
718 |
|
719 // |
|
720 // CPitBoss::TWorkerThreadRegister |
|
721 // |
|
722 |
|
723 TWorkerThreadRegister::TWorkerThreadRegister() |
|
724 { |
|
725 PeerCleanupPending(TWorkerThreadPublicInfo::EMainThread); |
|
726 #ifdef _DEBUG |
|
727 iHasGlobalAllocFails = EFalse; |
|
728 #endif |
|
729 } |
|
730 |
|
731 void TWorkerThreadRegister::Clear() |
|
732 { |
|
733 TWorkerThreadPublicInfo::Clear(); |
|
734 iDealer = NULL; |
|
735 iPlayer = NULL; |
|
736 } |
|
737 |
|
738 void TWorkerThreadRegister::PeerCleanupPending(TWorkerId aPeerId) |
|
739 { |
|
740 iPendingPeerCleanups |= (1 << aPeerId); |
|
741 } |
|
742 |
|
743 void TWorkerThreadRegister::PeerCleanupCompleted(TWorkerId aPeerId) |
|
744 { |
|
745 iPendingPeerCleanups &= ~(1 << aPeerId); |
|
746 } |
|
747 |
|
748 TBool TWorkerThreadRegister::AllPeerCleanupsCompleted() const |
|
749 { |
|
750 return iPendingPeerCleanups == 0; |
|
751 } |
|
752 |
|
753 |
|
754 // |
|
755 // THeapSwitcher |
|
756 // |
|
757 EXPORT_C THeapSwitcher::THeapSwitcher(CCommonPitBoss& aPitBoss, const Messages::TNodeId& aTarget) |
|
758 { |
|
759 MaybeSwitch(aPitBoss, aTarget.Thread()); |
|
760 } |
|
761 |
|
762 EXPORT_C THeapSwitcher::THeapSwitcher(CCommonPitBoss& aPitBoss, TWorkerId aForeignWorkerId) |
|
763 { |
|
764 MaybeSwitch(aPitBoss, aForeignWorkerId); |
|
765 } |
|
766 |
|
767 |
|
768 EXPORT_C THeapSwitcher::~THeapSwitcher() |
|
769 { |
|
770 RevertToOwnHeap(); |
|
771 } |
|
772 |
|
773 EXPORT_C void THeapSwitcher::RevertToOwnHeap() |
|
774 { |
|
775 if(iPrev) |
|
776 { |
|
777 User::SwitchHeap(iPrev); |
|
778 iPrev = NULL; |
|
779 } |
|
780 } |
|
781 |
|
782 EXPORT_C void THeapSwitcher::MaybeSwitch(CCommonPitBoss& aPitBoss, TWorkerId aForeignWorkerId) |
|
783 { |
|
784 iPrev = aPitBoss.MaybeSwitchHeap(aForeignWorkerId); |
|
785 } |
|
786 |
|
787 /* |
|
788 EXPORT_C void CCommonWorkerThread::MaybeIncorporateFCL(TPlayerRoleMasks aPlane, TInt aPeerKindex, const Messages::TNodeId& aPeerId) |
|
789 { |
|
790 DoMaybeIncorporateFCL(aPlane, aPeerKindex,aPeerId); |
|
791 } |
|
792 */ |
|
793 |