< Previous by Date Date Index Next by Date >
< Previous in Thread Thread Index Next in Thread >

Re: [reSIProcate] Timers: why system time?


Hi,

Attached is a proposed mod to enable monotonic Timer's on Windows and *unix.


Windows
=========

* _RESIP_WINDOWS_MONOTONIC_CLOCK  enables/disables the monotonic timer.  If
disabled, the current ::GetSystemTime is used.

* The default on Windows Vista and beyond is to use ::GetTickCount64().  No
special handling is required when using this function.

* The default on Windows versions < Vista is GTCLockDuringRange::GTC64().
This implementation is the one given by Alexander, based on his final
comments below.

* An alternate implementation GTCInterlocked is given and is intended to
decrease the frequency that the timer must be called and avoid locking on a
mutex.

* A third alternative is to simply lock each time the timer is called
(GTCLock)

* When not running on Vista or higher, GTCLockDuringRange is the default
because the GTCInterlocked requires the CMPXCHG8B instruction, which was
introduced in the intel pentium and AMD K5 and is only supported, without
assembly, using an intrinsic visual c++ function that is only available in
visual studio 2005 or higher.

Non-Windows
=========

* _RESIP_POSIX_MONOTONIC_CLOCK enables the __NR_clock_gettime call.  Note
that on some systems even if _POSIX_MONOTONIC_CLOCK is defined
__NR_clock_gettime may not be, or may require another library (librt?).
Some improvement in enabling this could probably be done in the build
scripts.

=========

Comments?

Thanks,

-justin

-----Original Message-----
From: slgodin@xxxxxxxxx [mailto:slgodin@xxxxxxxxx] On Behalf Of Scott Godin
Sent: Wednesday, November 05, 2008 2:40 PM
To: Alexander Altshuler
Cc: Justin Matthews; Byron Campen; Adam Roach; resiprocate-devel
Subject: Re: [reSIProcate] Timers: why system time?

I think a solution that does not depend on how often getTimeMs is
called is preferred, given that it doesn't have a huge hit on
performance.  This makes the Timer class more usable as a general
utiltiy fn, if used outside of the stack.

Scott

On Wed, Nov 5, 2008 at 2:26 PM, Alexander Altshuler <alt@xxxxxxxxx> wrote:
> Hi All
>
> The requirements for offered Win32 timer wrap around problem:
> Timer::getTimeMs() must be reasonable often called.
>
> The solution:
> - redefine SipStack::getTimeTillNextProcessMS() that it will return
> 0xfff maximum for example.
>
> PS: Timer::msTillNextWrapAroundTimer() stuff I posted today will be
> unnecessary.
>
> Regards
> Alexander Altshuler
> http://xeepe.com
>
> _______________________________________________
> resiprocate-devel mailing list
> resiprocate-devel@xxxxxxxxxxxxxxx
> https://list.resiprocate.org/mailman/listinfo/resiprocate-devel
>
#include "rutil/Socket.hxx"

#if defined( WIN32 )
#  include <windows.h>
#  include <winbase.h>
#if (_MSC_VER >= 1400) //no atomic intrinsics on Visual Studio 2003 and below
#include <intrin.h>
#pragma intrinsic(_InterlockedCompareExchange64)
#endif
#else
#  include <sys/time.h>
#  include <sys/syscall.h>
#  include <unistd.h>
#endif

#include <cassert>

#include "rutil/Timer.hxx"
#include "rutil/Logger.hxx"
#include "rutil/Random.hxx"

#define RESIPROCATE_SUBSYSTEM Subsystem::SIP

using namespace resip;
 
unsigned long
resip::Timer::mTimerCount = 1L;


unsigned long
resip::Timer::T1 = 500;

unsigned long
resip::Timer::T2 = 8 * T1;

unsigned long
resip::Timer::T4 = 10 * T1;

unsigned long
resip::Timer::T100 = 80;

unsigned long
resip::Timer::TB = 64*T1;

unsigned long
resip::Timer::TD = 32000;

unsigned long
resip::Timer::TC = 3*60*1000;

unsigned long
resip::Timer::TF = 64*T1;

unsigned long
resip::Timer::TH = 64*T1;

unsigned long
resip::Timer::TS = 32000;


#define RESIPROCATE_SUBSYSTEM Subsystem::SIP


Data
Timer::toData(Type timer)
{
   switch (timer)
   {
      case TimerA: // doubling
         return "Timer A";
      case TimerB:
         return "Timer B";
      case TimerC:
         return "Timer C";
      case TimerD:
         return "Timer D";
      case TimerE1:
         return "Timer E1";
      case TimerE2:
         return "Timer E2";
      case TimerF:
         return "Timer F";
      case TimerG: 
         return "Timer G";
      case TimerH:
         return "Timer H";
      case TimerI:
         return "Timer I";
      case TimerJ:
         return "Timer J";
      case TimerK:
         return "Timer K";
      case TimerTrying:
         return "Timer Trying";
      case TimerStaleClient:
         return "Timer StaleClient";
      case TimerStaleServer:
         return "Timer StaleServer";
      case TimerStateless:
         return "Timer Stateless";
      case TimerCleanUp:
         return "Timer Cleanup";
      default:
         assert(0);
   }
   return "Bad Bad Bad in timer";
}


Timer::Timer(unsigned long tms, Timer::Type type, const Data& transactionId) :
    mWhen(tms + getTimeMs()),
    mId(++mTimerCount),
    mType(type),
    mTransactionId(transactionId),
    mDuration(tms),
    mMessage(0)
{
}

Timer::Timer(unsigned long tms, Message* message) 
   : mWhen(tms + getTimeMs()),
     mId(++mTimerCount),
     mType(Timer::ApplicationTimer),
     mTransactionId(),
     mDuration(tms),
     mMessage(message)
{
   assert(mMessage);
}

Timer::Timer(unsigned long tms) :
    mWhen(tms + getTimeMs()),
    mId(0),
    mDuration(tms),
    mMessage(0)
{
}

