realtimenetprots/sipfw/SIP/LightWeightTimer/src/TimerManager.cpp
author Petteri Saari <petteri.saari@digia.com>
Thu, 25 Nov 2010 13:59:42 +0200
branchMSRP_FrameWork
changeset 58 cdb720e67852
parent 0 307788aac0a8
permissions -rw-r--r--
This release addresses the following issues: 1. The crash bug fix when receiving file 2. Now the sending is based on MSRP messages, there is no longer file receiving or sending. Client sends data as MSRP was designed. 3. Soma MSRP stack was created so that the client told the correct session-id, Symbian stack generated it by itself. This is not allowed, it was changed so that clients tell the session-id (same as used in SIP INVITE). 4. Unnecessary division of data to chunks removed when there is no need to interrupt sending. The message is sent in as few chunks as possible. 5. Stack can now receive files and chunks with ?unlimited? size. Old stack wrote the incoming data to memory and did not utilize disk space until the end of chunk was reached (large chunks from another client crashed it). 6. Now when writing the incoming data to file, it will take into account the byte-range header values. So, this complies with the RFC4975 requirements that stack must be able to handle chunks that come in any sequence. 7. Some buffering changes to outgoing/incoming data. 8. The outgoing data is now checked that it does not contain the created transaction-id before sending the data. 9. MSRP success reports are now implemented and tested against servers. 10. Progress report system fixed so progress is now visible on client (all the way to 100%). 11. Message Cancel receiving / Cancel sending now corrected and made to work as rfc4975 requires. (termination from sender and error code from receiver when cancelling). 12. Bug correction related to messages received not belonging to any session, old stack implementation did send error response, but after response was written it did give the buffer to client anyway. Now corrected.

// Copyright (c) 2005-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:
// Name          : timermanager.cpp
// Part of       : LightWeightTimer
// Version       : SIP/4.0
//



#include "SipAssert.h"
#include "TimerManager.h"
#include "timerrequest.h"
#include "timerstore.h"
#include "singletimer.h"
#include "timerlog.h"


// -----------------------------------------------------------------------------
// CTimerManager::NewL
// -----------------------------------------------------------------------------
//
CTimerManager* CTimerManager::NewL()
	{
	CTimerManager* self = CTimerManager::NewLC();	
	CleanupStack::Pop(self);
	return self;
	}

// -----------------------------------------------------------------------------
// CTimerManager::NewLC
// -----------------------------------------------------------------------------
//
CTimerManager* CTimerManager::NewLC()
	{
	CTimerManager* self = new (ELeave) CTimerManager();
	CleanupStack::PushL(self);	
	self->ConstructL();
	return self;
	}

// -----------------------------------------------------------------------------
// CTimerManager::CTimerManager
// -----------------------------------------------------------------------------
//
CTimerManager::CTimerManager() :
	iTimerIdCounter(KReservedTimer),
	iNowExpiring(EFalse)
	{
	}

// -----------------------------------------------------------------------------
// CTimerManager::ConstructL
// -----------------------------------------------------------------------------
//
void CTimerManager::ConstructL()
	{
	iTimerStore = CTimerStore::NewL();
	iTimer = CSingleTimer::NewL(*this);
	}

// -----------------------------------------------------------------------------
// CTimerManager::~CTimerManager
// -----------------------------------------------------------------------------
//
CTimerManager::~CTimerManager()
	{
	delete iTimerStore;
	delete iTimer;
	}

// -----------------------------------------------------------------------------
// CTimerManager::StartL
// -----------------------------------------------------------------------------
//
TTimerId
CTimerManager::StartL(MExpirationHandler* aObserver, TUint aMilliseconds)
	{
	__TEST_INVARIANT;
	__ASSERT_ALWAYS(aObserver, User::Leave(KErrArgument));

	return StartL(aObserver, aMilliseconds, NULL);
	}

