[libcamera-devel] [PATCH 1/2] cam: event_loop: Stop queuing calls when the event loop are exiting
Niklas Söderlund
niklas.soderlund at ragnatech.se
Tue Jan 19 15:07:16 CET 2021
On 2021-01-19 14:42:27 +0100, Niklas Söderlund wrote:
> Hi Laurent,
>
> Thanks for your feedback.
>
> On 2021-01-19 14:43:17 +0200, Laurent Pinchart wrote:
> > Hi Niklas,
> >
> > Thank you for the patch.
> >
> > On Tue, Jan 19, 2021 at 01:31:09PM +0100, Niklas Söderlund wrote:
> > > Terminating the event loop with EventLoop::exit() does not grantee that
> > > it will terminate. If the event loops 'call later' queue can be kept
> > > 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 not accepting new callbacks once the event loop is
> > > exiting.
> > >
> > > 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 | 2 +-
> > > 1 file changed, 1 insertion(+), 1 deletion(-)
> > >
> > > diff --git a/src/cam/event_loop.cpp b/src/cam/event_loop.cpp
> > > index 94c5d1d362455f33..34a55da30c537ac7 100644
> > > --- a/src/cam/event_loop.cpp
> > > +++ b/src/cam/event_loop.cpp
> > > @@ -62,7 +62,7 @@ void EventLoop::interrupt()
> > >
> > > void EventLoop::callLater(const std::function<void()> &func)
> > > {
> > > - {
> > > + if (!exit_.load(std::memory_order_acquire)) {
> > > std::unique_lock<std::mutex> locker(lock_);
> > > calls_.push_back(func);
> > > }
> >
> > That's a bit of a layering violation. Would it make sense to fix this
> > differently, by ensuring that EventLoop::dispatchCalls() will only
> > handle deferred calls that are on the list when the function is called ?
> > This would also ensure that callLater(), when called from the same
> > thread, will guarantee that pending events get processed before the
> > delayed call is processed.
> >
> > I mean something along those lines (only compile-tested).
> >
> > diff --git a/src/cam/event_loop.cpp b/src/cam/event_loop.cpp
> > index 94c5d1d36245..320ade525a3f 100644
> > --- a/src/cam/event_loop.cpp
> > +++ b/src/cam/event_loop.cpp
> > @@ -74,7 +74,7 @@ void EventLoop::dispatchCalls()
> > {
> > std::unique_lock<std::mutex> locker(lock_);
> >
> > - for (auto iter = calls_.begin(); iter != calls_.end(); ) {
> > + for (auto iter = calls_.begin(), end = calls_.end(); iter != end; ) {
> > std::function<void()> call = std::move(*iter);
> >
> > iter = calls_.erase(iter);
>
> I tried something like this but thought it was nastier then I proposed
> in this patch. I do however agree that what is proposed in my patch is
> also not the most cleanest of solutions. The change you propose here
> does not work as intended, it is still venerable to an ever increasing
> call_ vector.
>
> If we would like to go with this solution how about something like this?
> It would also reduce the lock_ contention as a side effect.
>
> diff --git a/src/cam/event_loop.cpp b/src/cam/event_loop.cpp
> index 94c5d1d362455f33..4bfcc503ac1f3b83 100644
> --- a/src/cam/event_loop.cpp
> +++ b/src/cam/event_loop.cpp
> @@ -72,15 +72,13 @@ void EventLoop::callLater(const std::function<void()> &func)
>
> void EventLoop::dispatchCalls()
> {
> - std::unique_lock<std::mutex> locker(lock_);
> + std::list<std::function<void()>> calls;
>
> - for (auto iter = calls_.begin(); iter != calls_.end(); ) {
> - std::function<void()> call = std::move(*iter);
> + {
> + std::unique_lock<std::mutex> locker(lock_);
> + calls = std::move(calls_);
> + }
>
> - iter = calls_.erase(iter);
> -
> - locker.unlock();
> + for (std::function<void()> call : calls)
> call();
> - locker.lock();
> - }
> }
I Think this back as both solutions that modifies dispatchCalls()
suffers from the same flaw. Events queued before the EventLoop::exit()
are not executed if they are not already part of the active list of
calls that are processed.
What do you think of injecting a 'end of call processing' marker into
the calls_ vector and break the loop in dispatchCalls() if it's
encounter? Spontaneously I'm thinking of a 'nullptr' marker, but it's
untested.
>
> >
> >
> > --
> > Regards,
> >
> > Laurent Pinchart
>
> --
> Regards,
> Niklas Söderlund
--
Regards,
Niklas Söderlund
More information about the libcamera-devel
mailing list