diff -r 000000000000 -r e4d67989cc36 genericservices/taskscheduler/SCHSVR/SchTimer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/genericservices/taskscheduler/SCHSVR/SchTimer.cpp Tue Feb 02 02:01:42 2010 +0200 @@ -0,0 +1,630 @@ +// Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "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: +// + +// User includes +#include "SchTimer.h" +#include "SCHMAN.H" +#include "SCHEDULE.H" + +// system includes +#include + +/** +An instance of this class is used for each different time that a +schedule needs to be run. It is created solely in the +CScheduleCriteriaManager class. The timer is updated via the +SetNext method. When the time has been reached it notifies the schedule +manager via the CScheduleCriteriaManager::DueSchedule() method. + +@internalComponent +*/ +NONSHARABLE_CLASS(CScheduleTimer) : public CTimer + { +public: + ~CScheduleTimer(); + static CScheduleTimer* NewL(TInt aSchedule, CScheduleCriteriaManager& aManager); + + void SetNext(const TTsTime& aNextTime); + TInt Id(); + + //list capability + static TInt Offset(); +private: + // From CTimer + void RunL(); + + CScheduleTimer(TInt aSchedule, CScheduleCriteriaManager& aManager); + void ConstructL(); + +private: + TTsTime iDueTime; + TInt iScheduleHandle; + TSglQueLink iLink; + CScheduleCriteriaManager& iConditonManager; + }; + +CScheduleTimer* CScheduleTimer::NewL(TInt aSchedule, CScheduleCriteriaManager& aManager) + { + CScheduleTimer* self = new(ELeave) CScheduleTimer(aSchedule, aManager); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + +CScheduleTimer::CScheduleTimer(TInt aSchedule, CScheduleCriteriaManager& aManager) +: CTimer(EPriorityStandard), + iScheduleHandle(aSchedule), + iConditonManager(aManager) + { + } + +CScheduleTimer::~CScheduleTimer() + { + Cancel(); + } + +void CScheduleTimer::ConstructL() + { + CTimer::ConstructL(); + CActiveScheduler::Add(this); + } + +void CScheduleTimer::SetNext(const TTsTime& aNewTime) + { + // Can't handle (unlikely but theoretical) situation when year is BC (not AD) + __ASSERT_ALWAYS(aNewTime.GetUtcTime().DateTime().Year()>0,User::Invariant()); + if (IsActive()) + Cancel(); + + iDueTime=aNewTime; + TTime currentTime; + currentTime.UniversalTime(); + + if (aNewTime.GetUtcTime()>currentTime) + AtUTC(aNewTime.GetUtcTime()); + else + AtUTC(currentTime); + } + +// Respond to an task being due. RunL is only called once! +void CScheduleTimer::RunL() + { + if(iStatus != KErrAbort) + iConditonManager.DueSchedule(iScheduleHandle); + + // RunL() will also be triggered if the system time is changed, with iStatus + // set to KErrAbort. In this case DueSchedule() should not be called. + // If the system time has been changed, the schedule needs to be requeued. + // This has already been done automatically by CTaskScheduler::HandleEnvironmentChange() + // [called by the AO CEnvironmentChangeNotifier], + // as the active object CEnvironmentChangeNotifier has a higher priority than CScheduleTimer. + } + +TInt CScheduleTimer::Offset() + { + return (_FOFF(CScheduleTimer, iLink)); + } + +TInt CScheduleTimer::Id() + { + return iScheduleHandle; + } + +// +// class CPropertyNotifier +// This class handles changes to P&S variables and notifies the +// CConditionManager class when a condition is satisfied. +NONSHARABLE_CLASS(CPropertyNotifier) : public CActive + { +public: + ~CPropertyNotifier(); + static CPropertyNotifier* NewL(CConditionManager& aManager); + +private: + CPropertyNotifier(CConditionManager& aManager); + void AttachL(); + // From CActive + void RunL(); + void DoCancel(); + +public: + void SetPropertyL(const TUid& aCategory, TUint aKey); + +private: + TUid iCategory; + TUint iKey; + RProperty iProperty; + CConditionManager& iConditionManager; + }; + +// +// class CConditionManager +// This class manages a set of conditions for each schedule. It is used +// solely by the CScheduleCriteriaManager class. When the set of conditions +// is met, a the schedule manager is notified. +NONSHARABLE_CLASS(CConditionManager) : public CActive + { +public: + ~CConditionManager(); + static CConditionManager* NewL(TInt aSchedule, CScheduleCriteriaManager& aManager); + +private: + CConditionManager(TInt aSchedule, CScheduleCriteriaManager& aManager); + TBool MatchAllConditionsL() const; + TBool HasVariable(const TUid& aCategory, TUint aKey) const; + void CompleteRequest(); + // From CActive + void RunL(); + void DoCancel(); + +public: + void ReplaceL(const RArray& aConditions); + TInt Id(); + void VariableChangedL(const TUid& aCategory, TUint aKey); + //list capability + static TInt Offset(); + +private: + RArray iConditions; + RPointerArray iPropertyNotifiers; + TInt iScheduleHandle; + TSglQueLink iLink; + CScheduleCriteriaManager& iManager; + }; + +CConditionManager* CConditionManager::NewL(TInt aSchedule, CScheduleCriteriaManager& aManager) + { + CConditionManager* self = new(ELeave) CConditionManager(aSchedule, aManager); + return self; + } + +CConditionManager::CConditionManager(TInt aSchedule, CScheduleCriteriaManager& aManager) +: CActive(EPriorityStandard+1), //make priority higher that propertynotifier AO + iScheduleHandle(aSchedule), + iManager(aManager) + { + CActiveScheduler::Add(this); + } + +CConditionManager::~CConditionManager() + { + Cancel(); + iPropertyNotifiers.ResetAndDestroy(); + iConditions.Reset(); + } + +//This function evaluates (aValue aState) expression, where "op" could be +//"==", "!=", ">", "<", depending on aType value, and returns the result of expression. +static TBool DoMatchCondition(TTaskSchedulerCondition::TConditionType aType, + TInt aValue, + TInt aState) + { + if(aType == TTaskSchedulerCondition::EEquals) + { + if(aValue == aState) + { + return ETrue; + } + } + else if(aType == TTaskSchedulerCondition::ENotEquals) + { + if(aValue != aState) + { + return ETrue; + } + } + else if(aType == TTaskSchedulerCondition::EGreaterThan) + { + if(aValue > aState) + { + return ETrue; + } + } + else if(aType == TTaskSchedulerCondition::ELessThan) + { + if(aValue < aState) + { + return ETrue; + } + } + else + { + __ASSERT_ALWAYS(0, User::Invariant()); + } + return EFalse; + } + +void CConditionManager::ReplaceL(const RArray& aConditions) + { + // Ensure any active requests are cancelled + if(IsActive()) + { + Cancel(); + } + + //destroying existing ones will cancel outstanding requests + iPropertyNotifiers.ResetAndDestroy(); + iConditions.Reset(); + const TInt count = aConditions.Count(); + TInt i; + //Check that the properties already exist + for(i=0;iSetPropertyL(condition.iCategory, condition.iKey); + //Add condition + User::LeaveIfError(iConditions.Append(condition)); + //Add notifier + TInt err = iPropertyNotifiers.Append(notifier); + if(err != KErrNone) + { + iConditions.Remove(iConditions.Count() - 1);//Remove the condition we've just added + User::Leave(err); + } + CleanupStack::Pop(notifier); + } + //Check to see that conditions are not already satisfied. + if(MatchAllConditionsL()) + { + SetActive(); //we need to set AO active here, otherwise RunL wont be called. + CompleteRequest(); + } + } + +void CConditionManager::CompleteRequest() + { + TRequestStatus *status = &iStatus; + User::RequestComplete(status, KErrNone); // now compete request so RunL is triggered + } + +//Respond to a condition changing. +//Called from CPropertyNotifier::RunL(). +void CConditionManager::VariableChangedL(const TUid& aCategory, TUint aKey) + { + //We have been notified that the value of one of the variables has been changed. + //It is not enough to check that the variable's value satisfies its condition! + //We have to check that all CConditionManager::iPropertyNotifiers satisfy their conditions. + //---------------- + //If this is a variable, which is a part of the variables, monitored by the + //current CConditionManager object, only then do check variables values against + //requested conditions + if(HasVariable(aCategory, aKey)) + { + if(MatchAllConditionsL()) + { + SetActive(); //we need to set AO active here, otherwise RunL wont be called. + CompleteRequest(); + } + } + } + +void CConditionManager::RunL() + { + // cancel outstanding notification requests by destroying AO's + iPropertyNotifiers.ResetAndDestroy(); + iManager.DueSchedule(iScheduleHandle); + } + +void CConditionManager::DoCancel() + { + CompleteRequest(); + } + +//The method returns ETrue, if all monitored variables (aConditions array) +//satisfy their conditions, EFalse otherwise. +TBool CConditionManager::MatchAllConditionsL() const + { + TInt satisfiedConditionsCnt = 0; + TInt count = iConditions.Count(); + for(TInt i=0;i-1;--i) + { + if(iConditions[i].iCategory == aCategory && iConditions[i].iKey == aKey) + { + return ETrue; + } + } + return EFalse; + } + +TInt CConditionManager::Offset() + { + return (_FOFF(CConditionManager, iLink)); + } + +TInt CConditionManager::Id() + { + return iScheduleHandle; + } + +// + +CPropertyNotifier* CPropertyNotifier::NewL(CConditionManager& aManager) + { + CPropertyNotifier* self = new(ELeave) CPropertyNotifier(aManager); + return self; + } + +CPropertyNotifier::CPropertyNotifier(CConditionManager& aManager) +: CActive(EPriorityStandard), + iConditionManager(aManager) + { + CActiveScheduler::Add(this); + } + +CPropertyNotifier::~CPropertyNotifier() + { + Cancel(); + } + +void CPropertyNotifier::AttachL() + { + User::LeaveIfError(iProperty.Attach(iCategory, iKey)); + iProperty.Subscribe(iStatus); + SetActive(); + } + +void CPropertyNotifier::SetPropertyL(const TUid& aCategory, TUint aKey) + { + if (IsActive()) + Cancel(); + iCategory = aCategory; + iKey = aKey; + AttachL(); + } + +// Respond to a condition changing +void CPropertyNotifier::RunL() + { + if (iStatus.Int() >= KErrNone) + { + iConditionManager.VariableChangedL(iCategory, iKey); + AttachL(); + } + // if status is KErrNotFound then P&S variable has been deleted! By + // resubscribing we wait for it to be created. If it never gets + // created then TRequestStatus never completes so this condition + // never gets met and iConditionManager.VariableChanged which + // makes sense. + else if (iStatus.Int() == KErrNotFound) + AttachL(); + // If status is another error we have a problem!!! Whatever the case + // we should just ignore this condition from now on by doing nothing. + } + +void CPropertyNotifier::DoCancel() + { + iProperty.Cancel(); + } + + + +// + +CScheduleCriteriaManager* CScheduleCriteriaManager::NewL(CTaskScheduler& aOwner) + { + CScheduleCriteriaManager* self = new(ELeave) CScheduleCriteriaManager(aOwner); + return self; + } + +CScheduleCriteriaManager::~CScheduleCriteriaManager() + { + Cancel(); + RemoveTimers(); + RemoveConditions(); + } + +CScheduleCriteriaManager::CScheduleCriteriaManager(CTaskScheduler& aOwner) +: CActive(EPriorityStandard+2), //make priority higher than condition AO + iTaskScheduler(aOwner), + iTimers(CScheduleTimer::Offset()), + iConditions(CConditionManager::Offset()) + { + CActiveScheduler::Add(this); + } + +void CScheduleCriteriaManager::CompleteRequest() + { + TRequestStatus *status = &iStatus; + User::RequestComplete(status, KErrNone); // now compete request so RunL is triggered + } + +void CScheduleCriteriaManager::DueSchedule(TInt aScheduleHandle) + { + iDueScheduleHandle = aScheduleHandle; + SetActive(); // need to set AO active so RunL will subsequently be called. + CompleteRequest(); + } + +void CScheduleCriteriaManager::RunL() + { + // remove schedule and then notify task scheduler manager + RemoveSchedule(iDueScheduleHandle); + iTaskScheduler.DueTaskNotifyL(iDueScheduleHandle); + } + +void CScheduleCriteriaManager::DoCancel() + { + CompleteRequest(); + } + +// If schedule timer for this ID doesnt exist then create and add new timer. If schedule +// timer does exist then just amend existing timer. +//When one of the schedule entries in this schedule has become due, +//this function will be called with aNotFirstTime = ETrue +//If this function is called because of environment changes then aSchChange = EOnlyTime and only update time based schedule +void CScheduleCriteriaManager::ReplaceScheduleL(CSchedule& aSchedule, TSchChangeType aSchChange , TBool aNotFirstTime) + { + aSchedule.CalculateDueTime(aNotFirstTime); + + TInt scheduleId = aSchedule.Id(); + const TTsTime nextTime = aSchedule.DueTime(); + ReplaceScheduleL(nextTime,scheduleId); + + //If this function is called because of environment changes then + //leave conditions unchanged + if(aSchChange == EOnlyTime) + return; + CConditionManager* condition = FindCondition(scheduleId); + // no point in doing work for + if(aSchedule.Conditions().Count() > 0) + { + if(!condition) + { + condition = CConditionManager::NewL(scheduleId, *this); + iConditions.AddLast(*condition); + } + condition->ReplaceL(aSchedule.Conditions()); + } + else if(condition) + RemoveCondition(condition); + } + +// If schedule timer for this ID doesnt exist then create and add new timer. If schedule +// timer does exist then just amend existing timer. +void CScheduleCriteriaManager::ReplaceScheduleL(const TTsTime& aNextTime, + TInt aSchedule) + { + CScheduleTimer* timer = Find(aSchedule); + // if time is set to MaxTTime then we don't want to set a timer + // off as it will complete straight away. + if((aNextTime.GetUtcTime() != Time::MaxTTime()) + && (aNextTime.GetLocalTime() != Time::MaxTTime())) + { + if(!timer) + { + timer = CScheduleTimer::NewL(aSchedule, *this); + iTimers.AddLast(*timer); + } + timer->SetNext(aNextTime); + } + else if(timer) + { + RemoveTimer(timer); // make sure we remove the old one! + } + } + +void CScheduleCriteriaManager::RemoveSchedule(TInt aSchedule) + { + CScheduleTimer* timer = Find(aSchedule); + if(timer) + RemoveTimer(timer); // remove timer also terminates AO + + CConditionManager* condition = FindCondition(aSchedule); + if(condition) + RemoveCondition(condition); // remove condition also terminates AO + + } + +//Timer methods +void CScheduleCriteriaManager::RemoveTimers() + { + CScheduleTimer* timer; + TSglQueIter timerIter(iTimers); + timerIter.SetToFirst(); + while ((timer = timerIter++) != NULL) + { + RemoveTimer(timer); + } + } + +void CScheduleCriteriaManager::RemoveTimer(CScheduleTimer* aTimer) + { + iTimers.Remove(*aTimer); + delete aTimer; + } + +CScheduleTimer* CScheduleCriteriaManager::Find(TInt aSchedule) + { + CScheduleTimer* timer = NULL; + TSglQueIter timerIter(iTimers); + timerIter.SetToFirst(); + while ((timer = timerIter++) != NULL) + { + if (timer->Id() == aSchedule) + break; + } + return timer; + } + +// condition methods +void CScheduleCriteriaManager::RemoveConditions() + { + CConditionManager* condition; + TSglQueIter conditionIter(iConditions); + conditionIter.SetToFirst(); + while ((condition = conditionIter++) != NULL) + { + RemoveCondition(condition); + } + } + +void CScheduleCriteriaManager::RemoveCondition(CConditionManager* aCondition) + { + iConditions.Remove(*aCondition); + delete aCondition; + } + +CConditionManager* CScheduleCriteriaManager::FindCondition(TInt aSchedule) + { + CConditionManager* condition = NULL; + TSglQueIter conditionIter(iConditions); + conditionIter.SetToFirst(); + while ((condition = conditionIter++) != NULL) + { + if (condition->Id() == aSchedule) + break; + } + return condition; + } + +