[libcamera-devel,v2,15/18] libcamera: object: Create parent-child relationships

Message ID 20190817152104.10834-16-laurent.pinchart@ideasonboard.com
State Accepted
Headers show
Series
  • Object & Thread enhancements
Related show

Commit Message

Laurent Pinchart Aug. 17, 2019, 3:21 p.m. UTC
Add a parent Object to Object instances, and track the parent-children
relationships. Children are bound to the same thread as their parent,
and moving an Object to a thread automatically moves all its children.

Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
---
Changes since v1:

- Document Object::~Object()
---
 include/libcamera/object.h     |  8 +++-
 src/libcamera/include/thread.h |  2 +
 src/libcamera/object.cpp       | 72 ++++++++++++++++++++++++++++------
 src/libcamera/thread.cpp       | 12 +++++-
 4 files changed, 81 insertions(+), 13 deletions(-)

Patch

diff --git a/include/libcamera/object.h b/include/libcamera/object.h
index e128c75e7db8..3308330af857 100644
--- a/include/libcamera/object.h
+++ b/include/libcamera/object.h
@@ -9,6 +9,7 @@ 
 
 #include <list>
 #include <memory>
+#include <vector>
 
 #include <libcamera/bound_method.h>
 
@@ -23,7 +24,7 @@  class Thread;
 class Object
 {
 public:
-	Object();
+	Object(Object *parent = nullptr);
 	virtual ~Object();
 
 	void postMessage(std::unique_ptr<Message> msg);
@@ -41,6 +42,8 @@  public:
 	Thread *thread() const { return thread_; }
 	void moveToThread(Thread *thread);
 
+	Object *parent() const { return parent_; }
+
 protected:
 	virtual void message(Message *msg);
 
@@ -57,6 +60,9 @@  private:
 	void connect(SignalBase *signal);
 	void disconnect(SignalBase *signal);
 
+	Object *parent_;
+	std::vector<Object *> children_;
+
 	Thread *thread_;
 	std::list<SignalBase *> signals_;
 	unsigned int pendingMessages_;
diff --git a/src/libcamera/include/thread.h b/src/libcamera/include/thread.h
index 630abb49534f..37edd4f5138b 100644
--- a/src/libcamera/include/thread.h
+++ b/src/libcamera/include/thread.h
@@ -61,6 +61,8 @@  private:
 	friend class ThreadMain;
 
 	void moveObject(Object *object);
+	void moveObject(Object *object, ThreadData *currentData,
+			ThreadData *targetData);
 
 	std::thread thread_;
 	ThreadData *data_;
diff --git a/src/libcamera/object.cpp b/src/libcamera/object.cpp
index bbb28f261405..98aa0af2f9b9 100644
--- a/src/libcamera/object.cpp
+++ b/src/libcamera/object.cpp
@@ -7,6 +7,8 @@ 
 
 #include <libcamera/object.h>
 
+#include <algorithm>
+
 #include <libcamera/signal.h>
 
 #include "log.h"
@@ -21,6 +23,8 @@ 
 
 namespace libcamera {
 
+LOG_DEFINE_CATEGORY(Object)
+
 /**
  * \class Object
  * \brief Base object to support automatic signal disconnection
@@ -29,10 +33,11 @@  namespace libcamera {
  * slots. By inheriting from Object, an object is automatically disconnected
  * from all connected signals when it gets destroyed.
  *
- * Object instances are bound to the thread in which they're created. When a
- * message is posted to an object, its handler will run in the object's thread.
- * This allows implementing easy message passing between threads by inheriting
- * from the Object class.
+ * Object instances are bound to the thread of their parent, or the thread in
+ * which they're created when they have no parent. When a message is posted to
+ * an object, its handler will run in the object's thread. This allows
+ * implementing easy message passing between threads by inheriting from the
+ * Object class.
  *
  * Object slots connected to signals will also run in the context of the
  * object's thread, regardless of whether the signal is emitted in the same or
@@ -41,12 +46,29 @@  namespace libcamera {
  * \sa Message, Signal, Thread
  */
 
-Object::Object()
-	: pendingMessages_(0)
+/**
+ * \brief Construct an Object instance
+ * \param[in] parent The object parent
+ *
+ * The new Object instance is bound to the thread of its \a parent, or to the
+ * current thread if the \a parent is nullptr.
+ */
+Object::Object(Object *parent)
+	: parent_(parent), pendingMessages_(0)
 {
-	thread_ = Thread::current();
+	thread_ = parent ? parent->thread() : Thread::current();
+
+	if (parent)
+		parent->children_.push_back(this);
 }
 
+/**
+ * \brief Destroy an Object instance
+ *
+ * Deleting an Object automatically disconnects all signals from the Object's
+ * slots. All the Object's children are made orphan, but stay bound to their
+ * current thread.
+ */
 Object::~Object()
 {
 	for (SignalBase *signal : signals_)
@@ -54,6 +76,16 @@  Object::~Object()
 
 	if (pendingMessages_)
 		thread()->removeMessages(this);
+
+	if (parent_) {
+		auto it = std::find(parent_->children_.begin(),
+				    parent_->children_.end(), this);
+		ASSERT(it != parent_->children_.end());
+		parent_->children_.erase(it);
+	}
+
+	for (auto child : children_)
+		child->parent_ = nullptr;
 }
 
 /**
@@ -129,16 +161,19 @@  void Object::invokeMethod(BoundMethodBase *method, void *args)
  */
 
 /**
- * \brief Move the object to a different thread
+ * \brief Move the object and all its children to a different thread
  * \param[in] thread The target thread
  *
- * This method moves the object from the current thread to the new \a thread.
- * It shall be called from the thread in which the object currently lives,
- * otherwise the behaviour is undefined.
+ * This method moves the object and all its children from the current thread to
+ * the new \a thread. It shall be called from the thread in which the object
+ * currently lives, otherwise the behaviour is undefined.
  *
  * Before the object is moved, a Message::ThreadMoveMessage message is sent to
  * it. The message() method can be reimplement in derived classes to be notified
  * of the upcoming thread move and perform any required processing.
+ *
+ * Moving an object that has a parent is not allowed, and causes undefined
+ * behaviour.
  */
 void Object::moveToThread(Thread *thread)
 {
@@ -147,6 +182,12 @@  void Object::moveToThread(Thread *thread)
 	if (thread_ == thread)
 		return;
 
+	if (parent_) {
+		LOG(Object, Error)
+			<< "Moving object to thread with a parent is not permitted";
+		return;
+	}
+
 	notifyThreadMove();
 
 	thread->moveObject(this);
@@ -156,8 +197,17 @@  void Object::notifyThreadMove()
 {
 	Message msg(Message::ThreadMoveMessage);
 	message(&msg);
+
+	for (auto child : children_)
+		child->notifyThreadMove();
 }
 
+/**
+ * \fn Object::parent()
+ * \brief Retrieve the object's parent
+ * \return The object's parent
+ */
+
 void Object::connect(SignalBase *signal)
 {
 	signals_.push_back(signal);
diff --git a/src/libcamera/thread.cpp b/src/libcamera/thread.cpp
index 24422f7b7bd0..872ad1bd9d69 100644
--- a/src/libcamera/thread.cpp
+++ b/src/libcamera/thread.cpp
@@ -448,7 +448,7 @@  void Thread::dispatchMessages()
 }
 
 /**
- * \brief Move an \a object to the thread
+ * \brief Move an \a object and all its children to the thread
  * \param[in] object The object
  */
 void Thread::moveObject(Object *object)
@@ -460,6 +460,12 @@  void Thread::moveObject(Object *object)
 	MutexLocker lockerTo(targetData->mutex_, std::defer_lock);
 	std::lock(lockerFrom, lockerTo);
 
+	moveObject(object, currentData, targetData);
+}
+
+void Thread::moveObject(Object *object, ThreadData *currentData,
+			ThreadData *targetData)
+{
 	/* Move pending messages to the message queue of the new thread. */
 	if (object->pendingMessages_) {
 		unsigned int movedMessages = 0;
@@ -483,6 +489,10 @@  void Thread::moveObject(Object *object)
 	}
 
 	object->thread_ = this;
+
+	/* Move all children. */
+	for (auto child : object->children_)
+		moveObject(child, currentData, targetData);
 }
 
 }; /* namespace libcamera */