Timer::Timer(const Timer& other) : 
    mWhen(other.mWhen),
    mId(other.mTimerCount),
    mType(other.mType),
    mTransactionId(other.mTransactionId),
    mDuration(other.mDuration),
    mMessage(other.mMessage)
{
}

Timer&
Timer::operator=(const Timer& other)
{
    if (this != &other)
    {
        mWhen = other.mWhen;
        mId = other.mTimerCount;
        mType = other.mType;
        mTransactionId = other.mTransactionId;
        mDuration = other.mDuration;
        mMessage = other.mMessage;
    }
    return *this;
}

Timer::~Timer() 
{}

//#define _RESIP_WINDOWS_MONOTONIC_CLOCK

#if defined(WIN32) && defined(_RESIP_WINDOWS_MONOTONIC_CLOCK)
unsigned Timer::mMaxSystemTimeWaitMs = 60000; //set low initially, may get 
adjusted by actual timer chosen
#else 
unsigned Timer::mMaxSystemTimeWaitMs = UINT_MAX;
#endif

#ifdef WIN32
Timer::WinMonoClock::PGTC64 Timer::WinMonoClock::mGTC64 = 
&Timer::WinMonoClock::GTCLockDuringRange::GTC64;

Timer::WinMonoClock::WinMonoClock()
{     
   static Mutex mtxStart;
        static volatile bool gtc64Test=false;
   PGTC64 Gtc64 = 0;   

   Lock lock(mtxStart);

   if (!gtc64Test)
        {    
      gtc64Test = true;

                HMODULE hm = ::LoadLibrary("Kernel32");
                
                if (hm != NULL)
                {
                        Gtc64 = (PGTC64)::GetProcAddress(hm,"GetTickCount64");
                }

      if (Gtc64)
           {
                   mGTC64 = Gtc64;
           }
      else
      {    
         //uncomment for GTCInterlocked
         /*
         Timer::mMaxSystemTimeWaitMs  = GTCInterlocked::::GetMaxWaitMs();
         mGTC64 = (PGTC64)&GTCInterlocked::GTC64;
         */

         //default to GTCLockDuringRange
         Timer::mMaxSystemTimeWaitMs  = GTCLockDuringRange::GetMaxWaitMs();
         mGTC64 = (PGTC64)&GTCLockDuringRange::GTC64;
      }         
        }       
}

#define IS_ALIGNED(_pointer, _alignment) ((((ULONG_PTR) (_pointer)) & 
((_alignment) - 1)) == 0) 

UInt64 Timer::WinMonoClock::GTCInterlocked::GTC64(void)
{       
#if (_MSC_VER >= 1400) //no atomic intrinsics on Visual Studio 2003 and below
        ULARGE_INTEGER timeVal;

   assert(IS_ALIGNED(&mBaseTime,8)); //if the implementation ever changes to 
use 64-bit atomic read/write then 64-bit alignment will be required.

   //InterlockedCompareExchange64 will issue a LOCK CMPXCHG8B to ensure atomic 
access to mBaseTime
   //Not the most efficient wat to do a 64-bit atomic read (see fild 
instruction), but no intrinsic for fild.
   timeVal.QuadPart = _InterlockedCompareExchange64((LONGLONG volatile 
*)&mBaseTime,0,0);

   DWORD tickNow = ::GetTickCount();

        if (tickNow != timeVal.LowPart)
        {
      //the difference in the low 32-bits and the current 32-bit GetTickCount 
will be the time difference from
      //the base time till now.  Integer arithmentic will correctly handle 
cases where tickNow < timeVal.LowPart (rollover).
      //This diff cannot be greater than 0xFFFFFFFF, so this function must be 
called more frequently than once 
      //every 49.7 days.

                DWORD diff = tickNow - timeVal.LowPart;         

      //periodically update the basetime, only really necessary at least once 
every 49.7 days.
                if (diff > mBaseTimeUpdateInterval)
                {         
         ULARGE_INTEGER newVal;

         newVal.QuadPart = timeVal.QuadPart + diff; //any 32-bit rollover is 
now part of the high 32-bits.

         //don't care if this CAS64 actually writes mBaseTime, as long as at 
least 1 thread updates mBaseTime.
         _InterlockedCompareExchange64((LONGLONG volatile 
*)&mBaseTime,(LONGLONG)newVal.QuadPart,(LONGLONG)timeVal.QuadPart);
                }
      
      timeVal.QuadPart += diff; //any 32-bit rollover is now part of the high 
32-bits.     
   }

        return timeVal.QuadPart;
#else
   assert(0); //this counter only compiles on Visual Studio 2005 + 
   return GTCLockDuringRange::GTC64();
#endif //#if (_MSC_VER >= 1400) //no atomic intrinsics on Visual Studio 2003 
and below
}

volatile UInt64 Timer::WinMonoClock::GTCInterlocked::mBaseTime = 0;

UInt64 
Timer::WinMonoClock::GTCLock::GTC64(void)
{   
   ULARGE_INTEGER ret;

   Lock lock(mMutex);

   DWORD tickNow = ::GetTickCount();

        if (tickNow != mPrevTick)
   {
      if (mPrevTick > tickNow)
           {
                   mWrapCounter++;
           }
           mPrevTick = tickNow;
   }

        ret.HighPart = mWrapCounter;
        ret.LowPart = tickNow;

   return (UInt64)ret.QuadPart;
}

UInt32 Timer::WinMonoClock::GTCLock::mWrapCounter=0;
DWORD Timer::WinMonoClock::GTCLock::mPrevTick=0;
Mutex Timer::WinMonoClock::GTCLock::mMutex;

