[{"id":2412,"web_url":"https://patchwork.libcamera.org/comment/2412/","msgid":"<20190815095647.ogsdyfofpthv635h@uno.localdomain>","date":"2019-08-15T09:56:47","subject":"Re: [libcamera-devel] [PATCH 15/18] libcamera: object: Create\n\tparent-child relationships","submitter":{"id":3,"url":"https://patchwork.libcamera.org/api/people/3/","name":"Jacopo Mondi","email":"jacopo@jmondi.org"},"content":"Hi Laurent,\n\nOn Mon, Aug 12, 2019 at 03:46:39PM +0300, Laurent Pinchart wrote:\n> Add a parent Object to Object instances, and track the parent-children\n> relationships. Children are bound to the same thread as their parent,\n> and moving an Object to a thread automatically moves all its children.\n>\n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n>  include/libcamera/object.h     |  8 ++++-\n>  src/libcamera/include/thread.h |  2 ++\n>  src/libcamera/object.cpp       | 65 ++++++++++++++++++++++++++++------\n>  src/libcamera/thread.cpp       | 12 ++++++-\n>  4 files changed, 74 insertions(+), 13 deletions(-)\n>\n> diff --git a/include/libcamera/object.h b/include/libcamera/object.h\n> index 14b939a9bd3d..4a0a272b875a 100644\n> --- a/include/libcamera/object.h\n> +++ b/include/libcamera/object.h\n> @@ -9,6 +9,7 @@\n>\n>  #include <list>\n>  #include <memory>\n> +#include <vector>\n>\n>  #include <libcamera/bound_method.h>\n>\n> @@ -23,7 +24,7 @@ class Thread;\n>  class Object\n>  {\n>  public:\n> -\tObject();\n> +\tObject(Object *parent = nullptr);\n>  \tvirtual ~Object();\n>\n>  \tvoid postMessage(std::unique_ptr<Message> msg);\n> @@ -42,6 +43,8 @@ public:\n>  \tThread *thread() const { return thread_; }\n>  \tvoid moveToThread(Thread *thread);\n>\n> +\tObject *parent() const { return parent_; }\n\nmaybe const Object * ?\n\n> +\n>  protected:\n>  \tvirtual void message(Message *msg);\n>\n> @@ -58,6 +61,9 @@ private:\n>  \tvoid connect(SignalBase *signal);\n>  \tvoid disconnect(SignalBase *signal);\n>\n> +\tObject *parent_;\n> +\tstd::vector<Object *> children_;\n> +\n>  \tThread *thread_;\n>  \tstd::list<SignalBase *> signals_;\n>  \tunsigned int pendingMessages_;\n> diff --git a/src/libcamera/include/thread.h b/src/libcamera/include/thread.h\n> index 630abb49534f..37edd4f5138b 100644\n> --- a/src/libcamera/include/thread.h\n> +++ b/src/libcamera/include/thread.h\n> @@ -61,6 +61,8 @@ private:\n>  \tfriend class ThreadMain;\n>\n>  \tvoid moveObject(Object *object);\n> +\tvoid moveObject(Object *object, ThreadData *currentData,\n> +\t\t\tThreadData *targetData);\n>\n>  \tstd::thread thread_;\n>  \tThreadData *data_;\n> diff --git a/src/libcamera/object.cpp b/src/libcamera/object.cpp\n> index 7c68ec01f78c..57cd6fefe20f 100644\n> --- a/src/libcamera/object.cpp\n> +++ b/src/libcamera/object.cpp\n> @@ -7,6 +7,8 @@\n>\n>  #include <libcamera/object.h>\n>\n> +#include <algorithm>\n> +\n>  #include <libcamera/signal.h>\n>\n>  #include \"log.h\"\n> @@ -21,6 +23,8 @@\n>\n>  namespace libcamera {\n>\n> +LOG_DEFINE_CATEGORY(Object)\n> +\n>  /**\n>   * \\class Object\n>   * \\brief Base object to support automatic signal disconnection\n> @@ -29,10 +33,11 @@ namespace libcamera {\n>   * slots. By inheriting from Object, an object is automatically disconnected\n>   * from all connected signals when it gets destroyed.\n>   *\n> - * Object instances are bound to the thread in which they're created. When a\n> - * message is posted to an object, its handler will run in the object's thread.\n> - * This allows implementing easy message passing between threads by inheriting\n> - * from the Object class.\n> + * Object instances are bound to the thread of their parent, or the thread in\n> + * which they're created when they have no parent. When a message is posted to\n> + * an object, its handler will run in the object's thread. This allows\n> + * implementing easy message passing between threads by inheriting from the\n> + * Object class.\n>   *\n>   * Object slots connected to signals will also run in the context of the\n>   * object's thread, regardless of whether the signal is emitted in the same or\n> @@ -41,10 +46,20 @@ namespace libcamera {\n>   * \\sa Message, Signal, Thread\n>   */\n>\n> -Object::Object()\n> -\t: pendingMessages_(0)\n> +/**\n> + * \\brief Construct an Object instance\n> + * \\param[in] parent The object parent\n> + *\n> + * The new Object instance is bound to the thread of its \\a parent, or to the\n> + * current thread if the \\a parent is nullptr.\n> + */\n> +Object::Object(Object *parent)\n> +\t: parent_(parent), pendingMessages_(0)\n>  {\n> -\tthread_ = Thread::current();\n> +\tthread_ = parent ? parent->thread() : Thread::current();\n> +\n> +\tif (parent)\n> +\t\tparent->children_.push_back(this);\n>  }\n>\n>  Object::~Object()\n> @@ -54,6 +69,16 @@ Object::~Object()\n>\n>  \tif (pendingMessages_)\n>  \t\tthread()->removeMessages(this);\n> +\n> +\tif (parent_) {\n> +\t\tauto it = std::find(parent_->children_.begin(),\n> +\t\t\t\t    parent_->children_.end(), this);\n> +\t\tASSERT(it != parent_->children_.end());\n> +\t\tparent_->children_.erase(it);\n> +\t}\n> +\n> +\tfor (auto child : children_)\n> +\t\tchild->parent_ = nullptr;\n>  }\n>\n>  /**\n> @@ -143,16 +168,19 @@ void Object::invokeMethod(BoundMethodBase *method, void *args)\n>   */\n>\n>  /**\n> - * \\brief Move the object to a different thread\n> + * \\brief Move the object and all its children to a different thread\n>   * \\param[in] thread The target thread\n>   *\n> - * This method moves the object from the current thread to the new \\a thread.\n> - * It shall be called from the thread in which the object currently lives,\n> - * otherwise the behaviour is undefined.\n> + * This method moves the object and all its children from the current thread to\n> + * the new \\a thread. It shall be called from the thread in which the object\n> + * currently lives, otherwise the behaviour is undefined.\n>   *\n>   * Before the object is moved, a Message::ThreadMoveMessage message is sent to\n>   * it. The message() method can be reimplement in derived classes to be notified\n>   * of the upcoming thread move and perform any required processing.\n> + *\n> + * Moving an object that has a parent is not allowed, and causes undefined\n> + * behaviour.\n>   */\n>  void Object::moveToThread(Thread *thread)\n>  {\n> @@ -161,6 +189,12 @@ void Object::moveToThread(Thread *thread)\n>  \tif (thread_ == thread)\n>  \t\treturn;\n>\n> +\tif (parent_) {\n> +\t\tLOG(Object, Error)\n> +\t\t\t<< \"Moving object to thread with a parent is not permitted\";\n> +\t\treturn;\n> +\t}\n> +\n>  \tnotifyThreadMove();\n>\n>  \tthread->moveObject(this);\n> @@ -170,8 +204,17 @@ void Object::notifyThreadMove()\n>  {\n>  \tMessage msg(Message::ThreadMoveMessage);\n>  \tsendMessage(&msg);\n> +\n> +\tfor (auto child : children_)\n> +\t\tchild->notifyThreadMove();\n>  }\n>\n> +/**\n> + * \\fn Object::parent()\n> + * \\brief Retrieve the object's parent\n> + * \\return The object's parent\n> + */\n> +\n>  void Object::connect(SignalBase *signal)\n>  {\n>  \tsignals_.push_back(signal);\n> diff --git a/src/libcamera/thread.cpp b/src/libcamera/thread.cpp\n> index 24422f7b7bd0..872ad1bd9d69 100644\n> --- a/src/libcamera/thread.cpp\n> +++ b/src/libcamera/thread.cpp\n> @@ -448,7 +448,7 @@ void Thread::dispatchMessages()\n>  }\n>\n>  /**\n> - * \\brief Move an \\a object to the thread\n> + * \\brief Move an \\a object and all its children to the thread\n>   * \\param[in] object The object\n>   */\n>  void Thread::moveObject(Object *object)\n> @@ -460,6 +460,12 @@ void Thread::moveObject(Object *object)\n>  \tMutexLocker lockerTo(targetData->mutex_, std::defer_lock);\n>  \tstd::lock(lockerFrom, lockerTo);\n>\n> +\tmoveObject(object, currentData, targetData);\n> +}\n> +\n> +void Thread::moveObject(Object *object, ThreadData *currentData,\n> +\t\t\tThreadData *targetData)\n> +{\n>  \t/* Move pending messages to the message queue of the new thread. */\n>  \tif (object->pendingMessages_) {\n>  \t\tunsigned int movedMessages = 0;\n> @@ -483,6 +489,10 @@ void Thread::moveObject(Object *object)\n>  \t}\n>\n>  \tobject->thread_ = this;\n> +\n> +\t/* Move all children. */\n> +\tfor (auto child : object->children_)\n> +\t\tmoveObject(child, currentData, targetData);\n>  }\n>\n>  }; /* namespace libcamera */\n> --\n> Regards,\n>\n> Laurent Pinchart\n>\n> _______________________________________________\n> libcamera-devel mailing list\n> libcamera-devel@lists.libcamera.org\n> https://lists.libcamera.org/listinfo/libcamera-devel","headers":{"Return-Path":"<jacopo@jmondi.org>","Received":["from relay12.mail.gandi.net (relay12.mail.gandi.net\n\t[217.70.178.232])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 4759960E2C\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 15 Aug 2019 11:55:21 +0200 (CEST)","from uno.localdomain\n\t(host64-130-dynamic.5-87-r.retail.telecomitalia.it [87.5.130.64])\n\t(Authenticated sender: jacopo@jmondi.org)\n\tby relay12.mail.gandi.net (Postfix) with ESMTPSA id 56F7720000A;\n\tThu, 15 Aug 2019 09:55:20 +0000 (UTC)"],"Date":"Thu, 15 Aug 2019 11:56:47 +0200","From":"Jacopo Mondi <jacopo@jmondi.org>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Message-ID":"<20190815095647.ogsdyfofpthv635h@uno.localdomain>","References":"<20190812124642.24287-1-laurent.pinchart@ideasonboard.com>\n\t<20190812124642.24287-16-laurent.pinchart@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"multipart/signed; micalg=pgp-sha256;\n\tprotocol=\"application/pgp-signature\"; boundary=\"vdyenb5vqklsnwro\"","Content-Disposition":"inline","In-Reply-To":"<20190812124642.24287-16-laurent.pinchart@ideasonboard.com>","User-Agent":"NeoMutt/20180716","Subject":"Re: [libcamera-devel] [PATCH 15/18] libcamera: object: Create\n\tparent-child relationships","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.23","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Thu, 15 Aug 2019 09:55:21 -0000"}},{"id":2419,"web_url":"https://patchwork.libcamera.org/comment/2419/","msgid":"<20190815102118.GG5011@pendragon.ideasonboard.com>","date":"2019-08-15T10:21:18","subject":"Re: [libcamera-devel] [PATCH 15/18] libcamera: object: Create\n\tparent-child relationships","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Jacopo,\n\nOn Thu, Aug 15, 2019 at 11:56:47AM +0200, Jacopo Mondi wrote:\n> On Mon, Aug 12, 2019 at 03:46:39PM +0300, Laurent Pinchart wrote:\n> > Add a parent Object to Object instances, and track the parent-children\n> > relationships. Children are bound to the same thread as their parent,\n> > and moving an Object to a thread automatically moves all its children.\n> >\n> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > ---\n> >  include/libcamera/object.h     |  8 ++++-\n> >  src/libcamera/include/thread.h |  2 ++\n> >  src/libcamera/object.cpp       | 65 ++++++++++++++++++++++++++++------\n> >  src/libcamera/thread.cpp       | 12 ++++++-\n> >  4 files changed, 74 insertions(+), 13 deletions(-)\n> >\n> > diff --git a/include/libcamera/object.h b/include/libcamera/object.h\n> > index 14b939a9bd3d..4a0a272b875a 100644\n> > --- a/include/libcamera/object.h\n> > +++ b/include/libcamera/object.h\n> > @@ -9,6 +9,7 @@\n> >\n> >  #include <list>\n> >  #include <memory>\n> > +#include <vector>\n> >\n> >  #include <libcamera/bound_method.h>\n> >\n> > @@ -23,7 +24,7 @@ class Thread;\n> >  class Object\n> >  {\n> >  public:\n> > -\tObject();\n> > +\tObject(Object *parent = nullptr);\n> >  \tvirtual ~Object();\n> >\n> >  \tvoid postMessage(std::unique_ptr<Message> msg);\n> > @@ -42,6 +43,8 @@ public:\n> >  \tThread *thread() const { return thread_; }\n> >  \tvoid moveToThread(Thread *thread);\n> >\n> > +\tObject *parent() const { return parent_; }\n> \n> maybe const Object * ?\n\nThere's a need to call non-const function of the parent, so we need a\nnon-const pointer. I could add two versions of this method, const Object\n*parent() const and Object *parent(), but I don't think that's needed.\nThe parent isn't part of the child, so modifying it doesn't modify the\nstate of the child.\n\n> > +\n> >  protected:\n> >  \tvirtual void message(Message *msg);\n> >\n> > @@ -58,6 +61,9 @@ private:\n> >  \tvoid connect(SignalBase *signal);\n> >  \tvoid disconnect(SignalBase *signal);\n> >\n> > +\tObject *parent_;\n> > +\tstd::vector<Object *> children_;\n> > +\n> >  \tThread *thread_;\n> >  \tstd::list<SignalBase *> signals_;\n> >  \tunsigned int pendingMessages_;\n> > diff --git a/src/libcamera/include/thread.h b/src/libcamera/include/thread.h\n> > index 630abb49534f..37edd4f5138b 100644\n> > --- a/src/libcamera/include/thread.h\n> > +++ b/src/libcamera/include/thread.h\n> > @@ -61,6 +61,8 @@ private:\n> >  \tfriend class ThreadMain;\n> >\n> >  \tvoid moveObject(Object *object);\n> > +\tvoid moveObject(Object *object, ThreadData *currentData,\n> > +\t\t\tThreadData *targetData);\n> >\n> >  \tstd::thread thread_;\n> >  \tThreadData *data_;\n> > diff --git a/src/libcamera/object.cpp b/src/libcamera/object.cpp\n> > index 7c68ec01f78c..57cd6fefe20f 100644\n> > --- a/src/libcamera/object.cpp\n> > +++ b/src/libcamera/object.cpp\n> > @@ -7,6 +7,8 @@\n> >\n> >  #include <libcamera/object.h>\n> >\n> > +#include <algorithm>\n> > +\n> >  #include <libcamera/signal.h>\n> >\n> >  #include \"log.h\"\n> > @@ -21,6 +23,8 @@\n> >\n> >  namespace libcamera {\n> >\n> > +LOG_DEFINE_CATEGORY(Object)\n> > +\n> >  /**\n> >   * \\class Object\n> >   * \\brief Base object to support automatic signal disconnection\n> > @@ -29,10 +33,11 @@ namespace libcamera {\n> >   * slots. By inheriting from Object, an object is automatically disconnected\n> >   * from all connected signals when it gets destroyed.\n> >   *\n> > - * Object instances are bound to the thread in which they're created. When a\n> > - * message is posted to an object, its handler will run in the object's thread.\n> > - * This allows implementing easy message passing between threads by inheriting\n> > - * from the Object class.\n> > + * Object instances are bound to the thread of their parent, or the thread in\n> > + * which they're created when they have no parent. When a message is posted to\n> > + * an object, its handler will run in the object's thread. This allows\n> > + * implementing easy message passing between threads by inheriting from the\n> > + * Object class.\n> >   *\n> >   * Object slots connected to signals will also run in the context of the\n> >   * object's thread, regardless of whether the signal is emitted in the same or\n> > @@ -41,10 +46,20 @@ namespace libcamera {\n> >   * \\sa Message, Signal, Thread\n> >   */\n> >\n> > -Object::Object()\n> > -\t: pendingMessages_(0)\n> > +/**\n> > + * \\brief Construct an Object instance\n> > + * \\param[in] parent The object parent\n> > + *\n> > + * The new Object instance is bound to the thread of its \\a parent, or to the\n> > + * current thread if the \\a parent is nullptr.\n> > + */\n> > +Object::Object(Object *parent)\n> > +\t: parent_(parent), pendingMessages_(0)\n> >  {\n> > -\tthread_ = Thread::current();\n> > +\tthread_ = parent ? parent->thread() : Thread::current();\n> > +\n> > +\tif (parent)\n> > +\t\tparent->children_.push_back(this);\n> >  }\n> >\n> >  Object::~Object()\n> > @@ -54,6 +69,16 @@ Object::~Object()\n> >\n> >  \tif (pendingMessages_)\n> >  \t\tthread()->removeMessages(this);\n> > +\n> > +\tif (parent_) {\n> > +\t\tauto it = std::find(parent_->children_.begin(),\n> > +\t\t\t\t    parent_->children_.end(), this);\n> > +\t\tASSERT(it != parent_->children_.end());\n> > +\t\tparent_->children_.erase(it);\n> > +\t}\n> > +\n> > +\tfor (auto child : children_)\n> > +\t\tchild->parent_ = nullptr;\n> >  }\n> >\n> >  /**\n> > @@ -143,16 +168,19 @@ void Object::invokeMethod(BoundMethodBase *method, void *args)\n> >   */\n> >\n> >  /**\n> > - * \\brief Move the object to a different thread\n> > + * \\brief Move the object and all its children to a different thread\n> >   * \\param[in] thread The target thread\n> >   *\n> > - * This method moves the object from the current thread to the new \\a thread.\n> > - * It shall be called from the thread in which the object currently lives,\n> > - * otherwise the behaviour is undefined.\n> > + * This method moves the object and all its children from the current thread to\n> > + * the new \\a thread. It shall be called from the thread in which the object\n> > + * currently lives, otherwise the behaviour is undefined.\n> >   *\n> >   * Before the object is moved, a Message::ThreadMoveMessage message is sent to\n> >   * it. The message() method can be reimplement in derived classes to be notified\n> >   * of the upcoming thread move and perform any required processing.\n> > + *\n> > + * Moving an object that has a parent is not allowed, and causes undefined\n> > + * behaviour.\n> >   */\n> >  void Object::moveToThread(Thread *thread)\n> >  {\n> > @@ -161,6 +189,12 @@ void Object::moveToThread(Thread *thread)\n> >  \tif (thread_ == thread)\n> >  \t\treturn;\n> >\n> > +\tif (parent_) {\n> > +\t\tLOG(Object, Error)\n> > +\t\t\t<< \"Moving object to thread with a parent is not permitted\";\n> > +\t\treturn;\n> > +\t}\n> > +\n> >  \tnotifyThreadMove();\n> >\n> >  \tthread->moveObject(this);\n> > @@ -170,8 +204,17 @@ void Object::notifyThreadMove()\n> >  {\n> >  \tMessage msg(Message::ThreadMoveMessage);\n> >  \tsendMessage(&msg);\n> > +\n> > +\tfor (auto child : children_)\n> > +\t\tchild->notifyThreadMove();\n> >  }\n> >\n> > +/**\n> > + * \\fn Object::parent()\n> > + * \\brief Retrieve the object's parent\n> > + * \\return The object's parent\n> > + */\n> > +\n> >  void Object::connect(SignalBase *signal)\n> >  {\n> >  \tsignals_.push_back(signal);\n> > diff --git a/src/libcamera/thread.cpp b/src/libcamera/thread.cpp\n> > index 24422f7b7bd0..872ad1bd9d69 100644\n> > --- a/src/libcamera/thread.cpp\n> > +++ b/src/libcamera/thread.cpp\n> > @@ -448,7 +448,7 @@ void Thread::dispatchMessages()\n> >  }\n> >\n> >  /**\n> > - * \\brief Move an \\a object to the thread\n> > + * \\brief Move an \\a object and all its children to the thread\n> >   * \\param[in] object The object\n> >   */\n> >  void Thread::moveObject(Object *object)\n> > @@ -460,6 +460,12 @@ void Thread::moveObject(Object *object)\n> >  \tMutexLocker lockerTo(targetData->mutex_, std::defer_lock);\n> >  \tstd::lock(lockerFrom, lockerTo);\n> >\n> > +\tmoveObject(object, currentData, targetData);\n> > +}\n> > +\n> > +void Thread::moveObject(Object *object, ThreadData *currentData,\n> > +\t\t\tThreadData *targetData)\n> > +{\n> >  \t/* Move pending messages to the message queue of the new thread. */\n> >  \tif (object->pendingMessages_) {\n> >  \t\tunsigned int movedMessages = 0;\n> > @@ -483,6 +489,10 @@ void Thread::moveObject(Object *object)\n> >  \t}\n> >\n> >  \tobject->thread_ = this;\n> > +\n> > +\t/* Move all children. */\n> > +\tfor (auto child : object->children_)\n> > +\t\tmoveObject(child, currentData, targetData);\n> >  }\n> >\n> >  }; /* namespace libcamera */\n> > --\n> > Regards,\n> >\n> > Laurent Pinchart\n> >\n> > _______________________________________________\n> > libcamera-devel mailing list\n> > libcamera-devel@lists.libcamera.org\n> > https://lists.libcamera.org/listinfo/libcamera-devel","headers":{"Return-Path":"<laurent.pinchart@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 1DCCD60E2D\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 15 Aug 2019 12:21:22 +0200 (CEST)","from pendragon.ideasonboard.com\n\t(dfj612yhrgyx302h3jwwy-3.rev.dnainternet.fi\n\t[IPv6:2001:14ba:21f5:5b00:ce28:277f:58d7:3ca4])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 890372B2;\n\tThu, 15 Aug 2019 12:21:21 +0200 (CEST)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1565864481;\n\tbh=MYTg0buYSTnSAw37HyTg7CV8+cSiNEoO1bPkw+QMmMI=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=mNh1tmEMZK8xip5Hh22eiedydy/W7INoC8+jW+iUnyIW4hQh+vN3vL3p6J/5hSv2F\n\tWgbKU4StlZZLhblYu8akj2MowM95VcxCYNfjPM1llP2HhMyoBSwKx9EaFaKjA9rX16\n\tsW57dObFGXqIJwZIUSWs7YabgNvrzHdyDwu8gQ+A=","Date":"Thu, 15 Aug 2019 13:21:18 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Jacopo Mondi <jacopo@jmondi.org>","Cc":"libcamera-devel@lists.libcamera.org","Message-ID":"<20190815102118.GG5011@pendragon.ideasonboard.com>","References":"<20190812124642.24287-1-laurent.pinchart@ideasonboard.com>\n\t<20190812124642.24287-16-laurent.pinchart@ideasonboard.com>\n\t<20190815095647.ogsdyfofpthv635h@uno.localdomain>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20190815095647.ogsdyfofpthv635h@uno.localdomain>","User-Agent":"Mutt/1.10.1 (2018-07-13)","Subject":"Re: [libcamera-devel] [PATCH 15/18] libcamera: object: Create\n\tparent-child relationships","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.23","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Thu, 15 Aug 2019 10:21:22 -0000"}},{"id":2440,"web_url":"https://patchwork.libcamera.org/comment/2440/","msgid":"<20190817151022.GQ16603@wyvern>","date":"2019-08-17T15:10:22","subject":"Re: [libcamera-devel] [PATCH 15/18] libcamera: object: Create\n\tparent-child relationships","submitter":{"id":5,"url":"https://patchwork.libcamera.org/api/people/5/","name":"Niklas Söderlund","email":"niklas.soderlund@ragnatech.se"},"content":"Hi Laurent,\n\nThanks for your work.\n\nOn 2019-08-12 15:46:39 +0300, Laurent Pinchart wrote:\n> Add a parent Object to Object instances, and track the parent-children\n> relationships. Children are bound to the same thread as their parent,\n> and moving an Object to a thread automatically moves all its children.\n> \n> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> ---\n>  include/libcamera/object.h     |  8 ++++-\n>  src/libcamera/include/thread.h |  2 ++\n>  src/libcamera/object.cpp       | 65 ++++++++++++++++++++++++++++------\n>  src/libcamera/thread.cpp       | 12 ++++++-\n>  4 files changed, 74 insertions(+), 13 deletions(-)\n> \n> diff --git a/include/libcamera/object.h b/include/libcamera/object.h\n> index 14b939a9bd3d..4a0a272b875a 100644\n> --- a/include/libcamera/object.h\n> +++ b/include/libcamera/object.h\n> @@ -9,6 +9,7 @@\n>  \n>  #include <list>\n>  #include <memory>\n> +#include <vector>\n>  \n>  #include <libcamera/bound_method.h>\n>  \n> @@ -23,7 +24,7 @@ class Thread;\n>  class Object\n>  {\n>  public:\n> -\tObject();\n> +\tObject(Object *parent = nullptr);\n>  \tvirtual ~Object();\n>  \n>  \tvoid postMessage(std::unique_ptr<Message> msg);\n> @@ -42,6 +43,8 @@ public:\n>  \tThread *thread() const { return thread_; }\n>  \tvoid moveToThread(Thread *thread);\n>  \n> +\tObject *parent() const { return parent_; }\n> +\n>  protected:\n>  \tvirtual void message(Message *msg);\n>  \n> @@ -58,6 +61,9 @@ private:\n>  \tvoid connect(SignalBase *signal);\n>  \tvoid disconnect(SignalBase *signal);\n>  \n> +\tObject *parent_;\n> +\tstd::vector<Object *> children_;\n> +\n>  \tThread *thread_;\n>  \tstd::list<SignalBase *> signals_;\n>  \tunsigned int pendingMessages_;\n> diff --git a/src/libcamera/include/thread.h b/src/libcamera/include/thread.h\n> index 630abb49534f..37edd4f5138b 100644\n> --- a/src/libcamera/include/thread.h\n> +++ b/src/libcamera/include/thread.h\n> @@ -61,6 +61,8 @@ private:\n>  \tfriend class ThreadMain;\n>  \n>  \tvoid moveObject(Object *object);\n> +\tvoid moveObject(Object *object, ThreadData *currentData,\n> +\t\t\tThreadData *targetData);\n>  \n>  \tstd::thread thread_;\n>  \tThreadData *data_;\n> diff --git a/src/libcamera/object.cpp b/src/libcamera/object.cpp\n> index 7c68ec01f78c..57cd6fefe20f 100644\n> --- a/src/libcamera/object.cpp\n> +++ b/src/libcamera/object.cpp\n> @@ -7,6 +7,8 @@\n>  \n>  #include <libcamera/object.h>\n>  \n> +#include <algorithm>\n> +\n>  #include <libcamera/signal.h>\n>  \n>  #include \"log.h\"\n> @@ -21,6 +23,8 @@\n>  \n>  namespace libcamera {\n>  \n> +LOG_DEFINE_CATEGORY(Object)\n> +\n>  /**\n>   * \\class Object\n>   * \\brief Base object to support automatic signal disconnection\n> @@ -29,10 +33,11 @@ namespace libcamera {\n>   * slots. By inheriting from Object, an object is automatically disconnected\n>   * from all connected signals when it gets destroyed.\n>   *\n> - * Object instances are bound to the thread in which they're created. When a\n> - * message is posted to an object, its handler will run in the object's thread.\n> - * This allows implementing easy message passing between threads by inheriting\n> - * from the Object class.\n> + * Object instances are bound to the thread of their parent, or the thread in\n> + * which they're created when they have no parent. When a message is posted to\n> + * an object, its handler will run in the object's thread. This allows\n> + * implementing easy message passing between threads by inheriting from the\n> + * Object class.\n>   *\n>   * Object slots connected to signals will also run in the context of the\n>   * object's thread, regardless of whether the signal is emitted in the same or\n> @@ -41,10 +46,20 @@ namespace libcamera {\n>   * \\sa Message, Signal, Thread\n>   */\n>  \n> -Object::Object()\n> -\t: pendingMessages_(0)\n> +/**\n> + * \\brief Construct an Object instance\n> + * \\param[in] parent The object parent\n> + *\n> + * The new Object instance is bound to the thread of its \\a parent, or to the\n> + * current thread if the \\a parent is nullptr.\n> + */\n> +Object::Object(Object *parent)\n> +\t: parent_(parent), pendingMessages_(0)\n>  {\n> -\tthread_ = Thread::current();\n> +\tthread_ = parent ? parent->thread() : Thread::current();\n> +\n> +\tif (parent)\n> +\t\tparent->children_.push_back(this);\n>  }\n>  \n>  Object::~Object()\n> @@ -54,6 +69,16 @@ Object::~Object()\n>  \n>  \tif (pendingMessages_)\n>  \t\tthread()->removeMessages(this);\n> +\n> +\tif (parent_) {\n> +\t\tauto it = std::find(parent_->children_.begin(),\n> +\t\t\t\t    parent_->children_.end(), this);\n> +\t\tASSERT(it != parent_->children_.end());\n> +\t\tparent_->children_.erase(it);\n> +\t}\n> +\n> +\tfor (auto child : children_)\n> +\t\tchild->parent_ = nullptr;\n\nIs this the behavior that is most logical? I'm not saying this is wrong \nbut want to discuss it a bit.\n\nIf a object with a parent and children is delete is it not more logical \nto move the children to the parent of the object being deleted? I fear \nthis could lead to subtle bugs if objects who are part of a hierarchy \nbecome disjointed from the hierarchy when its parent is deleted.\n\nMaybe my gut feeling is wrong and my fear is unfounded.\n\n>  }\n>  \n>  /**\n> @@ -143,16 +168,19 @@ void Object::invokeMethod(BoundMethodBase *method, void *args)\n>   */\n>  \n>  /**\n> - * \\brief Move the object to a different thread\n> + * \\brief Move the object and all its children to a different thread\n>   * \\param[in] thread The target thread\n>   *\n> - * This method moves the object from the current thread to the new \\a thread.\n> - * It shall be called from the thread in which the object currently lives,\n> - * otherwise the behaviour is undefined.\n> + * This method moves the object and all its children from the current thread to\n> + * the new \\a thread. It shall be called from the thread in which the object\n> + * currently lives, otherwise the behaviour is undefined.\n>   *\n>   * Before the object is moved, a Message::ThreadMoveMessage message is sent to\n>   * it. The message() method can be reimplement in derived classes to be notified\n>   * of the upcoming thread move and perform any required processing.\n> + *\n> + * Moving an object that has a parent is not allowed, and causes undefined\n> + * behaviour.\n>   */\n>  void Object::moveToThread(Thread *thread)\n>  {\n> @@ -161,6 +189,12 @@ void Object::moveToThread(Thread *thread)\n>  \tif (thread_ == thread)\n>  \t\treturn;\n>  \n> +\tif (parent_) {\n> +\t\tLOG(Object, Error)\n> +\t\t\t<< \"Moving object to thread with a parent is not permitted\";\n> +\t\treturn;\n> +\t}\n> +\n>  \tnotifyThreadMove();\n>  \n>  \tthread->moveObject(this);\n> @@ -170,8 +204,17 @@ void Object::notifyThreadMove()\n>  {\n>  \tMessage msg(Message::ThreadMoveMessage);\n>  \tsendMessage(&msg);\n> +\n> +\tfor (auto child : children_)\n> +\t\tchild->notifyThreadMove();\n>  }\n>  \n> +/**\n> + * \\fn Object::parent()\n> + * \\brief Retrieve the object's parent\n> + * \\return The object's parent\n> + */\n> +\n>  void Object::connect(SignalBase *signal)\n>  {\n>  \tsignals_.push_back(signal);\n> diff --git a/src/libcamera/thread.cpp b/src/libcamera/thread.cpp\n> index 24422f7b7bd0..872ad1bd9d69 100644\n> --- a/src/libcamera/thread.cpp\n> +++ b/src/libcamera/thread.cpp\n> @@ -448,7 +448,7 @@ void Thread::dispatchMessages()\n>  }\n>  \n>  /**\n> - * \\brief Move an \\a object to the thread\n> + * \\brief Move an \\a object and all its children to the thread\n>   * \\param[in] object The object\n>   */\n>  void Thread::moveObject(Object *object)\n> @@ -460,6 +460,12 @@ void Thread::moveObject(Object *object)\n>  \tMutexLocker lockerTo(targetData->mutex_, std::defer_lock);\n>  \tstd::lock(lockerFrom, lockerTo);\n>  \n> +\tmoveObject(object, currentData, targetData);\n> +}\n> +\n> +void Thread::moveObject(Object *object, ThreadData *currentData,\n> +\t\t\tThreadData *targetData)\n> +{\n>  \t/* Move pending messages to the message queue of the new thread. */\n>  \tif (object->pendingMessages_) {\n>  \t\tunsigned int movedMessages = 0;\n> @@ -483,6 +489,10 @@ void Thread::moveObject(Object *object)\n>  \t}\n>  \n>  \tobject->thread_ = this;\n> +\n> +\t/* Move all children. */\n> +\tfor (auto child : object->children_)\n> +\t\tmoveObject(child, currentData, targetData);\n>  }\n>  \n>  }; /* namespace libcamera */\n> -- \n> Regards,\n> \n> Laurent Pinchart\n> \n> _______________________________________________\n> libcamera-devel mailing list\n> libcamera-devel@lists.libcamera.org\n> https://lists.libcamera.org/listinfo/libcamera-devel","headers":{"Return-Path":"<niklas.soderlund@ragnatech.se>","Received":["from mail-ed1-x542.google.com (mail-ed1-x542.google.com\n\t[IPv6:2a00:1450:4864:20::542])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 222B1600F9\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 17 Aug 2019 17:10:26 +0200 (CEST)","by mail-ed1-x542.google.com with SMTP id r12so7572346edo.5\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 17 Aug 2019 08:10:26 -0700 (PDT)","from localhost ([185.224.57.161]) by smtp.gmail.com with ESMTPSA id\n\ti8sm1679740edg.12.2019.08.17.08.10.24\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tSat, 17 Aug 2019 08:10:24 -0700 (PDT)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=ragnatech-se.20150623.gappssmtp.com; s=20150623;\n\th=date:from:to:cc:subject:message-id:references:mime-version\n\t:content-disposition:content-transfer-encoding:in-reply-to\n\t:user-agent; bh=tuVEqgTtYRUJRieBiW0MWIxEePSSCCe/otE1Rw5GeL4=;\n\tb=W/1mfjtIdMI8O5L4y0KQCR7jNozjh6+MB+L8Ated8PjSoSf2prgWD75ovyM92oos/J\n\tw9UWYsLh1bMVy9V1Y+yFBX1P9+QabYzXR173TfLTyUDs4hvbsvhRKNIvUY/Cc8pIlUY+\n\tzDiMltjNdzLHKXP8KYXJ5Sh/elQcyaDHnwsOp7IM9gt0h1lXoQAOwFDeKzt4PWW1pRIS\n\tr6iDXWhCz58hSILvkPNwzixiVF8Nl26xzUv+5/TEniOI9NlRwdmHvqp3E1M1RXBRgoNl\n\tsp3rNulSWXBoK8pjhzJXxbmWnErgWRVp238YKaT9j1HuS28RhVCWnv5CU9d7GHJGgLo5\n\tHy8w==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:date:from:to:cc:subject:message-id:references\n\t:mime-version:content-disposition:content-transfer-encoding\n\t:in-reply-to:user-agent;\n\tbh=tuVEqgTtYRUJRieBiW0MWIxEePSSCCe/otE1Rw5GeL4=;\n\tb=I4Uq3bQNJTsIYT6UhhHO409U9bY58b88OelGQX+NyOvZYkulAeZvMOifZj5Iwn4DtJ\n\tm4xjmB7m+ybWPeqRK/myqDcradzpzuKF8u1pkfNDMzghE/ezav+qJyPNHp8A0FoV5/0E\n\t3VHdAViLANBO+elDEyuaS7J7B75ewbZ9MfX/MtpGu1okdfJBlxG4nlrwDn8JKQC39wed\n\t79xSm6RBJ4acP6vmvI4zC91Mk/zRkmvOa6nc+Qz5NhtXg+MDEr7Eh4VBG2cxlKkEqXBO\n\tSny3y0IakGozUiNqfnfLDeYXrOnpcw3wBgI5bzRo16ap0Lww+L06Az2g02U0KxUHu900\n\tX53Q==","X-Gm-Message-State":"APjAAAVVW6YBZIa8MqXSTgQy938p10UPhnEdf0wXm0Skvjm+IBtpGztc\n\txhVxOvrMfL4n6ZuffeWOYgb8suPPwrc=","X-Google-Smtp-Source":"APXvYqzRmelmu431O3BOOxNOnDesou4pXJYqDIAT5xzUeT1gYafoweqN+Yo/jLM3MP/5sigFzR3zVg==","X-Received":"by 2002:a17:906:578a:: with SMTP id\n\tk10mr13872185ejq.235.1566054625653; \n\tSat, 17 Aug 2019 08:10:25 -0700 (PDT)","Date":"Sat, 17 Aug 2019 17:10:22 +0200","From":"Niklas =?iso-8859-1?q?S=F6derlund?= <niklas.soderlund@ragnatech.se>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Message-ID":"<20190817151022.GQ16603@wyvern>","References":"<20190812124642.24287-1-laurent.pinchart@ideasonboard.com>\n\t<20190812124642.24287-16-laurent.pinchart@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=iso-8859-1","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<20190812124642.24287-16-laurent.pinchart@ideasonboard.com>","User-Agent":"Mutt/1.12.1 (2019-06-15)","Subject":"Re: [libcamera-devel] [PATCH 15/18] libcamera: object: Create\n\tparent-child relationships","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.23","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Sat, 17 Aug 2019 15:10:26 -0000"}},{"id":2442,"web_url":"https://patchwork.libcamera.org/comment/2442/","msgid":"<20190817151306.GB15630@pendragon.ideasonboard.com>","date":"2019-08-17T15:13:06","subject":"Re: [libcamera-devel] [PATCH 15/18] libcamera: object: Create\n\tparent-child relationships","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Niklas,\n\nOn Sat, Aug 17, 2019 at 05:10:22PM +0200, Niklas Söderlund wrote:\n> On 2019-08-12 15:46:39 +0300, Laurent Pinchart wrote:\n> > Add a parent Object to Object instances, and track the parent-children\n> > relationships. Children are bound to the same thread as their parent,\n> > and moving an Object to a thread automatically moves all its children.\n> > \n> > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > ---\n> >  include/libcamera/object.h     |  8 ++++-\n> >  src/libcamera/include/thread.h |  2 ++\n> >  src/libcamera/object.cpp       | 65 ++++++++++++++++++++++++++++------\n> >  src/libcamera/thread.cpp       | 12 ++++++-\n> >  4 files changed, 74 insertions(+), 13 deletions(-)\n> > \n> > diff --git a/include/libcamera/object.h b/include/libcamera/object.h\n> > index 14b939a9bd3d..4a0a272b875a 100644\n> > --- a/include/libcamera/object.h\n> > +++ b/include/libcamera/object.h\n> > @@ -9,6 +9,7 @@\n> >  \n> >  #include <list>\n> >  #include <memory>\n> > +#include <vector>\n> >  \n> >  #include <libcamera/bound_method.h>\n> >  \n> > @@ -23,7 +24,7 @@ class Thread;\n> >  class Object\n> >  {\n> >  public:\n> > -\tObject();\n> > +\tObject(Object *parent = nullptr);\n> >  \tvirtual ~Object();\n> >  \n> >  \tvoid postMessage(std::unique_ptr<Message> msg);\n> > @@ -42,6 +43,8 @@ public:\n> >  \tThread *thread() const { return thread_; }\n> >  \tvoid moveToThread(Thread *thread);\n> >  \n> > +\tObject *parent() const { return parent_; }\n> > +\n> >  protected:\n> >  \tvirtual void message(Message *msg);\n> >  \n> > @@ -58,6 +61,9 @@ private:\n> >  \tvoid connect(SignalBase *signal);\n> >  \tvoid disconnect(SignalBase *signal);\n> >  \n> > +\tObject *parent_;\n> > +\tstd::vector<Object *> children_;\n> > +\n> >  \tThread *thread_;\n> >  \tstd::list<SignalBase *> signals_;\n> >  \tunsigned int pendingMessages_;\n> > diff --git a/src/libcamera/include/thread.h b/src/libcamera/include/thread.h\n> > index 630abb49534f..37edd4f5138b 100644\n> > --- a/src/libcamera/include/thread.h\n> > +++ b/src/libcamera/include/thread.h\n> > @@ -61,6 +61,8 @@ private:\n> >  \tfriend class ThreadMain;\n> >  \n> >  \tvoid moveObject(Object *object);\n> > +\tvoid moveObject(Object *object, ThreadData *currentData,\n> > +\t\t\tThreadData *targetData);\n> >  \n> >  \tstd::thread thread_;\n> >  \tThreadData *data_;\n> > diff --git a/src/libcamera/object.cpp b/src/libcamera/object.cpp\n> > index 7c68ec01f78c..57cd6fefe20f 100644\n> > --- a/src/libcamera/object.cpp\n> > +++ b/src/libcamera/object.cpp\n> > @@ -7,6 +7,8 @@\n> >  \n> >  #include <libcamera/object.h>\n> >  \n> > +#include <algorithm>\n> > +\n> >  #include <libcamera/signal.h>\n> >  \n> >  #include \"log.h\"\n> > @@ -21,6 +23,8 @@\n> >  \n> >  namespace libcamera {\n> >  \n> > +LOG_DEFINE_CATEGORY(Object)\n> > +\n> >  /**\n> >   * \\class Object\n> >   * \\brief Base object to support automatic signal disconnection\n> > @@ -29,10 +33,11 @@ namespace libcamera {\n> >   * slots. By inheriting from Object, an object is automatically disconnected\n> >   * from all connected signals when it gets destroyed.\n> >   *\n> > - * Object instances are bound to the thread in which they're created. When a\n> > - * message is posted to an object, its handler will run in the object's thread.\n> > - * This allows implementing easy message passing between threads by inheriting\n> > - * from the Object class.\n> > + * Object instances are bound to the thread of their parent, or the thread in\n> > + * which they're created when they have no parent. When a message is posted to\n> > + * an object, its handler will run in the object's thread. This allows\n> > + * implementing easy message passing between threads by inheriting from the\n> > + * Object class.\n> >   *\n> >   * Object slots connected to signals will also run in the context of the\n> >   * object's thread, regardless of whether the signal is emitted in the same or\n> > @@ -41,10 +46,20 @@ namespace libcamera {\n> >   * \\sa Message, Signal, Thread\n> >   */\n> >  \n> > -Object::Object()\n> > -\t: pendingMessages_(0)\n> > +/**\n> > + * \\brief Construct an Object instance\n> > + * \\param[in] parent The object parent\n> > + *\n> > + * The new Object instance is bound to the thread of its \\a parent, or to the\n> > + * current thread if the \\a parent is nullptr.\n> > + */\n> > +Object::Object(Object *parent)\n> > +\t: parent_(parent), pendingMessages_(0)\n> >  {\n> > -\tthread_ = Thread::current();\n> > +\tthread_ = parent ? parent->thread() : Thread::current();\n> > +\n> > +\tif (parent)\n> > +\t\tparent->children_.push_back(this);\n> >  }\n> >  \n> >  Object::~Object()\n> > @@ -54,6 +69,16 @@ Object::~Object()\n> >  \n> >  \tif (pendingMessages_)\n> >  \t\tthread()->removeMessages(this);\n> > +\n> > +\tif (parent_) {\n> > +\t\tauto it = std::find(parent_->children_.begin(),\n> > +\t\t\t\t    parent_->children_.end(), this);\n> > +\t\tASSERT(it != parent_->children_.end());\n> > +\t\tparent_->children_.erase(it);\n> > +\t}\n> > +\n> > +\tfor (auto child : children_)\n> > +\t\tchild->parent_ = nullptr;\n> \n> Is this the behavior that is most logical? I'm not saying this is wrong \n> but want to discuss it a bit.\n> \n> If a object with a parent and children is delete is it not more logical \n> to move the children to the parent of the object being deleted? I fear \n> this could lead to subtle bugs if objects who are part of a hierarchy \n> become disjointed from the hierarchy when its parent is deleted.\n\nIt's a good question. This means that the children would need to be\nmoved to the thread of their grand-parent, which may be an unwanted side\neffect. Another option would be to delete the children :-) I've been\ntoying with this idea, I think it could simplify code, but I didn't want\nto include it in this series.\n\nIn any case I'll already update the documentation to explain that\nchildren of a delete object become orphan.\n\n> Maybe my gut feeling is wrong and my fear is unfounded.\n> \n> >  }\n> >  \n> >  /**\n> > @@ -143,16 +168,19 @@ void Object::invokeMethod(BoundMethodBase *method, void *args)\n> >   */\n> >  \n> >  /**\n> > - * \\brief Move the object to a different thread\n> > + * \\brief Move the object and all its children to a different thread\n> >   * \\param[in] thread The target thread\n> >   *\n> > - * This method moves the object from the current thread to the new \\a thread.\n> > - * It shall be called from the thread in which the object currently lives,\n> > - * otherwise the behaviour is undefined.\n> > + * This method moves the object and all its children from the current thread to\n> > + * the new \\a thread. It shall be called from the thread in which the object\n> > + * currently lives, otherwise the behaviour is undefined.\n> >   *\n> >   * Before the object is moved, a Message::ThreadMoveMessage message is sent to\n> >   * it. The message() method can be reimplement in derived classes to be notified\n> >   * of the upcoming thread move and perform any required processing.\n> > + *\n> > + * Moving an object that has a parent is not allowed, and causes undefined\n> > + * behaviour.\n> >   */\n> >  void Object::moveToThread(Thread *thread)\n> >  {\n> > @@ -161,6 +189,12 @@ void Object::moveToThread(Thread *thread)\n> >  \tif (thread_ == thread)\n> >  \t\treturn;\n> >  \n> > +\tif (parent_) {\n> > +\t\tLOG(Object, Error)\n> > +\t\t\t<< \"Moving object to thread with a parent is not permitted\";\n> > +\t\treturn;\n> > +\t}\n> > +\n> >  \tnotifyThreadMove();\n> >  \n> >  \tthread->moveObject(this);\n> > @@ -170,8 +204,17 @@ void Object::notifyThreadMove()\n> >  {\n> >  \tMessage msg(Message::ThreadMoveMessage);\n> >  \tsendMessage(&msg);\n> > +\n> > +\tfor (auto child : children_)\n> > +\t\tchild->notifyThreadMove();\n> >  }\n> >  \n> > +/**\n> > + * \\fn Object::parent()\n> > + * \\brief Retrieve the object's parent\n> > + * \\return The object's parent\n> > + */\n> > +\n> >  void Object::connect(SignalBase *signal)\n> >  {\n> >  \tsignals_.push_back(signal);\n> > diff --git a/src/libcamera/thread.cpp b/src/libcamera/thread.cpp\n> > index 24422f7b7bd0..872ad1bd9d69 100644\n> > --- a/src/libcamera/thread.cpp\n> > +++ b/src/libcamera/thread.cpp\n> > @@ -448,7 +448,7 @@ void Thread::dispatchMessages()\n> >  }\n> >  \n> >  /**\n> > - * \\brief Move an \\a object to the thread\n> > + * \\brief Move an \\a object and all its children to the thread\n> >   * \\param[in] object The object\n> >   */\n> >  void Thread::moveObject(Object *object)\n> > @@ -460,6 +460,12 @@ void Thread::moveObject(Object *object)\n> >  \tMutexLocker lockerTo(targetData->mutex_, std::defer_lock);\n> >  \tstd::lock(lockerFrom, lockerTo);\n> >  \n> > +\tmoveObject(object, currentData, targetData);\n> > +}\n> > +\n> > +void Thread::moveObject(Object *object, ThreadData *currentData,\n> > +\t\t\tThreadData *targetData)\n> > +{\n> >  \t/* Move pending messages to the message queue of the new thread. */\n> >  \tif (object->pendingMessages_) {\n> >  \t\tunsigned int movedMessages = 0;\n> > @@ -483,6 +489,10 @@ void Thread::moveObject(Object *object)\n> >  \t}\n> >  \n> >  \tobject->thread_ = this;\n> > +\n> > +\t/* Move all children. */\n> > +\tfor (auto child : object->children_)\n> > +\t\tmoveObject(child, currentData, targetData);\n> >  }\n> >  \n> >  }; /* namespace libcamera */","headers":{"Return-Path":"<laurent.pinchart@ideasonboard.com>","Received":["from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 55FF8600F9\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 17 Aug 2019 17:13:11 +0200 (CEST)","from pendragon.ideasonboard.com\n\t(dfj612yhrgyx302h3jwwy-3.rev.dnainternet.fi\n\t[IPv6:2001:14ba:21f5:5b00:ce28:277f:58d7:3ca4])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id D54AE556;\n\tSat, 17 Aug 2019 17:13:10 +0200 (CEST)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1566054791;\n\tbh=Oa2gcKSp0aMaPXP7VzsfNv8zHswh3ETVu6wE6OndagM=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=igtph8pLW4EdAWOREIPmy/siZsKyNYHP65Bfu2ujIntKDrU5jWz1/AO7uxGYLceRs\n\tpMhZZT8gjAVjopQJ37sgsE0ESzhju+zndGu8KNJ+yA9TBBxU1WEx6thqaKkIaPVKYm\n\tizJLHgFZ546n7dEvF3Tk53bw5I8W6hnBwv7dSQfs=","Date":"Sat, 17 Aug 2019 18:13:06 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Niklas =?utf-8?q?S=C3=B6derlund?= <niklas.soderlund@ragnatech.se>","Cc":"libcamera-devel@lists.libcamera.org","Message-ID":"<20190817151306.GB15630@pendragon.ideasonboard.com>","References":"<20190812124642.24287-1-laurent.pinchart@ideasonboard.com>\n\t<20190812124642.24287-16-laurent.pinchart@ideasonboard.com>\n\t<20190817151022.GQ16603@wyvern>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<20190817151022.GQ16603@wyvern>","User-Agent":"Mutt/1.10.1 (2018-07-13)","Subject":"Re: [libcamera-devel] [PATCH 15/18] libcamera: object: Create\n\tparent-child relationships","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.23","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Sat, 17 Aug 2019 15:13:11 -0000"}},{"id":2445,"web_url":"https://patchwork.libcamera.org/comment/2445/","msgid":"<20190817151745.GU16603@wyvern>","date":"2019-08-17T15:17:45","subject":"Re: [libcamera-devel] [PATCH 15/18] libcamera: object: Create\n\tparent-child relationships","submitter":{"id":5,"url":"https://patchwork.libcamera.org/api/people/5/","name":"Niklas Söderlund","email":"niklas.soderlund@ragnatech.se"},"content":"Hi Laurent,\n\nOn 2019-08-17 18:13:06 +0300, Laurent Pinchart wrote:\n> Hi Niklas,\n> \n> On Sat, Aug 17, 2019 at 05:10:22PM +0200, Niklas Söderlund wrote:\n> > On 2019-08-12 15:46:39 +0300, Laurent Pinchart wrote:\n> > > Add a parent Object to Object instances, and track the parent-children\n> > > relationships. Children are bound to the same thread as their parent,\n> > > and moving an Object to a thread automatically moves all its children.\n> > > \n> > > Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> > > ---\n> > >  include/libcamera/object.h     |  8 ++++-\n> > >  src/libcamera/include/thread.h |  2 ++\n> > >  src/libcamera/object.cpp       | 65 ++++++++++++++++++++++++++++------\n> > >  src/libcamera/thread.cpp       | 12 ++++++-\n> > >  4 files changed, 74 insertions(+), 13 deletions(-)\n> > > \n> > > diff --git a/include/libcamera/object.h b/include/libcamera/object.h\n> > > index 14b939a9bd3d..4a0a272b875a 100644\n> > > --- a/include/libcamera/object.h\n> > > +++ b/include/libcamera/object.h\n> > > @@ -9,6 +9,7 @@\n> > >  \n> > >  #include <list>\n> > >  #include <memory>\n> > > +#include <vector>\n> > >  \n> > >  #include <libcamera/bound_method.h>\n> > >  \n> > > @@ -23,7 +24,7 @@ class Thread;\n> > >  class Object\n> > >  {\n> > >  public:\n> > > -\tObject();\n> > > +\tObject(Object *parent = nullptr);\n> > >  \tvirtual ~Object();\n> > >  \n> > >  \tvoid postMessage(std::unique_ptr<Message> msg);\n> > > @@ -42,6 +43,8 @@ public:\n> > >  \tThread *thread() const { return thread_; }\n> > >  \tvoid moveToThread(Thread *thread);\n> > >  \n> > > +\tObject *parent() const { return parent_; }\n> > > +\n> > >  protected:\n> > >  \tvirtual void message(Message *msg);\n> > >  \n> > > @@ -58,6 +61,9 @@ private:\n> > >  \tvoid connect(SignalBase *signal);\n> > >  \tvoid disconnect(SignalBase *signal);\n> > >  \n> > > +\tObject *parent_;\n> > > +\tstd::vector<Object *> children_;\n> > > +\n> > >  \tThread *thread_;\n> > >  \tstd::list<SignalBase *> signals_;\n> > >  \tunsigned int pendingMessages_;\n> > > diff --git a/src/libcamera/include/thread.h b/src/libcamera/include/thread.h\n> > > index 630abb49534f..37edd4f5138b 100644\n> > > --- a/src/libcamera/include/thread.h\n> > > +++ b/src/libcamera/include/thread.h\n> > > @@ -61,6 +61,8 @@ private:\n> > >  \tfriend class ThreadMain;\n> > >  \n> > >  \tvoid moveObject(Object *object);\n> > > +\tvoid moveObject(Object *object, ThreadData *currentData,\n> > > +\t\t\tThreadData *targetData);\n> > >  \n> > >  \tstd::thread thread_;\n> > >  \tThreadData *data_;\n> > > diff --git a/src/libcamera/object.cpp b/src/libcamera/object.cpp\n> > > index 7c68ec01f78c..57cd6fefe20f 100644\n> > > --- a/src/libcamera/object.cpp\n> > > +++ b/src/libcamera/object.cpp\n> > > @@ -7,6 +7,8 @@\n> > >  \n> > >  #include <libcamera/object.h>\n> > >  \n> > > +#include <algorithm>\n> > > +\n> > >  #include <libcamera/signal.h>\n> > >  \n> > >  #include \"log.h\"\n> > > @@ -21,6 +23,8 @@\n> > >  \n> > >  namespace libcamera {\n> > >  \n> > > +LOG_DEFINE_CATEGORY(Object)\n> > > +\n> > >  /**\n> > >   * \\class Object\n> > >   * \\brief Base object to support automatic signal disconnection\n> > > @@ -29,10 +33,11 @@ namespace libcamera {\n> > >   * slots. By inheriting from Object, an object is automatically disconnected\n> > >   * from all connected signals when it gets destroyed.\n> > >   *\n> > > - * Object instances are bound to the thread in which they're created. When a\n> > > - * message is posted to an object, its handler will run in the object's thread.\n> > > - * This allows implementing easy message passing between threads by inheriting\n> > > - * from the Object class.\n> > > + * Object instances are bound to the thread of their parent, or the thread in\n> > > + * which they're created when they have no parent. When a message is posted to\n> > > + * an object, its handler will run in the object's thread. This allows\n> > > + * implementing easy message passing between threads by inheriting from the\n> > > + * Object class.\n> > >   *\n> > >   * Object slots connected to signals will also run in the context of the\n> > >   * object's thread, regardless of whether the signal is emitted in the same or\n> > > @@ -41,10 +46,20 @@ namespace libcamera {\n> > >   * \\sa Message, Signal, Thread\n> > >   */\n> > >  \n> > > -Object::Object()\n> > > -\t: pendingMessages_(0)\n> > > +/**\n> > > + * \\brief Construct an Object instance\n> > > + * \\param[in] parent The object parent\n> > > + *\n> > > + * The new Object instance is bound to the thread of its \\a parent, or to the\n> > > + * current thread if the \\a parent is nullptr.\n> > > + */\n> > > +Object::Object(Object *parent)\n> > > +\t: parent_(parent), pendingMessages_(0)\n> > >  {\n> > > -\tthread_ = Thread::current();\n> > > +\tthread_ = parent ? parent->thread() : Thread::current();\n> > > +\n> > > +\tif (parent)\n> > > +\t\tparent->children_.push_back(this);\n> > >  }\n> > >  \n> > >  Object::~Object()\n> > > @@ -54,6 +69,16 @@ Object::~Object()\n> > >  \n> > >  \tif (pendingMessages_)\n> > >  \t\tthread()->removeMessages(this);\n> > > +\n> > > +\tif (parent_) {\n> > > +\t\tauto it = std::find(parent_->children_.begin(),\n> > > +\t\t\t\t    parent_->children_.end(), this);\n> > > +\t\tASSERT(it != parent_->children_.end());\n> > > +\t\tparent_->children_.erase(it);\n> > > +\t}\n> > > +\n> > > +\tfor (auto child : children_)\n> > > +\t\tchild->parent_ = nullptr;\n> > \n> > Is this the behavior that is most logical? I'm not saying this is wrong \n> > but want to discuss it a bit.\n> > \n> > If a object with a parent and children is delete is it not more logical \n> > to move the children to the parent of the object being deleted? I fear \n> > this could lead to subtle bugs if objects who are part of a hierarchy \n> > become disjointed from the hierarchy when its parent is deleted.\n> \n> It's a good question. This means that the children would need to be\n> moved to the thread of their grand-parent, which may be an unwanted side\n> effect. Another option would be to delete the children :-) I've been\n> toying with this idea, I think it could simplify code, but I didn't want\n> to include it in this series.\n\nI toyed with the idea of deleting children too, but it seemed like such \na large change it might be best left for later once this is settled a \nbit.\n\n> \n> In any case I'll already update the documentation to explain that\n> children of a delete object become orphan.\n\nDocumenting the behavior solves the immediate issue,\n\nReviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>\n\n> \n> > Maybe my gut feeling is wrong and my fear is unfounded.\n> > \n> > >  }\n> > >  \n> > >  /**\n> > > @@ -143,16 +168,19 @@ void Object::invokeMethod(BoundMethodBase *method, void *args)\n> > >   */\n> > >  \n> > >  /**\n> > > - * \\brief Move the object to a different thread\n> > > + * \\brief Move the object and all its children to a different thread\n> > >   * \\param[in] thread The target thread\n> > >   *\n> > > - * This method moves the object from the current thread to the new \\a thread.\n> > > - * It shall be called from the thread in which the object currently lives,\n> > > - * otherwise the behaviour is undefined.\n> > > + * This method moves the object and all its children from the current thread to\n> > > + * the new \\a thread. It shall be called from the thread in which the object\n> > > + * currently lives, otherwise the behaviour is undefined.\n> > >   *\n> > >   * Before the object is moved, a Message::ThreadMoveMessage message is sent to\n> > >   * it. The message() method can be reimplement in derived classes to be notified\n> > >   * of the upcoming thread move and perform any required processing.\n> > > + *\n> > > + * Moving an object that has a parent is not allowed, and causes undefined\n> > > + * behaviour.\n> > >   */\n> > >  void Object::moveToThread(Thread *thread)\n> > >  {\n> > > @@ -161,6 +189,12 @@ void Object::moveToThread(Thread *thread)\n> > >  \tif (thread_ == thread)\n> > >  \t\treturn;\n> > >  \n> > > +\tif (parent_) {\n> > > +\t\tLOG(Object, Error)\n> > > +\t\t\t<< \"Moving object to thread with a parent is not permitted\";\n> > > +\t\treturn;\n> > > +\t}\n> > > +\n> > >  \tnotifyThreadMove();\n> > >  \n> > >  \tthread->moveObject(this);\n> > > @@ -170,8 +204,17 @@ void Object::notifyThreadMove()\n> > >  {\n> > >  \tMessage msg(Message::ThreadMoveMessage);\n> > >  \tsendMessage(&msg);\n> > > +\n> > > +\tfor (auto child : children_)\n> > > +\t\tchild->notifyThreadMove();\n> > >  }\n> > >  \n> > > +/**\n> > > + * \\fn Object::parent()\n> > > + * \\brief Retrieve the object's parent\n> > > + * \\return The object's parent\n> > > + */\n> > > +\n> > >  void Object::connect(SignalBase *signal)\n> > >  {\n> > >  \tsignals_.push_back(signal);\n> > > diff --git a/src/libcamera/thread.cpp b/src/libcamera/thread.cpp\n> > > index 24422f7b7bd0..872ad1bd9d69 100644\n> > > --- a/src/libcamera/thread.cpp\n> > > +++ b/src/libcamera/thread.cpp\n> > > @@ -448,7 +448,7 @@ void Thread::dispatchMessages()\n> > >  }\n> > >  \n> > >  /**\n> > > - * \\brief Move an \\a object to the thread\n> > > + * \\brief Move an \\a object and all its children to the thread\n> > >   * \\param[in] object The object\n> > >   */\n> > >  void Thread::moveObject(Object *object)\n> > > @@ -460,6 +460,12 @@ void Thread::moveObject(Object *object)\n> > >  \tMutexLocker lockerTo(targetData->mutex_, std::defer_lock);\n> > >  \tstd::lock(lockerFrom, lockerTo);\n> > >  \n> > > +\tmoveObject(object, currentData, targetData);\n> > > +}\n> > > +\n> > > +void Thread::moveObject(Object *object, ThreadData *currentData,\n> > > +\t\t\tThreadData *targetData)\n> > > +{\n> > >  \t/* Move pending messages to the message queue of the new thread. */\n> > >  \tif (object->pendingMessages_) {\n> > >  \t\tunsigned int movedMessages = 0;\n> > > @@ -483,6 +489,10 @@ void Thread::moveObject(Object *object)\n> > >  \t}\n> > >  \n> > >  \tobject->thread_ = this;\n> > > +\n> > > +\t/* Move all children. */\n> > > +\tfor (auto child : object->children_)\n> > > +\t\tmoveObject(child, currentData, targetData);\n> > >  }\n> > >  \n> > >  }; /* namespace libcamera */\n> \n> -- \n> Regards,\n> \n> Laurent Pinchart","headers":{"Return-Path":"<niklas.soderlund@ragnatech.se>","Received":["from mail-ed1-x543.google.com (mail-ed1-x543.google.com\n\t[IPv6:2a00:1450:4864:20::543])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 4A480600F9\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 17 Aug 2019 17:17:48 +0200 (CEST)","by mail-ed1-x543.google.com with SMTP id s15so7593382edx.0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 17 Aug 2019 08:17:48 -0700 (PDT)","from localhost ([185.224.57.161]) by smtp.gmail.com with ESMTPSA id\n\tbr8sm1262906ejb.92.2019.08.17.08.17.46\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tSat, 17 Aug 2019 08:17:47 -0700 (PDT)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=ragnatech-se.20150623.gappssmtp.com; s=20150623;\n\th=date:from:to:cc:subject:message-id:references:mime-version\n\t:content-disposition:content-transfer-encoding:in-reply-to\n\t:user-agent; bh=AX6Tdvealbd/71bpmCIkPJxQ61QOy0criX9l8dYUPgo=;\n\tb=fgp/5gh5tPm53P+mkpsaz6mBqP1Xz+dm9YljDMdQT++gMmYFmIlbVe4TBtBotcZo9Q\n\tsORMyglc0r1i/gGwDcmiOKArGYDqMyY7EfPhi9fA75pNMiZlS/kfM2qABj6TgO6UgSsI\n\t1ZfHgxnq67RVmjc4Z32WipgurQ29z0XFrK4g0BjEqkAiMY6uNV9sbqxHgJRfJOuINQyL\n\tIbCZnpTA933mNcDCnL+j0LMj0LAkEFQq8t+lZb3VBXnV2ZYcZ7+Q0Ex+4y9EsSXuFvu7\n\t7Tp7i9hdJivAFdmqJfcg/Kd0XilKEKYqqQjvCYJzkT0Dfxq33WayTCusw/FnYaunKoqG\n\tFibQ==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:date:from:to:cc:subject:message-id:references\n\t:mime-version:content-disposition:content-transfer-encoding\n\t:in-reply-to:user-agent;\n\tbh=AX6Tdvealbd/71bpmCIkPJxQ61QOy0criX9l8dYUPgo=;\n\tb=GDS4NJz1xPYnMWSFwpqzJsNjmGekOPUKRlqFPXfnw2c60rzbNqbaSv8hcWsIMLe7AX\n\t8gOFWZauuWB7mv4VCrumQKzWY35KPeKERI6sYL+pdiqw8wvye8aPX/UcB2s4JQLXRk2t\n\t0ZuQzTn2Q437MAKr8M+iHrFjYJ8iO0WQE86+FOyhX2tddG0OGeSQzEjtUAQ+wc9Ao7oC\n\t6TTYgFIhZ1YzBnSTByoxvya/nIztMZW+fdA4D+H8QHqiyAt+d0LVXEajKnq9BoPuDrTw\n\tcH5MNcsGdh5eyU1/5oiW+tSOpgDCgY8LV+L7/NxpI3aCL+tKpfda+bRIdvzvvUO/M9+L\n\tEnnQ==","X-Gm-Message-State":"APjAAAUFMLwTnOT+gZTmAJPgZsK4wHOXaMq2UMsIGtRSLMGRmNXNEsaH\n\tIZ3TUEAFMZCTeSr9an2bRqeYNg==","X-Google-Smtp-Source":"APXvYqz4Vns7rXI4xdrbbvnoS4RhDpZPpPdcybaZDXZ4EubVTr19AmJTQpqHqTnqjnFSvsHjMDby6A==","X-Received":"by 2002:a50:c081:: with SMTP id k1mr4105108edf.19.1566055067905; \n\tSat, 17 Aug 2019 08:17:47 -0700 (PDT)","Date":"Sat, 17 Aug 2019 17:17:45 +0200","From":"Niklas =?iso-8859-1?q?S=F6derlund?= <niklas.soderlund@ragnatech.se>","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Message-ID":"<20190817151745.GU16603@wyvern>","References":"<20190812124642.24287-1-laurent.pinchart@ideasonboard.com>\n\t<20190812124642.24287-16-laurent.pinchart@ideasonboard.com>\n\t<20190817151022.GQ16603@wyvern>\n\t<20190817151306.GB15630@pendragon.ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=iso-8859-1","Content-Disposition":"inline","Content-Transfer-Encoding":"8bit","In-Reply-To":"<20190817151306.GB15630@pendragon.ideasonboard.com>","User-Agent":"Mutt/1.12.1 (2019-06-15)","Subject":"Re: [libcamera-devel] [PATCH 15/18] libcamera: object: Create\n\tparent-child relationships","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.23","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","X-List-Received-Date":"Sat, 17 Aug 2019 15:17:48 -0000"}}]