kopia lustrzana https://github.com/jamescoxon/dl-fldigi
250 wiersze
6.9 KiB
C++
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:
|