diff --git a/ioc/group.cpp b/ioc/group.cpp index 1c444b2..f4182c1 100644 --- a/ioc/group.cpp +++ b/ioc/group.cpp @@ -23,7 +23,7 @@ static IOCGroupConfig* configInstance; static -void onceConfigInstance(void*) +void onceConfigInstance() { try { configInstance = new IOCGroupConfig; @@ -32,12 +32,9 @@ void onceConfigInstance(void*) } } -static -epicsThreadOnceId onceConfig = EPICS_THREAD_ONCE_INIT; - IOCGroupConfig& IOCGroupConfig::instance() { - epicsThreadOnce(&onceConfig, &onceConfigInstance, nullptr); + threadOnce<&onceConfigInstance>(); return *configInstance; } diff --git a/ioc/iochooks.cpp b/ioc/iochooks.cpp index a48fda9..020a119 100644 --- a/ioc/iochooks.cpp +++ b/ioc/iochooks.cpp @@ -56,9 +56,7 @@ struct pvxServer_t { server::Server srv; } *pvxServer; -epicsThreadOnceId pvxServerID = EPICS_THREAD_ONCE_INIT; - -void pvxServerInit(void*) { +void pvxServerInit() { pvxServer = new pvxServer_t(); } } // namespace @@ -69,7 +67,7 @@ void pvxServerInit(void*) { * @return the pvxs server instance */ server::Server server() { - threadOnce(&pvxServerID, &pvxServerInit); + threadOnce<&pvxServerInit>(); Guard (pvxServer->lock); if(pvxServer->srv) return pvxServer->srv; @@ -84,7 +82,7 @@ void initialisePvxsServer() { Config conf = ::pvxs::impl::inUnitTest() ? Config::isolated() : Config::from_env(); Server newsrv(conf); - threadOnce(&pvxServerID, &pvxServerInit); + threadOnce<&pvxServerInit>(); Guard G(pvxServer->lock); if(pvxServer->srv) throw std::logic_error(SB()<<__func__<<" found existing server?!?"); @@ -182,14 +180,12 @@ struct RefTrack { std::map refs; } *refTrack; -epicsThreadOnceId refSavedOnce = EPICS_THREAD_ONCE_INIT; - -void refSavedInit(void *) { +void refSavedInit() { refTrack = new RefTrack(); } void pvxrefsave() { - epicsThreadOnce(&refSavedOnce, &refSavedInit, nullptr); + threadOnce<&refSavedInit>(); epicsGuard G(refTrack->lock); refTrack->refs = instanceSnapshot(); } @@ -198,7 +194,7 @@ void pvxrefdiff() { auto cur(instanceSnapshot()); std::map diff; - epicsThreadOnce(&refSavedOnce, &refSavedInit, nullptr); + threadOnce<&refSavedInit>(); { epicsGuard G(refTrack->lock); diff --git a/src/evhelper.cpp b/src/evhelper.cpp index 88c8a10..2b2cff1 100644 --- a/src/evhelper.cpp +++ b/src/evhelper.cpp @@ -52,10 +52,7 @@ VFunctor0::~VFunctor0() {} } static -epicsThreadOnceId evthread_once = EPICS_THREAD_ONCE_INIT; - -static -void evthread_init(void* unused) +void evthread_init() { #if defined(EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED) evthread_use_windows_threads(); @@ -147,7 +144,7 @@ struct evbase::Pvt final : public epicsThreadRunable epicsThreadGetStackSize(epicsThreadStackBig), prio) { - threadOnce(&evthread_once, &evthread_init, nullptr); + threadOnce<&evthread_init>(); worker.start(); start_sync.wait(); @@ -699,19 +696,17 @@ bool evsocket::init_canIPv6() noexcept # define getMonotonic getCurrent #endif -static epicsThreadOnceId mapOnce = EPICS_THREAD_ONCE_INIT; - static IfaceMap* theinstance; static -void mapInit(void*) +void mapInit() { theinstance = new IfaceMap(); } IfaceMap& IfaceMap::instance() { - threadOnce(&mapOnce, &mapInit); + threadOnce<&mapInit>(); assert(theinstance); return *theinstance; } diff --git a/src/log.cpp b/src/log.cpp index 9f69fe2..cdac0cc 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -247,7 +247,7 @@ struct logger_gbl_t { } } *logger_gbl; -void logger_prepare(void *unused) +void logger_prepare() { logger_gbl = new logger_gbl_t; @@ -260,8 +260,6 @@ void logger_prepare(void *unused) } } -epicsThreadOnceId logger_once = EPICS_THREAD_ONCE_INIT; - } // namespace Level logger::init() @@ -274,7 +272,7 @@ Level logger::init() if(this->lvl.compare_exchange_strong(lvl, Level::Warn)) { // logger now has default config of Level::Err // we will fully initialize - threadOnce(&logger_once, &logger_prepare, nullptr); + threadOnce<&logger_prepare>(); assert(logger_gbl); Guard G(logger_gbl->lock); @@ -326,7 +324,7 @@ void xerrlogHexPrintf(const void *buf, size_t buflen) void logger_level_set(const char *name, int lvl) { - threadOnce(&logger_once, &logger_prepare, nullptr); + threadOnce<&logger_prepare>(); assert(logger_gbl); Guard G(logger_gbl->lock); @@ -335,7 +333,7 @@ void logger_level_set(const char *name, int lvl) void logger_level_clear() { - threadOnce(&logger_once, &logger_prepare, nullptr); + threadOnce<&logger_prepare>(); assert(logger_gbl); Guard G(logger_gbl->lock); @@ -348,7 +346,7 @@ void logger_config_env() if(!env || !*env) return; - threadOnce(&logger_once, &logger_prepare, nullptr); + threadOnce<&logger_prepare>(); Guard G(logger_gbl->lock); @@ -397,7 +395,7 @@ namespace pvxs {namespace impl { void logger_shutdown() { - threadOnce(&logger_once, &logger_prepare, nullptr); + threadOnce<&logger_prepare>(); errlogFlush(); diff --git a/src/os/WIN32/osdSockExt.cpp b/src/os/WIN32/osdSockExt.cpp index da35894..46ea752 100644 --- a/src/os/WIN32/osdSockExt.cpp +++ b/src/os/WIN32/osdSockExt.cpp @@ -38,10 +38,7 @@ static LPFN_WSARECVMSG WSARecvMsg; static -epicsThreadOnceId oseOnce = EPICS_THREAD_ONCE_INIT; - -static -void oseDoOnce(void*) +void oseDoOnce() { evsocket dummy(AF_INET, SOCK_DGRAM, 0); GUID guid = WSAID_WSARECVMSG; @@ -64,7 +61,7 @@ void oseDoOnce(void*) void osiSockAttachExt() { osiSockAttach(); - epicsThreadOnce(&oseOnce, &oseDoOnce, nullptr); + threadOnce<&oseDoOnce>(); } void evsocket::enable_SO_RXQ_OVFL() const {} diff --git a/src/os/default/osdSockExt.cpp b/src/os/default/osdSockExt.cpp index dba66df..5f0550c 100644 --- a/src/os/default/osdSockExt.cpp +++ b/src/os/default/osdSockExt.cpp @@ -38,10 +38,7 @@ DEFINE_LOGGER(log, "pvxs.util"); DEFINE_LOGGER(logiface, "pvxs.iface"); static -epicsThreadOnceId oseOnce = EPICS_THREAD_ONCE_INIT; - -static -void oseDoOnce(void*) +void oseDoOnce() { evsocket::canIPv6 = evsocket::init_canIPv6(); #ifdef __linux__ @@ -54,7 +51,7 @@ void oseDoOnce(void*) void osiSockAttachExt() { osiSockAttach(); - epicsThreadOnce(&oseOnce, &oseDoOnce, nullptr); + threadOnce<&oseDoOnce>(); } void evsocket::enable_SO_RXQ_OVFL() const diff --git a/src/udp_collector.cpp b/src/udp_collector.cpp index 94b0c7d..8e0366e 100644 --- a/src/udp_collector.cpp +++ b/src/udp_collector.cpp @@ -519,17 +519,15 @@ evbase& UDPManager::loop() } namespace { -epicsThreadOnceId collector_once = EPICS_THREAD_ONCE_INIT; -void collector_init(void *unused) +void collector_init() { - (void)unused; udp_gbl = new udp_gbl_t; } } // namespace UDPManager UDPManager::instance(bool share) { - threadOnce(&collector_once, &collector_init, nullptr); + threadOnce<&collector_init>(); assert(udp_gbl); Guard G(udp_gbl->lock); diff --git a/src/util.cpp b/src/util.cpp index 7007bf7..9ebab35 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -77,13 +77,12 @@ unsigned long version_abi_int() } namespace { -epicsThreadOnceId ICountOnce = EPICS_THREAD_ONCE_INIT; struct ICountGbl_t { RWLock lock; std::map*> counters; } *ICountGbl; -void ICountInit(void*) +void ICountInit() { ICountGbl = new ICountGbl_t; } @@ -92,7 +91,7 @@ void ICountInit(void*) void registerICount(const char *name, std::atomic& Cnt) { - epicsThreadOnce(&ICountOnce, &ICountInit, nullptr); + threadOnce<&ICountInit>(); auto& gbl = *ICountGbl; try { auto L(gbl.lock.lockWriter()); @@ -110,7 +109,7 @@ std::map instanceSnapshot() std::map ret; { - epicsThreadOnce(&ICountOnce, &ICountInit, nullptr); + threadOnce<&ICountInit>(); auto& gbl = *ICountGbl; auto L(gbl.lock.lockReader()); for(auto& pair : gbl.counters) { @@ -729,28 +728,31 @@ void compat_make_socket_nonblocking(SOCKET sock) namespace pvxs {namespace impl { struct onceArgs { - EPICSTHREADFUNC fn; - void *arg; + threadOnceInfo *info; std::exception_ptr err; }; static -void onceWrapper(void *raw) +void onceWrapper(void *raw) noexcept { auto args = static_cast(raw); try { - args->fn(args->arg); + args->info->fn(); + args->info->ok = true; }catch(...){ args->err = std::current_exception(); + args->info->ok = false; } } -void threadOnce(epicsThreadOnceId *id, EPICSTHREADFUNC fn, void *arg) +void threadOnce_(threadOnceInfo *info) { - onceArgs args{fn, arg}; - epicsThreadOnce(id, &onceWrapper, &args); + onceArgs args{info}; + epicsThreadOnce(&info->id, &onceWrapper, &args); if(args.err) std::rethrow_exception(args.err); + if(!info->ok) + throw std::logic_error("threadOnce() : Previous failure"); } template<> diff --git a/src/utilpvt.h b/src/utilpvt.h index 9a95349..e8ffb81 100644 --- a/src/utilpvt.h +++ b/src/utilpvt.h @@ -29,6 +29,7 @@ #include #include +#include #include #include @@ -90,8 +91,23 @@ void strDiff(std::ostream& out, const char *lhs, const char *rhs); +struct threadOnceInfo { + epicsThreadOnceId id = EPICS_THREAD_ONCE_INIT; + void (* const fn)(); + bool ok = false; + explicit constexpr threadOnceInfo(void (*fn)()) :fn(fn) {} +}; + PVXS_API -void threadOnce(epicsThreadOnceId *id, EPICSTHREADFUNC fn, void *arg=nullptr); +void threadOnce_(threadOnceInfo *info) ; + +template +void threadOnce() noexcept { + // global name qualified by onceFn address. + // effectively replicated for each onceFn + static threadOnceInfo info{onceFn}; + threadOnce_(&info); +} namespace idetail { template diff --git a/test/testutil.cpp b/test/testutil.cpp index 93336a3..5857363 100644 --- a/test/testutil.cpp +++ b/test/testutil.cpp @@ -267,11 +267,31 @@ void testStrDiff() "+ \" yyy\"\n"); } +size_t onceCount[2]; + +template +void onceInc() { + onceCount[I]++; +} + +void testOnce() +{ + testShow()<<__func__; + + threadOnce>(); + threadOnce>(); + threadOnce>(); + threadOnce>(); + + testEq(onceCount[0], 1u); + testEq(onceCount[1], 1u); +} + } // namespace MAIN(testutil) { - testPlan(33); + testPlan(35); testTrue(version_abi_check())<<" 0x"<