[v1] libcamera: camera_manager: Do not emit signals while holding lock
diff mbox series

Message ID 20250303142010.714280-1-barnabas.pocze@ideasonboard.com
State New
Headers show
Series
  • [v1] libcamera: camera_manager: Do not emit signals while holding lock
Related show

Commit Message

Barnabás Pőcze March 3, 2025, 2:20 p.m. UTC
Both `CameraManager::Private::{add,remove}Camera()` emit the
`camera{Added,Removed}` signals, respectively, while holding the
lock protecting the list of cameras.

This is problematic because if a callback tries to call `cameras()`,
then the same (non-recursive) lock would be locked again.

Furthermore, there is no real need to hold the lock while user code
is running, so release the lock as soon as possible.

Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
---
 src/libcamera/camera_manager.cpp | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

Comments

Cheng-Hao Yang March 3, 2025, 2:31 p.m. UTC | #1
Hi Barnabás,

Thank you for the patch. It makes a lot of sense to me, only that I'm
not sure if `std::move`s are necessary. It probably saves new
instances of shared_ptr though.

Reviewed-by: Harvey Yang <chenghaoyang@chromium.org>

BR,
Harvey

On Mon, Mar 3, 2025 at 10:20 PM Barnabás Pőcze
<barnabas.pocze@ideasonboard.com> wrote:
>
> Both `CameraManager::Private::{add,remove}Camera()` emit the
> `camera{Added,Removed}` signals, respectively, while holding the
> lock protecting the list of cameras.
>
> This is problematic because if a callback tries to call `cameras()`,
> then the same (non-recursive) lock would be locked again.
>
> Furthermore, there is no real need to hold the lock while user code
> is running, so release the lock as soon as possible.
>
> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
> ---
>  src/libcamera/camera_manager.cpp | 16 +++++++++-------
>  1 file changed, 9 insertions(+), 7 deletions(-)
>
> diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp
> index 87e6717ec..d728ac44a 100644
> --- a/src/libcamera/camera_manager.cpp
> +++ b/src/libcamera/camera_manager.cpp
> @@ -202,6 +202,7 @@ void CameraManager::Private::addCamera(std::shared_ptr<Camera> camera)
>  {
>         ASSERT(Thread::current() == this);
>
> +{
>         MutexLocker locker(mutex_);
>
>         for (const std::shared_ptr<Camera> &c : cameras_) {
> @@ -213,13 +214,12 @@ void CameraManager::Private::addCamera(std::shared_ptr<Camera> camera)
>                 }
>         }
>
> -       cameras_.push_back(std::move(camera));
> -
> -       unsigned int index = cameras_.size() - 1;
> +       cameras_.push_back(camera);
> +}
>
>         /* Report the addition to the public signal */
>         CameraManager *const o = LIBCAMERA_O_PTR();
> -       o->cameraAdded.emit(cameras_[index]);
> +       o->cameraAdded.emit(std::move(camera));
>  }
>
>  /**
> @@ -236,6 +236,7 @@ void CameraManager::Private::removeCamera(std::shared_ptr<Camera> camera)
>  {
>         ASSERT(Thread::current() == this);
>
> +{
>         MutexLocker locker(mutex_);
>
>         auto iter = std::find_if(cameras_.begin(), cameras_.end(),
> @@ -245,14 +246,15 @@ void CameraManager::Private::removeCamera(std::shared_ptr<Camera> camera)
>         if (iter == cameras_.end())
>                 return;
>
> +       cameras_.erase(iter);
> +}
> +
>         LOG(Camera, Debug)
>                 << "Unregistering camera '" << camera->id() << "'";
>
> -       cameras_.erase(iter);
> -
>         /* Report the removal to the public signal */
>         CameraManager *const o = LIBCAMERA_O_PTR();
> -       o->cameraRemoved.emit(camera);
> +       o->cameraRemoved.emit(std::move(camera));
>  }
>
>  /**
> --
> 2.48.1
>
Kieran Bingham March 4, 2025, 10:17 a.m. UTC | #2
Quoting Barnabás Pőcze (2025-03-03 14:20:10)
> Both `CameraManager::Private::{add,remove}Camera()` emit the
> `camera{Added,Removed}` signals, respectively, while holding the
> lock protecting the list of cameras.
> 
> This is problematic because if a callback tries to call `cameras()`,
> then the same (non-recursive) lock would be locked again.
> 
> Furthermore, there is no real need to hold the lock while user code
> is running, so release the lock as soon as possible.
> 

This all sounds quite reasonable to me, so only a stylistic comment
below...

> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
> ---
>  src/libcamera/camera_manager.cpp | 16 +++++++++-------
>  1 file changed, 9 insertions(+), 7 deletions(-)
> 
> diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp
> index 87e6717ec..d728ac44a 100644
> --- a/src/libcamera/camera_manager.cpp
> +++ b/src/libcamera/camera_manager.cpp
> @@ -202,6 +202,7 @@ void CameraManager::Private::addCamera(std::shared_ptr<Camera> camera)
>  {
>         ASSERT(Thread::current() == this);
>  
> +{

Having the inner scope at the leftmost layer is my only niggle here. I
think I would suggest that the MutexLocker scope should be indented

>         MutexLocker locker(mutex_);
>  
>         for (const std::shared_ptr<Camera> &c : cameras_) {
> @@ -213,13 +214,12 @@ void CameraManager::Private::addCamera(std::shared_ptr<Camera> camera)
>                 }
>         }
>  
> -       cameras_.push_back(std::move(camera));
> -
> -       unsigned int index = cameras_.size() - 1;
> +       cameras_.push_back(camera);
> +}
>  
>         /* Report the addition to the public signal */
>         CameraManager *const o = LIBCAMERA_O_PTR();
> -       o->cameraAdded.emit(cameras_[index]);
> +       o->cameraAdded.emit(std::move(camera));