UInt64 Timer::WinMonoClock::GTCLockDuringRange::GTC64(void)
{
   // we assume that this function is called reasonable often
        // monitor wrap around only in dangerous time range:
        // empiric constants
        const DWORD TIMER_BEGIN_SAFE_RANGE = 0xffff; // about one minute after
        const DWORD TIMER_END_SAFE_RANGE = 0xffff0000; // and before wrap around

        // may be replaced with timeGetTime() for more accuracy
        // but it will require timeBeginPeriod(),timeEndPeriod() and link with 
Winmm.lib 
        DWORD tick = ::GetTickCount();  

        if( ( tick > TIMER_BEGIN_SAFE_RANGE ) && ( tick < TIMER_END_SAFE_RANGE 
) )
        {
                LARGE_INTEGER ret;
                ret.HighPart = mWrapCounter;
                ret.LowPart = tick;
                return (UInt64)ret.QuadPart;
        }
        // most application will never be here - only long running servers
        Lock lock( mWrapCounterMutex );
        if( mPrevTick > tick )
        {
                mWrapCounter++;
        }
        mPrevTick = tick;
        LARGE_INTEGER ret;
        ret.HighPart = mWrapCounter;
        ret.LowPart = tick;
        return (UInt64)ret.QuadPart;

}

UInt32   Timer::WinMonoClock::GTCLockDuringRange::mWrapCounter = 0;
DWORD    Timer::WinMonoClock::GTCLockDuringRange::mPrevTick = 0;
Mutex    Timer::WinMonoClock::GTCLockDuringRange::mWrapCounterMutex;
#endif

UInt64
Timer::getSystemTime()
{
   assert(sizeof(UInt64) == 64/8);

#if defined(WIN32) || defined(UNDER_CE)
#ifdef _RESIP_WINDOWS_MONOTONIC_CLOCK
   static Timer::WinMonoClock clockInit;
   return WinMonoClock::GetClock64() * 1000;
#else
   FILETIME ft;

#ifdef UNDER_CE
   SYSTEMTIME st;
   ::GetSystemTime(&st);
   ::SystemTimeToFileTime(&st,&ft);
#else
   ::GetSystemTimeAsFileTime(&ft);
#endif

   ULARGE_INTEGER li;
   li.LowPart = ft.dwLowDateTime;
   li.HighPart = ft.dwHighDateTime;
   return li.QuadPart/10;

#endif //_RESIP_WINDOWS_MONOTONIC_CLOCK

#else //#if defined(WIN32) || defined(UNDER_CE)

   UInt64 time=0;
#ifdef _RESIP_POSIX_MONOTONIC_CLOCK
   struct timespec now_monotonic;
//    if( clock_gettime( CLOCK_MONOTONIC, &now_monotonic ) == 0 )
   if( syscall( __NR_clock_gettime, CLOCK_MONOTONIC, &now_monotonic ) == 0 )
   {
      time = now_monotonic.tv_sec;
      time = time*1000000;
      time += now_monotonic.tv_nsec/1000;
      return time;
   }
#endif
   struct timeval now;
   gettimeofday( &now , NULL );
   //assert( now );
   time = now.tv_sec;
   time = time*1000000;
   time += now.tv_usec;
   return time;
#endif
}


void 
Timer::setupTimeOffsets()
{
}


UInt64 
Timer::getTimeMicroSec()
{
    return getSystemTime();
}


UInt64 
Timer::getTimeMs()
{
    return getSystemTime()/1000LL;
}

UInt64 
Timer::getTimeSecs()
{
    return getSystemTime()/1000000LL;
}


UInt64 
Timer::getForever()
{
    assert( sizeof(UInt64) == 8 );
#if defined(WIN32) && !defined(__GNUC__)
    return 18446744073709551615ui64;
#else
    return 18446744073709551615ULL;
#endif
}


UInt64 
Timer::getRandomFutureTimeMs( UInt64 futureMs )
{
    UInt64 now = getTimeMs();
   
    // make r a random number between 5000 and 9000 
    int r = Random::getRandom()%4000;
    r += 5000;
   
    UInt64 ret = now;
    ret += (futureMs*r)/10000;

    assert( ret >= now );
    assert( ret >= now+(futureMs/2) );
    assert( ret <= now+futureMs );

    return ret;
}


bool 
resip::operator<(const Timer& t1, const Timer& t2)
{
    //std::cerr << "operator(<" << t1.mWhen << ", " << t2.mWhen << ") = " << 
(t1.mWhen < t2.mWhen) << std::endl;
    return t1.mWhen < t2.mWhen;
}

bool 
resip::operator>(const Timer& t1, const Timer& t2)
{
    return t1.mWhen > t2.mWhen;
}

#ifndef RESIP_USE_STL_STREAMS
std::ostream& 
resip::operator<<(std::ostream& str, const Timer& t)
{
   UInt64 now = Timer::getTimeMs();

   str << "Timer[id=" << t.mId << " when=" << t.mWhen << " rel=";
   if (t.mWhen < now)
   {
      str << "past";
   }
   else
   {
      str << (t.mWhen - now);
   }
   str << "]";
   return str;
}
#endif
EncodeStream& 
resip::operator<<(EncodeStream& str, const Timer& t)
{
   UInt64 now = Timer::getTimeMs();

   str << "Timer[id=" << t.mId << " when=" << t.mWhen << " rel=";
   if (t.mWhen < now)
   {
      str << "past";
   }
   else
   {
      str << (t.mWhen - now);
   }
   str << "]";
   return str;
}


/* ====================================================================
 * The Vovida Software License, Version 1.0 
 * 
 * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 
 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
 *    and "Vovida Open Communication Application Library (VOCAL)" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact vocal@xxxxxxxxxxx
 *
 * 4. Products derived from this software may not be called "VOCAL", nor
 *    may "VOCAL" appear in their name, without prior written
 *    permission of Vovida Networks, Inc.
 * 
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
 * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 * 
 * ====================================================================
 * 
 * This software consists of voluntary contributions made by Vovida
 * Networks, Inc. and many individuals on behalf of Vovida Networks,
 * Inc.  For more information on Vovida Networks, Inc., please see
 * <http://www.vovida.org/>.
 *
 */
#if !defined(RESIP_TIMER_HXX)
#define RESIP_TIMER_HXX 
     
