userlibandfileserver/fileserver/sfile/sf_thread.cpp
changeset 0 a41df078684a
child 15 4122176ea935
equal deleted inserted replaced
-1:000000000000 0:a41df078684a
       
     1 // Copyright (c) 2002-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 the License "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 // f32\sfile\sf_thread.cpp
       
    15 // 
       
    16 //
       
    17 
       
    18 #include "sf_std.h"
       
    19 #include <u32exec.h>
       
    20 #include "sf_file_cache.h"
       
    21 
       
    22 #define __CHECK_DRVNUM(d) {__ASSERT_DEBUG(d>=EDriveA && d<=EDriveZ,Fault(EFsThreadBadDrvNum));}
       
    23 
       
    24 #ifdef __X86__
       
    25 const TInt KRequestThreadStackSize = 0x4000;
       
    26 #else
       
    27 const TInt KRequestThreadStackSize = 0x3000;
       
    28 #endif
       
    29 
       
    30 const TInt KFinaliseTimerPeriod = 10 * 1000 * 1000;	// default 10S finalisation timeout
       
    31 
       
    32 TFsDriveThread FsThreadManager::iFsThreads[KMaxDrives];
       
    33 TUint FsThreadManager::iMainId=0;
       
    34 CDisconnectThread* FsThreadManager::iDisconnectThread=NULL;
       
    35 TUint FsThreadManager::iDisconnectThreadId=0;
       
    36 
       
    37 TFsDriveThread::TFsDriveThread()
       
    38 //
       
    39 //
       
    40 //
       
    41 :iIsAvailable(EFalse),iIsSync(EFalse),iThread(NULL),iId(0),iIsHung(EFalse),iMediaChangePending(EFalse)
       
    42 	{
       
    43 	TInt r=iFSLock.CreateLocal();
       
    44 	__ASSERT_ALWAYS(r==KErrNone,Fault(EFsThreadConstructor));
       
    45 	}
       
    46 
       
    47 TFsPluginThread::TFsPluginThread()
       
    48 //
       
    49 //
       
    50 //
       
    51 :iIsAvailable(ETrue),iThread(NULL),iId(0)
       
    52 	{
       
    53 	TInt r=iPluginLock.CreateLocal();
       
    54 	iDriveNumber= KMaxDrives+1;
       
    55 	__ASSERT_ALWAYS(r==KErrNone,Fault(EFsThreadConstructor));
       
    56 	}
       
    57 
       
    58 TInt FsThreadManager::CreateDisconnectThread()
       
    59 //
       
    60 // Called just once at startup
       
    61 //
       
    62 	{
       
    63 	__PRINT(_L("Create disconnect thread"));
       
    64 	TRAPD(r,iDisconnectThread=CDisconnectThread::NewL());
       
    65 	if(r!=KErrNone)
       
    66 		return(r);
       
    67 	TRAP(r,iDisconnectThreadId=iDisconnectThread->StartL());
       
    68 	if(r!=KErrNone)
       
    69 		{
       
    70 		delete(iDisconnectThread);
       
    71 		iDisconnectThread=NULL;
       
    72 		iDisconnectThreadId=0;
       
    73 		}
       
    74 	__THRD_PRINT2(_L("iDisconnectThread=0x%x id=0x%x"),iDisconnectThread,iDisconnectThreadId);
       
    75 	return(r);
       
    76 	}
       
    77 
       
    78 TBool FsThreadManager::IsDisconnectThread()
       
    79 //
       
    80 // Return ETrue if the calling thread is the disconnect thread
       
    81 //
       
    82 	{
       
    83 	return(iDisconnectThreadId==RThread().Id());
       
    84 	}
       
    85 
       
    86 
       
    87 TInt FsThreadManager::InitDrive(TInt aDrvNumber,TBool aIsSync)
       
    88 //
       
    89 // Create a drive thread
       
    90 // Should only by called from main file server thread with drive thread unavailable
       
    91 // 
       
    92 //
       
    93 	{
       
    94 	__PRINT1(_L("FsThreadManager::InitDrive() drive=%d"),aDrvNumber);
       
    95 	TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
       
    96 	__ASSERT_ALWAYS(!t.iIsAvailable,Fault(EThreadManagerInitDrive));
       
    97 	t.iIsSync=ETrue;
       
    98 	
       
    99 	return ChangeSync(aDrvNumber, aIsSync);
       
   100 	}
       
   101 
       
   102 
       
   103 TInt FsThreadManager::ChangeSync(TInt aDrvNumber,TBool aIsSync)
       
   104 //
       
   105 // Change if a drive is syncronouse after it has been inishalised.
       
   106 // Should be called from the main thread.  
       
   107 // Any pending oporations will be compleated.
       
   108 //
       
   109 
       
   110 	{
       
   111 	__PRINT1(_L("FsThreadManager::ChangeSync() drive=%d"),aDrvNumber);
       
   112 	__CHECK_DRVNUM(aDrvNumber);
       
   113 	__CHECK_MAINTHREAD();
       
   114 
       
   115 	LockDrive(aDrvNumber);
       
   116 	TFsDriveThread& t=FsThreadManager::GetFsDriveThread(aDrvNumber);
       
   117 	TInt r=KErrNone;
       
   118 
       
   119 	if (aIsSync!=t.iIsSync)
       
   120 		{
       
   121 		if (!aIsSync)
       
   122 			{
       
   123 			if(!t.iThread)
       
   124 				{
       
   125 				TRAP(r,t.iThread=CDriveThread::NewL());
       
   126 				if(r!=KErrNone)
       
   127 					{
       
   128 					UnlockDrive(aDrvNumber);
       
   129 					return(r);
       
   130 					}
       
   131 				}
       
   132 			TRAP(r,t.iId=t.iThread->StartL(aDrvNumber));
       
   133 			__THRD_PRINT2(_L("Starting thread 0x%x returned %d"),&t,r);
       
   134 			if(r!=KErrNone)
       
   135 				aIsSync=ETrue;
       
   136 			else
       
   137 				{
       
   138 				t.iIsSync=EFalse;
       
   139 				__THRD_PRINT1(_L("drive thread id=0x%x"),t.iId);
       
   140 				}
       
   141 			}
       
   142 		if (aIsSync)
       
   143 			{
       
   144 			if (t.iThread)
       
   145 				{
       
   146 				t.iThread->CompleteAllRequests(KErrNotReady);
       
   147 				t.iThread->iExit=ETrue;
       
   148 				t.iThread=NULL;
       
   149 				}
       
   150 			t.iIsSync=ETrue;
       
   151 			}
       
   152 		}
       
   153 	if (r==KErrNone)
       
   154 		t.iIsAvailable=ETrue;
       
   155 	
       
   156 	UnlockDrive(aDrvNumber);	
       
   157 	return r;
       
   158 	}
       
   159 
       
   160 void FsThreadManager::CloseDrive(TInt aDrvNumber)
       
   161 //
       
   162 // Close a drive thread
       
   163 // Assumes already locked or safe
       
   164 // If file system in not synchronous then should be called from a drive thread request
       
   165 //
       
   166 	{
       
   167 	__PRINT1(_L("FsThreadManager::CloseDrive() drive=%d"),aDrvNumber);
       
   168 	__CHECK_DRVNUM(aDrvNumber);
       
   169 
       
   170 	// no need to cancel requests if synchronous since queued
       
   171 	if(!FsThreadManager::IsDriveSync(aDrvNumber,EFalse))
       
   172 		{
       
   173 		CDriveThread* pT=NULL;
       
   174 		TInt r=FsThreadManager::GetDriveThread(aDrvNumber,&pT);
       
   175 		__ASSERT_ALWAYS(r==KErrNone && pT,Fault(EDismountFsDriveThread));
       
   176 		pT->CompleteAllRequests(KErrNotReady);
       
   177 		}
       
   178 
       
   179 	TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
       
   180 	__ASSERT_ALWAYS(t.iIsAvailable,Fault(EFsThreadDriveClose1));
       
   181 	if(!t.iIsSync)
       
   182 		{
       
   183 		__ASSERT_ALWAYS(FsThreadManager::IsDriveThread(aDrvNumber,EFalse),Fault(EFsThreadDriveClose2));
       
   184 		
       
   185 		StopFinalisationTimer(aDrvNumber);
       
   186 
       
   187 		// drive thread will exit when request completed
       
   188 		t.iThread->iExit=ETrue;
       
   189 
       
   190 		// Ensure that subsequent remounts use a new thread AND a new CDriveThread object - 
       
   191 		// re-use of the CDriveThread object can lead to deadlock while both old & new threads are active.
       
   192 		t.iThread = NULL;	
       
   193 
       
   194 		// Empty the closed file queue for this drive before the thread ends and the CDriveThread object 
       
   195 		// is deleted because the closed file queue contains a list of CFileCache objects which will 
       
   196 		// call CRequestThread::RemoveTimer() when closed
       
   197 		TClosedFileUtils::Remove(aDrvNumber);
       
   198 		}
       
   199 	else
       
   200 		{
       
   201 		__CHECK_MAINTHREAD();
       
   202 		t.iIsSync=EFalse;
       
   203 		}
       
   204 	t.iIsAvailable=EFalse;
       
   205 	t.iId=0;
       
   206 	}
       
   207 
       
   208 
       
   209 TBool FsThreadManager::IsDriveAvailable(TInt aDrvNumber,TBool aIsLock)
       
   210 //
       
   211 //
       
   212 //
       
   213 	{
       
   214 	__CHECK_DRVNUM(aDrvNumber);
       
   215 	TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
       
   216 	if(aIsLock)
       
   217 		t.iFSLock.Wait();
       
   218 	TBool b=t.iIsAvailable;
       
   219 	if(aIsLock)
       
   220 		t.iFSLock.Signal();
       
   221 	__THRD_PRINT2(_L("drive thread %d iIsAvailable=%d"),aDrvNumber,b);
       
   222 	return(b);
       
   223 	}
       
   224 
       
   225 TBool FsThreadManager::IsDriveSync(TInt aDrvNumber,TBool aLock)
       
   226 //
       
   227 // 
       
   228 //
       
   229 	{
       
   230 	__CHECK_DRVNUM(aDrvNumber);
       
   231 	TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
       
   232 	if(aLock)
       
   233 		t.iFSLock.Wait();
       
   234 	TBool b=(t.iIsAvailable&&t.iIsSync);
       
   235 	if(aLock)
       
   236 		t.iFSLock.Signal();
       
   237 	__THRD_PRINT2(_L("drive thread %d iIsSync=%d"),aDrvNumber,b);
       
   238 	return(b);
       
   239 	}
       
   240 
       
   241 TInt FsThreadManager::GetDriveThread(TInt aDrvNumber, CDriveThread** aDrvThread)
       
   242 //
       
   243 // Assumes locked or called from the drive thread
       
   244 //
       
   245 	{
       
   246 	__CHECK_DRVNUM(aDrvNumber);
       
   247 	TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
       
   248 	*aDrvThread=NULL;
       
   249 	TInt r=KErrNone;
       
   250 	if(!t.iIsAvailable)
       
   251 		r=KErrNotReady;
       
   252 	else if(t.iIsSync)
       
   253 		r=KErrAccessDenied;
       
   254 	else
       
   255 		{
       
   256 		*aDrvThread=t.iThread;
       
   257 		__ASSERT_DEBUG(*aDrvThread,Fault(EFsThreadGetThread));
       
   258 		}
       
   259 	__THRD_PRINT4(_L("GetDriveThread(%d) r %d id=0x%x *aDrvThread=0x%x"),aDrvNumber, r, t.iId, *aDrvThread);
       
   260 	return r;
       
   261 	}
       
   262 
       
   263 
       
   264 void FsThreadManager::LockDrive(TInt aDrvNumber)
       
   265 //
       
   266 // Lock the TFsDriveThread object for the aDrvNumber drive
       
   267 //
       
   268 	{
       
   269 	__CHECK_DRVNUM(aDrvNumber);
       
   270 	__THRD_PRINT1(_L("FsThreadManager::LockDrive(%d)"),aDrvNumber);
       
   271 	TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
       
   272 	t.iFSLock.Wait();
       
   273 	}
       
   274 
       
   275 void FsThreadManager::UnlockDrive(TInt aDrvNumber)
       
   276 //
       
   277 // Unlock the TFsDriveThread object for the aDrvNumber drive
       
   278 //
       
   279 	{
       
   280 	__CHECK_DRVNUM(aDrvNumber);
       
   281 	__THRD_PRINT1(_L("FsThreadManager::UnlockDrive(%d)"),aDrvNumber);
       
   282 	TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
       
   283 	t.iFSLock.Signal();
       
   284 	}
       
   285 
       
   286 void FsThreadManager::SetDriveHung(TInt aDrvNumber, TBool aIsHung)
       
   287 	{
       
   288 	if (aDrvNumber < EDriveA || aDrvNumber > EDriveZ)
       
   289 		return;
       
   290 
       
   291 	TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
       
   292 
       
   293 	// quick exit if hung state not changing or drive thread not available
       
   294 	if ((!t.iIsAvailable) || (t.iIsHung == aIsHung))
       
   295 		return;
       
   296 
       
   297 	t.iFSLock.Wait();
       
   298 
       
   299 	// Don't clear the hung state if this is a synchronous request
       
   300 	// and the drive is asynchronous - we need to wait for whatever 
       
   301 	// asynchronous request caused the hang to complete first.
       
   302 	TUint id=RThread().Id();
       
   303 	TBool isDriveThread = t.iIsSync || (!t.iIsSync && t.iId == id);
       
   304 	__THRD_PRINT3(_L("Set %d Hung %d. Is Drive thread %d"), aDrvNumber, aIsHung, isDriveThread);
       
   305 	if (!aIsHung && !isDriveThread)
       
   306 		{
       
   307 		t.iFSLock.Signal();
       
   308 		return;
       
   309 		}
       
   310 
       
   311 	t.iIsHung = aIsHung;
       
   312 
       
   313 	// if we're no longer hung, see if there's a media change pending
       
   314 	// and if so issue one now
       
   315 	TBool mediaChangePending = EFalse;
       
   316 	if(!aIsHung)
       
   317 		{
       
   318 		mediaChangePending = t.iMediaChangePending;
       
   319 		t.iMediaChangePending = EFalse;
       
   320 		}
       
   321 	t.iFSLock.Signal();
       
   322 
       
   323 	// If the drive is now hung we must complete all requests in the drive thread's
       
   324 	// queue - and all subsequent requests - with KErrNotReady to prevent deadlock.
       
   325 	// For example, the notifier server may try to access the loader but one of the
       
   326 	// requests in the queue may already belong to the loader.
       
   327 	if (aIsHung && t.iThread)
       
   328 		t.iThread->CompleteClientRequests(KErrNotReady);
       
   329 
       
   330 	if(mediaChangePending)
       
   331 		FsNotify::DiskChange(aDrvNumber);
       
   332 	}
       
   333 
       
   334 
       
   335 TBool FsThreadManager::IsDriveHung(TInt aDrvNumber)
       
   336 	{
       
   337 	if (aDrvNumber < EDriveA || aDrvNumber > EDriveZ)
       
   338 		return EFalse;
       
   339 
       
   340 	TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
       
   341 //	__THRD_PRINT3(_L("Is %d Hung = %d"), aDrvNumber, t.iIsHung);
       
   342 	return t.iIsHung;
       
   343 	}
       
   344 
       
   345 
       
   346 // If the drive is hung, then don't complete any disk change 
       
   347 // notifications until the request causing the hang completes.
       
   348 void FsThreadManager::SetMediaChangePending(TInt aDrvNumber)
       
   349 	{
       
   350 	if (aDrvNumber < EDriveA || aDrvNumber > EDriveZ)
       
   351 		return;
       
   352 
       
   353 	TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
       
   354 
       
   355 	if (!t.iIsAvailable)
       
   356 		return;
       
   357 
       
   358 	t.iFSLock.Wait();
       
   359 	t.iMediaChangePending = ETrue;
       
   360 	t.iFSLock.Signal();
       
   361 	}
       
   362 
       
   363 void FsThreadManager::SetMainThreadId()
       
   364 //
       
   365 // called at file server startup, assumes called from main file server thread
       
   366 //
       
   367 	{
       
   368 	iMainId=RThread().Id();
       
   369 	__THRD_PRINT1(_L("Main thread id = 0x%x"),iMainId);
       
   370 	}
       
   371 
       
   372 TBool FsThreadManager::IsDriveThread(TInt aDrvNumber,TBool aIsLock)
       
   373 //
       
   374 // Return ETrue if the calling thread is the aDrvNumber drive thread
       
   375 //
       
   376 	{
       
   377 	__CHECK_DRVNUM(aDrvNumber);
       
   378 	TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
       
   379 	TUint id=RThread().Id();
       
   380 	if(aIsLock)
       
   381 		t.iFSLock.Wait();
       
   382 	TBool b = t.iIsAvailable && (!t.iIsSync && t.iId==id || t.iIsSync);
       
   383 	if(aIsLock)
       
   384 		t.iFSLock.Signal();
       
   385 	return(b);
       
   386 	}
       
   387 	
       
   388 TBool FsThreadManager::IsMainThread()
       
   389 //
       
   390 // Returns ETrue if calling thread is same as main file server thread
       
   391 //
       
   392 	{
       
   393 	return((TUint)(RThread().Id())==iMainId);
       
   394 	}
       
   395 
       
   396 
       
   397 void FsThreadManager::StartFinalisationTimer(TInt aDrvNumber)
       
   398 	{
       
   399 	if (aDrvNumber < EDriveA || aDrvNumber > EDriveZ)
       
   400 		return;
       
   401 
       
   402 	// If the message could cause disk modification, make sure that the finalisation
       
   403 	// timer is queued so that we can mark the disk consistent at some point in the future
       
   404 	CDriveThread* driveThread=NULL;
       
   405 	TInt r = GetDriveThread(aDrvNumber, &driveThread);
       
   406 	if(r == KErrNone && driveThread != NULL)
       
   407 		driveThread->StartFinalisationTimer();
       
   408 	}
       
   409 
       
   410 void FsThreadManager::StopFinalisationTimer(TInt aDrvNumber)
       
   411 	{
       
   412 	if (aDrvNumber < EDriveA || aDrvNumber > EDriveZ)
       
   413 		return;
       
   414 
       
   415 	// If the message could cause disk modification, make sure that the finalisation
       
   416 	// timer is queued so that we can mark the disk consistent at some point in the future
       
   417 	CDriveThread* dT=NULL;
       
   418 	TInt r = GetDriveThread(aDrvNumber, &dT);
       
   419 	if(r == KErrNone && dT != NULL)
       
   420 		{
       
   421 		dT->StopFinalisationTimer();
       
   422 		}
       
   423 	}
       
   424 
       
   425 CRequestThread::CRequestThread()
       
   426 //
       
   427 //
       
   428 //
       
   429 :iList(_FOFF(CFsRequest,iLink))
       
   430 	{
       
   431 	//iRequest=NULL;
       
   432 	//iIsWaiting=EFalse;
       
   433 	iExit=EFalse;
       
   434 	}
       
   435 
       
   436 TInt CRequestThread::Initialise()
       
   437 //
       
   438 // Initialise
       
   439 //
       
   440 	{
       
   441 	TInt r=iListLock.CreateLocal();
       
   442 	return(r);
       
   443 	}
       
   444 
       
   445 CRequestThread::~CRequestThread()
       
   446 //
       
   447 //
       
   448 //
       
   449 	{
       
   450 	__ASSERT_ALWAYS(iList.IsEmpty(),Fault(ERequestThreadDestructor));
       
   451 	iListLock.Close();
       
   452 
       
   453 	if(iThread.Handle() != 0)
       
   454 		{
       
   455 		iThread.Close();
       
   456 		}
       
   457 	delete iTimer;
       
   458 	}
       
   459 
       
   460 LOCAL_C TInt ThreadFunction(TAny* aPtr)
       
   461 //
       
   462 //
       
   463 //
       
   464 	{
       
   465 	__THRD_PRINT(_L("ThreadFunction()"));
       
   466 	User::SetCritical(User::ESystemCritical);
       
   467 	CRequestThread* pT=(CRequestThread*)aPtr;
       
   468 	TInt r = pT->ThreadFunction();
       
   469 	delete pT;
       
   470 	return r;
       
   471 	}
       
   472 
       
   473 void CRequestThread::CompleteAllRequests(TInt aValue)
       
   474     {
       
   475     __THRD_PRINT(_L("CRequestThread::CompleteAllRequests()"));
       
   476     iListLock.Wait();
       
   477     while(!iList.IsEmpty())
       
   478         {
       
   479         CFsRequest* pR=iList.First();
       
   480         pR->iLink.Deque();
       
   481         iListLock.Signal();
       
   482         pR->Complete(aValue);
       
   483         iListLock.Wait();
       
   484         }
       
   485     iListLock.Signal();
       
   486     __THRD_PRINT(_L("all requests completed"));
       
   487     }
       
   488 
       
   489 TInt CRequestThread::ThreadFunction()
       
   490 //
       
   491 // entry point for the thread
       
   492 //
       
   493 	{
       
   494 	iTimer = CFsDeltaTimer::New(*this, EPriorityLess);
       
   495 	if (iTimer == NULL)
       
   496 		{
       
   497 		RThread::Rendezvous(KErrNoMemory);
       
   498 		return(KErrNone);
       
   499 		}
       
   500 	iTimer->iStatus = KErrNotReady;
       
   501 
       
   502 	CTrapCleanup* trapHandler=CTrapCleanup::New();
       
   503 	if (trapHandler==NULL)
       
   504 		{
       
   505 		RThread::Rendezvous(KErrNoMemory);
       
   506 		delete iTimer;
       
   507 		return(KErrNone);
       
   508 		}
       
   509 
       
   510 	RThread::Rendezvous(KErrNone);
       
   511 
       
   512 	TInt err = DoThreadInitialise();
       
   513 	if(err != KErrNone)
       
   514 		{
       
   515 		delete trapHandler;
       
   516 		return(KErrNone);
       
   517 		}
       
   518 
       
   519 	iExit=EFalse;
       
   520 	iIsWaiting=EFalse;
       
   521 	// start receiving
       
   522 	Receive();
       
   523 	CompleteAllRequests(KErrNotReady);
       
   524 	
       
   525 	delete trapHandler;
       
   526 	return(KErrNone);
       
   527 	}
       
   528 
       
   529 TInt CRequestThread::DoThreadInitialise()
       
   530 	{
       
   531 	return KErrNone;
       
   532 	}
       
   533 
       
   534 TInt CRequestThread::DoStart(RThread& aThread)
       
   535 //
       
   536 // create thread and return handle
       
   537 // necessary for client to close thread handle if successful
       
   538 //
       
   539 	{
       
   540 	TInt r=aThread.Create(KNullDesC,::ThreadFunction,KRequestThreadStackSize,NULL,(TAny*)this);
       
   541 	__PRINT1(_L("CRequestThread::DoStart() r=%d"),r);
       
   542 	if(r!=KErrNone)
       
   543 		return(r);
       
   544 	TRequestStatus status;
       
   545 	aThread.Rendezvous(status);
       
   546 	if(status==KRequestPending)
       
   547 		{
       
   548 		aThread.SetPriority(EPriorityLess);
       
   549 		aThread.Resume();
       
   550 		}
       
   551 	else
       
   552 		{
       
   553 		aThread.Kill(0);
       
   554 		}
       
   555 	User::WaitForRequest(status);
       
   556 	r = status.Int();
       
   557 	if(r!=KErrNone)
       
   558 		aThread.Close();
       
   559 	else
       
   560 		iThread = aThread;
       
   561 
       
   562 	return(r);
       
   563 	}
       
   564 	
       
   565 
       
   566 void CRequestThread::Receive()
       
   567 //
       
   568 // Receive and process requests
       
   569 //
       
   570 	{
       
   571 	FOREVER
       
   572 		{
       
   573 		iListLock.Wait();
       
   574 		if(!iList.IsEmpty())
       
   575 			{
       
   576 			iRequest=iList.First();
       
   577 			iRequest->iLink.Deque();
       
   578 			__THRD_PRINT(_L("CRequestThread::Receive() dequeing"));
       
   579 			iListLock.Signal();
       
   580 			}
       
   581 		else
       
   582 			{
       
   583 			iIsWaiting=ETrue;
       
   584 			iRequest = NULL;	// set to NULL so we can distinguish between a timer and a request signal
       
   585 			iListLock.Signal();
       
   586 			__THRD_PRINT(_L("CRequestThread::Receive() waiting"));
       
   587 			User::WaitForAnyRequest();
       
   588 			iIsWaiting=EFalse;	// force main thread to post new requests on queue to avoid suspending this thread unnecessarily
       
   589 			}
       
   590 		__THRD_PRINT2(_L("received req 0x%x, func 0x%x"),iRequest, iRequest ? iRequest->Operation()->iFunction : -1);
       
   591 
       
   592 		iTimer->RunL();
       
   593 
       
   594 		if (iRequest)
       
   595 			iRequest->Process();
       
   596 
       
   597 		if(iExit)
       
   598 			break;
       
   599 		}
       
   600 
       
   601 	}
       
   602 
       
   603 void CRequestThread::Deliver(CFsRequest* aRequest,TBool aIsFront, TBool aLowPriority)
       
   604 //
       
   605 // Deliver a request to the list from calling thread
       
   606 // Write request directly to current request if thread is waiting
       
   607 //
       
   608 	{
       
   609 	__THRD_PRINT4(_L("Deliver req %08x to threadId %lx aIsFront=%d iIsWaiting=%d"), aRequest, iThread.Id().Id(), aIsFront, iIsWaiting);
       
   610 	iListLock.Wait();
       
   611 	if (iList.IsEmpty())
       
   612 		{
       
   613 		// if this is a low priority request (and this is the only request in the queue),
       
   614 		// reduce the thread's priority to EPriorityAbsoluteBackground
       
   615 		if (iLowPriority != aLowPriority)
       
   616 			{
       
   617 			__THRD_PRINT(_L("LOWERING THREAD PRIORITY"));
       
   618 			iThread.SetPriority(aLowPriority?EPriorityAbsoluteBackground:EPriorityLess);
       
   619 			iLowPriority = aLowPriority;
       
   620 			}
       
   621 		}
       
   622 	else
       
   623 		{
       
   624 		// there's more than one request in the queue, so rather than go throught the entire queue
       
   625 		// to determine what the thread's priority should be, assume that it should be "high"
       
   626 		if (iLowPriority)
       
   627 			{
       
   628 			iThread.SetPriority(EPriorityLess);
       
   629 			iLowPriority = EFalse;
       
   630 			}
       
   631 		}
       
   632 
       
   633 	if(iIsWaiting)
       
   634 		{
       
   635 		// the request thread must be waiting on the iWaitLock
       
   636 		iIsWaiting=EFalse;
       
   637 		iListLock.Signal();
       
   638 		iRequest=aRequest;
       
   639 
       
   640 		iThread.RequestSignal();
       
   641 		}
       
   642 	else
       
   643 		{
       
   644 		if(aIsFront)
       
   645 			iList.AddFirst(*aRequest);
       
   646 		else
       
   647 			iList.AddLast(*aRequest);
       
   648 		iListLock.Signal();
       
   649 		}
       
   650 
       
   651 
       
   652 	}
       
   653 
       
   654 void CRequestThread::DeliverFront(CFsRequest* aRequest)
       
   655 //
       
   656 //
       
   657 //
       
   658 	{
       
   659 	Deliver(aRequest,ETrue);
       
   660 	}
       
   661 
       
   662 void CRequestThread::DeliverBack(CFsRequest* aRequest, TBool aLowPriority)
       
   663 //
       
   664 //
       
   665 //
       
   666 	{
       
   667 	Deliver(aRequest,EFalse,aLowPriority);
       
   668 	}
       
   669 
       
   670 
       
   671 
       
   672 CFsDeltaTimer* CRequestThread::Timer()
       
   673 	{
       
   674 	__ASSERT_ALWAYS(iTimer,Fault(ERequestThreadNotInitialised));
       
   675 	return iTimer;
       
   676 	}
       
   677 
       
   678 
       
   679 CDriveThread::CDriveThread()
       
   680 	: iFinaliseTimer(FinaliseTimerEvent, this)
       
   681 	{
       
   682 	}
       
   683 
       
   684 CDriveThread* CDriveThread::NewL()
       
   685 //
       
   686 //
       
   687 //
       
   688 	{
       
   689 	__PRINT(_L("CDriveThread::NewL()"));
       
   690 	CDriveThread* pT=new(ELeave) CDriveThread;
       
   691 	TInt r=pT->Initialise();
       
   692 	if(r!=KErrNone)
       
   693 		{
       
   694 		delete(pT);
       
   695 		User::Leave(r);
       
   696 		}
       
   697 	return(pT);
       
   698 	}
       
   699 
       
   700 TUint CDriveThread::StartL(TInt aDrvNumber)
       
   701 //
       
   702 //
       
   703 //
       
   704 	{
       
   705 	__PRINT1(_L("CDriveThread::StartL() on drive %d"),aDrvNumber);
       
   706 	iDriveNumber=aDrvNumber;
       
   707 	RThread t;
       
   708 	User::LeaveIfError(DoStart(t));
       
   709 	TUint id=t.Id();
       
   710 	return(id);
       
   711 	}
       
   712 
       
   713 TInt CDriveThread::DoThreadInitialise()
       
   714 //
       
   715 // Initialise function for the drive thread
       
   716 //  - Renames the thread to contain the drive number.
       
   717 //  - Note: Drive mappings are not available at this time, so we can't show the actual drive letter.
       
   718 //
       
   719 	{
       
   720 	__PRINT1(_L("CDriveThread::DoThreadInitialise() on drive %d"), iDriveNumber);
       
   721 	
       
   722 	TBuf<16> name;
       
   723 	name.Format(_L("DriveThread_%02d"), iDriveNumber);
       
   724 	return(RThread::RenameMe(name));
       
   725 	}
       
   726 
       
   727 void CDriveThread::CompleteSessionRequests(CSessionFs* aSession, TInt aValue)
       
   728 //
       
   729 //
       
   730 //
       
   731 	{
       
   732 	__THRD_PRINT1(_L("CDriveThread::CompleteSessionReqeusts() drive=%d"),iDriveNumber);
       
   733 	iListLock.Wait();
       
   734 	TDblQueIter<CFsRequest> q(iList);
       
   735 	CFsRequest* pR;
       
   736 	while((pR=q++)!=NULL)
       
   737 		{
       
   738 		if(pR->Session()==aSession)
       
   739 			{
       
   740 			pR->iLink.Deque();
       
   741 			iListLock.Signal();
       
   742 			pR->Complete(aValue);
       
   743 			iListLock.Wait();
       
   744 			// set iterator back to head of queue in case Complete() has itself removed requests from the queue
       
   745 			q.SetToFirst();
       
   746 			}
       
   747 		}
       
   748 	iListLock.Signal();
       
   749 	__THRD_PRINT(_L("session requests completed"));
       
   750 	}
       
   751 
       
   752 
       
   753 void CDriveThread::CompleteReadWriteRequests()
       
   754 	{
       
   755 	__THRD_PRINT1(_L("CDriveThread::CompleteReadWriteRequests() drive=%d"),iDriveNumber);
       
   756 
       
   757 	iListLock.Wait();
       
   758 
       
   759 	TDblQueIter<CFsRequest> q(iList);
       
   760 	CFsRequest* pR;
       
   761 	while((pR=q++)!=NULL)
       
   762 		{
       
   763 		TInt func = pR->Operation()->Function();
       
   764 		if (func == EFsFileRead || func == EFsFileWrite || func == EFsFileWriteDirty)
       
   765 			{
       
   766 			pR->iLink.Deque();
       
   767 			pR->Complete(KErrNotReady);
       
   768 			}
       
   769 		}
       
   770 	iListLock.Signal();
       
   771 
       
   772 	__THRD_PRINT(_L("file read/write requests completed"));
       
   773 	}
       
   774 
       
   775 /*
       
   776 This function is called by FsThreadManager::SetDriveHung() and attempts to purge the request queue 
       
   777 of all requests which MIGHT belong to the critical notifier server (or to the loader) to avoid causing 
       
   778 a deadlock when calling the server.
       
   779 
       
   780 All requests are completed with KErrNotReady apart from :
       
   781 - EFsFileWriteDirty requests, to avoid losing dirty data
       
   782 - KDispatchObjectClose requests as they are raised by the file server only and therefore cannot belong to the critical notifier server
       
   783 - EFsFileSubClose requests, to avoid closing files containing dirty data. These requests have their message completed
       
   784   so that clients are unblocked, but the request itself is not processed until later. (If the request WAS processed
       
   785   and completed, then this might result in the CFileCB and CMountCB object being deleted, leading to problems 
       
   786   dereferencing invalid pointers).
       
   787 */
       
   788 void CDriveThread::CompleteClientRequests(TInt aValue)
       
   789 	{
       
   790 	__THRD_PRINT1(_L("CDriveThread::CompleteClientRequests() drive=%d"),iDriveNumber);
       
   791 
       
   792 	iListLock.Wait();
       
   793 
       
   794 	TDblQueIter<CFsRequest> q(iList);
       
   795 	CFsRequest* pR;
       
   796 	while((pR=q++)!=NULL)
       
   797 		{
       
   798 		TInt func = pR->Operation()->Function();
       
   799 		if(func == EFsFileSubClose)
       
   800 			{
       
   801 			TInt msgHandle = pR->Message().Handle();
       
   802 			if ((msgHandle != KLocalMessageHandle) && (msgHandle != 0))
       
   803 				pR->Message().Complete(KErrNone);
       
   804 			}
       
   805 		else if (func != EFsFileWriteDirty && func != KDispatchObjectClose)
       
   806 			{
       
   807 			pR->iLink.Deque();
       
   808 			iListLock.Signal();
       
   809 			pR->Complete(aValue);
       
   810 			iListLock.Wait();
       
   811 			}
       
   812 		}
       
   813 	iListLock.Signal();
       
   814 
       
   815 	__THRD_PRINT(_L("client read requests completed"));
       
   816 	}
       
   817 
       
   818 TBool CDriveThread::IsRequestWriteable()
       
   819 //
       
   820 // return if current request may cause write to disk
       
   821 // must be called from drive thread
       
   822 //
       
   823 	{
       
   824 	__ASSERT_ALWAYS(FsThreadManager::IsDriveThread(iDriveNumber,EFalse),Fault(EDriveThreadWriteable));
       
   825 	return(iRequest->Operation()->IsWrite());
       
   826 	}
       
   827 
       
   828 TBool CDriveThread::IsSessionNotifyUser()
       
   829 //
       
   830 // return if request's session has notify user set
       
   831 // must be called from drive thread and request have a session set
       
   832 //
       
   833 	{
       
   834 	__ASSERT_ALWAYS(FsThreadManager::IsDriveThread(iDriveNumber,EFalse),Fault(EDriveThreadNotifyUser1));
       
   835 	// NB For read-ahead or a flush-dirty write request generated by the file cache, the session will be NULL: 
       
   836 	// in this case assume that notify user is set (as it's the safest option)
       
   837 	CSessionFs* session = iRequest->Session();
       
   838 	return session ?  session->GetNotifyUser() : ETrue;
       
   839 	}
       
   840 
       
   841 void CDriveThread::StartFinalisationTimer()
       
   842 	{
       
   843 	if(IsProxyDrive(iDriveNumber))
       
   844 		iFinaliseTimer.Start(this, KFinaliseTimerPeriod);
       
   845 	}
       
   846 	
       
   847 
       
   848 void CDriveThread::StopFinalisationTimer()
       
   849 	{
       
   850 	iFinaliseTimer.Stop();
       
   851 	}
       
   852 
       
   853 TInt CDriveThread::FinaliseTimerEvent(TAny* aSelfP)
       
   854 	{
       
   855 	CDriveThread& self = *(CDriveThread*)aSelfP;
       
   856 
       
   857 	TDrive& drive = TheDrives[self.iDriveNumber];
       
   858 	if(drive.IsMounted() && drive.CurrentMount().LockStatus() == 0)
       
   859 		{
       
   860 		// Ignore the error here, as there's nothing we can do about it...
       
   861 		(void)drive.FinaliseMount(RFs::EFinal_RW);
       
   862 		}
       
   863 
       
   864 	return KErrNone;
       
   865 	}
       
   866 
       
   867 
       
   868 CDisconnectThread::~CDisconnectThread()
       
   869 //
       
   870 //
       
   871 //
       
   872 	{
       
   873 	if(iRequest)
       
   874 		delete(iRequest);
       
   875 	}
       
   876 
       
   877 
       
   878 CDisconnectThread* CDisconnectThread::NewL()
       
   879 //
       
   880 //
       
   881 //
       
   882 	{
       
   883 	__THRD_PRINT(_L("CDisconnectThread::NewL()"));
       
   884 	CDisconnectThread* pT=new(ELeave) CDisconnectThread;
       
   885 	TInt r=pT->Initialise();
       
   886 	if(r!=KErrNone)
       
   887 		{
       
   888 		delete(pT);
       
   889 		User::Leave(r);
       
   890 		}
       
   891 	return(pT);
       
   892 	}
       
   893 
       
   894 TUint CDisconnectThread::StartL()
       
   895 //
       
   896 //
       
   897 //
       
   898 	{
       
   899 	__PRINT(_L("CDisconnectThread::StartL()"));
       
   900 	iRequest = new(ELeave) CFsInternalRequest;
       
   901 	__THRD_PRINT1(_L("internal request = 0x%x"),iRequest);
       
   902 	iRequest->Set(CancelSessionOp,NULL);	
       
   903 	
       
   904 	RThread t;
       
   905 	TInt r=DoStart(t);
       
   906 	if(r!=KErrNone)
       
   907 		{
       
   908 		delete(iRequest);
       
   909 		iRequest=NULL;
       
   910 		User::Leave(r);
       
   911 		}
       
   912 	iRequest->SetThreadHandle(t.Handle());
       
   913 	__THRD_PRINT1(_L("CDisconnect::StartL() handle=%d"),t.Handle());
       
   914 	iRequest->SetAllocated();
       
   915 	TUint id=t.Id();
       
   916 	return(id);
       
   917 	}
       
   918 
       
   919 
       
   920 CPluginThread::CPluginThread(CFsPlugin& aPlugin)
       
   921   : iPlugin(aPlugin)
       
   922 	{
       
   923 	/** @prototype */
       
   924 	iOperationLock.Close();
       
   925 	}
       
   926 
       
   927 
       
   928 CPluginThread* CPluginThread::NewL(CFsPlugin& aPlugin)
       
   929 	{
       
   930 	__PRINT(_L("CPluginThread::NewL()"));
       
   931 	CPluginThread* pT=new(ELeave) CPluginThread(aPlugin);
       
   932 	TInt r=pT->Initialise();
       
   933 
       
   934 	/** @prototype */
       
   935 	if(r == KErrNone)
       
   936 		r=pT->iOperationLock.CreateLocal(0);
       
   937 
       
   938 	if(r!=KErrNone)
       
   939 		{
       
   940 		delete(pT);
       
   941 		User::Leave(r);
       
   942 		}
       
   943 	return(pT);
       
   944 	}
       
   945 
       
   946 TUint CPluginThread::StartL()
       
   947 	{
       
   948 	__PRINT(_L("CPluginThread::StartL()"));
       
   949 	RThread t;
       
   950 	User::LeaveIfError(DoStart(t));
       
   951 	TUint id=t.Id();
       
   952 	return(id);
       
   953 	}
       
   954 
       
   955 void CPluginThread::CompleteSessionRequests(CSessionFs* aSession, TInt aValue)
       
   956 	{
       
   957 	__THRD_PRINT(_L("CPluginThread::CompleteSessionReqeusts()"));
       
   958 	iListLock.Wait();
       
   959 	TDblQueIter<CFsRequest> q(iList);
       
   960 	CFsRequest* pR;
       
   961 	while((pR=q++)!=NULL)
       
   962 		{
       
   963 		if(pR->Session()==aSession)
       
   964 			{
       
   965 			pR->iLink.Deque();
       
   966 			pR->Complete(aValue);
       
   967 			}
       
   968 		}
       
   969 	iListLock.Signal();
       
   970 	__THRD_PRINT(_L("session requests completed"));
       
   971 	}
       
   972 
       
   973 TInt CPluginThread::DoThreadInitialise()
       
   974 	{
       
   975 	__PRINT(_L("CPluginThread::DoThreadInitialise()"));
       
   976  	TRAPD(err, iPlugin.InitialiseL());
       
   977 
       
   978 	return err;
       
   979 	}
       
   980 
       
   981 /** @prototype */
       
   982 void CPluginThread::OperationLockWait()
       
   983 	{
       
   984 	iOperationLock.Wait();
       
   985 	}
       
   986 
       
   987 /** @prototype */
       
   988 void CPluginThread::OperationLockSignal()
       
   989 	{
       
   990 	iOperationLock.Signal();
       
   991 	}
       
   992 
       
   993 // Class TTickCountQue
       
   994 /**
       
   995 @internalComponent
       
   996 @released
       
   997 
       
   998 Constructs an empty list header
       
   999 */
       
  1000 TTickCountQue::TTickCountQue()
       
  1001 	{}
       
  1002 
       
  1003 
       
  1004 
       
  1005 
       
  1006 /**
       
  1007 @internalComponent
       
  1008 @released
       
  1009 
       
  1010 Adds the specified list element.
       
  1011 
       
  1012 The element is added into the list in order of its tick count.
       
  1013 
       
  1014 @param aRef The list element to be inserted.
       
  1015 */
       
  1016 void TTickCountQue::Add(TTickCountQueLink& aRef)
       
  1017 	{
       
  1018 	TTickCountQueLink* currentLink = (TTickCountQueLink*)(iHead.iNext);
       
  1019 	TTickCountQueLink* addLink = &aRef;
       
  1020 
       
  1021 	while (	(currentLink != (TTickCountQueLink*)&iHead) &&
       
  1022 			(((TInt)(addLink->iTickCount - currentLink->iTickCount)) >= 0)
       
  1023 		)
       
  1024 		{
       
  1025 		currentLink = (TTickCountQueLink*)currentLink->iNext;
       
  1026 		}
       
  1027 
       
  1028 	addLink->Enque(currentLink->iPrev);
       
  1029 	}
       
  1030 
       
  1031 
       
  1032 
       
  1033 
       
  1034 /**
       
  1035 @internalComponent
       
  1036 @released
       
  1037 
       
  1038 Removes the first list element from the linked list if its tick count
       
  1039 is prior to the current tick count.
       
  1040 
       
  1041 @param aTickCount The current tick count.
       
  1042 
       
  1043 @return A pointer to the element removed from the linked list. This is NULL 
       
  1044         if the first element has yet to expire or the queue is empty.
       
  1045 */
       
  1046 TTickCountQueLink* TTickCountQue::RemoveFirst(TUint aTickCount)
       
  1047 	{
       
  1048 	TTickCountQueLink* firstLink = (TTickCountQueLink*)iHead.iNext;
       
  1049 
       
  1050 	if (((TInt)(firstLink->iTickCount - aTickCount)) <= 0)
       
  1051 		{
       
  1052 		return RemoveFirst();
       
  1053 		}
       
  1054 	else
       
  1055 		{
       
  1056 		return NULL;
       
  1057 		}
       
  1058 	}
       
  1059 
       
  1060 
       
  1061 /**
       
  1062 @internalComponent
       
  1063 @released
       
  1064 
       
  1065 Removes the first list element from the linked list, if any.
       
  1066 
       
  1067 @return A pointer to the element removed from the linked list. This is NULL, 
       
  1068         if the queue is empty.
       
  1069 */
       
  1070 TTickCountQueLink* TTickCountQue::RemoveFirst()
       
  1071 	{
       
  1072 	TTickCountQueLink* firstLink = (TTickCountQueLink*)iHead.iNext;
       
  1073 
       
  1074 	if (firstLink != (TTickCountQueLink*)&iHead)
       
  1075 		{
       
  1076 		firstLink->Deque();
       
  1077 		return firstLink;
       
  1078 		}
       
  1079 
       
  1080 	return NULL;
       
  1081 	}
       
  1082 
       
  1083 
       
  1084 
       
  1085 
       
  1086 /**
       
  1087 @internalComponent
       
  1088 @released
       
  1089 
       
  1090 Gets a pointer to the first list element in the doubly linked list.
       
  1091 
       
  1092 @return A pointer to the first list element in the doubly linked list. If 
       
  1093         the list is empty, this pointer is not necessarily NULL and must not
       
  1094 		be assumed to point to a valid object.
       
  1095 */
       
  1096 TTickCountQueLink* TTickCountQue::First() const
       
  1097 	{
       
  1098 #if defined (_DEBUG)
       
  1099 	__DbgTestEmpty();
       
  1100 #endif
       
  1101     return((TTickCountQueLink*)iHead.iNext);
       
  1102     }
       
  1103 
       
  1104 
       
  1105 
       
  1106 
       
  1107 
       
  1108 CFsDeltaTimer* CFsDeltaTimer::New(CRequestThread& aRequestThread, TInt aPriority)
       
  1109 	{
       
  1110 	TTimeIntervalMicroSeconds32 tickPeriod;
       
  1111 	UserHal::TickPeriod(tickPeriod);
       
  1112 
       
  1113 	CFsDeltaTimer* timer = new CFsDeltaTimer(aRequestThread, aPriority, tickPeriod.Int());
       
  1114 	if (timer == NULL)
       
  1115 		return NULL;
       
  1116 
       
  1117 	if (timer->iTimer.CreateLocal() != KErrNone || 
       
  1118 		timer->iLock.CreateLocal() != KErrNone)
       
  1119 		{
       
  1120 		delete timer;
       
  1121 		return NULL;
       
  1122 		}
       
  1123 
       
  1124 	return timer;
       
  1125 	}
       
  1126 
       
  1127 CFsDeltaTimer::CFsDeltaTimer(CRequestThread& aRequestThread, TInt /*aPriority*/, TInt aTickPeriod) : 
       
  1128 	iRequestThread(aRequestThread), iTickPeriod(aTickPeriod)
       
  1129 	{
       
  1130 	iThreadId = RThread().Id(); 
       
  1131 	}
       
  1132 
       
  1133 /**
       
  1134 Destructor.
       
  1135 
       
  1136 Frees all resources before destruction of the object. Specifically, it cancels 
       
  1137 any outstanding timer requests generated by the RTimer object and then deletes 
       
  1138 all timed event entries from the timed event queue.
       
  1139 
       
  1140 @see RTimer
       
  1141 
       
  1142 @publishedAll
       
  1143 @released
       
  1144 */
       
  1145 CFsDeltaTimer::~CFsDeltaTimer()
       
  1146 	{
       
  1147 	Cancel();
       
  1148 
       
  1149 	while (!iQueue.IsEmpty())
       
  1150 		{
       
  1151 		iQueue.First()->Deque();
       
  1152 		}
       
  1153 
       
  1154 	iLock.Close();
       
  1155 	iTimer.Close();
       
  1156 	}
       
  1157 
       
  1158 
       
  1159 /**
       
  1160 Start the timer.
       
  1161 
       
  1162 @see RTimer
       
  1163 
       
  1164 @publishedAll
       
  1165 @released
       
  1166 */
       
  1167 void CFsDeltaTimer::Start(TThreadTimer& aEntry, TTimeIntervalMicroSeconds32 aTime)
       
  1168 	{
       
  1169 	iLock.Wait();
       
  1170 
       
  1171 	// must be already running on this thread or not running at all
       
  1172 	ASSERT(aEntry.iRequestThread == NULL || aEntry.iRequestThread  == &iRequestThread);
       
  1173 
       
  1174 	// attach the entry to this thread
       
  1175 	aEntry.iRequestThread = &iRequestThread;
       
  1176 
       
  1177 	// Remove the entry from the list (if it's already queued) 
       
  1178 	// and then add it again in the correct order
       
  1179 	aEntry.iLink.Deque();
       
  1180 	QueueLong(TTimeIntervalMicroSeconds(MAKE_TINT64(0, aTime.Int())), aEntry);
       
  1181 
       
  1182 	iLock.Signal();
       
  1183 	}
       
  1184 
       
  1185 void CFsDeltaTimer::Stop(TThreadTimer& aEntry)
       
  1186 	{
       
  1187 	iLock.Wait();
       
  1188 
       
  1189 	aEntry.iLink.Deque();
       
  1190 	aEntry.iRequestThread = NULL;
       
  1191 
       
  1192 	iLock.Signal();
       
  1193 	}
       
  1194 
       
  1195 
       
  1196 TInt CFsDeltaTimer::QueueLong(TTimeIntervalMicroSeconds aTimeInMicroSeconds, TThreadTimer& aEntry)
       
  1197 	{
       
  1198 	const TInt64 timeInTicks = (aTimeInMicroSeconds.Int64() + iTickPeriod - 1) / iTickPeriod;
       
  1199 
       
  1200 	TInt timeInTicks32 = I64LOW(timeInTicks);
       
  1201 
       
  1202 	// We are using deltas on tick values, hence using maximum signed number of ticks
       
  1203 	if (I64HIGH(timeInTicks) || (timeInTicks32 < 0))
       
  1204 		{
       
  1205 		return KErrOverflow;
       
  1206 		}
       
  1207 
       
  1208 	// Make sure we queue for at least one tick
       
  1209 	if (timeInTicks32 == 0)
       
  1210 		{
       
  1211 		timeInTicks32 = 1;
       
  1212 		}
       
  1213 	
       
  1214 	// Calculate tick count for new entry
       
  1215 	aEntry.iLink.iTickCount = User::TickCount() + timeInTicks32;
       
  1216 
       
  1217 	// Add this entry at the right spot
       
  1218 	iQueue.Add(aEntry.iLink);
       
  1219 
       
  1220 	// we only need to re-start the timer if we've added an entry to the head of the queue
       
  1221 	// or the timer is not already running
       
  1222 	if (&aEntry.iLink == iQueue.First() || iStatus == KRequestPending)
       
  1223 		Activate();
       
  1224 	
       
  1225 	return KErrNone;
       
  1226 	}
       
  1227 
       
  1228 void CFsDeltaTimer::Activate()
       
  1229 //
       
  1230 // Queue a request on the timer.
       
  1231 //
       
  1232 	{
       
  1233 	if (RThread().Id() != iThreadId)
       
  1234 		{
       
  1235 		iRestartNeeded = ETrue;
       
  1236 		iRequestThread.iThread.RequestSignal();
       
  1237 		return;
       
  1238 		}
       
  1239 
       
  1240 	if (iStatus == KRequestPending)
       
  1241 		Cancel();
       
  1242 
       
  1243 	if (!iQueue.IsEmpty() && !iQueueBusy)
       
  1244 		{
       
  1245 		const TInt ticksToWait = iQueue.First()->iTickCount - User::TickCount();
       
  1246 
       
  1247 		if (ticksToWait > 0)
       
  1248 			{
       
  1249 			iTimer.AfterTicks(iStatus, ticksToWait);
       
  1250 			}
       
  1251 		else
       
  1252 			{
       
  1253 			TRequestStatus* status = &iStatus;
       
  1254 			User::RequestComplete(status, KErrNone);
       
  1255 			}
       
  1256 		}
       
  1257 	}
       
  1258 
       
  1259 
       
  1260 
       
  1261 void CFsDeltaTimer::RunL()
       
  1262 //
       
  1263 // Call all zero delta callbacks
       
  1264 	{
       
  1265 	// if still running and no restart needed, then there's nothing to do
       
  1266 	if (iStatus == KRequestPending && !iRestartNeeded)
       
  1267 		return;
       
  1268 
       
  1269 
       
  1270 	iLock.Wait();
       
  1271 
       
  1272 	// Queue busy
       
  1273 	iQueueBusy = ETrue;
       
  1274 
       
  1275 	// Whilst the list of expired timers is being processed, time will pass and
       
  1276 	// the tick count may have increased such that there are now more expired
       
  1277 	// timers.  Loop until we have either emptied the queue or can wait for a
       
  1278 	// timer exipration in the future.
       
  1279 	if (iStatus == KErrNone)
       
  1280 		{
       
  1281 		iStatus = KErrNotReady;
       
  1282 		while (!iQueue.IsEmpty())
       
  1283 			{
       
  1284 			// Calculate how long till first timer expires
       
  1285 			const TUint tickCount = User::TickCount();
       
  1286 
       
  1287 			// If the first timer is yet to expire, wait some more
       
  1288 			if (((TInt)(iQueue.First()->iTickCount - tickCount)) > 0)
       
  1289 				{
       
  1290 				break;
       
  1291 				}
       
  1292 
       
  1293 			// Remove entry before callback to prevent re-entrancy issues
       
  1294 			TTickCountQueLink* entry = iQueue.RemoveFirst();
       
  1295 
       
  1296 			// Iterate through the timers we know have expired based on the
       
  1297 			// last calculation of delta
       
  1298 			while (entry)
       
  1299 				{
       
  1300 				TThreadTimer* threadTimer = reinterpret_cast<TThreadTimer*>(PtrSub(entry, _FOFF(TThreadTimer, iLink)));
       
  1301 				threadTimer->iRequestThread = NULL;	// indicate timer not running
       
  1302 
       
  1303 				// Make callback.  This could go reentrant on Queue[Long]() or Remove().
       
  1304 				iLock.Signal();
       
  1305 				threadTimer->iCallBack.CallBack();
       
  1306 				iLock.Wait();
       
  1307 
       
  1308 				// Remove the next expired entry, if any
       
  1309 				entry = iQueue.RemoveFirst(tickCount);
       
  1310 				}
       
  1311 			}
       
  1312 		}
       
  1313 
       
  1314 	// Queue idle
       
  1315 	iQueueBusy = EFalse;
       
  1316 
       
  1317 
       
  1318 	// Requeue timer if queue isn't empty
       
  1319 	Activate();
       
  1320 
       
  1321 	iRestartNeeded = EFalse;
       
  1322 
       
  1323 	iLock.Signal();
       
  1324 	}
       
  1325 	
       
  1326 void CFsDeltaTimer::Cancel()
       
  1327 	{
       
  1328 	if (iStatus == KRequestPending)
       
  1329 		{
       
  1330 		iTimer.Cancel();
       
  1331 		User::WaitForRequest(iStatus);
       
  1332 		}
       
  1333 	}
       
  1334 
       
  1335 
       
  1336 
       
  1337 TThreadTimer::TThreadTimer(TInt (*aCallBackFunction)(TAny*),TAny* aPtr) :
       
  1338 	iCallBack(aCallBackFunction, aPtr),
       
  1339 	iRequestThread(NULL)
       
  1340 	{
       
  1341 	};	
       
  1342 
       
  1343 
       
  1344 void TThreadTimer::Start(CRequestThread* aRequestThread, TTimeIntervalMicroSeconds32 aTime)
       
  1345 	{
       
  1346 	ASSERT(aRequestThread);
       
  1347 
       
  1348 	// NB: There are no locks here, so we have to be aware that CFsDeltaTimer::RunL()
       
  1349 	// may be running in another thread and set iRequestThread to NULL
       
  1350 	CRequestThread* requestThread = iRequestThread;
       
  1351 	if (!requestThread)	// if not already running, use caller's request thread
       
  1352 		requestThread = aRequestThread;
       
  1353 
       
  1354 
       
  1355 	__ASSERT_DEBUG(requestThread->Timer(),Fault(ERequestThreadNotInitialised));
       
  1356 	requestThread->Timer()->Start(*this, aTime);
       
  1357 	}
       
  1358 
       
  1359 
       
  1360 void TThreadTimer::Stop()
       
  1361 	{
       
  1362 	// NB: There are no locks here, so we have to be aware that CFsDeltaTimer::RunL()
       
  1363 	// may be running in another thread and set iRequestThread to NULL
       
  1364 	CRequestThread* requestThread = iRequestThread;
       
  1365 	if (requestThread)
       
  1366 		requestThread->Timer()->Stop(*this);
       
  1367 	}
       
  1368 
       
  1369