[libcamera-devel,08/10] libcamera: pipeline: Introduce stopDevice()
diff mbox series

Message ID 20211028111520.256612-9-jacopo@jmondi.org
State Superseded
Headers show
Series
  • libcamera: Introduce Fence support
Related show

Commit Message

Jacopo Mondi Oct. 28, 2021, 11:15 a.m. UTC
Since a queue of waiting Requests has been introduced, not all Requests
queued to the PipelineHandler are immediately queued to the device.

As a Camera can be stopped at any time, it is required to complete the
waiting requests after the ones queued to the device had been completed.

Introduce a pure virtual PipelineHandler::stopDevice() function to be
implemented by pipeline handlers and make the PipelineHandler::stop()
function call it before completing pending requests.

Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
---
 include/libcamera/internal/pipeline_handler.h |  3 ++-
 src/libcamera/pipeline/ipu3/ipu3.cpp          |  4 +--
 .../pipeline/raspberrypi/raspberrypi.cpp      |  4 +--
 src/libcamera/pipeline/rkisp1/rkisp1.cpp      |  4 +--
 src/libcamera/pipeline/simple/simple.cpp      |  4 +--
 src/libcamera/pipeline/uvcvideo/uvcvideo.cpp  |  4 +--
 src/libcamera/pipeline/vimc/vimc.cpp          |  4 +--
 src/libcamera/pipeline_handler.cpp            | 27 +++++++++++++++++--
 8 files changed, 39 insertions(+), 15 deletions(-)

Comments

