[libcamera-devel] [PATCH 8/8] android: Implement flush() camera operation

Hirokazu Honda hiroh at chromium.org
Tue May 11 08:15:17 CEST 2021


Hi Jacopo, thank you for the work.

On Tue, May 11, 2021 at 5:39 AM Niklas Söderlund <
niklas.soderlund at ragnatech.se> wrote:

> Hi Jacopo,
>
> Thanks for your work.
>
> On 2021-05-10 12:52:35 +0200, Jacopo Mondi wrote:
> > Implement the flush() camera operation in the CameraDevice class
> > and make it available to the camera framework by implementing the
> > operation wrapper in camera_ops.cpp.
> >
> > The flush() implementation stops the Camera and the worker thread and
> > waits for all in-flight requests to be returned. Stopping the Camera
> > forces all Requests already queued to be returned immediately in error
> > state. As flush() has to wait until all of them have been returned, make
> it
> > wait on a newly introduced condition variable which is notified by the
> > request completion handler when the queue of pending requests has been
> > exhausted.
> >
> > As flush() can race with processCaptureRequest() protect the requests
> > queueing by introducing a new CameraState::CameraFlushing state that
> > processCaptureRequest() inspects before queuing the Request to the
> > Camera. If flush() has been called while processCaptureRequest() was
> > executing, return the current Request immediately in error state.
> >
> > Signed-off-by: Jacopo Mondi <jacopo at jmondi.org>
> > ---
> >  src/android/camera_device.cpp | 63 +++++++++++++++++++++++++++++++++++
> >  src/android/camera_device.h   |  6 ++++
> >  src/android/camera_ops.cpp    |  8 ++++-
> >  3 files changed, 76 insertions(+), 1 deletion(-)
> >
> > diff --git a/src/android/camera_device.cpp
> b/src/android/camera_device.cpp
> > index fa12ce5b0133..01b3acd93c4b 100644
> > --- a/src/android/camera_device.cpp
> > +++ b/src/android/camera_device.cpp
> > @@ -756,6 +756,42 @@ void CameraDevice::close()
> >       camera_->release();
> >  }
> >
> > +/*
> > + * Flush is similar to stop() but sets the camera state to 'flushing'
> and wait
> > + * until all the in-flight requests have been returned.
> > + */
> > +void CameraDevice::flush()
> > +{
> > +     {
> > +             MutexLocker cameraLock(cameraMutex_);
> > +
> > +             if (state_ != CameraRunning)
> > +                     return;
> > +
> > +             state_ = CameraFlushing;
> > +
> > +             /*
> > +              * Stop the camera and set the state to flushing to
> prevent any
> > +              * new request to be queued from this point on.
> > +              */
> > +             worker_.stop();
> > +             camera_->stop();
> > +     }
>
> Releasing cameraMutex_ while waiting for the flushMutex_ signal seems
> like a race waiting to happen as the operation is acting in
> synchronization with the state_ statemachine.
>
> I understand you release it as you want to lock it in
> CameraDevice::stop(), is there any other potential race site? If not
> maybe CameraDevice::stop() can be reworked? It only needs to read the
> state so is the mutex really needed, could it be reworked to an atomic?
> Or maybe there is something in the HAL itself that would prevent other
> callbacks to change the state from CameraFlushing -> CameraRunning while
> flush() is executing?
>
> > +
> > +     /*
> > +      * Now wait for all the in-flight requests to be completed before
> > +      * returning. Stopping the Camera guarantees that all in-flight
> requests
> > +      * are completed in error state.
> > +      */
> > +     {
> > +             MutexLocker flushLock(flushMutex_);
> > +             flushing_.wait(flushLock, [&] { return
> descriptors_.empty(); });
> > +     }
> > +
> > +     MutexLocker cameraLock(cameraMutex_);
> > +     state_ = CameraStopped;
> > +}
> > +
> >  void CameraDevice::stop()
> >  {
> >       MutexLocker cameraLock(cameraMutex_);
> > @@ -2019,6 +2055,26 @@ int
> CameraDevice::processCaptureRequest(camera3_capture_request_t *camera3Reques
> >       if (ret)
> >               return ret;
> >
> > +
> > +     /*
> > +      * Just before queuing the request, make sure flush() has not
> > +      * been called after this function has been executed. In that
> > +      * case, immediately return the request with errors.
> > +      */
> > +     MutexLocker cameraLock(cameraMutex_);
> > +     if (state_ == CameraFlushing || state_ == CameraStopped) {
> > +             for (camera3_stream_buffer_t &buffer :
> descriptor.buffers_) {
> > +                     buffer.status = CAMERA3_BUFFER_STATUS_ERROR;
> > +                     buffer.release_fence = buffer.acquire_fence;
> > +             }
> > +
> > +             notifyError(descriptor.frameNumber_,
> > +                         descriptor.buffers_[0].stream,
> > +                         CAMERA3_MSG_ERROR_REQUEST);
> > +
> > +             return 0;
> > +     }
> > +
> >       worker_.queueRequest(descriptor.request_.get());
> >
> >       {
> > @@ -2052,6 +2108,13 @@ void CameraDevice::requestComplete(Request
> *request)
> >       }
> >       Camera3RequestDescriptor &descriptor = node.mapped();
> >
> > +     /* Release flush if all the pending requests have been completed.
> */
> > +     {
> > +             MutexLocker flushLock(flushMutex_);
> > +             if (descriptors_.empty())
> > +                     flushing_.notify_one();
> > +     }
> > +
>

Is flushMutex_ necessary? I think it should be replaced with requestMutex_.
It is because descriptors_ is accessed in the condition of the wait
function and here, before calling notify_one().


>       /*
> >        * Prepare the capture result for the Android camera stack.
> >        *
> > diff --git a/src/android/camera_device.h b/src/android/camera_device.h
> > index ed992ae56d5d..4a9ef495b776 100644
> > --- a/src/android/camera_device.h
> > +++ b/src/android/camera_device.h
> > @@ -7,6 +7,7 @@
> >  #ifndef __ANDROID_CAMERA_DEVICE_H__
> >  #define __ANDROID_CAMERA_DEVICE_H__
> >
> > +#include <condition_variable>
> >  #include <map>
> >  #include <memory>
> >  #include <mutex>
> > @@ -42,6 +43,7 @@ public:
> >
> >       int open(const hw_module_t *hardwareModule);
> >       void close();
> > +     void flush();
> >
> >       unsigned int id() const { return id_; }
> >       camera3_device_t *camera3Device() { return &camera3Device_; }
> > @@ -92,6 +94,7 @@ private:
> >       enum State {
> >               CameraStopped,
> >               CameraRunning,
> > +             CameraFlushing,
> >       };
> >
> >       void stop();
> > @@ -124,6 +127,9 @@ private:
> >       libcamera::Mutex cameraMutex_; /* Protects access to the camera
> state. */
> >       State state_;
> >
> > +     libcamera::Mutex flushMutex_; /* Protects the flushing condition
> variable. */
> > +     std::condition_variable flushing_;
> > +
> >       std::shared_ptr<libcamera::Camera> camera_;
> >       std::unique_ptr<libcamera::CameraConfiguration> config_;
> >
> > diff --git a/src/android/camera_ops.cpp b/src/android/camera_ops.cpp
> > index 696e80436821..8a3cfa175ff5 100644
> > --- a/src/android/camera_ops.cpp
> > +++ b/src/android/camera_ops.cpp
> > @@ -66,8 +66,14 @@ static void hal_dev_dump([[maybe_unused]] const
> struct camera3_device *dev,
> >  {
> >  }
> >
> > -static int hal_dev_flush([[maybe_unused]] const struct camera3_device
> *dev)
> > +static int hal_dev_flush(const struct camera3_device *dev)
> >  {
> > +     if (!dev)
> > +             return -EINVAL;
> > +
> > +     CameraDevice *camera = reinterpret_cast<CameraDevice *>(dev->priv);
> > +     camera->flush();
> > +
> >       return 0;
> >  }
> >


This implementation is good if close() and flush() are not called at any
time.
But, looks like it doesn't protect a concurrent call of close() and flush()
with configureStreams() and each other?

-Hiro


> > --
> > 2.31.1
> >
> > _______________________________________________
> > libcamera-devel mailing list
> > libcamera-devel at lists.libcamera.org
> > https://lists.libcamera.org/listinfo/libcamera-devel
>
> --
> Regards,
> Niklas Söderlund
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel at lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.libcamera.org/pipermail/libcamera-devel/attachments/20210511/00c543f8/attachment.htm>


More information about the libcamera-devel mailing list