mirror of https://github.com/sipwise/sems.git
added the call watcher, useful class to monitor/interface call status to external applications/network etc
git-svn-id: http://svn.berlios.de/svnroot/repos/sems/trunk@403 8eb893ce-cfd4-0310-b710-fb5ebe64c474sayer/1.4-spce2.6
parent
d49e62e3b9
commit
41795d6d9f
@ -0,0 +1,203 @@
|
||||
/*
|
||||
* $Id: AmCallWatcher.cpp 279 2007-03-23 21:30:44Z sayer $
|
||||
*
|
||||
* (c) 2007 iptego GmbH
|
||||
*
|
||||
* 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 ser 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 <sys/time.h>
|
||||
|
||||
#include "log.h"
|
||||
|
||||
#include "AmCallWatcher.h"
|
||||
|
||||
AmCallWatcher::AmCallWatcher()
|
||||
: garbage_collector(new AmCallWatcherGarbageCollector(soft_states_mut, soft_states)),
|
||||
AmEventQueue(this)
|
||||
{
|
||||
}
|
||||
|
||||
AmCallWatcher::~AmCallWatcher()
|
||||
{
|
||||
}
|
||||
|
||||
void AmCallWatcher::run() {
|
||||
DBG("starting call watcher.\n");
|
||||
garbage_collector->start();
|
||||
while (true) {
|
||||
waitForEvent();
|
||||
processEvents();
|
||||
}
|
||||
}
|
||||
|
||||
void AmCallWatcher::on_stop() {
|
||||
ERROR("The call watcher cannot be stopped.\n");
|
||||
}
|
||||
|
||||
void AmCallWatcher::process(AmEvent* ev) {
|
||||
CallStatusUpdateEvent* csu = dynamic_cast<CallStatusUpdateEvent*>(ev);
|
||||
if (NULL == csu) {
|
||||
ERROR("received invalid event!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
switch (csu->event_id) {
|
||||
case CallStatusUpdateEvent::Initialize: {
|
||||
states_mut.lock();
|
||||
DBG("adding call state '%s'\n",
|
||||
csu->get_call_id().c_str());
|
||||
|
||||
// check whether already there
|
||||
CallStatusMap::iterator it = states.find(csu->get_call_id());
|
||||
if (it != states.end()) {
|
||||
WARN("implementation error: state '%s' already in list!\n",
|
||||
csu->get_call_id().c_str());
|
||||
// avoid leak - delete the old state
|
||||
delete it->second;
|
||||
}
|
||||
|
||||
// insert the new one
|
||||
states[csu->get_call_id()] = csu->get_init_status();
|
||||
states_mut.unlock();
|
||||
} break;
|
||||
|
||||
case CallStatusUpdateEvent::Update: {
|
||||
states_mut.lock();
|
||||
CallStatusMap::iterator it = states.find(csu->get_call_id());
|
||||
if (it != states.end()) {
|
||||
it->second->update(csu);
|
||||
it->second->dump();
|
||||
states_mut.unlock();
|
||||
} else {
|
||||
states_mut.unlock();
|
||||
|
||||
soft_states_mut.lock();
|
||||
CallStatusTimedMap::iterator it =
|
||||
soft_states.find(csu->get_call_id());
|
||||
if (it != soft_states.end()) {
|
||||
it->second.first->update(csu);
|
||||
it->second.first->dump();
|
||||
} else {
|
||||
DBG("received update event for inexistent call '%s'\n",
|
||||
csu->get_call_id().c_str());
|
||||
}
|
||||
soft_states_mut.unlock();
|
||||
}
|
||||
} break;
|
||||
|
||||
case CallStatusUpdateEvent::Obsolete: {
|
||||
states_mut.lock();
|
||||
CallStatusMap::iterator it = states.find(csu->get_call_id());
|
||||
if (it != states.end()) {
|
||||
|
||||
CallStatus* cs = it->second;
|
||||
states.erase(it);
|
||||
size_t s_size = states.size();
|
||||
states_mut.unlock();
|
||||
|
||||
struct timeval now;
|
||||
gettimeofday(&now, NULL);
|
||||
|
||||
soft_states_mut.lock();
|
||||
soft_states[csu->get_call_id()] =
|
||||
std::make_pair(cs, now.tv_sec + WATCHER_SOFT_EXPIRE_SECONDS);
|
||||
size_t soft_size = soft_states.size();
|
||||
soft_states_mut.unlock();
|
||||
|
||||
DBG("moved call state '%s' to soft-state map (%u states, %u soft-states)\n",
|
||||
csu->get_call_id().c_str(), s_size, soft_size);
|
||||
|
||||
} else {
|
||||
DBG("received obsolete event for inexistent call '%s'\n",
|
||||
csu->get_call_id().c_str());
|
||||
states_mut.unlock();
|
||||
}
|
||||
}break;
|
||||
}
|
||||
}
|
||||
|
||||
void AmCallWatcher::dump() {
|
||||
states_mut.lock();
|
||||
for (CallStatusMap::iterator it = states.begin();
|
||||
it != states.end(); it++) {
|
||||
it->second->dump();
|
||||
}
|
||||
states_mut.unlock();
|
||||
}
|
||||
|
||||
CallStatus* AmCallWatcher::getStatus(const string& call_id) {
|
||||
CallStatus* res = NULL;
|
||||
|
||||
states_mut.lock();
|
||||
|
||||
CallStatusMap::iterator it = states.find(call_id);
|
||||
if (it != states.end()) {
|
||||
res = it->second->copy();
|
||||
states_mut.unlock();
|
||||
} else {
|
||||
states_mut.unlock();
|
||||
|
||||
// check obsolete states
|
||||
soft_states_mut.lock();
|
||||
CallStatusTimedMap::iterator it =
|
||||
soft_states.find(call_id);
|
||||
if (it != soft_states.end()) {
|
||||
// got it. return and remove from map
|
||||
res = it->second.first;
|
||||
soft_states.erase(it);
|
||||
DBG("erased call state '%s' (%u in list).\n",
|
||||
call_id.c_str(), soft_states.size());
|
||||
} else {
|
||||
DBG("state for call '%s' not found.\n",
|
||||
call_id.c_str());
|
||||
}
|
||||
soft_states_mut.unlock();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void AmCallWatcherGarbageCollector::run() {
|
||||
DBG("AmCallWatcherGarbageCollector started.\n");
|
||||
while (true) {
|
||||
sleep(2);
|
||||
struct timeval now;
|
||||
gettimeofday(&now, NULL);
|
||||
|
||||
bool erased = false;
|
||||
|
||||
mut.lock();
|
||||
for (AmCallWatcher::CallStatusTimedMap::iterator it = garbage.begin();
|
||||
it != garbage.end(); it++) {
|
||||
if (it->second.second < now.tv_sec) {
|
||||
garbage.erase(it); // map::erase does not invalidate map::iterator
|
||||
erased = true;
|
||||
}
|
||||
}
|
||||
if (erased){
|
||||
DBG("cleared old soft-states (%u soft-states remaining)\n",
|
||||
garbage.size());
|
||||
}
|
||||
mut.unlock();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,162 @@
|
||||
/*
|
||||
* $Id: AmCallWatcher.h 279 2007-03-23 21:30:44Z sayer $
|
||||
*
|
||||
* (c) 2007 iptego GmbH
|
||||
*
|
||||
* 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 ser 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
|
||||
*/
|
||||
|
||||
#ifndef _AM_CALL_WATCHER_H
|
||||
#define _AM_CALL_WATCHER_H
|
||||
|
||||
//
|
||||
// States are put into map on an Initialize event.
|
||||
// States are held in a map identified by call_id
|
||||
// (opaque identifier) and updated with Update events.
|
||||
// Once an Obsolete event is received, the states are
|
||||
// moved to soft-state map, where they are held until
|
||||
// queried to a maximum of WATCHER_SOFT_EXPIRE_SECONDS
|
||||
|
||||
#define WATCHER_SOFT_EXPIRE_SECONDS 5
|
||||
|
||||
#include <string>
|
||||
using std::string;
|
||||
|
||||
#include <map>
|
||||
using std::map;
|
||||
|
||||
#include <utility>
|
||||
using std::pair;
|
||||
|
||||
#include "AmEventQueue.h"
|
||||
#include "AmEvent.h"
|
||||
#include "AmThread.h"
|
||||
|
||||
/**
|
||||
* event that carries out call status update
|
||||
*/
|
||||
class CallStatus;
|
||||
|
||||
class CallStatusUpdateEvent : public AmEvent {
|
||||
string call_id;
|
||||
|
||||
CallStatus* init_status;
|
||||
|
||||
public:
|
||||
enum UpdateType {
|
||||
Initialize = 0,
|
||||
Update,
|
||||
Obsolete
|
||||
};
|
||||
|
||||
CallStatusUpdateEvent(UpdateType t, const string& call_id)
|
||||
: call_id(call_id), AmEvent(t) { }
|
||||
|
||||
// implicit: initialize
|
||||
CallStatusUpdateEvent(const string& call_id, CallStatus* init_status)
|
||||
: call_id(call_id), init_status(init_status), AmEvent(Initialize) { }
|
||||
|
||||
~CallStatusUpdateEvent() { }
|
||||
|
||||
string get_call_id() { return call_id; }
|
||||
CallStatus* get_init_status() { return init_status; }
|
||||
};
|
||||
|
||||
/**
|
||||
* interface for an update-able call status
|
||||
*/
|
||||
class CallStatus
|
||||
{
|
||||
public:
|
||||
CallStatus() { }
|
||||
virtual ~CallStatus() { }
|
||||
|
||||
/** update from an event */
|
||||
virtual void update(CallStatusUpdateEvent* e) = 0;
|
||||
|
||||
/** get a copy of self with relevant data */
|
||||
virtual CallStatus* copy() = 0;
|
||||
virtual void dump() { }
|
||||
};
|
||||
|
||||
class AmCallWatcherGarbageCollector;
|
||||
/**
|
||||
* call watcher is an entity for managing call status
|
||||
* via events that change status. Events are executed in a
|
||||
* separate thread serially by processing the event queue,
|
||||
* so synchronous status queries do not block the thread
|
||||
* reporting the status change.
|
||||
*/
|
||||
class AmCallWatcher
|
||||
: public AmThread,
|
||||
public AmEventQueue,
|
||||
public AmEventHandler
|
||||
{
|
||||
public:
|
||||
typedef map<string, CallStatus*> CallStatusMap;
|
||||
typedef map<string, pair<CallStatus*, time_t> > CallStatusTimedMap;
|
||||
|
||||
private:
|
||||
CallStatusMap states;
|
||||
AmMutex states_mut;
|
||||
|
||||
|
||||
CallStatusTimedMap soft_states;
|
||||
AmMutex soft_states_mut;
|
||||
AmCallWatcherGarbageCollector* garbage_collector;
|
||||
|
||||
public:
|
||||
AmCallWatcher();
|
||||
~AmCallWatcher();
|
||||
|
||||
// thread
|
||||
void run();
|
||||
void on_stop();
|
||||
|
||||
// eventhandler
|
||||
void process(AmEvent*);
|
||||
|
||||
CallStatus* getStatus(const string& call_id);
|
||||
|
||||
// dump all states
|
||||
void dump();
|
||||
};
|
||||
|
||||
/**
|
||||
* checks garbage every two seconds.
|
||||
* A bit inefficient with two threads, but AmCallWatcher
|
||||
* shouldn't be blocked by event.
|
||||
*/
|
||||
class AmCallWatcherGarbageCollector
|
||||
: public AmThread
|
||||
{
|
||||
AmMutex& mut;
|
||||
AmCallWatcher::CallStatusTimedMap& garbage;
|
||||
public:
|
||||
AmCallWatcherGarbageCollector(AmMutex& mut,
|
||||
AmCallWatcher::CallStatusTimedMap& garbage)
|
||||
: mut(mut), garbage(garbage) {}
|
||||
void run();
|
||||
void on_stop() { }
|
||||
};
|
||||
|
||||
#endif
|
||||
Loading…
Reference in new issue