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