[libcamera-devel] [PATCH v5 1/6] libcamera: CameraManager: Drop the vector of created PipelineHandlers

Laurent Pinchart laurent.pinchart at ideasonboard.com
Tue Jun 16 23:27:09 CEST 2020


Hi Umang,

Thank you for the patch.

On Tue, Jun 16, 2020 at 07:45:33PM +0000, Umang Jain wrote:
> The pipes_ vector was initially used to store pipeline handlers
> instances with the CameraManager when it cannot be referenced from
> anywhere else. It was used to retrieve cameras and deleting pipeline
> handlers when stopping the camera manager.
> 
> In f3695e9b09ce ("libcamera: camera_manager: Register cameras with the
> camera manager"), cameras started to get registered directly with camera
> manager and in 5b02e03199b7 ("libcamera: camera: Associate cameras with
> their pipeline handler") pipeline handler started to get stored in a

s/handler/handlers/

> std::shared_ptr<> with each camera starting to hold a strong reference
> to its associated pipeline-handler. At this point, both the camera

s/pipeline-handler/pipeline handler/

> manager and the camera held a strong reference to the pipeline handler.
> 
> Since the additional reference held by the camera manager gets released
> only on cleanup(), this lurking reference held on pipeline handler, did

s/handler,/handler/

> not allow it to get destroyed the even when cameras instances have been

s/the //

> destroyed. This situation of having a pipeline handler instance around
> without having a camera, may lead to problems (one of them explained

s/camera,/camera/

> below) especially when the camera manager is still running.
> 
> It was noticed that, there was a dangling driver directory issue (tested
> for UVC camera - in /sys/bus/usb/drivers/uvcvideo) on 'unbind' → 'bind'
> operation while the CameraManager is running. The directories were still
> kept around even after 'unbind' because of the lurking reference of
> pipeline handler holding onto them. That reference would clear if and
> only if the CameraManager is stopped and then only directories were
> getting removed in the above stated path.
> 
> Rather than writing a fix to release the pipeline handlers' reference
> from camera manager on camera disconnection, it is decided to eliminate
> the pipes_ vector from CameraManager moving forwards. There is no
> point in holding a reference to it from camera manager's point-of-view
> at this stage. It also helps us to fix the issue as explained above.
> 
> Now that the pipeline handler instances are referenced via cameras
> only, it can happen that the destruction of last camera instance may

s/of last/of the last/

> result into destruction of pipeline handler itself. Such a possibility

s/into/in/
s/pipeline/the pipeline/

> exists PipelineHandler::disconnect(), where the pipeline handler itself

s/exists/exists in/

> can get destroyed while removing the camera. This is acceptable as long
> as we make sure that there is no access of pipeline handler's members

s/of/to the/

> later on in the code-path. Address this situation and also add a

s/code-path/code path/

> detailed comment about it.
> 
> Signed-off-by: Umang Jain <email at uajain.com>

A commit message longer than the commit itself is often the sign a
problem well understood :-)

> ---
>  src/libcamera/camera_manager.cpp   |  8 ++------
>  src/libcamera/pipeline_handler.cpp | 18 +++++++++++++++---
>  2 files changed, 17 insertions(+), 9 deletions(-)
> 
> diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp
> index 576856a..dbdc78e 100644
> --- a/src/libcamera/camera_manager.cpp
> +++ b/src/libcamera/camera_manager.cpp
> @@ -63,7 +63,6 @@ private:
>  	bool initialized_;
>  	int status_;
>  
> -	std::vector<std::shared_ptr<PipelineHandler>> pipes_;
>  	std::unique_ptr<DeviceEnumerator> enumerator_;
>  
>  	IPAManager ipaManager_;
> @@ -144,7 +143,6 @@ int CameraManager::Private::init()
>  			LOG(Camera, Debug)
>  				<< "Pipeline handler \"" << factory->name()
>  				<< "\" matched";
> -			pipes_.push_back(std::move(pipe));
>  		}
>  	}
>  
> @@ -158,11 +156,9 @@ void CameraManager::Private::cleanup()
>  	/* TODO: unregister hot-plug callback here */
>  
>  	/*
> -	 * Release all references to cameras and pipeline handlers to ensure
> -	 * they all get destroyed before the device enumerator deletes the
> -	 * media devices.
> +	 * Release all references to cameras to ensure they all get destroyed
> +	 * before the device enumerator deletes the media devices.
>  	 */
> -	pipes_.clear();
>  	cameras_.clear();
>  
>  	enumerator_.reset(nullptr);
> diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
> index a0f6b0f..c457be9 100644
> --- a/src/libcamera/pipeline_handler.cpp
> +++ b/src/libcamera/pipeline_handler.cpp
> @@ -559,7 +559,21 @@ void PipelineHandler::mediaDeviceDisconnected(MediaDevice *media)
>   */
>  void PipelineHandler::disconnect()
>  {
> -	for (std::weak_ptr<Camera> ptr : cameras_) {
> +	/*
> +	 * Each camera holds a reference to its associated pipeline handler
> +	 * instance. Hence, when the last camera is dropped, the pipeline
> +	 * handler will get destroyed by the last manager_->removeCamera(camera)
> +	 * call in the loop below.
> +	 *
> +	 * This is acceptable as long as we make sure that the code path does not
> +	 * access any member of the (already destroyed)pipeline handler instance

s/pipeline/ pipeline/

Reviewed-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>

> +	 * afterwards. Therefore, we move the cameras_ vector to a local temporary
> +	 * container to avoid accessing freed memory later i.e. to explicitly run
> +	 * cameras_.clear().
> +	 */
> +	std::vector<std::weak_ptr<Camera>> cameras{ std::move(cameras_) };
> +
> +	for (std::weak_ptr<Camera> ptr : cameras) {
>  		std::shared_ptr<Camera> camera = ptr.lock();
>  		if (!camera)
>  			continue;
> @@ -567,8 +581,6 @@ void PipelineHandler::disconnect()
>  		camera->disconnect();
>  		manager_->removeCamera(camera.get());
>  	}
> -
> -	cameras_.clear();
>  }
>  
>  /**

-- 
Regards,

Laurent Pinchart


More information about the libcamera-devel mailing list