[libcamera-devel] [PATCH 09/13] gstreamer: Use dedicated lock for request queues

Nicolas Dufresne nicolas.dufresne at collabora.com
Tue Jun 28 15:53:56 CEST 2022


Le mardi 28 juin 2022 à 16:45 +0300, Laurent Pinchart a écrit :
> Hi Nicolas,
> 
> On Tue, Jun 28, 2022 at 09:05:52AM -0400, Nicolas Dufresne wrote:
> > Le vendredi 24 juin 2022 à 02:22 +0300, Laurent Pinchart a écrit :
> > > Add a new lock to the GstLibcameraSrcState class to protect the queued
> > > and completed requests queues. This replaces the GstObject lock, and
> > > minimizes the lock contention between the request completion handler and
> > > the task run handler as the former must run as fast as possible.
> > > 
> > > Signed-off-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> > > ---
> > >  src/gstreamer/gstlibcamerasrc.cpp | 39 ++++++++++++++++++++++---------
> > >  1 file changed, 28 insertions(+), 11 deletions(-)
> > > 
> > > diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp
> > > index e30d45fa2223..b85ba39fb808 100644
> > > --- a/src/gstreamer/gstlibcamerasrc.cpp
> > > +++ b/src/gstreamer/gstlibcamerasrc.cpp
> > > @@ -32,6 +32,8 @@
> > >  #include <queue>
> > >  #include <vector>
> > >  
> > > +#include <libcamera/base/mutex.h>
> > > +
> > >  #include <libcamera/camera.h>
> > >  #include <libcamera/camera_manager.h>
> > >  #include <libcamera/control_ids.h>
> > > @@ -111,8 +113,13 @@ struct GstLibcameraSrcState {
> > >  	std::shared_ptr<Camera> cam_;
> > >  	std::unique_ptr<CameraConfiguration> config_;
> > >  	std::vector<GstPad *> srcpads_;
> > > -	std::queue<std::unique_ptr<RequestWrap>> queuedRequests_;
> > > -	std::queue<std::unique_ptr<RequestWrap>> completedRequests_;
> > > +
> > > +	Mutex lock_;
> > 
> > I would love to see a comment explaining what this lock is for so that future
> > contributors knows when to take it, and when not.
> > 
> > Did you know that GLib Mutex are a lot faster then pthread_mutex ? Might sounds
> > surprising, but they don't obey all of the POSIX requirement, they remains a
> > perfect fit for streaming. I personally would have used these over a wrapper on
> > top of pthread, the change is fine though, I just thought of mentioning as you
> > already made pretty micro optimization removing few function call earlier in the
> > patchset. IIRC this is in the 20x level of improvement, at least on Linux.
> > 
> > > +	std::queue<std::unique_ptr<RequestWrap>> queuedRequests_
> > > +		LIBCAMERA_TSA_GUARDED_BY(lock_);
> > > +	std::queue<std::unique_ptr<RequestWrap>> completedRequests_
> > > +		LIBCAMERA_TSA_GUARDED_BY(lock_);
> > 
> > Of course, that would require extra effort to implement this "guarded by" thing.
> > I'm just mentioning, not action needed. There is also GstAtomicQueue if (and
> > only if) contention isn't expected.
> 
> The thread-safefy annotation is the reason why I picked the Mutex class,
> as it addresses your comment about documenting the data protected by the
> lock. It's also nice to get the compiler to validate the locking
> patterns. It's not strictly mandatory though.
> 
> I wasn't aware of the differences in performance between the pthread
> mutex implementation and the GLib implementation, and I don't know how
> much difference it would make in practice here. Would you rather replace
> Mutex + TSA with GLibMutex + a comment ? Or should this be done on top
> when/if needed ?

Only if needed. It does matter for lower latency audio, but for video which is
pretty low rate it will rarely make a big difference. This comment was just to
keep you inform that GMutex is not a pthread_mutex_t (and not POSIX compliant,
which is the reason pthread under perform). It might matter when we start
handling frame burst and highly concurrent streaming (e.g. 100 or more streams).

> 
> > > +
> > >  	guint group_id_;
> > >  
> > >  	void requestCompleted(Request *request);
> > > @@ -155,12 +162,15 @@ GstStaticPadTemplate request_src_template = {
> > >  void
> > >  GstLibcameraSrcState::requestCompleted(Request *request)
> > >  {
> > > -	GLibLocker lock(GST_OBJECT(src_));
> > > -
> > >  	GST_DEBUG_OBJECT(src_, "buffers are ready");
> > >  
> > > -	std::unique_ptr<RequestWrap> wrap = std::move(queuedRequests_.front());
> > > -	queuedRequests_.pop();
> > > +	std::unique_ptr<RequestWrap> wrap;
> > > +
> > > +	{
> > > +		MutexLocker locker(lock_);
> > > +		wrap = std::move(queuedRequests_.front());
> > > +		queuedRequests_.pop();
> > > +	}
> > >  
> > >  	g_return_if_fail(wrap->request_.get() == request);
> > >  
> > > @@ -183,7 +193,10 @@ GstLibcameraSrcState::requestCompleted(Request *request)
> > >  		wrap->latency_ = sys_now - timestamp;
> > >  	}
> > >  
> > > -	completedRequests_.push(std::move(wrap));
> > > +	{
> > > +		MutexLocker locker(lock_);
> > > +		completedRequests_.push(std::move(wrap));
> > > +	}
> > >  
> > >  	gst_task_resume(src_->task);
> > >  }
> > > @@ -289,16 +302,17 @@ gst_libcamera_src_task_run(gpointer user_data)
> > >  	}
> > >  
> > >  	if (wrap) {
> > > -		GLibLocker lock(GST_OBJECT(self));
> > >  		GST_TRACE_OBJECT(self, "Requesting buffers");
> > >  		state->cam_->queueRequest(wrap->request_.get());
> > > +
> > > +		MutexLocker locker(state->lock_);
> > >  		state->queuedRequests_.push(std::move(wrap));
> > >  
> > >  		/* The RequestWrap will be deleted in the completion handler. */
> > >  	}
> > >  
> > >  	{
> > > -		GLibLocker lock(GST_OBJECT(self));
> > > +		MutexLocker locker(state->lock_);
> > >  
> > >  		if (!state->completedRequests_.empty()) {
> > >  			wrap = std::move(state->completedRequests_.front());
> > > @@ -358,7 +372,7 @@ gst_libcamera_src_task_run(gpointer user_data)
> > >  		bool do_pause;
> > >  
> > >  		{
> > > -			GLibLocker lock(GST_OBJECT(self));
> > > +			MutexLocker locker(state->lock_);
> > >  			do_pause = state->completedRequests_.empty();
> > >  		}
> > >  
> > > @@ -513,7 +527,10 @@ gst_libcamera_src_task_leave([[maybe_unused]] GstTask *task,
> > >  
> > >  	state->cam_->stop();
> > >  
> > > -	state->completedRequests_ = {};
> > > +	{
> > > +		MutexLocker locker(state->lock_);
> > > +		state->completedRequests_ = {};
> > > +	}
> > >  
> > >  	for (GstPad *srcpad : state->srcpads_)
> > >  		gst_libcamera_pad_set_pool(srcpad, nullptr);
> > 
> > Does not solve concurrency between pads iteration and request/release pads, but
> > this wasn't the goal and was already broken.
> 
> Hopefully the next patches do :-)
> 
> > Reviewed-by: Nicolas Dufresne <nicolas.dufresne at collabora.com>
> 



More information about the libcamera-devel mailing list