sl@0: // Copyright (c) 1995-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: // e32\euser\cbase\ub_act.cpp sl@0: // sl@0: // sl@0: sl@0: #include "ub_std.h" sl@0: #include "us_data.h" sl@0: sl@0: #ifdef __SMP__ sl@0: #include sl@0: #endif sl@0: sl@0: sl@0: sl@0: #pragma warning( disable : 4705 ) // statement has no effect sl@0: EXPORT_C CActive::CActive(TInt aPriority) sl@0: /** sl@0: Constructs the active object with the specified priority. sl@0: sl@0: Derived classes must define and implement a constructor through which the sl@0: priority can be specified. A typical implementation calls this active object sl@0: constructor through a constructor initialization list. sl@0: sl@0: @param aPriority An integer specifying the priority of this active object. sl@0: CActive::TPriority defines a standard set of priorities. sl@0: */ sl@0: { sl@0: iLink.iPriority=aPriority; sl@0: } sl@0: #pragma warning( default : 4705 ) sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C CActive::~CActive() sl@0: /** sl@0: Frees resources prior to destruction. sl@0: sl@0: Specifically, it removes this active object from the active scheduler's sl@0: list of active objects. sl@0: sl@0: Typically, a derived class calls Cancel() in its destructor. sl@0: sl@0: @panic E32USER-CBase 40 if the active object has an outstanding request when sl@0: the destructor is called, sl@0: sl@0: @see CActive::Cancel sl@0: */ sl@0: { sl@0: __ASSERT_ALWAYS(!(iStatus.iFlags&TRequestStatus::EActive),Panic(EReqStillActiveOnDestruct)); sl@0: if (IsAdded()) sl@0: iLink.Deque(); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C void CActive::Cancel() sl@0: /** sl@0: Cancels the wait for completion of an outstanding request. sl@0: sl@0: If there is no request outstanding, then the function does nothing. sl@0: sl@0: If there is an outstanding request, the function: sl@0: sl@0: 1. calls the active object's DoCancel() function, provided by sl@0: the derived class to implement cancellation of the request. sl@0: sl@0: 2. waits for the cancelled request to complete; this must complete as fast as sl@0: possible. sl@0: sl@0: 3. marks the active object's request as complete (i.e. the request is no longer sl@0: outstanding). sl@0: sl@0: @see CActive::DoCancel sl@0: @see CActive::IsActive sl@0: @see CActive::~CActive sl@0: @see User::WaitForRequest sl@0: */ sl@0: { sl@0: if (iStatus.iFlags&TRequestStatus::EActive) sl@0: { sl@0: DoCancel(); sl@0: User::WaitForRequest(iStatus); sl@0: iStatus.iFlags&=~(TRequestStatus::EActive | TRequestStatus::ERequestPending); //iActive=EFalse; sl@0: } sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C void CActive::Deque() sl@0: /** sl@0: Removes the active object from the active scheduler's list of active objects. sl@0: sl@0: Before being removed from the active scheduler's list, the function cancels sl@0: any outstanding request. sl@0: sl@0: @see CActive::Cancel sl@0: */ sl@0: { sl@0: __ASSERT_ALWAYS(IsAdded(),Panic(EActiveNotAdded)); sl@0: Cancel(); sl@0: iLink.Deque(); sl@0: iLink.iNext=NULL; // Must do this or object cannot be re-queued sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C void CActive::SetActive() sl@0: /** sl@0: Indicates that the active object has issued a request and that sl@0: it is now outstanding. sl@0: sl@0: Derived classes must call this function after issuing a request. sl@0: sl@0: A request is automatically marked as complete (i.e. it is no longer sl@0: outstanding) by: sl@0: sl@0: 1. the active scheduler, immediately before it calls the active object's RunL() sl@0: function. sl@0: sl@0: or sl@0: sl@0: 2. the active object within the implementation of the Cancel() function. sl@0: sl@0: E32USER-CBase 46 panics may occur if an active object is set active but sl@0: no request is made on its TRequestStatus, or vice-versa. This panic happens sl@0: no earlier than the next time that the active scheduler assesses which sl@0: objects are ready to run, and may happen much later. This panic is termed sl@0: a 'stray event' because it indicates that some entity has sent an event sl@0: to the active scheduler thread, but this thread is not in a state ready to handle it. sl@0: sl@0: @see CActive::IsActive sl@0: @see CActive::RunL sl@0: @see CActive::Cancel sl@0: sl@0: @panic E32USER-CBase 42 if this active object is already active sl@0: @panic E32USER-CBase 49 if this active object has not been added to the active sl@0: scheduler. sl@0: */ sl@0: { sl@0: __ASSERT_ALWAYS(!(iStatus.iFlags&TRequestStatus::EActive),Panic(EReqAlreadyActive)); sl@0: __ASSERT_ALWAYS(IsAdded(),Panic(EActiveNotAdded)); sl@0: iStatus.iFlags|=TRequestStatus::EActive; sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C void CActive::SetPriority(TInt aPriority) sl@0: /** sl@0: Sets the priority of the active object. sl@0: sl@0: @param aPriority An integer specifying the new priority of this active object. sl@0: CActive::TPriority defines a standard set of priorities. sl@0: sl@0: @panic E32USER-CBase 50 if this function is called while a request sl@0: is outstanding. sl@0: */ sl@0: { sl@0: __ASSERT_ALWAYS(!(iStatus.iFlags&TRequestStatus::EActive),Panic(ESetPriorityActive)); sl@0: iLink.iPriority=aPriority; sl@0: if (IsAdded()) sl@0: { sl@0: Deque(); sl@0: iLink.iNext=NULL; // Make this not added sl@0: CActiveScheduler::Add(this); sl@0: } sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C TInt CActive::RunError(TInt aError) sl@0: /** sl@0: Handles a leave occurring in the request completion event handler RunL(). sl@0: sl@0: The active scheduler calls this function if this active object's RunL() sl@0: function leaves. This gives this active object the opportunity to perform sl@0: any necessary cleanup. sl@0: sl@0: A derived class implementation should handle the leave and return KErrNone. sl@0: Returning any other value results in the active scheduler function sl@0: CActiveScheduler::Error() being called. sl@0: sl@0: The default implementation simply returns the leave code. sl@0: sl@0: Note that if the active scheduler is to handle the error, a suitably derived sl@0: CActiveScheduler::Error() function must be supplied. sl@0: sl@0: @param aError The leave code sl@0: sl@0: @return The default implementation returns aError. A derived class sl@0: implementation should return KErrNone, if it handles the leave; sl@0: otherwise it should return any suitable value to cause the handling sl@0: of the error to be propagated back to the active scheduler. sl@0: sl@0: @see CActiveScheduler::Error sl@0: */ sl@0: { sl@0: return aError; sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: /** sl@0: Extension function sl@0: sl@0: sl@0: */ sl@0: EXPORT_C TInt CActive::Extension_(TUint aExtensionId, TAny*& a0, TAny* a1) sl@0: { sl@0: return CBase::Extension_(aExtensionId, a0, a1); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C CIdle* CIdle::New(TInt aPriority) sl@0: /** sl@0: Allocates and initialises an Idle time active object and adds it to the active sl@0: scheduler. sl@0: sl@0: @param aPriority An integer specifying the priority of this active object. sl@0: It must be lower than that of all other active objects on sl@0: the active scheduler. sl@0: The value CActive::TPriority::EPriorityIdle is recommended. sl@0: sl@0: @return Pointer to the new Idle time active object, or NULL if the object could sl@0: not be created. sl@0: */ sl@0: { sl@0: CIdle *pI=new CIdle(aPriority); sl@0: if (pI!=NULL) sl@0: CActiveScheduler::Add(pI); sl@0: return(pI); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C CIdle* CIdle::NewL(TInt aPriority) sl@0: /** sl@0: Allocates and initialises an Idle time active object, adds it to the active sl@0: scheduler, but leaves on failure. sl@0: sl@0: @param aPriority An integer specifying the priority of this active object. sl@0: It must be lower than that of all other active objects on sl@0: the active scheduler. sl@0: The value CActive::TPriority::EPriorityIdle is recommended. sl@0: sl@0: @return Pointer to the new Idle time active object. sl@0: */ sl@0: { sl@0: CIdle *pI=new(ELeave) CIdle(aPriority); sl@0: CActiveScheduler::Add(pI); sl@0: return(pI); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C CIdle::CIdle(TInt aPriority) sl@0: : CActive(aPriority) sl@0: /** sl@0: Protected constructor taking a priority value. sl@0: sl@0: Sets this active object's priority value. sl@0: sl@0: @param aPriority The active object priority value. sl@0: */ sl@0: {} sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C CIdle::~CIdle() sl@0: /** sl@0: Frees resources prior to destruction. sl@0: sl@0: Specifically, it cancels any outstanding request. sl@0: */ sl@0: { sl@0: Cancel(); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C void CIdle::Start(TCallBack aCallBack) sl@0: /** sl@0: Starts the background task. sl@0: sl@0: The background task is encapsulated in the callback. The function represented sl@0: by this callback is called every time this Idle time active object is scheduled sl@0: to run. sl@0: sl@0: The callback function should be structured to perform a background task in sl@0: many increments, i.e. it should voluntarily relinquish control (i.e. return) sl@0: after a suitable time interval to allow other, higher priority events to be sl@0: handled. sl@0: sl@0: If the callback function has further work to do, it should return a true value. sl@0: This ensures that the active object is scheduled to run again later. sl@0: sl@0: Once the callback function has finally completed its work, it should return sl@0: a false value. The active object is then no longer scheduled to run. sl@0: sl@0: @param aCallBack A callback object encapsulating a function which is called sl@0: when no higher priority active object is ready to run. sl@0: */ sl@0: { sl@0: iCallBack=aCallBack; sl@0: iStatus=KRequestPending; sl@0: TRequestStatus *pS=(&iStatus); sl@0: User::RequestComplete(pS,0); sl@0: SetActive(); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C void CIdle::RunL() sl@0: /** sl@0: Handles this idle active object's request completion event. sl@0: sl@0: It is called when nothing of a higher priority can be scheduled. sl@0: sl@0: @see CActive::RunL sl@0: */ sl@0: { sl@0: if (iCallBack.CallBack()) sl@0: Start(iCallBack); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C void CIdle::DoCancel() sl@0: /** sl@0: Implements the cancellation of an outstanding request. sl@0: sl@0: This function is called by the active object's Cancel() function. sl@0: sl@0: @see CActive::DoCancel sl@0: */ sl@0: { sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C void CAsyncOneShot::Call() sl@0: /** sl@0: Queues this active object to be run once. sl@0: sl@0: @panic E32USER-CBase 2 In debug builds only, if this active object has not sl@0: already been added to the active scheduler. sl@0: */ sl@0: { sl@0: __ASSERT_DEBUG(IsAdded(),Panic(ECAsyncOneShotNotAdded)); sl@0: TRequestStatus *pS=(&iStatus); sl@0: iStatus = KRequestPending; sl@0: SetActive(); sl@0: iThread.RequestComplete(pS,0); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C void CAsyncOneShot::DoCancel() sl@0: /** sl@0: Implements cancellation of an outstanding request. sl@0: sl@0: The class provides an empty implementation. sl@0: sl@0: This is called by the destructor. sl@0: */ sl@0: { sl@0: // Empty sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C CAsyncOneShot::CAsyncOneShot(TInt aPriority) sl@0: :CActive(aPriority) sl@0: /** sl@0: Constructor taking a priority value. sl@0: sl@0: Specifically, the constructor: sl@0: sl@0: 1. sets this active object's priority value sl@0: sl@0: 2. opens a handle to the current thread to ensure that the thread cannot be sl@0: closed until this CAsyncOneShot object is destroyed sl@0: sl@0: 3. adds this active object to the current active scheduler. sl@0: sl@0: @param aPriority The active object priority value. CActive::TPriority defines sl@0: a standard set of priorities. sl@0: sl@0: @panic E32USER-CBase 93 if the attempt to open a handle to the current thread sl@0: fails. sl@0: */ sl@0: { sl@0: Setup(); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: void CAsyncOneShot::Setup() sl@0: // sl@0: // ensures that we are added to the Scheduler. sl@0: // sl@0: { sl@0: // No error checking was done initially. As this function is called from sl@0: // the c'tor, there is no way to fix it properly without breaking BC. So sl@0: // we panic if something goes wrong (should only happen in extreme sl@0: // circumstances if the kernel heap is exhausted or heavily fragmented). sl@0: __ASSERT_ALWAYS(iThread.Duplicate(RThread()) == KErrNone, Panic(EAsyncOneShotSetupFailed)); sl@0: sl@0: // Add ourself to the current active scheduler sl@0: // This is because we might be being used as an inter thread call sl@0: // we need to make sure that we're on the correct scheduler for sl@0: // the RThread were going to duplicate. sl@0: CActiveScheduler::Add(this); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C CAsyncOneShot::~CAsyncOneShot() sl@0: /** sl@0: Frees resources prior to destruction. sl@0: sl@0: Specifically, it closes the handle to the current thread. sl@0: sl@0: @see CActive::~CActive sl@0: */ sl@0: { sl@0: Cancel(); sl@0: iThread.Close(); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C CAsyncCallBack::CAsyncCallBack(TInt aPriority) sl@0: : CAsyncOneShot(aPriority), iCallBack(NULL) sl@0: /** sl@0: Constructor taking a priority value. sl@0: sl@0: Specifically, the constructor sets this active object's priority value through sl@0: a call to the base class constructor in its ctor list. sl@0: sl@0: No call back is set, which means that it must be set subsequently through sl@0: a call to the Set() function. sl@0: sl@0: @param aPriority The active object priority value. CActive::TPriority defines sl@0: a standard set of priorities. sl@0: sl@0: @see CAsyncCallBack::Set sl@0: */ sl@0: { sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C CAsyncCallBack::CAsyncCallBack(const TCallBack& aCallBack, TInt aPriority) sl@0: : CAsyncOneShot(aPriority), iCallBack(aCallBack) sl@0: /** sl@0: Constructor taking a priority value and a callback. sl@0: sl@0: Specifically, the constructor: sl@0: sl@0: 1. sets this active object's priority value through a call to the base class sl@0: constructor in its ctor list sl@0: sl@0: 2. sets the callback; the function encapsulated by the callback is called when sl@0: this active object is scheduled to run. sl@0: sl@0: @param aCallBack A reference to a callback object encapsulating a function sl@0: which is called when this active object is ready to run. sl@0: The constructor takes a copy of this callback object, which sl@0: means that it can be safely discarded after construction. sl@0: @param aPriority The active object priority value. sl@0: */ sl@0: { sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C CAsyncCallBack::~CAsyncCallBack() sl@0: /** sl@0: Destructor. sl@0: */ sl@0: { sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C void CAsyncCallBack::CallBack() sl@0: /** sl@0: Queues this active object to be run, if it is not already queued. sl@0: */ sl@0: { sl@0: if (!IsActive()) sl@0: Call(); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C void CAsyncCallBack::Set(const TCallBack& aCallBack) sl@0: /** sl@0: Sets the call back. sl@0: sl@0: @param aCallBack A reference to a callback object encapsulating a function sl@0: which is called when this active object is ready to run. sl@0: sl@0: @panic E32USER-CBase 1 if the active object is currently active. sl@0: */ sl@0: { sl@0: __ASSERT_ALWAYS(!IsActive(), Panic(ECAsyncCBIsActive)); sl@0: iCallBack = aCallBack; sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: void CAsyncCallBack::RunL() sl@0: /** sl@0: Calls the callback function. sl@0: sl@0: @see TCallBack::CallBack sl@0: */ sl@0: { sl@0: iCallBack.CallBack(); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: struct CActiveScheduler::TLoop sl@0: { sl@0: TLoop* iNext; sl@0: CActiveScheduler::TLoopOwner* iOwner; sl@0: TCallBack iCallback; sl@0: TInt iExitCode; sl@0: }; sl@0: sl@0: CActiveScheduler::TLoopOwner* const KLoopNoOwner=reinterpret_cast(1); sl@0: CActiveScheduler::TLoopOwner* const KLoopInactive=0; sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C CActiveSchedulerWait::CActiveSchedulerWait() sl@0: /** sl@0: Default constructor. sl@0: */ sl@0: {} sl@0: sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C CActiveSchedulerWait::~CActiveSchedulerWait() sl@0: /** sl@0: Ensures that the attached scheduler loop, and all nested loops, are stopped sl@0: prior to destruction. sl@0: sl@0: @see AsyncStop() sl@0: */ sl@0: { sl@0: if (IsStarted()) sl@0: AsyncStop(); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C void CActiveSchedulerWait::Start() sl@0: /** sl@0: Starts a new wait loop under the control of the current active scheduler. sl@0: sl@0: Compared with CActiveScheduler::Start(), this object owns control of sl@0: the scheduling loop that is started, and that loop can only be stopped sl@0: by using this objects AsyncStop() function or the CActiveScheduler::Halt() sl@0: function. Start() only returns when either of thos has occurred. sl@0: sl@0: This is the preferred way to start a nested wait loop. Typically, a nested sl@0: wait loop is used when the handling of a completed event in an active object sl@0: requires processing further events from the other active objects before it sl@0: can complete. This is a form of modal processing. sl@0: sl@0: @panic E32USER-CBase 44 if the thread does not have an active scheduler installed. sl@0: @panic E32USER-CBase 91 if this object has already been started. sl@0: sl@0: @see CActiveSchedulerWait::AsyncStop sl@0: @see CActiveSchedulerWait::IsStarted sl@0: @see CActiveScheduler::Start sl@0: @see CActiveScheduler::Halt sl@0: */ sl@0: { sl@0: __ASSERT_ALWAYS(!IsStarted(), Panic(EActiveSchedulerWaitAlreadyStarted)); // can only start a CActiveSchedulerWait if it isn't already started sl@0: CActiveScheduler::Start(&iLoop); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C void CActiveSchedulerWait::AsyncStop() sl@0: /** sl@0: Stops the scheduling loop owned by this object. sl@0: sl@0: Note that the corresponding call to Start() only returns once all nested sl@0: scheduler loops have stopped. sl@0: sl@0: @panic E32USER-CBase 92 if the wait object has not been started. sl@0: */ sl@0: { sl@0: AsyncStop(TCallBack()); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C void CActiveSchedulerWait::AsyncStop(const TCallBack& aCallMeWhenStopped) sl@0: /** sl@0: Stops the scheduling loop owned by this object, specifying a callback. sl@0: sl@0: This version of AsyncStop() provides a callback which is invoked immediately sl@0: after the scheduler loop actually stops before the corresponding call sl@0: to Start() returns. sl@0: sl@0: Note that the corresponding call to Start() only returns once all nested sl@0: scheduler loops have stopped. sl@0: sl@0: @param aCallMeWhenStopped The callback to invoke when the scheduler loop exits. sl@0: sl@0: @panic E32USER-CBase 92 if the wait object has not been started. sl@0: */ sl@0: { sl@0: CActiveScheduler::TLoopOwner loop=iLoop; sl@0: __ASSERT_ALWAYS(loop, Panic(EActiveSchedulerWaitNotStarted)); // can only stop a CActiveSchedulerWait if it's started sl@0: __ASSERT_DEBUG(loop->iOwner==&iLoop, User::Invariant()); sl@0: sl@0: loop->iCallback = aCallMeWhenStopped; sl@0: loop->iOwner = KLoopInactive; // disconnect from owner sl@0: iLoop = 0; sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C TBool CActiveSchedulerWait::CanStopNow() const sl@0: /** sl@0: Reports whether stopping will have immediate effect. sl@0: sl@0: This returns an indication of whether a call to AsyncStop() would be sl@0: expected to stop the scheduler loop immediately, or whether it will sl@0: have to wait until nested scheduler loops have stopped. This may alter sl@0: which version of AsyncStop() you would want to call. sl@0: sl@0: @return Boolean indicating if the scheduling loop would stop immediately. sl@0: sl@0: @panic E32USER-CBase 92 if the wait object has not been started. sl@0: sl@0: @see CActiveSchedulerWait::Start sl@0: @see CActiveSchedulerWait::AsyncStop sl@0: */ sl@0: { sl@0: __ASSERT_ALWAYS(IsStarted(), Panic(EActiveSchedulerWaitNotStarted)); // Scheduler must be running sl@0: for (CActiveScheduler::TLoop* loop=GetActiveScheduler()->iStack; loop; loop=loop->iNext) sl@0: { sl@0: if (loop==iLoop) sl@0: return ETrue; sl@0: if (loop->iOwner != KLoopInactive) sl@0: break; sl@0: } sl@0: return EFalse; sl@0: } sl@0: sl@0: sl@0: sl@0: EXPORT_C CActiveScheduler::CActiveScheduler() sl@0: : iActiveQ(_FOFF(CActive,iLink)) sl@0: /** sl@0: Constructs an active scheduler. sl@0: sl@0: After construction, the scheduler should be installed. sl@0: sl@0: @see CActiveScheduler::Install sl@0: */ sl@0: {} sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C CActiveScheduler::~CActiveScheduler() sl@0: /** sl@0: Frees resources prior to destruction. sl@0: sl@0: Specifically, it removes all active objects from the active scheduler's list sl@0: of active objects. sl@0: sl@0: An active scheduler should only be destroyed when the top-level call to Start() sl@0: has returned. sl@0: sl@0: @see CActiveScheduler::Start sl@0: @see CActiveScheduler::Stop sl@0: */ sl@0: { sl@0: while (!iActiveQ.IsEmpty()) sl@0: iActiveQ.First()->Deque(); sl@0: if (GetActiveScheduler()==this) sl@0: SetActiveScheduler(NULL); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C void CActiveScheduler::Install(CActiveScheduler *aManager) sl@0: /** sl@0: Installs the specified active scheduler as the current active scheduler. sl@0: sl@0: The installed active scheduler now handles events for this thread. sl@0: sl@0: The current active scheduler can be uninstalled by passing a NULL pointer. sl@0: sl@0: @param aManager A pointer to the active scheduler to be installed. sl@0: If this is NULL, the current active scheduler is uninstalled. sl@0: sl@0: @panic E32USER-CBase 43 if If there is already an installed active scheduler. sl@0: */ sl@0: { sl@0: if (aManager!=NULL) sl@0: __ASSERT_ALWAYS(GetActiveScheduler()==NULL,Panic(EReqManagerAlreadyExists)); sl@0: SetActiveScheduler(aManager); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C void CActiveScheduler::Add(CActive *aRequest) sl@0: /** sl@0: Adds the specified active object to the current active scheduler. sl@0: sl@0: An active object can be removed from an active scheduler either by sl@0: destroying the active object or by using its Deque() member function. sl@0: sl@0: @param aRequest Pointer to the active object to be added. sl@0: sl@0: @panic E32USER-CBase 41 if the active object aRequest has already been added sl@0: to the current active scheduler. sl@0: @panic E32USER-CBase 48 if aRequest is NULL. sl@0: @panic E32USER-CBase 44 if the thread does not have an installed sl@0: active scheduler. sl@0: sl@0: @see CActive::Deque sl@0: */ sl@0: { sl@0: CActiveScheduler *pS=GetActiveScheduler(); sl@0: __ASSERT_ALWAYS(pS!=NULL,Panic(EReqManagerDoesNotExist)); sl@0: __ASSERT_ALWAYS(aRequest,Panic(EReqNull)); sl@0: __ASSERT_ALWAYS(!aRequest->IsAdded(),Panic(EReqAlreadyAdded)); sl@0: pS->iActiveQ.Add(*aRequest); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C void CActiveScheduler::WaitForAnyRequest() sl@0: /** sl@0: Wait for an asynchronous request to complete. sl@0: sl@0: The default implementation just calls User::WaitForAnyRequest(). sl@0: sl@0: Derived classes can replace this. Typically, this would be done to implement sl@0: code for maintaining an outstanding request; this would be followed by a call sl@0: to User::WaitForAnyRequest(). sl@0: sl@0: @see User::WaitForAnyRequest sl@0: */ sl@0: { sl@0: User::WaitForAnyRequest(); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C void CActiveScheduler::Start() sl@0: /** sl@0: Starts a new wait loop under the control of the current active scheduler. sl@0: sl@0: At least one active object, with an outstanding request, must be added sl@0: to the scheduler before the wait loop is started, otherwise no events sl@0: will occur and the thread will hang, or any events that do occur will be sl@0: counted as stray signals, raising a panic. sl@0: sl@0: While Start() is executing, user code runs only: sl@0: sl@0: 1. in the RunL() function of active objects known to the current active scheduler sl@0: sl@0: 2. in the RunError() function of an active object that leaves from its RunL() sl@0: sl@0: 3. in the current active scheduler’s Error() function, if an active object’s sl@0: RunError() returns an error code. sl@0: sl@0: Start() returns only when a corresponding Stop() or Halt() is issued. sl@0: sl@0: Although this can be used to start a nested wait loop, this API is deprecated sl@0: for that specific functionality, and a CActiveSchedulerWait object should be sl@0: used instead. sl@0: sl@0: (Note that a nested wait loop is used when the handling of a completed event sl@0: in an active object requires the processing of further events from the other sl@0: active objects before it can complete. This is a form of modal processing.) sl@0: sl@0: @panic E32USER-CBase 44 if the thread does not have an active sl@0: scheduler installed. sl@0: sl@0: @see CActiveScheduler::Stop sl@0: @see CActiveScheduler::Halt sl@0: @see CActive::RunL sl@0: @see CActive::RunError sl@0: @see CActiveScheduler::Error sl@0: @see CActiveSchedulerWait sl@0: */ sl@0: { sl@0: Start(KLoopNoOwner); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: void CActiveScheduler::Start(TLoopOwner* aOwner) sl@0: /** sl@0: @internalComponent sl@0: sl@0: Start a new nesting level sl@0: */ sl@0: { sl@0: CActiveScheduler* pS=GetActiveScheduler(); sl@0: __ASSERT_ALWAYS(pS!=NULL, Panic(EReqManagerDoesNotExist)); sl@0: sl@0: // Instantiate the local loop control sl@0: TLoop loop; sl@0: loop.iOwner=aOwner; sl@0: if (aOwner != KLoopNoOwner) sl@0: *aOwner=&loop; sl@0: loop.iNext=pS->iStack; sl@0: pS->iStack=&loop; sl@0: loop.iExitCode=0; sl@0: sl@0: // Run the scheduler loop sl@0: #if 1 sl@0: // FIXME!!! Will support old-style leave-from-Error() transiently sl@0: // in order to avoid simultaneous integration requirement. sl@0: // This should be reverted to the conditionally excluded code once sl@0: // fixes have been made elsewhere sl@0: TRAPD(r,pS->Run(loop.iOwner)); sl@0: if (r!=KErrNone) sl@0: { sl@0: loop.iExitCode = r; sl@0: TLoopOwner* owner=loop.iOwner; sl@0: if (TUint(owner) > TUint(KLoopNoOwner)) sl@0: *owner = NULL; sl@0: } sl@0: #else // fixme sl@0: #ifdef _DEBUG sl@0: // catch old-style bad behaviour - leaving from Error() sl@0: TRAPD(r,pS->Run(loop.iOwner)); sl@0: __ASSERT_DEBUG(r==KErrNone,User::Invariant()); sl@0: #else sl@0: pS->Run(loop.iOwner); sl@0: #endif sl@0: #endif sl@0: sl@0: pS->iStack=loop.iNext; sl@0: loop.iCallback.CallBack(); sl@0: // propagate the exit-code via a leave (yuck, but blame BAFL & co.) sl@0: if (loop.iExitCode) sl@0: User::Leave(loop.iExitCode); sl@0: } sl@0: sl@0: /* sl@0: @internalComponent sl@0: sl@0: Dummy Function. This is used as a dummy object to put onto the cleanupstack in order sl@0: to check for imbalance in the CActiveScheduler::DoRunL. sl@0: */ sl@0: void DummyFunc(TAny* /*aPtr*/) sl@0: {} sl@0: sl@0: sl@0: #ifdef __LEAVE_EQUALS_THROW__ sl@0: /** sl@0: @internalComponent sl@0: sl@0: Start dispatching request completions. sl@0: sl@0: Stop when aLoop becomes 'Inactive' sl@0: sl@0: This version uses the implementation of TRAP/Leave in terms of C++ exceptions. sl@0: We have to make sure here that we don't call Active Object's RunError() or Active Scheduler's Error() sl@0: while we are still in exception (within 'catch' brackets), as it can lead to nested-exceptions scenario. sl@0: It is not fatal by default, but if two nested exceptions are due to OOM condition, RVCT implementation sl@0: of exception will run out of emergency buffers and terminate the thread. sl@0: */ sl@0: void CActiveScheduler::Run(TLoopOwner* const volatile& aLoop) sl@0: { sl@0: CActive * volatile curr_obj = 0; sl@0: TBool leaveException = EFalse; sl@0: TInt exceptionReason = 0; sl@0: do sl@0: { sl@0: try { sl@0: __WIN32SEHTRAP sl@0: TTrapHandler* t = User::MarkCleanupStack(); sl@0: sl@0: #ifdef _DEBUG sl@0: //We cache the cleanupstack here do avoid repeated exec calls in DoRunL sl@0: TCleanupTrapHandler *pH=(TCleanupTrapHandler *)GetTrapHandler(); sl@0: CCleanup* cleanupPtr=NULL; sl@0: TCleanupBundle cleanupBundle; sl@0: sl@0: if(pH!=NULL) // test whether there's a CleanupTrapHandler installed sl@0: { sl@0: CCleanup& ccleanup =pH->Cleanup(); sl@0: //Store pointer as need the scope of ccleanup increased sl@0: cleanupPtr = &ccleanup; sl@0: cleanupBundle.iCleanupPtr = cleanupPtr; sl@0: sl@0: //Push a dummy item onto the stack - we check it after the AO's RunL has returned sl@0: //and we check to make sure its still at the top. sl@0: ccleanup.PushL(TCleanupItem(DummyFunc, &(cleanupBundle.iDummyInt))); sl@0: sl@0: DoRunL(aLoop, curr_obj, &cleanupBundle); sl@0: sl@0: //Dummy Int must (will) be at the top sl@0: //Cleanup our stack sl@0: cleanupPtr->Pop(1); sl@0: } sl@0: else // no cleanup stack installed sl@0: { sl@0: DoRunL(aLoop, curr_obj, NULL); sl@0: } sl@0: sl@0: #else sl@0: DoRunL(aLoop, curr_obj, NULL); sl@0: #endif sl@0: sl@0: User::UnMarkCleanupStack(t); sl@0: __WIN32SEHUNTRAP sl@0: return; sl@0: } sl@0: catch (XLeaveException& l) sl@0: { sl@0: Exec::LeaveEnd(); sl@0: leaveException = ETrue; sl@0: exceptionReason = l.Reason(); sl@0: } sl@0: catch (...) sl@0: { sl@0: User::Invariant(); sl@0: } sl@0: sl@0: if (leaveException) sl@0: { sl@0: if (exceptionReason != KErrNone) sl@0: { sl@0: TInt r = curr_obj->RunError(exceptionReason); sl@0: if (r != KErrNone) sl@0: Error(r); sl@0: } sl@0: leaveException = EFalse; sl@0: } sl@0: sl@0: } while (aLoop != KLoopInactive); sl@0: } sl@0: sl@0: #else sl@0: sl@0: /** sl@0: @internalComponent sl@0: sl@0: Start dispatching request completions. sl@0: sl@0: Stop when aLoop becomes 'Inactive' sl@0: sl@0: This version uses the original implementation of TRAP/Leave. sl@0: */ sl@0: void CActiveScheduler::Run(TLoopOwner* const volatile& aLoop) sl@0: { sl@0: CActive * volatile curr_obj = 0; sl@0: do sl@0: { sl@0: // explicitly expand the TRAPD macro here to enable single-step debugging sl@0: // of the scheduler loop sl@0: TInt r; sl@0: TTrap trap; sl@0: if (trap.Trap(r)==0) sl@0: { sl@0: #ifdef _DEBUG sl@0: //We cache the cleanupstack here do avoid repeated exec calls in DoRunL sl@0: TCleanupTrapHandler *pH=(TCleanupTrapHandler *)GetTrapHandler(); sl@0: CCleanup* cleanupPtr=NULL; sl@0: TCleanupBundle cleanupBundle; sl@0: sl@0: if(pH!=NULL) // test whether there's a CleanupTrapHandler installed sl@0: { sl@0: CCleanup& ccleanup =pH->Cleanup(); sl@0: //Store pointer as need the scope of ccleanup increased sl@0: cleanupPtr = &ccleanup; sl@0: cleanupBundle.iCleanupPtr = cleanupPtr; sl@0: sl@0: //Push a dummy item onto the stack - we check it after the AO's RunL has returned sl@0: //and we check to make sure its still at the top. sl@0: ccleanup.PushL(TCleanupItem(DummyFunc, &(cleanupBundle.iDummyInt))); sl@0: sl@0: DoRunL(aLoop, curr_obj, &cleanupBundle); sl@0: sl@0: //Dummy Int must (will) be at the top sl@0: //Cleanup our stack sl@0: cleanupPtr->Pop(1); sl@0: } sl@0: else // no cleanup stack installed sl@0: { sl@0: DoRunL(aLoop, curr_obj, NULL); sl@0: } sl@0: #else sl@0: DoRunL(aLoop, curr_obj, NULL); sl@0: #endif sl@0: sl@0: TTrap::UnTrap(); sl@0: return; // exit level sl@0: } sl@0: if (r != KErrNone) sl@0: { sl@0: r = curr_obj->RunError(r); sl@0: if (r != KErrNone) sl@0: Error(r); sl@0: } sl@0: } while (aLoop != KLoopInactive); sl@0: } sl@0: #endif sl@0: sl@0: #ifndef __CACTIVESCHEDULER_MACHINE_CODED__ sl@0: /** sl@0: @internalComponent sl@0: sl@0: The inner active scheduler loop. This repeatedly waits for a signal and then sl@0: dispatches the highest priority ready active object. The loop terminates either sl@0: if one of the RunLs stops the current active scheduler level or leaves. sl@0: sl@0: Stop when aLoop becomes 'Inactive' sl@0: @panic EClnCheckFailed 90 This will panic when the RunL has left the cleanup stack in an unbalanced state. sl@0: */ sl@0: #ifdef _DEBUG sl@0: void CActiveScheduler::DoRunL(TLoopOwner* const volatile& aLoop, CActive* volatile & aCurrentObj, TCleanupBundle* aCleanupBundlePtr) sl@0: #else sl@0: void CActiveScheduler::DoRunL(TLoopOwner* const volatile& aLoop, CActive* volatile & aCurrentObj, TCleanupBundle* /*aCleanupBundlePtr*/) sl@0: #endif sl@0: { sl@0: TDblQueIter q(iActiveQ); sl@0: do sl@0: { sl@0: WaitForAnyRequest(); sl@0: q.SetToFirst(); sl@0: CActive* pR; sl@0: do sl@0: { sl@0: pR=q++; sl@0: __ASSERT_ALWAYS(pR!=NULL,Panic(EReqStrayEvent)); sl@0: //if the line below panics it's either because you made a request but you haven't sl@0: //SetActive the object (pR->iStatus.iFlags&TRequestStatus::EActive==0) or you didn't set the iStatus sl@0: //to KRequestPending (pR->iStatus.iFlags&TRequestStatus::ERequestPending==0) sl@0: __ASSERT_DEBUG(!(pR->iStatus.iFlags&TRequestStatus::EActive)==!(pR->iStatus.iFlags&TRequestStatus::ERequestPending),Panic(EReqStrayEvent)); sl@0: } while (!pR->IsActive() || pR->iStatus==KRequestPending); sl@0: #ifdef __SMP__ sl@0: __e32_memory_barrier(); sl@0: #endif sl@0: pR->iStatus.iFlags&=~(TRequestStatus::EActive | TRequestStatus::ERequestPending); //pR->iActive=EFalse; sl@0: aCurrentObj = pR; sl@0: pR->RunL(); sl@0: sl@0: #ifdef _DEBUG sl@0: if(aCleanupBundlePtr!=NULL) sl@0: { sl@0: //If the following line panics, the RunL left the sl@0: //cleanup stack in an umbalanced state. sl@0: TInt* dummyInt = &(aCleanupBundlePtr->iDummyInt); sl@0: aCleanupBundlePtr->iCleanupPtr->Check(dummyInt); sl@0: } sl@0: #endif sl@0: sl@0: } while (aLoop != KLoopInactive); sl@0: return; // exit level sl@0: } sl@0: sl@0: #else sl@0: sl@0: extern "C" void PanicStrayEvent() sl@0: { sl@0: Panic(EReqStrayEvent); sl@0: } sl@0: #endif sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C void CActiveScheduler::Stop() sl@0: /** sl@0: Stops the wait loop started by the most recent call to Start(). sl@0: sl@0: Typically, this is called by the RunL() of one of the scheduler’s active sl@0: objects. When this RunL() finishes, the scheduler’s wait loop terminates, sl@0: i.e. it does not wait for the completion of the next request. sl@0: sl@0: It will not stop a wait loop started by a call sl@0: to CActiveSchedulerWait::Start(). sl@0: sl@0: Stop() may also be called from Error(). sl@0: sl@0: Note that stopping a nested wait loop is deprecated using this functionality, sl@0: use a CActiveSchedulerWait object instead. sl@0: sl@0: @see CActiveSchedulerWait::Start sl@0: @see CActive::RunL sl@0: @see CActiveSchedulerWait::Error sl@0: @see CActiveSchedulerWait::AsyncStop sl@0: */ sl@0: { sl@0: CActiveScheduler *pS=GetActiveScheduler(); sl@0: __ASSERT_ALWAYS(pS!=NULL,Panic(EReqManagerDoesNotExist)); sl@0: sl@0: for (CActiveScheduler::TLoop* loop=pS->iStack; loop; loop=loop->iNext) sl@0: { sl@0: if (loop->iOwner == KLoopNoOwner) sl@0: { sl@0: loop->iOwner=KLoopInactive; sl@0: return; sl@0: } sl@0: } sl@0: Panic(EReqTooManyStops); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C void CActiveScheduler::Halt(TInt aExitCode) const sl@0: /** sl@0: Unilaterally terminates the current scheduler loop. sl@0: sl@0: This causes the current scheduler loop to stop, whether it was started sl@0: using CActiveSchedulerWait::Start() or CActiveScheduler::Start(). It can sl@0: also trigger a leave from Start() if an exit code is provided. If the sl@0: current level has already been stopped, then this still records the exit code. sl@0: sl@0: @param aExitCode If non-zero, the reason code reported by Start(). sl@0: */ sl@0: { sl@0: CActiveScheduler::TLoop* loop=iStack; sl@0: __ASSERT_ALWAYS(loop!=NULL,Panic(EReqTooManyStops)); sl@0: TLoopOwner* owner=loop->iOwner; sl@0: if (TUint(owner) > TUint(KLoopNoOwner)) sl@0: *owner = NULL; sl@0: loop->iOwner = KLoopInactive; // disconnect from owner sl@0: loop->iExitCode = aExitCode; sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C TInt CActiveScheduler::StackDepth() const sl@0: /** sl@0: Gets the current number of nested wait loops. sl@0: sl@0: @return The number of nested calls to Start(). sl@0: */ sl@0: { sl@0: TInt depth=0; sl@0: for (CActiveScheduler::TLoop* loop=iStack; loop; loop=loop->iNext) sl@0: ++depth; sl@0: return depth; sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C CActiveScheduler* CActiveScheduler::Current() sl@0: /** sl@0: Gets a pointer to the currently installed active scheduler. sl@0: sl@0: @return A pointer to the active scheduler which is currently installed. sl@0: */ sl@0: { sl@0: return GetActiveScheduler(); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C void CActiveScheduler::Error(TInt /*aError*/) const sl@0: /** sl@0: Handles the result of a leave occurring in an active object’s RunL() sl@0: function. sl@0: sl@0: An active scheduler always invokes an active object’s RunL() sl@0: function under a trap harness. sl@0: sl@0: The default implementation must be replaced. sl@0: sl@0: Any cleanup relevant to the possible causes of leaving should sl@0: be performed. If Stop() or Halt() is called from within this function, the sl@0: current wait loop terminates. This may be an appropriate response to sl@0: catastrophic error conditions. sl@0: sl@0: @param aError The leave code propagated from the active object’s RunL() function sl@0: sl@0: @panic E32USER-CBase 47 if the default implementation is invoked. sl@0: sl@0: @see CActive::RunL sl@0: @see CActiveScheduler::Stop sl@0: @see CActiveScheduler::Halt sl@0: */ sl@0: { sl@0: Panic(EReqActiveObjectLeave); sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C TBool CActiveScheduler::RunIfReady(TInt& aError, TInt aMinimumPriority) sl@0: /** sl@0: @deprecated sl@0: sl@0: Causes the RunL() function of at most one pending active object of priority sl@0: aMinimumPriority or greater to be run. sl@0: sl@0: @param aError Error returned by called active object. sl@0: @param aMinimumPriority Minimum priority of active object to run. sl@0: sl@0: @return EFalse if no active object's RunL() function was run, i.e. if there sl@0: were no active objects of priority aMinimumPriority or greater pending. sl@0: */ sl@0: { sl@0: aError=KErrNone; sl@0: CActiveScheduler* pS=GetActiveScheduler(); sl@0: if (pS!=NULL) sl@0: { sl@0: TDblQueIter iterator(pS->iActiveQ); sl@0: for (CActive* active=iterator++; (active!=NULL) && (active->Priority()>=aMinimumPriority); active=iterator++) sl@0: { sl@0: if (active->IsActive() && (active->iStatus!=KRequestPending)) sl@0: { sl@0: active->iStatus.iFlags&=~(TRequestStatus::EActive | TRequestStatus::ERequestPending); //pR->iActive=EFalse; sl@0: TRAP(aError, active->RunL()); sl@0: if (aError!=KErrNone) sl@0: aError=active->RunError(aError); sl@0: return ETrue; sl@0: } sl@0: } sl@0: } sl@0: return EFalse; sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C CActiveScheduler* CActiveScheduler::Replace(CActiveScheduler* aNewActiveScheduler) sl@0: /** sl@0: Allows the current active scheduler to be replaced, while retaining its active sl@0: objects. sl@0: sl@0: @param aNewActiveScheduler The new active scheduler. sl@0: sl@0: @return Previous active scheduler. sl@0: */ sl@0: { sl@0: __ASSERT_ALWAYS(aNewActiveScheduler!=NULL, Panic(EReqManagerDoesNotExist)); sl@0: CActiveScheduler* oldActiveScheduler=GetActiveScheduler(); sl@0: __ASSERT_ALWAYS(aNewActiveScheduler!=oldActiveScheduler, Panic(EActiveSchedulerReplacingSelf)); sl@0: if (oldActiveScheduler!=NULL) sl@0: { sl@0: // steal all the CActive objects from oldActiveScheduler (without canceling any of them) sl@0: TPriQue& oldActiveQ=oldActiveScheduler->iActiveQ; sl@0: TPriQue& newActiveQ=aNewActiveScheduler->iActiveQ; sl@0: while (!oldActiveQ.IsEmpty()) sl@0: { sl@0: CActive& active=*oldActiveQ.First(); sl@0: // call the lower-level function active.iLink.Deque() rather than active.Deque() sl@0: // as the latter would also call active.Cancel() (which we don't want) sl@0: active.iLink.Deque(); sl@0: newActiveQ.Add(active); sl@0: } sl@0: } sl@0: SetActiveScheduler(aNewActiveScheduler); sl@0: return oldActiveScheduler; sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C void CActiveScheduler::OnStarting() sl@0: /** sl@0: @removed sl@0: sl@0: Dummy EXPORT for Binary Compatibility reasons. sl@0: This method is never called. sl@0: */ sl@0: { sl@0: } sl@0: sl@0: sl@0: sl@0: sl@0: EXPORT_C void CActiveScheduler::OnStopping() sl@0: /** sl@0: @removed sl@0: sl@0: Dummy EXPORT for Binary Compatibility reasons. sl@0: This method is never called. sl@0: */ sl@0: { sl@0: } sl@0: sl@0: sl@0: sl@0: EXPORT_C void CActiveScheduler::Reserved_1() sl@0: /** sl@0: @internalComponent sl@0: sl@0: Dummy EXPORT for Binary Compatibility reasons. sl@0: */ sl@0: { sl@0: } sl@0: sl@0: sl@0: sl@0: EXPORT_C void CActiveScheduler::Reserved_2() sl@0: /** sl@0: @internalComponent sl@0: sl@0: Dummy EXPORT for Binary Compatibility reasons. sl@0: */ sl@0: { sl@0: } sl@0: sl@0: sl@0: /** sl@0: Extension function sl@0: sl@0: sl@0: */ sl@0: EXPORT_C TInt CActiveScheduler::Extension_(TUint aExtensionId, TAny*& a0, TAny* a1) sl@0: { sl@0: return CBase::Extension_(aExtensionId, a0, a1); sl@0: }