commsfwsupport/commselements/serverden/src/sd_dealer.cpp
changeset 0 dfb7c4ff071f
equal deleted inserted replaced
-1:000000000000 0:dfb7c4ff071f
       
     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