diff --git a/src/evhelper.cpp b/src/evhelper.cpp index b530e17..31f23e1 100644 --- a/src/evhelper.cpp +++ b/src/evhelper.cpp @@ -105,6 +105,8 @@ struct evbase::Pvt : public epicsThreadRunable { SockAttach attach; + std::weak_ptr internal_self; + struct Work { std::function fn; std::exception_ptr *result; @@ -140,10 +142,7 @@ struct evbase::Pvt : public epicsThreadRunable } } - virtual ~Pvt() - { - join(); - } + virtual ~Pvt() {} void join() { @@ -238,12 +237,28 @@ struct evbase::Pvt : public epicsThreadRunable }; evbase::evbase(const std::string &name, unsigned prio) - :pvt(new Pvt(name, prio)) - ,base(pvt->base.get()) -{} +{ + auto internal(std::make_shared(name, prio)); + internal->internal_self = internal; + + pvt.reset(internal.get(), [internal](Pvt*) mutable { + auto temp(std::move(internal)); + temp->join(); + }); + + base = pvt->base.get(); +} evbase::~evbase() {} +evbase evbase::internal() const +{ + evbase ret; + ret.pvt = decltype(pvt)(pvt->internal_self); + ret.base = base; + return ret; +} + void evbase::join() const { pvt->join(); @@ -254,13 +269,16 @@ void evbase::sync() const call([](){}); } -void evbase::dispatch(std::function&& fn) const +bool evbase::_dispatch(std::function&& fn, bool dothrow) const { bool empty; { Guard G(pvt->lock); - if(!pvt->running) - throw std::logic_error("Worker stopped"); + if(!pvt->running) { + if(dothrow) + throw std::logic_error("Worker stopped"); + return false; + } empty = pvt->actions.empty(); pvt->actions.emplace_back(std::move(fn), nullptr, nullptr); } @@ -268,13 +286,15 @@ void evbase::dispatch(std::function&& fn) const timeval now{}; if(empty && event_add(pvt->dowork.get(), &now)) throw std::runtime_error("Unable to wakeup dispatch()"); + + return true; } -void evbase::call(std::function&& fn) const +bool evbase::_call(std::function&& fn, bool dothrow) const { if(pvt->worker.isCurrentThread()) { fn(); - return; + return true; } static ThreadEvent done; @@ -283,8 +303,11 @@ void evbase::call(std::function&& fn) const bool empty; { Guard G(pvt->lock); - if(!pvt->running) - throw std::logic_error("Worker stopped"); + if(!pvt->running) { + if(dothrow) + throw std::logic_error("Worker stopped"); + return false; + } empty = pvt->actions.empty(); pvt->actions.emplace_back(std::move(fn), &result, done.get()); } @@ -297,6 +320,7 @@ void evbase::call(std::function&& fn) const Guard G(pvt->lock); if(result) std::rethrow_exception(result); + return true; } void evbase::assertInLoop() const @@ -309,9 +333,20 @@ void evbase::assertInLoop() const } } -bool evbase::inLoop() const +bool evbase::assertInRunningLoop() const { - return pvt->worker.isCurrentThread(); + if(pvt->worker.isCurrentThread()) + return true; + + Guard G(pvt->lock); + if(!pvt->running) + return false; + + char name[32]; + pvt->worker.getName(name, sizeof(name)); + log_exc_printf(logerr, "Not in running evbase worker: \"%s\" != \"%s\"\n", + name, epicsThread::getNameSelf()); + throw std::logic_error("Not in running evbase worker"); } evsocket::evsocket(evutil_socket_t sock) diff --git a/src/evhelper.h b/src/evhelper.h index 2821640..218131d 100644 --- a/src/evhelper.h +++ b/src/evhelper.h @@ -61,22 +61,51 @@ struct owned_ptr : public std::unique_ptr struct PVXS_API evbase { evbase() = default; - evbase(const evbase&) = default; - evbase(evbase&&) = default; explicit evbase(const std::string& name, unsigned prio=0); ~evbase(); + + evbase internal() const; + void join() const; void sync() const; - // queue request to execute in event loop. return immediately. - void dispatch(std::function&& fn) const; +private: + bool _dispatch(std::function&& fn, bool dothrow) const; + bool _call(std::function&& fn, bool dothrow) const; +public: // queue request to execute in event loop. return after executed. - void call(std::function&& fn) const; + inline + void call(std::function&& fn) const { + _call(std::move(fn), true); + } + inline + bool tryCall(std::function&& fn) const { + return _call(std::move(fn), false); + } + + // queue request to execute in event loop. return immediately. + inline + void dispatch(std::function&& fn) const { + _dispatch(std::move(fn), true); + } + inline + bool tryDispatch(std::function&& fn) const { + return _dispatch(std::move(fn), false); + } + + bool tryInvoke(bool docall, std::function&& fn) const { + if(docall) + return tryCall(std::move(fn)); + else + return tryDispatch(std::move(fn)); + } void assertInLoop() const; - bool inLoop() const; + //! Caller must be on the worker, or the worker must be stopped. + //! @returns true if working is running. + bool assertInRunningLoop() const; inline void reset() { pvt.reset(); } diff --git a/test/testev.cpp b/test/testev.cpp index a65393a..16f2fae 100644 --- a/test/testev.cpp +++ b/test/testev.cpp @@ -29,15 +29,13 @@ void test_call() auto snap = instanceSnapshot(); testEq(snap["evbase"], 1u); - testOk1(!base.inLoop()); - { bool called = false; base.call([&called, &base]() { testDiag("in loop 1"); called = true; - testOk1(!!base.inLoop()); base.assertInLoop(); + testTrue(base.assertInRunningLoop()); }); testOk1(called==true); } @@ -65,6 +63,16 @@ void test_call() testFail("Caught wrong exception : %s \"%s\"", typeid(e).name(), e.what()); } + auto internal(base.internal()); + base = evbase(); + // loop stopped + + testFalse(internal.assertInRunningLoop()); + testThrows([&internal]() { + internal.call([]() {}); + }); + + testFalse(internal.tryCall([](){})); } void test_fill_evbuf() @@ -116,7 +124,7 @@ void test_fill_evbuf() MAIN(testev) { SockAttach attach; - testPlan(15); + testPlan(17); testSetup(); test_call(); test_fill_evbuf();