[libcamera-devel] [PATCH 0/2] libcamera: Simplify error handling with Cleaner class

Laurent Pinchart laurent.pinchart at ideasonboard.com
Tue Oct 11 22:27:22 CEST 2022


Hi Kieran,

On Tue, Oct 11, 2022 at 03:51:19PM +0100, Kieran Bingham wrote:
> Quoting Laurent Pinchart via libcamera-devel (2022-10-04 03:18:40)
> > Hello,
> > 
> > This small patch series stems from my review of "[PATCH 13/14]
> > libcamera: pipeline: rkisp1: Factorize errors handling path on start()".
> > While I agreed that the current error handling pattern in
> > PipelineHandlerRkISP1::start() is fairly bad, I wasn't thrilled by usage
> > of goto statements to fix it. This is an alternative proposal.
> > 
> > The Cleaner class introduced in patch 1/2 took multiple forms before
> > reaching this one. I first experimented with a class that handled a
> > single action and thus needed to be instantiated once per action. To
> > achieve the equivalent of the Cleaner::clear() call I had to either add
> > an 'if (ret)' statement to the lambda functions, or pass the ret
> > variable to the constructor of the Cleaner. I found ideas for syntactic
> > sugar in https://github.com/yuri-kilochek/boost.scope_guard, but in the
> > end, neither option made me very happy.
> > 
> > Handling multiple deferred cleanup actions with one Cleaner instance
> > makes the user code (in 2/2) simpler. The "defer" name for the
> > Cleaner::defer() function was influenced by an interesting C extension
> > proposal (https://hal.inria.fr/hal-03059076/document). It uses the name
> > "guard" for the conceptual equivalent of the Cleaner class, but that
> > didn't convince me. I'm not sure to really like the "Cleaner" name
> > though, alternative naming proposals are welcome.
> 
> I'm not sure I like 'Cleaner' either. To me, this looks very much like
> the scope_exit experimental C++ features:
> 
>  https://en.cppreference.com/w/cpp/experimental/scope_exit
> 
> I like the ability to add multiple cleanups to a single instance though.

Me too. I considered going for something more similar to scope_exit, but
it would have required calling the .release() function on each scope
guard individually at the end, which isn't very nice.

> https://github.com/SergiusTheBest/ScopeExit is another interesting
> implementation, but bases the choice for a Success or Failed return
> using exceptions, so that's not of any use to us.

Same as https://en.cppreference.com/w/cpp/experimental/scope_fail.

> I (almost) like the syntatic sugar that means lambda's are hidden away:
> 
>   https://github.com/SergiusTheBest/ScopeExit/blob/master/include/ScopeExit/ScopeExit.h
> 
>   SCOPE_EXIT{ cout << "hello"; }; // will be called at the scope exit

Almost indeed.

> Which could easily be ScopeExit { }; ... but that's creating a separate
> instance for each declaration which wouldn't be easy to 'clean up' or
> cancel..., so I think that's not any better.

Exactly :-)

> > One point I'm not happy with is the need to call Cleaner::clear() in the
> > success path. This is error-prone as it could be forgotten. We could
> > switch to the opposite, with cleanups disabled by default and an
> > explicit call in the error paths, but that could also be forgotten,
> > resulting in missing cleanups in some error paths. The result would
> > possibly be worse, as error paths are less tested. Another option would
> > be to require explicit calls in both the success and error paths, and
> > complain loudly in the destructor if none of those functions have been
> > called. I'm not sure to be convinced it would be the best idea, although
> 
> Explicitly requiring either Cleaner.success() or Cleaner.fail() with an
> error if one isn't called is interesting, but I am weary that then
> someone will end up simply calling the 'wrong one' on a code path ...
> But maybe that's worrying about 'possible' bugs, rather than actual
> bugs.

That's a possible issue too.

> > finding good names for the success/error functions may help. Alternative
> > ideas are welcome too, as well as opinions regarding whether this is
> > worth it or not.
> 
> If there is automatic operations at exit, I'd call it AtScopeExit or
> ScopeExit ...

The name sounds good.

> and use something like
> 
> 
> 	(At?)ScopeExit e;
> 	e.defer([&]{ std::out << "Failed" << std::endl; });

I could also add a constructor that takes a functor, so an instance that
would have a single action wouldn't require a separate call to defer().

> 
> 	if (fail)
> 		return -EINVAL;
> 	
> 	e.cancel();
> 
> 	return 0;
> 
> But it's also just 'deferred work' so perhaps DeferredWorker would be a
> better class name than Cleaner if it is enforced to be called at both
> success/failure paths.
> 
> 
> Remembering to cleanup before a successful exit is a pain, but I bet
> that's easier to catch if it goes wrong than the alternative, as the
> 'success' path is likely to be more often used... So I actually think
> this would be 'safer'?

I think so too.

> Anyway, overall - I like the idea of a cleanup helper...
> 
> > Laurent Pinchart (2):
> >   libcamera: utils: Add Cleaner class
> >   pipeline: rkisp1: Use utils::Cleaner to simplify error handling in
> >     start
> > 
> >  include/libcamera/base/utils.h           |  13 +++
> >  src/libcamera/base/utils.cpp             | 130 +++++++++++++++++++++++
> >  src/libcamera/pipeline/rkisp1/rkisp1.cpp |  31 ++----
> >  3 files changed, 154 insertions(+), 20 deletions(-)

-- 
Regards,

Laurent Pinchart


More information about the libcamera-devel mailing list