keepalive/flextimer/server/engine/src/flextimercontainer.cpp
author hgs
Mon, 24 May 2010 20:51:35 +0300
changeset 32 5c4486441ae6
permissions -rw-r--r--
201021

/*
 * Copyright (c) 2010 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
 *
 * Description:
 * This class contains implementation of CFlexTimerContainer.
 *
 */

// System include files
#include <hal.h>

// User include files
#include "flextimercommon.h"
#include "flextimercontainer.h"
#include "flextimeritem.h"
#include "mflextimerservicecb.h"
#include "OstTraceDefinitions.h"
#ifdef OST_TRACE_COMPILER_IN_USE
#include "flextimercontainerTraces.h"
#endif



// This literal is only used by __ASSERT_DEBUG macro. Therefore, to prevent 
// unnacessary warnings, it is not compiled into release builds.
#ifdef _DEBUG
static const TInt KCBNullPointer = 1;
_LIT( KCBNullPointerDescriptor, "FlexTimerService CB is NULL." );
#endif

// ---------------------------------------------------------------------------
// NewL
// ---------------------------------------------------------------------------
//
CFlexTimerContainer* CFlexTimerContainer::NewL()
    {
    CFlexTimerContainer* self = new (ELeave) CFlexTimerContainer();
    
    OstTrace1( TRACE_INTERNAL,
        CFLEXTIMERCONTAINER_NEWL,
        "CFlexTimerContainer::NewL;this=%x",
        ( TUint )self );
    
    return self;
    }

// ---------------------------------------------------------------------------
// destructor
// If the list is not empty when coming here something has already gone wrong.
// Lets just delete the timers and forget Timeout() calling
// ---------------------------------------------------------------------------
//
CFlexTimerContainer::~CFlexTimerContainer()
    {
    CFlexTimerItem* item = NULL;
    
    OstTrace1(
        TRACE_INTERNAL,
        DUP1_CFLEXTIMERCONTAINER_CFLEXTIMERCONTAINER,
        "CFlexTimerContainer::~CFlexTimerContainer;this=%x",
        ( TUint )this );

    while ( !iTimerList.IsEmpty() )
        {
        item = iTimerList.First();
        iTimerList.Remove( *item );
        delete item;
        }
    }
// ---------------------------------------------------------------------------
// Make new timer and add it to list. On purpose do not make sanity checks. 
// Invalid(in the past) timers are found later.
// ---------------------------------------------------------------------------
//
void CFlexTimerContainer::AddTimerL(
    const TTimeIntervalMicroSeconds& aWinStartInterval,
    const TTimeIntervalMicroSeconds& aWinEndInterval,
    TBool aCancelAtSystemTimeChange,
    const MFlexTimerServiceCB* aFlexTimerServiceCB )
    {
    OstTraceExt5( TRACE_INTERNAL,
        CFLEXTIMERCONTAINER_ADDTIMERL,
        "CFlexTimerContainer::AddTimerL;this=%x;"
        "aWinStartInterval=%Ld;"
        "aWinEndInterval=%Ld;"
        "aCancelAtSystemTimeChange=%u;"
        "aFlexTimerServiceCB=%x",
        ( TUint )this,
        aWinStartInterval.Int64(),
        aWinEndInterval.Int64(),
        aCancelAtSystemTimeChange,
        ( TUint )aFlexTimerServiceCB );
    
    __ASSERT_DEBUG( aFlexTimerServiceCB,
            User::Panic( KCBNullPointerDescriptor, KCBNullPointer ) );

    // Before creating new flextimer timeout item, the interval times are
    // converted to absolute tick based time used by the engine.
    TTime winAbsStart( IntervalToAbsoluteTime( aWinStartInterval ) );
    TTime winAbsEnd( IntervalToAbsoluteTime( aWinEndInterval ) );
    
    CFlexTimerItem* newItem = CFlexTimerItem::NewL( winAbsStart,
        winAbsEnd,
        aCancelAtSystemTimeChange,
        aFlexTimerServiceCB );

    iTimerList.AddLast( *newItem );
    }
