[libcamera-devel] [PATCH v2 04/11] libcamera: camera_manager: Make the class a singleton

Ricky Liang jcliang at chromium.org
Mon Jan 14 09:19:50 CET 2019


Hi Laurent,

On Sat, Jan 12, 2019 at 12:02 AM Laurent Pinchart
<laurent.pinchart at ideasonboard.com> wrote:
>
> Hi Ricky,
>
> On Tuesday, 8 January 2019 16:48:12 EET Ricky Liang wrote:
> > On Tue, Jan 8, 2019 at 7:10 AM Laurent Pinchart wrote:
> > > There can only be a single camera manager instance in the application.
> > > Creating it as a singleton helps avoiding mistakes. It also allows the
> > > camera manager to be used as a storage of global data, such as the
> > > future event dispatcher.
> > >
> > > Signed-off-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> > > Reviewed-by: Niklas Söderlund <niklas.soderlund at ragnatech.se>
> > > Reviewed-by: Jacopo Mondi <jacopo at jmondi.org>
> > > ---
> > > Changes since v1:
> > >
> > > - Delete copy constructor and assignment operator
> > > - Fix documentation style issue
> > > ---
> > >
> > >  include/libcamera/camera_manager.h |  8 ++++++--
> > >  src/libcamera/camera_manager.cpp   | 15 +++++++++++++++
> > >  test/list.cpp                      |  7 +------
> > >  3 files changed, 22 insertions(+), 8 deletions(-)
> > >
> > > diff --git a/include/libcamera/camera_manager.h
> > > b/include/libcamera/camera_manager.h index 2768a5bd2384..e14da0f893b3
> > > 100644
> > > --- a/include/libcamera/camera_manager.h
> > > +++ b/include/libcamera/camera_manager.h
> > > @@ -19,15 +19,19 @@ class PipelineHandler;
> > >  class CameraManager
> > >  {
> > >  public:
> > > -       CameraManager();
> > > -
> > >         int start();
> > >         void stop();
> > >
> > >         std::vector<std::string> list() const;
> > >         Camera *get(const std::string &name);
> > >
> > > +       static CameraManager *instance();
> > > +
> > >  private:
> > > +       CameraManager();
> > > +       CameraManager(const CameraManager &) = delete;
> > > +       void operator=(const CameraManager &) = delete;
> > > +
> > >         DeviceEnumerator *enumerator_;
> > >         std::vector<PipelineHandler *> pipes_;
> >
> > Not directly related to this patch, but have we considered making
> > these pointers std::unique_ptr? From our experience working in
> > Chromium we find it informative, easier to follow the code, and less
> > error-prone if an object's ownership can be clearly identified through
> > an std::unique_ptr.
> >
> > For example, in the current libcamera code base I noticed there's a
> > potential leak in DeviceEnumerator::create() if enumerator->init()
> > fails:
> >
> > https://git.linuxtv.org/libcamera.git/tree/src/libcamera/device_enumerator.c
> > pp#n137
> >
> > If we instead only create and pass std::unique_ptr<DeviceEnumerator>
> > around then we'd avoid leak like this, and people can also look at the
> > code and clearly understands that CameraManager has the ownership of
> > enumerator_.
>
> I agree with you, std::unique_ptr<> would here both provide the advantages of
> a scoped pointer with automatic deletion, and make it clear who owns the
> object. I gave it a try for enumerator_, and here is what I ended up with
> (quote characters added to comment inline).
>
> > diff --git a/include/libcamera/camera_manager.h b/include/libcamera/camera_manager.h
> > index 15e7c162032a..6cfcba3c3933 100644
> > --- a/include/libcamera/camera_manager.h
> > +++ b/include/libcamera/camera_manager.h
> > @@ -7,6 +7,7 @@
> >  #ifndef __LIBCAMERA_CAMERA_MANAGER_H__
> >  #define __LIBCAMERA_CAMERA_MANAGER_H__
> >
> > +#include <memory>
> >  #include <string>
> >  #include <vector>
> >
> > @@ -37,7 +38,7 @@ private:
> >       void operator=(const CameraManager &) = delete;
> >       ~CameraManager();
> >
> > -     DeviceEnumerator *enumerator_;
> > +     std::unique_ptr<DeviceEnumerator> enumerator_;
> >       std::vector<PipelineHandler *> pipes_;
>
> pipes_ left out as they will disappear in the near future, to be replaced with
> a vector of reference-counted camera objects.
>
> >
> >       EventDispatcher *dispatcher_;
> > diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp
> > index be327f5d5638..432f2b0a11c5 100644
> > --- a/src/libcamera/camera_manager.cpp
> > +++ b/src/libcamera/camera_manager.cpp
> > @@ -61,7 +61,6 @@ CameraManager::~CameraManager()
> >   */
> >  int CameraManager::start()
> >  {
> > -
> >       if (enumerator_)
> >               return -ENODEV;
> >
> > @@ -84,7 +83,7 @@ int CameraManager::start()
> >                * all pipelines it can provide.
> >                */
> >               do {
> > -                     pipe = PipelineHandlerFactory::create(handler, enumerator_);
> > +                     pipe = PipelineHandlerFactory::create(handler, enumerator_.get());
>
> We already break the unique_ptr<> promise :-) If this acceptable according to
> you, or is there a better way ?

If we're not going to change the internal state of enumerator_, then
we can make PipelineHandlerFactory::create take a const reference
instead of a pointer. In general we use const reference for
method/function arguments that stay unchanged after calling the
method/function, and pointer for output arguments.

Wdyt?

>
> >                       if (pipe)
> >                               pipes_.push_back(pipe);
> >               } while (pipe);
> > @@ -114,10 +113,7 @@ void CameraManager::stop()
> >
> >       pipes_.clear();
> >
> > -     if (enumerator_)
> > -             delete enumerator_;
> > -
> > -     enumerator_ = nullptr;
> > +     enumerator_.reset(nullptr);
> >  }
> >
> >  /**
> > diff --git a/src/libcamera/device_enumerator.cpp b/src/libcamera/device_enumerator.cpp
> > index 0d18e75525af..2b03b191fa5d 100644
> > --- a/src/libcamera/device_enumerator.cpp
> > +++ b/src/libcamera/device_enumerator.cpp
> > @@ -14,6 +14,7 @@
> >  #include "device_enumerator.h"
> >  #include "log.h"
> >  #include "media_device.h"
> > +#include "utils.h"
> >
> >  /**
> >   * \file device_enumerator.h
> > @@ -128,20 +129,20 @@ bool DeviceMatch::match(const MediaDevice *device) const
> >   * \return A pointer to the newly created device enumerator on success, or
> >   * nullptr if an error occurs
> >   */
> > -DeviceEnumerator *DeviceEnumerator::create()
> > +std::unique_ptr<DeviceEnumerator> DeviceEnumerator::create()
> >  {
> > -     DeviceEnumerator *enumerator;
> > +     std::unique_ptr<DeviceEnumerator> enumerator;
> >
> >       /**
> >        * \todo Add compile time checks to only try udev enumerator if libudev
> >        * is available.
> >        */
> > -     enumerator = new DeviceEnumeratorUdev();
> > +     enumerator = std::make_unique<DeviceEnumeratorUdev>(); /* [1] */
> > +     enumerator = std::unique_ptr<DeviceEnumeratorUdev>(new DeviceEnumeratorUdev()); /* [2] */
> > +     enumerator.reset(new DeviceEnumeratorUdev()); /* [3] */
>
> Here are three different ways of implementing the same operation.
> std::make_unique<> doesn't exist in C++11, so I've defined it in utils.c.
> Adding functions to the std namespace could be considered a big of a hack
> though, so two other implementations are proposed. The second option is
> explicit, but a bit too long for my taste, while the third option is short but
> uses reset() which sounds a bit strange for an assignment. Do you have any
> advice ?

Before we have C++11 in Chromium we also had a base::MakeUnique<>
template in the Chromium libbase handling creation of unique pointers.
If we have to stick with C++11 then we might consider doing the same,
probably with a utils:: namespace. Hacking the std:: namespace is
indeed a bad idea an can cause build errors.

Semantically I'd say [2] is more accurate than [3], but I don't really
have strong opinions between these two. If we don't want to define our
own make_unique template then we can use either.

Do we restrict ourselves in C++11 for compatibility reason?

>
> >       if (!enumerator->init())
> >               return enumerator;
> >
> > -     delete enumerator;
> > -
> >       /*
> >        * Either udev is not available or udev initialization failed. Fall back
> >        * on the sysfs enumerator.
> > diff --git a/src/libcamera/include/device_enumerator.h b/src/libcamera/include/device_enumerator.h
> > index 29737da7a225..0afaf88ce1ca 100644
> > --- a/src/libcamera/include/device_enumerator.h
> > +++ b/src/libcamera/include/device_enumerator.h
> > @@ -8,6 +8,7 @@
> >  #define __LIBCAMERA_DEVICE_ENUMERATOR_H__
> >
> >  #include <map>
> > +#include <memory>
> >  #include <string>
> >  #include <vector>
> >
> > @@ -34,7 +35,7 @@ private:
> >  class DeviceEnumerator
> >  {
> >  public:
> > -     static DeviceEnumerator *create();
> > +     static std::unique_ptr<DeviceEnumerator> create();
> >
> >       virtual ~DeviceEnumerator();
> >
> > diff --git a/src/libcamera/include/utils.h b/src/libcamera/include/utils.h
> > index 3ffa6f4ea591..6df54e758aa4 100644
> > --- a/src/libcamera/include/utils.h
> > +++ b/src/libcamera/include/utils.h
> > @@ -7,6 +7,19 @@
> >  #ifndef __LIBCAMERA_UTILS_H__
> >  #define __LIBCAMERA_UTILS_H__
> >
> > +#include <memory>
> > +
> >  #define ARRAY_SIZE(a)        (sizeof(a) / sizeof(a[0]))
> >
> > +namespace std {
> > +
> > +/* C++11 doesn't provide std::make_unique */
> > +template<typename T, typename... Args>
> > +std::unique_ptr<T> make_unique(Args&&... args)
> > +{
> > +     return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
> > +}
> > +
> > +} /* namespace std */
> > +
> >  #endif /* __LIBCAMERA_UTILS_H__ */
>
>
>
> > std::shared_ptr, on the other hand, shall be used only if absolutely
> > necessary, or it can be a recipe for creating ownership spaghetti.
> >
> > >  };
>
> [snip]
>
> --
> Regards,
>
> Laurent Pinchart
>
>


More information about the libcamera-devel mailing list