yate/engine/TelEngine.cpp

1145 lines
25 KiB
C++

/**
* TelEngine.cpp
* This file is part of the YATE Project http://YATE.null.ro
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2004-2014 Null Team
*
* This software is distributed under multiple licenses;
* see the COPYING file in the main directory for licensing
* information for this specific distribution.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#include "yateclass.h"
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#ifdef _WINDOWS
#ifndef HAVE_GMTIME_S
#include <errno.h>
static int _gmtime_s(struct tm* _tm, const time_t* time)
{
static TelEngine::Mutex m(false,"_gmtime_s");
struct tm* tmp;
if (!_tm)
return EINVAL;
_tm->tm_isdst = _tm->tm_yday = _tm->tm_wday = _tm->tm_year = _tm->tm_mon = _tm->tm_mday =
_tm->tm_hour = _tm->tm_min = _tm->tm_sec = -1;
if (!time)
return EINVAL;
m.lock();
tmp = gmtime(time);
if (!tmp) {
m.unlock();
return EINVAL;
}
*_tm = *tmp;
m.unlock();
return 0;
}
static int _localtime_s(struct tm* _tm, const time_t* time)
{
static TelEngine::Mutex m(false,"_localtime_s");
struct tm* tmp;
if (!_tm)
return EINVAL;
_tm->tm_isdst = _tm->tm_yday = _tm->tm_wday = _tm->tm_year = _tm->tm_mon = _tm->tm_mday =
_tm->tm_hour = _tm->tm_min = _tm->tm_sec = -1;
if (!time)
return EINVAL;
m.lock();
tmp = localtime(time);
if (!tmp) {
m.unlock();
return EINVAL;
}
*_tm = *tmp;
m.unlock();
return 0;
}
#endif
#else // !_WINDOWS
#include <sys/resource.h>
#endif
namespace { // anonymous
class ObjCounterList : public TelEngine::ObjList
{
public:
inline ObjCounterList()
: m_invalid(false)
{ }
~ObjCounterList()
{ m_invalid = true; }
inline bool invalid() const
{ return m_invalid; }
private:
bool m_invalid;
};
}; // anonymous namespace
namespace TelEngine {
#define DebugMin DebugFail
#define DebugVis DebugConf
#define DebugMax DebugAll
#define OUT_BUFFER_SIZE 8192
// RefObject mutex pool array size
#ifndef REFOBJECT_MUTEX_COUNT
#define REFOBJECT_MUTEX_COUNT 47
#endif
static int s_debug = DebugWarn;
static int s_indent = 0;
static bool s_debugging = true;
static bool s_abort = false;
static u_int64_t s_startTime = 0;
static u_int64_t s_timestamp = 0;
static Debugger::Formatting s_fmtstamp = Debugger::None;
static const char* const s_colors[11] = {
"\033[5;41;1;33m\033[K",// DebugFail - blinking yellow on red
"\033[44;1;37m\033[K", // DebugTest - white on blue
"\033[41;1;37m\033[K", // DebugGoOn - white on red
"\033[41;37m\033[K", // DebugConf - gray on red
"\033[40;31m\033[K", // DebugStub - red on black
"\033[40;1;31m\033[K", // DebugWarn - light red on black
"\033[40;1;33m\033[K", // DebugMild - yellow on black
"\033[40;1;37m\033[K", // DebugCall - white on black
"\033[40;1;32m\033[K", // DebugNote - light green on black
"\033[40;1;36m\033[K", // DebugInfo - light cyan on black
"\033[40;36m\033[K" // DebugAll - cyan on black
};
static const char* const s_levels[11] = {
"FAIL",
"TEST",
"GOON",
"CONF",
"STUB",
"WARN",
"MILD",
"CALL",
"NOTE",
"INFO",
"ALL",
};
static const char* dbg_level(int& level)
{
if (level < DebugMin)
level = DebugMin;
if (level > DebugMax)
level = DebugMax;
return s_levels[level];
}
static void dbg_stderr_func(const char* buf, int level)
{
::write(2,buf,::strlen(buf));
}
static void dbg_colorize_func(const char* buf, int level)
{
const char* col = debugColor(level);
::write(2,col,::strlen(col));
::write(2,buf,::strlen(buf));
col = debugColor(-2);
::write(2,col,::strlen(col));
}
static void (*s_output)(const char*,int) = dbg_stderr_func;
static void (*s_intout)(const char*,int) = 0;
static void (*s_alarms)(const char*,int,const char*,const char*) = 0;
static Mutex out_mux(false,"DebugOutput");
static Mutex ind_mux(false,"DebugIndent");
static Thread* s_thr = 0;
bool CapturedEvent::s_capturing = false;
ObjList CapturedEvent::s_events;
static bool reentered()
{
if (!s_thr)
return false;
return (Thread::current() == s_thr);
}
static void common_output(int level,char* buf)
{
if (level < -1)
level = -1;
if (level > DebugMax)
level = DebugMax;
int n = ::strlen(buf);
if (n && (buf[n-1] == '\n'))
n--;
// serialize the output strings
out_mux.lock();
// TODO: detect reentrant calls from foreign threads and main thread
s_thr = Thread::current();
if (CapturedEvent::capturing()) {
buf[n] = '\0';
bool save = s_debugging;
s_debugging = false;
CapturedEvent::append(level,buf);
s_debugging = save;
}
buf[n] = '\n';
buf[n+1] = '\0';
if (s_output)
s_output(buf,level);
if (s_intout)
s_intout(buf,level);
buf[n] = '\0';
s_thr = 0;
out_mux.unlock();
}
static void dbg_output(int level,const char* prefix, const char* format, va_list ap,
const char* alarmComp = 0, const char* alarmInfo = 0)
{
bool out = (s_output || s_intout) && (prefix || format);
bool alarm = alarmComp && format && s_alarms;
if (!(out || alarm))
return;
char buf[OUT_BUFFER_SIZE];
unsigned int n = Debugger::formatTime(buf,s_fmtstamp);
unsigned int l = s_indent*2;
if (l >= sizeof(buf)-n)
l = sizeof(buf)-n-1;
::memset(buf+n,' ',l);
n += l;
buf[n] = 0;
l = sizeof(buf)-n-2;
if (prefix)
::strncpy(buf+n,prefix,l);
n = ::strlen(buf);
l = sizeof(buf)-n-2;
char* msg = buf+n;
if (format) {
::vsnprintf(msg,l,format,ap);
buf[OUT_BUFFER_SIZE - 2] = 0;
}
if (out)
common_output(level,buf);
if (alarm) {
out_mux.lock();
if (s_alarms)
s_alarms(msg,level,alarmComp,alarmInfo);
out_mux.unlock();
}
}
void Output(const char* format, ...)
{
char buf[OUT_BUFFER_SIZE];
if (!((s_output || s_intout) && format && *format))
return;
if (reentered())
return;
va_list va;
va_start(va,format);
::vsnprintf(buf,sizeof(buf)-2,format,va);
va_end(va);
common_output(-1,buf);
}
void Debug(int level, const char* format, ...)
{
if (!s_debugging)
return;
if (level > s_debug || level < DebugMin)
return;
if (reentered())
return;
if (!format)
format = "";
char buf[32];
::sprintf(buf,"<%s> ",dbg_level(level));
va_list va;
va_start(va,format);
ind_mux.lock();
dbg_output(level,buf,format,va);
ind_mux.unlock();
va_end(va);
if (s_abort && (level == DebugFail))
abort();
}
void Debug(const char* facility, int level, const char* format, ...)
{
if (!s_debugging)
return;
if (level > s_debug || level < DebugMin)
return;
if (reentered())
return;
if (!format)
format = "";
char buf[64];
::snprintf(buf,sizeof(buf),"<%s:%s> ",facility,dbg_level(level));
va_list va;
va_start(va,format);
ind_mux.lock();
dbg_output(level,buf,format,va);
ind_mux.unlock();
va_end(va);
if (s_abort && (level == DebugFail))
abort();
}
void Debug(const DebugEnabler* local, int level, const char* format, ...)
{
if (!s_debugging)
return;
const char* facility = 0;
if (!local) {
if (level > s_debug || level < DebugMin)
return;
}
else {
if (!local->debugAt(level))
return;
facility = local->debugName();
}
if (reentered())
return;
if (!format)
format = "";
char buf[64];
if (facility)
::snprintf(buf,sizeof(buf),"<%s:%s> ",facility,dbg_level(level));
else
::sprintf(buf,"<%s> ",dbg_level(level));
va_list va;
va_start(va,format);
ind_mux.lock();
dbg_output(level,buf,format,va);
ind_mux.unlock();
va_end(va);
if (s_abort && (level == DebugFail))
abort();
}
void Alarm(const char* component, int level, const char* format, ...)
{
if (!format || level < DebugMin || reentered())
return;
if (TelEngine::null(component))
component = "unknown";
char buf[64];
::snprintf(buf,sizeof(buf),"<%s:%s> ",component,dbg_level(level));
va_list va;
va_start(va,format);
ind_mux.lock();
dbg_output(level,buf,format,va,component);
ind_mux.unlock();
va_end(va);
if (s_abort && (level == DebugFail))
abort();
}
void Alarm(const DebugEnabler* component, int level, const char* format, ...)
{
if (!format || level < DebugMin || reentered())
return;
const char* name = (component && !TelEngine::null(component->debugName()))
? component->debugName() : "unknown";
char buf[64];
::snprintf(buf,sizeof(buf),"<%s:%s> ",name,dbg_level(level));
va_list va;
va_start(va,format);
ind_mux.lock();
dbg_output(level,buf,format,va,name);
ind_mux.unlock();
va_end(va);
if (s_abort && (level == DebugFail))
abort();
}
void Alarm(const char* component, const char* info, int level, const char* format, ...)
{
if (!format || level < DebugMin || reentered())
return;
if (TelEngine::null(component))
component = "unknown";
char buf[64];
::snprintf(buf,sizeof(buf),"<%s:%s> ",component,dbg_level(level));
va_list va;
va_start(va,format);
ind_mux.lock();
dbg_output(level,buf,format,va,component,info);
ind_mux.unlock();
va_end(va);
if (s_abort && (level == DebugFail))
abort();
}
void Alarm(const DebugEnabler* component, const char* info, int level, const char* format, ...)
{
if (!format || level < DebugMin || reentered())
return;
const char* name = (component && !TelEngine::null(component->debugName()))
? component->debugName() : "unknown";
char buf[64];
::snprintf(buf,sizeof(buf),"<%s:%s> ",name,dbg_level(level));
va_list va;
va_start(va,format);
ind_mux.lock();
dbg_output(level,buf,format,va,name,info);
ind_mux.unlock();
va_end(va);
if (s_abort && (level == DebugFail))
abort();
}
void abortOnBug()
{
if (s_abort)
abort();
}
bool abortOnBug(bool doAbort)
{
bool tmp = s_abort;
s_abort = doAbort;
return tmp;
}
int debugLevel()
{
return s_debug;
}
int debugLevel(int level)
{
if (level < DebugVis)
level = DebugVis;
if (level > DebugMax)
level = DebugMax;
return (s_debug = level);
}
bool debugAt(int level)
{
return (s_debugging && (level <= s_debug));
}
const char* debugColor(int level)
{
if (level == -2)
return "\033[0m\033[K"; // reset to defaults
if ((level < DebugMin) || (level > DebugMax))
return "\033[0;40;37m\033[K"; // light gray on black
return s_colors[level];
}
const char* debugLevelName(int level)
{
return (level < DebugMin) ? "" : dbg_level(level);
}
int DebugEnabler::debugLevel(int level)
{
if (level < DebugVis)
level = DebugVis;
if (level > DebugMax)
level = DebugMax;
m_chain = 0;
return (m_level = level);
}
bool DebugEnabler::debugAt(int level) const
{
if (m_chain)
return m_chain->debugAt(level);
return (m_enabled && (level <= m_level));
}
void DebugEnabler::debugCopy(const DebugEnabler* original)
{
if (original) {
m_level = original->debugLevel();
m_enabled = original->debugEnabled();
}
else {
m_level = TelEngine::debugLevel();
m_enabled = debugEnabled();
}
m_chain = 0;
}
Debugger::Debugger(const char* name, const char* format, ...)
: m_name(name), m_level(DebugAll)
{
if (s_debugging && m_name && (s_debug >= DebugAll) && !reentered()) {
char buf[64];
::snprintf(buf,sizeof(buf),">>> %s",m_name);
va_list va;
va_start(va,format);
ind_mux.lock();
dbg_output(m_level,buf,format,va);
va_end(va);
s_indent++;
ind_mux.unlock();
}
else
m_name = 0;
}
Debugger::Debugger(int level, const char* name, const char* format, ...)
: m_name(name), m_level(level)
{
if (s_debugging && m_name && (s_debug >= level) && !reentered()) {
char buf[64];
::snprintf(buf,sizeof(buf),">>> %s",m_name);
va_list va;
va_start(va,format);
ind_mux.lock();
dbg_output(m_level,buf,format,va);
va_end(va);
s_indent++;
ind_mux.unlock();
}
else
m_name = 0;
}
static void dbg_dist_helper(int level, const char* buf, const char* fmt, ...)
{
va_list va;
va_start(va,fmt);
dbg_output(level,buf,fmt,va);
va_end(va);
}
bool controlReturn(NamedList* params, bool ret, const char* retVal)
{
if (retVal && params)
params->setParam("retVal",retVal);
if (!params || !params->getObject(YATOM("Message")))
return ret;
const String* module = params->getParam("module");
if (TelEngine::null(module) || YSTRING("rmanager") != *module)
return ret;
const String s_opStat("operation-status");
if (!params->getParam(s_opStat))
params->addParam(s_opStat,String::boolText(ret));
return ret;
}
Debugger::~Debugger()
{
if (m_name) {
ind_mux.lock();
s_indent--;
if (s_debugging)
dbg_dist_helper(m_level,"<<< ","%s",m_name);
ind_mux.unlock();
}
}
void Debugger::setOutput(void (*outFunc)(const char*,int))
{
out_mux.lock();
s_output = outFunc ? outFunc : dbg_stderr_func;
out_mux.unlock();
}
void Debugger::setIntOut(void (*outFunc)(const char*,int))
{
out_mux.lock();
s_intout = outFunc;
out_mux.unlock();
}
void Debugger::setAlarmHook(void (*alarmFunc)(const char*,int,const char*,const char*))
{
out_mux.lock();
s_alarms = alarmFunc;
out_mux.unlock();
}
void Debugger::enableOutput(bool enable, bool colorize)
{
s_debugging = enable;
if (colorize)
setOutput(dbg_colorize_func);
}
Debugger::Formatting Debugger::getFormatting()
{
return s_fmtstamp;
}
void Debugger::setFormatting(Formatting format)
{
// start stamp will be rounded to full second
s_timestamp = (Time::now() / 1000000) * 1000000;
s_fmtstamp = format;
}
unsigned int Debugger::formatTime(char* buf, Formatting format)
{
if (!buf)
return 0;
if (None != format) {
u_int64_t t = Time::now();
if (Relative == format)
t -= s_timestamp;
unsigned int s = (unsigned int)(t / 1000000);
unsigned int u = (unsigned int)(t % 1000000);
switch (format) {
case Textual:
case TextLocal:
case TextSep:
case TextLSep:
{
time_t sec = (time_t)s;
struct tm tmp;
if (TextLocal == format || TextLSep == format)
#ifdef _WINDOWS
_localtime_s(&tmp,&sec);
#else
localtime_r(&sec,&tmp);
#endif
else
#ifdef _WINDOWS
_gmtime_s(&tmp,&sec);
#else
gmtime_r(&sec,&tmp);
#endif
if (Textual == format || TextLocal == format)
::sprintf(buf,"%04d%02d%02d%02d%02d%02d.%06u ",
tmp.tm_year+1900,tmp.tm_mon+1,tmp.tm_mday,
tmp.tm_hour,tmp.tm_min,tmp.tm_sec,u);
else
::sprintf(buf,"%04d-%02d-%02d_%02d:%02d:%02d.%06u ",
tmp.tm_year+1900,tmp.tm_mon+1,tmp.tm_mday,
tmp.tm_hour,tmp.tm_min,tmp.tm_sec,u);
}
break;
default:
::sprintf(buf,"%07u.%06u ",s,u);
}
return ::strlen(buf);
}
buf[0] = '\0';
return 0;
}
u_int64_t Time::now()
{
#ifdef _WINDOWS
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
// Convert from FILETIME (100 nsec units since January 1, 1601)
// to extended time_t (1 usec units since January 1, 1970)
u_int64_t rval = ((ULARGE_INTEGER*)&ft)->QuadPart / 10;
rval -= 11644473600000000;
return rval;
#else
struct timeval tv;
return ::gettimeofday(&tv,0) ? 0 : fromTimeval(&tv);
#endif
}
u_int64_t Time::msecNow()
{
return (u_int64_t)(now() / 1000);
}
u_int32_t Time::secNow()
{
#ifdef _WINDOWS
return (u_int32_t)(now() / 1000000);
#else
struct timeval tv;
return ::gettimeofday(&tv,0) ? 0 : tv.tv_sec;
#endif
}
u_int64_t Time::fromTimeval(const struct timeval* tv)
{
u_int64_t rval = 0;
if (tv) {
// Please keep it this way or the compiler may b0rk
rval = tv->tv_sec;
rval *= 1000000;
rval += tv->tv_usec;
}
return rval;
}
void Time::toTimeval(struct timeval* tv, u_int64_t usec)
{
if (tv) {
tv->tv_usec = (long)(usec % 1000000);
tv->tv_sec = (long)(usec / 1000000);
}
}
// Build EPOCH time from date/time components
unsigned int Time::toEpoch(int year, unsigned int month, unsigned int day,
unsigned int hour, unsigned int minute, unsigned int sec, int offset)
{
DDebug(DebugAll,"Time::toEpoch(%d,%u,%u,%u,%u,%u,%d)",
year,month,day,hour,minute,sec,offset);
if (year < 1970)
return (unsigned int)-1;
if (month < 1 || month > 12 || !day)
return (unsigned int)-1;
if (hour == 24 && (minute || sec))
return (unsigned int)-1;
else if (hour > 23 || minute > 59 || sec > 59)
return (unsigned int)-1;
// Check if month and day are correct in the given year
month--;
unsigned int m[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
if (isLeap(year))
m[1] = 29;
if (day > m[month])
return (unsigned int)-1;
// Count the number of days since EPOCH
int64_t days = (year - 1970) * 365;
// Add a day for each leap year from 1970 to 'year' (not including)
for (int y = 1972; y < year; y += 4) {
if (isLeap(y))
days++;
}
// Add days ellapsed in given year
for (unsigned int i = 0; i < month; i++)
days += m[i];
days += day - 1;
int64_t ret = (days * 24 + hour) * 3600 + minute * 60 + sec + offset;
// Check for incorrect time or overflow
if (ret < 0 || ret > (unsigned int)-1)
return (unsigned int)-1;
return (unsigned int)ret;
}
// Split a given EPOCH time into its date/time components
bool Time::toDateTime(unsigned int epochTimeSec, int& year, unsigned int& month,
unsigned int& day, unsigned int& hour, unsigned int& minute, unsigned int& sec,
unsigned int* wDay)
{
#ifdef _WINDOWS
FILETIME ft;
SYSTEMTIME st;
// 11644473600: the number of seconds from 1601, January 1st (FILETIME)
// to EPOCH (1970, January 1st)
// Remember: FILETIME keeps the number of 100 nsec units
u_int64_t time = (11644473600 + epochTimeSec) * 10000000;
ft.dwLowDateTime = (DWORD)time;
ft.dwHighDateTime = (DWORD)(time >> 32);
if (!FileTimeToSystemTime(&ft,&st))
return false;
year = st.wYear;
month = st.wMonth;
day = st.wDay;
hour = st.wHour;
minute = st.wMinute;
sec = st.wSecond;
if (wDay)
*wDay = st.wDayOfWeek;
#else
struct tm t;
time_t time = (time_t)epochTimeSec;
if (!gmtime_r(&time,&t))
return false;
year = 1900 + t.tm_year;
month = t.tm_mon + 1;
day = t.tm_mday;
hour = t.tm_hour;
minute = t.tm_min;
sec = t.tm_sec;
if (wDay)
*wDay = t.tm_wday;
#endif
DDebug(DebugAll,"Time::toDateTime(%u,%d,%u,%u,%u,%u,%u)",
epochTimeSec,year,month,day,hour,minute,sec);
return true;
}
int Time::timeZone()
{
#ifdef _WINDOWS
struct tm t;
time_t time = (time_t)secNow();
_localtime_s(&t,&time);
if (t.tm_isdst)
return -(_timezone + _dstbias);
return -_timezone;
#else
#ifdef HAVE_GMTOFF
struct tm t;
time_t time = (time_t)secNow();
if (localtime_r(&time,&t))
return t.tm_gmtoff;
#endif
return -timezone;
#endif
}
static Random s_random;
static Mutex s_randomMutex(false,"Random");
u_int32_t Random::next()
{
return (m_random = (m_random + 1) * 0x8088405);
}
long int Random::random()
{
s_randomMutex.lock();
long int ret = s_random.next() % RAND_MAX;
s_randomMutex.unlock();
return ret;
}
void Random::srandom(unsigned int seed)
{
s_randomMutex.lock();
s_random.set(seed % RAND_MAX);
s_randomMutex.unlock();
}
bool GenObject::s_counting = false;
static ObjCounterList s_counters;
static Mutex s_countersMutex(false,"Counters");
GenObject::GenObject()
: m_counter(0)
{
NamedCounter* counter = Thread::getCurrentObjCounter();
if (counter && counter->enabled())
setObjCounter(counter);
}
bool GenObject::alive() const
{
return true;
}
void GenObject::destruct()
{
delete this;
}
NamedCounter* GenObject::setObjCounter(NamedCounter* counter)
{
if (counter == m_counter)
return counter;
if (s_counters.invalid())
return 0;
Lock mylock(0);
if (Mutex::count() >= 0)
mylock.acquire(s_countersMutex);
NamedCounter* oldCounter = m_counter;
if (counter != oldCounter) {
m_counter = counter;
mylock.drop();
if (counter)
counter->inc();
if (oldCounter)
oldCounter->dec();
}
return oldCounter;
}
NamedCounter* GenObject::getObjCounter(const String& name, bool create)
{
if (name.null() || s_counters.invalid())
return 0;
Lock mylock(s_countersMutex);
NamedCounter* cnt = static_cast<NamedCounter*>(s_counters[name]);
if (create && !cnt)
s_counters.append(cnt = new NamedCounter(name));
return cnt;
}
ObjList& GenObject::getObjCounters()
{
return s_counters;
}
#ifndef ATOMIC_OPS
static MutexPool s_refMutex(REFOBJECT_MUTEX_COUNT,false,"RefObject");
#endif
RefObject::RefObject()
: m_refcount(1), m_mutex(0)
{
#ifndef ATOMIC_OPS
m_mutex = s_refMutex.mutex(this);
#endif
}
RefObject::~RefObject()
{
if (m_refcount > 0)
Debug(DebugFail,"RefObject [%p] destroyed with count=%d",this,m_refcount);
}
void* RefObject::getObject(const String& name) const
{
if (name == YATOM("RefObject"))
return (void*)this;
return GenObject::getObject(name);
}
bool RefObject::alive() const
{
return m_refcount > 0;
}
void RefObject::destruct()
{
deref();
}
bool RefObject::ref()
{
#ifdef ATOMIC_OPS
#ifdef _WINDOWS
if (InterlockedIncrement((LONG*)&m_refcount) > 1)
return true;
InterlockedDecrement((LONG*)&m_refcount);
#else
if (__sync_add_and_fetch(&m_refcount,1) > 1)
return true;
__sync_sub_and_fetch(&m_refcount,1);
#endif
#else
Lock lock(m_mutex);
if (m_refcount > 0) {
++m_refcount;
return true;
}
#endif
return false;
}
bool RefObject::deref()
{
#ifdef ATOMIC_OPS
#ifdef _WINDOWS
int i = InterlockedDecrement((LONG*)&m_refcount) + 1;
if (i <= 0)
InterlockedIncrement((LONG*)&m_refcount);
#else
int i = __sync_fetch_and_sub(&m_refcount,1);
if (i <= 0)
__sync_fetch_and_add(&m_refcount,1);
#endif
#else
m_mutex->lock();
int i = m_refcount;
if (i > 0)
--m_refcount;
m_mutex->unlock();
#endif
if (i == 1)
zeroRefs();
else if (i <= 0)
Debug(DebugFail,"RefObject::deref() called with count=%d [%p]",i,this);
return (i <= 1);
}
void RefObject::zeroRefs()
{
destroyed();
delete this;
}
bool RefObject::resurrect()
{
#ifdef ATOMIC_OPS
#ifdef _WINDOWS
if (InterlockedIncrement((LONG*)&m_refcount) == 1)
return true;
InterlockedDecrement((LONG*)&m_refcount);
return false;
#else
if (__sync_add_and_fetch(&m_refcount,1) == 1)
return true;
__sync_sub_and_fetch(&m_refcount,1);
return false;
#endif
#else
m_mutex->lock();
bool ret = (0 == m_refcount);
if (ret)
m_refcount = 1;
m_mutex->unlock();
return ret;
#endif
}
void RefObject::destroyed()
{
}
bool RefObject::efficientIncDec()
{
#ifdef ATOMIC_OPS
return true;
#else
return false;
#endif
}
void RefPointerBase::assign(RefObject* oldptr, RefObject* newptr, void* pointer)
{
if (oldptr == newptr)
return;
// Always reference the new object before dereferencing the old one
// and also don't keep pointers to objects that fail referencing
m_pointer = (newptr && newptr->ref()) ? pointer : 0;
if (oldptr)
oldptr->deref();
}
NamedCounter::NamedCounter(const String& name)
: String(name), m_count(0), m_enabled(getObjCounting()), m_mutex(0)
{
#ifndef ATOMIC_OPS
m_mutex = s_refMutex.mutex(this);
#endif
}
int NamedCounter::inc()
{
#ifdef ATOMIC_OPS
#ifdef _WINDOWS
return InterlockedIncrement((LONG*)&m_count);
#else
return __sync_add_and_fetch(&m_count,1);
#endif
#else
Lock lock(m_mutex);
return ++m_count;
#endif
}
int NamedCounter::dec()
{
#ifdef ATOMIC_OPS
#ifdef _WINDOWS
return InterlockedDecrement((LONG*)&m_count);
#else
return __sync_fetch_and_sub(&m_count,1);
#endif
#else
Lock lock(m_mutex);
return --m_count;
#endif
}
void SysUsage::init()
{
if (!s_startTime)
s_startTime = Time::now();
}
u_int64_t SysUsage::startTime()
{
init();
return s_startTime;
}
u_int64_t SysUsage::usecRunTime(Type type)
{
switch (type) {
case WallTime:
return Time::now() - startTime();
case UserTime:
{
#ifdef _WINDOWS
FILETIME dummy,ft;
if (GetProcessTimes(GetCurrentProcess(),&dummy,&dummy,&dummy,&ft)) {
u_int64_t t = ft.dwLowDateTime | (((u_int64_t)ft.dwHighDateTime) << 32);
return t / 10;
}
#else
struct rusage usage;
// FIXME: this is broken, may not sum all threads
if (!::getrusage(RUSAGE_SELF,&usage))
return Time::fromTimeval(usage.ru_utime);
#endif
}
break;
case KernelTime:
{
#ifdef _WINDOWS
FILETIME dummy,ft;
if (GetProcessTimes(GetCurrentProcess(),&dummy,&dummy,&ft,&dummy)) {
u_int64_t t = ft.dwLowDateTime | (((u_int64_t)ft.dwHighDateTime) << 32);
return t / 10;
}
#else
struct rusage usage;
// FIXME: this is broken, may not sum all threads
if (!::getrusage(RUSAGE_SELF,&usage))
return Time::fromTimeval(usage.ru_stime);
#endif
}
break;
}
return 0;
}
u_int64_t SysUsage::msecRunTime(Type type)
{
return usecRunTime(type) / 1000;
}
u_int32_t SysUsage::secRunTime(Type type)
{
return (u_int32_t)(usecRunTime(type) / 1000000);
}
double SysUsage::runTime(Type type)
{
#ifdef _WINDOWS
// VC++ 6 does not implement conversion from UINT64 to double!
return 0.000001 * (int64_t)usecRunTime(type);
#else
return 0.000001 * usecRunTime(type);
#endif
}
};
/* vi: set ts=8 sw=4 sts=4 noet: */