[PATCH 12/12] libcamera: object: Add and use thread-bound assertion

Milan Zamazal mzamazal at redhat.com
Mon Jan 22 21:55:35 CET 2024


Laurent Pinchart <laurent.pinchart at ideasonboard.com> writes:

> Several functions in libcamera classes are marked as thread-bound,
> restricting the contexts in which those functions can be called. There
> is no infrastructure to enfore these restrictions, causing difficult to
                          ^^^^^^
enforce

> debug race conditions when they are not met by callers.
>
> As a first step to solve this, add an assertThreadBound() protected
> function to the Object class to test if the calling thread context is
> valid, and use it in member functions of Object subclasses marked as
> thread-bound. This replaces manual tests in a few locations.
>
> The thread-bound member functions of classes that do not inherit from
> Object are not checked, and neither are the functions of classes marked
> as thread-bound at the class level. These issue should be addressed in
> the future.
>
> Signed-off-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> ---
>  include/libcamera/base/object.h       |  2 ++
>  src/libcamera/base/event_notifier.cpp |  6 +++++
>  src/libcamera/base/object.cpp         | 32 ++++++++++++++++++++++++++-
>  src/libcamera/base/timer.cpp          | 10 +++------
>  4 files changed, 42 insertions(+), 8 deletions(-)
>
> diff --git a/include/libcamera/base/object.h b/include/libcamera/base/object.h
> index 933336361155..cb7e0a132be2 100644
> --- a/include/libcamera/base/object.h
> +++ b/include/libcamera/base/object.h
> @@ -49,6 +49,8 @@ public:
>  protected:
>  	virtual void message(Message *msg);
>  
> +	bool assertThreadBound(const char *message);
> +
>  private:
>  	friend class SignalBase;
>  	friend class Thread;
> diff --git a/src/libcamera/base/event_notifier.cpp b/src/libcamera/base/event_notifier.cpp
> index fd93c0878c6f..a519aec38efb 100644
> --- a/src/libcamera/base/event_notifier.cpp
> +++ b/src/libcamera/base/event_notifier.cpp
> @@ -8,6 +8,7 @@
>  #include <libcamera/base/event_notifier.h>
>  
>  #include <libcamera/base/event_dispatcher.h>
> +#include <libcamera/base/log.h>
>  #include <libcamera/base/message.h>
>  #include <libcamera/base/thread.h>
>  
> @@ -20,6 +21,8 @@
>  
>  namespace libcamera {
>  
> +LOG_DECLARE_CATEGORY(Event)
> +
>  /**
>   * \class EventNotifier
>   * \brief Notify of activity on a file descriptor
> @@ -104,6 +107,9 @@ EventNotifier::~EventNotifier()
>   */
>  void EventNotifier::setEnabled(bool enable)
>  {
> +	if (!assertThreadBound("EventNotifier can't be enabled from another thread"))
> +		return;
> +

Is it OK to continue with libcamera execution when the requested action is not
fulfilled here?  Can a wrong state of EventNotifier lead to serious trouble and
damages worse than exiting the program?

The same question for the other checks below.

> 
>  	if (enabled_ == enable) return;
>  
> diff --git a/src/libcamera/base/object.cpp b/src/libcamera/base/object.cpp
> index 8af0337f5448..d98a9157773e 100644
> --- a/src/libcamera/base/object.cpp
> +++ b/src/libcamera/base/object.cpp
> @@ -224,6 +224,35 @@ void Object::message(Message *msg)
>  	}
>  }
>  
> +/**
> + * \fn Object::assertThreadBound()
> + * \brief Check if the caller complies with thread-bound constraints
> + * \param[in] message The message to be printed on error
> + *
> + * This function verifies the calling constraints required by the \threadbound
> + * definition. It shall be called at the beginning of member functions of an
> + * Object subclass that are explicitly marked as thread-bound in their
> + * documentation.
> + *
> + * If the thread-bound constraints are not met, the function prints \a message
> + * as an error message. For debug builds, it additionally causes an assertion
> + * error.
> + *
> + * \todo Verify the thread-bound requirements for functions marked as
> + * thread-bound at the class level.
> + *
> + * \return True if the call is thread-bound compliant, false otherwise
> + */
> +bool Object::assertThreadBound(const char *message)
> +{
> +	if (Thread::current() == thread_)
> +		return true;
> +
> +	LOG(Object, Error) << message;
> +	ASSERT(false);
> +	return false;

Or maybe simply exiting here rather than leaving the decision on the callers?

(There can be different answers for "in this patch for now" and "in future once
more tested".)

> +}
> +
>  /**
>   * \fn R Object::invokeMethod()
>   * \brief Invoke a method asynchronously on an Object instance
> @@ -275,7 +304,8 @@ void Object::message(Message *msg)
>   */
>  void Object::moveToThread(Thread *thread)
>  {
> -	ASSERT(Thread::current() == thread_);
> +	if (!assertThreadBound("Object can't be moved from another thread"))
> +		return;
>  
>  	if (thread_ == thread)
>  		return;
> diff --git a/src/libcamera/base/timer.cpp b/src/libcamera/base/timer.cpp
> index 74b060af32ff..24dbf1e892c3 100644
> --- a/src/libcamera/base/timer.cpp
> +++ b/src/libcamera/base/timer.cpp
> @@ -85,10 +85,8 @@ void Timer::start(std::chrono::milliseconds duration)
>   */
>  void Timer::start(std::chrono::steady_clock::time_point deadline)
>  {
> -	if (Thread::current() != thread()) {
> -		LOG(Timer, Error) << "Timer " << this << " << can't be started from another thread";
> +	if (!assertThreadBound("Timer can't be started from another thread"))
>  		return;
> -	}
>  
>  	deadline_ = deadline;
>  
> @@ -114,13 +112,11 @@ void Timer::start(std::chrono::steady_clock::time_point deadline)
>   */
>  void Timer::stop()
>  {
> -	if (!isRunning())
> +	if (!assertThreadBound("Timer can't be stopped from another thread"))
>  		return;
>  
> -	if (Thread::current() != thread()) {
> -		LOG(Timer, Error) << "Timer " << this << " can't be stopped from another thread";
> +	if (!isRunning())
>  		return;
> -	}
>  
>  	unregisterTimer();
>  }



More information about the libcamera-devel mailing list