[{"id":11670,"web_url":"https://patchwork.libcamera.org/comment/11670/","msgid":"<20200728152401.GD13753@pendragon.ideasonboard.com>","date":"2020-07-28T15:24:01","subject":"Re: [libcamera-devel] [PATCH 1/2] libcamera: object: Add\n\tdeleteLater() support","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Umang,\n\nThank you for the patch.\n\nOn Tue, Jul 28, 2020 at 10:57:24AM +0000, Umang Jain wrote:\n> This commit adds support to schedule the deletion of an Object to the\n> thread it is bound to (similar to [1]). An Object getting destroyed\n> by a different thread, is considered as a violation as per the\n\ns/thread,/thread/\n\n> threading model.\n\ns/threading model/libcamera threading model/\n\n> This will be useful for an Object where its ownership is shared via\n> shared pointers in different threads. If the thread which drops the\n> last reference of the Object is a different thread, the destructors\n> get called in that particular thread, not the one Object is bound to.\n> Hence, in order to resolve this kind of situation, the creation of\n> shared pointer can be accompanied by a custom deleter which in turns\n> use deleteLater() to ensure the Object is destroyed in its own thread.\n> \n> [1] https://doc.qt.io/qt-5/qobject.html#deleteLater\n> \n> Signed-off-by: Umang Jain <email@uajain.com>\n> ---\n>  include/libcamera/internal/message.h |  1 +\n>  include/libcamera/object.h           |  2 ++\n>  src/libcamera/object.cpp             | 20 ++++++++++++++++++++\n>  3 files changed, 23 insertions(+)\n> \n> diff --git a/include/libcamera/internal/message.h b/include/libcamera/internal/message.h\n> index 92ea64a..f1b133b 100644\n> --- a/include/libcamera/internal/message.h\n> +++ b/include/libcamera/internal/message.h\n> @@ -25,6 +25,7 @@ public:\n>  \t\tNone = 0,\n>  \t\tInvokeMessage = 1,\n>  \t\tThreadMoveMessage = 2,\n> +\t\tDeferredDelete = 3,\n\nThis needs to be documented in message.cpp. Just \"Object is scheduled\nfor deletion\" should be enough.\n\n>  \t\tUserMessage = 1000,\n>  \t};\n>  \n> diff --git a/include/libcamera/object.h b/include/libcamera/object.h\n> index 9a3dd07..a1882f0 100644\n> --- a/include/libcamera/object.h\n> +++ b/include/libcamera/object.h\n> @@ -27,6 +27,8 @@ public:\n>  \tObject(Object *parent = nullptr);\n>  \tvirtual ~Object();\n>  \n> +\tvoid deleteLater();\n> +\n>  \tvoid postMessage(std::unique_ptr<Message> msg);\n>  \n>  \ttemplate<typename T, typename R, typename... FuncArgs, typename... Args,\n> diff --git a/src/libcamera/object.cpp b/src/libcamera/object.cpp\n> index 1544a23..93e683a 100644\n> --- a/src/libcamera/object.cpp\n> +++ b/src/libcamera/object.cpp\n> @@ -98,6 +98,22 @@ Object::~Object()\n>  \t\tchild->parent_ = nullptr;\n>  }\n>  \n> +/**\n> + * \\brief Defer the deletion of Object instance to their thread\n\nBeing a bit pendantic, the function doesn't defer the deletion of the\nobject (as there's no pending deletion before it's called), but\nschedules a deferred deletion. Maybe \"Schedule deletion of the instance\nin the thread it belongs to\" ?\n\n> + *\n> + * Schedule deletion of the Object in the thread to which they are bound.\n> + * This will prevent destroying the Object from a different thread, which is\n> + * not allowed by the threading model.\n\nI think we should mention the event loop here to give a bit more\ninformation about when the object will be deleted exactly. Maybe\n\n * This function schedules deletion of the Object when control returns to the\n * event loop that the object belongs to. This ensures the object is destroyed\n * from the right context, as required by the libcamera threading model.\n *\n * If this function is called before the thread's event loop is started, the\n * object will be deleted when the event loop starts.\n\nI think we also have an issue if the object belongs to a thread that\ndoesn't have an event loop. We can fix that later, just adding\n\n * \\todo Handle scheduled deletion when the thread doesn't have an event loop\n\nfor now.\n\n> + *\n> + * It will typically be used with a custom deleter when creating a shared\n> + * pointer. It can then be ensured that the Object is deleted in its\n> + * own thread even if its last reference is dropped in a different thread.\n\nEven though this is our first use case, I'm not sure it would be the\nmost typical one. Maybe we can mention it as just an example instead ?\n\n * Deferred deletion can be used to control the destruction context with shared\n * pointers. An object managed with shared pointers is deleted when the last\n * reference is destroyed, which makes difficult to ensure through software\n * design which context the deletion will take place in. With a custom deleter\n * for the shared pointer using deleteLater(), the deletion can be guaranteed to\n * happen in the thread the object is bound to.\n *\n * \\code{.cpp}\n * std::shared_ptr<MyObjet> createObject()\n * {\n * \tstruct Deleter : std::default_delete<MyObject> {\n * \t\tvoid operator()(MyObject *obj)\n * \t\t{\n * \t\t\tdelete obj;\n * \t\t}\n * \t};\n *\n * \tMyObject *obj = new MyObject();\n *\n * \treturn std::shared_ptr<MyObject>(obj, Deleter());\n * }\n * \\endcode\n\nAnd you should add\n\n * \\context This function is \\threadsafe.\n\n> + */\n\nWhile at it, should we add\n\n * Object instances shall be destroyed from the thread they are bound to,\n * otherwise undefined behaviour may occur. If deletion of an Object needs to\n * be scheduled from a different thread, deleteLater() shall be used.\n\nto the Object::~Object() documentation ?\n\n> +void Object::deleteLater()\n> +{\n> +\tpostMessage(std::make_unique<Message>(Message::DeferredDelete));\n> +}\n> +\n>  /**\n>   * \\brief Post a message to the object's thread\n>   * \\param[in] msg The message\n> @@ -143,6 +159,10 @@ void Object::message(Message *msg)\n>  \n>  \t\tbreak;\n>  \t}\n\nA blank line here would be nice.\n\n> +\tcase Message::DeferredDelete: {\n> +\t\tdelete this;\n> +\t\tbreak;\n> +\t}\n\nNo need for the curly braces.\n\nReviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\n>  \n>  \tdefault:\n>  \t\tbreak;","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 159A6BD86F\n\tfor <parsemail@patchwork.libcamera.org>;\n\tTue, 28 Jul 2020 15:24:13 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 73ABD617AF;\n\tTue, 28 Jul 2020 17:24:12 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 18E6E60923\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tTue, 28 Jul 2020 17:24:11 +0200 (CEST)","from pendragon.ideasonboard.com (81-175-216-236.bb.dnainternet.fi\n\t[81.175.216.236])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 0A79C563;\n\tTue, 28 Jul 2020 17:24:09 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"SaykK2vF\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1595949850;\n\tbh=I4kuNP4W+om0eix0kVs/RLit8ZlFGHnfM/MiOGE3jaI=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=SaykK2vFwFxIlPT3cgSCv9KWBAg4c8fnqKXMNmXg+KYo1v0BmCxBIhT8jhH6NE98v\n\tt61dTEx8Xb2tZJlAlRaWAykLbX1pDerAuRnFcJ5aspsAdzbNRmRsI1BaEBfSIz8TU3\n\tj2r7pAisocaHvsyePs3amyE6rOUK6dDxk40epOEU=","Date":"Tue, 28 Jul 2020 18:24:01 +0300","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Umang Jain <email@uajain.com>","Message-ID":"<20200728152401.GD13753@pendragon.ideasonboard.com>","References":"<20200728105541.13326-1-email@uajain.com>\n\t<20200728105541.13326-2-email@uajain.com>","MIME-Version":"1.0","Content-Disposition":"inline","In-Reply-To":"<20200728105541.13326-2-email@uajain.com>","Subject":"Re: [libcamera-devel] [PATCH 1/2] libcamera: object: Add\n\tdeleteLater() support","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","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>","Cc":"libcamera-devel@lists.libcamera.org","Content-Type":"text/plain; charset=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]