// -----------------------------------------------------------------------------
// CTimerManager::StartL
// -----------------------------------------------------------------------------
//
TTimerId CTimerManager::StartL(MExpirationHandler* aObserver,
							   TUint aMilliseconds,
							   TAny* aTimerParam)
	{
	__TEST_INVARIANT;
	__ASSERT_ALWAYS(aObserver, User::Leave(KErrArgument));
	
    TTimerId newTimerId(NewTimerId());

#if defined(WRITE_TIMER_LOG)
	__SIP_INT_LOG2( "LwTimer StartL (id,duration)", newTimerId, aMilliseconds )
#endif

	iTimerStore->AddL(aObserver,
					  MillisecToTTime(aMilliseconds),
					  newTimerId,
					  aTimerParam);		

	//If iNowExpiring is true, timer can't be set now, as execution is inside
	//MExpirationHandler::TimerExpiredL. Timer will be set when the control
	//returns from callback to CTimerManager::TimerExpiredL.
	if (!iNowExpiring)
		{
		CheckIsNewTimerShortestL(newTimerId, aMilliseconds);
		}

	__TEST_INVARIANT;
	return newTimerId;
	}

// -----------------------------------------------------------------------------
// CTimerManager::Stop
// User is not allowed to stop the internal timer
// -----------------------------------------------------------------------------
//
TInt CTimerManager::Stop(TTimerId aTimerId)
	{
	__TEST_INVARIANT;

#if defined(WRITE_TIMER_LOG)
	__SIP_INT_LOG1( "LwTimer Stop, timer id", aTimerId)
#endif

	if (aTimerId == KReservedTimer)
		{
		return KErrNotFound;
		}

	TInt index(0);
	const CTimerRequest* timerReq = iTimerStore->SearchById(aTimerId, index);
	if (!timerReq)
		{
		return KErrNotFound;
		}

	if (!iTimerStore->Remove(index))
		{
		return KErrNotFound;
		}

	//If Stop is called from MExpirationHandler::TimerExpiredL, this condition
	//isn't true, as iTimer has TimerId of the expiring timer which was removed
	//from iTimerStore. So no need to check iNowExpiring flag now.
	if (aTimerId == iTimer->TimerId())
		{
		//The currently running timer was stopped.
		iTimer->Cancel();

		TRAPD(err, SetTimerWithShortestValueL());
		if (err != KErrNone)
			{
			return err;
			}
		}

	__TEST_INVARIANT;
	return KErrNone;
	}

// -----------------------------------------------------------------------------
// CTimerManager::IsRunning
// Don't reveal the internal timer to user.
// -----------------------------------------------------------------------------
//
TBool CTimerManager::IsRunning(TTimerId aTimerId) const
	{
	__TEST_INVARIANT;

	if (aTimerId == KReservedTimer)
		{
		return EFalse;
		}

	TInt dummyIndex(0);
	return (iTimerStore->SearchById(aTimerId, dummyIndex) != NULL);
	}

// -----------------------------------------------------------------------------
// CTimerManager::ExpiresAfter
// -----------------------------------------------------------------------------
//
TInt CTimerManager::ExpiresAfter(
    TTimerId aTimerId, 
    TUint& aExpiresAfterInMillisecs) const
    {
    __TEST_INVARIANT;
    
    aExpiresAfterInMillisecs = 0;

    TInt index(0);
	const CTimerRequest* timerReq = iTimerStore->SearchById(aTimerId, index);
	if (!timerReq)
		{
		return KErrNotFound;
		}
	
	TTimeToMillisec(timerReq->ExpirationTime(), aExpiresAfterInMillisecs);
		
    return KErrNone;
    }
    
// -----------------------------------------------------------------------------
// CTimerManager::TimerExpiredL
// SearchById can return NULL, e.g. in a following case:
// - User has set two timers with very similar durations and they are the only
//   currently running timers.
// - User stops the shorter timer, CTimerManager::Stop uses
//   SetTimerWithShortestValueL but as the remaining timer's expiration is so
//   close to the first timer, CSingleTimer uses CSingleTimer::ExpireImmediately
//-  Before CSingleTimer::RunL has chance to be executed, user stops also the
//   remaining timer, which is removed from CTimerStore.
//-  Soon CSingleTimer::RunL is called and leads here but the expiring timer has
//   already been removed and CTimerStore::SearchById returns NULL.
// -----------------------------------------------------------------------------
//
void CTimerManager::TimerExpiredL(TTimerId aTimerId)
	{
	__TEST_INVARIANT;
    __SIP_ASSERT_LEAVE(aTimerId != KNoSuchTimer, KErrArgument);

	TInt index(0);
	CTimerRequest* timerReq = iTimerStore->SearchById(aTimerId, index);

	if (timerReq)
		{
#if defined(WRITE_TIMER_LOG)
    __SIP_INT_LOG1( "LwTimer TimerExpiredL id", aTimerId )
#endif
		//Store callback and parameter before freeing timerReq
		MExpirationHandler* notifier = timerReq->Observer();
		TAny* timerParam = timerReq->TimerParam();

		//Remove expired item BEFORE calling TimerExpiredL.
		//If user stops the expiring timer withing TimerExpiredL, would crash
		//as the timer would be used here after TimerExpiredL.
		iTimerStore->Remove(index);

		//Notifier can be NULL for the internal timer
		if (notifier)
			{
			iNowExpiring = ETrue;
			notifier->TimerExpiredL(aTimerId, timerParam);
			iNowExpiring = EFalse;
			}		
		}

	SetTimerWithShortestValueL();

	__TEST_INVARIANT;
	}

// -----------------------------------------------------------------------------
// CTimerManager::LeaveOccurred
// Do things that were not done in CTimerManager::TimerExpiredL due to leave.
// Clear iNowExpiring flag and start the next timer.
// -----------------------------------------------------------------------------
//
TInt CTimerManager::LeaveOccurred()
	{
    __TEST_INVARIANT;

	iNowExpiring = EFalse;
	TRAPD(err, SetTimerWithShortestValueL());

    __TEST_INVARIANT;
	return err;
	}

// -----------------------------------------------------------------------------
// CTimerManager::NewTimerId
// -----------------------------------------------------------------------------
//
TTimerId CTimerManager::NewTimerId()
	{
	__TEST_INVARIANT;

	iTimerIdCounter++;
	if (iTimerIdCounter == KNoSuchTimer || iTimerIdCounter == KReservedTimer)
		{
		iTimerIdCounter = KReservedTimer + 1;
		}

	__TEST_INVARIANT;
	return iTimerIdCounter;
	}

// -----------------------------------------------------------------------------
// CTimerManager::MillisecToTTime
// -----------------------------------------------------------------------------
//
TTime CTimerManager::MillisecToTTime(TUint aMilliseconds) const
	{
	__TEST_INVARIANT;

	TTime expirationTime;
	expirationTime.HomeTime();

	TTimeIntervalSeconds sec = aMilliseconds/1000;
	expirationTime += sec;

	TTimeIntervalMicroSeconds us = (TInt64)(aMilliseconds % 1000) * 1000;
	expirationTime += us;

	return expirationTime;
	}

// -----------------------------------------------------------------------------
// CTimerManager::TTimeToMillisec
// -----------------------------------------------------------------------------
//
TBool CTimerManager::TTimeToMillisec(TTime aTime, TUint& aMilliseconds) const
	{
	__TEST_INVARIANT;

	TTime now;
	now.HomeTime();

	TTimeIntervalSeconds deltaSeconds(0);
	if (aTime.SecondsFrom(now, deltaSeconds) != KErrNone)
		{
		return EFalse;
		}

	TTimeIntervalMicroSeconds deltaMicrosecs = aTime.MicroSecondsFrom(now);

	if (deltaSeconds.Int() < 0 || deltaMicrosecs.Int64() < 0)
		{
		//Timer should've already expired. Expire now. Can happen in debugger or
		//if there are so many short timers they can't be handled fast enough.
		aMilliseconds = 0;
		return ETrue;
		}

    // Set a value anyway but it should not be used for timer if EFalse is 
    // returned
    aMilliseconds = deltaSeconds.Int() * 1000;
    
	//"+ 1" is the worst case for the millisecond part
    if ((deltaSeconds.Int() + 1) > CSingleTimer::KMaxTimerAfterDuration / 1000)
		{
		return EFalse;
		}

	TInt64 milliseconds = (deltaMicrosecs.Int64()/1000) % 1000;
	//As milliseconds is always 0..999, the low 32 bits are enough
	aMilliseconds += I64LOW(milliseconds);

	return ETrue;
	}

// -----------------------------------------------------------------------------
// CTimerManager::CheckIsNewTimerShortestL
// If duration is too long for CSingleTimer::SetAfter, simulate it by chaining
// many shorter timers together. RTimer::At is avoided as it may end with
// KErrAbort ~23 minutes after setting it.
// -----------------------------------------------------------------------------
//
void CTimerManager::CheckIsNewTimerShortestL(TTimerId aTimerId,
											 TUint aMilliseconds)
	{
	__TEST_INVARIANT;

	TTimerId shortestTimerId(0);
	TTime shortestExpirationTime;

	if (iTimerStore->ShortestTimer(shortestTimerId, shortestExpirationTime))
		{
		if (shortestTimerId == aTimerId)
			{
			//The new timer is shorter than any of the existing, stop iTimer and
			//start it with the new expiration time.
			iTimer->Cancel();

            if (aMilliseconds <= CSingleTimer::KMaxTimerAfterDuration)
				{
				iTimer->SetAfter(aMilliseconds, aTimerId);
				}
			else
				{
				SetInternalTimerL();
				}
			}
		}
	else
		{
		//There must be at least one timer (the one just created).
		__ASSERT_DEBUG(EFalse,
			User::Panic(_L("TimerMgr:CheckIsNewTimerShortestL no timers"),
						KErrNotFound));
		}

	__TEST_INVARIANT;
	}

// -----------------------------------------------------------------------------
// CTimerManager::SetTimerWithShortestValueL
// -----------------------------------------------------------------------------
//
void CTimerManager::SetTimerWithShortestValueL()
	{
	__TEST_INVARIANT;
    __SIP_ASSERT_LEAVE(!iTimer->IsActive(), KErrNotReady);

	TTimerId timerId(0);
	TTime expirationTime;
	
	if (iTimerStore->ShortestTimer(timerId, expirationTime))
		{
		TUint milliseconds(0);

		if (TTimeToMillisec(expirationTime, milliseconds))
			{
			iTimer->SetAfter(milliseconds, timerId);
			}
		else
			{
			SetInternalTimerL();
			}
		}

#if defined(WRITE_TIMER_LOG)
	__SIP_INT_LOG1( "LwTimer SetTimerWithShortestValueL id", timerId )
#endif

	__TEST_INVARIANT;
	}

// -----------------------------------------------------------------------------
// CTimerManager::SetInternalTimerL
// -----------------------------------------------------------------------------
//
void CTimerManager::SetInternalTimerL() const
	{
    __TEST_INVARIANT;

	iTimerStore->AddL(NULL,
                      MillisecToTTime(CSingleTimer::KInternalTimerDuration),
					  KReservedTimer,
					  NULL);
    iTimer->SetAfter(CSingleTimer::KInternalTimerDuration, KReservedTimer);
	}

// -----------------------------------------------------------------------------
// CTimerManager::__DbgTestInvariant
// -----------------------------------------------------------------------------
//

void CTimerManager::__DbgTestInvariant() const
	{
	if (!iTimerStore || !iTimer)
		{
		User::Invariant();
		}
	}