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.
sems/core/AmThread.h

286 lines
5.6 KiB

/*
* Copyright (C) 2002-2003 Fhg Fokus
*
* 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. This program is released under
* the GPL with the additional exemption that compiling, linking,
* and/or using OpenSSL is allowed.
*
* 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
*/
/** @file AmThread.h */
#ifndef _AmThread_h_
#define _AmThread_h_
#include <pthread.h>
#include <sys/time.h>
#include <time.h>
#include <errno.h>
#include <queue>
/**
* \brief C++ Wrapper class for pthread mutex
*/
class AmMutex
{
pthread_mutex_t m;
public:
AmMutex(bool recursive = false);
~AmMutex();
void lock();
void unlock();
};
/**
* \brief Simple lock class
*/
class AmLock
{
AmMutex& m;
public:
AmLock(AmMutex& _m) : m(_m) {
m.lock();
}
~AmLock(){
m.unlock();
}
};
/**
* \brief Shared variable.
*
* Include a variable and its mutex.
* @warning Don't use safe functions (set,get)
* within a {lock(); ... unlock();} block. Use
* unsafe function instead.
*/
template<class T>
class AmSharedVar
{
T t;
AmMutex m;
public:
AmSharedVar(const T& _t) : t(_t) {}
AmSharedVar() {}
T get() {
lock();
T res = unsafe_get();
unlock();
return res;
}
void set(const T& new_val) {
lock();
unsafe_set(new_val);
unlock();
}
void lock() { m.lock(); }
void unlock() { m.unlock(); }
const T& unsafe_get() { return t; }
void unsafe_set(const T& new_val) { t = new_val; }
};
/**
* \brief C++ Wrapper class for pthread condition
*/
template<class T>
class AmCondition
{
T t;
pthread_mutex_t m;
pthread_cond_t cond;
void init_cond() {
pthread_mutex_init(&m,NULL);
pthread_cond_init(&cond,NULL);
}
public:
AmCondition() : t() { init_cond(); }
AmCondition(const T& _t) : t(_t) { init_cond(); }
~AmCondition()
{
pthread_cond_destroy(&cond);
pthread_mutex_destroy(&m);
}
/** Change the condition's value. */
void set(const T& newval)
{
pthread_mutex_lock(&m);
t = newval;
if(t)
pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&m);
}
T get()
{
T val;
pthread_mutex_lock(&m);
val = t;
pthread_mutex_unlock(&m);
return val;
}
/** Waits for the condition to be true. */
void wait_for()
{
pthread_mutex_lock(&m);
while(!t){
pthread_cond_wait(&cond,&m);
}
pthread_mutex_unlock(&m);
}
/** Waits for the condition to be true or a timeout. */
bool wait_for_to(unsigned long msec)
{
struct timeval now;
struct timespec timeout;
int retcode = 0;
bool ret = false;
gettimeofday(&now, NULL);
timeout.tv_sec = now.tv_sec + (msec / 1000);
timeout.tv_nsec = (now.tv_usec + (msec % 1000)*1000)*1000;
if(timeout.tv_nsec >= 1000000000){
timeout.tv_sec++;
timeout.tv_nsec -= 1000000000;
}
pthread_mutex_lock(&m);
while(!t && !retcode){
retcode = pthread_cond_timedwait(&cond,&m, &timeout);
}
if(t) ret = true;
pthread_mutex_unlock(&m);
return ret;
}
};
/**
* \brief C++ Wrapper class for pthread
*/
class AmThread
{
pthread_t _td;
AmMutex _m_td;
AmSharedVar<bool> _stopped;
static void* _start(void*);
protected:
virtual void run()=0;
virtual void on_stop()=0;
public:
unsigned long _pid;
AmThread();
virtual ~AmThread() {}
virtual void onIdle() {}
/** Start it ! */
void start();
/** Stop it ! */
void stop();
/** @return true if this thread doesn't run. */
bool is_stopped() { return _stopped.get(); }
/** Wait for this thread to finish */
void join();
/** kill the thread (if pthread_setcancelstate(PTHREAD_CANCEL_ENABLED) has been set) **/
void cancel();
int setRealtime();
};
/**
* \brief Container/garbage collector for threads.
*
* AmThreadWatcher waits for threads to stop
* and delete them.
* It gets started automatically when needed.
* Once you added a thread to the container,
* there is no mean to get it out.
*/
class AmThreadWatcher: public AmThread
{
static AmThreadWatcher* _instance;
static AmMutex _inst_mut;
std::queue<AmThread*> thread_queue;
AmMutex q_mut;
/** the daemon only runs if this is true */
AmCondition<bool> _run_cond;
AmThreadWatcher();
void run();
void on_stop();
public:
static AmThreadWatcher* instance();
void add(AmThread*);
};
template<class T>
class AmThreadLocalStorage
{
pthread_key_t key;
static void __del_tls_obj(void* obj) {
delete static_cast<T*>(obj);
}
public:
AmThreadLocalStorage() {
pthread_key_create(&key,__del_tls_obj);
}
~AmThreadLocalStorage() {
pthread_key_delete(key);
}
T* get() {
return static_cast<T*>(pthread_getspecific(key));
}
void set(T* p) {
pthread_setspecific(key,(void*)p);
}
};
#endif
// Local Variables:
// mode:C++
// End: