add iochooks and pvxsIoc library
This commit is contained in:
@@ -5,7 +5,7 @@ set -e
|
||||
|
||||
ret=0
|
||||
echo "Checking for GCC style static constructors or destructors"
|
||||
for ff in src/O.*/*.o
|
||||
for ff in src/O.*/*.o ioc/O.*/*.o
|
||||
do
|
||||
if nm "$ff" | grep __static_initialization_and_destruction
|
||||
then
|
||||
|
||||
@@ -11,8 +11,11 @@ src_DEPEND_DIRS = configure
|
||||
DIRS += tools
|
||||
tools_DEPEND_DIRS = src
|
||||
|
||||
DIRS += ioc
|
||||
ioc_DEPEND_DIRS = src
|
||||
|
||||
DIRS += test
|
||||
test_DEPEND_DIRS = src
|
||||
test_DEPEND_DIRS = src ioc
|
||||
|
||||
DIRS += example
|
||||
example_DEPEND_DIRS = src
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
TOP=..
|
||||
|
||||
include $(TOP)/configure/CONFIG
|
||||
# cfg/ sometimes isn't correctly included due to a Base bug
|
||||
# so we do here (maybe again) as workaround
|
||||
include $(TOP)/configure/CONFIG_PVXS_MODULE
|
||||
include $(TOP)/configure/CONFIG_PVXS_VERSION
|
||||
#----------------------------------------
|
||||
# ADD MACRO DEFINITIONS AFTER THIS LINE
|
||||
#=============================
|
||||
|
||||
# access to private headers
|
||||
USR_CPPFLAGS += -I$(TOP)/src
|
||||
USR_CPPFLAGS += -DPVXS_IOC_API_BUILDING
|
||||
|
||||
DBD += pvxsIoc.dbd
|
||||
|
||||
INC += pvxs/iochooks.h
|
||||
|
||||
LIBRARY += pvxsIoc
|
||||
|
||||
SHRLIB_VERSION = $(PVXS_MAJOR_VERSION).$(PVXS_MINOR_VERSION)
|
||||
|
||||
pvxsIoc_SRCS += iochooks.cpp
|
||||
|
||||
LIB_LIBS += pvxs
|
||||
LIB_LIBS += $(EPICS_BASE_IOC_LIBS)
|
||||
|
||||
#===========================
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
include $(TOP)/configure/RULES_PVXS_MODULE
|
||||
#----------------------------------------
|
||||
# ADD RULES AFTER THIS LINE
|
||||
|
||||
@@ -0,0 +1,244 @@
|
||||
/**
|
||||
* 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());
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t... Ns>
|
||||
struct index_sequence {};
|
||||
|
||||
template<typename Tag>
|
||||
struct next_index_sequence {};
|
||||
|
||||
template<size_t... Ns>
|
||||
struct next_index_sequence<index_sequence<Ns...>>
|
||||
{
|
||||
typedef index_sequence<Ns..., sizeof...(Ns)> type;
|
||||
};
|
||||
|
||||
template<size_t I, size_t Cnt, size_t... Idxs>
|
||||
struct build_index_sequence
|
||||
{
|
||||
typedef typename build_index_sequence<I+1, Cnt, Idxs..., I+1>::type type;
|
||||
};
|
||||
|
||||
template<size_t Cnt, size_t... Idxs>
|
||||
struct build_index_sequence<Cnt, Cnt, Idxs...>
|
||||
{
|
||||
typedef index_sequence<Idxs...> type;
|
||||
};
|
||||
|
||||
template<typename ...Args>
|
||||
using make_index_sequence = typename build_index_sequence<0, sizeof...(Args)>::type;
|
||||
|
||||
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 args[sizeof...(Args)] = {{argnames[Idxs], Arg<Args>::code}...};
|
||||
static const iocshFuncDef def = {name, sizeof...(Args), (const iocshArg* const*)&args};
|
||||
|
||||
iocshRegister(&def, &call<fn, Idxs...>);
|
||||
}
|
||||
|
||||
template<void (*fn)(Args...)>
|
||||
void ister()
|
||||
{
|
||||
doit<fn>(make_index_sequence<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);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
#ifndef PVXS_IOCHOOKS_H
|
||||
#define PVXS_IOCHOOKS_H
|
||||
|
||||
#include <pvxs/version.h>
|
||||
|
||||
#if defined(_WIN32) || defined(__CYGWIN__)
|
||||
|
||||
# if defined(PVXS_IOC_API_BUILDING) && defined(EPICS_BUILD_DLL)
|
||||
/* building library as dll */
|
||||
# define PVXS_IOC_API __declspec(dllexport)
|
||||
# elif !defined(PVXS_IOC_API_BUILDING) && defined(EPICS_CALL_DLL)
|
||||
/* calling library in dll form */
|
||||
# define PVXS_IOC_API __declspec(dllimport)
|
||||
# endif
|
||||
|
||||
#elif __GNUC__ >= 4
|
||||
# define PVXS_IOC_API __attribute__ ((visibility("default")))
|
||||
#endif
|
||||
|
||||
#ifndef PVXS_IOC_API
|
||||
# define PVXS_IOC_API
|
||||
#endif
|
||||
|
||||
|
||||
namespace pvxs {
|
||||
namespace server {
|
||||
class Server;
|
||||
}
|
||||
namespace ioc {
|
||||
|
||||
/** Return the singleton Server instance which is setup
|
||||
* for use in an IOC process.
|
||||
*
|
||||
* This Server instance is created during in a registrar function,
|
||||
* started by the initHookAfterCaServerRunning phase of iocInit(),
|
||||
* and stopped and destroyed via an epicsAtExit() hook.
|
||||
*
|
||||
* Any configuration changes via. epicsEnvSet() must be made before registrars are run
|
||||
* by \*_registerRecordDeviceDriver(pdbbase).
|
||||
*
|
||||
* server::SharedPV and server::Source added before iocInit() will be available immediately.
|
||||
* Others may be added (or removed) later.
|
||||
*
|
||||
* @throws std::logic_error if called before instance is created, or after instance is destroyed.
|
||||
*/
|
||||
PVXS_IOC_API
|
||||
server::Server server();
|
||||
|
||||
}} // namespace pvxs::ioc
|
||||
|
||||
#endif // PVXS_IOCHOOKS_H
|
||||
@@ -0,0 +1 @@
|
||||
registrar(pvxsRegistrar)
|
||||
+15
-1
@@ -12,7 +12,7 @@ include $(TOP)/configure/CONFIG_PVXS_VERSION
|
||||
# access to private headers
|
||||
USR_CPPFLAGS += -I$(TOP)/src
|
||||
|
||||
PROD_LIBS += pvxs Com
|
||||
PROD_LIBS = pvxs Com
|
||||
|
||||
TESTPROD_HOST += testsock
|
||||
testsock_SRCS += testsock.cpp
|
||||
@@ -78,6 +78,20 @@ TESTPROD_HOST += testrpc
|
||||
testrpc_SRCS += testrpc.cpp
|
||||
TESTS += testrpc
|
||||
|
||||
ifdef BASE_3_15
|
||||
|
||||
DBDDEPENDS_FILES += testioc.dbd$(DEP)
|
||||
testioc_DBD = base.dbd pvxsIoc.dbd
|
||||
TESTFILES += $(COMMON_DIR)/testioc.dbd
|
||||
|
||||
TESTPROD_HOST += testioc
|
||||
testioc_SRCS += testioc.cpp
|
||||
testioc_SRCS += testioc_registerRecordDeviceDriver.cpp
|
||||
testioc_LIBS = pvxsIoc pvxs $(EPICS_BASE_IOC_LIBS)
|
||||
TESTS += testioc
|
||||
|
||||
endif
|
||||
|
||||
TESTPROD_HOST += mcat
|
||||
mcat_SRCS += mcat.cpp
|
||||
# not a unittest
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* 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 <testMain.h>
|
||||
#include <dbUnitTest.h>
|
||||
#include <dbAccess.h>
|
||||
#include <iocsh.h>
|
||||
|
||||
#include <pvxs/iochooks.h>
|
||||
#include <pvxs/server.h>
|
||||
#include <pvxs/unittest.h>
|
||||
|
||||
extern "C" {
|
||||
extern int testioc_registerRecordDeviceDriver(struct dbBase*);
|
||||
}
|
||||
|
||||
using namespace pvxs;
|
||||
|
||||
namespace {
|
||||
|
||||
} // namespace
|
||||
|
||||
MAIN(testioc)
|
||||
{
|
||||
testPlan(3);
|
||||
testSetup();
|
||||
|
||||
testdbPrepare();
|
||||
|
||||
testThrows<std::logic_error>([]{
|
||||
ioc::server();
|
||||
});
|
||||
|
||||
testdbReadDatabase("testioc.dbd", nullptr, nullptr);
|
||||
testEq(0, testioc_registerRecordDeviceDriver(pdbbase));
|
||||
|
||||
testTrue(!!ioc::server());
|
||||
|
||||
testIocInitOk();
|
||||
|
||||
testIocShutdownOk();
|
||||
|
||||
testdbCleanup();
|
||||
|
||||
return testDone();
|
||||
}
|
||||
Reference in New Issue
Block a user