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

Laurent Pinchart laurent.pinchart at ideasonboard.com
Tue Jan 15 03:20:45 CET 2019


Hi Ricky,

On Monday, 14 January 2019 10:19:50 EET Ricky Liang wrote:
> On Sat, Jan 12, 2019 at 12:02 AM Laurent Pinchart wrote:
> > 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_enumerat
> >> or.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?

While we don't have to change the state of the enumerator strictly speaking, 
we need to change the state of the MediaDevice instances it stores (as 
pointers in a vector). We call the following method for that purpose:

MediaDevice *DeviceEnumerator::search(const DeviceMatch &dm) const

which I think should not be const as it allows changing the state of an object 
owned by the enumerator.

What we really need to convey through the API is that the called function 
PipelineHandlerFactory::create() receive a borrowed references to the 
enumerator and may not access it after it returns. As far as I know there's no 
simple construct in C++ that is universally understood as expressing this, 
unlike passing a unique_ptr to express that ownership is transferred. We could 
possibly standardize on using references for that purpose (const and non-
const), but in some cases we still need pointers when passing nullptr is 
valid, so it wouldn't be a great solution. What's your opinion on this, could 
we set in stone the rule that a reference received by a function means that 
the reference is borrowed for the duration of the function only ?

> >>                       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.

I'm tempted to add our own make_unique implementation then, most likely in the 
libcamera:: or libcamera::utils:: namespace though.

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

That was the rationale, but it could be reconsidered if needed.

> >>       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