add iochooks and pvxsIoc library

This commit is contained in:
Michael Davidsaver
2020-09-08 13:59:44 -07:00
parent b99e5c9ae6
commit 6bbcf8aebb
8 changed files with 405 additions and 3 deletions
+1 -1
View File
@@ -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
+4 -1
View File
@@ -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
+35
View File
@@ -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
+244
View File
@@ -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);
}
+56
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
registrar(pvxsRegistrar)
+15 -1
View File
@@ -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
+49
View File
@@ -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();
}