os/kernelhwsrv/userlibandfileserver/fileserver/sfile/sf_thread.cpp
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     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 		    {
   599 		    //Any requests that sneaked on to
   600 		    //the queue are cancelled in 
   601 		    //CRequestThread::ThreadFunction()
   602 		    break;
   603 		    }
   604 		}
   605 	}
   606 
   607 void CRequestThread::Deliver(CFsRequest* aRequest,TBool aIsFront, TBool aLowPriority)
   608 //
   609 // Deliver a request to the list from calling thread
   610 // Write request directly to current request if thread is waiting
   611 //
   612 	{
   613 	__THRD_PRINT4(_L("Deliver req %08x to threadId %lx aIsFront=%d iIsWaiting=%d"), aRequest, iThread.Id().Id(), aIsFront, iIsWaiting);
   614 	iListLock.Wait();
   615 	if (iList.IsEmpty())
   616 		{
   617 		// if this is a low priority request (and this is the only request in the queue),
   618 		// reduce the thread's priority to EPriorityAbsoluteBackground
   619 		if (iLowPriority != aLowPriority)
   620 			{
   621 			__THRD_PRINT(_L("LOWERING THREAD PRIORITY"));
   622 			iThread.SetPriority(aLowPriority?EPriorityAbsoluteBackground:EPriorityLess);
   623 			iLowPriority = aLowPriority;
   624 			}
   625 		}
   626 	else
   627 		{
   628 		// there's more than one request in the queue, so rather than go throught the entire queue
   629 		// to determine what the thread's priority should be, assume that it should be "high"
   630 		if (iLowPriority)
   631 			{
   632 			iThread.SetPriority(EPriorityLess);
   633 			iLowPriority = EFalse;
   634 			}
   635 		}
   636 
   637 	if(iIsWaiting)
   638 		{
   639 		// the request thread must be waiting on the iWaitLock
   640 		iIsWaiting=EFalse;
   641 		iListLock.Signal();
   642 		iRequest=aRequest;
   643 
   644 		iThread.RequestSignal();
   645 		}
   646 	else
   647 		{
   648 		if(aIsFront)
   649 			iList.AddFirst(*aRequest);
   650 		else
   651 			iList.AddLast(*aRequest);
   652 		iListLock.Signal();
   653 		}
   654 	}
   655 
   656 void CRequestThread::DeliverFront(CFsRequest* aRequest)
   657 //
   658 //
   659 //
   660 	{
   661 	Deliver(aRequest,ETrue);
   662 	}
   663 
   664 void CRequestThread::DeliverBack(CFsRequest* aRequest, TBool aLowPriority)
   665 //
   666 //
   667 //
   668 	{
   669 	Deliver(aRequest,EFalse,aLowPriority);
   670 	}
   671 
   672 
   673 
   674 CFsDeltaTimer* CRequestThread::Timer()
   675 	{
   676 	__ASSERT_ALWAYS(iTimer,Fault(ERequestThreadNotInitialised));
   677 	return iTimer;
   678 	}
   679 
   680 
   681 CDriveThread::CDriveThread()
   682 	: iFinaliseTimer(FinaliseTimerEvent, this)
   683 	{
   684 	}
   685 
   686 CDriveThread* CDriveThread::NewL()
   687 //
   688 //
   689 //
   690 	{
   691 	__PRINT(_L("CDriveThread::NewL()"));
   692 	CDriveThread* pT=new(ELeave) CDriveThread;
   693 	TInt r=pT->Initialise();
   694 	if(r!=KErrNone)
   695 		{
   696 		delete(pT);
   697 		User::Leave(r);
   698 		}
   699 	return(pT);
   700 	}
   701 
   702 TUint CDriveThread::StartL(TInt aDrvNumber)
   703 //
   704 //
   705 //
   706 	{
   707 	__PRINT1(_L("CDriveThread::StartL() on drive %d"),aDrvNumber);
   708 	iDriveNumber=aDrvNumber;
   709 	RThread t;
   710 	User::LeaveIfError(DoStart(t));
   711 	TUint id=t.Id();
   712 	return(id);
   713 	}
   714 
   715 TInt CDriveThread::DoThreadInitialise()
   716 //
   717 // Initialise function for the drive thread
   718 //  - Renames the thread to contain the drive number.
   719 //  - Note: Drive mappings are not available at this time, so we can't show the actual drive letter.
   720 //
   721 	{
   722 	__PRINT1(_L("CDriveThread::DoThreadInitialise() on drive %d"), iDriveNumber);
   723 	
   724 	TBuf<16> name;
   725 	name.Format(_L("DriveThread_%02d"), iDriveNumber);
   726 	return(RThread::RenameMe(name));
   727 	}
   728 
   729 void CDriveThread::CompleteSessionRequests(CSessionFs* aSession, TInt aValue)
   730 //
   731 //
   732 //
   733 	{
   734 	__THRD_PRINT1(_L("CDriveThread::CompleteSessionReqeusts() drive=%d"),iDriveNumber);
   735 	iListLock.Wait();
   736 	TDblQueIter<CFsRequest> q(iList);
   737 	CFsRequest* pR;
   738 	while((pR=q++)!=NULL)
   739 		{
   740 		if(pR->Session()==aSession)
   741 			{
   742 			pR->iLink.Deque();
   743 			iListLock.Signal();
   744 			pR->Complete(aValue);
   745 			iListLock.Wait();
   746 			// set iterator back to head of queue in case Complete() has itself removed requests from the queue
   747 			q.SetToFirst();
   748 			}
   749 		}
   750 	iListLock.Signal();
   751 	__THRD_PRINT(_L("session requests completed"));
   752 	}
   753 
   754 
   755 void CDriveThread::CompleteReadWriteRequests()
   756 	{
   757 	__THRD_PRINT1(_L("CDriveThread::CompleteReadWriteRequests() drive=%d"),iDriveNumber);
   758 
   759 	iListLock.Wait();
   760 
   761 	TDblQueIter<CFsRequest> q(iList);
   762 	CFsRequest* pR;
   763 	while((pR=q++)!=NULL)
   764 		{
   765 		TInt func = pR->Operation()->Function();
   766 		if (func == EFsFileRead || func == EFsFileWrite || func == EFsFileWriteDirty)
   767 			{
   768 			pR->iLink.Deque();
   769 			pR->Complete(KErrNotReady);
   770 			}
   771 		}
   772 	iListLock.Signal();
   773 
   774 	__THRD_PRINT(_L("file read/write requests completed"));
   775 	}
   776 
   777 /*
   778 This function is called by FsThreadManager::SetDriveHung() and attempts to purge the request queue 
   779 of all requests which MIGHT belong to the critical notifier server (or to the loader) to avoid causing 
   780 a deadlock when calling the server.
   781 
   782 All requests are completed with KErrNotReady apart from :
   783 - EFsFileWriteDirty requests, to avoid losing dirty data
   784 - KDispatchObjectClose requests as they are raised by the file server only and therefore cannot belong to the critical notifier server
   785 - EFsFileSubClose requests, to avoid closing files containing dirty data. These requests have their message completed
   786   so that clients are unblocked, but the request itself is not processed until later. (If the request WAS processed
   787   and completed, then this might result in the CFileCB and CMountCB object being deleted, leading to problems 
   788   dereferencing invalid pointers).
   789 */
   790 void CDriveThread::CompleteClientRequests(TInt aValue)
   791 	{
   792 	__THRD_PRINT1(_L("CDriveThread::CompleteClientRequests() drive=%d"),iDriveNumber);
   793 
   794 	iListLock.Wait();
   795 
   796 	TDblQueIter<CFsRequest> q(iList);
   797 	CFsRequest* pR;
   798 	while((pR=q++)!=NULL)
   799 		{
   800 		TInt func = pR->Operation()->Function();
   801 		if(func == EFsFileSubClose)
   802 			{
   803 			TInt msgHandle = pR->Message().Handle();
   804 			if ((msgHandle != KLocalMessageHandle) && (msgHandle != 0))
   805 				pR->Message().Complete(KErrNone);
   806 			}
   807 		else if (func != EFsFileWriteDirty && func != KDispatchObjectClose)
   808 			{
   809 			pR->iLink.Deque();
   810 			iListLock.Signal();
   811 			pR->Complete(aValue);
   812 			iListLock.Wait();
   813 			}
   814 		}
   815 	iListLock.Signal();
   816 
   817 	__THRD_PRINT(_L("client read requests completed"));
   818 	}
   819 
   820 TBool CDriveThread::IsRequestWriteable()
   821 //
   822 // return if current request may cause write to disk
   823 // must be called from drive thread
   824 //
   825 	{
   826 	__ASSERT_ALWAYS(FsThreadManager::IsDriveThread(iDriveNumber,EFalse),Fault(EDriveThreadWriteable));
   827 	return(iRequest->Operation()->IsWrite());
   828 	}
   829 
   830 TBool CDriveThread::IsSessionNotifyUser()
   831 //
   832 // return if request's session has notify user set
   833 // must be called from drive thread and request have a session set
   834 //
   835 	{
   836 	__ASSERT_ALWAYS(FsThreadManager::IsDriveThread(iDriveNumber,EFalse),Fault(EDriveThreadNotifyUser1));
   837 	// NB For read-ahead or a flush-dirty write request generated by the file cache, the session will be NULL: 
   838 	// in this case assume that notify user is set (as it's the safest option)
   839 	CSessionFs* session = iRequest->Session();
   840 	return session ?  session->GetNotifyUser() : ETrue;
   841 	}
   842 
   843 void CDriveThread::StartFinalisationTimer()
   844 	{
   845 	if(IsProxyDrive(iDriveNumber))
   846 		iFinaliseTimer.Start(this, KFinaliseTimerPeriod);
   847 	}
   848 	
   849 
   850 void CDriveThread::StopFinalisationTimer()
   851 	{
   852 	iFinaliseTimer.Stop();
   853 	}
   854 
   855 TInt CDriveThread::FinaliseTimerEvent(TAny* aSelfP)
   856 	{
   857 	CDriveThread& self = *(CDriveThread*)aSelfP;
   858 
   859 	TDrive& drive = TheDrives[self.iDriveNumber];
   860 	if(drive.IsMounted())
   861         {
   862         if (drive.CurrentMount().LockStatus() == 0)
   863             {
   864             // Ignore the error here, as there's nothing we can do about it...
   865             (void)drive.FinaliseMount(RFs::EFinal_RW);
   866             }
   867         else
   868             {
   869             self.StartFinalisationTimer();
   870             }
   871         }
   872 
   873 	return KErrNone;
   874 	}
   875 
   876 
   877 CDisconnectThread::~CDisconnectThread()
   878 //
   879 //
   880 //
   881 	{
   882 	if(iRequest)
   883 		delete(iRequest);
   884 	}
   885 
   886 
   887 CDisconnectThread* CDisconnectThread::NewL()
   888 //
   889 //
   890 //
   891 	{
   892 	__THRD_PRINT(_L("CDisconnectThread::NewL()"));
   893 	CDisconnectThread* pT=new(ELeave) CDisconnectThread;
   894 	TInt r=pT->Initialise();
   895 	if(r!=KErrNone)
   896 		{
   897 		delete(pT);
   898 		User::Leave(r);
   899 		}
   900 	return(pT);
   901 	}
   902 
   903 TUint CDisconnectThread::StartL()
   904 //
   905 //
   906 //
   907 	{
   908 	__PRINT(_L("CDisconnectThread::StartL()"));
   909 	iRequest = new(ELeave) CFsInternalRequest;
   910 	__THRD_PRINT1(_L("internal request = 0x%x"),iRequest);
   911 	iRequest->Set(CancelSessionOp,NULL);	
   912 	
   913 	RThread t;
   914 	TInt r=DoStart(t);
   915 	if(r!=KErrNone)
   916 		{
   917 		delete(iRequest);
   918 		iRequest=NULL;
   919 		User::Leave(r);
   920 		}
   921 	iRequest->SetThreadHandle(t.Handle());
   922 	__THRD_PRINT1(_L("CDisconnect::StartL() handle=%d"),t.Handle());
   923 	iRequest->SetAllocated();
   924 	TUint id=t.Id();
   925 	return(id);
   926 	}
   927 
   928 
   929 CPluginThread::CPluginThread(CFsPlugin& aPlugin)
   930   : iPlugin(aPlugin)
   931 	{
   932 	/** @prototype */
   933 	iOperationLock.Close();
   934 	iPlugin.Open();
   935 	}
   936 
   937 CPluginThread::~CPluginThread()
   938     {
   939     iPlugin.Close();
   940     }
   941 
   942 
   943 CPluginThread* CPluginThread::NewL(CFsPlugin& aPlugin)
   944 	{
   945 	__PRINT(_L("CPluginThread::NewL()"));
   946 	CPluginThread* pT=new(ELeave) CPluginThread(aPlugin);
   947 	TInt r=pT->Initialise();
   948 
   949 	/** @prototype */
   950 	if(r == KErrNone)
   951 		r=pT->iOperationLock.CreateLocal(0);
   952 
   953 	if(r!=KErrNone)
   954 		{
   955 		delete(pT);
   956 		User::Leave(r);
   957 		}
   958 	return(pT);
   959 	}
   960 
   961 TUint CPluginThread::StartL()
   962 	{
   963 	__PRINT(_L("CPluginThread::StartL()"));
   964 	RThread t;
   965 	User::LeaveIfError(DoStart(t));
   966 	TUint id=t.Id();
   967 	return(id);
   968 	}
   969 
   970 void CPluginThread::CompleteSessionRequests(CSessionFs* aSession, TInt aValue)
   971 	{
   972 	__THRD_PRINT(_L("CPluginThread::CompleteSessionRequests()"));
   973 	iListLock.Wait();
   974 	TDblQueIter<CFsRequest> q(iList);
   975 	CFsRequest* pR;
   976 	while((pR=q++)!=NULL)
   977 		{
   978 		if(pR->Session()==aSession)
   979 			{
   980 			pR->iLink.Deque();
   981 			pR->Complete(aValue);
   982 			}
   983 		}
   984 	iListLock.Signal();
   985 	__THRD_PRINT(_L("session requests completed"));
   986 	}
   987 
   988 TInt CPluginThread::DoThreadInitialise()
   989 	{
   990 	__PRINT(_L("CPluginThread::DoThreadInitialise()"));
   991  	TRAPD(err, iPlugin.InitialiseL());
   992 
   993 	return err;
   994 	}
   995 
   996 /** @prototype */
   997 void CPluginThread::OperationLockWait()
   998 	{
   999 	iOperationLock.Wait();
  1000 	}
  1001 
  1002 /** @prototype */
  1003 void CPluginThread::OperationLockSignal()
  1004 	{
  1005 	iOperationLock.Signal();
  1006 	}
  1007 
  1008 // Class TTickCountQue
  1009 /**
  1010 @internalComponent
  1011 @released
  1012 
  1013 Constructs an empty list header
  1014 */
  1015 TTickCountQue::TTickCountQue()
  1016 	{}
  1017 
  1018 
  1019 
  1020 
  1021 /**
  1022 @internalComponent
  1023 @released
  1024 
  1025 Adds the specified list element.
  1026 
  1027 The element is added into the list in order of its tick count.
  1028 
  1029 @param aRef The list element to be inserted.
  1030 */
  1031 void TTickCountQue::Add(TTickCountQueLink& aRef)
  1032 	{
  1033 	TTickCountQueLink* currentLink = (TTickCountQueLink*)(iHead.iNext);
  1034 	TTickCountQueLink* addLink = &aRef;
  1035 
  1036 	while (	(currentLink != (TTickCountQueLink*)&iHead) &&
  1037 			(((TInt)(addLink->iTickCount - currentLink->iTickCount)) >= 0)
  1038 		)
  1039 		{
  1040 		currentLink = (TTickCountQueLink*)currentLink->iNext;
  1041 		}
  1042 
  1043 	addLink->Enque(currentLink->iPrev);
  1044 	}
  1045 
  1046 
  1047 
  1048 
  1049 /**
  1050 @internalComponent
  1051 @released
  1052 
  1053 Removes the first list element from the linked list if its tick count
  1054 is prior to the current tick count.
  1055 
  1056 @param aTickCount The current tick count.
  1057 
  1058 @return A pointer to the element removed from the linked list. This is NULL 
  1059         if the first element has yet to expire or the queue is empty.
  1060 */
  1061 TTickCountQueLink* TTickCountQue::RemoveFirst(TUint aTickCount)
  1062 	{
  1063 	TTickCountQueLink* firstLink = (TTickCountQueLink*)iHead.iNext;
  1064 
  1065 	if (((TInt)(firstLink->iTickCount - aTickCount)) <= 0)
  1066 		{
  1067 		return RemoveFirst();
  1068 		}
  1069 	else
  1070 		{
  1071 		return NULL;
  1072 		}
  1073 	}
  1074 
  1075 
  1076 /**
  1077 @internalComponent
  1078 @released
  1079 
  1080 Removes the first list element from the linked list, if any.
  1081 
  1082 @return A pointer to the element removed from the linked list. This is NULL, 
  1083         if the queue is empty.
  1084 */
  1085 TTickCountQueLink* TTickCountQue::RemoveFirst()
  1086 	{
  1087 	TTickCountQueLink* firstLink = (TTickCountQueLink*)iHead.iNext;
  1088 
  1089 	if (firstLink != (TTickCountQueLink*)&iHead)
  1090 		{
  1091 		firstLink->Deque();
  1092 		return firstLink;
  1093 		}
  1094 
  1095 	return NULL;
  1096 	}
  1097 
  1098 
  1099 
  1100 
  1101 /**
  1102 @internalComponent
  1103 @released
  1104 
  1105 Gets a pointer to the first list element in the doubly linked list.
  1106 
  1107 @return A pointer to the first list element in the doubly linked list. If 
  1108         the list is empty, this pointer is not necessarily NULL and must not
  1109 		be assumed to point to a valid object.
  1110 */
  1111 TTickCountQueLink* TTickCountQue::First() const
  1112 	{
  1113 #if defined (_DEBUG)
  1114 	__DbgTestEmpty();
  1115 #endif
  1116     return((TTickCountQueLink*)iHead.iNext);
  1117     }
  1118 
  1119 
  1120 
  1121 
  1122 
  1123 CFsDeltaTimer* CFsDeltaTimer::New(CRequestThread& aRequestThread, TInt aPriority)
  1124 	{
  1125 	TTimeIntervalMicroSeconds32 tickPeriod;
  1126 	UserHal::TickPeriod(tickPeriod);
  1127 
  1128 	CFsDeltaTimer* timer = new CFsDeltaTimer(aRequestThread, aPriority, tickPeriod.Int());
  1129 	if (timer == NULL)
  1130 		return NULL;
  1131 
  1132 	if (timer->iTimer.CreateLocal() != KErrNone || 
  1133 		timer->iLock.CreateLocal() != KErrNone)
  1134 		{
  1135 		delete timer;
  1136 		return NULL;
  1137 		}
  1138 
  1139 	return timer;
  1140 	}
  1141 
  1142 CFsDeltaTimer::CFsDeltaTimer(CRequestThread& aRequestThread, TInt /*aPriority*/, TInt aTickPeriod) : 
  1143 	iRequestThread(aRequestThread), iTickPeriod(aTickPeriod)
  1144 	{
  1145 	iThreadId = RThread().Id(); 
  1146 	}
  1147 
  1148 /**
  1149 Destructor.
  1150 
  1151 Frees all resources before destruction of the object. Specifically, it cancels 
  1152 any outstanding timer requests generated by the RTimer object and then deletes 
  1153 all timed event entries from the timed event queue.
  1154 
  1155 @see RTimer
  1156 
  1157 @publishedAll
  1158 @released
  1159 */
  1160 CFsDeltaTimer::~CFsDeltaTimer()
  1161 	{
  1162 	Cancel();
  1163 
  1164 	while (!iQueue.IsEmpty())
  1165 		{
  1166 		iQueue.First()->Deque();
  1167 		}
  1168 
  1169 	iLock.Close();
  1170 	iTimer.Close();
  1171 	}
  1172 
  1173 
  1174 /**
  1175 Start the timer.
  1176 
  1177 @see RTimer
  1178 
  1179 @publishedAll
  1180 @released
  1181 */
  1182 void CFsDeltaTimer::Start(TThreadTimer& aEntry, TTimeIntervalMicroSeconds32 aTime)
  1183 	{
  1184 	iLock.Wait();
  1185 
  1186 	// must be already running on this thread or not running at all
  1187 	ASSERT(aEntry.iRequestThread == NULL || aEntry.iRequestThread  == &iRequestThread);
  1188 
  1189 	// attach the entry to this thread
  1190 	aEntry.iRequestThread = &iRequestThread;
  1191 
  1192 	// Remove the entry from the list (if it's already queued) 
  1193 	// and then add it again in the correct order
  1194 	aEntry.iLink.Deque();
  1195 	QueueLong(TTimeIntervalMicroSeconds(MAKE_TINT64(0, aTime.Int())), aEntry);
  1196 
  1197 	iLock.Signal();
  1198 	}
  1199 
  1200 void CFsDeltaTimer::Stop(TThreadTimer& aEntry)
  1201 	{
  1202 	iLock.Wait();
  1203 
  1204 	aEntry.iLink.Deque();
  1205 	aEntry.iRequestThread = NULL;
  1206 
  1207 	iLock.Signal();
  1208 	}
  1209 
  1210 
  1211 TInt CFsDeltaTimer::QueueLong(TTimeIntervalMicroSeconds aTimeInMicroSeconds, TThreadTimer& aEntry)
  1212 	{
  1213 	const TInt64 timeInTicks = (aTimeInMicroSeconds.Int64() + iTickPeriod - 1) / iTickPeriod;
  1214 
  1215 	TInt timeInTicks32 = I64LOW(timeInTicks);
  1216 
  1217 	// We are using deltas on tick values, hence using maximum signed number of ticks
  1218 	if (I64HIGH(timeInTicks) || (timeInTicks32 < 0))
  1219 		{
  1220 		return KErrOverflow;
  1221 		}
  1222 
  1223 	// Make sure we queue for at least one tick
  1224 	if (timeInTicks32 == 0)
  1225 		{
  1226 		timeInTicks32 = 1;
  1227 		}
  1228 	
  1229 	// Calculate tick count for new entry
  1230 	aEntry.iLink.iTickCount = User::TickCount() + timeInTicks32;
  1231 
  1232 	// Add this entry at the right spot
  1233 	iQueue.Add(aEntry.iLink);
  1234 
  1235 	// we only need to re-start the timer if we've added an entry to the head of the queue
  1236 	// or the timer is not already running
  1237 	if (&aEntry.iLink == iQueue.First() || iStatus == KRequestPending)
  1238 		Activate();
  1239 	
  1240 	return KErrNone;
  1241 	}
  1242 
  1243 void CFsDeltaTimer::Activate()
  1244 //
  1245 // Queue a request on the timer.
  1246 //
  1247 	{
  1248 	if (RThread().Id() != iThreadId)
  1249 		{
  1250 		iRestartNeeded = ETrue;
  1251 		iRequestThread.iThread.RequestSignal();
  1252 		return;
  1253 		}
  1254 
  1255 	if (iStatus == KRequestPending)
  1256 		Cancel();
  1257 
  1258 	if (!iQueue.IsEmpty() && !iQueueBusy)
  1259 		{
  1260 		const TInt ticksToWait = iQueue.First()->iTickCount - User::TickCount();
  1261 
  1262 		if (ticksToWait > 0)
  1263 			{
  1264 			iTimer.AfterTicks(iStatus, ticksToWait);
  1265 			}
  1266 		else
  1267 			{
  1268 			TRequestStatus* status = &iStatus;
  1269 			User::RequestComplete(status, KErrNone);
  1270 			}
  1271 		}
  1272 	}
  1273 
  1274 
  1275 
  1276 void CFsDeltaTimer::RunL()
  1277 //
  1278 // Call all zero delta callbacks
  1279 	{
  1280 	// if still running and no restart needed, then there's nothing to do
  1281 	if (iStatus == KRequestPending && !iRestartNeeded)
  1282 		return;
  1283 
  1284 
  1285 	iLock.Wait();
  1286 
  1287 	// Queue busy
  1288 	iQueueBusy = ETrue;
  1289 
  1290 	// Whilst the list of expired timers is being processed, time will pass and
  1291 	// the tick count may have increased such that there are now more expired
  1292 	// timers.  Loop until we have either emptied the queue or can wait for a
  1293 	// timer exipration in the future.
  1294 	if (iStatus == KErrNone)
  1295 		{
  1296 		iStatus = KErrNotReady;
  1297 		while (!iQueue.IsEmpty())
  1298 			{
  1299 			// Calculate how long till first timer expires
  1300 			const TUint tickCount = User::TickCount();
  1301 
  1302 			// If the first timer is yet to expire, wait some more
  1303 			if (((TInt)(iQueue.First()->iTickCount - tickCount)) > 0)
  1304 				{
  1305 				break;
  1306 				}
  1307 
  1308 			// Remove entry before callback to prevent re-entrancy issues
  1309 			TTickCountQueLink* entry = iQueue.RemoveFirst();
  1310 
  1311 			// Iterate through the timers we know have expired based on the
  1312 			// last calculation of delta
  1313 			while (entry)
  1314 				{
  1315 				TThreadTimer* threadTimer = reinterpret_cast<TThreadTimer*>(PtrSub(entry, _FOFF(TThreadTimer, iLink)));
  1316 				threadTimer->iRequestThread = NULL;	// indicate timer not running
  1317 
  1318 				// Make callback.  This could go reentrant on Queue[Long]() or Remove().
  1319 				iLock.Signal();
  1320 				threadTimer->iCallBack.CallBack();
  1321 				iLock.Wait();
  1322 
  1323 				// Remove the next expired entry, if any
  1324 				entry = iQueue.RemoveFirst(tickCount);
  1325 				}
  1326 			}
  1327 		}
  1328 
  1329 	// Queue idle
  1330 	iQueueBusy = EFalse;
  1331 
  1332 
  1333 	// Requeue timer if queue isn't empty
  1334 	Activate();
  1335 
  1336 	iRestartNeeded = EFalse;
  1337 
  1338 	iLock.Signal();
  1339 	}
  1340 	
  1341 void CFsDeltaTimer::Cancel()
  1342 	{
  1343 	if (iStatus == KRequestPending)
  1344 		{
  1345 		iTimer.Cancel();
  1346 		User::WaitForRequest(iStatus);
  1347 		}
  1348 	}
  1349 
  1350 
  1351 
  1352 TThreadTimer::TThreadTimer(TInt (*aCallBackFunction)(TAny*),TAny* aPtr) :
  1353 	iCallBack(aCallBackFunction, aPtr),
  1354 	iRequestThread(NULL)
  1355 	{
  1356 	};	
  1357 
  1358 
  1359 void TThreadTimer::Start(CRequestThread* aRequestThread, TTimeIntervalMicroSeconds32 aTime)
  1360 	{
  1361 	ASSERT(aRequestThread);
  1362 
  1363 	// NB: There are no locks here, so we have to be aware that CFsDeltaTimer::RunL()
  1364 	// may be running in another thread and set iRequestThread to NULL
  1365 	CRequestThread* requestThread = iRequestThread;
  1366 	if (!requestThread)	// if not already running, use caller's request thread
  1367 		requestThread = aRequestThread;
  1368 
  1369 
  1370 	__ASSERT_DEBUG(requestThread->Timer(),Fault(ERequestThreadNotInitialised));
  1371 	requestThread->Timer()->Start(*this, aTime);
  1372 	}
  1373 
  1374 
  1375 void TThreadTimer::Stop()
  1376 	{
  1377 	// NB: There are no locks here, so we have to be aware that CFsDeltaTimer::RunL()
  1378 	// may be running in another thread and set iRequestThread to NULL
  1379 	CRequestThread* requestThread = iRequestThread;
  1380 	if (requestThread)
  1381 		requestThread->Timer()->Stop(*this);
  1382 	}
  1383 
  1384