libcamera: object: Add and use thread-bound assertion

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 enforce these restrictions, causing difficult to
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@ideasonboard.com>
Reviewed-by: Milan Zamazal <mzamazal@redhat.com>
This commit is contained in:
Laurent Pinchart 2024-01-18 23:26:44 +02:00
parent 56f817892c
commit 6f1bd9cf55
4 changed files with 42 additions and 8 deletions

View file

@ -49,6 +49,8 @@ public:
protected: protected:
virtual void message(Message *msg); virtual void message(Message *msg);
bool assertThreadBound(const char *message);
private: private:
friend class SignalBase; friend class SignalBase;
friend class Thread; friend class Thread;

View file

@ -8,6 +8,7 @@
#include <libcamera/base/event_notifier.h> #include <libcamera/base/event_notifier.h>
#include <libcamera/base/event_dispatcher.h> #include <libcamera/base/event_dispatcher.h>
#include <libcamera/base/log.h>
#include <libcamera/base/message.h> #include <libcamera/base/message.h>
#include <libcamera/base/thread.h> #include <libcamera/base/thread.h>
@ -20,6 +21,8 @@
namespace libcamera { namespace libcamera {
LOG_DECLARE_CATEGORY(Event)
/** /**
* \class EventNotifier * \class EventNotifier
* \brief Notify of activity on a file descriptor * \brief Notify of activity on a file descriptor
@ -104,6 +107,9 @@ EventNotifier::~EventNotifier()
*/ */
void EventNotifier::setEnabled(bool enable) void EventNotifier::setEnabled(bool enable)
{ {
if (!assertThreadBound("EventNotifier can't be enabled from another thread"))
return;
if (enabled_ == enable) if (enabled_ == enable)
return; return;

View file

@ -225,6 +225,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;
}
/** /**
* \fn R Object::invokeMethod() * \fn R Object::invokeMethod()
* \brief Invoke a method asynchronously on an Object instance * \brief Invoke a method asynchronously on an Object instance
@ -276,7 +305,8 @@ void Object::message(Message *msg)
*/ */
void Object::moveToThread(Thread *thread) void Object::moveToThread(Thread *thread)
{ {
ASSERT(Thread::current() == thread_); if (!assertThreadBound("Object can't be moved from another thread"))
return;
if (thread_ == thread) if (thread_ == thread)
return; return;

View file

@ -85,10 +85,8 @@ void Timer::start(std::chrono::milliseconds duration)
*/ */
void Timer::start(std::chrono::steady_clock::time_point deadline) void Timer::start(std::chrono::steady_clock::time_point deadline)
{ {
if (Thread::current() != thread()) { if (!assertThreadBound("Timer can't be started from another thread"))
LOG(Timer, Error) << "Timer " << this << " << can't be started from another thread";
return; return;
}
deadline_ = deadline; deadline_ = deadline;
@ -114,13 +112,11 @@ void Timer::start(std::chrono::steady_clock::time_point deadline)
*/ */
void Timer::stop() void Timer::stop()
{ {
if (!isRunning()) if (!assertThreadBound("Timer can't be stopped from another thread"))
return; return;
if (Thread::current() != thread()) { if (!isRunning())
LOG(Timer, Error) << "Timer " << this << " can't be stopped from another thread";
return; return;
}
unregisterTimer(); unregisterTimer();
} }