#include "rutil/Data.hxx"
#include "rutil/HeapInstanceCounter.hxx"
#include "rutil/Mutex.hxx"
#include <iostream>


namespace resip
{

class Message;

/**
   @brief Instances of this class represent a SIP timer, while static functions
      in this class are used to get the current system time.

   @note Should we refactor this? It seems like the SIP-timer stuff should live
      in resip/stack somewhere.
*/
class Timer
{
   public:
      RESIP_HeapCount(Timer);
      typedef unsigned long Id;
      typedef enum 
      {
         TimerA, // doubling
         TimerB,
         TimerC,
         TimerD,
         TimerE1,// doubling
         TimerE2,// doubling
         TimerF,
         TimerG, // doubling
         TimerH,
         TimerI,
         TimerJ,
         TimerK,
         TimerTrying,
         TimerStaleClient,
         TimerStaleServer,
         TimerStateless,
         TimerCleanUp,
         ApplicationTimer // .dlb. Fifo, so no thread issues
      } Type;
      
      static Data toData(Type timer);

      Timer(unsigned long ms, Type type, const Data& transactionId);
      Timer(unsigned long ms, Message* message);
      Timer(const Timer& t);
      Timer& operator=(const Timer& t);

      ~Timer();

      // returns the unique identifier
      Id getId() const { return mId; }
      Type getType() const { return mType; }
      // return the message to queue, possibly null
      Message* getMessage() const { return mMessage;}

      static void setupTimeOffsets(); // initialize
      static UInt64 getTimeMicroSec(); // get a 64 bit time
      static UInt64 getTimeMs(); // in ms
      static UInt64 getTimeSecs(); // in secs

      /// returns an absolute time in ms that is between 50% and 90% of
      /// passed in ms from now 
      static UInt64 getRandomFutureTimeMs( UInt64 futureMs ); // in ms
      static UInt64 getForever(); //infinit time in future

      // These values can be changed but it is not recommended to do so after a
      // stack is up and running since they are not mutexed
      // These times are all in ms 
      static unsigned long T1;
      static unsigned long T2;
      static unsigned long T4;
      static unsigned long T100;
      
      static unsigned long TB; // default 64*T1
      static unsigned long TC; 
      static unsigned long TF; // default 64*T1
      static unsigned long TH; // default 64*T1
      
      static unsigned long TD;
      static unsigned long TS;

       /** On windows, change getSystemTime() to not use the actual clock time
          returned by ::GetSystemTime().  The replacement time function 
(GetTickCount()) returns a 32-bit value, so it will rollover at some point.
          Any code that waits on timers should not wait longer than this value.
          @see resiprocate.org devlist discussion "Timers: why system time?";
       */
      static unsigned mMaxSystemTimeWaitMs;
      
   private:
      Timer(unsigned long ms); // for TimerQueue only - don't use

#ifdef WIN32     
   private:
      /** Responsible for returning a 64-bit monotonic clock value for timing.  
Currently implemented using
        * GetTickCount or GetTickCount64 (on Vista & Server 2008). Precision is 
milliseconds, 
        * accuracy is limited to ::GetTickCount/::GetTickCount64 which is 
~15-50ms, but can vary 
        * with CPU load. GetTickCount values in the ballpark of 100ms have been 
seen under load.
        */
      class WinMonoClock
      {
         public:
         WinMonoClock();

         /** Returns a monotonic clock value in milliseconds.  Currently this 
is the system uptime as reported
           * by GetTickCount/GetTickCount64
           */
         static UInt64 GetClock64(void)
         {
            return mGTC64();
         }

         private:
         
         typedef UInt64 (*PGTC64)(void);

         /** Get Tick Count wrapper for 32-bit version of ::GetTickCount that 
is nearly lockless and handles 32-bit wraparound.
           * _InterlockedExchange64 is used, which requires the CMPXCHG8B 
instruction.  This instruction is found
           * on pentium and later intel processors and K5 and later AMD 
processors.
         */
         class GTCInterlocked
         {
            public:

            static UInt64 GTC64(void);

            /** The maximum time that can elapse when using GTC64.  
            */
            static UInt32 GetMaxWaitMs(void)
            {
               //Since the base time isn't updated on every call, need to 
ensure that it's updated once every 49.7 days.
               //The base time will lag behind the current tick count, which 
means the lag time must be used 
               //to determine the max wait.
               //Also need to add a cushion to this calculaton because 
::GetTickCount is not accurate to 1ms.               
               __int64 maxWait = (__int64)UINT_MAX - mBaseTimeUpdateInterval - 
mBaseTimeCushion;
               if (maxWait <= 0)
               {
                  assert(0);
                  const_cast<UInt32 &>(mBaseTimeUpdateInterval) = 60000;
                  const_cast<UInt32 &>(mBaseTimeCushion) = 120000;
                  return UINT_MAX - mBaseTimeUpdateInterval - mBaseTimeCushion;
               }               
               return static_cast<UInt32>(maxWait);
            }

         private:
            /** Last stored time. Using InterlockedExchange (CMPXCHG8B) the 
alignment is not necessary, but it shouldn't hurt.
                Align it on a cache line since it is rarely written and read 
often (to avoid false-sharing).
            */
            static _declspec(align(128)) volatile UInt64 mBaseTime;
            /** Max elapsed time since last GTC64 call, in milliseconds, before 
writing mBaseTime.
                Cannot exceed UINT_MAX - mBaseTimeCushion.
            */
            static const UInt32 mBaseTimeUpdateInterval = 60000; 
            static const UInt32 mBaseTimeCushion = 120000; //!< large cushion 
to be cautious
         };

         /** Get Tick Count wrapper for 32-bit version of ::GetTickCount that 
minimizes locking and handles 32-bit wraparound.
           * Issues a mutex lock only during a 2 minute window around the 49.7 
day threshold.  The lock is issued for each call to
           * GTC64 during this window and durinng this window only.
           * Requires SipStack::getTimeTillNextProcessMS() to not return a 
value greater than 2 minutes.
         */
         class GTCLockDuringRange
         {
            public:

