[libcamera-devel] [PATCH v3 1/2] cam: event_loop: Stop queuing calls when the event loop are exiting
Jacopo Mondi
jacopo at jmondi.org
Mon Feb 1 18:07:37 CET 2021
Hi Niklas,
On Mon, Feb 01, 2021 at 05:18:00PM +0100, Niklas Söderlund wrote:
> Hi Jacopo,
>
> Thanks for your feedback.
>
> On 2021-02-01 10:35:18 +0100, Jacopo Mondi wrote:
> > Hi Niklas,
> >
> > On Sat, Jan 30, 2021 at 01:19:14AM +0100, Niklas Söderlund wrote:
> > > Terminating the event loop with EventLoop::exit() does not grantee that
> >
> > s/grantee/guarantee
> >
> > > it will terminate. If the event loops 'call later' queue can be kept
> >
> > event loop
> >
> > > non-empty by continuously queuing new calls using EventLoop::callLater()
> > > either from a different thread or from callbacks in the loop itself
> > > EventLoop::dispatchCalls() will never complete and the loop will run
> > > forever.
> > >
> > > Solve this by only executing the already queued calls each invocation of
> > > dispatchCalls() and only enter the idle loop if dispatchCalls() had no
> > > calls to dispatch.
> > >
> > > Reported-by: Sebastian Fricke <sebastian.fricke at posteo.net>
> > > Fixes: f49e93338b6309a6 ("cam: event_loop: Add deferred calls support")
> > > Signed-off-by: Niklas Söderlund <niklas.soderlund at ragnatech.se>
> > > ---
> > > src/cam/event_loop.cpp | 21 +++++++++------------
> > > src/cam/event_loop.h | 2 +-
> > > 2 files changed, 10 insertions(+), 13 deletions(-)
> > >
> > > diff --git a/src/cam/event_loop.cpp b/src/cam/event_loop.cpp
> > > index 94c5d1d362455f33..f0b1ecbb6244c40a 100644
> > > --- a/src/cam/event_loop.cpp
> > > +++ b/src/cam/event_loop.cpp
> > > @@ -41,8 +41,8 @@ int EventLoop::exec()
> > > exit_.store(false, std::memory_order_release);
> > >
> > > while (!exit_.load(std::memory_order_acquire)) {
> > > - dispatchCalls();
> > > - event_base_loop(event_, EVLOOP_NO_EXIT_ON_EMPTY);
> > > + if (!dispatchCalls())
> > > + event_base_loop(event_, EVLOOP_NO_EXIT_ON_EMPTY);
> > > }
> > >
> > > return exitCode_;
> > > @@ -70,17 +70,14 @@ void EventLoop::callLater(const std::function<void()> &func)
> > > interrupt();
> > > }
> > >
> > > -void EventLoop::dispatchCalls()
> > > +bool EventLoop::dispatchCalls()
> > > {
> > > - std::unique_lock<std::mutex> locker(lock_);
> > > + lock_.lock();
> > > + std::list<std::function<void()>> calls = std::move(calls_);
> > > + lock_.unlock();
> >
> > After this last unlock:
> > calls contains all the callbacks
> > calls_ now empty
> > calls_ can be populated with more callbacks as there's no lock held
>
> Correct.
>
> >
> > >
> > > - for (auto iter = calls_.begin(); iter != calls_.end(); ) {
> > > - std::function<void()> call = std::move(*iter);
> > > -
> > > - iter = calls_.erase(iter);
> > > -
> > > - locker.unlock();
> > > + for (std::function<void()> call : calls)
> > > call();
> >
> > > - locker.lock();
> > > - }
> > > +
> > > + return !calls.empty();
> >
> > How can calls be empty as you never remove items ?
>
> If calls_ was empty at the time of the std::move(). And this only
> happens when there is no calls to be processed and we should enter the
> event_base_loop() idle loop.
>
> Reading the diff now I could have done something like this to make this
> more clear,
>
> bool EventLoop::dispatchCalls()
> {
> lock_.lock();
> std::list<std::function<void()>> calls = std::move(calls_);
> lock_.unlock();
>
> if (calls.empty())
> return false;
>
> for (std::function<void()> call : calls)
> call();
>
> return true;
> }
Ah ok, this is cleaner and..
>
> > As callbacks can be add to calls_ while you are iteraring here, won't
> > them be lost forever ?
>
> How?
>
> While iterating here more calls may be added calls_. If we iterate one
> ore more calls here dispatchCalls() will return true. The only call site
> is exec(),
.. make the windown smaller, as if I'm not mistaken you still have a
window.
lock()
calls = move(calls_)
unlock()
<------------- calls_.push_back(new callback)
if (calls.empty())
return false;
Can't this happen ? I don't know very much the cam event model, so I
might be missing something.
(nit: I would make dispatchCalls() return true if all calls are
dispatched)
if (dispatchCalls())
exit();
>
> int EventLoop::exec()
> {
> exitCode_ = -1;
> exit_.store(false, std::memory_order_release);
>
> while (!exit_.load(std::memory_order_acquire)) {
> if (!dispatchCalls())
> event_base_loop(event_, EVLOOP_NO_EXIT_ON_EMPTY);
> }
>
> return exitCode_;
> }
>
> On dispatchCalls() returning true we do not enter the event_base_loop()
> idle loop and instead call dispatchCalls() once more consuming any calls
> appended to calls_.
>
> >
> > I feel like I'm missing something
>
> Might as well be me who are missing things :-)
>
> >
> > > }
> > > diff --git a/src/cam/event_loop.h b/src/cam/event_loop.h
> > > index 408073c50594d09d..b2535f7bdd96742a 100644
> > > --- a/src/cam/event_loop.h
> > > +++ b/src/cam/event_loop.h
> > > @@ -38,7 +38,7 @@ private:
> > > std::mutex lock_;
> > >
> > > void interrupt();
> > > - void dispatchCalls();
> > > + bool dispatchCalls();
> > > };
> > >
> > > #endif /* __CAM_EVENT_LOOP_H__ */
> > > --
> > > 2.30.0
> > >
> > > _______________________________________________
> > > libcamera-devel mailing list
> > > libcamera-devel at lists.libcamera.org
> > > https://lists.libcamera.org/listinfo/libcamera-devel
>
> --
> Regards,
> Niklas Söderlund
More information about the libcamera-devel
mailing list