|
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 <cfshared.h> |
|
23 #include "sd_log.h" |
|
24 #include "sd_roles.h" |
|
25 #include "sd_msgs.h" |
|
26 #include "sd_std.h" |
|
27 #include "sd_errors.h" |
|
28 |
|
29 |
|
30 #ifdef _DEBUG |
|
31 // Panic category for "absolutely impossible!" vanilla ASSERT()-type panics from this module |
|
32 // (if it could happen through user error then you should give it an explicit, documented, category + code) |
|
33 _LIT(KSpecAssert_ElemSvrDenPlayrC, "ElemSvrDenPlayrC"); |
|
34 #endif |
|
35 |
|
36 using namespace Den; |
|
37 using namespace CommsFW; |
|
38 |
|
39 // |
|
40 // CSockSessionProxy |
|
41 // |
|
42 EXPORT_C CCommonSessionProxy::CCommonSessionProxy(CWorkerSession* aSession, CCommonPlayer& aPlayer) |
|
43 : iSession(aSession), |
|
44 iPlayer(aPlayer), |
|
45 iNumSubSessClosing(ELivingSession) |
|
46 { |
|
47 //COMMONLOG((WorkerId(),KECommonBootingTag, _L8("CSockSessionProxy %08x:\tCSockSessionProxy(), iSockSession %08x"), this, iSession) ); |
|
48 } |
|
49 |
|
50 EXPORT_C CCommonSessionProxy::~CCommonSessionProxy() |
|
51 { |
|
52 //LOG(ESockLog::Printf(KESockSessDetailTag, _L8("CSockSessionProxy %08x:\t~CSockSessionProxy(), iSockSession %08x"), this, iSockSession) ); |
|
53 if(iLink.iNext) |
|
54 { |
|
55 iLink.Deque(); |
|
56 } |
|
57 } |
|
58 |
|
59 void CCommonSessionProxy::BeginSessionClose() |
|
60 { |
|
61 //COMMONLOG((WorkerId(),KECommonBootingTag, _L8("CSockSessionProxy %08x:\tBeginSessionClose(), iSockSession %08x"), this, iSession) ); |
|
62 /* Only do something if the message is within the deadline and we're sure |
|
63 the session pointer is safe to use */ |
|
64 CCommonWorkerThread& worker = iPlayer.WorkerThread(); |
|
65 worker.IncProlongBindingLife(); |
|
66 __ASSERT_DEBUG(!IsClosing(), User::Panic(KSpecAssert_ElemSvrDenPlayrC, 1)); |
|
67 iNumSubSessClosing = 1; // dummy subsession to prevent premature suicide during this close loop |
|
68 |
|
69 // The object container is stored as a packed array, so working backwards through it avoids invalidating |
|
70 // the iterator when removing entries (and as a bonus is more efficient) |
|
71 CCommonPlayer::TSubSessionContainer& subSessions(iPlayer.SubSessions()); |
|
72 for(TInt i = subSessions.Count() - 1; i >= 0; --i) |
|
73 { |
|
74 CWorkerSubSession* subSession = subSessions[i]; |
|
75 if(subSession->Session() == iSession) |
|
76 { |
|
77 ++iNumSubSessClosing; |
|
78 if(!subSession->IsClosing()) |
|
79 { |
|
80 subSession->DeleteMe(); |
|
81 } |
|
82 } |
|
83 } |
|
84 |
|
85 NotifySubSessionDestroyed(); // remove the dummy subsession |
|
86 } |
|
87 |
|
88 EXPORT_C void CCommonSessionProxy::NotifySubSessionDestroyed() |
|
89 { |
|
90 //COMMONLOG((WorkerId(),KECommonBootingTag, _L8("CSockSessionProxy %08x:\tNotifySubSessionDestroyed(), iSockSession %08x"), this, iSession) ); |
|
91 if(IsClosing() && --iNumSubSessClosing <= 0) |
|
92 { |
|
93 __ASSERT_DEBUG(iNumSubSessClosing == 0, User::Panic(KSpecAssert_ElemSvrDenPlayrC, 2)); |
|
94 CCommonWorkerThread& worker = iPlayer.WorkerThread(); |
|
95 worker.CompleteSessionClose(iSession); |
|
96 delete this; |
|
97 } |
|
98 } |
|
99 |
|
100 |
|
101 // |
|
102 // CCommonPlayer |
|
103 // |
|
104 |
|
105 /** |
|
106 The Player destructor doesn't have much to do as a lot of the cleanup is done during the |
|
107 normal shutdown routines. Here the Player merely deletes all sub-sessions it owns. |
|
108 */ |
|
109 EXPORT_C CCommonPlayer::~CCommonPlayer() |
|
110 { |
|
111 // The object container is stored as a packed array, so working backwards through it avoids invalidating |
|
112 // the iterator when removing entries (and as a bonus is more efficient) |
|
113 COMMONLOG((WorkerId(),KECommonBootingTag, _L8("CCommonPlayer::~CCommonPlayer()"))); |
|
114 |
|
115 for(TInt i = iSubSessions.Count() - 1; i >= 0; --i) |
|
116 { |
|
117 CWorkerSubSession* subSession = iSubSessions[i]; |
|
118 COMMONLOG((WorkerId(),KECommonBootingTag, _L8("-- destroying %08x"), subSession)); |
|
119 subSession->DeleteMe(); |
|
120 } |
|
121 |
|
122 // Subsessions belong to sessions and get removed when those close. The only time a Player should be going down |
|
123 // with leftover subsessions is an immediate shutdown, else either a session wasn't destroyed properly or a subsession |
|
124 // got leaked off it. We do this after the cleanup loop above so the ptrs are helpfully listed |
|
125 __ASSERT_DEBUG((iSubSessions.Count() == 0 || PitBoss().TestImmediateShutdownPresent()), User::Panic(KSpecAssert_ElemSvrDenPlayrC, 3)); |
|
126 |
|
127 iSubSessions.ResetAndDestroy(); |
|
128 } |
|
129 |
|
130 EXPORT_C CCommonPlayer::CCommonPlayer(CCommonWorkerThread* aOwnerThread, TPlayerRole aPlayerRole) |
|
131 : iOwnerThread(aOwnerThread), |
|
132 iPlayerRole(aPlayerRole) |
|
133 { |
|
134 COMMONLOG((WorkerId(),KECommonBootingTag, _L8("CCommonPlayer::CCommonPlayer()"))); |
|
135 iSessionProxies.SetOffset(CCommonSessionProxy::GetLinkOffset()); |
|
136 } |
|
137 |
|
138 /** |
|
139 MultiTool function to return the current session proxy for a session, but also to create it |
|
140 if it doesn't exist. This avoids having the same pattern in multiple places where we first check |
|
141 to see if it exists then creates it if not. |
|
142 */ |
|
143 EXPORT_C CCommonSessionProxy* CCommonPlayer::CurrentSessionProxyL() |
|
144 { |
|
145 if(!iCurrentSessionProxy) |
|
146 { |
|
147 iCurrentSessionProxy = FindOrCreateSessionProxyL(Session()); |
|
148 } |
|
149 return iCurrentSessionProxy; |
|
150 } |
|
151 |
|
152 CCommonSessionProxy* CCommonPlayer::FindOrCreateSessionProxyL(CWorkerSession* aSession) |
|
153 { |
|
154 CCommonSessionProxy* proxy = FindSessionProxy(aSession); |
|
155 if(proxy==NULL) |
|
156 { |
|
157 // No existing proxy; create a new one and append to list |
|
158 proxy = DoCreateSessionProxyL(aSession); |
|
159 iSessionProxies.AddLast(*proxy); |
|
160 } |
|
161 return proxy; |
|
162 } |
|
163 |
|
164 CCommonSessionProxy* CCommonPlayer::FindSessionProxy(CWorkerSession* aSession) |
|
165 { |
|
166 TDblQueIter<CCommonSessionProxy> iter(iSessionProxies); |
|
167 CCommonSessionProxy* proxy; |
|
168 while((proxy = iter++) != NULL) |
|
169 { |
|
170 if(proxy->Session() == aSession) |
|
171 { |
|
172 break; |
|
173 } |
|
174 } |
|
175 return proxy; |
|
176 } |
|
177 |
|
178 /** |
|
179 The Player can unbind from another worker thread if it doesn't have any sub-sessions |
|
180 belonging to a session in the peer's Dealer and there are no interface cookies left registered |
|
181 at all |
|
182 */ |
|
183 TBool CCommonPlayer::CanUnbindFromWorker(TWorkerId aWorker) |
|
184 { |
|
185 COMMONLOG((WorkerId(), KECommonBootingTag, _L8("CCommonPlayer::CanUnbindFromWorker(%d): %d subsess"), aWorker, iSubSessions.Count())); |
|
186 |
|
187 TInt regItfCount = WorkerThread().Transport()->RegisteredCount(); |
|
188 TInt persistentItfCount = Messages::TlsGlobals::Get().PersistentItfCount(); |
|
189 if(regItfCount > persistentItfCount) |
|
190 { |
|
191 COMMONLOG((WorkerId(), KECommonBootingTag, _L8("-- can't; %d interfaces still registered (%d transient)"), regItfCount, regItfCount - persistentItfCount) ); |
|
192 return EFalse; |
|
193 } |
|
194 |
|
195 for(TInt idx = iSubSessions.Count() - 1; idx >= 0; --idx) |
|
196 { |
|
197 const CWorkerSubSession* ss = iSubSessions[idx]; |
|
198 COMMONLOG((WorkerId(),KECommonBootingTag, _L8("-- subsess %08x worker=%d"), ss, ss->Session()->WorkerId())); |
|
199 if(ss->Session()->WorkerId() == aWorker) |
|
200 { |
|
201 COMMONLOG((WorkerId(),KECommonBootingTag, _L8("-- can't; subsess %08x belongs to this peer"), ss) ); |
|
202 return EFalse; |
|
203 } |
|
204 } |
|
205 return ETrue; |
|
206 } |
|
207 |
|
208 EXPORT_C void CCommonPlayer::MaybeSetPlayerShutdownComplete(TBool aForceShutdownNow) |
|
209 { |
|
210 //A forced shutdown trumps all other considerations |
|
211 TBool shutdownComplete = aForceShutdownNow || (SubSessions().Count() == 0 && IsPlayerShutdownComplete()); |
|
212 |
|
213 COMMONLOG((WorkerId(), KECommonBootingTag, _L8("CPlayer::MaybeSetPlayerShutdownComplete(), shutdownComplete = %d [forced=%d, #subSess=%d]"), |
|
214 shutdownComplete, aForceShutdownNow, SubSessions().Count())); |
|
215 |
|
216 WorkerThread().SetPlayerShutdownComplete(shutdownComplete); |
|
217 } |
|
218 |
|
219 EXPORT_C CWorkerSubSession* CCommonPlayer::SubSession(const TSubSessionUniqueId& aSubSessionUniqueId) const |
|
220 { |
|
221 __ASSERT_DEBUG(aSubSessionUniqueId!=0, User::Panic(KDenFaultPanic, ECommonBadSubSessionUniqueId)); |
|
222 CWorkerSubSession* ss = NULL; |
|
223 for (TInt i = iSubSessions.Count() - 1; i>=0 && ss==NULL ; i--) |
|
224 { |
|
225 if (iSubSessions[i]->UniqueId()==aSubSessionUniqueId) |
|
226 { |
|
227 ss = iSubSessions[i]; |
|
228 } |
|
229 } |
|
230 return ss; |
|
231 } |
|
232 |
|
233 EXPORT_C void CCommonPlayer::ProcessMessageL(const RSafeMessage& aMessage, CWorkerSubSession* aSubSession) |
|
234 { |
|
235 SetSession(static_cast<CWorkerSession*>(aMessage.Session())); |
|
236 iCurrentSessionProxy = NULL; |
|
237 Reset(); |
|
238 iCurrentMessage = &aMessage; |
|
239 |
|
240 DoProcessMessageL(aMessage,aSubSession); |
|
241 } |
|
242 |
|
243 /** |
|
244 Panic the client in response to an operation on an existing subsession, after |
|
245 which the request mustn't be completed |
|
246 */ |
|
247 void CCommonPlayer::PanicClient(CWorkerSubSession& /*aSubSession*/, TInt aPanic) |
|
248 { |
|
249 SafeMessage().PanicClient(KDenFaultPanic, aPanic); |
|
250 DontCompleteCurrentRequest(); |
|
251 } |
|
252 |
|
253 /** |
|
254 Panic the client in response to an operation on an existing subsession, after |
|
255 which the request mustn't be completed |
|
256 |
|
257 This version allows panic categories to be passed in to support expected legacy |
|
258 behaviour. |
|
259 */ |
|
260 void CCommonPlayer::PanicClient(CWorkerSubSession& /*aSubSession*/, const TDesC &aCategory, TInt aPanic) |
|
261 { |
|
262 SafeMessage().PanicClient(aCategory, aPanic); |
|
263 DontCompleteCurrentRequest(); |
|
264 } |
|
265 |
|
266 EXPORT_C void CCommonPlayer::PanicClient(TInt aPanic) |
|
267 { |
|
268 SafeMessage().PanicClient(KDenFaultPanic, aPanic); |
|
269 DontCompleteCurrentRequest(); |
|
270 } |
|
271 |
|
272 EXPORT_C void CCommonPlayer::PanicClient(const TDesC& aCategory, TInt aPanic) |
|
273 { |
|
274 SafeMessage().PanicClient(aCategory, aPanic); |
|
275 DontCompleteCurrentRequest(); |
|
276 } |
|
277 |
|
278 EXPORT_C void CCommonPlayer::DontCompleteCurrentRequest() |
|
279 { |
|
280 iComplete=EFalse; |
|
281 } |
|
282 |
|
283 EXPORT_C TBool CCommonPlayer::ShouldCompleteCurrentRequest() const |
|
284 { |
|
285 return iComplete; |
|
286 } |
|
287 |
|
288 EXPORT_C void CCommonPlayer::SetReturn(TInt aReturn/*, TInt anError, TUid anOwner*/) |
|
289 { |
|
290 iReturn=aReturn; |
|
291 |
|
292 } |
|
293 |
|
294 /** |
|
295 Remove all subsessions belonging to sessions of the nominated worker. No locks required; only the |
|
296 Player accesses the object container. |
|
297 @see TWorkerMsg::ECleanupDeadPeer |
|
298 */ |
|
299 EXPORT_C void CCommonPlayer::CleanupDeadWorker(TWorkerId aWorkerId) |
|
300 { |
|
301 (void)aWorkerId; |
|
302 //TBDAA - it's in ss_glob.h ASSERT_HOME_THREAD; |
|
303 |
|
304 // The object container is stored as a packed array, so working backwards through it avoids invalidating |
|
305 // the iterator when removing entries (and as a bonus is more efficient) |
|
306 for(TInt i = iSubSessions.Count() - 1; i >= 0; --i) |
|
307 { |
|
308 iSubSessions[i]->CleanupDeadWorker(aWorkerId); |
|
309 } |
|
310 } |
|
311 /** |
|
312 If an incoming shutdown request is of type EImmediate, informs the |
|
313 Worker Thread that Player shutdown is complete, otherwise do nothing here. |
|
314 */ |
|
315 void CCommonPlayer::ProcessShutdownRequest(TCFShutdownType aType) |
|
316 { |
|
317 COMMONLOG((WorkerId(),KECommonBootingTag, _L8("CPlayer::ProcessShutdownRequest(%d)"), aType)); |
|
318 WorkerThread().SetPlayerShutdownComplete(aType == CommsFW::EImmediate); |
|
319 } |
|
320 |
|
321 /** |
|
322 Walk through all of the subsessions, telling each to complete all blocked requests |
|
323 with KErrServerAbort. We don't attempt to free them - although with a shared |
|
324 heap this could reduce the amount of leakage it seems likely that the more we do |
|
325 with these data structures, the greater the risk that the contagion spreads to |
|
326 other users of the heap. Clients that attempt almost any further operation after KErrAbort |
|
327 will get panicked by the Dealer in the usual bad handle way - this is harsh but fair, and |
|
328 better than simply leaving them hung. |
|
329 */ |
|
330 TInt CCommonPlayer::PostMortemCleanup() |
|
331 { |
|
332 // No locks are required - we're only here because the owner has died |
|
333 for(TInt i = 0; i < iSubSessions.Count(); ++i) |
|
334 { |
|
335 CWorkerSubSession* subSession = static_cast<CWorkerSubSession*>((iSubSessions[i])); |
|
336 subSession->FinalCompleteAllBlockedMessages(KErrAbort); |
|
337 } |
|
338 return KErrNone; |
|
339 } |
|
340 |