dl-fldigi/src/include/qrunner.h

250 wiersze
6.9 KiB
C++

// ----------------------------------------------------------------------------
// qrunner.h
//
// Copyright (C) 2007-2009
// Stelios Bounanos, M0GLD
//
// This file is part of fldigi.
//
// fldigi 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 3 of the License, or
// (at your option) any later version.
//
// fldigi 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, see <http://www.gnu.org/licenses/>.
// ----------------------------------------------------------------------------
#ifndef QRUNNER_H_
#define QRUNNER_H_
#ifndef NDEBUG
# include <inttypes.h>
# include "debug.h"
#endif
#include <unistd.h>
#include <cerrno>
#include <stdexcept>
#include <cstring>
#if HAVE_STD_BIND
# include <functional>
namespace qrbind {
using std::bind;
};
#elif HAVE_STD_TR1_BIND
# include <tr1/functional>
namespace qrbind {
using std::tr1::bind;
};
#else
# error need std::bind or std::tr1::bind
#endif
#include "threads.h"
#include "qrunner/fqueue.h"
extern void write_message(int line_no, const char *func, const char *msg);
extern void qrunner_debug(int tid, const char * name);
extern const char *sztid[];
#ifndef __MINGW32__
# define QRUNNER_READ(fd__, buf__, len__) read(fd__, buf__, len__)
# define QRUNNER_WRITE(fd__, buf__, len__) write(fd__, buf__, len__)
#else
# define QRUNNER_READ(fd__, buf__, len__) recv(fd__, (char*)buf__, len__, 0)
# define QRUNNER_WRITE(fd__, buf__, len__) send(fd__, (const char*)buf__, len__, 0)
#endif
class qexception : public std::exception
{
public:
qexception(const char *msg_) : msg(msg_) { }
qexception(int e) : msg(strerror(e)) { }
~qexception() throw() { }
const char *what(void) const throw() { perror(msg.c_str()); return msg.c_str(); }
private:
std::string msg;
};
struct fsignal
{
typedef void result_type;
pthread_mutex_t* m;
pthread_cond_t* c;
fsignal(pthread_mutex_t* m_, pthread_cond_t* c_) : m(m_), c(c_) { }
void operator()(void) const
{
pthread_mutex_lock(m);
pthread_cond_signal(c);
pthread_mutex_unlock(m);
}
};
struct nop
{
typedef void result_type;
void operator()(void) const { }
};
class qrunner
{
public:
qrunner();
~qrunner();
void attach(void);
void attach(int, std::string);
void detach(void);
template <typename F>
bool request(const F& f)
{
if (fifo->push(f)) {
int resp = QRUNNER_WRITE(pfd[1], "", 1);
qrunner_debug(GET_THREAD_ID(), typeid(F).name());
#ifdef NDEBUG
if (unlikely(resp != 1)) {
throw qexception(errno);
}
#else
assert(resp);
#endif
return true;
}
#ifndef NDEBUG
//Remi's extra debugging info LOG_ERROR("qrunner: thread %" PRIdPTR " fifo full!", GET_THREAD_ID());
LOG_ERROR("qrunner: thread %" PRIdPTR " fifo full at %s!", GET_THREAD_ID(),typeid(F).name() );
#endif
return false;
}
template <typename F>
bool request_sync(const F& f)
{
if (!attached)
return request(f);
for (;;) {
if (request(f))
break;
sched_yield();
}
static pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t c = PTHREAD_COND_INITIALIZER;
fsignal s(&m, &c);
pthread_mutex_lock(&m);
for (;;) {
if (request(s))
break;
sched_yield();
}
pthread_cond_wait(&c, &m);
pthread_mutex_unlock(&m);
return true;
}
static void execute(int fd, void *arg);
void flush(void);
void drop(void) { fifo->drop(); }
size_t size(void) { return fifo->size(); }
std::string id_str(void) { return _id_string; }
protected:
fqueue *fifo;
int pfd[2];
bool attached;
int _id_no;
std::string _id_string;
bool inprog;
public:
bool drop_flag;
};
extern qrunner *cbq[NUM_QRUNNER_THREADS];
#if BENCHMARK_MODE
#define REQ(...) ((void)0)
#define REQ_DROP(...) ((void)0)
#define REQ_SYNC(...) ((void)0)
#define REQ_FLUSH(...) ((void)0)
#define QRUNNER_DROP(...) ((void)0)
#else
#define REQ REQ_ASYNC
#define REQ_DROP REQ_ASYNC_DROP
#define REQ_ASYNC(...) \
do { \
if (GET_THREAD_ID() != FLMAIN_TID) \
cbq[GET_THREAD_ID()]->request(qrbind::bind(__VA_ARGS__)); \
else \
qrbind::bind(__VA_ARGS__)(); \
} while (0)
#define REQ_SYNC(...) \
do { \
if (GET_THREAD_ID() != FLMAIN_TID) \
cbq[GET_THREAD_ID()]->request_sync(qrbind::bind(__VA_ARGS__)); \
else \
qrbind::bind(__VA_ARGS__)(); \
} while (0)
#define REQ_ASYNC_DROP(...) \
do { \
if (GET_THREAD_ID() != FLMAIN_TID) { \
if (unlikely(cbq[GET_THREAD_ID()]->drop_flag)) \
break; \
cbq[GET_THREAD_ID()]->request(qrbind::bind(__VA_ARGS__)); \
} \
else \
qrbind::bind(__VA_ARGS__)(); \
} while (0)
#define REQ_SYNC_DROP(...) \
do { \
if (GET_THREAD_ID() != FLMAIN_TID) { \
if (unlikely(cbq[GET_THREAD_ID()]->drop_flag)) \
break; \
cbq[GET_THREAD_ID()]->request_sync(qrbind::bind(__VA_ARGS__)); \
} \
else \
qrbind::bind(__VA_ARGS__)(); \
} while (0)
#define REQ_FLUSH(t_) \
do { \
if (GET_THREAD_ID() != FLMAIN_TID) \
cbq[GET_THREAD_ID()]->request_sync(nop()); \
else if (t_ < NUM_QRUNNER_THREADS) \
cbq[t_]->flush(); \
else \
for (int i = 0; i < NUM_QRUNNER_THREADS; i++) \
cbq[i]->flush(); \
} while (0)
#define QRUNNER_DROP(v_) \
do { \
if ((GET_THREAD_ID() != FLMAIN_TID)) \
cbq[GET_THREAD_ID()]->drop_flag = v_; \
} while (0)
#endif // BENCHMARK_MODE
#endif // QRUNNER_H_
// Local Variables:
// mode: c++
// c-file-style: "linux"
// End: