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)>CInterlocked::GTC64;
*/
//default to GTCLockDuringRange
Timer::mMaxSystemTimeWaitMs = GTCLockDuringRange::GetMaxWaitMs();
mGTC64 = (PGTC64)>CLockDuringRange::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/>.
*
*/