sl@0: // Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of the License "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // f32\sfile\sf_thread.cpp sl@0: // sl@0: // sl@0: sl@0: #include "sf_std.h" sl@0: #include sl@0: #include "sf_file_cache.h" sl@0: sl@0: #define __CHECK_DRVNUM(d) {__ASSERT_DEBUG(d>=EDriveA && d<=EDriveZ,Fault(EFsThreadBadDrvNum));} sl@0: sl@0: #ifdef __X86__ sl@0: const TInt KRequestThreadStackSize = 0x4000; sl@0: #else sl@0: const TInt KRequestThreadStackSize = 0x3000; sl@0: #endif sl@0: sl@0: const TInt KFinaliseTimerPeriod = 10 * 1000 * 1000; // default 10S finalisation timeout sl@0: sl@0: TFsDriveThread FsThreadManager::iFsThreads[KMaxDrives]; sl@0: TUint FsThreadManager::iMainId=0; sl@0: CDisconnectThread* FsThreadManager::iDisconnectThread=NULL; sl@0: TUint FsThreadManager::iDisconnectThreadId=0; sl@0: sl@0: TFsDriveThread::TFsDriveThread() sl@0: // sl@0: // sl@0: // sl@0: :iIsAvailable(EFalse),iIsSync(EFalse),iThread(NULL),iId(0),iIsHung(EFalse),iMediaChangePending(EFalse) sl@0: { sl@0: TInt r=iFSLock.CreateLocal(); sl@0: __ASSERT_ALWAYS(r==KErrNone,Fault(EFsThreadConstructor)); sl@0: } sl@0: sl@0: TFsPluginThread::TFsPluginThread() sl@0: // sl@0: // sl@0: // sl@0: :iIsAvailable(ETrue),iThread(NULL),iId(0) sl@0: { sl@0: TInt r=iPluginLock.CreateLocal(); sl@0: iDriveNumber= KMaxDrives+1; sl@0: __ASSERT_ALWAYS(r==KErrNone,Fault(EFsThreadConstructor)); sl@0: } sl@0: sl@0: TInt FsThreadManager::CreateDisconnectThread() sl@0: // sl@0: // Called just once at startup sl@0: // sl@0: { sl@0: __PRINT(_L("Create disconnect thread")); sl@0: TRAPD(r,iDisconnectThread=CDisconnectThread::NewL()); sl@0: if(r!=KErrNone) sl@0: return(r); sl@0: TRAP(r,iDisconnectThreadId=iDisconnectThread->StartL()); sl@0: if(r!=KErrNone) sl@0: { sl@0: delete(iDisconnectThread); sl@0: iDisconnectThread=NULL; sl@0: iDisconnectThreadId=0; sl@0: } sl@0: __THRD_PRINT2(_L("iDisconnectThread=0x%x id=0x%x"),iDisconnectThread,iDisconnectThreadId); sl@0: return(r); sl@0: } sl@0: sl@0: TBool FsThreadManager::IsDisconnectThread() sl@0: // sl@0: // Return ETrue if the calling thread is the disconnect thread sl@0: // sl@0: { sl@0: return(iDisconnectThreadId==RThread().Id()); sl@0: } sl@0: sl@0: sl@0: TInt FsThreadManager::InitDrive(TInt aDrvNumber,TBool aIsSync) sl@0: // sl@0: // Create a drive thread sl@0: // Should only by called from main file server thread with drive thread unavailable sl@0: // sl@0: // sl@0: { sl@0: __PRINT1(_L("FsThreadManager::InitDrive() drive=%d"),aDrvNumber); sl@0: TFsDriveThread& t=GetFsDriveThread(aDrvNumber); sl@0: __ASSERT_ALWAYS(!t.iIsAvailable,Fault(EThreadManagerInitDrive)); sl@0: t.iIsSync=ETrue; sl@0: sl@0: return ChangeSync(aDrvNumber, aIsSync); sl@0: } sl@0: sl@0: sl@0: TInt FsThreadManager::ChangeSync(TInt aDrvNumber,TBool aIsSync) sl@0: // sl@0: // Change if a drive is syncronouse after it has been inishalised. sl@0: // Should be called from the main thread. sl@0: // Any pending oporations will be compleated. sl@0: // sl@0: sl@0: { sl@0: __PRINT1(_L("FsThreadManager::ChangeSync() drive=%d"),aDrvNumber); sl@0: __CHECK_DRVNUM(aDrvNumber); sl@0: __CHECK_MAINTHREAD(); sl@0: sl@0: LockDrive(aDrvNumber); sl@0: TFsDriveThread& t=FsThreadManager::GetFsDriveThread(aDrvNumber); sl@0: TInt r=KErrNone; sl@0: sl@0: if (aIsSync!=t.iIsSync) sl@0: { sl@0: if (!aIsSync) sl@0: { sl@0: if(!t.iThread) sl@0: { sl@0: TRAP(r,t.iThread=CDriveThread::NewL()); sl@0: if(r!=KErrNone) sl@0: { sl@0: UnlockDrive(aDrvNumber); sl@0: return(r); sl@0: } sl@0: } sl@0: TRAP(r,t.iId=t.iThread->StartL(aDrvNumber)); sl@0: __THRD_PRINT2(_L("Starting thread 0x%x returned %d"),&t,r); sl@0: if(r!=KErrNone) sl@0: aIsSync=ETrue; sl@0: else sl@0: { sl@0: t.iIsSync=EFalse; sl@0: __THRD_PRINT1(_L("drive thread id=0x%x"),t.iId); sl@0: } sl@0: } sl@0: if (aIsSync) sl@0: { sl@0: if (t.iThread) sl@0: { sl@0: t.iThread->CompleteAllRequests(KErrNotReady); sl@0: t.iThread->iExit=ETrue; sl@0: t.iThread=NULL; sl@0: } sl@0: t.iIsSync=ETrue; sl@0: } sl@0: } sl@0: if (r==KErrNone) sl@0: t.iIsAvailable=ETrue; sl@0: sl@0: UnlockDrive(aDrvNumber); sl@0: return r; sl@0: } sl@0: sl@0: void FsThreadManager::CloseDrive(TInt aDrvNumber) sl@0: // sl@0: // Close a drive thread sl@0: // Assumes already locked or safe sl@0: // If file system in not synchronous then should be called from a drive thread request sl@0: // sl@0: { sl@0: __PRINT1(_L("FsThreadManager::CloseDrive() drive=%d"),aDrvNumber); sl@0: __CHECK_DRVNUM(aDrvNumber); sl@0: sl@0: // no need to cancel requests if synchronous since queued sl@0: if(!FsThreadManager::IsDriveSync(aDrvNumber,EFalse)) sl@0: { sl@0: CDriveThread* pT=NULL; sl@0: TInt r=FsThreadManager::GetDriveThread(aDrvNumber,&pT); sl@0: __ASSERT_ALWAYS(r==KErrNone && pT,Fault(EDismountFsDriveThread)); sl@0: pT->CompleteAllRequests(KErrNotReady); sl@0: } sl@0: sl@0: TFsDriveThread& t=GetFsDriveThread(aDrvNumber); sl@0: __ASSERT_ALWAYS(t.iIsAvailable,Fault(EFsThreadDriveClose1)); sl@0: if(!t.iIsSync) sl@0: { sl@0: __ASSERT_ALWAYS(FsThreadManager::IsDriveThread(aDrvNumber,EFalse),Fault(EFsThreadDriveClose2)); sl@0: sl@0: StopFinalisationTimer(aDrvNumber); sl@0: sl@0: // drive thread will exit when request completed sl@0: t.iThread->iExit=ETrue; sl@0: sl@0: // Ensure that subsequent remounts use a new thread AND a new CDriveThread object - sl@0: // re-use of the CDriveThread object can lead to deadlock while both old & new threads are active. sl@0: t.iThread = NULL; sl@0: sl@0: // Empty the closed file queue for this drive before the thread ends and the CDriveThread object sl@0: // is deleted because the closed file queue contains a list of CFileCache objects which will sl@0: // call CRequestThread::RemoveTimer() when closed sl@0: TClosedFileUtils::Remove(aDrvNumber); sl@0: } sl@0: else sl@0: { sl@0: __CHECK_MAINTHREAD(); sl@0: t.iIsSync=EFalse; sl@0: } sl@0: t.iIsAvailable=EFalse; sl@0: t.iId=0; sl@0: } sl@0: sl@0: sl@0: TBool FsThreadManager::IsDriveAvailable(TInt aDrvNumber,TBool aIsLock) sl@0: // sl@0: // sl@0: // sl@0: { sl@0: __CHECK_DRVNUM(aDrvNumber); sl@0: TFsDriveThread& t=GetFsDriveThread(aDrvNumber); sl@0: if(aIsLock) sl@0: t.iFSLock.Wait(); sl@0: TBool b=t.iIsAvailable; sl@0: if(aIsLock) sl@0: t.iFSLock.Signal(); sl@0: __THRD_PRINT2(_L("drive thread %d iIsAvailable=%d"),aDrvNumber,b); sl@0: return(b); sl@0: } sl@0: sl@0: TBool FsThreadManager::IsDriveSync(TInt aDrvNumber,TBool aLock) sl@0: // sl@0: // sl@0: // sl@0: { sl@0: __CHECK_DRVNUM(aDrvNumber); sl@0: TFsDriveThread& t=GetFsDriveThread(aDrvNumber); sl@0: if(aLock) sl@0: t.iFSLock.Wait(); sl@0: TBool b=(t.iIsAvailable&&t.iIsSync); sl@0: if(aLock) sl@0: t.iFSLock.Signal(); sl@0: __THRD_PRINT2(_L("drive thread %d iIsSync=%d"),aDrvNumber,b); sl@0: return(b); sl@0: } sl@0: sl@0: TInt FsThreadManager::GetDriveThread(TInt aDrvNumber, CDriveThread** aDrvThread) sl@0: // sl@0: // Assumes locked or called from the drive thread sl@0: // sl@0: { sl@0: __CHECK_DRVNUM(aDrvNumber); sl@0: TFsDriveThread& t=GetFsDriveThread(aDrvNumber); sl@0: *aDrvThread=NULL; sl@0: TInt r=KErrNone; sl@0: if(!t.iIsAvailable) sl@0: r=KErrNotReady; sl@0: else if(t.iIsSync) sl@0: r=KErrAccessDenied; sl@0: else sl@0: { sl@0: *aDrvThread=t.iThread; sl@0: __ASSERT_DEBUG(*aDrvThread,Fault(EFsThreadGetThread)); sl@0: } sl@0: __THRD_PRINT4(_L("GetDriveThread(%d) r %d id=0x%x *aDrvThread=0x%x"),aDrvNumber, r, t.iId, *aDrvThread); sl@0: return r; sl@0: } sl@0: sl@0: sl@0: void FsThreadManager::LockDrive(TInt aDrvNumber) sl@0: // sl@0: // Lock the TFsDriveThread object for the aDrvNumber drive sl@0: // sl@0: { sl@0: __CHECK_DRVNUM(aDrvNumber); sl@0: __THRD_PRINT1(_L("FsThreadManager::LockDrive(%d)"),aDrvNumber); sl@0: TFsDriveThread& t=GetFsDriveThread(aDrvNumber); sl@0: t.iFSLock.Wait(); sl@0: } sl@0: sl@0: void FsThreadManager::UnlockDrive(TInt aDrvNumber) sl@0: // sl@0: // Unlock the TFsDriveThread object for the aDrvNumber drive sl@0: // sl@0: { sl@0: __CHECK_DRVNUM(aDrvNumber); sl@0: __THRD_PRINT1(_L("FsThreadManager::UnlockDrive(%d)"),aDrvNumber); sl@0: TFsDriveThread& t=GetFsDriveThread(aDrvNumber); sl@0: t.iFSLock.Signal(); sl@0: } sl@0: sl@0: void FsThreadManager::SetDriveHung(TInt aDrvNumber, TBool aIsHung) sl@0: { sl@0: if (aDrvNumber < EDriveA || aDrvNumber > EDriveZ) sl@0: return; sl@0: sl@0: TFsDriveThread& t=GetFsDriveThread(aDrvNumber); sl@0: sl@0: // quick exit if hung state not changing or drive thread not available sl@0: if ((!t.iIsAvailable) || (t.iIsHung == aIsHung)) sl@0: return; sl@0: sl@0: t.iFSLock.Wait(); sl@0: sl@0: // Don't clear the hung state if this is a synchronous request sl@0: // and the drive is asynchronous - we need to wait for whatever sl@0: // asynchronous request caused the hang to complete first. sl@0: TUint id=RThread().Id(); sl@0: TBool isDriveThread = t.iIsSync || (!t.iIsSync && t.iId == id); sl@0: __THRD_PRINT3(_L("Set %d Hung %d. Is Drive thread %d"), aDrvNumber, aIsHung, isDriveThread); sl@0: if (!aIsHung && !isDriveThread) sl@0: { sl@0: t.iFSLock.Signal(); sl@0: return; sl@0: } sl@0: sl@0: t.iIsHung = aIsHung; sl@0: sl@0: // if we're no longer hung, see if there's a media change pending sl@0: // and if so issue one now sl@0: TBool mediaChangePending = EFalse; sl@0: if(!aIsHung) sl@0: { sl@0: mediaChangePending = t.iMediaChangePending; sl@0: t.iMediaChangePending = EFalse; sl@0: } sl@0: t.iFSLock.Signal(); sl@0: sl@0: // If the drive is now hung we must complete all requests in the drive thread's sl@0: // queue - and all subsequent requests - with KErrNotReady to prevent deadlock. sl@0: // For example, the notifier server may try to access the loader but one of the sl@0: // requests in the queue may already belong to the loader. sl@0: if (aIsHung && t.iThread) sl@0: t.iThread->CompleteClientRequests(KErrNotReady); sl@0: sl@0: if(mediaChangePending) sl@0: FsNotify::DiskChange(aDrvNumber); sl@0: } sl@0: sl@0: sl@0: TBool FsThreadManager::IsDriveHung(TInt aDrvNumber) sl@0: { sl@0: if (aDrvNumber < EDriveA || aDrvNumber > EDriveZ) sl@0: return EFalse; sl@0: sl@0: TFsDriveThread& t=GetFsDriveThread(aDrvNumber); sl@0: // __THRD_PRINT3(_L("Is %d Hung = %d"), aDrvNumber, t.iIsHung); sl@0: return t.iIsHung; sl@0: } sl@0: sl@0: sl@0: // If the drive is hung, then don't complete any disk change sl@0: // notifications until the request causing the hang completes. sl@0: void FsThreadManager::SetMediaChangePending(TInt aDrvNumber) sl@0: { sl@0: if (aDrvNumber < EDriveA || aDrvNumber > EDriveZ) sl@0: return; sl@0: sl@0: TFsDriveThread& t=GetFsDriveThread(aDrvNumber); sl@0: sl@0: if (!t.iIsAvailable) sl@0: return; sl@0: sl@0: t.iFSLock.Wait(); sl@0: t.iMediaChangePending = ETrue; sl@0: t.iFSLock.Signal(); sl@0: } sl@0: sl@0: void FsThreadManager::SetMainThreadId() sl@0: // sl@0: // called at file server startup, assumes called from main file server thread sl@0: // sl@0: { sl@0: iMainId=RThread().Id(); sl@0: __THRD_PRINT1(_L("Main thread id = 0x%x"),iMainId); sl@0: } sl@0: sl@0: TBool FsThreadManager::IsDriveThread(TInt aDrvNumber,TBool aIsLock) sl@0: // sl@0: // Return ETrue if the calling thread is the aDrvNumber drive thread sl@0: // sl@0: { sl@0: __CHECK_DRVNUM(aDrvNumber); sl@0: TFsDriveThread& t=GetFsDriveThread(aDrvNumber); sl@0: TUint id=RThread().Id(); sl@0: if(aIsLock) sl@0: t.iFSLock.Wait(); sl@0: TBool b = t.iIsAvailable && (!t.iIsSync && t.iId==id || t.iIsSync); sl@0: if(aIsLock) sl@0: t.iFSLock.Signal(); sl@0: return(b); sl@0: } sl@0: sl@0: TBool FsThreadManager::IsMainThread() sl@0: // sl@0: // Returns ETrue if calling thread is same as main file server thread sl@0: // sl@0: { sl@0: return((TUint)(RThread().Id())==iMainId); sl@0: } sl@0: sl@0: sl@0: void FsThreadManager::StartFinalisationTimer(TInt aDrvNumber) sl@0: { sl@0: if (aDrvNumber < EDriveA || aDrvNumber > EDriveZ) sl@0: return; sl@0: sl@0: // If the message could cause disk modification, make sure that the finalisation sl@0: // timer is queued so that we can mark the disk consistent at some point in the future sl@0: CDriveThread* driveThread=NULL; sl@0: TInt r = GetDriveThread(aDrvNumber, &driveThread); sl@0: if(r == KErrNone && driveThread != NULL) sl@0: driveThread->StartFinalisationTimer(); sl@0: } sl@0: sl@0: void FsThreadManager::StopFinalisationTimer(TInt aDrvNumber) sl@0: { sl@0: if (aDrvNumber < EDriveA || aDrvNumber > EDriveZ) sl@0: return; sl@0: sl@0: // If the message could cause disk modification, make sure that the finalisation sl@0: // timer is queued so that we can mark the disk consistent at some point in the future sl@0: CDriveThread* dT=NULL; sl@0: TInt r = GetDriveThread(aDrvNumber, &dT); sl@0: if(r == KErrNone && dT != NULL) sl@0: { sl@0: dT->StopFinalisationTimer(); sl@0: } sl@0: } sl@0: sl@0: CRequestThread::CRequestThread() sl@0: // sl@0: // sl@0: // sl@0: :iList(_FOFF(CFsRequest,iLink)) sl@0: { sl@0: //iRequest=NULL; sl@0: //iIsWaiting=EFalse; sl@0: iExit=EFalse; sl@0: } sl@0: sl@0: TInt CRequestThread::Initialise() sl@0: // sl@0: // Initialise sl@0: // sl@0: { sl@0: TInt r=iListLock.CreateLocal(); sl@0: return(r); sl@0: } sl@0: sl@0: CRequestThread::~CRequestThread() sl@0: // sl@0: // sl@0: // sl@0: { sl@0: __ASSERT_ALWAYS(iList.IsEmpty(),Fault(ERequestThreadDestructor)); sl@0: iListLock.Close(); sl@0: sl@0: if(iThread.Handle() != 0) sl@0: { sl@0: iThread.Close(); sl@0: } sl@0: delete iTimer; sl@0: } sl@0: sl@0: LOCAL_C TInt ThreadFunction(TAny* aPtr) sl@0: // sl@0: // sl@0: // sl@0: { sl@0: __THRD_PRINT(_L("ThreadFunction()")); sl@0: User::SetCritical(User::ESystemCritical); sl@0: CRequestThread* pT=(CRequestThread*)aPtr; sl@0: TInt r = pT->ThreadFunction(); sl@0: delete pT; sl@0: return r; sl@0: } sl@0: sl@0: void CRequestThread::CompleteAllRequests(TInt aValue) sl@0: { sl@0: __THRD_PRINT(_L("CRequestThread::CompleteAllRequests()")); sl@0: iListLock.Wait(); sl@0: while(!iList.IsEmpty()) sl@0: { sl@0: CFsRequest* pR=iList.First(); sl@0: pR->iLink.Deque(); sl@0: iListLock.Signal(); sl@0: pR->Complete(aValue); sl@0: iListLock.Wait(); sl@0: } sl@0: iListLock.Signal(); sl@0: __THRD_PRINT(_L("all requests completed")); sl@0: } sl@0: sl@0: TInt CRequestThread::ThreadFunction() sl@0: // sl@0: // entry point for the thread sl@0: // sl@0: { sl@0: iTimer = CFsDeltaTimer::New(*this, EPriorityLess); sl@0: if (iTimer == NULL) sl@0: { sl@0: RThread::Rendezvous(KErrNoMemory); sl@0: return(KErrNone); sl@0: } sl@0: iTimer->iStatus = KErrNotReady; sl@0: sl@0: CTrapCleanup* trapHandler=CTrapCleanup::New(); sl@0: if (trapHandler==NULL) sl@0: { sl@0: RThread::Rendezvous(KErrNoMemory); sl@0: delete iTimer; sl@0: return(KErrNone); sl@0: } sl@0: sl@0: RThread::Rendezvous(KErrNone); sl@0: sl@0: TInt err = DoThreadInitialise(); sl@0: if(err != KErrNone) sl@0: { sl@0: delete trapHandler; sl@0: return(KErrNone); sl@0: } sl@0: sl@0: iExit=EFalse; sl@0: iIsWaiting=EFalse; sl@0: // start receiving sl@0: Receive(); sl@0: CompleteAllRequests(KErrNotReady); sl@0: sl@0: delete trapHandler; sl@0: return(KErrNone); sl@0: } sl@0: sl@0: TInt CRequestThread::DoThreadInitialise() sl@0: { sl@0: return KErrNone; sl@0: } sl@0: sl@0: TInt CRequestThread::DoStart(RThread& aThread) sl@0: // sl@0: // create thread and return handle sl@0: // necessary for client to close thread handle if successful sl@0: // sl@0: { sl@0: TInt r=aThread.Create(KNullDesC,::ThreadFunction,KRequestThreadStackSize,NULL,(TAny*)this); sl@0: __PRINT1(_L("CRequestThread::DoStart() r=%d"),r); sl@0: if(r!=KErrNone) sl@0: return(r); sl@0: TRequestStatus status; sl@0: aThread.Rendezvous(status); sl@0: if(status==KRequestPending) sl@0: { sl@0: aThread.SetPriority(EPriorityLess); sl@0: aThread.Resume(); sl@0: } sl@0: else sl@0: { sl@0: aThread.Kill(0); sl@0: } sl@0: User::WaitForRequest(status); sl@0: r = status.Int(); sl@0: if(r!=KErrNone) sl@0: aThread.Close(); sl@0: else sl@0: iThread = aThread; sl@0: sl@0: return(r); sl@0: } sl@0: sl@0: sl@0: void CRequestThread::Receive() sl@0: // sl@0: // Receive and process requests sl@0: // sl@0: { sl@0: FOREVER sl@0: { sl@0: iListLock.Wait(); sl@0: if(!iList.IsEmpty()) sl@0: { sl@0: iRequest=iList.First(); sl@0: iRequest->iLink.Deque(); sl@0: __THRD_PRINT(_L("CRequestThread::Receive() dequeing")); sl@0: iListLock.Signal(); sl@0: } sl@0: else sl@0: { sl@0: iIsWaiting=ETrue; sl@0: iRequest = NULL; // set to NULL so we can distinguish between a timer and a request signal sl@0: iListLock.Signal(); sl@0: __THRD_PRINT(_L("CRequestThread::Receive() waiting")); sl@0: User::WaitForAnyRequest(); sl@0: iIsWaiting=EFalse; // force main thread to post new requests on queue to avoid suspending this thread unnecessarily sl@0: } sl@0: __THRD_PRINT2(_L("received req 0x%x, func 0x%x"),iRequest, iRequest ? iRequest->Operation()->iFunction : -1); sl@0: sl@0: iTimer->RunL(); sl@0: sl@0: if (iRequest) sl@0: iRequest->Process(); sl@0: sl@0: if(iExit) sl@0: { sl@0: //Any requests that sneaked on to sl@0: //the queue are cancelled in sl@0: //CRequestThread::ThreadFunction() sl@0: break; sl@0: } sl@0: } sl@0: } sl@0: sl@0: void CRequestThread::Deliver(CFsRequest* aRequest,TBool aIsFront, TBool aLowPriority) sl@0: // sl@0: // Deliver a request to the list from calling thread sl@0: // Write request directly to current request if thread is waiting sl@0: // sl@0: { sl@0: __THRD_PRINT4(_L("Deliver req %08x to threadId %lx aIsFront=%d iIsWaiting=%d"), aRequest, iThread.Id().Id(), aIsFront, iIsWaiting); sl@0: iListLock.Wait(); sl@0: if (iList.IsEmpty()) sl@0: { sl@0: // if this is a low priority request (and this is the only request in the queue), sl@0: // reduce the thread's priority to EPriorityAbsoluteBackground sl@0: if (iLowPriority != aLowPriority) sl@0: { sl@0: __THRD_PRINT(_L("LOWERING THREAD PRIORITY")); sl@0: iThread.SetPriority(aLowPriority?EPriorityAbsoluteBackground:EPriorityLess); sl@0: iLowPriority = aLowPriority; sl@0: } sl@0: } sl@0: else sl@0: { sl@0: // there's more than one request in the queue, so rather than go throught the entire queue sl@0: // to determine what the thread's priority should be, assume that it should be "high" sl@0: if (iLowPriority) sl@0: { sl@0: iThread.SetPriority(EPriorityLess); sl@0: iLowPriority = EFalse; sl@0: } sl@0: } sl@0: sl@0: if(iIsWaiting) sl@0: { sl@0: // the request thread must be waiting on the iWaitLock sl@0: iIsWaiting=EFalse; sl@0: iListLock.Signal(); sl@0: iRequest=aRequest; sl@0: sl@0: iThread.RequestSignal(); sl@0: } sl@0: else sl@0: { sl@0: if(aIsFront) sl@0: iList.AddFirst(*aRequest); sl@0: else sl@0: iList.AddLast(*aRequest); sl@0: iListLock.Signal(); sl@0: } sl@0: } sl@0: sl@0: void CRequestThread::DeliverFront(CFsRequest* aRequest) sl@0: // sl@0: // sl@0: // sl@0: { sl@0: Deliver(aRequest,ETrue); sl@0: } sl@0: sl@0: void CRequestThread::DeliverBack(CFsRequest* aRequest, TBool aLowPriority) sl@0: // sl@0: // sl@0: // sl@0: { sl@0: Deliver(aRequest,EFalse,aLowPriority); sl@0: } sl@0: sl@0: sl@0: sl@0: CFsDeltaTimer* CRequestThread::Timer() sl@0: { sl@0: __ASSERT_ALWAYS(iTimer,Fault(ERequestThreadNotInitialised)); sl@0: return iTimer; sl@0: } sl@0: sl@0: sl@0: CDriveThread::CDriveThread() sl@0: : iFinaliseTimer(FinaliseTimerEvent, this) sl@0: { sl@0: } sl@0: sl@0: CDriveThread* CDriveThread::NewL() sl@0: // sl@0: // sl@0: // sl@0: { sl@0: __PRINT(_L("CDriveThread::NewL()")); sl@0: CDriveThread* pT=new(ELeave) CDriveThread; sl@0: TInt r=pT->Initialise(); sl@0: if(r!=KErrNone) sl@0: { sl@0: delete(pT); sl@0: User::Leave(r); sl@0: } sl@0: return(pT); sl@0: } sl@0: sl@0: TUint CDriveThread::StartL(TInt aDrvNumber) sl@0: // sl@0: // sl@0: // sl@0: { sl@0: __PRINT1(_L("CDriveThread::StartL() on drive %d"),aDrvNumber); sl@0: iDriveNumber=aDrvNumber; sl@0: RThread t; sl@0: User::LeaveIfError(DoStart(t)); sl@0: TUint id=t.Id(); sl@0: return(id); sl@0: } sl@0: sl@0: TInt CDriveThread::DoThreadInitialise() sl@0: // sl@0: // Initialise function for the drive thread sl@0: // - Renames the thread to contain the drive number. sl@0: // - Note: Drive mappings are not available at this time, so we can't show the actual drive letter. sl@0: // sl@0: { sl@0: __PRINT1(_L("CDriveThread::DoThreadInitialise() on drive %d"), iDriveNumber); sl@0: sl@0: TBuf<16> name; sl@0: name.Format(_L("DriveThread_%02d"), iDriveNumber); sl@0: return(RThread::RenameMe(name)); sl@0: } sl@0: sl@0: void CDriveThread::CompleteSessionRequests(CSessionFs* aSession, TInt aValue) sl@0: // sl@0: // sl@0: // sl@0: { sl@0: __THRD_PRINT1(_L("CDriveThread::CompleteSessionReqeusts() drive=%d"),iDriveNumber); sl@0: iListLock.Wait(); sl@0: TDblQueIter q(iList); sl@0: CFsRequest* pR; sl@0: while((pR=q++)!=NULL) sl@0: { sl@0: if(pR->Session()==aSession) sl@0: { sl@0: pR->iLink.Deque(); sl@0: iListLock.Signal(); sl@0: pR->Complete(aValue); sl@0: iListLock.Wait(); sl@0: // set iterator back to head of queue in case Complete() has itself removed requests from the queue sl@0: q.SetToFirst(); sl@0: } sl@0: } sl@0: iListLock.Signal(); sl@0: __THRD_PRINT(_L("session requests completed")); sl@0: } sl@0: sl@0: sl@0: void CDriveThread::CompleteReadWriteRequests() sl@0: { sl@0: __THRD_PRINT1(_L("CDriveThread::CompleteReadWriteRequests() drive=%d"),iDriveNumber); sl@0: sl@0: iListLock.Wait(); sl@0: sl@0: TDblQueIter q(iList); sl@0: CFsRequest* pR; sl@0: while((pR=q++)!=NULL) sl@0: { sl@0: TInt func = pR->Operation()->Function(); sl@0: if (func == EFsFileRead || func == EFsFileWrite || func == EFsFileWriteDirty) sl@0: { sl@0: pR->iLink.Deque(); sl@0: pR->Complete(KErrNotReady); sl@0: } sl@0: } sl@0: iListLock.Signal(); sl@0: sl@0: __THRD_PRINT(_L("file read/write requests completed")); sl@0: } sl@0: sl@0: /* sl@0: This function is called by FsThreadManager::SetDriveHung() and attempts to purge the request queue sl@0: of all requests which MIGHT belong to the critical notifier server (or to the loader) to avoid causing sl@0: a deadlock when calling the server. sl@0: sl@0: All requests are completed with KErrNotReady apart from : sl@0: - EFsFileWriteDirty requests, to avoid losing dirty data sl@0: - KDispatchObjectClose requests as they are raised by the file server only and therefore cannot belong to the critical notifier server sl@0: - EFsFileSubClose requests, to avoid closing files containing dirty data. These requests have their message completed sl@0: so that clients are unblocked, but the request itself is not processed until later. (If the request WAS processed sl@0: and completed, then this might result in the CFileCB and CMountCB object being deleted, leading to problems sl@0: dereferencing invalid pointers). sl@0: */ sl@0: void CDriveThread::CompleteClientRequests(TInt aValue) sl@0: { sl@0: __THRD_PRINT1(_L("CDriveThread::CompleteClientRequests() drive=%d"),iDriveNumber); sl@0: sl@0: iListLock.Wait(); sl@0: sl@0: TDblQueIter q(iList); sl@0: CFsRequest* pR; sl@0: while((pR=q++)!=NULL) sl@0: { sl@0: TInt func = pR->Operation()->Function(); sl@0: if(func == EFsFileSubClose) sl@0: { sl@0: TInt msgHandle = pR->Message().Handle(); sl@0: if ((msgHandle != KLocalMessageHandle) && (msgHandle != 0)) sl@0: pR->Message().Complete(KErrNone); sl@0: } sl@0: else if (func != EFsFileWriteDirty && func != KDispatchObjectClose) sl@0: { sl@0: pR->iLink.Deque(); sl@0: iListLock.Signal(); sl@0: pR->Complete(aValue); sl@0: iListLock.Wait(); sl@0: } sl@0: } sl@0: iListLock.Signal(); sl@0: sl@0: __THRD_PRINT(_L("client read requests completed")); sl@0: } sl@0: sl@0: TBool CDriveThread::IsRequestWriteable() sl@0: // sl@0: // return if current request may cause write to disk sl@0: // must be called from drive thread sl@0: // sl@0: { sl@0: __ASSERT_ALWAYS(FsThreadManager::IsDriveThread(iDriveNumber,EFalse),Fault(EDriveThreadWriteable)); sl@0: return(iRequest->Operation()->IsWrite()); sl@0: } sl@0: sl@0: TBool CDriveThread::IsSessionNotifyUser() sl@0: // sl@0: // return if request's session has notify user set sl@0: // must be called from drive thread and request have a session set sl@0: // sl@0: { sl@0: __ASSERT_ALWAYS(FsThreadManager::IsDriveThread(iDriveNumber,EFalse),Fault(EDriveThreadNotifyUser1)); sl@0: // NB For read-ahead or a flush-dirty write request generated by the file cache, the session will be NULL: sl@0: // in this case assume that notify user is set (as it's the safest option) sl@0: CSessionFs* session = iRequest->Session(); sl@0: return session ? session->GetNotifyUser() : ETrue; sl@0: } sl@0: sl@0: void CDriveThread::StartFinalisationTimer() sl@0: { sl@0: if(IsProxyDrive(iDriveNumber)) sl@0: iFinaliseTimer.Start(this, KFinaliseTimerPeriod); sl@0: } sl@0: sl@0: sl@0: void CDriveThread::StopFinalisationTimer() sl@0: { sl@0: iFinaliseTimer.Stop(); sl@0: } sl@0: sl@0: TInt CDriveThread::FinaliseTimerEvent(TAny* aSelfP) sl@0: { sl@0: CDriveThread& self = *(CDriveThread*)aSelfP; sl@0: sl@0: TDrive& drive = TheDrives[self.iDriveNumber]; sl@0: if(drive.IsMounted()) sl@0: { sl@0: if (drive.CurrentMount().LockStatus() == 0) sl@0: { sl@0: // Ignore the error here, as there's nothing we can do about it... sl@0: (void)drive.FinaliseMount(RFs::EFinal_RW); sl@0: } sl@0: else sl@0: { sl@0: self.StartFinalisationTimer(); sl@0: } sl@0: } sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: sl@0: CDisconnectThread::~CDisconnectThread() sl@0: // sl@0: // sl@0: // sl@0: { sl@0: if(iRequest) sl@0: delete(iRequest); sl@0: } sl@0: sl@0: sl@0: CDisconnectThread* CDisconnectThread::NewL() sl@0: // sl@0: // sl@0: // sl@0: { sl@0: __THRD_PRINT(_L("CDisconnectThread::NewL()")); sl@0: CDisconnectThread* pT=new(ELeave) CDisconnectThread; sl@0: TInt r=pT->Initialise(); sl@0: if(r!=KErrNone) sl@0: { sl@0: delete(pT); sl@0: User::Leave(r); sl@0: } sl@0: return(pT); sl@0: } sl@0: sl@0: TUint CDisconnectThread::StartL() sl@0: // sl@0: // sl@0: // sl@0: { sl@0: __PRINT(_L("CDisconnectThread::StartL()")); sl@0: iRequest = new(ELeave) CFsInternalRequest; sl@0: __THRD_PRINT1(_L("internal request = 0x%x"),iRequest); sl@0: iRequest->Set(CancelSessionOp,NULL); sl@0: sl@0: RThread t; sl@0: TInt r=DoStart(t); sl@0: if(r!=KErrNone) sl@0: { sl@0: delete(iRequest); sl@0: iRequest=NULL; sl@0: User::Leave(r); sl@0: } sl@0: iRequest->SetThreadHandle(t.Handle()); sl@0: __THRD_PRINT1(_L("CDisconnect::StartL() handle=%d"),t.Handle()); sl@0: iRequest->SetAllocated(); sl@0: TUint id=t.Id(); sl@0: return(id); sl@0: } sl@0: sl@0: sl@0: CPluginThread::CPluginThread(CFsPlugin& aPlugin) sl@0: : iPlugin(aPlugin) sl@0: { sl@0: /** @prototype */ sl@0: iOperationLock.Close(); sl@0: iPlugin.Open(); sl@0: } sl@0: sl@0: CPluginThread::~CPluginThread() sl@0: { sl@0: iPlugin.Close(); sl@0: } sl@0: sl@0: sl@0: CPluginThread* CPluginThread::NewL(CFsPlugin& aPlugin) sl@0: { sl@0: __PRINT(_L("CPluginThread::NewL()")); sl@0: CPluginThread* pT=new(ELeave) CPluginThread(aPlugin); sl@0: TInt r=pT->Initialise(); sl@0: sl@0: /** @prototype */ sl@0: if(r == KErrNone) sl@0: r=pT->iOperationLock.CreateLocal(0); sl@0: sl@0: if(r!=KErrNone) sl@0: { sl@0: delete(pT); sl@0: User::Leave(r); sl@0: } sl@0: return(pT); sl@0: } sl@0: sl@0: TUint CPluginThread::StartL() sl@0: { sl@0: __PRINT(_L("CPluginThread::StartL()")); sl@0: RThread t; sl@0: User::LeaveIfError(DoStart(t)); sl@0: TUint id=t.Id(); sl@0: return(id); sl@0: } sl@0: sl@0: void CPluginThread::CompleteSessionRequests(CSessionFs* aSession, TInt aValue) sl@0: { sl@0: __THRD_PRINT(_L("CPluginThread::CompleteSessionRequests()")); sl@0: iListLock.Wait(); sl@0: TDblQueIter q(iList); sl@0: CFsRequest* pR; sl@0: while((pR=q++)!=NULL) sl@0: { sl@0: if(pR->Session()==aSession) sl@0: { sl@0: pR->iLink.Deque(); sl@0: pR->Complete(aValue); sl@0: } sl@0: } sl@0: iListLock.Signal(); sl@0: __THRD_PRINT(_L("session requests completed")); sl@0: } sl@0: sl@0: TInt CPluginThread::DoThreadInitialise() sl@0: { sl@0: __PRINT(_L("CPluginThread::DoThreadInitialise()")); sl@0: TRAPD(err, iPlugin.InitialiseL()); sl@0: sl@0: return err; sl@0: } sl@0: sl@0: /** @prototype */ sl@0: void CPluginThread::OperationLockWait() sl@0: { sl@0: iOperationLock.Wait(); sl@0: } sl@0: sl@0: /** @prototype */ sl@0: void CPluginThread::OperationLockSignal() sl@0: { sl@0: iOperationLock.Signal(); sl@0: } sl@0: sl@0: // Class TTickCountQue sl@0: /** sl@0: @internalComponent sl@0: @released sl@0: sl@0: Constructs an empty list header sl@0: */ sl@0: TTickCountQue::TTickCountQue() sl@0: {} sl@0: sl@0: sl@0: sl@0: sl@0: /** sl@0: @internalComponent sl@0: @released sl@0: sl@0: Adds the specified list element. sl@0: sl@0: The element is added into the list in order of its tick count. sl@0: sl@0: @param aRef The list element to be inserted. sl@0: */ sl@0: void TTickCountQue::Add(TTickCountQueLink& aRef) sl@0: { sl@0: TTickCountQueLink* currentLink = (TTickCountQueLink*)(iHead.iNext); sl@0: TTickCountQueLink* addLink = &aRef; sl@0: sl@0: while ( (currentLink != (TTickCountQueLink*)&iHead) && sl@0: (((TInt)(addLink->iTickCount - currentLink->iTickCount)) >= 0) sl@0: ) sl@0: { sl@0: currentLink = (TTickCountQueLink*)currentLink->iNext; sl@0: } sl@0: sl@0: addLink->Enque(currentLink->iPrev); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: /** sl@0: @internalComponent sl@0: @released sl@0: sl@0: Removes the first list element from the linked list if its tick count sl@0: is prior to the current tick count. sl@0: sl@0: @param aTickCount The current tick count. sl@0: sl@0: @return A pointer to the element removed from the linked list. This is NULL sl@0: if the first element has yet to expire or the queue is empty. sl@0: */ sl@0: TTickCountQueLink* TTickCountQue::RemoveFirst(TUint aTickCount) sl@0: { sl@0: TTickCountQueLink* firstLink = (TTickCountQueLink*)iHead.iNext; sl@0: sl@0: if (((TInt)(firstLink->iTickCount - aTickCount)) <= 0) sl@0: { sl@0: return RemoveFirst(); sl@0: } sl@0: else sl@0: { sl@0: return NULL; sl@0: } sl@0: } sl@0: sl@0: sl@0: /** sl@0: @internalComponent sl@0: @released sl@0: sl@0: Removes the first list element from the linked list, if any. sl@0: sl@0: @return A pointer to the element removed from the linked list. This is NULL, sl@0: if the queue is empty. sl@0: */ sl@0: TTickCountQueLink* TTickCountQue::RemoveFirst() sl@0: { sl@0: TTickCountQueLink* firstLink = (TTickCountQueLink*)iHead.iNext; sl@0: sl@0: if (firstLink != (TTickCountQueLink*)&iHead) sl@0: { sl@0: firstLink->Deque(); sl@0: return firstLink; sl@0: } sl@0: sl@0: return NULL; sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: /** sl@0: @internalComponent sl@0: @released sl@0: sl@0: Gets a pointer to the first list element in the doubly linked list. sl@0: sl@0: @return A pointer to the first list element in the doubly linked list. If sl@0: the list is empty, this pointer is not necessarily NULL and must not sl@0: be assumed to point to a valid object. sl@0: */ sl@0: TTickCountQueLink* TTickCountQue::First() const sl@0: { sl@0: #if defined (_DEBUG) sl@0: __DbgTestEmpty(); sl@0: #endif sl@0: return((TTickCountQueLink*)iHead.iNext); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: CFsDeltaTimer* CFsDeltaTimer::New(CRequestThread& aRequestThread, TInt aPriority) sl@0: { sl@0: TTimeIntervalMicroSeconds32 tickPeriod; sl@0: UserHal::TickPeriod(tickPeriod); sl@0: sl@0: CFsDeltaTimer* timer = new CFsDeltaTimer(aRequestThread, aPriority, tickPeriod.Int()); sl@0: if (timer == NULL) sl@0: return NULL; sl@0: sl@0: if (timer->iTimer.CreateLocal() != KErrNone || sl@0: timer->iLock.CreateLocal() != KErrNone) sl@0: { sl@0: delete timer; sl@0: return NULL; sl@0: } sl@0: sl@0: return timer; sl@0: } sl@0: sl@0: CFsDeltaTimer::CFsDeltaTimer(CRequestThread& aRequestThread, TInt /*aPriority*/, TInt aTickPeriod) : sl@0: iRequestThread(aRequestThread), iTickPeriod(aTickPeriod) sl@0: { sl@0: iThreadId = RThread().Id(); sl@0: } sl@0: sl@0: /** sl@0: Destructor. sl@0: sl@0: Frees all resources before destruction of the object. Specifically, it cancels sl@0: any outstanding timer requests generated by the RTimer object and then deletes sl@0: all timed event entries from the timed event queue. sl@0: sl@0: @see RTimer sl@0: sl@0: @publishedAll sl@0: @released sl@0: */ sl@0: CFsDeltaTimer::~CFsDeltaTimer() sl@0: { sl@0: Cancel(); sl@0: sl@0: while (!iQueue.IsEmpty()) sl@0: { sl@0: iQueue.First()->Deque(); sl@0: } sl@0: sl@0: iLock.Close(); sl@0: iTimer.Close(); sl@0: } sl@0: sl@0: sl@0: /** sl@0: Start the timer. sl@0: sl@0: @see RTimer sl@0: sl@0: @publishedAll sl@0: @released sl@0: */ sl@0: void CFsDeltaTimer::Start(TThreadTimer& aEntry, TTimeIntervalMicroSeconds32 aTime) sl@0: { sl@0: iLock.Wait(); sl@0: sl@0: // must be already running on this thread or not running at all sl@0: ASSERT(aEntry.iRequestThread == NULL || aEntry.iRequestThread == &iRequestThread); sl@0: sl@0: // attach the entry to this thread sl@0: aEntry.iRequestThread = &iRequestThread; sl@0: sl@0: // Remove the entry from the list (if it's already queued) sl@0: // and then add it again in the correct order sl@0: aEntry.iLink.Deque(); sl@0: QueueLong(TTimeIntervalMicroSeconds(MAKE_TINT64(0, aTime.Int())), aEntry); sl@0: sl@0: iLock.Signal(); sl@0: } sl@0: sl@0: void CFsDeltaTimer::Stop(TThreadTimer& aEntry) sl@0: { sl@0: iLock.Wait(); sl@0: sl@0: aEntry.iLink.Deque(); sl@0: aEntry.iRequestThread = NULL; sl@0: sl@0: iLock.Signal(); sl@0: } sl@0: sl@0: sl@0: TInt CFsDeltaTimer::QueueLong(TTimeIntervalMicroSeconds aTimeInMicroSeconds, TThreadTimer& aEntry) sl@0: { sl@0: const TInt64 timeInTicks = (aTimeInMicroSeconds.Int64() + iTickPeriod - 1) / iTickPeriod; sl@0: sl@0: TInt timeInTicks32 = I64LOW(timeInTicks); sl@0: sl@0: // We are using deltas on tick values, hence using maximum signed number of ticks sl@0: if (I64HIGH(timeInTicks) || (timeInTicks32 < 0)) sl@0: { sl@0: return KErrOverflow; sl@0: } sl@0: sl@0: // Make sure we queue for at least one tick sl@0: if (timeInTicks32 == 0) sl@0: { sl@0: timeInTicks32 = 1; sl@0: } sl@0: sl@0: // Calculate tick count for new entry sl@0: aEntry.iLink.iTickCount = User::TickCount() + timeInTicks32; sl@0: sl@0: // Add this entry at the right spot sl@0: iQueue.Add(aEntry.iLink); sl@0: sl@0: // we only need to re-start the timer if we've added an entry to the head of the queue sl@0: // or the timer is not already running sl@0: if (&aEntry.iLink == iQueue.First() || iStatus == KRequestPending) sl@0: Activate(); sl@0: sl@0: return KErrNone; sl@0: } sl@0: sl@0: void CFsDeltaTimer::Activate() sl@0: // sl@0: // Queue a request on the timer. sl@0: // sl@0: { sl@0: if (RThread().Id() != iThreadId) sl@0: { sl@0: iRestartNeeded = ETrue; sl@0: iRequestThread.iThread.RequestSignal(); sl@0: return; sl@0: } sl@0: sl@0: if (iStatus == KRequestPending) sl@0: Cancel(); sl@0: sl@0: if (!iQueue.IsEmpty() && !iQueueBusy) sl@0: { sl@0: const TInt ticksToWait = iQueue.First()->iTickCount - User::TickCount(); sl@0: sl@0: if (ticksToWait > 0) sl@0: { sl@0: iTimer.AfterTicks(iStatus, ticksToWait); sl@0: } sl@0: else sl@0: { sl@0: TRequestStatus* status = &iStatus; sl@0: User::RequestComplete(status, KErrNone); sl@0: } sl@0: } sl@0: } sl@0: sl@0: sl@0: sl@0: void CFsDeltaTimer::RunL() sl@0: // sl@0: // Call all zero delta callbacks sl@0: { sl@0: // if still running and no restart needed, then there's nothing to do sl@0: if (iStatus == KRequestPending && !iRestartNeeded) sl@0: return; sl@0: sl@0: sl@0: iLock.Wait(); sl@0: sl@0: // Queue busy sl@0: iQueueBusy = ETrue; sl@0: sl@0: // Whilst the list of expired timers is being processed, time will pass and sl@0: // the tick count may have increased such that there are now more expired sl@0: // timers. Loop until we have either emptied the queue or can wait for a sl@0: // timer exipration in the future. sl@0: if (iStatus == KErrNone) sl@0: { sl@0: iStatus = KErrNotReady; sl@0: while (!iQueue.IsEmpty()) sl@0: { sl@0: // Calculate how long till first timer expires sl@0: const TUint tickCount = User::TickCount(); sl@0: sl@0: // If the first timer is yet to expire, wait some more sl@0: if (((TInt)(iQueue.First()->iTickCount - tickCount)) > 0) sl@0: { sl@0: break; sl@0: } sl@0: sl@0: // Remove entry before callback to prevent re-entrancy issues sl@0: TTickCountQueLink* entry = iQueue.RemoveFirst(); sl@0: sl@0: // Iterate through the timers we know have expired based on the sl@0: // last calculation of delta sl@0: while (entry) sl@0: { sl@0: TThreadTimer* threadTimer = reinterpret_cast(PtrSub(entry, _FOFF(TThreadTimer, iLink))); sl@0: threadTimer->iRequestThread = NULL; // indicate timer not running sl@0: sl@0: // Make callback. This could go reentrant on Queue[Long]() or Remove(). sl@0: iLock.Signal(); sl@0: threadTimer->iCallBack.CallBack(); sl@0: iLock.Wait(); sl@0: sl@0: // Remove the next expired entry, if any sl@0: entry = iQueue.RemoveFirst(tickCount); sl@0: } sl@0: } sl@0: } sl@0: sl@0: // Queue idle sl@0: iQueueBusy = EFalse; sl@0: sl@0: sl@0: // Requeue timer if queue isn't empty sl@0: Activate(); sl@0: sl@0: iRestartNeeded = EFalse; sl@0: sl@0: iLock.Signal(); sl@0: } sl@0: sl@0: void CFsDeltaTimer::Cancel() sl@0: { sl@0: if (iStatus == KRequestPending) sl@0: { sl@0: iTimer.Cancel(); sl@0: User::WaitForRequest(iStatus); sl@0: } sl@0: } sl@0: sl@0: sl@0: sl@0: TThreadTimer::TThreadTimer(TInt (*aCallBackFunction)(TAny*),TAny* aPtr) : sl@0: iCallBack(aCallBackFunction, aPtr), sl@0: iRequestThread(NULL) sl@0: { sl@0: }; sl@0: sl@0: sl@0: void TThreadTimer::Start(CRequestThread* aRequestThread, TTimeIntervalMicroSeconds32 aTime) sl@0: { sl@0: ASSERT(aRequestThread); sl@0: sl@0: // NB: There are no locks here, so we have to be aware that CFsDeltaTimer::RunL() sl@0: // may be running in another thread and set iRequestThread to NULL sl@0: CRequestThread* requestThread = iRequestThread; sl@0: if (!requestThread) // if not already running, use caller's request thread sl@0: requestThread = aRequestThread; sl@0: sl@0: sl@0: __ASSERT_DEBUG(requestThread->Timer(),Fault(ERequestThreadNotInitialised)); sl@0: requestThread->Timer()->Start(*this, aTime); sl@0: } sl@0: sl@0: sl@0: void TThreadTimer::Stop() sl@0: { sl@0: // NB: There are no locks here, so we have to be aware that CFsDeltaTimer::RunL() sl@0: // may be running in another thread and set iRequestThread to NULL sl@0: CRequestThread* requestThread = iRequestThread; sl@0: if (requestThread) sl@0: requestThread->Timer()->Stop(*this); sl@0: } sl@0: sl@0: