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

Laurent Pinchart laurent.pinchart at ideasonboard.com
Fri Jan 11 17:03:52 CET 2019


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

>  	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