From 6926d911eeb1b8ccb4da14ed7717f381379a7434 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Tue, 6 Jun 2017 08:35:03 +0200 Subject: [PATCH] caProvider: init issues avoid static init order issues ref. count to allow multiple calls to CAClientFactory::start() --- src/ca/caProvider.cpp | 113 +++++++++++++---------------------------- src/ca/pv/caProvider.h | 4 +- 2 files changed, 37 insertions(+), 80 deletions(-) diff --git a/src/ca/caProvider.cpp b/src/ca/caProvider.cpp index f3765ef..dc2a329 100644 --- a/src/ca/caProvider.cpp +++ b/src/ca/caProvider.cpp @@ -9,9 +9,11 @@ /* for CA */ #include #include +#include #define epicsExportSharedSymbols #include +#include #include #include @@ -23,13 +25,20 @@ using namespace epics::pvAccess::ca; catch (std::exception &e) { LOG(logLevelError, "Unhandled exception caught from client code at %s:%d: %s", __FILE__, __LINE__, e.what()); } \ catch (...) { LOG(logLevelError, "Unhandled exception caught from client code at %s:%d.", __FILE__, __LINE__); } -std::string CAChannelProvider::PROVIDER_NAME = "ca"; - CAChannelProvider::CAChannelProvider() : current_context(0), destroyed(false) { initialize(); } +CAChannelProvider::CAChannelProvider(const std::tr1::shared_ptr&) + : current_context(0) + , destroyed(false) +{ + // Ignoring Configuration as CA only allows config via. environment, + // and we don't want to change this here. + initialize(); +} + CAChannelProvider::~CAChannelProvider() { // call destroy() to destroy CA context @@ -38,7 +47,7 @@ CAChannelProvider::~CAChannelProvider() std::string CAChannelProvider::getProviderName() { - return PROVIDER_NAME; + return "ca"; } ChannelFind::shared_pointer CAChannelProvider::channelFind( @@ -165,94 +174,42 @@ void CAChannelProvider::initialize() } +static epicsThreadOnceId cafactory_once = EPICS_THREAD_ONCE_INIT; +static struct cafactory_gbl_t { + Mutex mutex; + int count; + cafactory_gbl_t() :count(0u) {} +} *cafactory_gbl; - - - - - - - - -// TODO global static variable (de/initialization order not guaranteed) -static Mutex mutex; -static CAChannelProvider::shared_pointer sharedProvider; - -class CAChannelProviderFactoryImpl : public ChannelProviderFactory +static +void cafactory_init(void*) { -public: - POINTER_DEFINITIONS(CAChannelProviderFactoryImpl); - - virtual std::string getFactoryName() - { - return CAChannelProvider::PROVIDER_NAME; - } - - virtual ChannelProvider::shared_pointer sharedInstance() - { - Lock guard(mutex); - if (!sharedProvider.get()) - { - try { - // TODO use std::make_shared - std::tr1::shared_ptr tp(new CAChannelProvider()); - sharedProvider = tp; - } catch (std::exception &e) { - LOG(logLevelError, "Unhandled exception caught at %s:%d: %s", __FILE__, __LINE__, e.what()); - } catch (...) { - LOG(logLevelError, "Unhandled exception caught at %s:%d.", __FILE__, __LINE__); - } - } - return sharedProvider; - } - - virtual ChannelProvider::shared_pointer newInstance(const std::tr1::shared_ptr& conf) - { - // Ignoring configuration as CA only allows config via. environment, - // and we don't want to change this here. - try { - // TODO use std::make_shared - std::tr1::shared_ptr tp(new CAChannelProvider()); - ChannelProvider::shared_pointer ni = tp; - return ni; - } catch (std::exception &e) { - LOG(logLevelError, "Unhandled exception caught at %s:%d: %s", __FILE__, __LINE__, e.what()); - return ChannelProvider::shared_pointer(); - } catch (...) { - LOG(logLevelError, "Unhandled exception caught at %s:%d.", __FILE__, __LINE__); - return ChannelProvider::shared_pointer(); - } - } - - void destroySharedInstance() - { - if(!sharedProvider) return; - sharedProvider->destroy(); - sharedProvider.reset(); - } -}; - -static CAChannelProviderFactoryImpl::shared_pointer factory; + cafactory_gbl = new cafactory_gbl_t; +} void CAClientFactory::start() { epicsSignalInstallSigAlarmIgnore(); epicsSignalInstallSigPipeIgnore(); - Lock guard(mutex); - if (!factory.get()) - factory.reset(new CAChannelProviderFactoryImpl()); + epicsThreadOnce(&cafactory_once, &cafactory_init, 0); - registerChannelProviderFactory(factory); + Lock guard(cafactory_gbl->mutex); + if(cafactory_gbl->count++==0) { + if(!getChannelProviderRegistry()->add("ca", false)) + LOG(logLevelError, "Unable to register \"ca\" provider\n"); + } } void CAClientFactory::stop() { - Lock guard(mutex); + epicsThreadOnce(&cafactory_once, &cafactory_init, 0); - if (factory.get()) - { - unregisterChannelProviderFactory(factory); - factory->destroySharedInstance(); + Lock guard(cafactory_gbl->mutex); + + if(--cafactory_gbl->count==0) { + getChannelProviderRegistry()->remove("ca"); } + if(cafactory_gbl->count<0) + LOG(logLevelError, "too many calls to CAClientFactory::stop()"); } diff --git a/src/ca/pv/caProvider.h b/src/ca/pv/caProvider.h index 775ded4..d4593dd 100644 --- a/src/ca/pv/caProvider.h +++ b/src/ca/pv/caProvider.h @@ -16,6 +16,7 @@ namespace epics { namespace pvAccess { +class Configuration; namespace ca { class epicsShareClass CAChannelProvider : @@ -25,9 +26,8 @@ class epicsShareClass CAChannelProvider : public: POINTER_DEFINITIONS(CAChannelProvider); - static std::string PROVIDER_NAME; - CAChannelProvider(); + CAChannelProvider(const std::tr1::shared_ptr&); virtual ~CAChannelProvider(); /* --------------- epics::pvAccess::ChannelProvider --------------- */