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".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
14 // f32\sfile\sf_thread.cpp
20 #include "sf_file_cache.h"
22 #define __CHECK_DRVNUM(d) {__ASSERT_DEBUG(d>=EDriveA && d<=EDriveZ,Fault(EFsThreadBadDrvNum));}
25 const TInt KRequestThreadStackSize = 0x4000;
27 const TInt KRequestThreadStackSize = 0x3000;
30 const TInt KFinaliseTimerPeriod = 10 * 1000 * 1000; // default 10S finalisation timeout
32 TFsDriveThread FsThreadManager::iFsThreads[KMaxDrives];
33 TUint FsThreadManager::iMainId=0;
34 CDisconnectThread* FsThreadManager::iDisconnectThread=NULL;
35 TUint FsThreadManager::iDisconnectThreadId=0;
37 TFsDriveThread::TFsDriveThread()
41 :iIsAvailable(EFalse),iIsSync(EFalse),iThread(NULL),iId(0),iIsHung(EFalse),iMediaChangePending(EFalse)
43 TInt r=iFSLock.CreateLocal();
44 __ASSERT_ALWAYS(r==KErrNone,Fault(EFsThreadConstructor));
47 TFsPluginThread::TFsPluginThread()
51 :iIsAvailable(ETrue),iThread(NULL),iId(0)
53 TInt r=iPluginLock.CreateLocal();
54 iDriveNumber= KMaxDrives+1;
55 __ASSERT_ALWAYS(r==KErrNone,Fault(EFsThreadConstructor));
58 TInt FsThreadManager::CreateDisconnectThread()
60 // Called just once at startup
63 __PRINT(_L("Create disconnect thread"));
64 TRAPD(r,iDisconnectThread=CDisconnectThread::NewL());
67 TRAP(r,iDisconnectThreadId=iDisconnectThread->StartL());
70 delete(iDisconnectThread);
71 iDisconnectThread=NULL;
72 iDisconnectThreadId=0;
74 __THRD_PRINT2(_L("iDisconnectThread=0x%x id=0x%x"),iDisconnectThread,iDisconnectThreadId);
78 TBool FsThreadManager::IsDisconnectThread()
80 // Return ETrue if the calling thread is the disconnect thread
83 return(iDisconnectThreadId==RThread().Id());
87 TInt FsThreadManager::InitDrive(TInt aDrvNumber,TBool aIsSync)
89 // Create a drive thread
90 // Should only by called from main file server thread with drive thread unavailable
94 __PRINT1(_L("FsThreadManager::InitDrive() drive=%d"),aDrvNumber);
95 TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
96 __ASSERT_ALWAYS(!t.iIsAvailable,Fault(EThreadManagerInitDrive));
99 return ChangeSync(aDrvNumber, aIsSync);
103 TInt FsThreadManager::ChangeSync(TInt aDrvNumber,TBool aIsSync)
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.
111 __PRINT1(_L("FsThreadManager::ChangeSync() drive=%d"),aDrvNumber);
112 __CHECK_DRVNUM(aDrvNumber);
113 __CHECK_MAINTHREAD();
115 LockDrive(aDrvNumber);
116 TFsDriveThread& t=FsThreadManager::GetFsDriveThread(aDrvNumber);
119 if (aIsSync!=t.iIsSync)
125 TRAP(r,t.iThread=CDriveThread::NewL());
128 UnlockDrive(aDrvNumber);
132 TRAP(r,t.iId=t.iThread->StartL(aDrvNumber));
133 __THRD_PRINT2(_L("Starting thread 0x%x returned %d"),&t,r);
139 __THRD_PRINT1(_L("drive thread id=0x%x"),t.iId);
146 t.iThread->CompleteAllRequests(KErrNotReady);
147 t.iThread->iExit=ETrue;
154 t.iIsAvailable=ETrue;
156 UnlockDrive(aDrvNumber);
160 void FsThreadManager::CloseDrive(TInt aDrvNumber)
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
167 __PRINT1(_L("FsThreadManager::CloseDrive() drive=%d"),aDrvNumber);
168 __CHECK_DRVNUM(aDrvNumber);
170 // no need to cancel requests if synchronous since queued
171 if(!FsThreadManager::IsDriveSync(aDrvNumber,EFalse))
173 CDriveThread* pT=NULL;
174 TInt r=FsThreadManager::GetDriveThread(aDrvNumber,&pT);
175 __ASSERT_ALWAYS(r==KErrNone && pT,Fault(EDismountFsDriveThread));
176 pT->CompleteAllRequests(KErrNotReady);
179 TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
180 __ASSERT_ALWAYS(t.iIsAvailable,Fault(EFsThreadDriveClose1));
183 __ASSERT_ALWAYS(FsThreadManager::IsDriveThread(aDrvNumber,EFalse),Fault(EFsThreadDriveClose2));
185 StopFinalisationTimer(aDrvNumber);
187 // drive thread will exit when request completed
188 t.iThread->iExit=ETrue;
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.
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);
201 __CHECK_MAINTHREAD();
204 t.iIsAvailable=EFalse;
209 TBool FsThreadManager::IsDriveAvailable(TInt aDrvNumber,TBool aIsLock)
214 __CHECK_DRVNUM(aDrvNumber);
215 TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
218 TBool b=t.iIsAvailable;
221 __THRD_PRINT2(_L("drive thread %d iIsAvailable=%d"),aDrvNumber,b);
225 TBool FsThreadManager::IsDriveSync(TInt aDrvNumber,TBool aLock)
230 __CHECK_DRVNUM(aDrvNumber);
231 TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
234 TBool b=(t.iIsAvailable&&t.iIsSync);
237 __THRD_PRINT2(_L("drive thread %d iIsSync=%d"),aDrvNumber,b);
241 TInt FsThreadManager::GetDriveThread(TInt aDrvNumber, CDriveThread** aDrvThread)
243 // Assumes locked or called from the drive thread
246 __CHECK_DRVNUM(aDrvNumber);
247 TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
256 *aDrvThread=t.iThread;
257 __ASSERT_DEBUG(*aDrvThread,Fault(EFsThreadGetThread));
259 __THRD_PRINT4(_L("GetDriveThread(%d) r %d id=0x%x *aDrvThread=0x%x"),aDrvNumber, r, t.iId, *aDrvThread);
264 void FsThreadManager::LockDrive(TInt aDrvNumber)
266 // Lock the TFsDriveThread object for the aDrvNumber drive
269 __CHECK_DRVNUM(aDrvNumber);
270 __THRD_PRINT1(_L("FsThreadManager::LockDrive(%d)"),aDrvNumber);
271 TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
275 void FsThreadManager::UnlockDrive(TInt aDrvNumber)
277 // Unlock the TFsDriveThread object for the aDrvNumber drive
280 __CHECK_DRVNUM(aDrvNumber);
281 __THRD_PRINT1(_L("FsThreadManager::UnlockDrive(%d)"),aDrvNumber);
282 TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
286 void FsThreadManager::SetDriveHung(TInt aDrvNumber, TBool aIsHung)
288 if (aDrvNumber < EDriveA || aDrvNumber > EDriveZ)
291 TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
293 // quick exit if hung state not changing or drive thread not available
294 if ((!t.iIsAvailable) || (t.iIsHung == aIsHung))
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)
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;
318 mediaChangePending = t.iMediaChangePending;
319 t.iMediaChangePending = EFalse;
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);
330 if(mediaChangePending)
331 FsNotify::DiskChange(aDrvNumber);
335 TBool FsThreadManager::IsDriveHung(TInt aDrvNumber)
337 if (aDrvNumber < EDriveA || aDrvNumber > EDriveZ)
340 TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
341 // __THRD_PRINT3(_L("Is %d Hung = %d"), aDrvNumber, t.iIsHung);
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)
350 if (aDrvNumber < EDriveA || aDrvNumber > EDriveZ)
353 TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
359 t.iMediaChangePending = ETrue;
363 void FsThreadManager::SetMainThreadId()
365 // called at file server startup, assumes called from main file server thread
368 iMainId=RThread().Id();
369 __THRD_PRINT1(_L("Main thread id = 0x%x"),iMainId);
372 TBool FsThreadManager::IsDriveThread(TInt aDrvNumber,TBool aIsLock)
374 // Return ETrue if the calling thread is the aDrvNumber drive thread
377 __CHECK_DRVNUM(aDrvNumber);
378 TFsDriveThread& t=GetFsDriveThread(aDrvNumber);
379 TUint id=RThread().Id();
382 TBool b = t.iIsAvailable && (!t.iIsSync && t.iId==id || t.iIsSync);
388 TBool FsThreadManager::IsMainThread()
390 // Returns ETrue if calling thread is same as main file server thread
393 return((TUint)(RThread().Id())==iMainId);
397 void FsThreadManager::StartFinalisationTimer(TInt aDrvNumber)
399 if (aDrvNumber < EDriveA || aDrvNumber > EDriveZ)
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();
410 void FsThreadManager::StopFinalisationTimer(TInt aDrvNumber)
412 if (aDrvNumber < EDriveA || aDrvNumber > EDriveZ)
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)
421 dT->StopFinalisationTimer();
425 CRequestThread::CRequestThread()
429 :iList(_FOFF(CFsRequest,iLink))
436 TInt CRequestThread::Initialise()
441 TInt r=iListLock.CreateLocal();
445 CRequestThread::~CRequestThread()
450 __ASSERT_ALWAYS(iList.IsEmpty(),Fault(ERequestThreadDestructor));
453 if(iThread.Handle() != 0)
460 LOCAL_C TInt ThreadFunction(TAny* aPtr)
465 __THRD_PRINT(_L("ThreadFunction()"));
466 User::SetCritical(User::ESystemCritical);
467 CRequestThread* pT=(CRequestThread*)aPtr;
468 TInt r = pT->ThreadFunction();
473 void CRequestThread::CompleteAllRequests(TInt aValue)
475 __THRD_PRINT(_L("CRequestThread::CompleteAllRequests()"));
477 while(!iList.IsEmpty())
479 CFsRequest* pR=iList.First();
482 pR->Complete(aValue);
486 __THRD_PRINT(_L("all requests completed"));
489 TInt CRequestThread::ThreadFunction()
491 // entry point for the thread
494 iTimer = CFsDeltaTimer::New(*this, EPriorityLess);
497 RThread::Rendezvous(KErrNoMemory);
500 iTimer->iStatus = KErrNotReady;
502 CTrapCleanup* trapHandler=CTrapCleanup::New();
503 if (trapHandler==NULL)
505 RThread::Rendezvous(KErrNoMemory);
510 RThread::Rendezvous(KErrNone);
512 TInt err = DoThreadInitialise();
523 CompleteAllRequests(KErrNotReady);
529 TInt CRequestThread::DoThreadInitialise()
534 TInt CRequestThread::DoStart(RThread& aThread)
536 // create thread and return handle
537 // necessary for client to close thread handle if successful
540 TInt r=aThread.Create(KNullDesC,::ThreadFunction,KRequestThreadStackSize,NULL,(TAny*)this);
541 __PRINT1(_L("CRequestThread::DoStart() r=%d"),r);
544 TRequestStatus status;
545 aThread.Rendezvous(status);
546 if(status==KRequestPending)
548 aThread.SetPriority(EPriorityLess);
555 User::WaitForRequest(status);
566 void CRequestThread::Receive()
568 // Receive and process requests
576 iRequest=iList.First();
577 iRequest->iLink.Deque();
578 __THRD_PRINT(_L("CRequestThread::Receive() dequeing"));
584 iRequest = NULL; // set to NULL so we can distinguish between a timer and a request 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
590 __THRD_PRINT2(_L("received req 0x%x, func 0x%x"),iRequest, iRequest ? iRequest->Operation()->iFunction : -1);
599 //Any requests that sneaked on to
600 //the queue are cancelled in
601 //CRequestThread::ThreadFunction()
607 void CRequestThread::Deliver(CFsRequest* aRequest,TBool aIsFront, TBool aLowPriority)
609 // Deliver a request to the list from calling thread
610 // Write request directly to current request if thread is waiting
613 __THRD_PRINT4(_L("Deliver req %08x to threadId %lx aIsFront=%d iIsWaiting=%d"), aRequest, iThread.Id().Id(), aIsFront, iIsWaiting);
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)
621 __THRD_PRINT(_L("LOWERING THREAD PRIORITY"));
622 iThread.SetPriority(aLowPriority?EPriorityAbsoluteBackground:EPriorityLess);
623 iLowPriority = aLowPriority;
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"
632 iThread.SetPriority(EPriorityLess);
633 iLowPriority = EFalse;
639 // the request thread must be waiting on the iWaitLock
644 iThread.RequestSignal();
649 iList.AddFirst(*aRequest);
651 iList.AddLast(*aRequest);
656 void CRequestThread::DeliverFront(CFsRequest* aRequest)
661 Deliver(aRequest,ETrue);
664 void CRequestThread::DeliverBack(CFsRequest* aRequest, TBool aLowPriority)
669 Deliver(aRequest,EFalse,aLowPriority);
674 CFsDeltaTimer* CRequestThread::Timer()
676 __ASSERT_ALWAYS(iTimer,Fault(ERequestThreadNotInitialised));
681 CDriveThread::CDriveThread()
682 : iFinaliseTimer(FinaliseTimerEvent, this)
686 CDriveThread* CDriveThread::NewL()
691 __PRINT(_L("CDriveThread::NewL()"));
692 CDriveThread* pT=new(ELeave) CDriveThread;
693 TInt r=pT->Initialise();
702 TUint CDriveThread::StartL(TInt aDrvNumber)
707 __PRINT1(_L("CDriveThread::StartL() on drive %d"),aDrvNumber);
708 iDriveNumber=aDrvNumber;
710 User::LeaveIfError(DoStart(t));
715 TInt CDriveThread::DoThreadInitialise()
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.
722 __PRINT1(_L("CDriveThread::DoThreadInitialise() on drive %d"), iDriveNumber);
725 name.Format(_L("DriveThread_%02d"), iDriveNumber);
726 return(RThread::RenameMe(name));
729 void CDriveThread::CompleteSessionRequests(CSessionFs* aSession, TInt aValue)
734 __THRD_PRINT1(_L("CDriveThread::CompleteSessionReqeusts() drive=%d"),iDriveNumber);
736 TDblQueIter<CFsRequest> q(iList);
738 while((pR=q++)!=NULL)
740 if(pR->Session()==aSession)
744 pR->Complete(aValue);
746 // set iterator back to head of queue in case Complete() has itself removed requests from the queue
751 __THRD_PRINT(_L("session requests completed"));
755 void CDriveThread::CompleteReadWriteRequests()
757 __THRD_PRINT1(_L("CDriveThread::CompleteReadWriteRequests() drive=%d"),iDriveNumber);
761 TDblQueIter<CFsRequest> q(iList);
763 while((pR=q++)!=NULL)
765 TInt func = pR->Operation()->Function();
766 if (func == EFsFileRead || func == EFsFileWrite || func == EFsFileWriteDirty)
769 pR->Complete(KErrNotReady);
774 __THRD_PRINT(_L("file read/write requests completed"));
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.
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).
790 void CDriveThread::CompleteClientRequests(TInt aValue)
792 __THRD_PRINT1(_L("CDriveThread::CompleteClientRequests() drive=%d"),iDriveNumber);
796 TDblQueIter<CFsRequest> q(iList);
798 while((pR=q++)!=NULL)
800 TInt func = pR->Operation()->Function();
801 if(func == EFsFileSubClose)
803 TInt msgHandle = pR->Message().Handle();
804 if ((msgHandle != KLocalMessageHandle) && (msgHandle != 0))
805 pR->Message().Complete(KErrNone);
807 else if (func != EFsFileWriteDirty && func != KDispatchObjectClose)
811 pR->Complete(aValue);
817 __THRD_PRINT(_L("client read requests completed"));
820 TBool CDriveThread::IsRequestWriteable()
822 // return if current request may cause write to disk
823 // must be called from drive thread
826 __ASSERT_ALWAYS(FsThreadManager::IsDriveThread(iDriveNumber,EFalse),Fault(EDriveThreadWriteable));
827 return(iRequest->Operation()->IsWrite());
830 TBool CDriveThread::IsSessionNotifyUser()
832 // return if request's session has notify user set
833 // must be called from drive thread and request have a session set
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;
843 void CDriveThread::StartFinalisationTimer()
845 if(IsProxyDrive(iDriveNumber))
846 iFinaliseTimer.Start(this, KFinaliseTimerPeriod);
850 void CDriveThread::StopFinalisationTimer()
852 iFinaliseTimer.Stop();
855 TInt CDriveThread::FinaliseTimerEvent(TAny* aSelfP)
857 CDriveThread& self = *(CDriveThread*)aSelfP;
859 TDrive& drive = TheDrives[self.iDriveNumber];
860 if(drive.IsMounted())
862 if (drive.CurrentMount().LockStatus() == 0)
864 // Ignore the error here, as there's nothing we can do about it...
865 (void)drive.FinaliseMount(RFs::EFinal_RW);
869 self.StartFinalisationTimer();
877 CDisconnectThread::~CDisconnectThread()
887 CDisconnectThread* CDisconnectThread::NewL()
892 __THRD_PRINT(_L("CDisconnectThread::NewL()"));
893 CDisconnectThread* pT=new(ELeave) CDisconnectThread;
894 TInt r=pT->Initialise();
903 TUint CDisconnectThread::StartL()
908 __PRINT(_L("CDisconnectThread::StartL()"));
909 iRequest = new(ELeave) CFsInternalRequest;
910 __THRD_PRINT1(_L("internal request = 0x%x"),iRequest);
911 iRequest->Set(CancelSessionOp,NULL);
921 iRequest->SetThreadHandle(t.Handle());
922 __THRD_PRINT1(_L("CDisconnect::StartL() handle=%d"),t.Handle());
923 iRequest->SetAllocated();
929 CPluginThread::CPluginThread(CFsPlugin& aPlugin)
933 iOperationLock.Close();
937 CPluginThread::~CPluginThread()
943 CPluginThread* CPluginThread::NewL(CFsPlugin& aPlugin)
945 __PRINT(_L("CPluginThread::NewL()"));
946 CPluginThread* pT=new(ELeave) CPluginThread(aPlugin);
947 TInt r=pT->Initialise();
951 r=pT->iOperationLock.CreateLocal(0);
961 TUint CPluginThread::StartL()
963 __PRINT(_L("CPluginThread::StartL()"));
965 User::LeaveIfError(DoStart(t));
970 void CPluginThread::CompleteSessionRequests(CSessionFs* aSession, TInt aValue)
972 __THRD_PRINT(_L("CPluginThread::CompleteSessionRequests()"));
974 TDblQueIter<CFsRequest> q(iList);
976 while((pR=q++)!=NULL)
978 if(pR->Session()==aSession)
981 pR->Complete(aValue);
985 __THRD_PRINT(_L("session requests completed"));
988 TInt CPluginThread::DoThreadInitialise()
990 __PRINT(_L("CPluginThread::DoThreadInitialise()"));
991 TRAPD(err, iPlugin.InitialiseL());
997 void CPluginThread::OperationLockWait()
999 iOperationLock.Wait();
1003 void CPluginThread::OperationLockSignal()
1005 iOperationLock.Signal();
1008 // Class TTickCountQue
1013 Constructs an empty list header
1015 TTickCountQue::TTickCountQue()
1025 Adds the specified list element.
1027 The element is added into the list in order of its tick count.
1029 @param aRef The list element to be inserted.
1031 void TTickCountQue::Add(TTickCountQueLink& aRef)
1033 TTickCountQueLink* currentLink = (TTickCountQueLink*)(iHead.iNext);
1034 TTickCountQueLink* addLink = &aRef;
1036 while ( (currentLink != (TTickCountQueLink*)&iHead) &&
1037 (((TInt)(addLink->iTickCount - currentLink->iTickCount)) >= 0)
1040 currentLink = (TTickCountQueLink*)currentLink->iNext;
1043 addLink->Enque(currentLink->iPrev);
1053 Removes the first list element from the linked list if its tick count
1054 is prior to the current tick count.
1056 @param aTickCount The current tick count.
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.
1061 TTickCountQueLink* TTickCountQue::RemoveFirst(TUint aTickCount)
1063 TTickCountQueLink* firstLink = (TTickCountQueLink*)iHead.iNext;
1065 if (((TInt)(firstLink->iTickCount - aTickCount)) <= 0)
1067 return RemoveFirst();
1080 Removes the first list element from the linked list, if any.
1082 @return A pointer to the element removed from the linked list. This is NULL,
1083 if the queue is empty.
1085 TTickCountQueLink* TTickCountQue::RemoveFirst()
1087 TTickCountQueLink* firstLink = (TTickCountQueLink*)iHead.iNext;
1089 if (firstLink != (TTickCountQueLink*)&iHead)
1105 Gets a pointer to the first list element in the doubly linked list.
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.
1111 TTickCountQueLink* TTickCountQue::First() const
1113 #if defined (_DEBUG)
1116 return((TTickCountQueLink*)iHead.iNext);
1123 CFsDeltaTimer* CFsDeltaTimer::New(CRequestThread& aRequestThread, TInt aPriority)
1125 TTimeIntervalMicroSeconds32 tickPeriod;
1126 UserHal::TickPeriod(tickPeriod);
1128 CFsDeltaTimer* timer = new CFsDeltaTimer(aRequestThread, aPriority, tickPeriod.Int());
1132 if (timer->iTimer.CreateLocal() != KErrNone ||
1133 timer->iLock.CreateLocal() != KErrNone)
1142 CFsDeltaTimer::CFsDeltaTimer(CRequestThread& aRequestThread, TInt /*aPriority*/, TInt aTickPeriod) :
1143 iRequestThread(aRequestThread), iTickPeriod(aTickPeriod)
1145 iThreadId = RThread().Id();
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.
1160 CFsDeltaTimer::~CFsDeltaTimer()
1164 while (!iQueue.IsEmpty())
1166 iQueue.First()->Deque();
1182 void CFsDeltaTimer::Start(TThreadTimer& aEntry, TTimeIntervalMicroSeconds32 aTime)
1186 // must be already running on this thread or not running at all
1187 ASSERT(aEntry.iRequestThread == NULL || aEntry.iRequestThread == &iRequestThread);
1189 // attach the entry to this thread
1190 aEntry.iRequestThread = &iRequestThread;
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);
1200 void CFsDeltaTimer::Stop(TThreadTimer& aEntry)
1204 aEntry.iLink.Deque();
1205 aEntry.iRequestThread = NULL;
1211 TInt CFsDeltaTimer::QueueLong(TTimeIntervalMicroSeconds aTimeInMicroSeconds, TThreadTimer& aEntry)
1213 const TInt64 timeInTicks = (aTimeInMicroSeconds.Int64() + iTickPeriod - 1) / iTickPeriod;
1215 TInt timeInTicks32 = I64LOW(timeInTicks);
1217 // We are using deltas on tick values, hence using maximum signed number of ticks
1218 if (I64HIGH(timeInTicks) || (timeInTicks32 < 0))
1220 return KErrOverflow;
1223 // Make sure we queue for at least one tick
1224 if (timeInTicks32 == 0)
1229 // Calculate tick count for new entry
1230 aEntry.iLink.iTickCount = User::TickCount() + timeInTicks32;
1232 // Add this entry at the right spot
1233 iQueue.Add(aEntry.iLink);
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)
1243 void CFsDeltaTimer::Activate()
1245 // Queue a request on the timer.
1248 if (RThread().Id() != iThreadId)
1250 iRestartNeeded = ETrue;
1251 iRequestThread.iThread.RequestSignal();
1255 if (iStatus == KRequestPending)
1258 if (!iQueue.IsEmpty() && !iQueueBusy)
1260 const TInt ticksToWait = iQueue.First()->iTickCount - User::TickCount();
1262 if (ticksToWait > 0)
1264 iTimer.AfterTicks(iStatus, ticksToWait);
1268 TRequestStatus* status = &iStatus;
1269 User::RequestComplete(status, KErrNone);
1276 void CFsDeltaTimer::RunL()
1278 // Call all zero delta callbacks
1280 // if still running and no restart needed, then there's nothing to do
1281 if (iStatus == KRequestPending && !iRestartNeeded)
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)
1296 iStatus = KErrNotReady;
1297 while (!iQueue.IsEmpty())
1299 // Calculate how long till first timer expires
1300 const TUint tickCount = User::TickCount();
1302 // If the first timer is yet to expire, wait some more
1303 if (((TInt)(iQueue.First()->iTickCount - tickCount)) > 0)
1308 // Remove entry before callback to prevent re-entrancy issues
1309 TTickCountQueLink* entry = iQueue.RemoveFirst();
1311 // Iterate through the timers we know have expired based on the
1312 // last calculation of delta
1315 TThreadTimer* threadTimer = reinterpret_cast<TThreadTimer*>(PtrSub(entry, _FOFF(TThreadTimer, iLink)));
1316 threadTimer->iRequestThread = NULL; // indicate timer not running
1318 // Make callback. This could go reentrant on Queue[Long]() or Remove().
1320 threadTimer->iCallBack.CallBack();
1323 // Remove the next expired entry, if any
1324 entry = iQueue.RemoveFirst(tickCount);
1330 iQueueBusy = EFalse;
1333 // Requeue timer if queue isn't empty
1336 iRestartNeeded = EFalse;
1341 void CFsDeltaTimer::Cancel()
1343 if (iStatus == KRequestPending)
1346 User::WaitForRequest(iStatus);
1352 TThreadTimer::TThreadTimer(TInt (*aCallBackFunction)(TAny*),TAny* aPtr) :
1353 iCallBack(aCallBackFunction, aPtr),
1354 iRequestThread(NULL)
1359 void TThreadTimer::Start(CRequestThread* aRequestThread, TTimeIntervalMicroSeconds32 aTime)
1361 ASSERT(aRequestThread);
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;
1370 __ASSERT_DEBUG(requestThread->Timer(),Fault(ERequestThreadNotInitialised));
1371 requestThread->Timer()->Start(*this, aTime);
1375 void TThreadTimer::Stop()
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;
1381 requestThread->Timer()->Stop(*this);