mirror of https://github.com/sipwise/sems.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
285 lines
7.1 KiB
285 lines
7.1 KiB
/*
|
|
* Copyright (C) 2011 Stefan Sayer
|
|
*
|
|
* This file is part of SEMS, a free SIP media server.
|
|
*
|
|
* SEMS is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* For a license to use the sems software under conditions
|
|
* other than those described here, or to purchase support for this
|
|
* software, please contact iptel.org by e-mail at the following addresses:
|
|
* info@iptel.org
|
|
*
|
|
* SEMS 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. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "RegistrationTimer.h"
|
|
#include <stdlib.h>
|
|
|
|
RegistrationTimer::RegistrationTimer()
|
|
: current_bucket(0)
|
|
{
|
|
struct timeval now;
|
|
gettimeofday(&now, 0);
|
|
current_bucket_start = now.tv_sec;
|
|
}
|
|
|
|
// unsafe!
|
|
int RegistrationTimer::get_bucket_index(time_t tv) {
|
|
time_t buckets_start_time = current_bucket_start;
|
|
|
|
if (tv < buckets_start_time)
|
|
return -1;
|
|
|
|
// offset
|
|
int bucket_index = (tv - buckets_start_time);
|
|
bucket_index /= TIMER_BUCKET_LENGTH;
|
|
|
|
if (bucket_index > TIMER_BUCKETS) { // too far in the future
|
|
ERROR("requested timer too far in the future (index %d vs %d TIMER_BUCKETS)\n",
|
|
bucket_index, TIMER_BUCKETS);
|
|
return -2;
|
|
}
|
|
|
|
bucket_index += current_bucket;
|
|
bucket_index %= TIMER_BUCKETS; // circular array
|
|
|
|
return bucket_index;
|
|
}
|
|
|
|
void RegistrationTimer::place_timer(RegTimer* timer, int bucket_index) {
|
|
std::list<RegTimer*>::iterator it = buckets[bucket_index].timers.begin();
|
|
while (it != buckets[bucket_index].timers.end() &&
|
|
(timer->expires > (*it)->expires))
|
|
it++;
|
|
|
|
buckets[bucket_index].timers.insert(it, timer);
|
|
size_t b_size = buckets[bucket_index].timers.size();
|
|
|
|
DBG("inserted timer [%p] in bucket %i (now sized %zd)\n",
|
|
timer, bucket_index, b_size);
|
|
}
|
|
|
|
void RegistrationTimer::fire_timer(RegTimer* timer) {
|
|
if (timer && timer->cb) {
|
|
DBG("firing timer [%p]\n", timer);
|
|
timer->cb(timer, timer->data1, timer->data2);
|
|
}
|
|
}
|
|
|
|
bool RegistrationTimer::insert_timer(RegTimer* timer) {
|
|
if (!timer)
|
|
return false;
|
|
|
|
buckets_mut.lock();
|
|
int bucket_index = get_bucket_index(timer->expires);
|
|
|
|
if (bucket_index == -1) {
|
|
// already expired, fire timer
|
|
buckets_mut.unlock();
|
|
DBG("inserting already expired timer [%p], firing\n", timer);
|
|
fire_timer(timer);
|
|
return false;
|
|
}
|
|
|
|
if (bucket_index == -2) {
|
|
ERROR("trying to place timer too far in the future\n");
|
|
buckets_mut.unlock();
|
|
return false;
|
|
}
|
|
|
|
place_timer(timer, bucket_index);
|
|
|
|
buckets_mut.unlock();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool RegistrationTimer::remove_timer(RegTimer* timer) {
|
|
if (!timer)
|
|
return false;
|
|
|
|
bool res = false;
|
|
|
|
buckets_mut.lock();
|
|
int bucket_index = get_bucket_index(timer->expires);
|
|
|
|
if (bucket_index < 0) {
|
|
buckets_mut.unlock();
|
|
return false;
|
|
}
|
|
|
|
std::list<RegTimer*>& timerlist = buckets[bucket_index].timers;
|
|
|
|
for (std::list<RegTimer*>::iterator it = timerlist.begin();
|
|
it != timerlist.end(); it++) {
|
|
if (*it == timer) {
|
|
timerlist.erase(it);
|
|
res = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
buckets_mut.unlock();
|
|
|
|
if (res) {
|
|
DBG("successfully removed timer [%p]\n", timer);
|
|
} else {
|
|
DBG("timer [%p] not found for removing\n", timer);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
void RegistrationTimer::run_timers() {
|
|
std::list<RegTimer*> timers_tbf;
|
|
|
|
struct timeval now;
|
|
gettimeofday(&now, 0);
|
|
|
|
buckets_mut.lock();
|
|
|
|
// bucket over?
|
|
if (now.tv_sec > current_bucket_start + TIMER_BUCKET_LENGTH) {
|
|
timers_tbf.insert(timers_tbf.begin(),
|
|
buckets[current_bucket].timers.begin(),
|
|
buckets[current_bucket].timers.end());
|
|
buckets[current_bucket].timers.clear();
|
|
current_bucket++;
|
|
current_bucket %= TIMER_BUCKETS;
|
|
current_bucket_start += TIMER_BUCKET_LENGTH;
|
|
// DBG("turned bucket to %i\n", current_bucket);
|
|
}
|
|
|
|
// move timers from current_bucket
|
|
RegTimerBucket& bucket = buckets[current_bucket];
|
|
std::list<RegTimer*>::iterator it = bucket.timers.begin();
|
|
while (it != bucket.timers.end() &&
|
|
now.tv_sec > (*it)->expires) {
|
|
std::list<RegTimer*>::iterator c_it = it;
|
|
it++;
|
|
timers_tbf.push_back(*c_it);
|
|
bucket.timers.erase(c_it);
|
|
}
|
|
|
|
buckets_mut.unlock();
|
|
|
|
if (!timers_tbf.empty()) {
|
|
DBG("firing %zd timers\n", timers_tbf.size());
|
|
for (std::list<RegTimer*>::iterator it=timers_tbf.begin();
|
|
it != timers_tbf.end(); it++) {
|
|
fire_timer(*it);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RegistrationTimer::run()
|
|
{
|
|
struct timeval now,next_tick,diff,tick;
|
|
|
|
tick.tv_sec = 0;
|
|
tick.tv_usec = TIMER_RESOLUTION;
|
|
|
|
gettimeofday(&now, NULL);
|
|
timeradd(&tick,&now,&next_tick);
|
|
|
|
while(true){
|
|
|
|
gettimeofday(&now,NULL);
|
|
|
|
if(timercmp(&now,&next_tick,<)){
|
|
|
|
struct timespec sdiff,rem;
|
|
timersub(&next_tick, &now,&diff);
|
|
|
|
sdiff.tv_sec = diff.tv_sec;
|
|
sdiff.tv_nsec = diff.tv_usec * 1000;
|
|
|
|
if(sdiff.tv_nsec > 2000000) // 2 ms
|
|
nanosleep(&sdiff,&rem);
|
|
}
|
|
//else {
|
|
//printf("missed one tick\n");
|
|
//}
|
|
|
|
run_timers();
|
|
timeradd(&tick,&next_tick,&next_tick);
|
|
}
|
|
}
|
|
|
|
void RegistrationTimer::on_stop() {
|
|
}
|
|
|
|
bool RegistrationTimer::insert_timer_leastloaded(RegTimer* timer,
|
|
time_t from_time,
|
|
time_t to_time) {
|
|
|
|
buckets_mut.lock();
|
|
|
|
int from_index = get_bucket_index(from_time);
|
|
int to_index = get_bucket_index(to_time);
|
|
|
|
if (from_index < 0 && to_index < 0) {
|
|
ERROR("could not find timer bucket indices - "
|
|
"from_index = %d, to_index = %d, from_time = %ld, to_time %ld, "
|
|
"current_bucket_start = %ld\n",
|
|
from_index, to_index, from_time, to_time, current_bucket_start);
|
|
buckets_mut.unlock();
|
|
return false;
|
|
}
|
|
|
|
if (from_index < 0) {
|
|
// use now .. to_index
|
|
DBG("from_time (%ld) in the past - searching load loaded from now()\n", from_time);
|
|
from_index = current_bucket;
|
|
}
|
|
// find least loaded bucket
|
|
int res_index = from_index;
|
|
size_t least_load = buckets[from_index].timers.size();
|
|
|
|
int i = from_index;
|
|
while (i != to_index) {
|
|
if (buckets[i].timers.size() <= least_load) {
|
|
least_load = buckets[i].timers.size();
|
|
res_index = i;
|
|
}
|
|
|
|
i++;
|
|
i %= TIMER_BUCKETS;
|
|
}
|
|
DBG("found bucket %i with least load %zd (between %i and %i)\n",
|
|
res_index, least_load, from_index, to_index);
|
|
|
|
// update expires to some random value inside the selected bucket
|
|
int diff = (unsigned)res_index - current_bucket;
|
|
|
|
if ((unsigned)res_index < current_bucket) {
|
|
diff+=TIMER_BUCKETS;
|
|
}
|
|
|
|
timer->expires = current_bucket_start +
|
|
diff * TIMER_BUCKET_LENGTH + // bucket start
|
|
rand() % TIMER_BUCKET_LENGTH;
|
|
DBG("setting expires to %ld (between %ld and %ld)\n",
|
|
timer->expires, from_time, to_time);
|
|
|
|
place_timer(timer, res_index);
|
|
|
|
buckets_mut.unlock();
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|