[{"id":24520,"web_url":"https://patchwork.libcamera.org/comment/24520/","msgid":"<166014406801.15821.18006622305930030211@Monstersaurus>","date":"2022-08-10T15:07:48","subject":"Re: [libcamera-devel] [PATCH] libcamera: Allow concurrent use of\n\tcameras from same pipeline handler","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Hi Laurent,\n\nQuoting Laurent Pinchart (2022-07-21 22:03:51)\n> libcamera implements a pipeline handler locking mechanism based on\n> advisory locks on media devices, to prevent concurrent access to cameras\n> from the same pipeline handler from different processes (this only works\n> between multiple libcamera instances, as other processes won't use\n> advisory locks on media devices).\n> \n> A side effect of the implementation prevents multiple cameras created by\n> the same pipeline handler from being used concurrently. Fix this by\n> turning the PipelineHandler lock() and unlock() functions into acquire()\n> and release(), with a use count to replace the boolean lock flag. The\n> Camera class is updated accordingly.\n> \n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n> This may help addressing the problem reported in \"[RFC PATCH] qcam: Fix\n> IPU3 camera switching\".\n\nThis does indeed fix things for me on IPU3 testing with qcam.\nIt however seemed to introduce a regression in CTS (which perhaps\nHarvey's patches will then resolve)\n\nHowever, I think we'll hear from David that this would benefit the RPi\nplatform already...\n\n\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\n> ---\n>  include/libcamera/internal/camera.h           |  1 +\n>  include/libcamera/internal/pipeline_handler.h |  8 ++-\n>  src/libcamera/camera.cpp                      | 12 +++-\n>  src/libcamera/pipeline_handler.cpp            | 63 ++++++++++++-------\n>  4 files changed, 55 insertions(+), 29 deletions(-)\n> \n> diff --git a/include/libcamera/internal/camera.h b/include/libcamera/internal/camera.h\n> index 597426a6f9d2..38dd94ff8156 100644\n> --- a/include/libcamera/internal/camera.h\n> +++ b/include/libcamera/internal/camera.h\n> @@ -50,6 +50,7 @@ private:\n>                 CameraRunning,\n>         };\n>  \n> +       bool isAcquired() const;\n>         bool isRunning() const;\n>         int isAccessAllowed(State state, bool allowDisconnected = false,\n>                             const char *from = __builtin_FUNCTION()) const;\n> diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h\n> index c3e4c2587ecd..20f1cbb07fea 100644\n> --- a/include/libcamera/internal/pipeline_handler.h\n> +++ b/include/libcamera/internal/pipeline_handler.h\n> @@ -45,8 +45,8 @@ public:\n>         MediaDevice *acquireMediaDevice(DeviceEnumerator *enumerator,\n>                                         const DeviceMatch &dm);\n>  \n> -       bool lock();\n> -       void unlock();\n> +       bool acquire();\n> +       void release();\n>  \n>         virtual CameraConfiguration *generateConfiguration(Camera *camera,\n>                 const StreamRoles &roles) = 0;\n> @@ -77,6 +77,8 @@ protected:\n>         CameraManager *manager_;\n>  \n>  private:\n> +       void unlockMediaDevices();\n> +\n>         void mediaDeviceDisconnected(MediaDevice *media);\n>         virtual void disconnect();\n>  \n> @@ -91,7 +93,7 @@ private:\n>         const char *name_;\n>  \n>         Mutex lock_;\n> -       bool lockOwner_ LIBCAMERA_TSA_GUARDED_BY(lock_); /* *Not* ownership of lock_ */\n> +       unsigned int useCount_ LIBCAMERA_TSA_GUARDED_BY(lock_);\n>  \n>         friend class PipelineHandlerFactory;\n>  };\n> diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp\n> index 2a8ef60e3862..9ce32db1ac78 100644\n> --- a/src/libcamera/camera.cpp\n> +++ b/src/libcamera/camera.cpp\n> @@ -508,6 +508,11 @@ static const char *const camera_state_names[] = {\n>         \"Running\",\n>  };\n>  \n> +bool Camera::Private::isAcquired() const\n> +{\n> +       return state_.load(std::memory_order_acquire) == CameraRunning;\n> +}\n> +\n>  bool Camera::Private::isRunning() const\n>  {\n>         return state_.load(std::memory_order_acquire) == CameraRunning;\n> @@ -811,7 +816,7 @@ int Camera::exportFrameBuffers(Stream *stream,\n>   * not blocking, if the device has already been acquired (by the same or another\n>   * process) the -EBUSY error code is returned.\n>   *\n> - * Acquiring a camera will limit usage of any other camera(s) provided by the\n> + * Acquiring a camera may limit usage of any other camera(s) provided by the\n>   * same pipeline handler to the same instance of libcamera. The limit is in\n>   * effect until all cameras from the pipeline handler are released. Other\n>   * instances of libcamera can still list and examine the cameras but will fail\n> @@ -839,7 +844,7 @@ int Camera::acquire()\n>         if (ret < 0)\n>                 return ret == -EACCES ? -EBUSY : ret;\n>  \n> -       if (!d->pipe_->lock()) {\n> +       if (!d->pipe_->acquire()) {\n>                 LOG(Camera, Info)\n>                         << \"Pipeline handler in use by another process\";\n>                 return -EBUSY;\n> @@ -873,7 +878,8 @@ int Camera::release()\n>         if (ret < 0)\n>                 return ret == -EACCES ? -EBUSY : ret;\n>  \n> -       d->pipe_->unlock();\n> +       if (d->isAcquired())\n> +               d->pipe_->release();\n>  \n>         d->setState(Private::CameraAvailable);\n>  \n> diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp\n> index 675405330f45..7d2d00ef45e6 100644\n> --- a/src/libcamera/pipeline_handler.cpp\n> +++ b/src/libcamera/pipeline_handler.cpp\n> @@ -68,7 +68,7 @@ LOG_DEFINE_CATEGORY(Pipeline)\n>   * respective factories.\n>   */\n>  PipelineHandler::PipelineHandler(CameraManager *manager)\n> -       : manager_(manager), lockOwner_(false)\n> +       : manager_(manager), useCount_(0)\n>  {\n>  }\n>  \n> @@ -143,58 +143,75 @@ MediaDevice *PipelineHandler::acquireMediaDevice(DeviceEnumerator *enumerator,\n>  }\n>  \n>  /**\n> - * \\brief Lock all media devices acquired by the pipeline\n> + * \\brief Acquire exclusive access to the pipeline handler for the process\n>   *\n> - * This function shall not be called from pipeline handler implementation, as\n> - * the Camera class handles locking directly.\n> + * This function locks all the media devices used by the pipeline to ensure\n> + * that no other process can access them concurrently.\n> + *\n> + * Access to a pipeline handler may be acquired recursively from within the\n> + * same process. Every successful acquire() call shall be matched with a\n> + * release() call. This allows concurrent access to the same pipeline handler\n> + * from different cameras within the same process.\n> + *\n> + * Pipeline handlers shall not call this function directly as the Camera class\n> + * handles access internally.\n>   *\n>   * \\context This function is \\threadsafe.\n>   *\n> - * \\return True if the devices could be locked, false otherwise\n> - * \\sa unlock()\n> - * \\sa MediaDevice::lock()\n> + * \\return True if the pipeline handler was acquired, false if another process\n> + * has already acquired it\n> + * \\sa release()\n>   */\n> -bool PipelineHandler::lock()\n> +bool PipelineHandler::acquire()\n>  {\n>         MutexLocker locker(lock_);\n>  \n> -       /* Do not allow nested locking in the same libcamera instance. */\n> -       if (lockOwner_)\n> -               return false;\n> +       if (useCount_) {\n> +               ++useCount_;\n> +               return true;\n> +       }\n>  \n>         for (std::shared_ptr<MediaDevice> &media : mediaDevices_) {\n>                 if (!media->lock()) {\n> -                       unlock();\n> +                       unlockMediaDevices();\n>                         return false;\n>                 }\n>         }\n>  \n> -       lockOwner_ = true;\n> -\n> +       ++useCount_;\n>         return true;\n>  }\n>  \n>  /**\n> - * \\brief Unlock all media devices acquired by the pipeline\n> + * \\brief Release exclusive access to the pipeline handler\n>   *\n> - * This function shall not be called from pipeline handler implementation, as\n> - * the Camera class handles locking directly.\n> + * This function releases access to the pipeline handler previously acquired by\n> + * a call to acquire(). Every release() call shall match a previous successful\n> + * acquire() call. Calling this function on a pipeline handler that hasn't been\n> + * acquired results in undefined behaviour.\n> + *\n> + * Pipeline handlers shall not call this function directly as the Camera class\n> + * handles access internally.\n>   *\n>   * \\context This function is \\threadsafe.\n>   *\n> - * \\sa lock()\n> + * \\sa acquire()\n>   */\n> -void PipelineHandler::unlock()\n> +void PipelineHandler::release()\n>  {\n>         MutexLocker locker(lock_);\n>  \n> -       if (!lockOwner_)\n> -               return;\n> +       ASSERT(useCount_);\n>  \n> +       unlockMediaDevices();\n> +\n> +       --useCount_;\n> +}\n> +\n> +void PipelineHandler::unlockMediaDevices()\n> +{\n>         for (std::shared_ptr<MediaDevice> &media : mediaDevices_)\n>                 media->unlock();\n> -\n> -       lockOwner_ = false;\n>  }\n>  \n>  /**\n> \n> base-commit: d4eee094e684806dbdb54fbe1bc02c9c590aa7f4\n> -- \n> Regards,\n> \n> Laurent Pinchart\n>","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id E9F3EC3272\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 10 Aug 2022 15:07:51 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 54AFC6332B;\n\tWed, 10 Aug 2022 17:07:51 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 8BF11600EA\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 10 Aug 2022 17:07:50 +0200 (CEST)","from pendragon.ideasonboard.com\n\t(cpc89244-aztw30-2-0-cust3082.18-1.cable.virginm.net [86.31.172.11])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 202603F1;\n\tWed, 10 Aug 2022 17:07:50 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1660144071;\n\tbh=Oq1LyQeqi1FFVznQrPFgdnw+SFV/wBw1ZeCEVf8on8Q=;\n\th=In-Reply-To:References:To:Date:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:\n\tFrom;\n\tb=uh3prCVzW6+dWp3YgE6HpXwr/uB4YAbhTwPjX191EzwwlyJ43Xv9kwl1q1BZkfIy6\n\tMDN1LYqwClqi0Ejv1EPQMo6fc8fq173Kk/kfFckAcax7LPL+7f4KEz63yAYMgX+zJD\n\tqyr2J/Ni1pRoes/s31N0a/R0onYDOUvDXQXyOtfwnT9NEpH7adKYYfEPBWpeM8QcLY\n\tQmANsIeOBPeXhfu0ussqkIoTWvPC9LFF34Y7v6WZiHcmjePWhO8nSohZwhrvuZxF7X\n\tbZGUmBT5oCGQvV3G2rxFLzmH85bL2INuKd8qYTOh5bAqCobX/GCXLKzrlSzmiggU9O\n\t9kOsxiN0P+iug==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1660144070;\n\tbh=Oq1LyQeqi1FFVznQrPFgdnw+SFV/wBw1ZeCEVf8on8Q=;\n\th=In-Reply-To:References:Subject:From:To:Cc:Date:From;\n\tb=JRbNKH1eWVXusvdZsLVy0sZ6ILdpedFvdDtdVJr9i5kZbAFEGhopXOL7YqZrxEdhn\n\t08yKXcRsTlH1XVBj6oU7ALCbsWnPfBbRTT8nT4A++xXr/2f37mqVuaZJqMkslRmPu9\n\tu3yLVg/agkvlpfLcxEUmcShtu6QEWklplmR1N3pc="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"JRbNKH1e\"; dkim-atps=neutral","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20220721210351.1908-1-laurent.pinchart@ideasonboard.com>","References":"<20220721210351.1908-1-laurent.pinchart@ideasonboard.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>,\n\tlibcamera-devel@lists.libcamera.org","Date":"Wed, 10 Aug 2022 16:07:48 +0100","Message-ID":"<166014406801.15821.18006622305930030211@Monstersaurus>","User-Agent":"alot/0.10","Subject":"Re: [libcamera-devel] [PATCH] libcamera: Allow concurrent use of\n\tcameras from same pipeline handler","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","From":"Kieran Bingham via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":24521,"web_url":"https://patchwork.libcamera.org/comment/24521/","msgid":"<CAHW6GYKbpJxjtj8q_w50aU2+Hj-aOXNHOrzdFSuxCi4PK-Buuw@mail.gmail.com>","date":"2022-08-10T15:24:55","subject":"Re: [libcamera-devel] [PATCH] libcamera: Allow concurrent use of\n\tcameras from same pipeline handler","submitter":{"id":42,"url":"https://patchwork.libcamera.org/api/people/42/","name":"David Plowman","email":"david.plowman@raspberrypi.com"},"content":"Hi Kieran, Laurent\n\nOn Wed, 10 Aug 2022 at 16:07, Kieran Bingham\n<kieran.bingham@ideasonboard.com> wrote:\n>\n> Hi Laurent,\n>\n> Quoting Laurent Pinchart (2022-07-21 22:03:51)\n> > libcamera implements a pipeline handler locking mechanism based on\n> > advisory locks on media devices, to prevent concurrent access to cameras\n> > from the same pipeline handler from different processes (this only works\n> > between multiple libcamera instances, as other processes won't use\n> > advisory locks on media devices).\n> >\n> > A side effect of the implementation prevents multiple cameras created by\n> > the same pipeline handler from being used concurrently. Fix this by\n> > turning the PipelineHandler lock() and unlock() functions into acquire()\n> > and release(), with a use count to replace the boolean lock flag. The\n> > Camera class is updated accordingly.\n> >\n> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > ---\n> > This may help addressing the problem reported in \"[RFC PATCH] qcam: Fix\n> > IPU3 camera switching\".\n>\n> This does indeed fix things for me on IPU3 testing with qcam.\n> It however seemed to introduce a regression in CTS (which perhaps\n> Harvey's patches will then resolve)\n>\n> However, I think we'll hear from David that this would benefit the RPi\n> platform already...\n\nYes, this does fix something for me\n(https://github.com/raspberrypi/picamera2/issues/229), so I'd be very\nhappy to see it merged! In my case the real problem was that lock()\ncalls unlock() when it fails, and unlock() tries to grab the mutex\nthat lock() already took. But I can certainly add this for this patch:\n\nTested-by: David Plowman <david.plowman@raspberrypi.com>\n\nThanks!\n\nDavid\n\n>\n>\n> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n>\n> > ---\n> >  include/libcamera/internal/camera.h           |  1 +\n> >  include/libcamera/internal/pipeline_handler.h |  8 ++-\n> >  src/libcamera/camera.cpp                      | 12 +++-\n> >  src/libcamera/pipeline_handler.cpp            | 63 ++++++++++++-------\n> >  4 files changed, 55 insertions(+), 29 deletions(-)\n> >\n> > diff --git a/include/libcamera/internal/camera.h b/include/libcamera/internal/camera.h\n> > index 597426a6f9d2..38dd94ff8156 100644\n> > --- a/include/libcamera/internal/camera.h\n> > +++ b/include/libcamera/internal/camera.h\n> > @@ -50,6 +50,7 @@ private:\n> >                 CameraRunning,\n> >         };\n> >\n> > +       bool isAcquired() const;\n> >         bool isRunning() const;\n> >         int isAccessAllowed(State state, bool allowDisconnected = false,\n> >                             const char *from = __builtin_FUNCTION()) const;\n> > diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h\n> > index c3e4c2587ecd..20f1cbb07fea 100644\n> > --- a/include/libcamera/internal/pipeline_handler.h\n> > +++ b/include/libcamera/internal/pipeline_handler.h\n> > @@ -45,8 +45,8 @@ public:\n> >         MediaDevice *acquireMediaDevice(DeviceEnumerator *enumerator,\n> >                                         const DeviceMatch &dm);\n> >\n> > -       bool lock();\n> > -       void unlock();\n> > +       bool acquire();\n> > +       void release();\n> >\n> >         virtual CameraConfiguration *generateConfiguration(Camera *camera,\n> >                 const StreamRoles &roles) = 0;\n> > @@ -77,6 +77,8 @@ protected:\n> >         CameraManager *manager_;\n> >\n> >  private:\n> > +       void unlockMediaDevices();\n> > +\n> >         void mediaDeviceDisconnected(MediaDevice *media);\n> >         virtual void disconnect();\n> >\n> > @@ -91,7 +93,7 @@ private:\n> >         const char *name_;\n> >\n> >         Mutex lock_;\n> > -       bool lockOwner_ LIBCAMERA_TSA_GUARDED_BY(lock_); /* *Not* ownership of lock_ */\n> > +       unsigned int useCount_ LIBCAMERA_TSA_GUARDED_BY(lock_);\n> >\n> >         friend class PipelineHandlerFactory;\n> >  };\n> > diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp\n> > index 2a8ef60e3862..9ce32db1ac78 100644\n> > --- a/src/libcamera/camera.cpp\n> > +++ b/src/libcamera/camera.cpp\n> > @@ -508,6 +508,11 @@ static const char *const camera_state_names[] = {\n> >         \"Running\",\n> >  };\n> >\n> > +bool Camera::Private::isAcquired() const\n> > +{\n> > +       return state_.load(std::memory_order_acquire) == CameraRunning;\n> > +}\n> > +\n> >  bool Camera::Private::isRunning() const\n> >  {\n> >         return state_.load(std::memory_order_acquire) == CameraRunning;\n> > @@ -811,7 +816,7 @@ int Camera::exportFrameBuffers(Stream *stream,\n> >   * not blocking, if the device has already been acquired (by the same or another\n> >   * process) the -EBUSY error code is returned.\n> >   *\n> > - * Acquiring a camera will limit usage of any other camera(s) provided by the\n> > + * Acquiring a camera may limit usage of any other camera(s) provided by the\n> >   * same pipeline handler to the same instance of libcamera. The limit is in\n> >   * effect until all cameras from the pipeline handler are released. Other\n> >   * instances of libcamera can still list and examine the cameras but will fail\n> > @@ -839,7 +844,7 @@ int Camera::acquire()\n> >         if (ret < 0)\n> >                 return ret == -EACCES ? -EBUSY : ret;\n> >\n> > -       if (!d->pipe_->lock()) {\n> > +       if (!d->pipe_->acquire()) {\n> >                 LOG(Camera, Info)\n> >                         << \"Pipeline handler in use by another process\";\n> >                 return -EBUSY;\n> > @@ -873,7 +878,8 @@ int Camera::release()\n> >         if (ret < 0)\n> >                 return ret == -EACCES ? -EBUSY : ret;\n> >\n> > -       d->pipe_->unlock();\n> > +       if (d->isAcquired())\n> > +               d->pipe_->release();\n> >\n> >         d->setState(Private::CameraAvailable);\n> >\n> > diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp\n> > index 675405330f45..7d2d00ef45e6 100644\n> > --- a/src/libcamera/pipeline_handler.cpp\n> > +++ b/src/libcamera/pipeline_handler.cpp\n> > @@ -68,7 +68,7 @@ LOG_DEFINE_CATEGORY(Pipeline)\n> >   * respective factories.\n> >   */\n> >  PipelineHandler::PipelineHandler(CameraManager *manager)\n> > -       : manager_(manager), lockOwner_(false)\n> > +       : manager_(manager), useCount_(0)\n> >  {\n> >  }\n> >\n> > @@ -143,58 +143,75 @@ MediaDevice *PipelineHandler::acquireMediaDevice(DeviceEnumerator *enumerator,\n> >  }\n> >\n> >  /**\n> > - * \\brief Lock all media devices acquired by the pipeline\n> > + * \\brief Acquire exclusive access to the pipeline handler for the process\n> >   *\n> > - * This function shall not be called from pipeline handler implementation, as\n> > - * the Camera class handles locking directly.\n> > + * This function locks all the media devices used by the pipeline to ensure\n> > + * that no other process can access them concurrently.\n> > + *\n> > + * Access to a pipeline handler may be acquired recursively from within the\n> > + * same process. Every successful acquire() call shall be matched with a\n> > + * release() call. This allows concurrent access to the same pipeline handler\n> > + * from different cameras within the same process.\n> > + *\n> > + * Pipeline handlers shall not call this function directly as the Camera class\n> > + * handles access internally.\n> >   *\n> >   * \\context This function is \\threadsafe.\n> >   *\n> > - * \\return True if the devices could be locked, false otherwise\n> > - * \\sa unlock()\n> > - * \\sa MediaDevice::lock()\n> > + * \\return True if the pipeline handler was acquired, false if another process\n> > + * has already acquired it\n> > + * \\sa release()\n> >   */\n> > -bool PipelineHandler::lock()\n> > +bool PipelineHandler::acquire()\n> >  {\n> >         MutexLocker locker(lock_);\n> >\n> > -       /* Do not allow nested locking in the same libcamera instance. */\n> > -       if (lockOwner_)\n> > -               return false;\n> > +       if (useCount_) {\n> > +               ++useCount_;\n> > +               return true;\n> > +       }\n> >\n> >         for (std::shared_ptr<MediaDevice> &media : mediaDevices_) {\n> >                 if (!media->lock()) {\n> > -                       unlock();\n> > +                       unlockMediaDevices();\n> >                         return false;\n> >                 }\n> >         }\n> >\n> > -       lockOwner_ = true;\n> > -\n> > +       ++useCount_;\n> >         return true;\n> >  }\n> >\n> >  /**\n> > - * \\brief Unlock all media devices acquired by the pipeline\n> > + * \\brief Release exclusive access to the pipeline handler\n> >   *\n> > - * This function shall not be called from pipeline handler implementation, as\n> > - * the Camera class handles locking directly.\n> > + * This function releases access to the pipeline handler previously acquired by\n> > + * a call to acquire(). Every release() call shall match a previous successful\n> > + * acquire() call. Calling this function on a pipeline handler that hasn't been\n> > + * acquired results in undefined behaviour.\n> > + *\n> > + * Pipeline handlers shall not call this function directly as the Camera class\n> > + * handles access internally.\n> >   *\n> >   * \\context This function is \\threadsafe.\n> >   *\n> > - * \\sa lock()\n> > + * \\sa acquire()\n> >   */\n> > -void PipelineHandler::unlock()\n> > +void PipelineHandler::release()\n> >  {\n> >         MutexLocker locker(lock_);\n> >\n> > -       if (!lockOwner_)\n> > -               return;\n> > +       ASSERT(useCount_);\n> >\n> > +       unlockMediaDevices();\n> > +\n> > +       --useCount_;\n> > +}\n> > +\n> > +void PipelineHandler::unlockMediaDevices()\n> > +{\n> >         for (std::shared_ptr<MediaDevice> &media : mediaDevices_)\n> >                 media->unlock();\n> > -\n> > -       lockOwner_ = false;\n> >  }\n> >\n> >  /**\n> >\n> > base-commit: d4eee094e684806dbdb54fbe1bc02c9c590aa7f4\n> > --\n> > Regards,\n> >\n> > Laurent Pinchart\n> >","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id ACBFDBE173\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 10 Aug 2022 15:25:09 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 0FEDD6332B;\n\tWed, 10 Aug 2022 17:25:09 +0200 (CEST)","from mail-ej1-x62a.google.com (mail-ej1-x62a.google.com\n\t[IPv6:2a00:1450:4864:20::62a])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 6319F600EA\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 10 Aug 2022 17:25:06 +0200 (CEST)","by mail-ej1-x62a.google.com with SMTP id gk3so28311311ejb.8\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 10 Aug 2022 08:25:06 -0700 (PDT)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1660145109;\n\tbh=Wo2dUp4gaT3feitXJk7rQR0llen4wlM6IRUCc/Snk2s=;\n\th=References:In-Reply-To:Date:To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=S9RTv0kGnxZxQk70TdO+d3qgNZ94yi4j7E0iu3QjqF86dNErNEzx/DN+eNCedDiIl\n\t07MnMlRuYuu9n+u0XWKzZ7zz5mR06Y8j1ybjDx/5f7KPjayQ9bgrt+IYwjmWzCapoL\n\tW02Msveu6h7v+fRaZkooW5gDtjX1OBvnjAyZhl9b9UNJKevTJBaaM8FjWQF1TavKtk\n\tYz+ENrq2PuaMgHtzQYH4mQ0ehxc5oIyEgKzueu9m93iT/mqm1NlD2NzPUm/cjaFZFc\n\tV5HBsLHoVkQEPmLVoYg7lyZDkH6d9ESi0xUUcZ1rO2vFUpsm3LlCXpCdtNko6SC5q/\n\tXRUxAJLhAYDQw==","v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=raspberrypi.com; s=google;\n\th=cc:to:subject:message-id:date:from:in-reply-to:references\n\t:mime-version:from:to:cc;\n\tbh=CZFzLX15IJMXL9bzhwYLM661IYUZj67RZCdb0IokWgA=;\n\tb=VvWaDWSFCw9GbsziJNSWng9WQtXTO2yfiaVbE4U63hvSk9Yc6Ssz7LoG0dk/TL8V4R\n\teT9y1j0kb+l1jHnD2kLLWP21P8SkxhzozScUvqDlCPsw8QxbErmeWURJovc+5bxCoeCi\n\tCQw03d8iMYQUyBWQm2Z5c7o8W8viwm1GtC2WBRMnJW9BOQdH4bBKB6X6+hLsZQb3swXX\n\ta6Q37foQ+TzlqllxykQdQBiv9P1maJqKmeK1OpRS6/RUv8RZ3MKV9Sik8/7BrfGg+fQD\n\t1F+VLUwWvYGej1BB2oLnNzH0qJh6/c2xofzHAiHXzzZ7hBVhfPewvVL0yKy0TAITnnnM\n\t2w9A=="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key; \n\tunprotected) header.d=raspberrypi.com\n\theader.i=@raspberrypi.com\n\theader.b=\"VvWaDWSF\"; dkim-atps=neutral","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20210112;\n\th=cc:to:subject:message-id:date:from:in-reply-to:references\n\t:mime-version:x-gm-message-state:from:to:cc;\n\tbh=CZFzLX15IJMXL9bzhwYLM661IYUZj67RZCdb0IokWgA=;\n\tb=5oKzhSOGEYAgvSkUJLc0N7hlzPGC+SuVspDvuKZSwXPMfdao0UCF6eSxQKtb35f08F\n\t9eXl7J2xx9cCDfOAchHzpDIJov91lORvB5AZTymMXXc9oOkBJHAXTIoeCIfYMCxVFR6h\n\tparVO5hdK9alOpIqEi0A3KZXCXmAyxX4cPueBEqoeLaZQJqQlsdgtEkiRMJjDmyYHdlG\n\tFQDrVz9zUIsOymYermiZ4fZlscVwJ9UIHA4AGE3rlJ17T6nPNdyDsUYFxWC4vAgD4bIc\n\twE/834tlF8Ty8+3KMxuX6lo0ZfKgOMoEoXsW6aaymuevd6jMCpaK1WRa7RWhQPt+fEQ7\n\tlYHA==","X-Gm-Message-State":"ACgBeo0XXPG4tkWBlPpJL3+Bz2XxmUHYYCz784MlTdtxorDNO3xLZ7Sn\n\t2ajXhPRrPtsUYoMYlJA7L98WehQs3WpR+lIqAc3h33WlPcKACw==","X-Google-Smtp-Source":"AA6agR5jCNzsmwNC7pEGyaUXXeg2KKz0i67slpP23sf6IApEcHvuC86iOcJLvwfcBnh5j88yTjpXnTSjupgTxZSiJmo=","X-Received":"by 2002:a17:907:1c01:b0:6f4:2692:e23 with SMTP id\n\tnc1-20020a1709071c0100b006f426920e23mr20570556ejc.243.1660145105827;\n\tWed, 10 Aug 2022 08:25:05 -0700 (PDT)","MIME-Version":"1.0","References":"<20220721210351.1908-1-laurent.pinchart@ideasonboard.com>\n\t<166014406801.15821.18006622305930030211@Monstersaurus>","In-Reply-To":"<166014406801.15821.18006622305930030211@Monstersaurus>","Date":"Wed, 10 Aug 2022 16:24:55 +0100","Message-ID":"<CAHW6GYKbpJxjtj8q_w50aU2+Hj-aOXNHOrzdFSuxCi4PK-Buuw@mail.gmail.com>","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Content-Type":"text/plain; charset=\"UTF-8\"","Subject":"Re: [libcamera-devel] [PATCH] libcamera: Allow concurrent use of\n\tcameras from same pipeline handler","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","From":"David Plowman via libcamera-devel <libcamera-devel@lists.libcamera.org>","Reply-To":"David Plowman <david.plowman@raspberrypi.com>","Cc":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":24523,"web_url":"https://patchwork.libcamera.org/comment/24523/","msgid":"<166015050427.15821.11318304832071515549@Monstersaurus>","date":"2022-08-10T16:55:04","subject":"Re: [libcamera-devel] [PATCH] libcamera: Allow concurrent use of\n\tcameras from same pipeline handler","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Laurent,\n\nQuoting David Plowman (2022-08-10 16:24:55)\n> Hi Kieran, Laurent\n> \n> On Wed, 10 Aug 2022 at 16:07, Kieran Bingham\n> <kieran.bingham@ideasonboard.com> wrote:\n> >\n> > Hi Laurent,\n> >\n> > Quoting Laurent Pinchart (2022-07-21 22:03:51)\n> > > libcamera implements a pipeline handler locking mechanism based on\n> > > advisory locks on media devices, to prevent concurrent access to cameras\n> > > from the same pipeline handler from different processes (this only works\n> > > between multiple libcamera instances, as other processes won't use\n> > > advisory locks on media devices).\n> > >\n> > > A side effect of the implementation prevents multiple cameras created by\n> > > the same pipeline handler from being used concurrently. Fix this by\n> > > turning the PipelineHandler lock() and unlock() functions into acquire()\n> > > and release(), with a use count to replace the boolean lock flag. The\n> > > Camera class is updated accordingly.\n> > >\n> > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > ---\n> > > This may help addressing the problem reported in \"[RFC PATCH] qcam: Fix\n> > > IPU3 camera switching\".\n> >\n> > This does indeed fix things for me on IPU3 testing with qcam.\n> > It however seemed to introduce a regression in CTS (which perhaps\n> > Harvey's patches will then resolve)\n\nI've re-run this through CTS, and the regression is (unsurprisingly):\n\n android.hardware.camera2.cts.MultiViewTest#testDualCameraPreview\n\n android.hardware.camera2.CameraAccessException: CAMERA_ERROR (3): endConfigure:513: Camera 1: Error configuring streams: Function not implemented (-38)\n\tat android.hardware.camera2.CameraManager.throwAsPublicException(CameraManager.java:810)\n\tat android.hardware.camera2.impl.ICameraDeviceUserWrapper.endConfigure(ICameraDeviceUserWrapper.java:115)\n\tat android.hardware.camera2.impl.CameraDeviceImpl.configureStreamsChecked(CameraDeviceImpl.java:480)\n\tat android.hardware.camera2.impl.CameraDeviceImpl.createCaptureSessionInternal(CameraDeviceImpl.java:680)\n\tat android.hardware.camera2.impl.CameraDeviceImpl.createCaptureSession(CameraDeviceImpl.java:523)\n\tat android.hardware.camera2.cts.CameraTestUtils.configureCameraSession(CameraTestUtils.java:770)\n\tat android.hardware.camera2.cts.CameraTestUtils.configureCameraSession(CameraTestUtils.java:911)\n\tat android.hardware.camera2.cts.testcases.Camera2MultiViewTestCase$CameraHolder.startPreview(Camera2MultiViewTestCase.java:490)\n\tat android.hardware.camera2.cts.testcases.Camera2MultiViewTestCase.startPreview(Camera2MultiViewTestCase.java:252)\n\tat android.hardware.camera2.cts.MultiViewTest.startTextureViewPreview(MultiViewTest.java:881)\n\tat android.hardware.camera2.cts.MultiViewTest.testDualCameraPreview(MultiViewTest.java:228)\n\tat java.lang.reflect.Method.invoke(Native Method)\n\tat android.test.InstrumentationTestCase.runMethod(InstrumentationTestCase.java:220)\n\tat android.test.InstrumentationTestCase.runTest(InstrumentationTestCase.java:205)\n\tat android.test.ActivityInstrumentationTestCase2.runTest(ActivityInstrumentationTestCase2.java:192)\n\tat junit.framework.TestCase.runBare(TestCase.java:134)\n\tat junit.framework.TestResult$1.protect(TestResult.java:115)\n\tat androidx.test.internal.runner.junit3.AndroidTestResult.runProtected(AndroidTestResult.java:73)\n\tat junit.framework.TestResult.run(TestResult.java:118)\n\tat androidx.test.internal.runner.junit3.AndroidTestResult.run(AndroidTestResult.java:51)\n\tat junit.framework.TestCase.run(TestCase.java:124)\n\tat androidx.test.internal.runner.junit3.NonLeakyTestSuite$NonLeakyTest.run(NonLeakyTestSuite.java:62)\n\tat androidx.test.internal.runner.junit3.AndroidTestSuite$2.run(AndroidTestSuite.java:101)\n\tat java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:458)\n\tat java.util.concurrent.FutureTask.run(FutureTask.java:266)\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)\n\tat java.lang.Thread.run(Thread.java:764)\nCaused by: android.os.ServiceSpecificException: endConfigure:513: Camera 1: Error configuring streams: Function not implemented (-38) (code 10)\n\tat android.os.Parcel.createException(Parcel.java:1964)\n\tat android.os.Parcel.readException(Parcel.java:1918)\n\tat android.os.Parcel.readException(Parcel.java:1868)\n\tat android.hardware.camera2.ICameraDeviceUser$Stub$Proxy.endConfigure(ICameraDeviceUser.java:451)\n\tat android.hardware.camera2.impl.ICameraDeviceUserWrapper.endConfigure(ICameraDeviceUserWrapper.java:112)\n\t... 26 more\n\nGiven we'll know the exact cause of this regression, it's a single test\nfail; it's already not supported, and we anticipate a solution\ncoming from the ChromeOS team - I don't mind merging this patch early\nwith the regression noted.\n\n--\nKieran\n\n\n\n> >\n> > However, I think we'll hear from David that this would benefit the RPi\n> > platform already...\n> \n> Yes, this does fix something for me\n> (https://github.com/raspberrypi/picamera2/issues/229), so I'd be very\n> happy to see it merged! In my case the real problem was that lock()\n> calls unlock() when it fails, and unlock() tries to grab the mutex\n> that lock() already took. But I can certainly add this for this patch:\n> \n> Tested-by: David Plowman <david.plowman@raspberrypi.com>\n> \n> Thanks!\n> \n> David\n> \n> >\n> >\n> > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> >\n> > > ---\n> > >  include/libcamera/internal/camera.h           |  1 +\n> > >  include/libcamera/internal/pipeline_handler.h |  8 ++-\n> > >  src/libcamera/camera.cpp                      | 12 +++-\n> > >  src/libcamera/pipeline_handler.cpp            | 63 ++++++++++++-------\n> > >  4 files changed, 55 insertions(+), 29 deletions(-)\n> > >\n> > > diff --git a/include/libcamera/internal/camera.h b/include/libcamera/internal/camera.h\n> > > index 597426a6f9d2..38dd94ff8156 100644\n> > > --- a/include/libcamera/internal/camera.h\n> > > +++ b/include/libcamera/internal/camera.h\n> > > @@ -50,6 +50,7 @@ private:\n> > >                 CameraRunning,\n> > >         };\n> > >\n> > > +       bool isAcquired() const;\n> > >         bool isRunning() const;\n> > >         int isAccessAllowed(State state, bool allowDisconnected = false,\n> > >                             const char *from = __builtin_FUNCTION()) const;\n> > > diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h\n> > > index c3e4c2587ecd..20f1cbb07fea 100644\n> > > --- a/include/libcamera/internal/pipeline_handler.h\n> > > +++ b/include/libcamera/internal/pipeline_handler.h\n> > > @@ -45,8 +45,8 @@ public:\n> > >         MediaDevice *acquireMediaDevice(DeviceEnumerator *enumerator,\n> > >                                         const DeviceMatch &dm);\n> > >\n> > > -       bool lock();\n> > > -       void unlock();\n> > > +       bool acquire();\n> > > +       void release();\n> > >\n> > >         virtual CameraConfiguration *generateConfiguration(Camera *camera,\n> > >                 const StreamRoles &roles) = 0;\n> > > @@ -77,6 +77,8 @@ protected:\n> > >         CameraManager *manager_;\n> > >\n> > >  private:\n> > > +       void unlockMediaDevices();\n> > > +\n> > >         void mediaDeviceDisconnected(MediaDevice *media);\n> > >         virtual void disconnect();\n> > >\n> > > @@ -91,7 +93,7 @@ private:\n> > >         const char *name_;\n> > >\n> > >         Mutex lock_;\n> > > -       bool lockOwner_ LIBCAMERA_TSA_GUARDED_BY(lock_); /* *Not* ownership of lock_ */\n> > > +       unsigned int useCount_ LIBCAMERA_TSA_GUARDED_BY(lock_);\n> > >\n> > >         friend class PipelineHandlerFactory;\n> > >  };\n> > > diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp\n> > > index 2a8ef60e3862..9ce32db1ac78 100644\n> > > --- a/src/libcamera/camera.cpp\n> > > +++ b/src/libcamera/camera.cpp\n> > > @@ -508,6 +508,11 @@ static const char *const camera_state_names[] = {\n> > >         \"Running\",\n> > >  };\n> > >\n> > > +bool Camera::Private::isAcquired() const\n> > > +{\n> > > +       return state_.load(std::memory_order_acquire) == CameraRunning;\n> > > +}\n> > > +\n> > >  bool Camera::Private::isRunning() const\n> > >  {\n> > >         return state_.load(std::memory_order_acquire) == CameraRunning;\n> > > @@ -811,7 +816,7 @@ int Camera::exportFrameBuffers(Stream *stream,\n> > >   * not blocking, if the device has already been acquired (by the same or another\n> > >   * process) the -EBUSY error code is returned.\n> > >   *\n> > > - * Acquiring a camera will limit usage of any other camera(s) provided by the\n> > > + * Acquiring a camera may limit usage of any other camera(s) provided by the\n> > >   * same pipeline handler to the same instance of libcamera. The limit is in\n> > >   * effect until all cameras from the pipeline handler are released. Other\n> > >   * instances of libcamera can still list and examine the cameras but will fail\n> > > @@ -839,7 +844,7 @@ int Camera::acquire()\n> > >         if (ret < 0)\n> > >                 return ret == -EACCES ? -EBUSY : ret;\n> > >\n> > > -       if (!d->pipe_->lock()) {\n> > > +       if (!d->pipe_->acquire()) {\n> > >                 LOG(Camera, Info)\n> > >                         << \"Pipeline handler in use by another process\";\n> > >                 return -EBUSY;\n> > > @@ -873,7 +878,8 @@ int Camera::release()\n> > >         if (ret < 0)\n> > >                 return ret == -EACCES ? -EBUSY : ret;\n> > >\n> > > -       d->pipe_->unlock();\n> > > +       if (d->isAcquired())\n> > > +               d->pipe_->release();\n> > >\n> > >         d->setState(Private::CameraAvailable);\n> > >\n> > > diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp\n> > > index 675405330f45..7d2d00ef45e6 100644\n> > > --- a/src/libcamera/pipeline_handler.cpp\n> > > +++ b/src/libcamera/pipeline_handler.cpp\n> > > @@ -68,7 +68,7 @@ LOG_DEFINE_CATEGORY(Pipeline)\n> > >   * respective factories.\n> > >   */\n> > >  PipelineHandler::PipelineHandler(CameraManager *manager)\n> > > -       : manager_(manager), lockOwner_(false)\n> > > +       : manager_(manager), useCount_(0)\n> > >  {\n> > >  }\n> > >\n> > > @@ -143,58 +143,75 @@ MediaDevice *PipelineHandler::acquireMediaDevice(DeviceEnumerator *enumerator,\n> > >  }\n> > >\n> > >  /**\n> > > - * \\brief Lock all media devices acquired by the pipeline\n> > > + * \\brief Acquire exclusive access to the pipeline handler for the process\n> > >   *\n> > > - * This function shall not be called from pipeline handler implementation, as\n> > > - * the Camera class handles locking directly.\n> > > + * This function locks all the media devices used by the pipeline to ensure\n> > > + * that no other process can access them concurrently.\n> > > + *\n> > > + * Access to a pipeline handler may be acquired recursively from within the\n> > > + * same process. Every successful acquire() call shall be matched with a\n> > > + * release() call. This allows concurrent access to the same pipeline handler\n> > > + * from different cameras within the same process.\n> > > + *\n> > > + * Pipeline handlers shall not call this function directly as the Camera class\n> > > + * handles access internally.\n> > >   *\n> > >   * \\context This function is \\threadsafe.\n> > >   *\n> > > - * \\return True if the devices could be locked, false otherwise\n> > > - * \\sa unlock()\n> > > - * \\sa MediaDevice::lock()\n> > > + * \\return True if the pipeline handler was acquired, false if another process\n> > > + * has already acquired it\n> > > + * \\sa release()\n> > >   */\n> > > -bool PipelineHandler::lock()\n> > > +bool PipelineHandler::acquire()\n> > >  {\n> > >         MutexLocker locker(lock_);\n> > >\n> > > -       /* Do not allow nested locking in the same libcamera instance. */\n> > > -       if (lockOwner_)\n> > > -               return false;\n> > > +       if (useCount_) {\n> > > +               ++useCount_;\n> > > +               return true;\n> > > +       }\n> > >\n> > >         for (std::shared_ptr<MediaDevice> &media : mediaDevices_) {\n> > >                 if (!media->lock()) {\n> > > -                       unlock();\n> > > +                       unlockMediaDevices();\n> > >                         return false;\n> > >                 }\n> > >         }\n> > >\n> > > -       lockOwner_ = true;\n> > > -\n> > > +       ++useCount_;\n> > >         return true;\n> > >  }\n> > >\n> > >  /**\n> > > - * \\brief Unlock all media devices acquired by the pipeline\n> > > + * \\brief Release exclusive access to the pipeline handler\n> > >   *\n> > > - * This function shall not be called from pipeline handler implementation, as\n> > > - * the Camera class handles locking directly.\n> > > + * This function releases access to the pipeline handler previously acquired by\n> > > + * a call to acquire(). Every release() call shall match a previous successful\n> > > + * acquire() call. Calling this function on a pipeline handler that hasn't been\n> > > + * acquired results in undefined behaviour.\n> > > + *\n> > > + * Pipeline handlers shall not call this function directly as the Camera class\n> > > + * handles access internally.\n> > >   *\n> > >   * \\context This function is \\threadsafe.\n> > >   *\n> > > - * \\sa lock()\n> > > + * \\sa acquire()\n> > >   */\n> > > -void PipelineHandler::unlock()\n> > > +void PipelineHandler::release()\n> > >  {\n> > >         MutexLocker locker(lock_);\n> > >\n> > > -       if (!lockOwner_)\n> > > -               return;\n> > > +       ASSERT(useCount_);\n> > >\n> > > +       unlockMediaDevices();\n> > > +\n> > > +       --useCount_;\n> > > +}\n> > > +\n> > > +void PipelineHandler::unlockMediaDevices()\n> > > +{\n> > >         for (std::shared_ptr<MediaDevice> &media : mediaDevices_)\n> > >                 media->unlock();\n> > > -\n> > > -       lockOwner_ = false;\n> > >  }\n> > >\n> > >  /**\n> > >\n> > > base-commit: d4eee094e684806dbdb54fbe1bc02c9c590aa7f4\n> > > --\n> > > Regards,\n> > >\n> > > Laurent Pinchart\n> > >","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 3B027BE173\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 10 Aug 2022 16:55:09 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 9501E6332B;\n\tWed, 10 Aug 2022 18:55:08 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 8FDE0600EA\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 10 Aug 2022 18:55:07 +0200 (CEST)","from pendragon.ideasonboard.com\n\t(cpc89244-aztw30-2-0-cust3082.18-1.cable.virginm.net [86.31.172.11])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 0791F3F1;\n\tWed, 10 Aug 2022 18:55:06 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1660150508;\n\tbh=lQAkWUgn6hGTSbgeAwM1awJVH0A4B1QjrQ/W4FEZIcU=;\n\th=In-Reply-To:References:To:Date:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=NQtd3vhmtoKiCa9zpElSl26/GDgBpLOoNyqr9/h1YNsHmh1qdYiXUl5EvsAN+z6f3\n\tuYhZ6dU6tWKrE6AsW6KPGnT742t3B1D3o3xUgQ7jZpplFs+23UmOaeNc93+BI5aPvM\n\tx5+s0vA/BCsGk/4Xc/S7UoxwAh/SqQ12A7IGoaxyu/2cqXNFltVQRAkP92OAMhb3pm\n\txdYMxyciOZ+Jo7kLfaRcyDc8ERK8XIECguavQwSy83Vb1FB0SCOzjrTU/T1678j1i0\n\tKD516SHiZqUy5UHxCA66qcFmJabV7Poc8FcWyGTjN4u16L3/ml62VKvRVP0rXaXOrk\n\tCiojdHYxLjDnA==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1660150507;\n\tbh=lQAkWUgn6hGTSbgeAwM1awJVH0A4B1QjrQ/W4FEZIcU=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=cjlnICNzS9sRwVbkJOMrl5F5QUXUmTC0I6+t5xqDnP6I9ShB8R2DJDwK6MDGLtd52\n\tEfFc3ffoQTCGHt9P6GJ+8Ej3pytUmkqBKYSikyq8I2GEyg6PawDEV47pr9F6Qpv3cZ\n\tswL+ulRbg7o4e7F7+pcnPSF7Mi//ynBhpBcOgjKQ="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"cjlnICNz\"; dkim-atps=neutral","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<CAHW6GYKbpJxjtj8q_w50aU2+Hj-aOXNHOrzdFSuxCi4PK-Buuw@mail.gmail.com>","References":"<20220721210351.1908-1-laurent.pinchart@ideasonboard.com>\n\t<166014406801.15821.18006622305930030211@Monstersaurus>\n\t<CAHW6GYKbpJxjtj8q_w50aU2+Hj-aOXNHOrzdFSuxCi4PK-Buuw@mail.gmail.com>","To":"David Plowman <david.plowman@raspberrypi.com>","Date":"Wed, 10 Aug 2022 17:55:04 +0100","Message-ID":"<166015050427.15821.11318304832071515549@Monstersaurus>","User-Agent":"alot/0.10","Subject":"Re: [libcamera-devel] [PATCH] libcamera: Allow concurrent use of\n\tcameras from same pipeline handler","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","From":"Kieran Bingham via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":24524,"web_url":"https://patchwork.libcamera.org/comment/24524/","msgid":"<YvPmt7FdRwgUmqeX@pendragon.ideasonboard.com>","date":"2022-08-10T17:11:19","subject":"Re: [libcamera-devel] [PATCH] libcamera: Allow concurrent use of\n\tcameras from same pipeline handler","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Kieran,\n\nOn Wed, Aug 10, 2022 at 05:55:04PM +0100, Kieran Bingham wrote:\n> Quoting David Plowman (2022-08-10 16:24:55)\n> > On Wed, 10 Aug 2022 at 16:07, Kieran Bingham wrote:\n> > > Quoting Laurent Pinchart (2022-07-21 22:03:51)\n> > > > libcamera implements a pipeline handler locking mechanism based on\n> > > > advisory locks on media devices, to prevent concurrent access to cameras\n> > > > from the same pipeline handler from different processes (this only works\n> > > > between multiple libcamera instances, as other processes won't use\n> > > > advisory locks on media devices).\n> > > >\n> > > > A side effect of the implementation prevents multiple cameras created by\n> > > > the same pipeline handler from being used concurrently. Fix this by\n> > > > turning the PipelineHandler lock() and unlock() functions into acquire()\n> > > > and release(), with a use count to replace the boolean lock flag. The\n> > > > Camera class is updated accordingly.\n> > > >\n> > > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > > ---\n> > > > This may help addressing the problem reported in \"[RFC PATCH] qcam: Fix\n> > > > IPU3 camera switching\".\n> > >\n> > > This does indeed fix things for me on IPU3 testing with qcam.\n> > > It however seemed to introduce a regression in CTS (which perhaps\n> > > Harvey's patches will then resolve)\n> \n> I've re-run this through CTS, and the regression is (unsurprisingly):\n> \n>  android.hardware.camera2.cts.MultiViewTest#testDualCameraPreview\n> \n>  android.hardware.camera2.CameraAccessException: CAMERA_ERROR (3): endConfigure:513: Camera 1: Error configuring streams: Function not implemented (-38)\n> \tat android.hardware.camera2.CameraManager.throwAsPublicException(CameraManager.java:810)\n> \tat android.hardware.camera2.impl.ICameraDeviceUserWrapper.endConfigure(ICameraDeviceUserWrapper.java:115)\n> \tat android.hardware.camera2.impl.CameraDeviceImpl.configureStreamsChecked(CameraDeviceImpl.java:480)\n> \tat android.hardware.camera2.impl.CameraDeviceImpl.createCaptureSessionInternal(CameraDeviceImpl.java:680)\n> \tat android.hardware.camera2.impl.CameraDeviceImpl.createCaptureSession(CameraDeviceImpl.java:523)\n> \tat android.hardware.camera2.cts.CameraTestUtils.configureCameraSession(CameraTestUtils.java:770)\n> \tat android.hardware.camera2.cts.CameraTestUtils.configureCameraSession(CameraTestUtils.java:911)\n> \tat android.hardware.camera2.cts.testcases.Camera2MultiViewTestCase$CameraHolder.startPreview(Camera2MultiViewTestCase.java:490)\n> \tat android.hardware.camera2.cts.testcases.Camera2MultiViewTestCase.startPreview(Camera2MultiViewTestCase.java:252)\n> \tat android.hardware.camera2.cts.MultiViewTest.startTextureViewPreview(MultiViewTest.java:881)\n> \tat android.hardware.camera2.cts.MultiViewTest.testDualCameraPreview(MultiViewTest.java:228)\n> \tat java.lang.reflect.Method.invoke(Native Method)\n> \tat android.test.InstrumentationTestCase.runMethod(InstrumentationTestCase.java:220)\n> \tat android.test.InstrumentationTestCase.runTest(InstrumentationTestCase.java:205)\n> \tat android.test.ActivityInstrumentationTestCase2.runTest(ActivityInstrumentationTestCase2.java:192)\n> \tat junit.framework.TestCase.runBare(TestCase.java:134)\n> \tat junit.framework.TestResult$1.protect(TestResult.java:115)\n> \tat androidx.test.internal.runner.junit3.AndroidTestResult.runProtected(AndroidTestResult.java:73)\n> \tat junit.framework.TestResult.run(TestResult.java:118)\n> \tat androidx.test.internal.runner.junit3.AndroidTestResult.run(AndroidTestResult.java:51)\n> \tat junit.framework.TestCase.run(TestCase.java:124)\n> \tat androidx.test.internal.runner.junit3.NonLeakyTestSuite$NonLeakyTest.run(NonLeakyTestSuite.java:62)\n> \tat androidx.test.internal.runner.junit3.AndroidTestSuite$2.run(AndroidTestSuite.java:101)\n> \tat java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:458)\n> \tat java.util.concurrent.FutureTask.run(FutureTask.java:266)\n> \tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)\n> \tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)\n> \tat java.lang.Thread.run(Thread.java:764)\n> Caused by: android.os.ServiceSpecificException: endConfigure:513: Camera 1: Error configuring streams: Function not implemented (-38) (code 10)\n> \tat android.os.Parcel.createException(Parcel.java:1964)\n> \tat android.os.Parcel.readException(Parcel.java:1918)\n> \tat android.os.Parcel.readException(Parcel.java:1868)\n> \tat android.hardware.camera2.ICameraDeviceUser$Stub$Proxy.endConfigure(ICameraDeviceUser.java:451)\n> \tat android.hardware.camera2.impl.ICameraDeviceUserWrapper.endConfigure(ICameraDeviceUserWrapper.java:112)\n> \t... 26 more\n> \n> Given we'll know the exact cause of this regression, it's a single test\n> fail; it's already not supported, and we anticipate a solution\n> coming from the ChromeOS team - I don't mind merging this patch early\n> with the regression noted.\n\nI'll add the following paragraph to the commit message:\n\nAs a consequence of this change, the IPU3 pipeline handler will fail to\noperate properly when the cameras it exposes are operated concurrently.\nThe android.hardware.camera2.cts.MultiViewTest#testDualCameraPreview\ntest fails as a result. This should be fixed in the IPU3 pipeline\nhandler to implement mutual exclusion between cameras.\n\n> > > However, I think we'll hear from David that this would benefit the RPi\n> > > platform already...\n> > \n> > Yes, this does fix something for me\n> > (https://github.com/raspberrypi/picamera2/issues/229), so I'd be very\n> > happy to see it merged! In my case the real problem was that lock()\n> > calls unlock() when it fails, and unlock() tries to grab the mutex\n> > that lock() already took. But I can certainly add this for this patch:\n> > \n> > Tested-by: David Plowman <david.plowman@raspberrypi.com>\n> > \n> > Thanks!\n> > \n> > David\n> > \n> > >\n> > >\n> > > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> > >\n> > > > ---\n> > > >  include/libcamera/internal/camera.h           |  1 +\n> > > >  include/libcamera/internal/pipeline_handler.h |  8 ++-\n> > > >  src/libcamera/camera.cpp                      | 12 +++-\n> > > >  src/libcamera/pipeline_handler.cpp            | 63 ++++++++++++-------\n> > > >  4 files changed, 55 insertions(+), 29 deletions(-)\n> > > >\n> > > > diff --git a/include/libcamera/internal/camera.h b/include/libcamera/internal/camera.h\n> > > > index 597426a6f9d2..38dd94ff8156 100644\n> > > > --- a/include/libcamera/internal/camera.h\n> > > > +++ b/include/libcamera/internal/camera.h\n> > > > @@ -50,6 +50,7 @@ private:\n> > > >                 CameraRunning,\n> > > >         };\n> > > >\n> > > > +       bool isAcquired() const;\n> > > >         bool isRunning() const;\n> > > >         int isAccessAllowed(State state, bool allowDisconnected = false,\n> > > >                             const char *from = __builtin_FUNCTION()) const;\n> > > > diff --git a/include/libcamera/internal/pipeline_handler.h b/include/libcamera/internal/pipeline_handler.h\n> > > > index c3e4c2587ecd..20f1cbb07fea 100644\n> > > > --- a/include/libcamera/internal/pipeline_handler.h\n> > > > +++ b/include/libcamera/internal/pipeline_handler.h\n> > > > @@ -45,8 +45,8 @@ public:\n> > > >         MediaDevice *acquireMediaDevice(DeviceEnumerator *enumerator,\n> > > >                                         const DeviceMatch &dm);\n> > > >\n> > > > -       bool lock();\n> > > > -       void unlock();\n> > > > +       bool acquire();\n> > > > +       void release();\n> > > >\n> > > >         virtual CameraConfiguration *generateConfiguration(Camera *camera,\n> > > >                 const StreamRoles &roles) = 0;\n> > > > @@ -77,6 +77,8 @@ protected:\n> > > >         CameraManager *manager_;\n> > > >\n> > > >  private:\n> > > > +       void unlockMediaDevices();\n> > > > +\n> > > >         void mediaDeviceDisconnected(MediaDevice *media);\n> > > >         virtual void disconnect();\n> > > >\n> > > > @@ -91,7 +93,7 @@ private:\n> > > >         const char *name_;\n> > > >\n> > > >         Mutex lock_;\n> > > > -       bool lockOwner_ LIBCAMERA_TSA_GUARDED_BY(lock_); /* *Not* ownership of lock_ */\n> > > > +       unsigned int useCount_ LIBCAMERA_TSA_GUARDED_BY(lock_);\n> > > >\n> > > >         friend class PipelineHandlerFactory;\n> > > >  };\n> > > > diff --git a/src/libcamera/camera.cpp b/src/libcamera/camera.cpp\n> > > > index 2a8ef60e3862..9ce32db1ac78 100644\n> > > > --- a/src/libcamera/camera.cpp\n> > > > +++ b/src/libcamera/camera.cpp\n> > > > @@ -508,6 +508,11 @@ static const char *const camera_state_names[] = {\n> > > >         \"Running\",\n> > > >  };\n> > > >\n> > > > +bool Camera::Private::isAcquired() const\n> > > > +{\n> > > > +       return state_.load(std::memory_order_acquire) == CameraRunning;\n> > > > +}\n> > > > +\n> > > >  bool Camera::Private::isRunning() const\n> > > >  {\n> > > >         return state_.load(std::memory_order_acquire) == CameraRunning;\n> > > > @@ -811,7 +816,7 @@ int Camera::exportFrameBuffers(Stream *stream,\n> > > >   * not blocking, if the device has already been acquired (by the same or another\n> > > >   * process) the -EBUSY error code is returned.\n> > > >   *\n> > > > - * Acquiring a camera will limit usage of any other camera(s) provided by the\n> > > > + * Acquiring a camera may limit usage of any other camera(s) provided by the\n> > > >   * same pipeline handler to the same instance of libcamera. The limit is in\n> > > >   * effect until all cameras from the pipeline handler are released. Other\n> > > >   * instances of libcamera can still list and examine the cameras but will fail\n> > > > @@ -839,7 +844,7 @@ int Camera::acquire()\n> > > >         if (ret < 0)\n> > > >                 return ret == -EACCES ? -EBUSY : ret;\n> > > >\n> > > > -       if (!d->pipe_->lock()) {\n> > > > +       if (!d->pipe_->acquire()) {\n> > > >                 LOG(Camera, Info)\n> > > >                         << \"Pipeline handler in use by another process\";\n> > > >                 return -EBUSY;\n> > > > @@ -873,7 +878,8 @@ int Camera::release()\n> > > >         if (ret < 0)\n> > > >                 return ret == -EACCES ? -EBUSY : ret;\n> > > >\n> > > > -       d->pipe_->unlock();\n> > > > +       if (d->isAcquired())\n> > > > +               d->pipe_->release();\n> > > >\n> > > >         d->setState(Private::CameraAvailable);\n> > > >\n> > > > diff --git a/src/libcamera/pipeline_handler.cpp b/src/libcamera/pipeline_handler.cpp\n> > > > index 675405330f45..7d2d00ef45e6 100644\n> > > > --- a/src/libcamera/pipeline_handler.cpp\n> > > > +++ b/src/libcamera/pipeline_handler.cpp\n> > > > @@ -68,7 +68,7 @@ LOG_DEFINE_CATEGORY(Pipeline)\n> > > >   * respective factories.\n> > > >   */\n> > > >  PipelineHandler::PipelineHandler(CameraManager *manager)\n> > > > -       : manager_(manager), lockOwner_(false)\n> > > > +       : manager_(manager), useCount_(0)\n> > > >  {\n> > > >  }\n> > > >\n> > > > @@ -143,58 +143,75 @@ MediaDevice *PipelineHandler::acquireMediaDevice(DeviceEnumerator *enumerator,\n> > > >  }\n> > > >\n> > > >  /**\n> > > > - * \\brief Lock all media devices acquired by the pipeline\n> > > > + * \\brief Acquire exclusive access to the pipeline handler for the process\n> > > >   *\n> > > > - * This function shall not be called from pipeline handler implementation, as\n> > > > - * the Camera class handles locking directly.\n> > > > + * This function locks all the media devices used by the pipeline to ensure\n> > > > + * that no other process can access them concurrently.\n> > > > + *\n> > > > + * Access to a pipeline handler may be acquired recursively from within the\n> > > > + * same process. Every successful acquire() call shall be matched with a\n> > > > + * release() call. This allows concurrent access to the same pipeline handler\n> > > > + * from different cameras within the same process.\n> > > > + *\n> > > > + * Pipeline handlers shall not call this function directly as the Camera class\n> > > > + * handles access internally.\n> > > >   *\n> > > >   * \\context This function is \\threadsafe.\n> > > >   *\n> > > > - * \\return True if the devices could be locked, false otherwise\n> > > > - * \\sa unlock()\n> > > > - * \\sa MediaDevice::lock()\n> > > > + * \\return True if the pipeline handler was acquired, false if another process\n> > > > + * has already acquired it\n> > > > + * \\sa release()\n> > > >   */\n> > > > -bool PipelineHandler::lock()\n> > > > +bool PipelineHandler::acquire()\n> > > >  {\n> > > >         MutexLocker locker(lock_);\n> > > >\n> > > > -       /* Do not allow nested locking in the same libcamera instance. */\n> > > > -       if (lockOwner_)\n> > > > -               return false;\n> > > > +       if (useCount_) {\n> > > > +               ++useCount_;\n> > > > +               return true;\n> > > > +       }\n> > > >\n> > > >         for (std::shared_ptr<MediaDevice> &media : mediaDevices_) {\n> > > >                 if (!media->lock()) {\n> > > > -                       unlock();\n> > > > +                       unlockMediaDevices();\n> > > >                         return false;\n> > > >                 }\n> > > >         }\n> > > >\n> > > > -       lockOwner_ = true;\n> > > > -\n> > > > +       ++useCount_;\n> > > >         return true;\n> > > >  }\n> > > >\n> > > >  /**\n> > > > - * \\brief Unlock all media devices acquired by the pipeline\n> > > > + * \\brief Release exclusive access to the pipeline handler\n> > > >   *\n> > > > - * This function shall not be called from pipeline handler implementation, as\n> > > > - * the Camera class handles locking directly.\n> > > > + * This function releases access to the pipeline handler previously acquired by\n> > > > + * a call to acquire(). Every release() call shall match a previous successful\n> > > > + * acquire() call. Calling this function on a pipeline handler that hasn't been\n> > > > + * acquired results in undefined behaviour.\n> > > > + *\n> > > > + * Pipeline handlers shall not call this function directly as the Camera class\n> > > > + * handles access internally.\n> > > >   *\n> > > >   * \\context This function is \\threadsafe.\n> > > >   *\n> > > > - * \\sa lock()\n> > > > + * \\sa acquire()\n> > > >   */\n> > > > -void PipelineHandler::unlock()\n> > > > +void PipelineHandler::release()\n> > > >  {\n> > > >         MutexLocker locker(lock_);\n> > > >\n> > > > -       if (!lockOwner_)\n> > > > -               return;\n> > > > +       ASSERT(useCount_);\n> > > >\n> > > > +       unlockMediaDevices();\n> > > > +\n> > > > +       --useCount_;\n> > > > +}\n> > > > +\n> > > > +void PipelineHandler::unlockMediaDevices()\n> > > > +{\n> > > >         for (std::shared_ptr<MediaDevice> &media : mediaDevices_)\n> > > >                 media->unlock();\n> > > > -\n> > > > -       lockOwner_ = false;\n> > > >  }\n> > > >\n> > > >  /**\n> > > >\n> > > > base-commit: d4eee094e684806dbdb54fbe1bc02c9c590aa7f4","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id B5F06C3272\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 10 Aug 2022 17:11:34 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 3842B63328;\n\tWed, 10 Aug 2022 19:11:34 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 00118600EA\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 10 Aug 2022 19:11:31 +0200 (CEST)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 66A4C3F1;\n\tWed, 10 Aug 2022 19:11:31 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1660151494;\n\tbh=nu76V75aeHHwpihWjd0DnP31IYDV0kKqE7P/99b4MXA=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=KRfS6wRQpeOdik0VuVOJuAmrsJhTMNxqNN3ewC9GV070N2KYwJKBEm8qY/s8ebJQI\n\tCCvc9CTzydN+yQ2cCouQVdx+SmKHgdU2DeBuUqYci6Vnp6wBSGe8bw11SFNmyN9A51\n\tpnSIiQ76J/V/TTfADBJxi1T84tS/2n6JW6M1iEuYdbTmOQpwAns5uYDruvyiIim6x3\n\tHFqe6EBj6LVH/nJ+m0Qd0MAYR7k2P/nl/BIGTogEfYMIIIJmlFsEBdN9xSg4WWhHSV\n\t+ZQZe/759rYF2Fqkye7uV+zNUGiQ5pAhb/740PRc+NqYysfYiPusP4A+YD5skc7nWo\n\tOJ6V6yPPOLlvw==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1660151491;\n\tbh=nu76V75aeHHwpihWjd0DnP31IYDV0kKqE7P/99b4MXA=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=p3JKI7nnn/Xqe7QFR5a8W5Xwf5JZ7by66G3p3T84fFoi6XGfFg6/h1V2JOhhZhiwZ\n\txIUIluuHNTxu/N62eHeK8j5YmnZhhB79ymMqc9mw5bzj/byT/BuHT725++xsgWLd6t\n\tWh/B+FNk2CqmkPmQUwvd6a1FCw9RwCrQklVMNQe0="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"p3JKI7nn\"; dkim-atps=neutral","Date":"Wed, 10 Aug 2022 20:11:19 +0300","To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Message-ID":"<YvPmt7FdRwgUmqeX@pendragon.ideasonboard.com>","References":"<20220721210351.1908-1-laurent.pinchart@ideasonboard.com>\n\t<166014406801.15821.18006622305930030211@Monstersaurus>\n\t<CAHW6GYKbpJxjtj8q_w50aU2+Hj-aOXNHOrzdFSuxCi4PK-Buuw@mail.gmail.com>\n\t<166015050427.15821.11318304832071515549@Monstersaurus>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<166015050427.15821.11318304832071515549@Monstersaurus>","Subject":"Re: [libcamera-devel] [PATCH] libcamera: Allow concurrent use of\n\tcameras from same pipeline handler","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","From":"Laurent Pinchart via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":24525,"web_url":"https://patchwork.libcamera.org/comment/24525/","msgid":"<166016653374.15821.16225479429068552849@Monstersaurus>","date":"2022-08-10T21:22:13","subject":"Re: [libcamera-devel] [PATCH] libcamera: Allow concurrent use of\n\tcameras from same pipeline handler","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Quoting Laurent Pinchart (2022-08-10 18:11:19)\n> Hi Kieran,\n> \n> On Wed, Aug 10, 2022 at 05:55:04PM +0100, Kieran Bingham wrote:\n> > Quoting David Plowman (2022-08-10 16:24:55)\n> > > On Wed, 10 Aug 2022 at 16:07, Kieran Bingham wrote:\n> > > > Quoting Laurent Pinchart (2022-07-21 22:03:51)\n> > > > > libcamera implements a pipeline handler locking mechanism based on\n> > > > > advisory locks on media devices, to prevent concurrent access to cameras\n> > > > > from the same pipeline handler from different processes (this only works\n> > > > > between multiple libcamera instances, as other processes won't use\n> > > > > advisory locks on media devices).\n> > > > >\n> > > > > A side effect of the implementation prevents multiple cameras created by\n> > > > > the same pipeline handler from being used concurrently. Fix this by\n> > > > > turning the PipelineHandler lock() and unlock() functions into acquire()\n> > > > > and release(), with a use count to replace the boolean lock flag. The\n> > > > > Camera class is updated accordingly.\n> > > > >\n> > > > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > > > ---\n> > > > > This may help addressing the problem reported in \"[RFC PATCH] qcam: Fix\n> > > > > IPU3 camera switching\".\n> > > >\n> > > > This does indeed fix things for me on IPU3 testing with qcam.\n> > > > It however seemed to introduce a regression in CTS (which perhaps\n> > > > Harvey's patches will then resolve)\n> > \n> > I've re-run this through CTS, and the regression is (unsurprisingly):\n> > \n> >  android.hardware.camera2.cts.MultiViewTest#testDualCameraPreview\n> > \n> >  android.hardware.camera2.CameraAccessException: CAMERA_ERROR (3): endConfigure:513: Camera 1: Error configuring streams: Function not implemented (-38)\n> >       at android.hardware.camera2.CameraManager.throwAsPublicException(CameraManager.java:810)\n> >       at android.hardware.camera2.impl.ICameraDeviceUserWrapper.endConfigure(ICameraDeviceUserWrapper.java:115)\n> >       at android.hardware.camera2.impl.CameraDeviceImpl.configureStreamsChecked(CameraDeviceImpl.java:480)\n> >       at android.hardware.camera2.impl.CameraDeviceImpl.createCaptureSessionInternal(CameraDeviceImpl.java:680)\n> >       at android.hardware.camera2.impl.CameraDeviceImpl.createCaptureSession(CameraDeviceImpl.java:523)\n> >       at android.hardware.camera2.cts.CameraTestUtils.configureCameraSession(CameraTestUtils.java:770)\n> >       at android.hardware.camera2.cts.CameraTestUtils.configureCameraSession(CameraTestUtils.java:911)\n> >       at android.hardware.camera2.cts.testcases.Camera2MultiViewTestCase$CameraHolder.startPreview(Camera2MultiViewTestCase.java:490)\n> >       at android.hardware.camera2.cts.testcases.Camera2MultiViewTestCase.startPreview(Camera2MultiViewTestCase.java:252)\n> >       at android.hardware.camera2.cts.MultiViewTest.startTextureViewPreview(MultiViewTest.java:881)\n> >       at android.hardware.camera2.cts.MultiViewTest.testDualCameraPreview(MultiViewTest.java:228)\n> >       at java.lang.reflect.Method.invoke(Native Method)\n> >       at android.test.InstrumentationTestCase.runMethod(InstrumentationTestCase.java:220)\n> >       at android.test.InstrumentationTestCase.runTest(InstrumentationTestCase.java:205)\n> >       at android.test.ActivityInstrumentationTestCase2.runTest(ActivityInstrumentationTestCase2.java:192)\n> >       at junit.framework.TestCase.runBare(TestCase.java:134)\n> >       at junit.framework.TestResult$1.protect(TestResult.java:115)\n> >       at androidx.test.internal.runner.junit3.AndroidTestResult.runProtected(AndroidTestResult.java:73)\n> >       at junit.framework.TestResult.run(TestResult.java:118)\n> >       at androidx.test.internal.runner.junit3.AndroidTestResult.run(AndroidTestResult.java:51)\n> >       at junit.framework.TestCase.run(TestCase.java:124)\n> >       at androidx.test.internal.runner.junit3.NonLeakyTestSuite$NonLeakyTest.run(NonLeakyTestSuite.java:62)\n> >       at androidx.test.internal.runner.junit3.AndroidTestSuite$2.run(AndroidTestSuite.java:101)\n> >       at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:458)\n> >       at java.util.concurrent.FutureTask.run(FutureTask.java:266)\n> >       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)\n> >       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)\n> >       at java.lang.Thread.run(Thread.java:764)\n> > Caused by: android.os.ServiceSpecificException: endConfigure:513: Camera 1: Error configuring streams: Function not implemented (-38) (code 10)\n> >       at android.os.Parcel.createException(Parcel.java:1964)\n> >       at android.os.Parcel.readException(Parcel.java:1918)\n> >       at android.os.Parcel.readException(Parcel.java:1868)\n> >       at android.hardware.camera2.ICameraDeviceUser$Stub$Proxy.endConfigure(ICameraDeviceUser.java:451)\n> >       at android.hardware.camera2.impl.ICameraDeviceUserWrapper.endConfigure(ICameraDeviceUserWrapper.java:112)\n> >       ... 26 more\n> > \n> > Given we'll know the exact cause of this regression, it's a single test\n> > fail; it's already not supported, and we anticipate a solution\n> > coming from the ChromeOS team - I don't mind merging this patch early\n> > with the regression noted.\n> \n> I'll add the following paragraph to the commit message:\n> \n> As a consequence of this change, the IPU3 pipeline handler will fail to\n> operate properly when the cameras it exposes are operated concurrently.\n> The android.hardware.camera2.cts.MultiViewTest#testDualCameraPreview\n> test fails as a result. This should be fixed in the IPU3 pipeline\n> handler to implement mutual exclusion between cameras.\n\nInteresting to note, that before this patch - the Chrome Camera App\ncan't switch cameras (much like qcam can't) - and now it can. I hope\nthat the mutual exclusion doesn't re-introduce this limitation ;-)\n\n--\nKieran","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 90DF6C3272\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 10 Aug 2022 21:22:18 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id DD4156332B;\n\tWed, 10 Aug 2022 23:22:17 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 4586E600EA\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 10 Aug 2022 23:22:16 +0200 (CEST)","from pendragon.ideasonboard.com\n\t(cpc89244-aztw30-2-0-cust3082.18-1.cable.virginm.net [86.31.172.11])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id BF8833F1;\n\tWed, 10 Aug 2022 23:22:15 +0200 (CEST)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1660166537;\n\tbh=FaK3RqduItftLegmephOXyaqfnr6WevI4fRP6JK0X/I=;\n\th=In-Reply-To:References:To:Date:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=AgXpPLSRBPZUTAI22RTQPz7T4yEPUTBizV/l3vQbxIEuxChbSlZbrlrYJBBRxFyeE\n\to8QN4ZdoQ63Wv5ajXQiTFasnx7psx2YPzNxvpEOfr5FEKCMDRw1dUKIFhGuiI3R3ej\n\tZrl+Q0r7OIOtshckcuUYQGHwdL4x/q7kNFkAOmlJS+DgN0jHG8gOZDUraj2ggwOjp1\n\t9aIkCMPXl3FhNZw/tamY5wiwLymW45dsaLuzUeqcc/8a5e3hmnHsknnEa4bVASEPup\n\ti2i5Gz8Aio+tfzI5+OrchbEr4Vz3CucgjSdMIRNASesGAv/AVMVgEoiHn3bagKx7dJ\n\th/nf51tEIPcxA==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1660166535;\n\tbh=FaK3RqduItftLegmephOXyaqfnr6WevI4fRP6JK0X/I=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=WiYadmW2jV4qcCu6mTSFLi1F/GTg4nz2TVNq9xHavvashLqKwDbdgR28pVu1vfGXi\n\tvjOV7c7Y4kmL1eQuiEwg/R0cWDdTN8LS32EKD0TYpdnE3VVfMB9NCKsYj2FUtxqv4c\n\tUE6GJGzdGkmUXk/O5OhZnOz+cAT/nwuFYqLkFddE="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"WiYadmW2\"; dkim-atps=neutral","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<YvPmt7FdRwgUmqeX@pendragon.ideasonboard.com>","References":"<20220721210351.1908-1-laurent.pinchart@ideasonboard.com>\n\t<166014406801.15821.18006622305930030211@Monstersaurus>\n\t<CAHW6GYKbpJxjtj8q_w50aU2+Hj-aOXNHOrzdFSuxCi4PK-Buuw@mail.gmail.com>\n\t<166015050427.15821.11318304832071515549@Monstersaurus>\n\t<YvPmt7FdRwgUmqeX@pendragon.ideasonboard.com>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Date":"Wed, 10 Aug 2022 22:22:13 +0100","Message-ID":"<166016653374.15821.16225479429068552849@Monstersaurus>","User-Agent":"alot/0.10","Subject":"Re: [libcamera-devel] [PATCH] libcamera: Allow concurrent use of\n\tcameras from same pipeline handler","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","From":"Kieran Bingham via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"libcamera devel <libcamera-devel@lists.libcamera.org>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]