It feels a bit weird that we move the camera to the signal... It's a
shared pointer, so I guess it doesn't matter ... it's not actually
transferring ownership to the transient signal emitter ?

I guess this is because it's not used locally after this so it avoids a
copy of the shared_ptr? 

>  }
>  
>  /**
> @@ -236,6 +236,7 @@ void CameraManager::Private::removeCamera(std::shared_ptr<Camera> camera)
>  {
>         ASSERT(Thread::current() == this);
>  
> +{
>         MutexLocker locker(mutex_);
>  
>         auto iter = std::find_if(cameras_.begin(), cameras_.end(),
> @@ -245,14 +246,15 @@ void CameraManager::Private::removeCamera(std::shared_ptr<Camera> camera)
>         if (iter == cameras_.end())
>                 return;
>  
> +       cameras_.erase(iter);
> +}
> +

Same thoughts on the indent and std::move usage below too.

>         LOG(Camera, Debug)
>                 << "Unregistering camera '" << camera->id() << "'";
>  
> -       cameras_.erase(iter);
> -
>         /* Report the removal to the public signal */
>         CameraManager *const o = LIBCAMERA_O_PTR();
> -       o->cameraRemoved.emit(camera);
> +       o->cameraRemoved.emit(std::move(camera));
>  }
>  
>  /**
> -- 
> 2.48.1
>
Barnabás Pőcze March 4, 2025, 1:34 p.m. UTC | #3
Hi


2025. 03. 03. 15:31 keltezéssel, Cheng-Hao Yang írta:
> Hi Barnabás,
> 
> Thank you for the patch. It makes a lot of sense to me, only that I'm
> not sure if `std::move`s are necessary. It probably saves new
> instances of shared_ptr though.

They are not necessary, but I thought I'll make that change
if I am already here.


Regards,
Barnabás Pőcze

> 
> Reviewed-by: Harvey Yang <chenghaoyang@chromium.org>
> 
> BR,
> Harvey
> 
> On Mon, Mar 3, 2025 at 10:20 PM Barnabás Pőcze
> <barnabas.pocze@ideasonboard.com> wrote:
>>
>> Both `CameraManager::Private::{add,remove}Camera()` emit the
>> `camera{Added,Removed}` signals, respectively, while holding the
>> lock protecting the list of cameras.
>>
>> This is problematic because if a callback tries to call `cameras()`,
>> then the same (non-recursive) lock would be locked again.
>>
>> Furthermore, there is no real need to hold the lock while user code
>> is running, so release the lock as soon as possible.
>>
>> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
>> ---
>>   src/libcamera/camera_manager.cpp | 16 +++++++++-------
>>   1 file changed, 9 insertions(+), 7 deletions(-)
>>
>> diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp
>> index 87e6717ec..d728ac44a 100644
>> --- a/src/libcamera/camera_manager.cpp
>> +++ b/src/libcamera/camera_manager.cpp
>> @@ -202,6 +202,7 @@ void CameraManager::Private::addCamera(std::shared_ptr<Camera> camera)
>>   {
>>          ASSERT(Thread::current() == this);
>>
>> +{
>>          MutexLocker locker(mutex_);
>>
>>          for (const std::shared_ptr<Camera> &c : cameras_) {
>> @@ -213,13 +214,12 @@ void CameraManager::Private::addCamera(std::shared_ptr<Camera> camera)
>>                  }
>>          }
>>
>> -       cameras_.push_back(std::move(camera));
>> -
>> -       unsigned int index = cameras_.size() - 1;
>> +       cameras_.push_back(camera);
>> +}
>>
>>          /* Report the addition to the public signal */
>>          CameraManager *const o = LIBCAMERA_O_PTR();
>> -       o->cameraAdded.emit(cameras_[index]);
>> +       o->cameraAdded.emit(std::move(camera));
>>   }
>>
>>   /**
>> @@ -236,6 +236,7 @@ void CameraManager::Private::removeCamera(std::shared_ptr<Camera> camera)
>>   {
>>          ASSERT(Thread::current() == this);
>>
>> +{
>>          MutexLocker locker(mutex_);
>>
>>          auto iter = std::find_if(cameras_.begin(), cameras_.end(),
>> @@ -245,14 +246,15 @@ void CameraManager::Private::removeCamera(std::shared_ptr<Camera> camera)
>>          if (iter == cameras_.end())
>>                  return;
>>
>> +       cameras_.erase(iter);
>> +}
>> +
>>          LOG(Camera, Debug)
>>                  << "Unregistering camera '" << camera->id() << "'";
>>
>> -       cameras_.erase(iter);
>> -
>>          /* Report the removal to the public signal */
>>          CameraManager *const o = LIBCAMERA_O_PTR();
>> -       o->cameraRemoved.emit(camera);
>> +       o->cameraRemoved.emit(std::move(camera));
>>   }
>>
>>   /**
>> --
>> 2.48.1
>>
Barnabás Pőcze March 4, 2025, 1:34 p.m. UTC | #4
Hi


2025. 03. 04. 11:17 keltezéssel, Kieran Bingham írta:
> Quoting Barnabás Pőcze (2025-03-03 14:20:10)
>> Both `CameraManager::Private::{add,remove}Camera()` emit the
>> `camera{Added,Removed}` signals, respectively, while holding the
>> lock protecting the list of cameras.
>>
>> This is problematic because if a callback tries to call `cameras()`,
>> then the same (non-recursive) lock would be locked again.
>>
>> Furthermore, there is no real need to hold the lock while user code
>> is running, so release the lock as soon as possible.
>>
> 
> This all sounds quite reasonable to me, so only a stylistic comment
> below...
> 
>> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
>> ---
>>   src/libcamera/camera_manager.cpp | 16 +++++++++-------
>>   1 file changed, 9 insertions(+), 7 deletions(-)
>>
>> diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp
>> index 87e6717ec..d728ac44a 100644
>> --- a/src/libcamera/camera_manager.cpp
>> +++ b/src/libcamera/camera_manager.cpp
>> @@ -202,6 +202,7 @@ void CameraManager::Private::addCamera(std::shared_ptr<Camera> camera)
>>   {
>>          ASSERT(Thread::current() == this);
>>   
>> +{
> 
> Having the inner scope at the leftmost layer is my only niggle here. I
> think I would suggest that the MutexLocker scope should be indented

Okay, I'll indent it; my thinking was to avoid the excessive diff that
it causes; but I agree it does look odd.


> 
>>          MutexLocker locker(mutex_);
>>   
>>          for (const std::shared_ptr<Camera> &c : cameras_) {
>> @@ -213,13 +214,12 @@ void CameraManager::Private::addCamera(std::shared_ptr<Camera> camera)
>>                  }
>>          }
>>   
>> -       cameras_.push_back(std::move(camera));
>> -
>> -       unsigned int index = cameras_.size() - 1;
>> +       cameras_.push_back(camera);
>> +}
>>   
>>          /* Report the addition to the public signal */
>>          CameraManager *const o = LIBCAMERA_O_PTR();
>> -       o->cameraAdded.emit(cameras_[index]);
>> +       o->cameraAdded.emit(std::move(camera));
> 
> It feels a bit weird that we move the camera to the signal... It's a
> shared pointer, so I guess it doesn't matter ... it's not actually
> transferring ownership to the transient signal emitter ?
> 
> I guess this is because it's not used locally after this so it avoids a
> copy of the shared_ptr?

Yes, the `CameraManager` already has a reference to it in `cameras_`, so
the local reference in `camera` can be transferred.


Regards,
Barnabás Pőcze


> 
>>   }
>>   
>>   /**
>> @@ -236,6 +236,7 @@ void CameraManager::Private::removeCamera(std::shared_ptr<Camera> camera)
>>   {
>>          ASSERT(Thread::current() == this);
>>   
>> +{
>>          MutexLocker locker(mutex_);
>>   
>>          auto iter = std::find_if(cameras_.begin(), cameras_.end(),
>> @@ -245,14 +246,15 @@ void CameraManager::Private::removeCamera(std::shared_ptr<Camera> camera)
>>          if (iter == cameras_.end())
>>                  return;
>>   
>> +       cameras_.erase(iter);
>> +}
>> +
> 
> Same thoughts on the indent and std::move usage below too.
> 
>>          LOG(Camera, Debug)
>>                  << "Unregistering camera '" << camera->id() << "'";
>>   
>> -       cameras_.erase(iter);
>> -
>>          /* Report the removal to the public signal */
>>          CameraManager *const o = LIBCAMERA_O_PTR();
>> -       o->cameraRemoved.emit(camera);
>> +       o->cameraRemoved.emit(std::move(camera));
>>   }
>>   
>>   /**
>> -- 
>> 2.48.1
>>
Laurent Pinchart March 5, 2025, 4:52 a.m. UTC | #5
On Tue, Mar 04, 2025 at 02:34:51PM +0100, Barnabás Pőcze wrote:
> 2025. 03. 04. 11:17 keltezéssel, Kieran Bingham írta:
> > Quoting Barnabás Pőcze (2025-03-03 14:20:10)
> >> Both `CameraManager::Private::{add,remove}Camera()` emit the
> >> `camera{Added,Removed}` signals, respectively, while holding the
> >> lock protecting the list of cameras.
> >>
> >> This is problematic because if a callback tries to call `cameras()`,
> >> then the same (non-recursive) lock would be locked again.
> >>
> >> Furthermore, there is no real need to hold the lock while user code
> >> is running, so release the lock as soon as possible.
> > 
> > This all sounds quite reasonable to me, so only a stylistic comment
> > below...
> > 
> >> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>
> >> ---
> >>   src/libcamera/camera_manager.cpp | 16 +++++++++-------
> >>   1 file changed, 9 insertions(+), 7 deletions(-)
> >>
> >> diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp
> >> index 87e6717ec..d728ac44a 100644
> >> --- a/src/libcamera/camera_manager.cpp
> >> +++ b/src/libcamera/camera_manager.cpp
> >> @@ -202,6 +202,7 @@ void CameraManager::Private::addCamera(std::shared_ptr<Camera> camera)
> >>   {
> >>          ASSERT(Thread::current() == this);
> >>   
> >> +{
> > 
> > Having the inner scope at the leftmost layer is my only niggle here. I
> > think I would suggest that the MutexLocker scope should be indented
> 
> Okay, I'll indent it; my thinking was to avoid the excessive diff that
> it causes; but I agree it does look odd.

With that fixed,

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

> >>          MutexLocker locker(mutex_);
> >>   
> >>          for (const std::shared_ptr<Camera> &c : cameras_) {
> >> @@ -213,13 +214,12 @@ void CameraManager::Private::addCamera(std::shared_ptr<Camera> camera)
> >>                  }
> >>          }
> >>   
> >> -       cameras_.push_back(std::move(camera));
> >> -
> >> -       unsigned int index = cameras_.size() - 1;
> >> +       cameras_.push_back(camera);
> >> +}
> >>   
> >>          /* Report the addition to the public signal */
> >>          CameraManager *const o = LIBCAMERA_O_PTR();
> >> -       o->cameraAdded.emit(cameras_[index]);
> >> +       o->cameraAdded.emit(std::move(camera));
> > 
> > It feels a bit weird that we move the camera to the signal... It's a
> > shared pointer, so I guess it doesn't matter ... it's not actually
> > transferring ownership to the transient signal emitter ?
> > 
> > I guess this is because it's not used locally after this so it avoids a
> > copy of the shared_ptr?
> 
> Yes, the `CameraManager` already has a reference to it in `cameras_`, so
> the local reference in `camera` can be transferred.
> 
> >>   }
> >>   
> >>   /**
> >> @@ -236,6 +236,7 @@ void CameraManager::Private::removeCamera(std::shared_ptr<Camera> camera)
> >>   {
> >>          ASSERT(Thread::current() == this);
> >>   
> >> +{
> >>          MutexLocker locker(mutex_);
> >>   
> >>          auto iter = std::find_if(cameras_.begin(), cameras_.end(),
> >> @@ -245,14 +246,15 @@ void CameraManager::Private::removeCamera(std::shared_ptr<Camera> camera)
> >>          if (iter == cameras_.end())
> >>                  return;
> >>   
> >> +       cameras_.erase(iter);
> >> +}
> >> +
> > 
> > Same thoughts on the indent and std::move usage below too.
> > 
> >>          LOG(Camera, Debug)
> >>                  << "Unregistering camera '" << camera->id() << "'";
> >>   
> >> -       cameras_.erase(iter);
> >> -
> >>          /* Report the removal to the public signal */
> >>          CameraManager *const o = LIBCAMERA_O_PTR();
> >> -       o->cameraRemoved.emit(camera);
> >> +       o->cameraRemoved.emit(std::move(camera));
> >>   }
> >>   
> >>   /**

Patch
diff mbox series

diff --git a/src/libcamera/camera_manager.cpp b/src/libcamera/camera_manager.cpp
index 87e6717ec..d728ac44a 100644
--- a/src/libcamera/camera_manager.cpp
+++ b/src/libcamera/camera_manager.cpp
@@ -202,6 +202,7 @@  void CameraManager::Private::addCamera(std::shared_ptr<Camera> camera)
 {
 	ASSERT(Thread::current() == this);
 
+{
 	MutexLocker locker(mutex_);
 
 	for (const std::shared_ptr<Camera> &c : cameras_) {
@@ -213,13 +214,12 @@  void CameraManager::Private::addCamera(std::shared_ptr<Camera> camera)
 		}
 	}
 
-	cameras_.push_back(std::move(camera));
-
-	unsigned int index = cameras_.size() - 1;
+	cameras_.push_back(camera);
+}
 
 	/* Report the addition to the public signal */
 	CameraManager *const o = LIBCAMERA_O_PTR();
-	o->cameraAdded.emit(cameras_[index]);
+	o->cameraAdded.emit(std::move(camera));
 }
 
 /**
@@ -236,6 +236,7 @@  void CameraManager::Private::removeCamera(std::shared_ptr<Camera> camera)
 {
 	ASSERT(Thread::current() == this);
 
+{
 	MutexLocker locker(mutex_);
 
 	auto iter = std::find_if(cameras_.begin(), cameras_.end(),
@@ -245,14 +246,15 @@  void CameraManager::Private::removeCamera(std::shared_ptr<Camera> camera)
 	if (iter == cameras_.end())
 		return;
 
+	cameras_.erase(iter);
+}
+
 	LOG(Camera, Debug)
 		<< "Unregistering camera '" << camera->id() << "'";
 
-	cameras_.erase(iter);
-
 	/* Report the removal to the public signal */
 	CameraManager *const o = LIBCAMERA_O_PTR();
-	o->cameraRemoved.emit(camera);
+	o->cameraRemoved.emit(std::move(camera));
 }
 
 /**