Kieran Bingham Nov. 9, 2021, 2:49 p.m. UTC | #1
Quoting Jacopo Mondi (2021-10-28 12:15:18)
> Since a queue of waiting Requests has been introduced, not all Requests
> queued to the PipelineHandler are immediately queued to the device.
> 
> As a Camera can be stopped at any time, it is required to complete the
> waiting requests after the ones queued to the device had been completed.
> 
> Introduce a pure virtual PipelineHandler::stopDevice() function to be
> implemented by pipeline handlers and make the PipelineHandler::stop()
> function call it before completing pending requests.
> 
> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> ---
>  include/libcamera/internal/pipeline_handler.h |  3 ++-
>  src/libcamera/pipeline/ipu3/ipu3.cpp          |  4 +--
>  .../pipeline/raspberrypi/raspberrypi.cpp      |  4 +--
>  src/libcamera/pipeline/rkisp1/rkisp1.cpp      |  4 +--
>  src/libcamera/pipeline/simple/simple.cpp      |  4 +--
>  src/libcamera/pipeline/uvcvideo/uvcvideo.cpp  |  4 +--
>  src/libcamera/pipeline/vimc/vimc.cpp          |  4 +--
>  src/libcamera/pipeline_handler.cpp            | 27 +++++++++++++++++--
>  8 files changed, 39 insertions(+), 15 deletions(-)
> 
> diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h
> index afb7990ea21b..5c3c0bc5a8b3 100644
> --- a/include/libcamera/internal/pipeline_handler.h
> +++ b/include/libcamera/internal/pipeline_handler.h
> @@ -56,7 +56,7 @@ public:
>                                        std::vector<std::unique_ptr<FrameBuffer>> *buffers) = 0;
>  
>         virtual int start(Camera *camera, const ControlList *controls) = 0;
> -       virtual void stop(Camera *camera) = 0;
> +       void stop(Camera *camera);
>         bool hasPendingRequests(const Camera *camera) const;
>  
>         void queueRequest(Request *request);
> @@ -71,6 +71,7 @@ protected:
>         void hotplugMediaDevice(MediaDevice *media);
>  
>         virtual int queueRequestDevice(Camera *camera, Request *request) = 0;
> +       virtual void stopDevice(Camera *camera) = 0;
>  
>         CameraManager *manager_;
>  
> diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
> index eb714aa61099..2b2d7904f275 100644
> --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> @@ -135,7 +135,7 @@ public:
>                                std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
>  
>         int start(Camera *camera, const ControlList *controls) override;
> -       void stop(Camera *camera) override;
> +       void stopDevice(Camera *camera) override;
>  
>         int queueRequestDevice(Camera *camera, Request *request) override;
>  
> @@ -791,7 +791,7 @@ error:
>         return ret;
>  }
>  
> -void PipelineHandlerIPU3::stop(Camera *camera)
> +void PipelineHandlerIPU3::stopDevice(Camera *camera)
>  {
>         IPU3CameraData *data = cameraData(camera);
>         int ret = 0;
> diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> index 1634ca98f481..b5b7a240d5ed 100644
> --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> @@ -255,7 +255,7 @@ public:
>                                std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
>  
>         int start(Camera *camera, const ControlList *controls) override;
> -       void stop(Camera *camera) override;
> +       void stopDevice(Camera *camera) override;
>  
>         int queueRequestDevice(Camera *camera, Request *request) override;
>  
> @@ -878,7 +878,7 @@ int PipelineHandlerRPi::start(Camera *camera, const ControlList *controls)
>         return 0;
>  }
>  
> -void PipelineHandlerRPi::stop(Camera *camera)
> +void PipelineHandlerRPi::stopDevice(Camera *camera)
>  {
>         RPiCameraData *data = cameraData(camera);
>  
> diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> index 980088628523..9860fa84a5ae 100644
> --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> @@ -146,7 +146,7 @@ public:
>                                std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
>  
>         int start(Camera *camera, const ControlList *controls) override;
> -       void stop(Camera *camera) override;
> +       void stopDevice(Camera *camera) override;
>  
>         int queueRequestDevice(Camera *camera, Request *request) override;
>  
> @@ -827,7 +827,7 @@ int PipelineHandlerRkISP1::start(Camera *camera, [[maybe_unused]] const ControlL
>         return ret;
>  }
>  
> -void PipelineHandlerRkISP1::stop(Camera *camera)
> +void PipelineHandlerRkISP1::stopDevice(Camera *camera)
>  {
>         RkISP1CameraData *data = cameraData(camera);
>         int ret;
> diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
> index 701fb4be0b71..fdff4ebd5134 100644
> --- a/src/libcamera/pipeline/simple/simple.cpp
> +++ b/src/libcamera/pipeline/simple/simple.cpp
> @@ -279,7 +279,7 @@ public:
>                                std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
>  
>         int start(Camera *camera, const ControlList *controls) override;
> -       void stop(Camera *camera) override;
> +       void stopDevice(Camera *camera) override;
>  
>         bool match(DeviceEnumerator *enumerator) override;
>  
> @@ -1036,7 +1036,7 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
>         return 0;
>  }
>  
> -void SimplePipelineHandler::stop(Camera *camera)
> +void SimplePipelineHandler::stopDevice(Camera *camera)
>  {
>         SimpleCameraData *data = cameraData(camera);
>         V4L2VideoDevice *video = data->video_;
> diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> index 264f5370cf32..40654a0bc40c 100644
> --- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> +++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> @@ -74,7 +74,7 @@ public:
>                                std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
>  
>         int start(Camera *camera, const ControlList *controls) override;
> -       void stop(Camera *camera) override;
> +       void stopDevice(Camera *camera) override;
>  
>         int queueRequestDevice(Camera *camera, Request *request) override;
>  
> @@ -250,7 +250,7 @@ int PipelineHandlerUVC::start(Camera *camera, [[maybe_unused]] const ControlList
>         return 0;
>  }
>  
> -void PipelineHandlerUVC::stop(Camera *camera)
> +void PipelineHandlerUVC::stopDevice(Camera *camera)
>  {
>         UVCCameraData *data = cameraData(camera);
>         data->video_->streamOff();
> diff --git a/src/libcamera/pipeline/vimc/vimc.cpp b/src/libcamera/pipeline/vimc/vimc.cpp
> index e453091da4b2..29960fe3f038 100644
> --- a/src/libcamera/pipeline/vimc/vimc.cpp
> +++ b/src/libcamera/pipeline/vimc/vimc.cpp
> @@ -91,7 +91,7 @@ public:
>                                std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
>  
>         int start(Camera *camera, const ControlList *controls) override;
> -       void stop(Camera *camera) override;
> +       void stopDevice(Camera *camera) override;
>  
>         int queueRequestDevice(Camera *camera, Request *request) override;
>  
> @@ -359,7 +359,7 @@ int PipelineHandlerVimc::start(Camera *camera, [[maybe_unused]] const ControlLis
>         return 0;
>  }
>  
> -void PipelineHandlerVimc::stop(Camera *camera)
> +void PipelineHandlerVimc::stopDevice(Camera *camera)
>  {
>         VimcCameraData *data = cameraData(camera);
>         data->video_->streamOff();
> diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
> index 38edd00cebad..0f9fec1b618f 100644
> --- a/src/libcamera/pipeline_handler.cpp
> +++ b/src/libcamera/pipeline_handler.cpp
> @@ -268,8 +268,7 @@ void PipelineHandler::unlock()
>   */
>  
>  /**
> - * \fn PipelineHandler::stop()
> - * \brief Stop capturing from all running streams
> + * \brief Stop capturing from all running streams and cancel pending requests
>   * \param[in] camera The camera to stop
>   *
>   * This function stops capturing and processing requests immediately. All
> @@ -277,6 +276,30 @@ void PipelineHandler::unlock()
>   *
>   * \context This function is called from the CameraManager thread.
>   */
> +void PipelineHandler::stop(Camera *camera)
> +{
> +       /* Stop the pipeline handler and let the queued requests complete. */
> +       stopDevice(camera);
> +

I /think/ it's fine - but are we certain that nothing on the
waitingRequests_ will get queued between stopDevice above and clearing
below?



> +       /* Cancel and signal as complete all requests waiting for fences. */
> +       MutexLocker lock(waitingRequestsMutex_);
> +
> +       for (auto &waitingRequest : waitingRequests_) {
> +               waitingRequest->cancel();
> +               completeRequest(waitingRequest);
> +       }
> +
> +       waitingRequests_.clear();

I'd be very happy to see:

	Camera::Private *data = camera->_d();
	ASSERT(data->queuedRequests_.empty());

as an enforcement to be sure that all requests are completed by the end
of this function.

But I think I can get away with this on this patch:


Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>


> +}
> +
> +/**
> + * \fn PipelineHandler::stopDevice()
> + * \brief Stop capturing from all running streams
> + * \param[in] camera The camera to stop
> + *
> + * This function stops capturing and processing requests immediately. All
> + * pending requests are cancelled and complete immediately in an error state.
> + */
>  
>  /**
>   * \brief Determine if the camera has any requests pending
> -- 
> 2.33.1
>
Jacopo Mondi Nov. 9, 2021, 5:47 p.m. UTC | #2
Hi Kieran

On Tue, Nov 09, 2021 at 02:49:42PM +0000, Kieran Bingham wrote:
> Quoting Jacopo Mondi (2021-10-28 12:15:18)
> > Since a queue of waiting Requests has been introduced, not all Requests
> > queued to the PipelineHandler are immediately queued to the device.
> >
> > As a Camera can be stopped at any time, it is required to complete the
> > waiting requests after the ones queued to the device had been completed.
> >
> > Introduce a pure virtual PipelineHandler::stopDevice() function to be
> > implemented by pipeline handlers and make the PipelineHandler::stop()
> > function call it before completing pending requests.
> >
> > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> > ---
> >  include/libcamera/internal/pipeline_handler.h |  3 ++-
> >  src/libcamera/pipeline/ipu3/ipu3.cpp          |  4 +--
> >  .../pipeline/raspberrypi/raspberrypi.cpp      |  4 +--
> >  src/libcamera/pipeline/rkisp1/rkisp1.cpp      |  4 +--
> >  src/libcamera/pipeline/simple/simple.cpp      |  4 +--
> >  src/libcamera/pipeline/uvcvideo/uvcvideo.cpp  |  4 +--
> >  src/libcamera/pipeline/vimc/vimc.cpp          |  4 +--
> >  src/libcamera/pipeline_handler.cpp            | 27 +++++++++++++++++--
> >  8 files changed, 39 insertions(+), 15 deletions(-)
> >
> > diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h
> > index afb7990ea21b..5c3c0bc5a8b3 100644
> > --- a/include/libcamera/internal/pipeline_handler.h
> > +++ b/include/libcamera/internal/pipeline_handler.h
> > @@ -56,7 +56,7 @@ public:
> >                                        std::vector<std::unique_ptr<FrameBuffer>> *buffers) = 0;
> >
> >         virtual int start(Camera *camera, const ControlList *controls) = 0;
> > -       virtual void stop(Camera *camera) = 0;
> > +       void stop(Camera *camera);
> >         bool hasPendingRequests(const Camera *camera) const;
> >
> >         void queueRequest(Request *request);
> > @@ -71,6 +71,7 @@ protected:
> >         void hotplugMediaDevice(MediaDevice *media);
> >
> >         virtual int queueRequestDevice(Camera *camera, Request *request) = 0;
> > +       virtual void stopDevice(Camera *camera) = 0;
> >
> >         CameraManager *manager_;
> >
> > diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
> > index eb714aa61099..2b2d7904f275 100644
> > --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> > +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> > @@ -135,7 +135,7 @@ public:
> >                                std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
> >
> >         int start(Camera *camera, const ControlList *controls) override;
> > -       void stop(Camera *camera) override;
> > +       void stopDevice(Camera *camera) override;
> >
> >         int queueRequestDevice(Camera *camera, Request *request) override;
> >
> > @@ -791,7 +791,7 @@ error:
> >         return ret;
> >  }
> >
> > -void PipelineHandlerIPU3::stop(Camera *camera)
> > +void PipelineHandlerIPU3::stopDevice(Camera *camera)
> >  {
> >         IPU3CameraData *data = cameraData(camera);
> >         int ret = 0;
> > diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> > index 1634ca98f481..b5b7a240d5ed 100644
> > --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> > +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> > @@ -255,7 +255,7 @@ public:
> >                                std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
> >
> >         int start(Camera *camera, const ControlList *controls) override;
> > -       void stop(Camera *camera) override;
> > +       void stopDevice(Camera *camera) override;
> >
> >         int queueRequestDevice(Camera *camera, Request *request) override;
> >
> > @@ -878,7 +878,7 @@ int PipelineHandlerRPi::start(Camera *camera, const ControlList *controls)
> >         return 0;
> >  }
> >
> > -void PipelineHandlerRPi::stop(Camera *camera)
> > +void PipelineHandlerRPi::stopDevice(Camera *camera)
> >  {
> >         RPiCameraData *data = cameraData(camera);
> >
> > diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> > index 980088628523..9860fa84a5ae 100644
> > --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> > +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> > @@ -146,7 +146,7 @@ public:
> >                                std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
> >
> >         int start(Camera *camera, const ControlList *controls) override;
> > -       void stop(Camera *camera) override;
> > +       void stopDevice(Camera *camera) override;
> >
> >         int queueRequestDevice(Camera *camera, Request *request) override;
> >
> > @@ -827,7 +827,7 @@ int PipelineHandlerRkISP1::start(Camera *camera, [[maybe_unused]] const ControlL
> >         return ret;
> >  }
> >
> > -void PipelineHandlerRkISP1::stop(Camera *camera)
> > +void PipelineHandlerRkISP1::stopDevice(Camera *camera)
> >  {
> >         RkISP1CameraData *data = cameraData(camera);
> >         int ret;
> > diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
> > index 701fb4be0b71..fdff4ebd5134 100644
> > --- a/src/libcamera/pipeline/simple/simple.cpp
> > +++ b/src/libcamera/pipeline/simple/simple.cpp
> > @@ -279,7 +279,7 @@ public:
> >                                std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
> >
> >         int start(Camera *camera, const ControlList *controls) override;
> > -       void stop(Camera *camera) override;
> > +       void stopDevice(Camera *camera) override;
> >
> >         bool match(DeviceEnumerator *enumerator) override;
> >
> > @@ -1036,7 +1036,7 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
> >         return 0;
> >  }
> >
> > -void SimplePipelineHandler::stop(Camera *camera)
> > +void SimplePipelineHandler::stopDevice(Camera *camera)
> >  {
> >         SimpleCameraData *data = cameraData(camera);
> >         V4L2VideoDevice *video = data->video_;
> > diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> > index 264f5370cf32..40654a0bc40c 100644
> > --- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> > +++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> > @@ -74,7 +74,7 @@ public:
> >                                std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
> >
> >         int start(Camera *camera, const ControlList *controls) override;
> > -       void stop(Camera *camera) override;
> > +       void stopDevice(Camera *camera) override;
> >
> >         int queueRequestDevice(Camera *camera, Request *request) override;
> >
> > @@ -250,7 +250,7 @@ int PipelineHandlerUVC::start(Camera *camera, [[maybe_unused]] const ControlList
> >         return 0;
> >  }
> >
> > -void PipelineHandlerUVC::stop(Camera *camera)
> > +void PipelineHandlerUVC::stopDevice(Camera *camera)
> >  {
> >         UVCCameraData *data = cameraData(camera);
> >         data->video_->streamOff();
> > diff --git a/src/libcamera/pipeline/vimc/vimc.cpp b/src/libcamera/pipeline/vimc/vimc.cpp
> > index e453091da4b2..29960fe3f038 100644
> > --- a/src/libcamera/pipeline/vimc/vimc.cpp
> > +++ b/src/libcamera/pipeline/vimc/vimc.cpp
> > @@ -91,7 +91,7 @@ public:
> >                                std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
> >
> >         int start(Camera *camera, const ControlList *controls) override;
> > -       void stop(Camera *camera) override;
> > +       void stopDevice(Camera *camera) override;
> >
> >         int queueRequestDevice(Camera *camera, Request *request) override;
> >
> > @@ -359,7 +359,7 @@ int PipelineHandlerVimc::start(Camera *camera, [[maybe_unused]] const ControlLis
> >         return 0;
> >  }
> >
> > -void PipelineHandlerVimc::stop(Camera *camera)
> > +void PipelineHandlerVimc::stopDevice(Camera *camera)
> >  {
> >         VimcCameraData *data = cameraData(camera);
> >         data->video_->streamOff();
> > diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
> > index 38edd00cebad..0f9fec1b618f 100644
> > --- a/src/libcamera/pipeline_handler.cpp
> > +++ b/src/libcamera/pipeline_handler.cpp
> > @@ -268,8 +268,7 @@ void PipelineHandler::unlock()
> >   */
> >
> >  /**
> > - * \fn PipelineHandler::stop()
> > - * \brief Stop capturing from all running streams
> > + * \brief Stop capturing from all running streams and cancel pending requests
> >   * \param[in] camera The camera to stop
> >   *
> >   * This function stops capturing and processing requests immediately. All
> > @@ -277,6 +276,30 @@ void PipelineHandler::unlock()
> >   *
> >   * \context This function is called from the CameraManager thread.
> >   */
> > +void PipelineHandler::stop(Camera *camera)
> > +{
> > +       /* Stop the pipeline handler and let the queued requests complete. */
> > +       stopDevice(camera);
> > +
>
> I /think/ it's fine - but are we certain that nothing on the
> waitingRequests_ will get queued between stopDevice above and clearing
> below?

The camera state machine shouldn't allow new Requests to be queued if
the camera is stopped and the Camera::stop() documentations says

 * \context This function may be called in any camera state as defined in \ref
 * camera_operation, and shall be synchronized by the caller with other
 * functions that affect the camera state. If called when the camera isn't
 * running, it is a no-op.

>
>
>
> > +       /* Cancel and signal as complete all requests waiting for fences. */
> > +       MutexLocker lock(waitingRequestsMutex_);
> > +
> > +       for (auto &waitingRequest : waitingRequests_) {
> > +               waitingRequest->cancel();
> > +               completeRequest(waitingRequest);
> > +       }
> > +
> > +       waitingRequests_.clear();
>
> I'd be very happy to see:
>
> 	Camera::Private *data = camera->_d();
> 	ASSERT(data->queuedRequests_.empty());
>
> as an enforcement to be sure that all requests are completed by the end
> of this function.

Afaict stopDevice() is synchronous, it should complete all requests in
flight in error state before returning
>
> But I think I can get away with this on this patch:
>

Might be a useful validation to ASSERT though

>
> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
>
>
> > +}
> > +
> > +/**
> > + * \fn PipelineHandler::stopDevice()
> > + * \brief Stop capturing from all running streams
> > + * \param[in] camera The camera to stop
> > + *
> > + * This function stops capturing and processing requests immediately. All
> > + * pending requests are cancelled and complete immediately in an error state.
> > + */
> >
> >  /**
> >   * \brief Determine if the camera has any requests pending
> > --
> > 2.33.1
> >
Kieran Bingham Nov. 9, 2021, 5:54 p.m. UTC | #3
Quoting Jacopo Mondi (2021-11-09 17:47:26)
> Hi Kieran
> 
> On Tue, Nov 09, 2021 at 02:49:42PM +0000, Kieran Bingham wrote:
> > Quoting Jacopo Mondi (2021-10-28 12:15:18)
> > > Since a queue of waiting Requests has been introduced, not all Requests
> > > queued to the PipelineHandler are immediately queued to the device.
> > >
> > > As a Camera can be stopped at any time, it is required to complete the
> > > waiting requests after the ones queued to the device had been completed.
> > >
> > > Introduce a pure virtual PipelineHandler::stopDevice() function to be
> > > implemented by pipeline handlers and make the PipelineHandler::stop()
> > > function call it before completing pending requests.
> > >
> > > Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> > > ---
> > >  include/libcamera/internal/pipeline_handler.h |  3 ++-
> > >  src/libcamera/pipeline/ipu3/ipu3.cpp          |  4 +--
> > >  .../pipeline/raspberrypi/raspberrypi.cpp      |  4 +--
> > >  src/libcamera/pipeline/rkisp1/rkisp1.cpp      |  4 +--
> > >  src/libcamera/pipeline/simple/simple.cpp      |  4 +--
> > >  src/libcamera/pipeline/uvcvideo/uvcvideo.cpp  |  4 +--
> > >  src/libcamera/pipeline/vimc/vimc.cpp          |  4 +--
> > >  src/libcamera/pipeline_handler.cpp            | 27 +++++++++++++++++--
> > >  8 files changed, 39 insertions(+), 15 deletions(-)
> > >
> > > diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h
> > > index afb7990ea21b..5c3c0bc5a8b3 100644
> > > --- a/include/libcamera/internal/pipeline_handler.h
> > > +++ b/include/libcamera/internal/pipeline_handler.h
> > > @@ -56,7 +56,7 @@ public:
> > >                                        std::vector<std::unique_ptr<FrameBuffer>> *buffers) = 0;
> > >
> > >         virtual int start(Camera *camera, const ControlList *controls) = 0;
> > > -       virtual void stop(Camera *camera) = 0;
> > > +       void stop(Camera *camera);
> > >         bool hasPendingRequests(const Camera *camera) const;
> > >
> > >         void queueRequest(Request *request);
> > > @@ -71,6 +71,7 @@ protected:
> > >         void hotplugMediaDevice(MediaDevice *media);
> > >
> > >         virtual int queueRequestDevice(Camera *camera, Request *request) = 0;
> > > +       virtual void stopDevice(Camera *camera) = 0;
> > >
> > >         CameraManager *manager_;
> > >
> > > diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
> > > index eb714aa61099..2b2d7904f275 100644
> > > --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> > > +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> > > @@ -135,7 +135,7 @@ public:
> > >                                std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
> > >
> > >         int start(Camera *camera, const ControlList *controls) override;
> > > -       void stop(Camera *camera) override;
> > > +       void stopDevice(Camera *camera) override;
> > >
> > >         int queueRequestDevice(Camera *camera, Request *request) override;
> > >
> > > @@ -791,7 +791,7 @@ error:
> > >         return ret;
> > >  }
> > >
> > > -void PipelineHandlerIPU3::stop(Camera *camera)
> > > +void PipelineHandlerIPU3::stopDevice(Camera *camera)
> > >  {
> > >         IPU3CameraData *data = cameraData(camera);
> > >         int ret = 0;
> > > diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> > > index 1634ca98f481..b5b7a240d5ed 100644
> > > --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> > > +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> > > @@ -255,7 +255,7 @@ public:
> > >                                std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
> > >
> > >         int start(Camera *camera, const ControlList *controls) override;
> > > -       void stop(Camera *camera) override;
> > > +       void stopDevice(Camera *camera) override;
> > >
> > >         int queueRequestDevice(Camera *camera, Request *request) override;
> > >
> > > @@ -878,7 +878,7 @@ int PipelineHandlerRPi::start(Camera *camera, const ControlList *controls)
> > >         return 0;
> > >  }
> > >
> > > -void PipelineHandlerRPi::stop(Camera *camera)
> > > +void PipelineHandlerRPi::stopDevice(Camera *camera)
> > >  {
> > >         RPiCameraData *data = cameraData(camera);
> > >
> > > diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> > > index 980088628523..9860fa84a5ae 100644
> > > --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> > > +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> > > @@ -146,7 +146,7 @@ public:
> > >                                std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
> > >
> > >         int start(Camera *camera, const ControlList *controls) override;
> > > -       void stop(Camera *camera) override;
> > > +       void stopDevice(Camera *camera) override;
> > >
> > >         int queueRequestDevice(Camera *camera, Request *request) override;
> > >
> > > @@ -827,7 +827,7 @@ int PipelineHandlerRkISP1::start(Camera *camera, [[maybe_unused]] const ControlL
> > >         return ret;
> > >  }
> > >
> > > -void PipelineHandlerRkISP1::stop(Camera *camera)
> > > +void PipelineHandlerRkISP1::stopDevice(Camera *camera)
> > >  {
> > >         RkISP1CameraData *data = cameraData(camera);
> > >         int ret;
> > > diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
> > > index 701fb4be0b71..fdff4ebd5134 100644
> > > --- a/src/libcamera/pipeline/simple/simple.cpp
> > > +++ b/src/libcamera/pipeline/simple/simple.cpp
> > > @@ -279,7 +279,7 @@ public:
> > >                                std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
> > >
> > >         int start(Camera *camera, const ControlList *controls) override;
> > > -       void stop(Camera *camera) override;
> > > +       void stopDevice(Camera *camera) override;
> > >
> > >         bool match(DeviceEnumerator *enumerator) override;
> > >
> > > @@ -1036,7 +1036,7 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
> > >         return 0;
> > >  }
> > >
> > > -void SimplePipelineHandler::stop(Camera *camera)
> > > +void SimplePipelineHandler::stopDevice(Camera *camera)
> > >  {
> > >         SimpleCameraData *data = cameraData(camera);
> > >         V4L2VideoDevice *video = data->video_;
> > > diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> > > index 264f5370cf32..40654a0bc40c 100644
> > > --- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> > > +++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> > > @@ -74,7 +74,7 @@ public:
> > >                                std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
> > >
> > >         int start(Camera *camera, const ControlList *controls) override;
> > > -       void stop(Camera *camera) override;
> > > +       void stopDevice(Camera *camera) override;
> > >
> > >         int queueRequestDevice(Camera *camera, Request *request) override;
> > >
> > > @@ -250,7 +250,7 @@ int PipelineHandlerUVC::start(Camera *camera, [[maybe_unused]] const ControlList
> > >         return 0;
> > >  }
> > >
> > > -void PipelineHandlerUVC::stop(Camera *camera)
> > > +void PipelineHandlerUVC::stopDevice(Camera *camera)
> > >  {
> > >         UVCCameraData *data = cameraData(camera);
> > >         data->video_->streamOff();
> > > diff --git a/src/libcamera/pipeline/vimc/vimc.cpp b/src/libcamera/pipeline/vimc/vimc.cpp
> > > index e453091da4b2..29960fe3f038 100644
> > > --- a/src/libcamera/pipeline/vimc/vimc.cpp
> > > +++ b/src/libcamera/pipeline/vimc/vimc.cpp
> > > @@ -91,7 +91,7 @@ public:
> > >                                std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
> > >
> > >         int start(Camera *camera, const ControlList *controls) override;
> > > -       void stop(Camera *camera) override;
> > > +       void stopDevice(Camera *camera) override;
> > >
> > >         int queueRequestDevice(Camera *camera, Request *request) override;
> > >
> > > @@ -359,7 +359,7 @@ int PipelineHandlerVimc::start(Camera *camera, [[maybe_unused]] const ControlLis
> > >         return 0;
> > >  }
> > >
> > > -void PipelineHandlerVimc::stop(Camera *camera)
> > > +void PipelineHandlerVimc::stopDevice(Camera *camera)
> > >  {
> > >         VimcCameraData *data = cameraData(camera);
> > >         data->video_->streamOff();
> > > diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
> > > index 38edd00cebad..0f9fec1b618f 100644
> > > --- a/src/libcamera/pipeline_handler.cpp
> > > +++ b/src/libcamera/pipeline_handler.cpp
> > > @@ -268,8 +268,7 @@ void PipelineHandler::unlock()
> > >   */
> > >
> > >  /**
> > > - * \fn PipelineHandler::stop()
> > > - * \brief Stop capturing from all running streams
> > > + * \brief Stop capturing from all running streams and cancel pending requests
> > >   * \param[in] camera The camera to stop
> > >   *
> > >   * This function stops capturing and processing requests immediately. All
> > > @@ -277,6 +276,30 @@ void PipelineHandler::unlock()
> > >   *
> > >   * \context This function is called from the CameraManager thread.
> > >   */
> > > +void PipelineHandler::stop(Camera *camera)
> > > +{
> > > +       /* Stop the pipeline handler and let the queued requests complete. */
> > > +       stopDevice(camera);
> > > +
> >
> > I /think/ it's fine - but are we certain that nothing on the
> > waitingRequests_ will get queued between stopDevice above and clearing
> > below?
> 
> The camera state machine shouldn't allow new Requests to be queued if
> the camera is stopped and the Camera::stop() documentations says
> 
>  * \context This function may be called in any camera state as defined in \ref
>  * camera_operation, and shall be synchronized by the caller with other
>  * functions that affect the camera state. If called when the camera isn't
>  * running, it is a no-op.

My concern wasn't so much from external requests being queued, but from
any potential event triggering (such as a fence completion) that might
move a request from the waitingRequests_ to the data->queuedRequests_()
at this point, before we clear it down...



> 
> >
> >
> >
> > > +       /* Cancel and signal as complete all requests waiting for fences. */
> > > +       MutexLocker lock(waitingRequestsMutex_);

Perhaps taking this lock before calling stopDevice() (if allowed) would
prevent that possibility?


> > > +
> > > +       for (auto &waitingRequest : waitingRequests_) {
> > > +               waitingRequest->cancel();
> > > +               completeRequest(waitingRequest);
> > > +       }
> > > +
> > > +       waitingRequests_.clear();
> >
> > I'd be very happy to see:
> >
> >       Camera::Private *data = camera->_d();
> >       ASSERT(data->queuedRequests_.empty());
> >
> > as an enforcement to be sure that all requests are completed by the end
> > of this function.
> 
> Afaict stopDevice() is synchronous, it should complete all requests in
> flight in error state before returning
> >
> > But I think I can get away with this on this patch:
> >
> 
> Might be a useful validation to ASSERT though
> 
> >
> > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> >
> >
> > > +}
> > > +
> > > +/**
> > > + * \fn PipelineHandler::stopDevice()
> > > + * \brief Stop capturing from all running streams
> > > + * \param[in] camera The camera to stop
> > > + *
> > > + * This function stops capturing and processing requests immediately. All
> > > + * pending requests are cancelled and complete immediately in an error state.
> > > + */
> > >
> > >  /**
> > >   * \brief Determine if the camera has any requests pending
> > > --
> > > 2.33.1
> > >
Umang Jain Nov. 10, 2021, 1:01 p.m. UTC | #4
Hello,

The patch looks fine to me as such,

On 11/9/21 11:24 PM, Kieran Bingham wrote:
> Quoting Jacopo Mondi (2021-11-09 17:47:26)
>> Hi Kieran
>>
>> On Tue, Nov 09, 2021 at 02:49:42PM +0000, Kieran Bingham wrote:
>>> Quoting Jacopo Mondi (2021-10-28 12:15:18)
>>>> Since a queue of waiting Requests has been introduced, not all Requests
>>>> queued to the PipelineHandler are immediately queued to the device.
>>>>
>>>> As a Camera can be stopped at any time, it is required to complete the
>>>> waiting requests after the ones queued to the device had been completed.
>>>>
>>>> Introduce a pure virtual PipelineHandler::stopDevice() function to be
>>>> implemented by pipeline handlers and make the PipelineHandler::stop()
>>>> function call it before completing pending requests.
>>>>
>>>> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
>>>> ---
>>>>   include/libcamera/internal/pipeline_handler.h |  3 ++-
>>>>   src/libcamera/pipeline/ipu3/ipu3.cpp          |  4 +--
>>>>   .../pipeline/raspberrypi/raspberrypi.cpp      |  4 +--
>>>>   src/libcamera/pipeline/rkisp1/rkisp1.cpp      |  4 +--
>>>>   src/libcamera/pipeline/simple/simple.cpp      |  4 +--
>>>>   src/libcamera/pipeline/uvcvideo/uvcvideo.cpp  |  4 +--
>>>>   src/libcamera/pipeline/vimc/vimc.cpp          |  4 +--
>>>>   src/libcamera/pipeline_handler.cpp            | 27 +++++++++++++++++--
>>>>   8 files changed, 39 insertions(+), 15 deletions(-)
>>>>
>>>> diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h
>>>> index afb7990ea21b..5c3c0bc5a8b3 100644
>>>> --- a/include/libcamera/internal/pipeline_handler.h
>>>> +++ b/include/libcamera/internal/pipeline_handler.h
>>>> @@ -56,7 +56,7 @@ public:
>>>>                                         std::vector<std::unique_ptr<FrameBuffer>> *buffers) = 0;
>>>>
>>>>          virtual int start(Camera *camera, const ControlList *controls) = 0;
>>>> -       virtual void stop(Camera *camera) = 0;
>>>> +       void stop(Camera *camera);
>>>>          bool hasPendingRequests(const Camera *camera) const;
>>>>
>>>>          void queueRequest(Request *request);
>>>> @@ -71,6 +71,7 @@ protected:
>>>>          void hotplugMediaDevice(MediaDevice *media);
>>>>
>>>>          virtual int queueRequestDevice(Camera *camera, Request *request) = 0;
>>>> +       virtual void stopDevice(Camera *camera) = 0;
>>>>
>>>>          CameraManager *manager_;
>>>>
>>>> diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
>>>> index eb714aa61099..2b2d7904f275 100644
>>>> --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
>>>> +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
>>>> @@ -135,7 +135,7 @@ public:
>>>>                                 std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
>>>>
>>>>          int start(Camera *camera, const ControlList *controls) override;
>>>> -       void stop(Camera *camera) override;
>>>> +       void stopDevice(Camera *camera) override;
>>>>
>>>>          int queueRequestDevice(Camera *camera, Request *request) override;
>>>>
>>>> @@ -791,7 +791,7 @@ error:
>>>>          return ret;
>>>>   }
>>>>
>>>> -void PipelineHandlerIPU3::stop(Camera *camera)
>>>> +void PipelineHandlerIPU3::stopDevice(Camera *camera)
>>>>   {
>>>>          IPU3CameraData *data = cameraData(camera);
>>>>          int ret = 0;
>>>> diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
>>>> index 1634ca98f481..b5b7a240d5ed 100644
>>>> --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
>>>> +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
>>>> @@ -255,7 +255,7 @@ public:
>>>>                                 std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
>>>>
>>>>          int start(Camera *camera, const ControlList *controls) override;
>>>> -       void stop(Camera *camera) override;
>>>> +       void stopDevice(Camera *camera) override;
>>>>
>>>>          int queueRequestDevice(Camera *camera, Request *request) override;
>>>>
>>>> @@ -878,7 +878,7 @@ int PipelineHandlerRPi::start(Camera *camera, const ControlList *controls)
>>>>          return 0;
>>>>   }
>>>>
>>>> -void PipelineHandlerRPi::stop(Camera *camera)
>>>> +void PipelineHandlerRPi::stopDevice(Camera *camera)
>>>>   {
>>>>          RPiCameraData *data = cameraData(camera);
>>>>
>>>> diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
>>>> index 980088628523..9860fa84a5ae 100644
>>>> --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
>>>> +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
>>>> @@ -146,7 +146,7 @@ public:
>>>>                                 std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
>>>>
>>>>          int start(Camera *camera, const ControlList *controls) override;
>>>> -       void stop(Camera *camera) override;
>>>> +       void stopDevice(Camera *camera) override;
>>>>
>>>>          int queueRequestDevice(Camera *camera, Request *request) override;
>>>>
>>>> @@ -827,7 +827,7 @@ int PipelineHandlerRkISP1::start(Camera *camera, [[maybe_unused]] const ControlL
>>>>          return ret;
>>>>   }
>>>>
>>>> -void PipelineHandlerRkISP1::stop(Camera *camera)
>>>> +void PipelineHandlerRkISP1::stopDevice(Camera *camera)
>>>>   {
>>>>          RkISP1CameraData *data = cameraData(camera);
>>>>          int ret;
>>>> diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
>>>> index 701fb4be0b71..fdff4ebd5134 100644
>>>> --- a/src/libcamera/pipeline/simple/simple.cpp
>>>> +++ b/src/libcamera/pipeline/simple/simple.cpp
>>>> @@ -279,7 +279,7 @@ public:
>>>>                                 std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
>>>>
>>>>          int start(Camera *camera, const ControlList *controls) override;
>>>> -       void stop(Camera *camera) override;
>>>> +       void stopDevice(Camera *camera) override;
>>>>
>>>>          bool match(DeviceEnumerator *enumerator) override;
>>>>
>>>> @@ -1036,7 +1036,7 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
>>>>          return 0;
>>>>   }
>>>>
>>>> -void SimplePipelineHandler::stop(Camera *camera)
>>>> +void SimplePipelineHandler::stopDevice(Camera *camera)
>>>>   {
>>>>          SimpleCameraData *data = cameraData(camera);
>>>>          V4L2VideoDevice *video = data->video_;
>>>> diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
>>>> index 264f5370cf32..40654a0bc40c 100644
>>>> --- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
>>>> +++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
>>>> @@ -74,7 +74,7 @@ public:
>>>>                                 std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
>>>>
>>>>          int start(Camera *camera, const ControlList *controls) override;
>>>> -       void stop(Camera *camera) override;
>>>> +       void stopDevice(Camera *camera) override;
>>>>
>>>>          int queueRequestDevice(Camera *camera, Request *request) override;
>>>>
>>>> @@ -250,7 +250,7 @@ int PipelineHandlerUVC::start(Camera *camera, [[maybe_unused]] const ControlList
>>>>          return 0;
>>>>   }
>>>>
>>>> -void PipelineHandlerUVC::stop(Camera *camera)
>>>> +void PipelineHandlerUVC::stopDevice(Camera *camera)
>>>>   {
>>>>          UVCCameraData *data = cameraData(camera);
>>>>          data->video_->streamOff();
>>>> diff --git a/src/libcamera/pipeline/vimc/vimc.cpp b/src/libcamera/pipeline/vimc/vimc.cpp
>>>> index e453091da4b2..29960fe3f038 100644
>>>> --- a/src/libcamera/pipeline/vimc/vimc.cpp
>>>> +++ b/src/libcamera/pipeline/vimc/vimc.cpp
>>>> @@ -91,7 +91,7 @@ public:
>>>>                                 std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
>>>>
>>>>          int start(Camera *camera, const ControlList *controls) override;
>>>> -       void stop(Camera *camera) override;
>>>> +       void stopDevice(Camera *camera) override;
>>>>
>>>>          int queueRequestDevice(Camera *camera, Request *request) override;
>>>>
>>>> @@ -359,7 +359,7 @@ int PipelineHandlerVimc::start(Camera *camera, [[maybe_unused]] const ControlLis
>>>>          return 0;
>>>>   }
>>>>
>>>> -void PipelineHandlerVimc::stop(Camera *camera)
>>>> +void PipelineHandlerVimc::stopDevice(Camera *camera)
>>>>   {
>>>>          VimcCameraData *data = cameraData(camera);
>>>>          data->video_->streamOff();
>>>> diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
>>>> index 38edd00cebad..0f9fec1b618f 100644
>>>> --- a/src/libcamera/pipeline_handler.cpp
>>>> +++ b/src/libcamera/pipeline_handler.cpp
>>>> @@ -268,8 +268,7 @@ void PipelineHandler::unlock()
>>>>    */
>>>>
>>>>   /**
>>>> - * \fn PipelineHandler::stop()
>>>> - * \brief Stop capturing from all running streams
>>>> + * \brief Stop capturing from all running streams and cancel pending requests
>>>>    * \param[in] camera The camera to stop
>>>>    *
>>>>    * This function stops capturing and processing requests immediately. All
>>>> @@ -277,6 +276,30 @@ void PipelineHandler::unlock()
>>>>    *
>>>>    * \context This function is called from the CameraManager thread.
>>>>    */
>>>> +void PipelineHandler::stop(Camera *camera)
>>>> +{
>>>> +       /* Stop the pipeline handler and let the queued requests complete. */
>>>> +       stopDevice(camera);
>>>> +
>>> I /think/ it's fine - but are we certain that nothing on the
>>> waitingRequests_ will get queued between stopDevice above and clearing
>>> below?
>> The camera state machine shouldn't allow new Requests to be queued if
>> the camera is stopped and the Camera::stop() documentations says
>>
>>   * \context This function may be called in any camera state as defined in \ref
>>   * camera_operation, and shall be synchronized by the caller with other
>>   * functions that affect the camera state. If called when the camera isn't
>>   * running, it is a no-op.
> My concern wasn't so much from external requests being queued, but from
> any potential event triggering (such as a fence completion) that might
> move a request from the waitingRequests_ to the data->queuedRequests_()
> at this point, before we clear it down...
>
>
>
>>>
>>>
>>>> +       /* Cancel and signal as complete all requests waiting for fences. */
>>>> +       MutexLocker lock(waitingRequestsMutex_);
> Perhaps taking this lock before calling stopDevice() (if allowed) would
> prevent that possibility?


As mentioned in previous patch, I am still un-sure why do we need this 
lock or I don't understand if any other thread is trying to access 
waitingRequests_ (either reading or queuing to it).

>
>
>>>> +
>>>> +       for (auto &waitingRequest : waitingRequests_) {
>>>> +               waitingRequest->cancel();
>>>> +               completeRequest(waitingRequest);
>>>> +       }
>>>> +
>>>> +       waitingRequests_.clear();
>>> I'd be very happy to see:
>>>
>>>        Camera::Private *data = camera->_d();
>>>        ASSERT(data->queuedRequests_.empty());
>>>
>>> as an enforcement to be sure that all requests are completed by the end
>>> of this function.
>> Afaict stopDevice() is synchronous, it should complete all requests in
>> flight in error state before returning


yes, stopDevice() (earlier's stop()) will complete all request, so it's 
safe to assume all in flight requests are completed.

>>> But I think I can get away with this on this patch:
>>>
>> Might be a useful validation to ASSERT though


Agreed, I see no harm (and more tightening)

>>
>>> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
>>>
>>>
>>>> +}
>>>> +
>>>> +/**
>>>> + * \fn PipelineHandler::stopDevice()
>>>> + * \brief Stop capturing from all running streams
>>>> + * \param[in] camera The camera to stop
>>>> + *
>>>> + * This function stops capturing and processing requests immediately. All
>>>> + * pending requests are cancelled and complete immediately in an error state.
>>>> + */
>>>>
>>>>   /**
>>>>    * \brief Determine if the camera has any requests pending
>>>> --
>>>> 2.33.1
>>>>
Laurent Pinchart Nov. 12, 2021, 3:55 p.m. UTC | #5
Hello,

On Wed, Nov 10, 2021 at 06:31:09PM +0530, Umang Jain wrote:
> On 11/9/21 11:24 PM, Kieran Bingham wrote:
> > Quoting Jacopo Mondi (2021-11-09 17:47:26)
> >> On Tue, Nov 09, 2021 at 02:49:42PM +0000, Kieran Bingham wrote:
> >>> Quoting Jacopo Mondi (2021-10-28 12:15:18)
> >>>> Since a queue of waiting Requests has been introduced, not all Requests
> >>>> queued to the PipelineHandler are immediately queued to the device.
> >>>>
> >>>> As a Camera can be stopped at any time, it is required to complete the
> >>>> waiting requests after the ones queued to the device had been completed.
> >>>>
> >>>> Introduce a pure virtual PipelineHandler::stopDevice() function to be
> >>>> implemented by pipeline handlers and make the PipelineHandler::stop()
> >>>> function call it before completing pending requests.
> >>>>
> >>>> Signed-off-by: Jacopo Mondi <jacopo@jmondi.org>
> >>>> ---
> >>>>   include/libcamera/internal/pipeline_handler.h |  3 ++-
> >>>>   src/libcamera/pipeline/ipu3/ipu3.cpp          |  4 +--
> >>>>   .../pipeline/raspberrypi/raspberrypi.cpp      |  4 +--
> >>>>   src/libcamera/pipeline/rkisp1/rkisp1.cpp      |  4 +--
> >>>>   src/libcamera/pipeline/simple/simple.cpp      |  4 +--
> >>>>   src/libcamera/pipeline/uvcvideo/uvcvideo.cpp  |  4 +--
> >>>>   src/libcamera/pipeline/vimc/vimc.cpp          |  4 +--
> >>>>   src/libcamera/pipeline_handler.cpp            | 27 +++++++++++++++++--
> >>>>   8 files changed, 39 insertions(+), 15 deletions(-)
> >>>>
> >>>> diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h
> >>>> index afb7990ea21b..5c3c0bc5a8b3 100644
> >>>> --- a/include/libcamera/internal/pipeline_handler.h
> >>>> +++ b/include/libcamera/internal/pipeline_handler.h
> >>>> @@ -56,7 +56,7 @@ public:
> >>>>                                         std::vector<std::unique_ptr<FrameBuffer>> *buffers) = 0;
> >>>>
> >>>>          virtual int start(Camera *camera, const ControlList *controls) = 0;
> >>>> -       virtual void stop(Camera *camera) = 0;
> >>>> +       void stop(Camera *camera);
> >>>>          bool hasPendingRequests(const Camera *camera) const;
> >>>>
> >>>>          void queueRequest(Request *request);
> >>>> @@ -71,6 +71,7 @@ protected:
> >>>>          void hotplugMediaDevice(MediaDevice *media);
> >>>>
> >>>>          virtual int queueRequestDevice(Camera *camera, Request *request) = 0;
> >>>> +       virtual void stopDevice(Camera *camera) = 0;
> >>>>
> >>>>          CameraManager *manager_;
> >>>>
> >>>> diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
> >>>> index eb714aa61099..2b2d7904f275 100644
> >>>> --- a/src/libcamera/pipeline/ipu3/ipu3.cpp
> >>>> +++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
> >>>> @@ -135,7 +135,7 @@ public:
> >>>>                                 std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
> >>>>
> >>>>          int start(Camera *camera, const ControlList *controls) override;
> >>>> -       void stop(Camera *camera) override;
> >>>> +       void stopDevice(Camera *camera) override;
> >>>>
> >>>>          int queueRequestDevice(Camera *camera, Request *request) override;
> >>>>
> >>>> @@ -791,7 +791,7 @@ error:
> >>>>          return ret;
> >>>>   }
> >>>>
> >>>> -void PipelineHandlerIPU3::stop(Camera *camera)
> >>>> +void PipelineHandlerIPU3::stopDevice(Camera *camera)
> >>>>   {
> >>>>          IPU3CameraData *data = cameraData(camera);
> >>>>          int ret = 0;
> >>>> diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> >>>> index 1634ca98f481..b5b7a240d5ed 100644
> >>>> --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> >>>> +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
> >>>> @@ -255,7 +255,7 @@ public:
> >>>>                                 std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
> >>>>
> >>>>          int start(Camera *camera, const ControlList *controls) override;
> >>>> -       void stop(Camera *camera) override;
> >>>> +       void stopDevice(Camera *camera) override;
> >>>>
> >>>>          int queueRequestDevice(Camera *camera, Request *request) override;
> >>>>
> >>>> @@ -878,7 +878,7 @@ int PipelineHandlerRPi::start(Camera *camera, const ControlList *controls)
> >>>>          return 0;
> >>>>   }
> >>>>
> >>>> -void PipelineHandlerRPi::stop(Camera *camera)
> >>>> +void PipelineHandlerRPi::stopDevice(Camera *camera)
> >>>>   {
> >>>>          RPiCameraData *data = cameraData(camera);
> >>>>
> >>>> diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> >>>> index 980088628523..9860fa84a5ae 100644
> >>>> --- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> >>>> +++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
> >>>> @@ -146,7 +146,7 @@ public:
> >>>>                                 std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
> >>>>
> >>>>          int start(Camera *camera, const ControlList *controls) override;
> >>>> -       void stop(Camera *camera) override;
> >>>> +       void stopDevice(Camera *camera) override;
> >>>>
> >>>>          int queueRequestDevice(Camera *camera, Request *request) override;
> >>>>
> >>>> @@ -827,7 +827,7 @@ int PipelineHandlerRkISP1::start(Camera *camera, [[maybe_unused]] const ControlL
> >>>>          return ret;
> >>>>   }
> >>>>
> >>>> -void PipelineHandlerRkISP1::stop(Camera *camera)
> >>>> +void PipelineHandlerRkISP1::stopDevice(Camera *camera)
> >>>>   {
> >>>>          RkISP1CameraData *data = cameraData(camera);
> >>>>          int ret;
> >>>> diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
> >>>> index 701fb4be0b71..fdff4ebd5134 100644
> >>>> --- a/src/libcamera/pipeline/simple/simple.cpp
> >>>> +++ b/src/libcamera/pipeline/simple/simple.cpp
> >>>> @@ -279,7 +279,7 @@ public:
> >>>>                                 std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
> >>>>
> >>>>          int start(Camera *camera, const ControlList *controls) override;
> >>>> -       void stop(Camera *camera) override;
> >>>> +       void stopDevice(Camera *camera) override;
> >>>>
> >>>>          bool match(DeviceEnumerator *enumerator) override;
> >>>>
> >>>> @@ -1036,7 +1036,7 @@ int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
> >>>>          return 0;
> >>>>   }
> >>>>
> >>>> -void SimplePipelineHandler::stop(Camera *camera)
> >>>> +void SimplePipelineHandler::stopDevice(Camera *camera)
> >>>>   {
> >>>>          SimpleCameraData *data = cameraData(camera);
> >>>>          V4L2VideoDevice *video = data->video_;
> >>>> diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> >>>> index 264f5370cf32..40654a0bc40c 100644
> >>>> --- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> >>>> +++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> >>>> @@ -74,7 +74,7 @@ public:
> >>>>                                 std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
> >>>>
> >>>>          int start(Camera *camera, const ControlList *controls) override;
> >>>> -       void stop(Camera *camera) override;
> >>>> +       void stopDevice(Camera *camera) override;
> >>>>
> >>>>          int queueRequestDevice(Camera *camera, Request *request) override;
> >>>>
> >>>> @@ -250,7 +250,7 @@ int PipelineHandlerUVC::start(Camera *camera, [[maybe_unused]] const ControlList
> >>>>          return 0;
> >>>>   }
> >>>>
> >>>> -void PipelineHandlerUVC::stop(Camera *camera)
> >>>> +void PipelineHandlerUVC::stopDevice(Camera *camera)
> >>>>   {
> >>>>          UVCCameraData *data = cameraData(camera);
> >>>>          data->video_->streamOff();
> >>>> diff --git a/src/libcamera/pipeline/vimc/vimc.cpp b/src/libcamera/pipeline/vimc/vimc.cpp
> >>>> index e453091da4b2..29960fe3f038 100644
> >>>> --- a/src/libcamera/pipeline/vimc/vimc.cpp
> >>>> +++ b/src/libcamera/pipeline/vimc/vimc.cpp
> >>>> @@ -91,7 +91,7 @@ public:
> >>>>                                 std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
> >>>>
> >>>>          int start(Camera *camera, const ControlList *controls) override;
> >>>> -       void stop(Camera *camera) override;
> >>>> +       void stopDevice(Camera *camera) override;
> >>>>
> >>>>          int queueRequestDevice(Camera *camera, Request *request) override;
> >>>>
> >>>> @@ -359,7 +359,7 @@ int PipelineHandlerVimc::start(Camera *camera, [[maybe_unused]] const ControlLis
> >>>>          return 0;
> >>>>   }
> >>>>
> >>>> -void PipelineHandlerVimc::stop(Camera *camera)
> >>>> +void PipelineHandlerVimc::stopDevice(Camera *camera)
> >>>>   {
> >>>>          VimcCameraData *data = cameraData(camera);
> >>>>          data->video_->streamOff();
> >>>> diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
> >>>> index 38edd00cebad..0f9fec1b618f 100644
> >>>> --- a/src/libcamera/pipeline_handler.cpp
> >>>> +++ b/src/libcamera/pipeline_handler.cpp
> >>>> @@ -268,8 +268,7 @@ void PipelineHandler::unlock()
> >>>>    */
> >>>>
> >>>>   /**
> >>>> - * \fn PipelineHandler::stop()
> >>>> - * \brief Stop capturing from all running streams
> >>>> + * \brief Stop capturing from all running streams and cancel pending requests
> >>>>    * \param[in] camera The camera to stop
> >>>>    *
> >>>>    * This function stops capturing and processing requests immediately. All
> >>>> @@ -277,6 +276,30 @@ void PipelineHandler::unlock()
> >>>>    *
> >>>>    * \context This function is called from the CameraManager thread.
> >>>>    */
> >>>> +void PipelineHandler::stop(Camera *camera)
> >>>> +{
> >>>> +       /* Stop the pipeline handler and let the queued requests complete. */
> >>>> +       stopDevice(camera);
> >>>> +
> >>>
> >>> I /think/ it's fine - but are we certain that nothing on the
> >>> waitingRequests_ will get queued between stopDevice above and clearing
> >>> below?
> >>
> >> The camera state machine shouldn't allow new Requests to be queued if
> >> the camera is stopped and the Camera::stop() documentations says
> >>
> >>   * \context This function may be called in any camera state as defined in \ref
> >>   * camera_operation, and shall be synchronized by the caller with other
> >>   * functions that affect the camera state. If called when the camera isn't
> >>   * running, it is a no-op.
> >
> > My concern wasn't so much from external requests being queued, but from
> > any potential event triggering (such as a fence completion) that might
> > move a request from the waitingRequests_ to the data->queuedRequests_()
> > at this point, before we clear it down...

We're single threaded, so as long as we make sure to really stop
everything in stop() to prevent any further event from being generated
once control returns to the event loop, it should be fine.

> >>>> +       /* Cancel and signal as complete all requests waiting for fences. */
> >>>> +       MutexLocker lock(waitingRequestsMutex_);
> >
> > Perhaps taking this lock before calling stopDevice() (if allowed) would
> > prevent that possibility?
> 
> As mentioned in previous patch, I am still un-sure why do we need this 
> lock or I don't understand if any other thread is trying to access 
> waitingRequests_ (either reading or queuing to it).

I don't think we need the lock.

> >>>> +
> >>>> +       for (auto &waitingRequest : waitingRequests_) {
> >>>> +               waitingRequest->cancel();
> >>>> +               completeRequest(waitingRequest);
> >>>> +       }
> >>>> +
> >>>> +       waitingRequests_.clear();
> >>>
> >>> I'd be very happy to see:
> >>>
> >>>        Camera::Private *data = camera->_d();
> >>>        ASSERT(data->queuedRequests_.empty());
> >>>
> >>> as an enforcement to be sure that all requests are completed by the end
> >>> of this function.
> >>
> >> Afaict stopDevice() is synchronous, it should complete all requests in
> >> flight in error state before returning
> 
> yes, stopDevice() (earlier's stop()) will complete all request, so it's 
> safe to assume all in flight requests are completed.
> 
> >>> But I think I can get away with this on this patch:
> >>
> >> Might be a useful validation to ASSERT though
> 
> Agreed, I see no harm (and more tightening)
> 
> >>> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> >>>
> >>>> +}
> >>>> +
> >>>> +/**
> >>>> + * \fn PipelineHandler::stopDevice()
> >>>> + * \brief Stop capturing from all running streams
> >>>> + * \param[in] camera The camera to stop
> >>>> + *
> >>>> + * This function stops capturing and processing requests immediately. All
> >>>> + * pending requests are cancelled and complete immediately in an error state.
> >>>> + */
> >>>>
> >>>>   /**
> >>>>    * \brief Determine if the camera has any requests pending

Patch
diff mbox series

diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h
index afb7990ea21b..5c3c0bc5a8b3 100644
--- a/include/libcamera/internal/pipeline_handler.h
+++ b/include/libcamera/internal/pipeline_handler.h
@@ -56,7 +56,7 @@  public:
 				       std::vector<std::unique_ptr<FrameBuffer>> *buffers) = 0;
 
 	virtual int start(Camera *camera, const ControlList *controls) = 0;
-	virtual void stop(Camera *camera) = 0;
+	void stop(Camera *camera);
 	bool hasPendingRequests(const Camera *camera) const;
 
 	void queueRequest(Request *request);
@@ -71,6 +71,7 @@  protected:
 	void hotplugMediaDevice(MediaDevice *media);
 
 	virtual int queueRequestDevice(Camera *camera, Request *request) = 0;
+	virtual void stopDevice(Camera *camera) = 0;
 
 	CameraManager *manager_;
 
diff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp
index eb714aa61099..2b2d7904f275 100644
--- a/src/libcamera/pipeline/ipu3/ipu3.cpp
+++ b/src/libcamera/pipeline/ipu3/ipu3.cpp
@@ -135,7 +135,7 @@  public:
 			       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
 
 	int start(Camera *camera, const ControlList *controls) override;
-	void stop(Camera *camera) override;
+	void stopDevice(Camera *camera) override;
 
 	int queueRequestDevice(Camera *camera, Request *request) override;
 
@@ -791,7 +791,7 @@  error:
 	return ret;
 }
 
-void PipelineHandlerIPU3::stop(Camera *camera)
+void PipelineHandlerIPU3::stopDevice(Camera *camera)
 {
 	IPU3CameraData *data = cameraData(camera);
 	int ret = 0;
diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
index 1634ca98f481..b5b7a240d5ed 100644
--- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
+++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp
@@ -255,7 +255,7 @@  public:
 			       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
 
 	int start(Camera *camera, const ControlList *controls) override;
-	void stop(Camera *camera) override;
+	void stopDevice(Camera *camera) override;
 
 	int queueRequestDevice(Camera *camera, Request *request) override;
 
@@ -878,7 +878,7 @@  int PipelineHandlerRPi::start(Camera *camera, const ControlList *controls)
 	return 0;
 }
 
-void PipelineHandlerRPi::stop(Camera *camera)
+void PipelineHandlerRPi::stopDevice(Camera *camera)
 {
 	RPiCameraData *data = cameraData(camera);
 
diff --git a/src/libcamera/pipeline/rkisp1/rkisp1.cpp b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
index 980088628523..9860fa84a5ae 100644
--- a/src/libcamera/pipeline/rkisp1/rkisp1.cpp
+++ b/src/libcamera/pipeline/rkisp1/rkisp1.cpp
@@ -146,7 +146,7 @@  public:
 			       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
 
 	int start(Camera *camera, const ControlList *controls) override;
-	void stop(Camera *camera) override;
+	void stopDevice(Camera *camera) override;
 
 	int queueRequestDevice(Camera *camera, Request *request) override;
 
@@ -827,7 +827,7 @@  int PipelineHandlerRkISP1::start(Camera *camera, [[maybe_unused]] const ControlL
 	return ret;
 }
 
-void PipelineHandlerRkISP1::stop(Camera *camera)
+void PipelineHandlerRkISP1::stopDevice(Camera *camera)
 {
 	RkISP1CameraData *data = cameraData(camera);
 	int ret;
diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
index 701fb4be0b71..fdff4ebd5134 100644
--- a/src/libcamera/pipeline/simple/simple.cpp
+++ b/src/libcamera/pipeline/simple/simple.cpp
@@ -279,7 +279,7 @@  public:
 			       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
 
 	int start(Camera *camera, const ControlList *controls) override;
-	void stop(Camera *camera) override;
+	void stopDevice(Camera *camera) override;
 
 	bool match(DeviceEnumerator *enumerator) override;
 
@@ -1036,7 +1036,7 @@  int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] const ControlL
 	return 0;
 }
 
-void SimplePipelineHandler::stop(Camera *camera)
+void SimplePipelineHandler::stopDevice(Camera *camera)
 {
 	SimpleCameraData *data = cameraData(camera);
 	V4L2VideoDevice *video = data->video_;
diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
index 264f5370cf32..40654a0bc40c 100644
--- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
+++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
@@ -74,7 +74,7 @@  public:
 			       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
 
 	int start(Camera *camera, const ControlList *controls) override;
-	void stop(Camera *camera) override;
+	void stopDevice(Camera *camera) override;
 
 	int queueRequestDevice(Camera *camera, Request *request) override;
 
@@ -250,7 +250,7 @@  int PipelineHandlerUVC::start(Camera *camera, [[maybe_unused]] const ControlList
 	return 0;
 }
 
-void PipelineHandlerUVC::stop(Camera *camera)
+void PipelineHandlerUVC::stopDevice(Camera *camera)
 {
 	UVCCameraData *data = cameraData(camera);
 	data->video_->streamOff();
diff --git a/src/libcamera/pipeline/vimc/vimc.cpp b/src/libcamera/pipeline/vimc/vimc.cpp
index e453091da4b2..29960fe3f038 100644
--- a/src/libcamera/pipeline/vimc/vimc.cpp
+++ b/src/libcamera/pipeline/vimc/vimc.cpp
@@ -91,7 +91,7 @@  public:
 			       std::vector<std::unique_ptr<FrameBuffer>> *buffers) override;
 
 	int start(Camera *camera, const ControlList *controls) override;
-	void stop(Camera *camera) override;
+	void stopDevice(Camera *camera) override;
 
 	int queueRequestDevice(Camera *camera, Request *request) override;
 
@@ -359,7 +359,7 @@  int PipelineHandlerVimc::start(Camera *camera, [[maybe_unused]] const ControlLis
 	return 0;
 }
 
-void PipelineHandlerVimc::stop(Camera *camera)
+void PipelineHandlerVimc::stopDevice(Camera *camera)
 {
 	VimcCameraData *data = cameraData(camera);
 	data->video_->streamOff();
diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp
index 38edd00cebad..0f9fec1b618f 100644
--- a/src/libcamera/pipeline_handler.cpp
+++ b/src/libcamera/pipeline_handler.cpp
@@ -268,8 +268,7 @@  void PipelineHandler::unlock()
  */
 
 /**
- * \fn PipelineHandler::stop()
- * \brief Stop capturing from all running streams
+ * \brief Stop capturing from all running streams and cancel pending requests
  * \param[in] camera The camera to stop
  *
  * This function stops capturing and processing requests immediately. All
@@ -277,6 +276,30 @@  void PipelineHandler::unlock()
  *
  * \context This function is called from the CameraManager thread.
  */
+void PipelineHandler::stop(Camera *camera)
+{
+	/* Stop the pipeline handler and let the queued requests complete. */
+	stopDevice(camera);
+
+	/* Cancel and signal as complete all requests waiting for fences. */
+	MutexLocker lock(waitingRequestsMutex_);
+
+	for (auto &waitingRequest : waitingRequests_) {
+		waitingRequest->cancel();
+		completeRequest(waitingRequest);
+	}
+
+	waitingRequests_.clear();
+}
+
+/**
+ * \fn PipelineHandler::stopDevice()
+ * \brief Stop capturing from all running streams
+ * \param[in] camera The camera to stop
+ *
+ * This function stops capturing and processing requests immediately. All
+ * pending requests are cancelled and complete immediately in an error state.
+ */
 
 /**
  * \brief Determine if the camera has any requests pending