Files
pvxs/ioc/iochooks.cpp
T
Vodopivec, Klemen eb11d9e1bc Fix registering functions with EPICS
Replace index_sequence implementation with one compatible with C++14
which also properly initializes arguments. Secondly, changed args to be array
of pointers instead of array of entries. This seems to fix problems and allows
to invoke functions from ioc shell.
2020-09-08 13:59:46 -07:00

247 lines
6.5 KiB
C++

/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* pvxs is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
#include <atomic>
#include <memory>
#include <stdexcept>
#include <pvxs/log.h>
#include <pvxs/server.h>
#include <pvxs/source.h>
#include <pvxs/iochooks.h>
#include <iocsh.h>
#include <initHooks.h>
#include <epicsStdio.h>
#include <epicsExit.h>
#include <epicsExport.h>
using namespace pvxs;
namespace {
std::atomic<server::Server*> instance{};
DEFINE_LOGGER(log, "pvxs.ioc");
void pvxsl(int detail)
{
try {
if(auto serv = instance.load()) {
for(auto& pair : serv->listSource()) {
auto src = serv->getSource(pair.first);
if(!src)
continue; // race?
auto list = src->onList();
if(detail>0)
printf("# Source %s@%d%s\n",
pair.first.c_str(), pair.second,
list.dynamic ? " [dynamic]":"");
if(!list.names) {
if(detail>0)
printf("# no PVs\n");
} else {
for(auto& name : *list.names) {
printf("%s\n", name.c_str());
}
}
}
}
} catch(std::exception& e) {
fprintf(stderr, "Error in %s : %s\n", __func__, e.what());
}
}
void pvxsr(int detail)
{
try {
if(auto serv = instance.load()) {
// TODO
(void)serv;
}
} catch(std::exception& e) {
fprintf(stderr, "Error in %s : %s\n", __func__, e.what());
}
}
// index_sequence from:
//http://stackoverflow.com/questions/17424477/implementation-c14-make-integer-sequence
template< std::size_t ... I >
struct index_sequence {
using type = index_sequence;
using value_type = std::size_t;
static constexpr std::size_t size() {
return sizeof ... (I);
}
};
template< typename Seq1, typename Seq2 >
struct concat_sequence;
template< std::size_t ... I1, std::size_t ... I2 >
struct concat_sequence< index_sequence< I1 ... >, index_sequence< I2 ... > > : public index_sequence< I1 ..., (sizeof ... (I1)+I2) ... > {};
template< std::size_t I >
struct make_index_sequence : public concat_sequence< typename make_index_sequence< I/2 >::type,
typename make_index_sequence< I-I/2 >::type > {};
template<>
struct make_index_sequence< 0 > : public index_sequence<> {};
template<>
struct make_index_sequence< 1 > : public index_sequence< 0 > {};
template<typename E>
struct Arg;
template<>
struct Arg<int> {
static constexpr iocshArgType code = iocshArgInt;
static int get(const iocshArgBuf& buf) { return buf.ival; }
};
template<>
struct Arg<double> {
static constexpr iocshArgType code = iocshArgDouble;
static double get(const iocshArgBuf& buf) { return buf.dval; }
};
template<>
struct Arg<const char*> {
static constexpr iocshArgType code = iocshArgString;
static const char* get(const iocshArgBuf& buf) { return buf.sval; }
};
template<typename T>
struct ToStr { typedef const char* type; };
template<typename ...Args>
struct Reg {
const char* const name;
const char* const argnames[sizeof...(Args)];
constexpr explicit Reg(const char* name, typename ToStr<Args>::type... descs)
:name(name)
,argnames{descs...}
{}
template<void (*fn)(Args...), size_t... Idxs>
static
void call(const iocshArgBuf* args)
{
(*fn)(Arg<Args>::get(args[Idxs])...);
}
template<void (*fn)(Args...), size_t... Idxs>
void doit(index_sequence<Idxs...>)
{
static const iocshArg argstack[sizeof...(Args)] = {{argnames[Idxs], Arg<Args>::code}...};
static const iocshArg * const args[] = {&argstack[Idxs]...};
static const iocshFuncDef def = {name, sizeof...(Args), args};
iocshRegister(&def, &call<fn, Idxs...>);
}
template<void (*fn)(Args...)>
void ister()
{
doit<fn>(make_index_sequence<sizeof...(Args)>{});
}
};
void pvxsAtExit(void* unused)
{
try {
if(auto serv = instance.load()) {
if(instance.compare_exchange_strong(serv, nullptr)) {
// take ownership
std::unique_ptr<server::Server> trash(serv);
trash->stop();
log_debug_printf(log, "Stopped Server?%s", "\n");
}
}
} catch(std::exception& e) {
fprintf(stderr, "Error in %s : %s\n", __func__, e.what());
}
}
void pvxsInitHook(initHookState state)
{
try {
// iocBuild()
if(state==initHookAfterInitDatabase) {
// we want to run before exitDatabase
epicsAtExit(&pvxsAtExit, nullptr);
}
// iocRun()/iocPause()
if(state==initHookAfterCaServerRunning) {
if(auto serv = instance.load()) {
serv->start();
log_debug_printf(log, "Started Server %p", serv);
}
}
if(state==initHookAfterCaServerPaused) {
if(auto serv = instance.load()) {
serv->stop();
log_debug_printf(log, "Stopped Server %p", serv);
}
}
} catch(std::exception& e) {
fprintf(stderr, "Error in %s : %s\n", __func__, e.what());
}
}
void pvxsRegistrar()
{
try {
pvxs::logger_config_env();
Reg<int>("pvxsl", "detail").ister<&pvxsl>();
Reg<int>("pvxsr", "detail").ister<&pvxsr>();
auto serv = instance.load();
if(!serv) {
std::unique_ptr<server::Server> temp(new server::Server(server::Config::from_env()));
if(instance.compare_exchange_strong(serv, temp.get())) {
log_debug_printf(log, "Installing Server %p\n", temp.get());
temp.release();
} else {
log_crit_printf(log, "Race installing Server? %p\n", serv);
}
} else {
log_err_printf(log, "Stale Server? %p\n", serv);
}
initHookRegister(&pvxsInitHook);
} catch(std::exception& e) {
fprintf(stderr, "Error in %s : %s\n", __func__, e.what());
}
}
} // namesapce
namespace pvxs {
namespace ioc {
server::Server server()
{
if(auto serv = instance.load()) {
return *serv;
} else {
throw std::logic_error("No Instance");
}
}
}} // namespace pvxs::ioc
extern "C" {
epicsExportRegistrar(pvxsRegistrar);
}