// ---------------------------------------------------------------------------
// Loop through list and if timer containing same CB that is given as a
// parameter is found, remove timer. If no timer found just return error code.
// ---------------------------------------------------------------------------
//
TInt CFlexTimerContainer::RemoveTimer(
    const MFlexTimerServiceCB* aFlexTimerServiceCB )
    {
    OstTraceExt2( TRACE_INTERNAL,
        CFLEXTIMERCONTAINER_REMOVETIMER,
        "CFlexTimerContainer::RemoveTimer;this=%x;aFlexTimerServiceCB=%x",
        ( TUint )this,
        ( TUint )aFlexTimerServiceCB );

    __ASSERT_DEBUG( aFlexTimerServiceCB,
        User::Panic( KCBNullPointerDescriptor, KCBNullPointer ) );
    
    TSglQueIter<CFlexTimerItem> listIter( iTimerList );
    CFlexTimerItem* item = NULL;

    listIter.SetToFirst();
    // Iter++ makes iterator to point to next element if one exists.
    // Otherwise NULL.
    while ( (item = listIter++) != NULL )
        {
        if ( item->GetCB() == aFlexTimerServiceCB )
            {
            iTimerList.Remove( *item );
            delete item;
            return KErrNone;
            }
        }
    return KErrNotFound;
    }
// ---------------------------------------------------------------------------
// Loop through timers and find time/timer that must expire next
// Calculate time left to that moment and return it. 
// ---------------------------------------------------------------------------
//
TBool CFlexTimerContainer::GetNextTimeout(
    TTimeIntervalMicroSeconds& aNextTimeoutDelay )
    {
    TSglQueIter<CFlexTimerItem> listIter( iTimerList );
    TTime currentAbsoluteTime( 0 );
    CFlexTimerItem* item = NULL;
    TTime tempTime( 0 );
    TTime nextTimeout( 0 );

    GetCurrentTime( currentAbsoluteTime );
    // Take first item as a reference value.
    listIter.SetToFirst();
    item = listIter++;
    if ( item )
        {
        item->GetMaxAbsoluteTime( nextTimeout );
        }

    // Find the timer that needs to expire next, and set nextTimeout to the
    // time, when the timer needs to expire.
    while ( (item = listIter++) != NULL )
        {
        item->GetMaxAbsoluteTime( tempTime );
        if ( tempTime < nextTimeout )
            {
            nextTimeout = tempTime;
            }
        }
    // Calculate difference between now -> min timeout time. If in past,
    // return zero interval
    aNextTimeoutDelay = nextTimeout.MicroSecondsFrom( currentAbsoluteTime );
    if ( aNextTimeoutDelay < TTimeIntervalMicroSeconds( 0 ) )
        {
        aNextTimeoutDelay = 0;
        }
    
    OstTraceExt2( TRACE_INTERNAL,
        CFLEXTIMERCONTAINER_GETNEXTTIMEOUT,
        "CFlexTimerContainer::GetNextTimeout;this=%x;aNextTimeoutDelay=%Ld",
        ( TUint )this,
        aNextTimeoutDelay.Int64() );
    
    return !iTimerList.IsEmpty();
    }
// ---------------------------------------------------------------------------
// Excecute selected algorithms and finally fire all timers in candidate list.
// ---------------------------------------------------------------------------
//
void CFlexTimerContainer::FireTimers( TFlexTimerAlgorithm aAlgorithmToBeUsed )
    {
    OstTraceExt2( TRACE_INTERNAL,
        CFLEXTIMERCONTAINER_FIRETIMERS,
        "CFlexTimerContainer::FireTimers;this=%x;aAlgorithmToBeUsed=%x",
        ( TUint )this,
        ( TUint )aAlgorithmToBeUsed );

    TSglQue<CFlexTimerItem> candidateList( _FOFF( CFlexTimerItem, iLink ) );

    TTime currentAbsoluteTime( 0 );

    GetCurrentTime( currentAbsoluteTime );
    
    // Simple algorithm is always executed
    SimpleAlgorithm( candidateList, currentAbsoluteTime );

    if ( EFlexTimerAlgorithmLatestPossible == aAlgorithmToBeUsed )
        {
        LatestPossibleAlgorithm( candidateList );
        }
    ExpireTimers( candidateList );
    }

//
// ---------------------------------------------------------------------------
// Loop through timer list, call abort to timer if it is abortable.
// Then remove timer from list and delete it.
// ---------------------------------------------------------------------------
//
void CFlexTimerContainer::AbortTimersDueToTimeChange( TInt aReason )
    {
    OstTraceExt2( TRACE_INTERNAL,
        CFLEXTIMERCONTAINER_ABORTTIMERSDUETOTIMECHANGE,
        "CFlexTimerContainer::AbortTimersDueToTimeChange;this=%x;aReason=%d",
        ( TUint )this,
        aReason );

    TSglQueIter<CFlexTimerItem> listIter( iTimerList );
    CFlexTimerItem* item = NULL;
    listIter.SetToFirst();
    
    // Go through all timers and check if the timer has 
    // AbortAtSystemTimeChange flag set, if so, abort the timer.
    while ( (item = listIter++) != NULL )
        {
        if ( item->IsAbortedAtSystemTimeChange() )
            {
            item->GetCB()->Abort( aReason );
            iTimerList.Remove( *item );
            delete item;
            }
        }
    }
// ---------------------------------------------------------------------------
// Constructor for CFlexTimerContainer
// ---------------------------------------------------------------------------
//
CFlexTimerContainer::CFlexTimerContainer() :
    iTimerList( _FOFF( CFlexTimerItem, iLink ) ),
    iLastTicks( 0 ),
    iCurrentAbsoluteTime( 0 )    
    {
    OstTrace1( TRACE_INTERNAL,
        CFLEXTIMERCONTAINER_CFLEXTIMERCONTAINER,
        "CFlexTimerContainer::CFlexTimerContainer;this=%x",
        ( TUint )this );
    TInt err;
    
    // Get system tick length for converting tics to microseconds.
    err = HAL::Get( HAL::ESystemTickPeriod, iTickPeriod );
    
    __ASSERT_ALWAYS(
        err == KErrNone,
        User::Panic(
            KFlexTimerContainerPanicCat,
            static_cast <TInt> ( EFlexTimerContainerNoTickPeriod ) ) );
    }
// ---------------------------------------------------------------------------
// Loops through timer list and moves all timers that can be fired to
// candidate list. Can be fired means that minimum time is reached. Note that
// if we are late for some reason (very likely if someone has win size zero),
// candidate list can now contain timers that are late (max time passed).
// ---------------------------------------------------------------------------
//
void CFlexTimerContainer::SimpleAlgorithm(
    TSglQue<CFlexTimerItem>& aCandidateList,
    TTime& aCurrentTime )
    {
    OstTraceExt3( TRACE_INTERNAL,
        CFLEXTIMERCONTAINER_SIMPLEALGORITHM,
        "CFlexTimerContainer::SimpleAlgorithm;"
        "this=%x;aCandidateList=%x;aCurrentTime=%Ld;",
        ( TUint )this,
        ( TUint )&aCandidateList,
        aCurrentTime.Int64() );

    TSglQueIter<CFlexTimerItem> listIter( iTimerList );
    CFlexTimerItem* item = NULL;
    TTime minTime( 0 );

    listIter.SetToFirst();
    // Iter++ makes iterator to point to next element if one exists.
    // Otherwise NULL.
    while ( (item = listIter++) != NULL )
        {
        item->GetMinAbsoluteTime( minTime );
        if ( minTime <= aCurrentTime )
            {
            OstTraceExt2( TRACE_INTERNAL,
                DUP1_CFLEXTIMERCONTAINER_SIMPLEALGORITHM,
                "CFlexTimerContainer::SimpleAlgorithm - Adding item;"
                "this=%x;item=%x",
                ( TUint )this,
                ( TUint )item );
            // Remove from candidate list needs to be done before the
            // item is added to back to iTimerList, because the lists
            // use the same iLink member.
            iTimerList.Remove( *item );
            
            // This timer can be timeouted, add it to timeout candidate list.
            // The list is called candidate list, because the content of the
            // list may be pruned by passing it to other algorithms for furher
            // prosessing.
            aCandidateList.AddLast( *item );
            }
        }
    }

