diff --git a/src/evhelper.cpp b/src/evhelper.cpp index 98ee4c1..eca14de 100644 --- a/src/evhelper.cpp +++ b/src/evhelper.cpp @@ -1023,7 +1023,8 @@ bool Timer::cancel() Timer::Pvt::~Pvt() { log_debug_printf(logtimer, "Timer %p %s\n", this, __func__); - (void)cancel(); + if(base.assertInRunningLoop()) + (void)cancel(); } bool Timer::Pvt::cancel() @@ -1064,23 +1065,38 @@ Timer Timer::Pvt::buildOneShot(double delay, const evbase& base, std::function(base, std::move(cb)); + auto internal(std::make_shared(base, std::move(cb))); - base.call([&ret, &base, delay](){ - evevent timer(event_new(base.base, -1, EV_TIMEOUT, &expire_cb, ret.pvt.get())); - ret.pvt->timer = std::move(timer); + Timer ret; + ret.pvt = decltype (internal)(internal.get(), [internal](Timer::Pvt*) mutable { + // from user thread + auto temp(std::move(internal)); + auto loop(temp->base); + // std::bind for lack of c++14 generalized capture + // to move internal ref to worker for dtor + + loop.tryCall(std::bind([](std::shared_ptr& internal) { + // on worker + // ordering of dispatch()/call() ensures creation before destruction + internal->cancel(); + }, std::move(temp))); + }); + + base.call([internal, delay](){ + // on worker + evevent timer(event_new(internal->base.base, -1, EV_TIMEOUT, &expire_cb, internal.get())); + internal->timer = std::move(timer); auto timo(totv(delay)); - if(event_add(ret.pvt->timer.get(), &timo)) + if(event_add(internal->timer.get(), &timo)) throw std::runtime_error("Unable to start oneshot timer"); log_debug_printf(logtimer, "Create timer %p as %p with delay %f and %s\n", - ret.pvt.get(), - ret.pvt->timer.get(), + internal.get(), + internal->timer.get(), delay, - ret.pvt->cb.target_type().name()); + internal->cb.target_type().name()); }); return ret; diff --git a/test/testget.cpp b/test/testget.cpp index 83af28e..51a81d6 100644 --- a/test/testget.cpp +++ b/test/testget.cpp @@ -373,24 +373,27 @@ struct Tester { { testShow()<<__func__; - epicsEvent done; - Timer slowdown; + // ref'd by both put and timer functors + auto done(std::make_shared()); + // ref'd by put functor + auto slowdown(std::make_shared()); - mbox.onPut([this, &done, &slowdown](server::SharedPV& pv, std::unique_ptr&& rawop, Value&& rawval) { + mbox.onPut([this, done, slowdown](server::SharedPV& pv, + std::unique_ptr&& rawop, Value&& rawval) { // on server worker std::shared_ptr op(std::move(rawop)); auto val(std::move(rawval)); testPass("In onPut"); - slowdown = op->timerOneShot(0.01, [](){ + *slowdown = op->timerOneShot(0.01, [](){ testFail("I should not run."); }); - testTrue(slowdown.cancel()); + testTrue(slowdown->cancel()); - slowdown = op->timerOneShot(0.01, [this, &done, op, val](){ + *slowdown = op->timerOneShot(0.01, [this, done, op, val](){ testPass("I should run"); - done.signal(); + done->signal(); mbox.post(val); op->reply(); });