               static UInt64 GTC64(void);

               static UInt32 GetMaxWaitMs(void) 
               {
                  return 120000;
               }

            private:
              /** GetTickCounter() and timeGetTime() return DWORD - ms since 
system start
                     Therefore, the time will wrap around to zero if the system 
is run continuously for 49.7 days
                    if timer is called reasonable often we may manage wrap 
around by counter below
               */
                   static UInt32 mWrapCounter;

                   /** Last obtained tick to detect the need to increment 
mWrapCounter
              */
                   static DWORD mPrevTick;

                   /** we have to made it thread safe
              */
                   static Mutex mWrapCounterMutex;

         };

         /** Get Tick Count wrapper for 32-bit version of ::GetTickCount that 
locks on a mutex on every call to GTC64() 
             to safely handle 32-bit wraparound.
         */
         class GTCLock 
         {
            public:
               static UInt64 GTC64(void);

            private:
               static UInt32 mWrapCounter; //!< The number of times 
GetTickCount() has wrapped around it's 32-bit maximum value.
               static DWORD mPrevTick; //!< Last value returned from 
::GetTickCount() from a call to GTC64.

              /** Primary lock that is executed on each call to GTC64().
              */
              static Mutex mMutex;
         };
         
         static PGTC64 mGTC64;
      };      
#endif
      static UInt64 getSystemTime();

      UInt64 mWhen; // time when the timer "goes off" in MS 
      Id mId;
      Type mType;
      Data mTransactionId;
      unsigned long mDuration; // duration of time in ms 
      Message* mMessage; // message to queue on timeout

      static unsigned long mTimerCount; // counter to genreate unique timers 
ids 

      friend bool operator<(const Timer& t1, const Timer& t2);
      friend bool operator>(const Timer& t1, const Timer& t2);
#ifndef RESIP_USE_STL_STREAMS
          friend std::ostream& operator<<(std::ostream&, const Timer&);
#endif
      friend EncodeStream& operator<<(EncodeStream&, const Timer&);
      friend class BaseTimeLimitTimerQueue;
      friend class BaseTimerQueue;
      friend class DtlsTimerQueue;
      friend class TimeLimitTimerQueue;
      friend class TimerQueue;
      friend class TuSelectorTimerQueue;
};
 
EncodeStream& operator<<(EncodeStream&, const Timer&);
bool operator<(const Timer& t1, const Timer& t2);
bool operator>(const Timer& t1, const Timer& t2);

}

#if 0
Timer    Value            Section               Meaning
----------------------------------------------------------------------
T1       500ms default    Section 17.1.1.1     RTT Estimate
T2       4s               Section 17.1.2.2     The maximum retransmit
                                               interval for non-INVITE
                                               requests and INVITE
                                               responses
T4       5s               Section 17.1.2.2     Maximum duration a
                                               message will
                                               remain in the network
Timer A  initially T1     Section 17.1.1.2     INVITE request retransmit
                                               interval, for UDP only
Timer B  64*T1            Section 17.1.1.2     INVITE transaction
                                               timeout timer
Timer C  > 3min           Section 16.6         proxy INVITE transaction
                           bullet 11            timeout
Timer D  > 32s for UDP    Section 17.1.1.2     Wait time for response
         0s for TCP/SCTP                       retransmits
Timer E  initially T1     Section 17.1.2.2     non-INVITE request
                                               retransmit interval,
                                               UDP only
Timer F  64*T1            Section 17.1.2.2     non-INVITE transaction
                                               timeout timer
Timer G  initially T1     Section 17.2.1       INVITE response
                                               retransmit interval
Timer H  64*T1            Section 17.2.1       Wait time for
                                               ACK receipt
Timer I  T4 for UDP       Section 17.2.1       Wait time for
         0s for TCP/SCTP                       ACK retransmits
Timer J  64*T1 for UDP    Section 17.2.2       Wait time for
         0s for TCP/SCTP                       non-INVITE request
                                               retransmits
Timer K  T4 for UDP       Section 17.1.2.2     Wait time for
         0s for TCP/SCTP                       response retransmits

                   Table 4: Summary of timers
#endif

#endif

/* ====================================================================
 * The Vovida Software License, Version 1.0 
 * 
 * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 
 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
 *    and "Vovida Open Communication Application Library (VOCAL)" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact vocal@xxxxxxxxxxx
 *
 * 4. Products derived from this software may not be called "VOCAL", nor
 *    may "VOCAL" appear in their name, without prior written
 *    permission of Vovida Networks, Inc.
 * 
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
 * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 * 
 * ====================================================================
 * 
 * This software consists of voluntary contributions made by Vovida
 * Networks, Inc. and many individuals on behalf of Vovida Networks,
 * Inc.  For more information on Vovida Networks, Inc., please see
 * <http://www.vovida.org/>.
 *
 */
#if defined(HAVE_CONFIG_H)
#include "resip/stack/config.hxx"
#endif

#ifndef WIN32
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#endif

#include "rutil/compat.hxx"
#include "rutil/Data.hxx"
#include "rutil/Fifo.hxx"
#include "rutil/Logger.hxx"
#include "rutil/Random.hxx"
#include "rutil/Socket.hxx"
#include "rutil/Timer.hxx"

#include "resip/stack/Message.hxx"
#include "resip/stack/ShutdownMessage.hxx"
#include "resip/stack/SipMessage.hxx"
#include "resip/stack/ApplicationMessage.hxx"
#include "resip/stack/SipStack.hxx"
#include "rutil/Inserter.hxx"
#include "resip/stack/StatisticsManager.hxx"
#include "rutil/AsyncProcessHandler.hxx"
#include "resip/stack/TcpTransport.hxx"
#include "resip/stack/UdpTransport.hxx"
#include "resip/stack/TransactionUser.hxx"
#include "resip/stack/TransactionUserMessage.hxx"
#include "rutil/WinLeakCheck.hxx"