//
// ---------------------------------------------------------------------------
// Loops through dropped list and candidate list and find timers where:
// dropped timers right side of the window is earlier than candidate timers
// right side of the window i.e. Candidate can be fired later with dropped
// timer, without additional timer firings
// ---------------------------------------------------------------------------
//
void CFlexTimerContainer::LatestPossibleAlgorithm(
    TSglQue<CFlexTimerItem>& aCandidateList )
    {   
    TSglQueIter<CFlexTimerItem> listIter( iTimerList );
    TSglQueIter<CFlexTimerItem> candidateListIter( aCandidateList );
    CFlexTimerItem* droppedItem = NULL;
    CFlexTimerItem* candidateItem = NULL;
    TTime droppedMaxTime( 0 );
    TTime candidateMaxTime( 0 );
    
    OstTraceExt2( TRACE_INTERNAL,
        CFLEXTIMERCONTAINER_LATESTPOSSIBLEALGORITHM,
        "CFlexTimerContainer::LatestPossibleAlgorithm;"
        "this=%x;aCandidateList=%x",
        ( TUint )this,
        ( TUint )&aCandidateList );
    
    listIter.SetToFirst();
    candidateListIter.SetToFirst();
            
    droppedItem = listIter++;
    
    if ( droppedItem != NULL )
        {
        // Initiliaze next dropped timeout time to some value.
        droppedItem->GetMaxAbsoluteTime( droppedMaxTime );

        // Loop through dropped timers and find the dropped timer that will have
        // shortest time to its timeout.
        while ( (droppedItem = listIter++) != NULL )
            {
            droppedItem->GetMaxAbsoluteTime( candidateMaxTime );
            if ( droppedMaxTime > candidateMaxTime )
                {
                droppedMaxTime = candidateMaxTime;
                }
            }

        // Loop through candidate timers
        while ( (candidateItem = candidateListIter++) != NULL )
            {
            candidateItem->GetMaxAbsoluteTime( candidateMaxTime );
            // If candidate can be fired together with dropped timer ->
            // don't fire candidate yet.
            if ( droppedMaxTime <= candidateMaxTime )
                {
                OstTraceExt2( TRACE_INTERNAL,
                    DUP1_CFLEXTIMERCONTAINER_LATESTPOSSIBLEALGORITHM,
                    "CFlexTimerContainer::LatestPossibleAlgorithm -"
                    " Removing item;this=%x;candidateItem=%x",
                    ( TUint )this,
                    ( TUint )candidateItem );

                // Remove from candidate list needs to be done before the
                // item is added to back to iTimerList, because the lists
                // use the same iLink member.
                aCandidateList.Remove( *candidateItem );
                // Add to first so we don't handle this again.
                iTimerList.AddFirst( *candidateItem );
                }
            }
        }
    }
// ---------------------------------------------------------------------------
// Loops through candidate list and calls timeout to all timers.
// Then removes timer from list and deletes it. 
// ---------------------------------------------------------------------------
//
void CFlexTimerContainer::ExpireTimers(
    TSglQue<CFlexTimerItem>& aCandidateList )
    {
    OstTraceExt2( TRACE_INTERNAL,
        CFLEXTIMERCONTAINER_EXPIRETIMERS,
        "CFlexTimerContainer::ExpireTimers;this=%x;candidateList=%x",
        ( TUint )this,
        ( TUint )&aCandidateList );

    TSglQueIter<CFlexTimerItem> candidateListIter( aCandidateList );
    CFlexTimerItem* item = NULL;

    candidateListIter.SetToFirst();
    // Iter++ makes iterator to point to next element if one exists.
    // Otherwise NULL.
    while ( (item = candidateListIter++) != NULL )
        {
        item->GetCB()->Timeout();
        aCandidateList.Remove( *item );
        delete item;
        }
    }
// ---------------------------------------------------------------------------
// Get current time return current time in FlexTimer engine time base. This
// time base is begins from the first call to GetCurrentTime and it is base on
// system ticks.
// ---------------------------------------------------------------------------
//
void CFlexTimerContainer::GetCurrentTime( TTime& aAbsoluteTime )
    {
    TUint32 currentTicks = User::TickCount();

    // Accumulate current absolute time with the time passed since last
    // update. Both currentTicks and iLastTicks are unsigned int types, thus
    // "currentTicks - iLastTicks" will handle also the case currentTicks
    // overflows.
    iCurrentAbsoluteTime += TicksToAbsoluteTime( currentTicks
            - iLastTicks );
        
    iLastTicks = currentTicks;
    
    // N.B. Even though the time is is returned as TTime, this time has own
    // time base. (See function description)
    aAbsoluteTime = iCurrentAbsoluteTime;
    }