#ifdef USE_SSL
#include "resip/stack/ssl/Security.hxx"
#include "resip/stack/ssl/DtlsTransport.hxx"
#include "resip/stack/ssl/TlsTransport.hxx"
#endif

#if defined(WIN32) && !defined(__GNUC__)
#pragma warning( disable : 4355 )
#endif

using namespace resip;

#define RESIPROCATE_SUBSYSTEM Subsystem::SIP

SipStack::SipStack(Security* pSecurity, 
                   const DnsStub::NameserverList& additional,
                   AsyncProcessHandler* handler, 
                   bool stateless,
                   AfterSocketCreationFuncPtr socketFunc,
                   Compression *compression
   ) : 
#ifdef USE_SSL
   mSecurity( pSecurity ? pSecurity : new Security()),
#else
   mSecurity(0),
#endif
   mDnsStub(new DnsStub(additional, socketFunc, handler)),
   mCompression(compression ? compression : new Compression(Compression::NONE)),
   mAsyncProcessHandler(handler),
   mTUFifo(TransactionController::MaxTUFifoTimeDepthSecs,
           TransactionController::MaxTUFifoSize),
   mAppTimers(mTuSelector),
   mStatsManager(*this),
   mTransactionController(*this),
   mShuttingDown(false),
   mStatisticsManagerEnabled(true),
   mTuSelector(mTUFifo),
   mSocketFunc(socketFunc)
{
   Timer::getTimeMs(); // initalize time offsets
   Random::initialize();
   initNetwork();
   if (pSecurity)
   {
#ifdef USE_SSL
      pSecurity->preload();
#else
      assert(0);
#endif
   }

   assert(!mShuttingDown);
}

SipStack::~SipStack()
{
   DebugLog (<< "SipStack::~SipStack()");
#ifdef USE_SSL
   delete mSecurity;
#endif
   delete mCompression;
   delete mDnsStub;
}

void
SipStack::shutdown()
{
   InfoLog (<< "Shutting down sip stack " << this);

   static Mutex shutDownMutex;
   {
      Lock lock(shutDownMutex);
      assert(!mShuttingDown);
      mShuttingDown = true;
   }

   mTransactionController.shutdown();
}

Transport*
SipStack::addTransport( TransportType protocol,
                        int port, 
                        IpVersion version,
                        StunSetting stun,
                        const Data& ipInterface, 
                        const Data& sipDomainname,
                        const Data& privateKeyPassPhrase,
                        SecurityTypes::SSLType sslType)
{
   assert(!mShuttingDown);
   assert( port >  0 );
   InternalTransport* transport=0;
   Fifo<TransactionMessage>& stateMacFifo = 
mTransactionController.transportSelector().stateMacFifo();   
   try
   {
      switch (protocol)
      {
         case UDP:
            transport = new UdpTransport(stateMacFifo, port, version, stun, 
ipInterface, mSocketFunc, *mCompression);
            break;
         case TCP:
            transport = new TcpTransport(stateMacFifo, port, version, 
ipInterface, *mCompression);
            break;
         case TLS:
#if defined( USE_SSL )
            transport = new TlsTransport(stateMacFifo,
                                         port,
                                         version,
                                         ipInterface,
                                         *mSecurity,
                                         sipDomainname,
                                         sslType, 
                                         *mCompression);
#else
            CritLog (<< "TLS not supported in this stack. You don't have 
openssl");
            assert(0);
#endif
            break;
         case DTLS:
#if defined( USE_DTLS )
            transport = new DtlsTransport(stateMacFifo,
                                          port,
                                          version, // !jf! stun
                                          ipInterface,
                                          *mSecurity,
                                          sipDomainname,
                                          *mCompression);
#else
            CritLog (<< "DTLS not supported in this stack.");
            assert(0);
#endif
            break;
         default:
            assert(0);
            break;
      }
   }
   catch (Transport::Exception& )
   {
      ErrLog(<< "Failed to create transport: "
             << (version == V4 ? "V4" : "V6") << " "
             << Tuple::toData(protocol) << " " << port << " on "
             << (ipInterface.empty() ? "ANY" : ipInterface.c_str()));
      throw;
   }
   addTransport(std::auto_ptr<Transport>(transport));   
   return transport;
}

void 
SipStack::addTransport(std::auto_ptr<Transport> transport)
{
   //.dcm. once addTransport starts throwing, need to back out alias
   if (!transport->interfaceName().empty()) 
   {
      addAlias(transport->interfaceName(), transport->port());
   }
   mPorts.insert(transport->port());
   mTransactionController.transportSelector().addTransport(transport);
}

Fifo<TransactionMessage>& 
SipStack::stateMacFifo()
{
   return mTransactionController.transportSelector().stateMacFifo();
}

void
SipStack::addAlias(const Data& domain, int port)
{
   int portToUse = (port == 0) ? Symbols::DefaultSipPort : port;
   
   DebugLog (<< "Adding domain alias: " << domain << ":" << portToUse);
   assert(!mShuttingDown);
   mDomains.insert(domain + ":" + Data(portToUse));
}

Data 
SipStack::getHostname()
{
   // if you change this, please #def old version for windows 
   char hostName[1024];
   int err =  gethostname( hostName, sizeof(hostName) );
   assert( err == 0 );
   
   struct hostent* hostEnt = gethostbyname( hostName );
   if ( !hostEnt )
   {
      // this can fail when there is no name server 
      // !cj! - need to decided what error to return 
      ErrLog( << "gethostbyname failed - name server is probably down" );
      return "localhost";
   }
   assert( hostEnt );
   
   struct in_addr* addr = (struct in_addr*) hostEnt->h_addr_list[0];
   assert( addr );
   
   // if you change this, please #def old version for windows 
   char* addrA = inet_ntoa( *addr );
   Data ret(addrA);

   Data retHost( hostEnt->h_name );
      
   return retHost;
}


Data 
SipStack::getHostAddress()
{
   // if you change this, please #def old version for windows 
   char hostName[1024];
   int err =  gethostname( hostName, sizeof(hostName) );
   assert( err == 0 );
   
   struct hostent* hostEnt = gethostbyname( hostName );
   assert( hostEnt );
   
   struct in_addr* addr = (struct in_addr*) hostEnt->h_addr_list[0];
   assert( addr );
   
   // if you change this, please #def old version for windows 
   char* addrA = inet_ntoa( *addr );
   Data ret(addrA);

   //Data retHost( hostEnt->h_name );
      
   return ret;
}


bool 
SipStack::isMyDomain(const Data& domain, int port) const
{
   return (mDomains.count(domain + ":" + 
                          Data(port == 0 ? Symbols::DefaultSipPort : port)) != 
0);
}

bool
SipStack::isMyPort(int port) const
{
   return mPorts.count(port) != 0;
}

const Uri&
SipStack::getUri() const
{
   if (mDomains.empty())
   {
      CritLog(<< "There are no associated transports");
      throw Exception("No associated transports", __FILE__, __LINE__);
   }

   static Uri myUri("sip:" + *mDomains.begin());

   return myUri;
}

void 
SipStack::send(const SipMessage& msg, TransactionUser* tu)
{
   DebugLog (<< "SEND: " << msg.brief());
   //DebugLog (<< msg);
   //assert(!mShuttingDown);
   
   SipMessage* toSend = new SipMessage(msg);
   if (tu) 
   {
      toSend->setTransactionUser(tu);
   }         
   toSend->setFromTU();

   mTransactionController.send(toSend);
   checkAsyncProcessHandler();
}

void
SipStack::send(std::auto_ptr<SipMessage> msg, TransactionUser* tu)
{
   DebugLog (<< "SEND: " << msg->brief());
   
   if (tu) 
   {
      msg->setTransactionUser(tu);
   }         
   msg->setFromTU();

   mTransactionController.send(msg.release());
   checkAsyncProcessHandler();
}

void
SipStack::sendTo(std::auto_ptr<SipMessage> msg, const Uri& uri, 
TransactionUser* tu)
{
   if (tu) msg->setTransactionUser(tu);
   msg->setForceTarget(uri);
   msg->setFromTU();

   mTransactionController.send(msg.release());
   checkAsyncProcessHandler();
}

void 
SipStack::sendTo(std::auto_ptr<SipMessage> msg, const Tuple& destination, 
TransactionUser* tu)
{
   assert(!mShuttingDown);
   
   if (tu) msg->setTransactionUser(tu);
   msg->setDestination(destination);
   msg->setFromTU();

   mTransactionController.send(msg.release());
   checkAsyncProcessHandler();
}

// this is only if you want to send to a destination not in the route. You
// probably don't want to use it. 
void 
SipStack::sendTo(const SipMessage& msg, const Uri& uri, TransactionUser* tu)
{
   //assert(!mShuttingDown);

   SipMessage* toSend = new SipMessage(msg);
   if (tu) toSend->setTransactionUser(tu);
   toSend->setForceTarget(uri);
   toSend->setFromTU();

   mTransactionController.send(toSend);
   checkAsyncProcessHandler();
}

// this is only if you want to send to a destination not in the route. You
// probably don't want to use it. 
void 
SipStack::sendTo(const SipMessage& msg, const Tuple& destination, 
TransactionUser* tu)
{
   assert(!mShuttingDown);
   
   //SipMessage* toSend = new SipMessage(msg);
   SipMessage* toSend = dynamic_cast<SipMessage*>(msg.clone());
   if (tu) toSend->setTransactionUser(tu);
   toSend->setDestination(destination);
   toSend->setFromTU();

   mTransactionController.send(toSend);
   checkAsyncProcessHandler();
}

void 
SipStack::checkAsyncProcessHandler()
{
   if (mAsyncProcessHandler)
   {
      mAsyncProcessHandler->handleProcessNotification();
   }
}


void
SipStack::post(const ApplicationMessage& message)
{
   assert(!mShuttingDown);
   Message* toPost = message.clone();
   //mTUFifo.add(toPost, TimeLimitFifo<Message>::InternalElement);
   mTuSelector.add(toPost, TimeLimitFifo<Message>::InternalElement);
}

void
SipStack::post(const ApplicationMessage& message,  unsigned int secondsLater,
               TransactionUser* tu)
{
   assert(!mShuttingDown);
   postMS(message, secondsLater*1000, tu);
}

void
SipStack::postMS(const ApplicationMessage& message, unsigned int ms,
                 TransactionUser* tu)
{
   assert(!mShuttingDown);
   Message* toPost = message.clone();
   if (tu) toPost->setTransactionUser(tu);
   Lock lock(mAppTimerMutex);
   mAppTimers.add(Timer(ms, toPost));
   //.dcm. timer update rather than process cycle...optimize by checking if 
sooner
   //than current timeTillNextProcess?
   checkAsyncProcessHandler();
}

void 
SipStack::post(std::auto_ptr<ApplicationMessage> message, 
               unsigned int secondsLater,
               TransactionUser* tu)
{
   postMS(message, secondsLater*1000, tu);
}


void 
SipStack::postMS( std::auto_ptr<ApplicationMessage> message, 
                  unsigned int ms,
                  TransactionUser* tu)
{
   assert(!mShuttingDown);
   if (tu) message->setTransactionUser(tu);
   Lock lock(mAppTimerMutex);
   mAppTimers.add(Timer(ms, message.release()));
   //.dcm. timer update rather than process cycle...optimize by checking if 
sooner
   //than current timeTillNextProcess?
   checkAsyncProcessHandler();
}

void 
SipStack::abandonServerTransaction(const Data& tid)
{
   mTransactionController.abandonServerTransaction(tid);
}

void 
SipStack::cancelClientInviteTransaction(const Data& tid)
{
   mTransactionController.cancelClientInviteTransaction(tid);
}


bool
SipStack::hasMessage() const
{
   return mTUFifo.messageAvailable();
}

SipMessage* 
SipStack::receive()
{
   // Check to see if a message is available and if it is return the 
   // waiting message. Otherwise, return 0
   if (mTUFifo.messageAvailable())
   {
      // we should only ever have SIP messages on the TU Fifo
      // unless we've registered for termination messages. 
      Message* msg = mTUFifo.getNext();
      SipMessage* sip = dynamic_cast<SipMessage*>(msg);
      if (sip)
      {
         DebugLog (<< "RECV: " << sip->brief());
         return sip;
      }
      else
      {
         // assert(0); // !CJ! removed the assert - happens 1 minute after
         // stack starts up
         delete msg;
         return 0;
      }
   }
   else
   {
      return 0;
   }
}

Message*
SipStack::receiveAny()
{
   // Check to see if a message is available and if it is return the 
   // waiting message. Otherwise, return 0
   if (mTUFifo.messageAvailable())
   {
      // application messages can flow through
      Message* msg = mTUFifo.getNext();
      SipMessage* sip=dynamic_cast<SipMessage*>(msg);
      if (sip)
      {
         DebugLog (<< "RECV: " << sip->brief());
      }
      return msg;
   }
   else
   {
      return 0;
   }
}

void 
SipStack::process(FdSet& fdset)
{
   if(!mShuttingDown && mStatisticsManagerEnabled)
   {
      mStatsManager.process();
   }
   mTransactionController.process(fdset);
   mTuSelector.process();
   mDnsStub->process(fdset);
   
   Lock lock(mAppTimerMutex); 
   mAppTimers.process();
}

/// returns time in milliseconds when process next needs to be called 
unsigned int 
SipStack::getTimeTillNextProcessMS()
{
   Lock lock(mAppTimerMutex);

   unsigned int dnsNextProcess = (mDnsStub->requiresProcess() ? 200 : 
0xffffffff);
   return resipMin(Timer::mMaxSystemTimeWaitMs,resipMin(dnsNextProcess,
                   resipMin(mTransactionController.getTimeTillNextProcessMS(),
                            resipMin(mTuSelector.getTimeTillNextProcessMS(), 
mAppTimers.msTillNextTimer()))));
} 

void 
SipStack::buildFdSet(FdSet& fdset)
{
   mTransactionController.buildFdSet(fdset);
   mDnsStub->buildFdSet(fdset);
}

Security*
SipStack::getSecurity() const 
{
    return mSecurity;
}

void
SipStack::setStatisticsInterval(unsigned long seconds)
{
   mStatsManager.setInterval(seconds);
}

void 
SipStack::registerTransactionUser(TransactionUser& tu)
{
   mTuSelector.registerTransactionUser(tu);
}

void 
SipStack::requestTransactionUserShutdown(TransactionUser& tu)
{
   mTuSelector.requestTransactionUserShutdown(tu);
   checkAsyncProcessHandler();
}

void 
SipStack::unregisterTransactionUser(TransactionUser& tu)
{
   mTuSelector.unregisterTransactionUser(tu);
   checkAsyncProcessHandler();
}

void
SipStack::registerMarkListener(MarkListener* listener)
{
   mTransactionController.registerMarkListener(listener);
}

void
SipStack::unregisterMarkListener(MarkListener* listener)
{
   mTransactionController.unregisterMarkListener(listener);
}

DnsStub&
SipStack::getDnsStub() const
{
   return *mDnsStub;
}

void
SipStack::setEnumSuffixes(const std::vector<Data>& suffixes)
{
   mDnsStub->setEnumSuffixes(suffixes);
}

void
SipStack::clearDnsCache()
{
   mDnsStub->clearDnsCache();
}

void
SipStack::logDnsCache()
{
   mDnsStub->logDnsCache();
}

volatile bool& 
SipStack::statisticsManagerEnabled()
{
   return mStatisticsManagerEnabled;   
}

const bool 
SipStack::statisticsManagerEnabled() const
{
   return mStatisticsManagerEnabled;   
}

EncodeStream& 
SipStack::dump(EncodeStream& strm)  const
{
   Lock lock(mAppTimerMutex);
   strm << "SipStack: " << (this->mSecurity ? "with security " : "without 
security ")
        << std::endl
        << "domains: " << Inserter(this->mDomains)
        << std::endl
        << " TUFifo size=" << this->mTUFifo.size() << std::endl
        << " Timers size=" << this->mTransactionController.mTimers.size() << 
std::endl
        << " AppTimers size=" << this->mAppTimers.size() << std::endl
        << " ServerTransactionMap size=" << 
this->mTransactionController.mServerTransactionMap.size() << std::endl
        << " ClientTransactionMap size=" << 
this->mTransactionController.mClientTransactionMap.size() << std::endl
        << " Exact Transports=" << 
Inserter(this->mTransactionController.mTransportSelector.mExactTransports) << 
std::endl
        << " Any Transports=" << 
Inserter(this->mTransactionController.mTransportSelector.mAnyInterfaceTransports)
 << std::endl;
   return strm;
}

EncodeStream& 
resip::operator<<(EncodeStream& strm, 
const SipStack& stack) 
{
   return stack.dump(strm);
}

bool
SipStack::isFlowAlive(const resip::Tuple& flow) const
{
   return flow.getType()==UDP || 
         mTransactionController.transportSelector().connectionAlive(flow);
}

/* ====================================================================
 * The Vovida Software License, Version 1.0 
 * 
 * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 
 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
 *    and "Vovida Open Communication Application Library (VOCAL)" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact vocal@xxxxxxxxxxx
 *
 * 4. Products derived from this software may not be called "VOCAL", nor
 *    may "VOCAL" appear in their name, without prior written
 *    permission of Vovida Networks, Inc.
 * 
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
 * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 * 
 * ====================================================================
 * 
 * This software consists of voluntary contributions made by Vovida
 * Networks, Inc. and many individuals on behalf of Vovida Networks,
 * Inc.  For more information on Vovida Networks, Inc., please see
 * <http://www.vovida.org/>.
 *
 */